Kubernetesでは、Deployment等のPod templateに対しイメージタグにてlatestを指定することは推奨されておりません
そのため、sha256によるコンテナイメージのダイジェスト値指定か、バージョンタグを用いた指定を行う必要があります。
Pod templateにバージョンないしはdigest値を書くということは手動でアップデートの管理を行うということです。別に大した手間ではないですしそれでも良いのですが、せっかく(何が??)ですので自動更新をさせてみたいものです。
但し、X.Y.ZバージョンのうちY部分の更新は何らかの管理者によるマイグレーション操作が発生する可能性を鑑みて、Z版のみ更新できるようにしてみました。(Z版で手動マイグレが必要にならない根拠は、無し。)
本来であれば以前記載した、「Deploymentでのアプリケーション単一性維持が保証できない問題」も含めて解決できる、何らかのOperatorを実装するほうがスマートです。しかし、当たり前ですがそういった実装には手間がかかりますので今回はKubernetesのCronJobでお茶を濁します。
今回の手法を実現するにあたり、JobのPodからKubernetesのDeploymentを書き換える方法を考える必要があります。
kubectl等の汎用クライアントを動かせるようにしてもよいのですが、このコマンドはフットプリントが結構大きく、また、kubeconfigファイルなどを用意する必要がある、応答を基に処理をしようとすると別途jsonの解釈が必要になる、信頼できそうな既製イメージでjqが入っているものが無さそうである、など、少々都合が悪いです。
そこで、今回はcurlコマンドとjqコマンドのみを用いてkubernetes APIを叩き、DeploymentのpodTemplateへ設定されているイメージのバージョン取得、及びその書き換えを行ってみます。
下準備として、PodからkubeAPIを叩くためにServiceAccountやRole等を作成します。以下に例を示します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: updater
namespace: hollo-1
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: get-patch-deploy
namespace: hollo-1
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: updater
namespace: hollo-1
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: get-patch-deploy
subjects:
- apiGroup: ""
kind: ServiceAccount
name: updater
namespace: hollo-1
次に、Pod内部で実行するスクリプトを用意します。(力技。。。)curlでKubernetes APIを操作する方法については以下のサイト等を参考にしました。
https://qiita.com/iaoiui/items/36e86d173e451a7b18be
patch周り→わすれた…
MastodonへのToot : https://gist.github.com/YuzuRyo61/6388e6e8bde15712348e95714c82b0e1
#!/bin/bash -e
DEPLOYNAME="hollo-app"
TOKEN=$(curl -s "https://ghcr.io/token?service=ghcr.io&scope=repository:dahlia/hollo:pull" | jq -r ".token")
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
SATOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)
API="https://${KUBERNETES_PORT_443_TCP_ADDR}:${KUBERNETES_PORT_443_TCP_PORT}/apis"
function getcurl(){
curl -s --cacert /run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Accept: application/json" -H "Authorization: Bearer ${SATOKEN}" "${1}"
}
function patchcurl(){
curl -s --cacert /run/secrets/kubernetes.io/serviceaccount/ca.crt -X PATCH -H "Accept: application/json" -H "Content-Type: application/json-patch+json" -H "Authorization: Bearer ${SATOKEN}" "${1}" -d "${2}"
}
REPOVER=$(curl -s -H "Accept: application/vnd.oci.image.manifest.v1+json" -H "Authorization: Bearer ${TOKEN}" https://ghcr.io/v2/dahlia/hollo/manifests/$(curl -s -H "Accept: application/vnd.oci.image.index.v1+json" -H "Authorization: Bearer ${TOKEN}" https://ghcr.io/v2/dahlia/hollo/manifests/latest | jq -r ".manifests[]|select(.platform.architecture==\"amd64\").digest") | jq -r ".annotations[\"org.opencontainers.image.version\"]")
RUNVER=$(getcurl ${API}/apps/v1/namespaces/${NAMESPACE}/deployments/${DEPLOYNAME} | jq -r ".spec.template.spec.containers[].image" | cut -d":" -f2)
echo REPOVER: ${REPOVER}
echo RUNVER: ${RUNVER}
ACCESS_TOKEN="<Another mastodon account Token>"
INSTANCE_ADDRESS="<Another mastodon server>"
curl https://$INSTANCE_ADDRESS/ >/dev/null 2>&1
case $? in
0)
;;
6)
echo "[ERROR] Can't resolve " $INSTANCE_ADDRESS ". Are you connected to the network or are you using the wrong address?"
exit 1
;;
127)
echo "[ERROR] curl package is not found. please install curl package."
exit 1
;;
*)
echo "[ERROR] An unknown error occurred."
exit 1
;;
esac
PATCH='[{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"ghcr.io/dahlia/hollo:'${REPOVER}'"}]'
echo "${PATCH}"
if [[ $(echo ${REPOVER}|cut -d"." -f 1) -eq $(echo ${RUNVER}|cut -d"." -f 1) ]];then
# X version valid
if [[ $(echo ${REPOVER}|cut -d"." -f 2) -eq $(echo ${RUNVER}|cut -d"." -f 2) ]];then
# Y version valid
if [[ $(echo ${REPOVER}|cut -d"." -f 3) -ne $(echo ${RUNVER}|cut -d"." -f 3) ]];then
# Z version different
echo "do upgrade"
curl -X POST -d "status=Hollo autoUpdater / Upgrade start ${RUNVER} to ${REPOVER}" -d "visibility=private" --header "Authorization: Bearer $ACCESS_TOKEN" -sS https://$INSTANCE_ADDRESS/api/v1/statuses > /dev/null 2>&1
patchcurl ${API}/apps/v1/namespaces/${NAMESPACE}/deployments/${DEPLOYNAME} "${PATCH}"
else
echo "do nothing"
curl -X POST -d "status=Hollo autoUpdater / do nothing RunVer:${RUNVER}" -d "visibility=private" --header "Authorization: Bearer $ACCESS_TOKEN" -sS https://$INSTANCE_ADDRESS/api/v1/statuses > /dev/null 2>&1
fi
else
echo "Y version different"
curl -X POST -d "status=Hollo autoUpdater / Y version different RunVer:${RUNVER} / Latest:${REPOVER}" -d "visibility=private" --header "Authorization: Bearer $ACCESS_TOKEN" -sS https://$INSTANCE_ADDRESS/api/v1/statuses > /dev/null 2>&1
exit 0
fi
else
echo "X version different"
curl -X POST -d "status=Hollo autoUpdater / X version different RunVer:${RUNVER} / Latest:${REPOVER}" -d "visibility=private" --header "Authorization: Bearer $ACCESS_TOKEN" -sS https://$INSTANCE_ADDRESS/api/v1/statuses > /dev/null 2>&1
exit 0
fi
上記スクリプトを含めたイメージを作成するのは手間ですので、ConfigMapかSecretあたりを使い無理やりJobで起動されるContainerにファイルとしてマウントします。今回は面倒なのでConfigMapを使用しました。botアカウントに喋らせるためのTokenが含まれるため、気をつけて取り扱う必要があります。他の人も使うような環境などでは絶対にやらないようにしましょうね。(botいらんよって場合は該当部分消せばいい感じになります。)
kubectl create cm -n hollo-1 vercheck-shell --from-file=./vercheck.sh --dry-run=client --save-config -o yaml | kubectl apply -f -
また、Jobについて上記で作成したServiceAccountを使用するようにします。今回、bash、curl、jqが既にインストール済みのイメージとしてnetshootを使用しました。上記コマンドが入っていれば別のイメージでも可です。(とにかく、イメージを自前でビルドしたくない。。。)ConfigMapのマウントも忘れずに行います。
apiVersion: batch/v1
kind: CronJob
metadata:
name: z-release-autoupdater
namespace: hollo-1
spec:
schedule: <Cronのスケジュール> 例:"16 1,13 * * *"
jobTemplate:
spec:
template:
spec:
serviceAccountName: updater
volumes:
- name: vercheck-shell
configMap:
name: vercheck-shell
items:
- key: vercheck.sh
path: vercheck.sh
mode: 0777
containers:
- name: updater
image: ghcr.io/nicolaka/netshoot:v0.13
imagePullPolicy: IfNotPresent
volumeMounts:
- name: vercheck-shell
mountPath: /app
command:
- bash
- /app/vercheck.sh
restartPolicy: Never
それではテストしてみましょう。CronJobからJobを手動でcreateしてみます。
kubectl create job -n hollo-1 --from=cronjob/z-release-autoupdater test
以下のようにPodが生成され、無事に動作しました。(^o^)v
user@k-cp-1:~/hollo-1$ k get pod -n hollo-1 | grep test
test-mshz4 0/1 Completed 0 3m
色々ガバいですが、一応うごきましたということで。。。
※免責:上記記事が原因で問題が発生しても一切保証しません。よろしくお願いします。