“stable”なHelm ChartリポジトリからChartCenterでNGINXを移行する

過去4年間、Kubernetes用のIngress NGINX Controllerをデプロイしたい人はHelmプロジェクトで管理されているstableリポジトリで公式のHelm chartであるnginx-ingressを見つけることができました。 

ですがもうそんな時代は終わりました。リポジトリはリバースプロキシやロードバランサーとして使用されている最も人気のあるIngressコントローラーであるNGINXだけでなく、すべてのオープンソースのK8sアプリのためのものです。

Helm 3の登場に伴い、Helmプロジェクトはstableリポジトリを非推奨としています。2019年11月時点ではchart所有者が個々のリポジトリに移行していく中で、新規chartはstableとして受入れられませんでした。 — stableリポジトリはHelm Hubから削除され、今年の11月には正式に廃止される予定です。

これはNGINXデプロイのインストーラーやメンテナーにとって何を意味するのでしょうか?まず最初にNGINXプロジェクトはGitHubリポジトリにKubernetes用に新しいingress-nginx Helm chartを提供しています。NGINX Ingress Controllerのデプロイメントをインストールおよび更新する場合はこのリポジトリのchartを使用してください。

新しいchartは現在、同じバージョンのNGINXアプリを展開しているにもかかわらず、stableのchartと同じではありません。このため、新しいchartを使用してNGINXを更新する際には多少の調整が必要になります。

ここではどのような機能があるのか、またJFrog ChartCenterが移行に際してどのようにして役立つのかを見てみましょう。

Helmセントラル・リポジトリ

stableなHelm chartは人気のある多数のKubernetesアプリの公式chartを常にセントラル・リポジトリで見つけることができることを意味していました。それはHelmクライアントにstableリポジトリを追加するだけです:

$ helm repo add stable https://kubernetes-charts.storage.googleapis.com/

 

この単一のstableリポジトリから作成者が承認した最新のHelm chartを使用して間違いなくnginx-ingressをデプロイすることができます。

stableリポジトリはほぼ陳腐化しているため、既知のHelm chart唯一のソースとしては利用できなくなっています。NGINXはHelmクライアントに個別にingress-nginxを追加するように指示するようになっています:

$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

 

セントラル・リポジトリがなければ別のK8sアプリを維持する度に別のhelm repo addを実行する必要があります。

もっと良い方法はないのでしょうか?

ChartCenterのIngress NGINXコントローラー

JFrog ChartCenterはHelmコミュニティによって変更不可であり、安全で信頼性の高いchartを見つけ、1つの場所からすべてのchartをプロキシするための信頼できる唯一の情報源を持つために構築された無料のHelm chartセントラル・リポジトリです。Helmクライアントから1つのHelmセントラル・リポジトリとして使用できるので、多数公開されているHelmリポジトリを追加する必要がなく、代わりに1つだけを利用することができます。

ChartCenterを通して、30,000バージョン以上のHelm chartが利用可能であり、NGINX Ingressコントローラーを含む多数の人気のあるアプリchartがホームページに掲載されているため、容易に検索できます。

ChartCenterからstablehelm chartからnginx-ingressを検索可能です:

また、ChartCenterでcurrentなchartからingress-nginxを見つけることもできます。

ChartCenterの利用

一度、ChartCenterをHelmクライアントに追加すると我々のデモで使用するNGINXリポジトリの両方を含む全てをHelm chartセントラル・リポジトリとして使用することができます。

ステップ1: HelmリポジトリとしてChartCenterを追加

Helmクライアントを設定し、ChartCenterリポジトリを単一のセントラルロケーションとしてchartが使用できるように設定します:

$ helm repo add center https://repo.chartcenter.io

$ helm repo add center https://repo.chartcenter.io
$ helm repo update

 

 

ステップ: リポジトリとしてChartCenterを利用

それではhelmクライアントからnginx-ingressingress-nginxのchartを確認してみましょう:

