Featured image of post KubernetesのJobを試してみる

KubernetesのJobを試してみる

YAML書いてみる

なんだかんだで一度もリソースを手で書いたことが無いので, 自分でつくってみる.


やったことのまとめ

  • 任意のコマンドが実行できるDocker imageを作成してKubernetesJobを実行してみた

Jobとは

  • 使い切りのPodを実行し, 設定した回数ぶん終了したら完了となるリソース
    • Podが処理を行った後停止することが前提
    • Podを監視し, 正常終了の回数が設定値を満たすまで再起動等の処理を行う
    • 一回の処理を確実に行いたい場合やバッチ的な処理に向いている

参考 : https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/

やったこと

Docker imageの作成

まずはDocker imageをつくる.
Dockerfileの書き方はだいたいわかっているつもり…

下記のDockerfileを適当なディレクトリで作成する.
特に何かの処理をするわけではなく, ただ alpineimageをベースにして適当な環境変数等を設定しただけ.
ENTRYPOINT/bin/sh を指定することで引数で任意のコマンドを渡せるようにする.

Dockerfile

# ベースイメージはalpine
FROM alpine:3.10.3

# 管理者の名前
MAINTAINER uzimihsr-01

# ディレクトリを変更
WORKDIR /uzimihsr/workdir

# 環境変数を設定
ENV AUTHOR uzimihsr
ENV NUMBER 777

# エントリーポイントはsh
ENTRYPOINT ["/bin/sh"]

# デフォルトでは環境変数AUTHORを表示する
CMD ["-c", "echo ${AUTHOR}"]

imageをビルドしてDocker Hubにアップロードする.

# Dockerfileがあるところで作業
$ cd path/to/Dockerfile
$ ls
Dockerfile

# uzimihsr(Docker HubのID)とrepository, tagを指定してビルド
$ docker image build -t uzimihsr/uzimihsr-sample-image:0.1 .
Sending build context to Docker daemon  58.37kB
Step 1/7 : FROM alpine:3.10.3
 ---> 965ea09ff2eb
Step 2/7 : MAINTAINER uzimihsr-01
 ---> Using cache
 ---> be67ea5322ce
Step 3/7 : WORKDIR /uzimihsr/workdir
 ---> Using cache
 ---> d953d022ec9e
Step 4/7 : ENV AUTHOR uzimihsr
 ---> Using cache
 ---> f2e3624824fd
Step 5/7 : ENV NUMBER 777
 ---> Using cache
 ---> c5ccb27bba82
Step 6/7 : ENTRYPOINT ["/bin/sh"]
 ---> Using cache
 ---> 42d33fa1c9ab
Step 7/7 : CMD ["-c", "echo ${AUTHOR}"]
 ---> Using cache
 ---> ec0ff7287ea6
Successfully built ec0ff7287ea6
Successfully tagged uzimihsr/uzimihsr-sample-image:0.1

# imageをDocker Hubにpush
$ docker image push uzimihsr/uzimihsr-sample-image:0.1
The push refers to repository [docker.io/uzimihsr/uzimihsr-sample-image]
47cb607f05a0: Pushed
77cae8ab23bf: Mounted from library/alpine
0.1: digest: sha256:eab7f925c7b33c5604196d30a9daa3e2d9c763166543069b8f31676a74003702 size: 735

https://hub.docker.com/r/uzimihsr/uzimihsr-sample-image

試しにローカルにある uzimihsr/uzimihsr-sample-image:0.1 を削除した状態でコンテナを実行してみる.

# Docker Hubにあるimageを指定してコンテナを実行
# Dockerfileで指定した通り$AUTHORが表示される
$ docker container run uzimihsr/uzimihsr-sample-image:0.1
Unable to find image 'uzimihsr/uzimihsr-sample-image:0.1' locally
0.1: Pulling from uzimihsr/uzimihsr-sample-image
Digest: sha256:eab7f925c7b33c5604196d30a9daa3e2d9c763166543069b8f31676a74003702
Status: Downloaded newer image for uzimihsr/uzimihsr-sample-image:0.1
uzimihsr

動作確認はOK.

マニフェストファイルの作成

いよいよYAMLを書いていく.
基本的にはapiVersion, kind, metadata, specの項目を埋めていけばいいはず.

job.yaml

apiVersion: batch/v1 # APIのバージョン
kind: Job # リソースの種類
metadata: # メタ情報
  name: uzimihsr-echo-number # リソース名
spec: # Jobの定義
  template: # Pod情報
    spec: # Jobで作成されるPodの定義
      containers: # Podに含まれるコンテナ
      - name: echo-number # コンテナ名
        image: uzimihsr/uzimihsr-sample-image:0.1 # 使用するimage
        args: ["-c", "echo ${NUMBER}"] # imageのCMDを上書きする
      restartPolicy: Never # Podが失敗した場合の挙動
  backoffLimit: 1 # 再トライ回数の制限

ちょっとコメントを多めに入れたのでごちゃごちゃしているが, やっていることは非常にシンプル.
specにはJobで実行されるPod内のコンテナとその設定を書いている.
今回は先程作ったimageの実行時引数をargsで指定するとDockerfileで指定したCMDを上書きしてくれるので,
imageが持つ環境変数 NUMBER を表示するように指定している.
JobではPod失敗時の挙動を指定するrestartPolicyNever(新しいPodを作成)かOnFailure(NodePodを残したままコンテナを再実行)が指定できるが, Neverにしておくのが無難そう.

Jobの実行

