Nexus Helm Chartを複数のDockerレジストリに対応できるようにする


はじめに

Sonatype Nexusはmaven, gradle, npm, dockerなどに対応したプライベートリポジトリマネージャーです。
権限制御も充実しているのでこれを採用している企業も多いようです。

公式Helm ChartもあるのでKubernetes上に簡単にデプロイすることができます。
しかしながらデフォルトのChartではDockerレジストリを1つしか作成することができません。
そこで今回はNexus Helm Chartを改変して複数のDockerレジストリに対応できるようにします。

使用したコードはGitHubにあげてあります。

環境情報

macOS Mojave 10.14.1
Helm: 3.0.1
EKS: 1.14
Nexus Helm Chart: 1.22.0

アーキテクチャ構成

公式のNexus Helm Chartをそのままデプロイすると下図の構成になります。
nexus.sample.comはHTTPでNexusにアクセスする際のドメインでdocker.sample.comはDockerレジストリにアクセスする際のドメインです。
また、ALB Ingress Controllerは独自にデプロイしたものでありNexus Helm Chartには含まれません。

Podの中にはNexusのコンテナに加えてNexus Proxyというコンテナが含まれます。
Nexus Proxyはその名の通りプロキシとして機能し、下記の環境変数を設定することでHTTPホストとDockerホストのルーティングを行います。

  • NEXUS_HTTP_HOST
  • UPSTREAM_HTTP_PORT
  • NEXUS_DOCKER_HOST
  • UPSTREAM_DOCKER_PORT

使用する環境変数が定められているのでDockerレジストリは1つに限られてしまうのが問題となります。
実際の業務においてはリリース用や開発用のレジストリで分けて誤リリースを避けたいというケースもあるかと思います。

今回は公式のHelm Chartを改修して下図のような構成にします。
至って普通の構成です。(そもそも何故公式ではプロキシコンテナ使ってるのか謎)

Helm Chart改修

まずはNexus Helm Chartを取得します。templateを編集するためローカルに持って来る必要があります。

$ helm fetch stable/sonatype-nexus --version 1.22.0
$ tar -xvzf sonatype-nexus-1.22.0.tgz

ディレクトリ構成は以下になります。デプロイ時はvalues.yamlを使わず自分で作成したmy-values.yamlを使います。
*が付いているファイルを今回編集していきます。

.
├── Chart.yaml
├── README.md
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── backup-pv.yaml
│   ├── backup-pvc.yaml
│   ├── backup-secret.yaml
│   ├── configmap.yaml
│   ├── deployment-statefulset.yaml *
│   ├── ingress.yaml *
│   ├── proxy-ks-secret.yaml
│   ├── proxy-route.yaml
│   ├── proxy-svc.yaml *
│   ├── pv.yaml
│   ├── pvc.yaml
│   ├── route.yaml
│   ├── secret.yaml
│   ├── service.yaml *
│   └── serviceaccount.yaml
├── values.yaml
└── my-values.yaml *

values

まずはmy-values.yamlを作成します。

my-values.yaml
statefulset:
  enabled: true
nexus:
  livenessProbe:
    port: 8081
  readinessProbe:
    port: 8081
  hosts:
  - host: nexus.sample.com
    port: 8081
  - host: docker-dev.sample.com
    port: 5003
  - host: docker-release.sample.com
    port: 15003
nexusProxy:
  enabled: false
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
  tls:
    enabled: false
service:
  enabled: true
  serviceType: NodePort

nexus.livenessProbe.portnexus.readinessProbe.portnexus.hostsは自分で定義したkeyとなります。Ingress、Service、StatefulSetに使われます。
Ingress ControllerにはALB Ingress Controllerを使用します。これはデプロイされている前提として進めます。

template

続いてtemplateを編集していきます。

proxy-svc.yaml.Values.nexusProxy.enabled=falseにするだけでプロキシ用のServiceが作成されないようにします。

proxy-svc.yaml
- {{- if or .Values.nexusProxy.enabled .Values.ingress.enabled }}
+ {{- if .Values.nexusProxy.enabled }}

ingress.yamlは自分で定義した.Values.nexus.hostsが同一のServiceにルーティングされるように下記内容を追記します。

ingress.yaml
spec:
  rules:
+     {{- range .Values.nexus.hosts }}
+     - host: {{ .host }}
+       http:
+         paths:
+           - backend:
+               serviceName: {{ template "nexus.name" $ }}-service
+               servicePort: {{ .port}}
+     {{- end }}

service.yamlも同様に自分で定義した.Values.nexus.hostsの値が反映されるように編集します。

service.yaml
spec:
  ports:
-   {{- with .Values.service.ports  }}
- {{ toYaml . | indent 2 }}
-   {{- end }}
+   {{- range .Values.nexus.hosts }}
+   - name: port-{{ .port }}
+     port: {{ .port }}
+     targetPort: {{ .port }}
+   {{- end }}

deployment-statefulset.yamlでも同様です。既存のcontainerPortは不要なので取り除きます。

deployment-statefulset.yaml
spec:
  template:
    spec:
      containers:
        - name: nexus
          ports:
-             - containerPort: {{ .Values.nexus.dockerPort }}
-               name: nexus-docker-g
-             - containerPort: {{ .Values.nexus.nexusPort }}
-               name: nexus-http
+             {{- range .Values.nexus.hosts }}
+             - name: port-{{ .port }}
+               containerPort: {{ .port }}
+             {{- end }}
          livenessProbe:
            httpGet:
-               port: {{ .Values.nexus.nexusPort }}
+               port: {{ .Values.nexus.livenessProbe.port }}
          readinessProbe:
            httpGet:
-               port: {{ .Values.nexus.nexusPort }}
+               port: {{ .Values.nexus.readinessProbe.port }}

デプロイ

あとはhelmコマンドでデプロイするだけです。

$ helm install --values ./my-values.yaml sonatype-nexus ./

Ingressで定義したドメインについて名前解決できるようRoute53等で設定すればNexusにアクセスできるようになります。

Dockerレジストリ作成

Dockerレジストリ作成はNexusの画面から行う必要があります。

adminユーザでログインし、Repositories設定画面からリポジトリ作成をします。

Recipeにはdocker(hosted)を選択します。

詳細設定はNameとHTTP Portだけ入力して他はデフォルトで問題ありません。

「Create repository」を押せば完了です。Dockerレジストリが作成されていることが確認できます。

開発時用のDockerレジストリを作成したので同様の手順でリリース用のものも作成します。

検証

実際に2つのDockerレジストリを検証します。
まずはローカルにDockerイメージを落とします。

$ docker pull alpine:3.9

$ docker images
REPOSITORY  TAG  IMAGE ID      CREATED      SIZE
alpine      3.9  82f67be598eb  2 weeks ago  5.53MB

タグ付けしてdocker pushします。
HTTPの場合はinsecure registryに登録することに注意しましょう。

$ docker tag 82f67be598eb docker-dev.sample.com/alpine:3.7
$ docker push docker-dev.sample.com/alpine:3.7

$ docker tag 82f67be598eb docker-release.sample.com/alpine:3.7
$ docker push docker-release.sample.com/alpine:3.7

画面から2種類のDockerレジストリにイメージが格納されていることが確認できます。

おわりに

公式のNexus Helm Chartを改修して複数Dockerレジストリに対応できることを確認しました。
Helm Chartのtemplateの編集箇所が多いとバージョンが上がったときに都度対応しなくてはならないのが面倒ですが、今回についてはしょうがないかなと思います。