/nexus-operator

Sonatype Nexus OSS Kubernetes Operator based on Operator SDK

Primary LanguageGoApache License 2.0Apache-2.0

Nexus Operator Integration Checks Go Report Card GitHub release (latest by date) License GitHub go.mod Go version

Table of Contents

Nexus Operator

A Nexus OSS Kubernetes Operator based on the Operator SDK.

You can find us at OperatorHub or at the "Operators" tab in your OpenShift 4.x web console, just search for "Nexus". If you don't have access to OLM, try installing it manually following our quick installation guide.

If you have any questions please either open an issue or send an email to the mailing list: nexus-operator@googlegroups.com.

Pre Requisites

  • kubectl installed
  • Kubernetes (1.16+) or OpenShift (4.5+) cluster available (minikube or crc also supported)
  • Cluster admin credentials to install the Operator

Note: since version 0.6.0 we do not support OpenShift 3.11 or Kubernetes 1.11 anymore. If you need to install in these clusters, please use version 0.5.0 instead.

Quick Install

The installation procedure will create a Namespace named nexus-operator-system and will install every resources needed for the operator to run:

# requires python and kubectl
bash <(curl -s https://github.com/m88i/nexus-operator/blob/main/hack/install.sh)

Alternatively, you can manually elect a released version:

VERSION=<version from GitHub releases page>

kubectl apply -f https://github.com/m88i/nexus-operator/releases/download/${VERSION}/nexus-operator.yaml

You can choose any flavors of Nexus 3.x server from our examples directory and apply the YAML in any namespace in your cluster. Use these examples as a starting point to customize the server to meet your requirements.

Openshift

If you're running the Operator on Openshift (4.5+) and you're not using Red Hat image with persistence enabled, that's anything other than spec.useRedHatImage: true and spec.persistence.persistent: true, it's also necessary to configure a Security Context Constraints (SCC) resource.

This is necessary because the Nexus image requires its container to be ran as UID 200. The use of the restricted default SCC in Openshift results in a failure when starting the pods, as seen in Issue #41 and Issue #51 (see this issue for more details on why can't the Operator handle this for you as things are now).

Valid SCC resources can be found at the examples/ directory. You must associate the SCC with the ServiceAccount in use.

For persistent configurations:

$ oc apply -f examples/scc-persistent.yaml

For volatile configurations:

$ oc apply -f examples/scc-volatile.yaml

Note: you must choose one or the other, applying both will result in using the one applied last. Note: These have changed with the introduction of Nexus Operator version 0.6.0 to include configMap volumes.

Once the SCC has been created, run:

$ oc adm policy add-scc-to-user allow-nexus-userid-200 -z <ServiceAccountName>

This command will bind the SCC we just created with the ServiceAccount being used to create the Pods.

If you're using a custom ServiceAccount, replace "<ServiceAccountName>" with the name of that account. If you're not using a custom ServiceAccount, the operator has created a default one which has the same name as your Nexus CR, replace "<ServiceAccountName>" with that.

Clean up

Considering that you ran the install command above, to remove the operator completely from your cluster, just run:

make uninstall

Automatic Updates

The Nexus Operator is capable of conducting automatic updates within a minor (the y in x.y.z) when using the community default image (docker.io/sonatype/nexus3). In the future Red Hat images will also be supported by this feature.

Note: custom images will not be supported as there is no guarantee that they follow semantic versioning and as such, updates within the same minor may be disruptive.

Two fields within the Nexus CR control this behavior:

  • spec.automaticUpdate.disabled (boolean): Whether the Operator should perform automatic updates. Defaults to false (auto updates are enabled). Is set to false if spec.image is not empty and is different from the default community image.
  • spec.automaticUpdate.minorVersion (integer): The Nexus image minor version the deployment should stay in. If left blank and automatic updates are enabled the latest minor is set.

Note: if you wish to set a specific tag when using the default community image you must first disable automatic updates.

Important: a change of minors will not be monitored or acted upon as an automatic update. Changing the minor is a manual process initiated by the human operator and as such must be monitored by the human operator.

The state of ongoing updates is written to status.updateConditions, which can be easily accessed with kubectl:

$ kubectl describe nexus
# (output omitted)
  Update Conditions:
    Starting automatic update from 3.26.0 to 3.26.1
    Successfully updated from 3.26.0 to 3.26.1
Events:
  Type    Reason         Age   From    Message
  ----    ------         ----  ----    -------
  Normal  UpdateSuccess  59s   nexus3  Successfully updated to 3.26.1

Note: do not modify these conditions manually, the Operator reconstructs the update state from these.

Successful Updates

Once an update finishes successfully, an Event is raised. You may view the events from a particular Nexus CR by describing it:

$ kubectl describe <Nexus CR>

Or you may query all events:

$ kubectl get events

A successful update event looks like:

apiVersion: v1
count: 1
eventTime: null
firstTimestamp: "2020-08-26T13:56:16Z"
involvedObject:
  apiVersion: apps.m88i.io/v1alpha1
  kind: Nexus
  name: nexus3
  namespace: update
  resourceVersion: "66087"
  uid: f017e60f-21b5-4b14-b67c-341e029afae3
kind: Event
lastTimestamp: "2020-08-26T13:56:16Z"
message: Successfully updated to 3.26.1
# (output omitted)
reason: UpdateSuccess
reportingComponent: ""
reportingInstance: ""
source:
  component: nexus3
type: Normal
$ kubectl get events         
LAST SEEN   TYPE      REASON              OBJECT                         MESSAGE
12m         Normal    UpdateSuccess       nexus/nexus3                   Successfully updated to 3.26.1
# (output omitted)

Failed Updates

When an update fails, since the Deployments produced by the Operator use a Rolling Deployment Strategy there is no disruption and the previous version is still available. The Operator will then:

  1. disable automatic updates
  2. set spec.image to the version that was set before the update began
  3. raise a failure event

A failed update event looks like:

apiVersion: v1
count: 1
eventTime: null
firstTimestamp: "2020-08-21T18:29:11Z"
involvedObject:
  apiVersion: apps.m88i.io/v1alpha1
  kind: Nexus
  name: nexus3
  namespace: update
  resourceVersion: "51602"
  uid: 2e9ef49a-7d37-4c96-bfae-0642a9487c95
kind: Event
lastTimestamp: "2020-08-21T18:29:11Z"
message: Failed to update to 3.26.1. Human intervention may be required
# (output omitted)
reason: UpdateFailed
reportingComponent: ""
reportingInstance: ""
source:
  component: nexus3
type: Warning
$ kubectl get events         
  LAST SEEN   TYPE      REASON              OBJECT                         MESSAGE
  9m45s       Warning   UpdateFailed        nexus/nexus3                   Failed to update to 3.26.1. Human intervention may be required
# (output omitted)

Custom Configuration

Starting on version 0.6.0, the operator now mounts a ConfigMap with the contents of the nexus.properties file in the path $NEXUS_DATA/etc/nexus.properties.

The Nexus Operator mount this file with the contents of the field Spec.Properties using the Java properties format. If you change this field, the operator will deploy a new pod immediately to reflect the changes applied in the ConfigMap.

Don't update the managed ConfigMap directly, otherwise the operator will replace its contents with Spec.Properties field. Always use the Nexus CR as the only source of truth. See this example to learn how to properly set your properties directly in the CR.

Beware! Since we don't support HA yet, the server will be unavailable until the next pod comes up. Try to update the configuration only when you can afford to have the server unavailable.

Networking

There are three flavours for exposing the Nexus server deployed with the Nexus Operator: NodePort, Route (for OpenShift) and Ingress (for Kubernetes).

Use NodePort

You can expose the Nexus server via NodePort by setting the following parameters in the CR:

apiVersion: apps.m88i.io/v1alpha1
kind: Nexus
metadata:
  name: nexus3
spec:
  (...)
  networking:
    expose: true
    exposeAs: "NodePort"
    nodePort: 31031

It's not the recommended approach, but fits whatever Kubernetes flavour you have.

Network on OpenShift

On OpenShift, the Nexus server can be exposed via Routes. Set the following parameters in the CR:

apiVersion: apps.m88i.io/v1alpha1
kind: Nexus
metadata:
  name: nexus3
spec:
  (...)
  networking:
    expose: true

Network on Kubernetes 1.14+

On Kubernetes, we leverage from an Ingress to expose the Nexus service:

apiVersion: apps.m88i.io/v1alpha1
kind: Nexus
metadata:
  name: nexus3
spec:
  (...)
  networking:
    expose: true
    exposeAs: "Ingress"
    host: "nexus.example.com"

Please note that host is a required parameter when exposing via Ingress. Just make sure that that the host resolves to your cluster.

If you're running on Minikube, take a look in the article "Set up Ingress on Minikube with the NGINX Ingress Controller"

NGINX Ingress troubleshooting

If you've deployed the NGINX Ingress controller, you might see 413 ERROR - Entity too large in uploading the artifacts to the Nexus server.

You would need to enter the maximum size allowed for the data packet in the configMap for the controller.

If you've deployed the Ingress controller in Minikube it'll be available in the kube-system namespace

$ kubectl get deploy -n kube-system
                                                              
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
coredns                    1/1     1            1           47h
ingress-nginx-controller   1/1     1            1           47h

For checking out the name of the configMap you can run:

$ kubectl get deploy/ingress-nginx-controller -o yaml -n kube-system | grep "\--configmap" 

- --configmap=$(POD_NAMESPACE)/nginx-load-balancer-conf

Now you would need to edit the config map:

$ kubectl edit configmaps nginx-load-balancer-conf -n kube-system

In the root of the opened yaml file add:

data:
  proxy-body-size: 10m

Note: If you want to have no limit for the data packet you can specify the proxy-body-size: 0m

Ignoring external changes to Ingress/Route resources

Route and Ingress resources are highly configurable, and often the need to change them arises. For example, further configuration can be performed by webhooks, but these changes get undone by the Operator as soon as it detects them.

Starting at version 0.6.0 you may specify that the Operator should ignore external changes made to Ingress and Route resources. This is controlled by the spec.networking.ignoreUpdates boolean field in the Nexus resource. It defaults to false, meaning the Operator will change the Ingress/Route specification to match its state as defined by this resource. Set to true in order to prevent the Operator from undoing external changes in the resources' configuration.

apiVersion: apps.m88i.io/v1alpha1
kind: Nexus
metadata:
  name: nexus3
spec:
  networking:
    ignoreUpdates: true

TLS/SSL

For details about TLS configuration check out our TLS guide.

Annotations and Labels

You may provide custom labels and annotations to Route/Ingress resources by setting them on .spec.networking.annotations and .spec.networking.labels. For example:

apiVersion: apps.m88i.io/v1alpha1
kind: Nexus
metadata:
  name: nexus3
spec:
  networking:
    annotations:
      my-cool-annotation: "even-cooler-value"
      my-other-cool-annotation: "not-as-cool-value"
    labels:
      my-cool-label: "even-cooler-value"

Persistence

Extra volumes

Starting at version 0.6.0 you may specify extra volumes to be mounted at the pod running Nexus, which comes in handy for migrating existing blob stores, for example. These volumes are controlled by the spec.persistence.extraVolumes field.

For example, if you wanted to mount an AWS EBS volume, some PVC of yours and an EmptyDir volume:

apiVersion: apps.m88i.io/v1alpha1
kind: Nexus
metadata:
  name: nexus3
spec:
  persistence:
    extraVolumes:
      - name: "my-cool-ebs-vol"
        mountPath: "/path/for/AWS-EBS/"
        # This AWS EBS volume must already exist.
        awsElasticBlockStore:
          volumeID: "<volume id>"
          fsType: ext4
      - name: "my-cool-claim-vol"
        mountPath: "/path/for/persistent-vol-claim/"
        # This PVC must exist on the same namespace
        persistentVolumeClaim:
          claimName: "my-cool-claim"
      - name: "my-cool-empty-dir-vol"
        mountPath: "/path/for/emptyDir/"
        emptyDir: { }

Each item of this extraVolumes array provides:

  • mountPath: a string representing the path at which this volume should be mounted
  • a Kubernetes Volume specification

For more information about Kubernetes Volumes refer to their documentation and each specific plugin documentation. For additional details about Persistent Volumes and using Claims as volumes refer to the documentation.

Important: updating the spec.persistence.extraVolumes field may lead to temporary unavailability while the new deployment with the new volume configuration rolls out.

Minikube

On Minikube the dynamic PV creation might fail. If this happens in your environment, before creating the Nexus server, create a PV with this template: examples/pv-minikube.yaml. Then give the correct permissions to the directory in Minikube VM:

$ minikube ssh
                         _             _            
            _         _ ( )           ( )           
  ___ ___  (_)  ___  (_)| |/')  _   _ | |_      __  
/' _ ` _ `\| |/' _ `\| || , <  ( ) ( )| '_`\  /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )(  ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)

