この記事は、TUNA-JP Advent Calendar 2022 の24日目のエントリーとして、VMware がメインスポンサーとなって開発が進められているオープンソースソフトウェア群であり Cloud Native Computing Foundation の Sandbox project のひとつである Carvel について、そもそもこのようなソフトウェア群が生まれた背景を、Kubernetes 登場以前から存在するオープンソースソフトウェアプロジェクトである Cloud Foundry BOSH の設計思想や開発メンバーとの関わりと併せて解き明かしていこうとするものです。
ほとんどの内容が過去の経緯を振り返るものであり、目新しい内容はあまりありません。おっさんのくだらない昔話ばっかりです。ただ1点、大事なことを最後に書いときますので、お急ぎの方はそこだけ見ていってくださいませ。
4年前の Advent Calendar 記事を振り返る
まだ Kubernetes のことを大して知らず、転職も未経験だった私は、『BOSHとリリースエンジニアリング』という記事を書いていました。
ひとつのYAMLで仮想マシンベースの様々な分散システムを定義づけることができる BOSH というものが、確固たる理念のもとで設計されており、Cloud Foundry という非常に複雑なアプリケーションプラットフォームの構築と運用を支えていることをご紹介しました。
その後時代は流れ、今では Kubernetes をコンテナプラットフォームとしてその上でのアプリケーションの構築と運用が当たり前となりました。YAMLによってコンテナプラットフォーム上のワークロードを定義づけることもかなり当たり前になり、さらに Terraform などの Infrastructure as Code を支えるツールもいよいよ普及してきていることから、もはやBOSHを推してく世の中でもない感じになっています。
最近では、YAML や HCL などを Git レポジトリに格納して source of truth とし、その変更を契機にアプリケーションやインフラを自動的に更新してゆく継続的デリバリー(CD)のプラクティスである GitOps を、運用の省力化やワークフローの高速化の文脈で実践している皆さんも多いのではないでしょうか。
Kubernetes とリリースエンジニアリング
『BOSHとリリースエンジニアリング』の記事で、モダンなリリースエンジニアリングを考えるなら、そのへんはBOSHがよしなにやるから、いいからBOSHを使えよ!という主旨をお伝えしたところです。
では、今われわれがようやく使い慣れた Kubernetes は、BOSH によって実現されていたリリースエンジニアリングの各要素をどこまで満たしてくれているのでしょうか。
一見すると、Kubernetes を使うことで、かなりの部分を満たしているように思えます。
- Kubernetes では、ひとつまたは複数のYAMLによってコンテナプラットフォーム上のワークロードを統合的に定義づけることができるので、Identifiability と Reproduciability を満足している!
- オレたちには手塩にかけて書き上げたYAMLがある。これさえあれば、どんな Kubernetes クラスタでも、全く同じ構成でワークロードをデプロイできる!
-
kubectl apply
コマンドには冪等性があり、YAMLをうまいこと整えておきさえすれば、このコマンドだけでワークロードの新規デプロイも更新もできるので、Consistency と Agility を満足している!- 俺たちはもはや、
kubectl create
やらkubectl patch
やら、そういう手続き的な運用をしていない。オレたちには手塩にかけて書き上げたYAMLがある。こいつを更新したら、必ずkubectl apply
コマンドを使って反映させることで宣言的な運用を実現、冪等性もバッチリや!
- 俺たちはもはや、
これらは本当でしょうか?
Kubernetes でも残るリリースエンジニアリング面の悩み
以下に示す2つのケースを考えてみましょう。どうやら Kubernetes を単に使うだけでは、うまい具合に解決しなさそうな雰囲気がします。
その1: 切り戻しや不具合再現がうまくいかない
貴方のその手塩にかけたYAMLに、例えば以下のような記述はないでしょうか?
例1
image: hoge-container-registry.com/hoge-project/hoge-app:latest
Pod や Deployment などのYAMLで使われるコンテナイメージの参照(ほとんどは image:
のハッシュ(or ディクショナリー)の値として書かれる)において、例1のような latest
タグは、とりあえずコンテナレジストリに置いてある最新のイメージを使ってデプロイできればそれでいい、動くのが確認できたらそれでOKという、動作検証の場合などには、とても便利です。
しかし、本番環境やそれに近い環境(ステージング環境、QA環境など)ではよくある話なのですが、直近にビルドしたコンテナイメージにバグが見つかったので、その前に正常動作していたコンテナイメージに戻して修正デプロイする、いわゆる「切り戻し」操作が必要になったとき、 latest
タグを使っていると、いきなり困ることになります。コンテナレジストリのUIを眺め、リリースの直前まで動いてたイメージはどれだったかな・・・あれ、前回のリリースからだいぶ経ってて、それ以降にコンテナイメージの再ビルド・再pushがたくさん行われてて、どれが正解だか分からないぞ・・・みたいな状況。あるある、と思われた方が結構おられるのではないでしょうか。
例2
image: nginx:1.23.3-alpine
latest
タグだとどのバージョンのソフトウェアを使うのかが明示できないので、リリースの際には image:
にバージョン番号をタグとして設定するお作法を採用している。そうすれば、切り戻しの際には以前正常動作していたときの同じコンテナイメージが使われるので、安心して切り戻せるはずである。大丈夫大丈夫・・・
残念。世の中そんなに甘くはありません。
nginx:1.23.3-alpine
というコンテナイメージには nginx 1.23.3 に関連するファイルだけが含まれているわけではありません。2022/12/24時点のベースイメージである alpine:3.17.0
には libc.musl や libssl などといった共有ライブラリも含まれており、これらにバグ改修や改善などが行われると alpine:3.17.0
というコンテナイメージが再ビルドされ DockerHubに 再pushされて差し替わります。さらに nginx:1.23.3-alpine
も自動的に再ビルド・再pushされて差し替わります。
すなわち、同じタグでも時が経てばコンテナイメージの中身が変わっているということは、起こり得るのです。上記の例では、共有ライブラリが差し替わることによって、nginxの動作が変わることもあり得る話です。そのため、切り戻そうと思ってもうまくいかない、不具合をなぜか再現できない、ということも起こり得ることになります。
自チームで開発しているアプリであれば、ビルド時やリリース時などにコンテナイメージにタグを適切に付与することで、切り戻しなどの際に所望のコンテナイメージを使えるため、その点で困ることはなさそうです。しかし、nginxや各種のDBMS(MySQLやPostgreSQLなど)といった、アプリに付随して動作させるようなコンテナを、外部のコンテナレジストリ(DockerHubなど)のイメージを持ち込んでデプロイする場合には、コンテナイメージに対して勝手にタグを振るわけにもいかず、困ってしまうことがあるのではないでしょうか。
考えられる対策
コンテナイメージの参照に、 :latest
:3.17.0
:1.23.3-alpine
などのいったタグは使わず、 index.docker.io/library/nginx@sha256:dd8a054d7ef030e94a6449783605d6c306c1f69c10c2fa06b66a030e0d1db793
などという digest 値を使う記法にします。
こうすれば、コンテナイメージ内のディレクトリツリー全てに対して一意性を担保できますので、所望の時点への切り戻しや不具合再現がうまくいくことになります。
image: index.docker.io/library/nginx@sha256:dd8a054d7ef030e94a6449783605d6c306c1f69c10c2fa06b66a030e0d1db793
ただ、YAMLに書かれている全てのコンテナイメージの参照を digest 記法とするのは、手動で行うのはとても面倒です。
多くのコンテナからなる複雑な構成の場合、それぞれのコンテナイメージの digest をいちいち調べるのか・・・そのへん、何とか自動的によしなにやってくれないものなのか。
BOSH ではどうしていたのか?
BOSH において、Kubernetes におけるコンテナイメージに当たるものは、強いて言えば BOSH stemcell と BOSH release との組み合わせ、ということになります。
BOSH stemcell は VMのベースイメージであり、ビルドされた順番に沿ってバージョン番号が振られています。
BOSH release は言わば Source Tarball の形式で格納されたソフトウェアパッケージの集合体であり、それぞれのソフトウェアパッケージとしてはソースコードのバージョン番号あるいは Git commit ID などによって識別される特定のバージョンのみが格納されています。
BOSH によってデプロイされたVMに着目すると、stemcell のバージョン と release に含まれる各ソフトウェアパッケージのバージョンがありますが、stemcell バージョンと release バージョンが明記されているひとつの BOSH deployment config からデプロイされた結果として、いずれのバージョンも必ず一意となっています。
すなわち、BOSH deployment config において BOSH stemcell と BOSH release の各バージョンを明示すれば、過去のいかなる構成も再現可能となるわけです。
その2: ConfigMap や Secret の更新が反映されない
ConfigMap や Secret としてワークロードの設定値や設定ファイルを定義しておき、コンテナには configMapKeyRef や secretKeyRef 経由で環境変数を設定したり、ボリュームマウントして設定ファイルとしたりするようなことがよくありますね。
しかしほとんどの場合、ConfigMap や Secret のYAMLを更新して kubectl apply
しても、その設定の変更が反映されるべき Pod が再作成されることもコンテナが再起動されることもなく、平然と変更前の設定のままで動き続けるだろうと思います。もし設定ファイルの変更を検知して勝手に設定をリロードするようなソフトウェアだったら、それはそれで便利かもしれませんが、コンテナじゃない環境で動かすような場合は逆に運用がシビアになりそうで、なかなかこわい実装だと思います。
例として、コンテナにボリュームマウントする nginx の設定ファイルを ConfigMap として設定し、これを kubectl apply
で更新しても、その変更が効かない模様を、以下に示します。
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf
labels:
app: nginx
data:
nginx.conf: |
worker_processes 1;
events {}
http {
access_log /mnt/access.log combined;
server {
listen 80 default_server;
root /usr/share/nginx/html;
index index.html;
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx-lb
labels:
app: nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.23.3-alpine
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx
readOnly: true
name: nginx-conf
volumes:
- name: nginx-conf
configMap:
name: nginx-conf
items:
- key: nginx.conf
path: nginx.conf
EOF
configmap/nginx-conf created
service/nginx-lb created
deployment.apps/nginx created
$ kubectl get all -l app=nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-7d8ffdbdb9-j8vn5 1/1 Running 0 13s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx-lb LoadBalancer 10.43.82.56 192.168.1.131 80:31716/TCP 13s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 13s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-7d8ffdbdb9 1 1 1 13s
$ curl 192.168.1.131
【略】
$ k exec nginx-7d8ffdbdb9-j8vn5 -- cat /mnt/access.log
10.42.0.0 - - [24/Dec/2022:12:34:56 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.68.0"
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf
labels:
app: nginx
data:
nginx.conf: |
worker_processes 1;
events {}
http {
log_format short "time: \$time_iso8601";
access_log /mnt/access.log short;
server {
listen 80 default_server;
root /usr/share/nginx/html;
index index.html;
}
}
EOF
configmap/nginx-conf configured
$ curl 192.168.1.131
【略】
$ k exec nginx-7d8ffdbdb9-j8vn5 -- cat /mnt/access.log
10.42.0.0 - - [24/Dec/2022:12:34:56 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.68.0"
10.42.0.0 - - [25/Dec/2022:12:35:02 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.68.0"
仕組みがよく分かっていないと、いかにも理不尽を突きつけられている雰囲気のログですが、要はnginxが新しい設定ファイルを読み込んでないのが原因です。
更新デプロイを完結させるためには、kubectl rollout restart deployment/???
するだの、 kubectl delete pod
して Deployment から Pod が自動再作成されるのを待つだの、ちょっとした追加コマンドを実行しないといけないことになります。
CDとかがんばってやろうとしても、その点でハマったり、そこを解決するためにCDパイプラインに変な設定を追加しないといけなかったり、いっそやむなく諦めて手運用に戻ったりなんかしたりして。
考えられる対策
ConfigMap や Secret を更新するたびに、そのリソースの名前(metadata.name)を新しいものに付け替えていき、その都度参照元となる Deployment などに設定する ConfigMap 名 や Secret 名を併せて書き換える、という手法があるようです。こうすることで、ConfigMap や Secret の更新が反映されるべき Deployment などにも何らかの更新がなされるため、更新デプロイがめでたく完結する、と。
うーん。泥臭い。泥臭すぎる・・・そのへん、何とか自動的によしなにやってくれないものなのか。
BOSH ではどうしていたのか?
BOSH stemcell や BOSH release に含まれるタスク群(BOSHでは job と呼ばれる)に対する設定値は、deployment config もしくは director runtime config に記載します。
それらの設定値を更新デプロイする際、BOSH director は diff をとって更新が必要なVMがどれかをピックアップし、該当のVMについては設定値を流し込んで全ての job を再起動(もしくは必要に応じてVMごと再作成)します。
deployment config と director runtime config のいずれも、どのVMが設定更新対象となるのかを特定しやすいようなスキーマとなっており、漏れなくムダなく設定変更を行える設計となっています。
Kubernetes 上のリリースエンジニアリングのかゆいところに手が届く Carvel
2020年8月に投稿された Carvel 公式のブログ記事には、思いっきり
Carvel was born from frustration with existing tools
とか書いてあります。Kubernetes 自体は良いものだが、リリースエンジニアリング的にかゆいところには微妙に手が届かなかったし、他のツールも帯に短し襷に長しで、しょうがなく作ったのが Carvel の各ツール群だったのです。
YAMLに書かれた全てのコンテナイメージの参照を digest 記法に塗り替える kbld
Carvel のツール群のひとつに kbld というものがあります。
例えば、以下のように使えます。
$ cat <<EOF | kbld -f -
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.23.3-alpine
EOF
resolve | final: nginx:1.23.3-alpine -> index.docker.io/library/nginx@sha256:dd8a054d7ef030e94a6449783605d6c306c1f69c10c2fa06b66a030e0d1db793
---
apiVersion: v1
kind: Pod
metadata:
annotations:
kbld.k14s.io/images: |
- origins:
- resolved:
tag: 1.23.3-alpine
url: nginx:1.23.3-alpine
url: index.docker.io/library/nginx@sha256:dd8a054d7ef030e94a6449783605d6c306c1f69c10c2fa06b66a030e0d1db793
name: nginx
spec:
containers:
- image: index.docker.io/library/nginx@sha256:dd8a054d7ef030e94a6449783605d6c306c1f69c10c2fa06b66a030e0d1db793
name: nginx
Succeeded
元のYAMLをパイプで kbld に通せば、image:
を拾って digest 記法に塗り替えたYAMLを出力してくれます。
GitOps を行う中で、切り戻しや不具合再現の際のトラブルを解消するためには、YAMLを Git リポジトリに格納する前に kbld を通して全てのコンテナイメージの参照を digest 記法に塗り替えておくべきでしょう。
ワークロードの設定変更を漏らさず更新デプロイする kapp
Carvel のツール群のひとつに kapp というものがあります。
kubectl apply -f
の代わりに kapp deploy -a [app_name] -c -f
のコマンドを使うことで、変更差分をプレビューしたうえで更新デプロイできたりする便利なコマンドなのですが、実はニッチな機能も数多くもっています。
そのひとつが、 Versioned Resources というものです。
ワークロードの設定更新の際に反映させたい ConfigMap や Secret などのリソースに対して kapp.k14s.io/versioned: ""
という annotation を追加しておくことで、元のYAMLに書いていたリソース名にバージョン番号を付加した別名をつけて実際のリソースを作成し、このリソースを参照するべき Deployment などからの参照先も勝手に別名の方に付け替えてくれます。
どういうことかよくわからないと思いますので、kubectl apply
でうまくいかなかった nginx の ConfigMap による設定変更が kapp を使うとうまくいく模様を以下に示します。
$ cat <<EOF | kapp deploy -a nginx -f - -y
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf
labels:
app: nginx
annotations:
kapp.k14s.io/versioned: ""
data:
nginx.conf: |
worker_processes 1;
events {}
http {
access_log /mnt/access.log combined;
server {
listen 80 default_server;
root /usr/share/nginx/html;
index index.html;
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx-lb
labels:
app: nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.23.3-alpine
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx
readOnly: true
name: nginx-conf
volumes:
- name: nginx-conf
configMap:
name: nginx-conf
items:
- key: nginx.conf
path: nginx.conf
EOF
Target cluster 'https://127.0.0.1:6443' (nodes: home03, 1+)
Changes
Namespace Name Kind Age Op Op st. Wait to Rs Ri
default nginx Deployment - create - reconcile - -
^ nginx-conf-ver-1 ConfigMap - create - reconcile - -
^ nginx-lb Service - create - reconcile - -
Op: 3 create, 0 delete, 0 update, 0 noop, 0 exists
Wait to: 3 reconcile, 0 delete, 0 noop
4:33:06PM: ---- applying 1 changes [0/3 done] ----
4:33:06PM: create configmap/nginx-conf-ver-1 (v1) namespace: default
4:33:06PM: ---- waiting on 1 changes [0/3 done] ----
4:33:06PM: ok: reconcile configmap/nginx-conf-ver-1 (v1) namespace: default
4:33:06PM: ---- applying 2 changes [1/3 done] ----
4:33:07PM: create service/nginx-lb (v1) namespace: default
4:33:07PM: create deployment/nginx (apps/v1) namespace: default
4:33:07PM: ---- waiting on 2 changes [1/3 done] ----
4:33:07PM: ok: reconcile service/nginx-lb (v1) namespace: default
4:33:07PM: ongoing: reconcile deployment/nginx (apps/v1) namespace: default
4:33:07PM: ^ Waiting for generation 2 to be observed
4:33:07PM: L ok: waiting on replicaset/nginx-859ffcd8c (apps/v1) namespace: default
4:33:07PM: L ok: waiting on pod/nginx-859ffcd8c-ft64b (v1) namespace: default
4:33:07PM: ---- waiting on 1 changes [2/3 done] ----
4:33:07PM: ok: reconcile deployment/nginx (apps/v1) namespace: default
4:33:07PM: ---- applying complete [3/3 done] ----
4:33:07PM: ---- waiting complete [3/3 done] ----
Succeeded
$ kubectl get all,cm -l app=nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-5667ccd54b-ll67q 1/1 Running 0 5m9s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx-lb LoadBalancer 10.43.154.165 192.168.1.131 80:31150/TCP 2m34s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 2m34s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-5667ccd54b 1 1 1 5m9s
replicaset.apps/nginx-859ffcd8c 0 0 0 8m59s
NAME DATA AGE
configmap/nginx-conf-ver-1 1 2m34s
$ curl 192.168.1.131
【略】
$ k exec nginx-859ffcd8c-ft64b -- cat /mnt/access.log
10.42.0.0 - - [24/Dec/2022:12:56:02 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.68.0"
$ cat <<EOF | kapp deploy -a nginx -f - -y
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf
labels:
app: nginx
annotations:
kapp.k14s.io/versioned: ""
data:
nginx.conf: |
worker_processes 1;
events {}
http {
log_format short "time: \$time_iso8601";
access_log /mnt/access.log short;
server {
listen 80 default_server;
root /usr/share/nginx/html;
index index.html;
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx-lb
labels:
app: nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.23.3-alpine
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx
readOnly: true
name: nginx-conf
volumes:
- name: nginx-conf
configMap:
name: nginx-conf
items:
- key: nginx.conf
path: nginx.conf
EOF
Target cluster 'https://127.0.0.1:6443' (nodes: home03, 1+)
Changes
Namespace Name Kind Age Op Op st. Wait to Rs Ri
default nginx Deployment 3m update - reconcile ok -
^ nginx-conf-ver-2 ConfigMap - create - reconcile - -
Op: 1 create, 0 delete, 1 update, 0 noop, 0 exists
Wait to: 2 reconcile, 0 delete, 0 noop
4:36:56PM: ---- applying 1 changes [0/2 done] ----
4:36:56PM: create configmap/nginx-conf-ver-2 (v1) namespace: default
4:36:56PM: ---- waiting on 1 changes [0/2 done] ----
4:36:56PM: ok: reconcile configmap/nginx-conf-ver-2 (v1) namespace: default
4:36:56PM: ---- applying 1 changes [1/2 done] ----
4:36:57PM: update deployment/nginx (apps/v1) namespace: default
4:36:57PM: ---- waiting on 1 changes [1/2 done] ----
4:36:57PM: ongoing: reconcile deployment/nginx (apps/v1) namespace: default
4:36:57PM: ^ Waiting for generation 4 to be observed
4:36:57PM: L ok: waiting on replicaset/nginx-859ffcd8c (apps/v1) namespace: default
4:36:57PM: L ok: waiting on replicaset/nginx-5667ccd54b (apps/v1) namespace: default
4:36:57PM: L ok: waiting on pod/nginx-859ffcd8c-ft64b (v1) namespace: default
4:36:57PM: L ongoing: waiting on pod/nginx-5667ccd54b-ll67q (v1) namespace: default
4:36:57PM: ^ Pending: ContainerCreating
4:36:58PM: ok: reconcile deployment/nginx (apps/v1) namespace: default
4:36:58PM: ---- applying complete [2/2 done] ----
4:36:58PM: ---- waiting complete [2/2 done] ----
Succeeded
$ kubectl get all,cm -l app=nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-5667ccd54b-ll67q 1/1 Running 0 5m9s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx-lb LoadBalancer 10.43.154.165 192.168.1.131 80:31150/TCP 8m59s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 8m59s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-5667ccd54b 1 1 1 5m9s
replicaset.apps/nginx-859ffcd8c 0 0 0 8m59s
NAME DATA AGE
configmap/nginx-conf-ver-1 1 8m59s
configmap/nginx-conf-ver-2 1 5m9s
$ curl 192.168.1.131
【略】
$ k exec nginx-5667ccd54b-ll67q -- cat /mnt/access.log
time: 2022-12-24T12:57:09+00:00
$ k describe deploy nginx
Name: nginx
Namespace: default
【略】
Volumes:
nginx-conf:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: nginx-conf-ver-2
Optional: false
あー、これは、地味に便利なやつや。
特に、GitOps で kapp (というか、むしろ kapp-controller) を使う場合は、是非とも忘れずに設定しておきたい annotation ということになります。
Carvel と BOSH の浅からぬ関係
Carvel プロジェクトに古くから参加している Dmitriy Kalinin 氏や John Ryan 氏は、実は Carvel 以前に Pivotal に所属して Cloud Foundry の Contributor として活躍をしてきた人たちであり、 Pivotal が VMware に買収された後も VMware Tanzu のプロダクト開発を続けています。他にも Nima Kaviani 氏など、Pivotal / VMware には所属してはいないが Cloud Foundry の Contributor をされていた方も、Carvel に深く関わっておられるようです。
Cloud Foundry BOSH ではちゃんとできてたリリースエンジニアリングが、Kubernetes に移った途端にデグレードするのは忍びないというのは無理からぬことだと思います。
長年にわたって積み上げてきた BOSH の崇高な設計理念は、今や Carvel に引き継がれて Kubernetes をとりまくエコシステムにおいても生き続けていくということなんですねえ。
最後に一つ、大事なこと
GitOps で kapp やら kapp-controller をお使いの方はまだ少数派で、多くの方は ArgoCD、Flux、Spinnaker などで GitOps を実現されているのだろうと思います。そんな方たちにもひとつだけ是非ともお勧めしたい。
YAMLを Git リポジトリに格納する際には、全てのコンテナイメージの参照を digest 記法 (index.docker.io/library/nginx@sha256:dd8a054d7ef030e94a6449783605d6c306c1f69c10c2fa06b66a030e0d1db793
の形式) に塗り替えておきましょう。