いよいよマニフェストを実行する.
kubectlでリソースを作成して, その後の挙動を確認する.

# リソースの作成
$ kubectl apply -f job.yaml
job.batch/uzimihsr-echo-number created

# Jobの一覧を確認
$ kubectl get jobs
NAME                   COMPLETIONS   DURATION   AGE
uzimihsr-echo-number   1/1           5s         5s

# Podの一覧を確認
# JobによってPodが起動している
$ kubectl get pods
NAME                         READY     STATUS      RESTARTS   AGE
uzimihsr-echo-number-vpbg7   0/1       Completed   0          14s

# Podのログを確認
# imageの元々の挙動ではなくJobで定義した処理が実行されている
$ kubectl logs uzimihsr-echo-number-vpbg7
777

# Podが作成された際のeventを確認
$ kubectl get events
LAST SEEN   TYPE      REASON             OBJECT                           MESSAGE
<unknown>   Normal    Scheduled          pod/uzimihsr-echo-number-vpbg7   Successfully assigned default/uzimihsr-echo-number-vpbg7 to minikube
3m          Normal    Pulled             pod/uzimihsr-echo-number-vpbg7   Container image "uzimihsr/uzimihsr-sample-image:0.1" already present on machine
3m          Normal    Created            pod/uzimihsr-echo-number-vpbg7   Created container echo-number
3m          Normal    Started            pod/uzimihsr-echo-number-vpbg7   Started container echo-number
3m1s        Normal    SuccessfulCreate   job/uzimihsr-echo-number         Created pod: uzimihsr-echo-number-vpbg7

# Jobの情報を確認
# Job定義の他, Podのステータス監視情報などが表示される
$ kubectl describe job uzimihsr-echo-number
Name:           uzimihsr-echo-number
Namespace:      default
Selector:       controller-uid=77d09e29-33f9-4175-a8e4-7b903ee44a9c
Labels:         controller-uid=77d09e29-33f9-4175-a8e4-7b903ee44a9c
                job-name=uzimihsr-echo-number
Annotations:    kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"uzimihsr-echo-number","namespace":"default"},"spec":{"backoffLimit":1,"templ..."
Parallelism:    1
Completions:    1
Start Time:     Sun, 15 Dec 2019 20:29:29 +0900
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=77d09e29-33f9-4175-a8e4-7b903ee44a9c
           job-name=uzimihsr-echo-number
  Containers:
   echo-number:
    Image:      uzimihsr/uzimihsr-sample-image:0.1
    Port:       <none>
    Host Port:  <none>
    Args:
      -c
      echo ${NUMBER}
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  3m    job-controller  Created pod: uzimihsr-echo-number-vpbg7

# Podの情報を確認
$ kubectl describe pod uzimihsr-echo-number-vpbg7
Name:               uzimihsr-echo-number-vpbg7
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               minikube/192.168.99.110
Start Time:         Sun, 15 Dec 2019 20:29:29 +0900
Labels:             controller-uid=77d09e29-33f9-4175-a8e4-7b903ee44a9c
                    job-name=uzimihsr-echo-number
Annotations:        <none>
Status:             Succeeded
IP:                 172.17.0.6
Controlled By:      Job/uzimihsr-echo-number
Containers:
  echo-number:
    Container ID:  docker://d3cb3192f650cb916995ad25d533fb4f9edcd42a800cbf41bf2bbc0221ef204c
    Image:         uzimihsr/uzimihsr-sample-image:0.1
    Image ID:      docker-pullable://uzimihsr/uzimihsr-sample-image@sha256:eab7f925c7b33c5604196d30a9daa3e2d9c763166543069b8f31676a74003702
    Port:          <none>
    Host Port:     <none>
    Args:
      -c
      echo ${NUMBER}
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Sun, 15 Dec 2019 20:29:30 +0900
      Finished:     Sun, 15 Dec 2019 20:29:30 +0900
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-smq54 (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             False
  ContainersReady   False
  PodScheduled      True
Volumes:
  default-token-smq54:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-smq54
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age        From               Message
  ----    ------     ----       ----               -------
  Normal  Scheduled  <unknown>  default-scheduler  Successfully assigned default/uzimihsr-echo-number-vpbg7 to minikube
  Normal  Pulled     10m        kubelet, minikube  Container image "uzimihsr/uzimihsr-sample-image:0.1" already present on machine
  Normal  Created    10m        kubelet, minikube  Created container echo-number
  Normal  Started    10m        kubelet, minikube  Started container echo-number

できた.
eventの順番を見るとJobが作成された際の各コンポーネントの動きが推測できておもしろい.
多分以下の流れ.

  1. kubectlから指示を受けたkube-apiserverJobの情報をetcdに書き込み
  2. kube-controller-managerJobControllerが作成される
  3. JobControllerkube-apiserverに指示を出してPodの情報をetcdに書き込み
  4. kube-schedulerによってPodNodeに割り当てられる
  5. NodeDockerPodに使うimageを探してコンテナを作成+起動, これをkubeletが報告
  6. Podが作成されたことをJobControllerが検知

Podが完了したときにそれを示すeventが作成されないのはなんでだろう…
たぶん意図があるはずなのでそのうち調べたい.

おわり

Kubernetesのマニフェストを初めて手書きして実行までやってみた.
やっぱり先にコンポーネントの役割を理解しておいたほうがクラスタ全体の挙動がなんとなくわかって良い気がする.
(間違ってたらかなしい)

おまけ

ほっぺがかわいいそとちゃん