TECHSTEP

ITインフラ関連の記事を公開してます。

【メモ】CronJob経由で定期的にKubernetesリソースをデプロイする

はじめに

今回はKubernetesCronJobリソースでkubectlコマンドを実行し、定期的にリソースを更新する方法について、メモを残しておきます。

やりたいことは以下の通りです。

今回はgit kubectlそれぞれのコマンドを実行することのできるコンテナイメージを利用し、2つのコンテナ間でEmptyDir Volumeを共有することで、マニフェストファイルのデプロイを実行しました。

CronJobの利用方法

CronJobからkubectlコマンドを実行するには、CronJobkubectlコマンドを実行できるよう権限を付与する必要があります。そのため、まずはService AccountとRole/Rolebindingを作成します。今回はDeploymentリソースの作成を目的としていたため、以下のようなRoleを付与しました。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: internal-kubectl
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: modify-deployments
rules:
  - apiGroups: ["apps"]
    resources:
      - deployments
    verbs:
      - get
      - create
      - update
      - patch
      - list
      - delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: modify-pods-to-sa
subjects:
  - kind: ServiceAccount
    name: internal-kubectl
roleRef:
  kind: Role
  name: modify-deployments
  apiGroup: rbac.authorization.k8s.io

また、利用するGitHubリポジトリがプライベートリポジトリの場合は、以下のようなSecretリソースをあらかじめ作成しておきます。

apiVersion: v1
kind: Secret
metadata:
  name: forgithubaccess
type: Opaque
data:
  username: <base64-GItHub account>
  password: <base64-GitHub access token>

これでCronJobを利用する準備ができたので、次にCronJobを作成します。今回は、以下のような定義ファイルを利用しました。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-git-kubectl
spec:
  schedule: "*/1 * * * *"  # 毎分実行する
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: internal-kubectl  # 事前に作成したService Accountを指定
          containers:
          - name: git
            image: alpine/git:latest
            env:
              - name: SECRET_USERNAME
                valueFrom:
                  secretKeyRef:
                    name: forgithubaccess
                    key: username
              - name: SECRET_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: forgithubaccess
                    key: password
            command: ["git", "clone"]
            args: ["https://$(SECRET_USERNAME):$(SECRET_PASSWORD)@github.com/<Account>/<Repository>.git", "/cache"]
            volumeMounts:
            - mountPath: /cache
              name: git-volume
          - name: kubectl
            image: bitnami/kubectl
            command: ["/bin/sh", "-c"]
            args: 
            - |
              sleep 10;  # 上のgit cloneが終了するまで待機
              kubectl apply -f /cache/sample-app.yaml  # GitHubリポジトリのディレクトリ・ファイル構成に応じて変更
            volumeMounts:
            - mountPath: /cache
              name: git-volume
          volumes:
          - name: git-volume
            emptyDir: {}
          restartPolicy: Never  
      backoffLimit: 4

実行内容としては、①対象の定義ファイルを格納したGitリポジトリからファイルを取得する、②取得したファイルを指定してkubectl applyコマンドを実行する、という流れになります。①の処理が完了する前に②を実行すると、「指定したファイルが存在しない」というエラーが発生するため、sleepで一定時間待機してからkubectlコマンドを実行するようにしています。

CronJob中で2つのコンテナを実行し、取得したリポジトリのデータを共有できるようemptyDirリソースを利用しています。

CronJobのスケジューリングはひとまず1分ごとに実行するよう設定し、1分ごとに①②の操作を繰り返すことになります。

これを利用してCronJobを作成します。

$ kubectl apply -f cronjob-git-kubectl.yml
cronjob.batch/cronjob-git-kubectl created


$ kubectl get cronjob
NAME                  SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob-git-kubectl   */1 * * * *   False     0        <none>          6s

作成後にPodの状態を確認すると、1分ごとにJobが起動し、動作している様子が確認できます。

# CronJobの様子
# “sample-app”がkubectlコマンドで作成したPod

$ kubectl get pods -w
NAME                                          READY   STATUS    RESTARTS   AGE
cronjob-git-kubectl-1607865300-ff4q6          0/2     Pending   0          0s
cronjob-git-kubectl-1607865300-ff4q6          0/2     Pending   0          0s
cronjob-git-kubectl-1607865300-ff4q6          0/2     ContainerCreating   0          0s

# (1回目のJob実行)

cronjob-git-kubectl-1607865300-ff4q6          1/2     Running             0          13s
sample-app-75ff8897f5-rgmgt                   0/1     Pending             0          0s
sample-app-75ff8897f5-rgmgt                   0/1     Pending             0          0s
sample-app-75ff8897f5-rgmgt                   0/1     ContainerCreating   0          0s
cronjob-git-kubectl-1607865300-ff4q6          0/2     Completed           0          23s
sample-app-75ff8897f5-rgmgt                   1/1     Running             0          9s


# (2回目以降のJob実行)

cronjob-git-kubectl-1607865360-9h7ds          0/2     Pending             0          0s
cronjob-git-kubectl-1607865360-9h7ds          0/2     Pending             0          0s
cronjob-git-kubectl-1607865360-9h7ds          0/2     ContainerCreating   0          0s
cronjob-git-kubectl-1607865360-9h7ds          1/2     Running             0          7s
cronjob-git-kubectl-1607865360-9h7ds          0/2     Completed           0          17s
cronjob-git-kubectl-1607865420-6ncqj          0/2     Pending             0          0s
cronjob-git-kubectl-1607865420-6ncqj          0/2     Pending             0          0s
cronjob-git-kubectl-1607865420-6ncqj          0/2     ContainerCreating   0          1s
cronjob-git-kubectl-1607865420-6ncqj          1/2     Running             0          8s
cronjob-git-kubectl-1607865420-6ncqj          0/2     Completed           0          18s


# 実行結果

$  kubectl get pods
NAME                                          READY   STATUS      RESTARTS   AGE
cronjob-git-kubectl-1607865300-ff4q6          0/2     Completed   0          2m26s
cronjob-git-kubectl-1607865360-9h7ds          0/2     Completed   0          86s
cronjob-git-kubectl-1607865420-6ncqj          0/2     Completed   0          26s
sample-app-75ff8897f5-rgmgt                   1/1     Running     0          2m4s


# 1回目のJobのログ
$ kubectl logs cronjob-git-kubectl-1607865300-ff4q6 -c git
Cloning into '/cache'...

$ kubectl logs cronjob-git-kubectl-1607865300-ff4q6 -c kubectl
deployment.apps/sample-app created


# 2回目以降のJobのログ
$ kubectl logs cronjob-git-kubectl-1607865420-6ncqj -c git
Cloning into '/cache'...

$ kubectl logs cronjob-git-kubectl-1607865420-6ncqj -c kubectl
deployment.apps/sample-app unchanged

参考ドキュメント