$ helm search repo center/stable/nginx-ingress
NAME                       	CHART VERSION	APP VERSION	DESCRIPTION
center/stable/nginx-ingress	1.41.2       	v0.34.1    	An nginx Ingress controller that uses ConfigMap...
 
$ helm search repo center/kubernetes-ingress-nginx/ingress-nginx
NAME                                         	CHART VERSION	APP VERSION	DESCRIPTION
center/kubernetes-ingress-nginx/ingress-nginx	2.11.2       	0.34.1     	Ingress controller for Kubernetes using NGINX a...

 

 

ChartCenter UIで検索した同一バージョンのchartが表示されます。

そして、ここでは異なるHelmリポジトリからのchartを1つのHelmセントラル・リポジトリで利用することがいかに簡単なのかが分かります。

nginx-ingress Helm chartのインストール

アップグレードをテストするためにnginx-ingress chartをインストールする必要があります。小さなシェルスクリプトnginx-ingress.shを使用して、上書き用のvaluesファイルを作成し、nginx-ingressをインストールします。

nginx-ingress.shにはchart名、バージョン、ロードバランサーの固定IPがあります:

#!/bin/bash
 
CHART_NAME="center/stable/nginx-ingress"
CHART_VERSION="1.41.2"
RELEASE=nginx-ingress
NAMESPACE=nginx-ingress
VALUES_FILE=nginx-ingress.yaml
LB_STATIC_IP=35.197.192.35
 
generateValues() {
   cat << EOF > "${VALUES_FILE}"
# Override values for nginx-ingress
 
controller:
 
 ## Use host ports 80 and 443
 daemonset:
   useHostPort: true
 
 kind: DaemonSet
 
 service:
 
   ## Set static IP for LoadBalancer
   loadBalancerIP: ${LB_STATIC_IP}
 
   externalTrafficPolicy: Local
 
 stats:
   enabled: true
 
 metrics:
   enabled: true
EOF
}
 
generateValues
kubectl create ns nginx-ingress || true
echo
helm upgrade --install ${RELEASE} -n ${NAMESPACE} ${CHART_NAME} --version ${CHART_VERSION} -f ${VALUES_FILE}
echo
kubectl -n ${NAMESPACE} get all

 

nginx-ingress.shを実行し、nginx-ingressをインストールしてみましょう:

$ ./nginx-ingress.sh
namespace/nginx-ingress created
 
Release "nginx-ingress" does not exist. Installing it now.
NAME: nginx-ingress
LAST DEPLOYED: Mon Aug 10 17:27:13 2020
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The nginx-ingress controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl --namespace nginx-ingress get services -o wide -w nginx-ingress-controller'
 
An example Ingress that makes use of the controller:
 
  apiVersion: extensions/v1beta1
  kind: Ingress
  metadata:
    annotations:
      kubernetes.io/ingress.class: nginx
    name: example
    namespace: foo
  spec:
    rules:
      - host: www.example.com
        http:
          paths:
            - backend:
                serviceName: exampleService
                servicePort: 80
              path: /
    # This section is only required if TLS is to be enabled for the Ingress
    tls:
        - hosts:
            - www.example.com
          secretName: example-tls
 
If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:
 
  apiVersion: v1
  kind: Secret
  metadata:
    name: example-tls
    namespace: foo
  data:
    tls.crt: 
    tls.key: 
  type: kubernetes.io/tls
 
NAME                                                 READY   STATUS              RESTARTS   AGE
pod/nginx-ingress-controller-rrsl9                   0/1     ContainerCreating   0          1s
pod/nginx-ingress-default-backend-5b967cf596-wrrfl   0/1     ContainerCreating   0          1s
 
NAME                                       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/nginx-ingress-controller           LoadBalancer   10.242.2.213         80:30643/TCP,443:31622/TCP   2s
service/nginx-ingress-controller-metrics   ClusterIP      10.242.10.112           9913/TCP                     2s
service/nginx-ingress-default-backend      ClusterIP      10.242.11.172           80/TCP                       2s
 
NAME                                      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/nginx-ingress-controller   1         1         0       1            0                     3s
 
