はじめに
実行中の deployment に後付けで volume をマウントしたくなりました。
- そもそも実行中 deployment に新規 volume アサインできるの?
- kubeclt patch コマンドで実現できるの?
等を解消していきたいと思います。
目的
実行中 deployment に volume を追加する。
手段
kubectl patch コマンドで行けると思いますが...
事前調査
まずは patch コマンドの仕様を理解したいと思います。
kubecctl patch コマンドとは?
リソースフィールドを更新するコマンドです。また、JSON/YAML形式を利用可能です。
patch コマンド実行時、対象リソースの追加 or リプレイス動作はリソースに依存して異なります。(patch strategy と呼ばれます)
patch の種類?タイプ?は三つ存在します。
- JSON Patch
- JSON merge patch
- Strategic merge patch
※ kubectl patch コマンドは type パラメータで patch タイプを指定可能
# kubectl patch <type>
各リソースの path stragegy は OpenApi specまたはAPI OVERVIEWから確認できます。
確認方法は、volumes
等のキーワード検索、またはどのリソース内で指定している要素なのかを確認すると良いです。例えば、containers 内の項目であれば contaisner の項目を確認すればよいです。
なお、pathStrategy キーが存在しないリソースはデフォルト動作(replace)となります。
公式ドキュメントに従ってサンプルの deployment を作成します。
cat <<EOF > deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: patch-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: patch-demo-ctr
image: nginx
tolerations:
- effect: NoSchedule
key: dedicated
value: test-team
EOF
次に patch 用の yaml を作成して適用します。
cat << EOF > patch-file.yaml
spec:
template:
spec:
containers:
- name: patch-demo-ctr-2
image: redis
EOF
# kubectl patch deployment patch-demo --patch-file patch-file.yaml
deployment.apps/patch-demo patched
Pod 内のコンテナイメージ数を確認します。
kubectl get deployment patch-demo --output yaml
--- snip ---
spec:
containers:
- image: redis <---- HERE
imagePullPolicy: Always
name: patch-demo-ctr-2
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
- image: nginx <---- HERE
imagePullPolicy: Always
name: patch-demo-ctr
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
--- snip ---
OpenApi から今回更新対象の volumeMounts
と volumes patch strategy
を確認します。
まず、volumeMounts
は merge なので追加される動作ですね。
"volumeMounts": { "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", "items": { "$ref": "#/definitions/io.k8s.api.core.v1.VolumeMount" }, "type": "array", "x-kubernetes-patch-merge-key": "mountPath", "x-kubernetes-patch-strategy": "merge" },
次に、volumes
は merge,retainKeys なので追加ですね。
retainKeys が何を指すのか理解できていませんが、追って確認予定です。
"volumes": {
"description": "List of volumes that can be mounted by containers belonging to the pod. More info: >https://kubernetes.io/docs/concepts/storage/volumes",
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.Volume"
},
"type": "array",
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge,retainKeys"
}
追加で patch 用の yaml を作成します。
cat << EOF > patch-file-tolerations.yaml
spec:
template:
spec:
tolerations:
- effect: NoSchedule
key: disktype
value: ssd
EOF
作成した patch 用の yaml を適用します。
# kubectl patch deployment patch-demo --patch-file patch-file-tolerations.yaml
deployment.apps/patch-demo patched
merge タイプの patch コマンド用 yaml を作成します。
cat << EOF > patch-file-2.yaml
spec:
template:
spec:
containers:
- name: patch-demo-ctr-3
image: gcr.io/google-samples/node-hello:1.0
EOF
作成した yaml を merge
タイプで適用します。
# kubectl patch deployment patch-demo --type merge --patch-file patch-file-2.yaml
deployment.apps/patch-demo patched
retainkeys を使用した patch コマンドを確認してきます。
cat << EOF > deployment-retainkeys.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: retainkeys-demo
spec:
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 30%
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: retainkeys-demo-ctr
image: nginx
EOF
# kubectl apply -f deployment-retainkeys.yaml
deployment.apps/retainkeys-demo created
cat << EOF > patch-file-no-retainkeys.yaml
spec:
strategy:
type: Recreate
EOF
"strategy": { "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStrategy", "description": "The deployment strategy to use to replace existing pods with new ones.", "x-kubernetes-patch-strategy": "retainKeys" },
strategy
は retainKeys なので、strategy
を更新する場合は retainKeys を使用する必要があります。
cat << EOF > patch-file-retainkeys.yaml
spec:
strategy:
$retainKeys:
- type
type: Recreate
EOF
# kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-retainkeys.yaml
deployment.apps/retainkeys-demo patched
また、kubectl patch コマンドは json 形式でも実行可能です。
cat << EOF > patch-file.json
{
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "patch-demo-ctr-2",
"image": "redis"
}
]
}
}
}
}
EOF
# kubectl patch deployment patch-demo --patch-file patch-file.yaml
deployment.apps/patch-demo patched
kubectl patch deployment patch-demo --patch-file patch-file.yaml
kubectl patch deployment patch-demo --patch 'spec:\n template:\n spec:\n containers:\n - name: patch-demo-ctr-2\n image: redis'
kubectl patch deployment patch-demo --patch-file patch-file.json
kubectl patch deployment patch-demo --patch '{"spec": {"template": {"spec": {"containers": [{"name": "patch-demo-ctr-2","image": "redis"}]}}}}'
〇 参考文献
ここまでの理解を整理すると以下の通りです。
-
strategic merge patch
は k8s が良い感じにマージしてくれるやつ -
JSON Patch
: 更新対象ではない部分も記載してあげないといけないやつ -
JSON Merge
: 更新対象のみの記載でいけるやつ
動作確認
概ね patch コマンド動作は理解できたので、目的である volume 追加を動作確認します。
作業手順は以下の通りです。
- テスト用 yaml ファイル作成
cat << EOF > patch-volume-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: patch-demo
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes:
- name: before-volume-pvc
persistentVolumeClaim:
claimName: before-volume
containers:
- name: patch-demo-ctr
image: nginx
volumeMounts:
- mountPath: "/tmp/"
name: before-volume-pvc
tolerations:
- effect: NoSchedule
key: dedicated
value: test-team
EOF
- nfs volume 作成
# mkdir -p before-volume
# touch before-volume/before-volume.txt
# vi /etc/exports
# exportfs -ra
- persistentvolume 作成
cat << EOF > patch-persistentvolume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: before-volume
labels:
volume: before-volume
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
nfs:
server: x.x.x.x
path: /tmp/work/patch/before-volume
EOF
# kubectl apply -f patch-persistentvolume.yaml
persistentvolume/before-volume created
# kubectl get persistentvolume |grep before
before-volume 1Gi RWX Retain Available 16s
- persistentvolumeclaim 作成
cat << EOF > patch-persistentvolumeclaim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: before-volume
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
selector:
matchLabels:
volume: before-volume
EOF
# kubectl apply -f patch-persistentvolumeclaim.yaml
persistentvolumeclaim/before-volume created
# kubectl get persistentvolumeclaim |grep before
before-volume Bound before-volume 1Gi RWX 11s
- テスト用 yaml 適用
# kubectl apply -f patch-volume-test.yaml
deployment.apps/patch-demo configured
# k exec -it patch-demo-79b9fbb79c-j8nq5 -- cat /tmp/before-volume.txt
update 12:30
- 追加用 volume 作成
# mkdir -p after-volume
# touch after-volume/after-volume.txt
# vi /etc/exports
# exportfs -ra
cat << EOF > patch-add-persistentvolume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: after-volume
labels:
volume: after-volume
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
nfs:
server: x.x.x.x
path: /tmp/work/patch/before-volume
EOF
# kubectl apply -f patch-add-persistentvolume.yaml
persistentvolume/after-volume created
cat << EOF > patch-add-persistentvolumeclaim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: after-volume
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
selector:
matchLabels:
volume: after-volume
EOF
# kubectl apply -f patch-add-persistentvolumeclaim.yaml
persistentvolumeclaim/after-volume created
- patch 用 yaml 作成
cat << EOF > patch-add-volume.yaml
spec:
template:
spec:
volumes:
- name: after-volume-pvc
persistentVolumeClaim:
claimName: after-volume
containers:
volumeMounts:
- mountPath: "/opt/"
name: after-volume-pvc
EOF
- patch コマンド実行
# k patch deployment patch-demo --patch-file patch-add-volume.yaml
The request is invalid: patch: Invalid value: "map[spec:map[template:map[spec:map[containers:map[volumeMounts:[map[mountPath:/opt/ name:after-volume-pvc]]] volumes:[map[name:after-volume-pvc persistentVolumeClaim:map[claimName:after-volume]]]]]]]": cannot restore slice from map
エラーですね...
切り分けとして、更新箇所を volumes のみに変更して実施。
spec:
template:
spec:
volumes:
- name: after-volume-pvc
persistentVolumeClaim:
claimName: after-volume
# k patch deployment patch-demo --patch-file patch-add-volume.yaml
deployment.apps/patch-demo patched
volumes 追加は問題ありません。
続いて、更新対象を volumeMounts のみに変更して実施します。
]# cat patch-add-volume.yaml
spec:
template:
spec:
containers:
volumeMounts:
- mountPath: "/opt/"
name: after-volume-pvc
# k patch deployment patch-demo --patch-file patch-add-volume.yaml
The request is invalid: patch: Invalid value: "map[spec:map[template:map[spec:map[containers:map[volumeMounts:[map[mountPath:/opt/ name:after-volume-pvc]]]]]]]": cannot restore slice from map
むむむ、各リソースで patch strategy で異なるのでしょうか?
"volumes": { "description": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", "items": { "$ref": "#/definitions/io.k8s.api.core.v1.Volume" }, "type": "array", "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }
"volumeMounts": { "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", "items": { "$ref": "#/definitions/io.k8s.api.core.v1.VolumeMount" }, "type": "array", "x-kubernetes-patch-merge-key": "mountPath", "x-kubernetes-patch-strategy": "merge" },
Cannot be updated と言っています。これはシンプルに patch コマンド使えないやつなんでしょうか。。。
おさらい
- openapi や k8s api reference で更新可否やpatch strategyを確認
- 目的と手段が合えば patch 用の yaml を作成&適用
終わりに
リソース更新は kubectl patch コマンドだけではなく、kubectl replace 等のコマンドも存在します。その他コマンドとの棲み分けは追って理解したいと思います。
volumeMounts は patch コマンドで更新できないようですので、yaml 修正して Pod を再作成したいと思います。
patch コマンドで volumeMounts を更新する方法を知っているつよつよの方がいましたら教えてください。。。