KubernetesにおけるPodの初期化処理
2018-12-04
Qiitakubernetesこれはなに?
本記事では Kubernetes における Pod という形でアプリケーションを起動する際、どのように初期化処理を実行できるのかについて取り上げる。 なお終了処理は触れない。
lifecycle.postStart を使う
spec.containers.lifecycle.postStart
を利用すればライフサイクルにおける"起動"直後に何かしら処理を差し込むことが可能。
これは Pod の起動、ENTRYPOINT や CMD と同時(非同期)に実行される。
ドキュメント: Attach Handlers to Container Lifecycle Events - Kubernetes
postStart で初期化処理をする deployment の yaml 例を書いてみる。 サンプルのファイルはGithubにおいてある。
apiVersion: apps/v1
kind: Deployment
metadata:
name: python
labels:
name: python
spec:
replicas: 1
selector:
matchLabels:
app: python
template:
metadata:
labels:
app: python
spec:
containers:
- name: python
image: python:3.7.1
tty: true
lifecycle:
postStart:
exec:
command:
- sh
- -c
- "mkdir -p /var/log/backup/${HOSTNAME} \
&& mv /var/log/python.log /var/log/backup/${HOSTNAME} \
&& echo START! >> /var/log/python.log"
volumeMounts:
- name: log
mountPath: /var/log
command:
- sh
- -c
- "python -m http.server 8080 --directory /var/log/ 1>/var/log/python.log 2>/var/log/python.log"
volumeMounts:
- name: log
mountPath: /var/log
volumes:
- name: log
hostPath:
path: /var/log/python
type: DirectoryOrCreate
Pod そのものは Python で Web サーバーを立ち上げるだけだが、postStart においてログファイルをバックアップとっている。 kubernetes のサンプルとしてこれが正しいかどうかはさておき...。
apply して様子を見てみる。
$ kubectl apply -f post_start.yaml
...
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
python-7d57c47cb9-d7zxr 1/1 Running 0 1m
$ kubectl exec -it python-7d57c47cb9-d7zxr -- ls -lh /var/log/backup
total 4.0K
drwxr-xr-x 2 root root 4.0K Dec 1 13:08 python-7d57c47cb9-d7zxr
kubectl exec
してls
した結果、バックアップ処理が正常に動いているのがわかる。
postStart の問題点
ドキュメントによると以下のような事が書いてある。
Kubernetes sends the postStart event immediately after the Container is created. There is no guarantee, however, that the postStart handler is called before the Container’s entrypoint is called.
つまり、コンテナ(Pod)の entrypoint より先に実行される保証がないということ。 今回の例で考えて見ると、アプリケーションが起動する前に以前のログをバックアップとっておきたいが、postStart の実行タイミングによってはアプリケーション起動後に実行される可能性があるということ。 そこで欲しくなってくるのは ENTRYPOINT より前に実行されることが保証された初期化処理の設定。 また、postStart で使用したいソフトウェアは Pod の実行に本来不要なソフトウェアが必要になる可能性もある。
初期化処理を別コンテナで
postStart の欠点を補うことが出来るのが、Init Containers というもの。 Pod の初期化処理を実行するコンテナを実行することが出来る。 ドキュメント:Init Containers - Kubernetes
Pod 自体の機能としていくつかのコンテナをまとめて動かすことが出来るが、コンテナ群の初期化順は決まっておらず、さらに非同期に初期化されるため、初期化処理をするコンテナを Pod そのものに組み込んでも期待するような初期化を保証することが出来ない。 さらに何かしらの初期化処理だけで必要なソフトウェアがあるなどする場合に、そのためだけに本来不要なコマンドを Docker イメージに追加しておくなどが必要になるケースもある。 たとえば設定ファイルを外部から読み込んだり、Git リポジトリを clone してきたりしたいようなケース。 具体的に後者だと Web アプリを動かすのに Git コマンドは必要ないが、初期化のために必要になってしまう。
そういった時に Init Containers が使える。
Init Containers を使った初期化を行ってみる。 こちらのサンプル用ファイルもGithubにあげてある。
apiVersion: apps/v1
kind: Deployment
metadata:
name: python
labels:
name: python
spec:
replicas: 1
selector:
matchLabels:
app: python
template:
metadata:
labels:
app: python
spec:
initContainers:
- name: git
image: alpine/git
command:
- git
- clone
- https://github.com/petitviolet/kubernetes_prac.git
- /tmp/git/
volumeMounts:
- name: storage
mountPath: /tmp/git/
containers:
- name: python
image: python:3.7.1
tty: true
command:
- sh
- -c
- "python -m http.server 8080 --directory /www/public"
volumeMounts:
- name: storage
mountPath: /www/public
volumes:
- name: storage
emptyDir: {}
先ほどとは少し違って、git clone
をしている。
必要なファイルを git リポジトリから取得してくる、というような初期化処理。
起動してみると、正しく動作していることが確認できる。
$ kubectl apply -f ./init_container.yaml
kudeployment "python" created
service "python-service" created
$
$ kubectl get po -w
NAME READY STATUS RESTARTS AGE
python-86cc996576-glntt 0/1 Pending 0 0s
python-86cc996576-glntt 0/1 Init:0/1 0 0s
python-86cc996576-glntt 0/1 PodInitializing 0 17s
python-86cc996576-glntt 1/1 Running 0 18s
^C%
$
$ curl $(minikube service python-service --url)/message.txt
Hello, initContainer!
最後に curl でとってきているのはGithub に置いた message.txt。
ちゃんと Github からgit clone
出来ていることがわかる。
また、kubectl get po -w
の様子から、Pod の STATUS が Pending→Init→PodInitializing→Running と遷移しているのがわかる。
ここで注目したいのが Init という STATUS があること。
lifecycle.postStart
と異なり、初期化用のステータスがあり、それが終わらないと本体の Pod の起動に遷移しないことが保証できている。
ちなみにInit:0/1
となっているのは Init Containers が 1 個あってそのうち 0 個が完了していることを示す。
なので Init Containers を複数個用意すれば分母は変わる。
また、Init に使われたコンテナのログや詳細なステータスkubectl
コマンド経由で取得することは出来る。
$ kubectl logs python-86cc996576-glntt -c git
Cloning into '/tmp/git'...
$ kubectl get pod python-86cc996576-glntt -ojson | jq '.status.initContainerStatuses'
[
{
"containerID": "docker://3685acb1a79188c840d6723f2b29b4aaaa876f561ab832732c455512cd2d9ff1",
"image": "alpine/git:latest",
"imageID": "docker-pullable://alpine/git@sha256:d76d5ab84de3a35514f8621df4550c59680c3bb623e8c1332ed7af39e33afb0b",
"lastState": {},
"name": "git",
"ready": true,
"restartCount": 0,
"state": {
"terminated": {
"containerID": "docker://3685acb1a79188c840d6723f2b29b4aaaa876f561ab832732c455512cd2d9ff1",
"exitCode": 0,
"finishedAt": "2018-12-02T02:13:50Z",
"reason": "Completed",
"startedAt": "2018-12-02T02:13:49Z"
}
}
}
]
使いどころとしては
- 確実に初期化処理を本体の Pod 起動前に完了することを保証したい場合
- 本体の Pod に持たせたくないソフトウェアが初期化に必要な場合
というあたり。
まとめ
Pod の初期化処理に使えるのは 2 つ。
spec.containers.lifecycle.postStart
- Pod の起動と同時に実行される
- 実行は Pod の各コンテナ内
- Init Containers
- Pod の本体のコンテナ起動前の Pending フェーズで実行される
- 実行は Pod とは別のコンテナ
それぞれキックされるタイミング等が異なるので用途に応じて使い分けることが大事。
from: https://qiita.com/petitviolet/items/41aa9abe106a29ba4667