GitHub Container Registry でプライベートなイメージを GitHub Actions + Kubernetes で使う

かなり前 GitHub Packages でコンテナイメージを扱ってました。

kondoumh.hatenablog.com

その後 Container Registry が登場しました。Packages の時は public なイメージの Pull にも認証が必要でしたが、Container Registry では不要になっています。そして既存の Packages のコンテナイメージは順次 Container Registry に移行されるみたいです。

docs.github.com

OSS じゃない開発では private な Container Registry に開発中のアプリのイメージを push し、CI パイプラインで pull して利用するケースが多いと思います。CI で Kubernetes クラスターにデプロイしてテストするケースもあるでしょう。その時の認証情報の 扱いなどが気になったので調べてみました。

docs.github.com

まず、手元の PC からの push / pull。

GitHub の Settings / Developer settings で PAT (Personal Access Token) を作成します。この時、write:packages をスコープに含める必要があります。この PAT を環境変数に設定して ghcr.io に docker login します*1

export CR_PAT=YOUR_TOKEN
echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin

この状態で docker build / tag / push するとプライベートなパッケージとしてコンテナが登録されます *2

docker push ghcr.io/OWNER/IMAGE_NAME:latest

GitHub Actions でイメージをビルドして push するには Container Registry と Actions を実行するリポジトリを接続した上で Container Registry への書き込み権限を付与する必要があります。

docs.github.com

手動で push した直後はリポジトリとの関連付けがないので Registry のページには接続用の UI が表示されています。

Actions を実行するリポジトリを追加して、Manage Actions access のセクションでリポジトリの Role を Write に指定します。

これで、GitHub Actions で PAT を使わなくても暗黙に設定される GITHUB_TOKEN を使ってプライベートイメージを扱えるようになります。PAT はセキュリティのため比較的短い期間で expire するのが普通で、長期間 expire しない PAT を GitHub Actions では利用しないことが推奨されています。

docs.github.com

イメージをビルドして、push するワークフローの例です。docker コマンドも使えますが、ビルド、タグ付け、push をしてくれる Action を利用しました。

jobs:
  Build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Build and push container image
      uses: elgohr/Publish-Docker-Github-Action@master
      with:
        name: ghcr.io/kondoumh/gh-container-registry/nodejs-server
        username: kondoumh
        password: ${{ secrets.GITHUB_TOKEN }}
        registry: ghcr.io

GitHub Actions で Minikube や Kind などの Kubernetes 環境にプライベートなコンテナイメージを使ってデプロイするには、事前に Secret を適用しておき、イメージの manifest で Secret を指定します。GitHub Actions のステップでは以下のように GITHUB_TOKEN を docker-password に指定して Secret を作成すれば OK です。

  Deploy:
    needs: [Build]
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - run: minikube start
    - name: Create secret for GitHub container registry
      run: |
        kubectl create secret docker-registry regcred \
          --docker-server=https://ghcr.io \
          --docker-username=kondoumh \
          --docker-password=${{ secrets.GITHUB_TOKEN }}
    - name: Deploy to minikube
      run: kubectl apply -f manifest.yml

適用している manifest では以下のように imagePullSecrets で Secret を指定します。

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nodejs-api
  name: nodejs-api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nodejs-api
  template:
    metadata:
      labels:
        app: nodejs-api
    spec:
      containers:
      - image: ghcr.io/kondoumh/gh-container-registry/nodejs-server:latest
        name: nodejs-server
        imagePullPolicy: Always
      imagePullSecrets:
      - name: regcred

パブリックなイメージを使用する場合は Secret は不要で使えました。やはりこれが Package registry に比べた改善点ですかね。 プライベートなイメージはデータ転送やストレージへの課金が発生するので注意が必要です。GitHub Actions によってトリガーされるデータ転送は無料ですが、GITHUB_TOKEN 使用しているかどうかで判定しているようですので料金の面からも PAT は使わない方がよさそうです。

docs.github.com

*1:Packages の時は docker.pkg.github.com でした

*2:デフォルトの公開設定はプライベートのようです