$ sudo chown 200:200 -R /data/pv0001/

$ ls -la /data/
total 8
drwxr-xr-x  3 root root 4096 Apr 26 15:42 .
drwxr-xr-x 19 root root  500 Apr 26 20:47 ..
drwxr-xr-x  2  200  200 4096 Apr 26 15:42 pv0001

Service Account

It is possible to use a custom ServiceAccount to perform your Deployments with the Nexus Operator via:

  • spec.serviceAccountName (string): ServiceAccountName is the name of the ServiceAccount used to run the Pods. If left blank, a default ServiceAccount is created with the same name as the Nexus CR.

Important: the Operator handles the creation of default resources necessary to run. If you choose to use a custom ServiceAccount be sure to also configure Role and RoleBinding resources.

Control Random Admin Password Generation

By default, from version 0.3.0 the Nexus Operator does not generate a random password for the admin user. This means that you can login in the server right away with the default administrator credentials (admin/admin123). Comes in handy for development purposes, but consider changing this password right away on production environments.

To enable random password generation, you can set the attribute generateRandomAdminPassword in the Nexus CR spec to true. Then the Nexus service will create a random password in the file system. You have to grab the password from a file inside the Nexus Server container in order to login in the web console:

$ kubectl exec <nexus server pod name> -- cat /nexus-data/admin.password