NAME                                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-ingress-default-backend   0/1     1            0           2s
 
NAME                                                       DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-ingress-default-backend-5b967cf596   1         1         0       2s

 

そしてPodとserviceを確認してみましょう:

$ kubectl -n nginx-ingress get pods
NAME                                             READY   STATUS    RESTARTS   AGE
nginx-ingress-controller-rrsl9                   1/1     Running   0          78s
nginx-ingress-default-backend-5b967cf596-wrrfl   1/1     Running   0          78s
 
$ kubectl -n nginx-ingress get svc
NAME                               TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
nginx-ingress-controller           LoadBalancer   10.242.2.213    35.197.192.35   80:30643/TCP,443:31622/TCP   89s
nginx-ingress-controller-metrics   ClusterIP      10.242.10.112             9913/TCP                     89s
nginx-ingress-default-backend      ClusterIP      10.242.11.172             80/TCP                       89s

 

NGINX IngressコントローラーPodが起動し、ロードバランサーで外部IPが割当てられました。

 

nginx-ingress chartのインストールが成功したので、アップグレードに移りましょう。 

ingress-nginx Helm chartのアップグレード

NGINX Ingress Controllerのアップグレードを最新のchartを利用して実施しましょう。

ingress-nginx.shという名前に変えて今回もシェルスクリプトを実行します。

ingress-nginx.shはchart名とバージョンは異なりますがHelmのリリース名とロードバランサーの静的IPは同じです。

#!/bin/bash
 
CHART_NAME="center/kubernetes-ingress-nginx/ingress-nginx"
CHART_VERSION="2.11.1"
RELEASE=nginx-ingress
NAMESPACE=nginx-ingress
VALUES_FILE=ingress-nginx.yaml
LB_STATIC_IP=35.197.192.35
 
generateValues() {
   cat << EOF > "${VALUES_FILE}"
# Override values for ingress-nginx
 
controller:
 
 ## Use host ports 80 and 443
 hostPort:
   enabled: true
 
 kind: DaemonSet
 
 service:
 
   ## Set static IP for LoadBalancer
   loadBalancerIP: ${LB_STATIC_IP}
 
   externalTrafficPolicy: Local
 
 stats:
   enabled: true
 
 metrics:
   enabled: true
 
 admissionWebhooks:
   enabled: false
 
defaultBackend:
 enabled: true
EOF
}
 
generateValues
echo
helm upgrade --install ${RELEASE} -n ${NAMESPACE} ${CHART_NAME} --version ${CHART_VERSION} -f ${VALUES_FILE}
echo
kubectl -n ${NAMESPACE} get all

 

ingress-nginx.shnginx-ingress.shといくつかの違いがあります:

controller:
 
 ## Use host ports 80 and 443
 daemonset:
   useHostPort: true

 

 

いくつかの値が変更されました:

controller:
 
 ## Use host ports 80 and 443
 hostPort:
   enabled: true
 
 kind: DaemonSet

 

いくつかの値が追加されました:

admissionWebhooks:
   enabled: false
 
defaultBackend:
  enabled: true

 

 

今回のアップグレードシナリオではadmissionWebhooksを使用していないため無効にし、nginx-ingressのchartではデフォルトで有効になっていますがdefaultBackendを有効にしています。もちろん、必要に応じて値を微調整することができます。

ingress-nginx.shを実行し、nginx-ingressをアップグレードしてみましょう:

Release "nginx-ingress" has been upgraded. Happy Helming!
NAME: nginx-ingress
LAST DEPLOYED: Mon Aug 10 18:00:31 2020
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl --namespace nginx-ingress get services -o wide -w nginx-ingress-ingress-nginx-controller'
 
An example Ingress that makes use of the controller:
 
  apiVersion: networking.k8s.io/v1beta1
  kind: Ingress
  metadata:
    annotations:
      kubernetes.io/ingress.class: nginx
    name: example
    namespace: foo
  spec:
    rules:
      - host: www.example.com
        http:
          paths:
            - backend:
                serviceName: exampleService
                servicePort: 80
              path: /
    # This section is only required if TLS is to be enabled for the Ingress
    tls:
        - hosts:
            - www.example.com
          secretName: example-tls
 
