“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からstableなhelm 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-ingressとingress-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.shはnginx-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