Use this password to login into the web console with the username admin.

Red Hat Certified Images

If you have access to Red Hat Catalog, you might change the flag spec.useRedHatImage to true. You'll have to set your Red Hat credentials in the namespace where Nexus is deployed to be able to pull the image.

In future versions the Operator will handle this step for you.

Image Pull Policy

You can control the pods Image Pull Policy using the spec.imagePullPolicy field. It accepts either of the following values:

  • Always
  • IfNotPresent
  • Never

If this field is set to an invalid value this configuration will be omitted, deferring to Kubernetes default behavior, which is Always if the image's tag is "latest" and IfNotPresent otherwise.

Leaving this field blank will also result in deferring to Kubernetes default behavior.

Repositories Auto Creation

From 0.3.0 version, the Operator will try to create an administrator user to be used on internal operations, such as creating community Maven repositories.

The default Nexus user admin is used to create the nexus-operator user, whose credentials are then stored in a secret with the same name as the Nexus CR.

It's possible to disable the operator user creation by setting spec.serverOperatons.disableOperatorUserCreation to true. In this case, the admin user will be used instead. This configuration is not recommended, since you can track all the operations, change the operator user permissions and enable or disable it if you need. By disabling the operator user creation, the Operator will use the default admin credentials to perform all server operations, which will fail if you change the default credentials (something that must be done when aiming for a secure environment).

