🏗️

Mastodon のコンテナイメージを作成する Kubernetes の Job を作った

2022/11/28に公開

概要

これまでは Docker Compose 環境を使っていたので、手元でコンテナイメージを作りたい時は docker build .すれば良かったのですが、今後、Kuberenets を使い倒していきたいことから、Kubernetes 環境の中でコンテナイメージを作れるようにしたいと考えて試行錯誤してみました。
コンテナイメージを作りたい Mastodon は公式リポジトリの中で Dockerfile を提供してくれていますので、これをそのまま使っていく方向で実装しています。

全体イメージ

出てくるコンポーネントの説明

  • Job : コンテナイメージのビルド処理だけをワンショットで実行する Pod を作成する
  • PVC / PV : 過去に作成したコンテナイメージなどをキャッシュして置くための永続ストレージ領域
  • ConfigMap : 具体的なビルド処理を記載したシェルスクリプト(ファイル)を持たせておく
  • Secret : Harbor(プライベートのコンテナレジストリ)に push するためのパスワードなどの機密情報

Namespace

今回作成する各種のリソースの入れ物となる Namespcae を定義しておきます。

namespcae.yml
kind: Namespace
apiVersion: v1
metadata:
  name: mastodon-image-builder
  labels:
    name: mastodon-image-builder

Job

実行する Pod (Container) の枠組みを定義しています。

この Pod は 2つのコンテナーを同居させているのが工夫ポイントです。

  1. GitHub リポジトリから Mastodon をgit cloneしてきて、docker buildコマンドを投げ込むための Ubuntu コンテナ
  2. Ubuntu コンテナからの要求に応じて Docker サービスを実行する(Docker in Kubernetes)ための DIND コンテナ ※ DIND = Docker in Docker
job.yml
apiVersion: batch/v1
kind: Job
metadata:
  name: image-builder
  namespace: mastodon-image-builder
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
      - image: docker:git
        name: ubuntu
        command: ['sh', '/data/build.sh'] # ConfigMap のシェルをキックする
        env:
        - name: DOCKER_HOST
          value: tcp://localhost:2375
        envFrom:
        - secretRef:
            name: secret
        volumeMounts: # ConfigMap のシェルスクリプトをマウントしている
        - name: build-sh
          mountPath: /data
      - name: dind-daemon
        image: docker:dind
        env:
        - name: DOCKER_TLS_CERTDIR
          value: ""
        volumeMounts:
        - name: dind-var-lib-docker
          mountPath: /var/lib/docker
        securityContext:
          privileged: true
      volumes:
      - name: dind-var-lib-docker
        persistentVolumeClaim:
          claimName: docker-cache
      - name: build-sh
        configMap:
          name: build.sh

ConfigMap

ビルド処理を行う Pod にマウントされるシェルスクリプトを保持しています。
Job 定義と分けて個別に持たせることで、シェルスクリプトのメンテナンスがちょっとだけ楽になります。

shell.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: build.sh
  namespace: mastodon-image-builder
data:
  build.sh: |
    set -e
  
    # Mastodon 公式リポジトリをクローンしてくる
    git clone https://github.com/mastodon/mastodon.git
    cd mastodon

    # DinDが起動してくるまでちょっと待つ
    sleep 10

    # BuildKit を有効化して docker build する
    BUILD_DATE=$(date -u +"%Y%m%d-%H%M")
    VCS_REF=$(git rev-parse --short HEAD)
    export DOCKER_BUILDKIT=1
    docker build --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") --build-arg VCS_REF=${VCS_REF} -t ${IMAGE_NAME} -t ${IMAGE_REGISTRY}/${PROJECT_NAME}/${IMAGE_NAME}:${BUILD_DATE} -t ${IMAGE_REGISTRY}/${PROJECT_NAME}/${IMAGE_NAME}:latest .
  
    # Harbor にログインして、作成したコンテナイメージを push する
    echo "${BOT_PASS}" | docker login https://${IMAGE_REGISTRY} --username ${BOT_NAME} --password-stdin
    docker push -a ${IMAGE_REGISTRY}/${PROJECT_NAME}/${IMAGE_NAME}

PersistentVolumeClaim

Docker サービスが利用する /var/lib/docker ディレクトリ配下を永続化して、ビルド処理の時に実行済みの処理を省略するためのキャッシュ用となるボリュームの定義。
内容的には LocalHostPath でも十分な程度の要件ですが、TrueNAS の iSCSI 領域に保持ししたら予想外に速度が出たので外出ししています。

pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: docker-cache
  namespace: mastodon-image-builder
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 32Gi

Secret

コンテナイメージのビルド処理の中で使う環境変数や、Harbor へ push する時に利用するキー情報を保持させています。

secret.yml
apiVersion: v1
kind: Secret
metadata:
  name: secret
  namespace: mastodon-image-builder
stringData:
  IMAGE_NAME: mastodon
  PROJECT_NAME: mastodon
  IMAGE_REGISTRY: registry.home.wirednet.jp
  BOT_NAME: "robot$mastodon_build_bot"
  BOT_PASS: "zxcvbhjkl....qwertyui1234567890"

実行する

Job リソースを作成すると、自動的に Pod を作成して指定されたシェルスクリプトを実行しはじめます。

$ kubectl apply -f job.yml

実行中の処理の様子は、kubectl logsで見ることが出来ます。

$ kubectl -n mastodon-image-builder logs -f jobs/image-builder

シェルスクリプトの処理が終わったら、Harbor にビルドされたコンテナイメージが格納されていました。
(何度もテスト実行しているので同じハッシュ値にタグがたくさん付いていますが……)

Discussion