If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:
 
  apiVersion: v1
  kind: Secret
  metadata:
    name: example-tls
    namespace: foo
  data:
    tls.crt: 
    tls.key: 
  type: kubernetes.io/tls
 
NAME                                                              READY   STATUS        RESTARTS   AGE
pod/nginx-ingress-controller-rrsl9                                1/1     Terminating   0          33m
pod/nginx-ingress-default-backend-5b967cf596-wrrfl                0/1     Terminating   0          33m
pod/nginx-ingress-ingress-nginx-controller-f9ztr                  0/1     Pending       0          5s
pod/nginx-ingress-ingress-nginx-defaultbackend-845f7cfd46-56grw   1/1     Running       0          5s
 
NAME                                                     TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
service/nginx-ingress-controller                         LoadBalancer   10.242.2.213    35.197.192.35   80:30643/TCP,443:31622/TCP   33m
service/nginx-ingress-ingress-nginx-controller           LoadBalancer   10.242.13.184          80:30601/TCP,443:30644/TCP   6s
service/nginx-ingress-ingress-nginx-controller-metrics   ClusterIP      10.242.12.190             9913/TCP                     6s
service/nginx-ingress-ingress-nginx-defaultbackend       ClusterIP      10.242.11.112             80/TCP                       5s
 
NAME                                                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/nginx-ingress-ingress-nginx-controller   1         1         0       1            0                     6s
 
NAME                                                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-ingress-ingress-nginx-defaultbackend   1/1     1            1           6s
 
NAME                                                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-ingress-ingress-nginx-defaultbackend-845f7cfd46   1         1         1       6s

 

Podとservice を確認してみましょう:

$ kubectl -n nginx-ingress get pods
NAME                                                          READY   STATUS    RESTARTS   AGE
nginx-ingress-ingress-nginx-controller-f9ztr                  0/1     Running   0          34s
nginx-ingress-ingress-nginx-defaultbackend-845f7cfd46-56grw   1/1     Running   0          34s

$ kubectl -n nginx-ingress get svc
NAME                                             TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
nginx-ingress-controller                         LoadBalancer   10.242.2.213    35.197.192.35   80:30643/TCP,443:31622/TCP   34m
nginx-ingress-ingress-nginx-controller           LoadBalancer   10.242.13.184          80:30601/TCP,443:30644/TCP   40s
nginx-ingress-ingress-nginx-controller-metrics   ClusterIP      10.242.12.190             9913/TCP                     40s
nginx-ingress-ingress-nginx-defaultbackend       ClusterIP      10.242.11.112             80/TCP                       39s

 

 

Podが更新されていて、新旧2つのserviceがあります。

kubectl -n nginx-ingress get svcを再度、実行してみましょう:

$ kubectl -n nginx-ingress get svc
NAME                                             TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
nginx-ingress-ingress-nginx-controller           LoadBalancer   10.242.13.184   35.197.192.35   80:30601/TCP,443:30644/TCP   3m26s
nginx-ingress-ingress-nginx-controller-metrics   ClusterIP      10.242.12.190             9913/TCP                     3m26s
nginx-ingress-ingress-nginx-defaultbackend       ClusterIP      10.242.11.112             80/TCP                       3m25s

 

すると、古いserviceは削除され、kubectlを使った他のコマンドを利用せすにhelm upgradeを実行しただけで新しいserviceが作成されました。もちろん、serviceを置換する場合は新しいserviceのために新しいロードバランサーを作成する必要があるため、ダウンタイムが発生します。

ありがとう、そして幸運を

2つの異なるchart間のシームレスなアップグレードを実現してくれたNGINX Ingressコントローラーのchartメンテナーに感謝します。

他のK8sアプリの個々のchartリポジトリへの移行も同様にスムーズに実施できます。Helm chartセントラル・リポジトリとしてChartCenterを利用することで、これらを実施することができます。

 


Happy Ingressing