The Operator also will create three Maven repositories by default:

  1. Apache
  2. JBoss
  3. Red Hat

All of these repositories will be also added to the maven-public group. This group will gather the vast majority of jars needed by the most common use cases out there. If you won't need them, just disable this behavior by setting the attribute spec.serverOperatons.disableRepositoryCreation to true in the Nexus CR.

All of these operations are disabled if the attribute spec.generateRandomAdminPassword is set to true, since default credentials are needed to create the nexus-operator user. You can safely change the default credentials after this user has been created.

Scaling

For now, the Nexus Operator won't accept a number higher than 1 to the spec.replicas attribute. This is because the Nexus server can't share its mounted persistent volume with other pods. See #191 for more details.

Horizontal scaling will only work once we add HA support to the operator (see #61). If you need to scale the server, you should take the vertical approach and increase the numbers of resource limits used by the Nexus server. For example:

apiVersion: apps.m88i.io/v1alpha1
kind: Nexus
metadata:
  name: nexus3
spec:
  replicas: 1
  # Set the resources requests and limits for Nexus pods. See: https://help.sonatype.com/repomanager3/system-requirements
  resources:
    limits:
      cpu: "4"
      memory: "8Gi"
    requests:
      cpu: "1"
      memory: "2Gi"
  persistence:
    persistent: true
    volumeSize: 10Gi

We are working to support HA in the future.

Contributing

Please read our Contribution Guide.