k3sでGitHub Container Registryにおいたプライベートなコンテナイメージをつかう

内容を整理していて気づいたが、本記事には

  • (A) k3sでprivateなコンテナレジストリを使う
  • (B) Github Package Registry (GPR)からGithub Container Registry (GHCR *1 )へ移行する

という2つのトピックが混ざっていることに気が付いた。備忘録も兼ねて先にアウトラインを示しておく。

(A) k3sでprivateなコンテナレジストリを使う

(B) Github Package Registry (GPR)からGithub Container Registry (GHCR)へ移行する

  • Github Package RegistryとはGithub社で提供しているパッケージレジストリ
    • Github Actionsと組み合わることでコードベース管理からレジストリ登録までgithubで完結できる
  • Github Container Registryとは同じくGithub社が提供するコンテナレジストリ
    • オフィシャルアナウンス曰く、

      GitHub Container Registry improves how we handle containers within GitHub Packages. With the new capabilities introduced today, you can better enforce access policies, encourage usage of a standard base image, and promote innersourcing through easier sharing across the organization.

    • とのことだが、後述するとおり基本的にはGPRでDocker imageをホスティングするのと使い方は大きく変わらない。DockerHubやAmazon Elastic Container Registryなどと同じ位置づけ
    • まだpublic beta段階なので利用するには機能の有効化が必要
    • publicなコンテナイメージに対しては無料利用でき、public bataのフェーズの間はprivateなものも無料で利用できる。generally availableになったあともGPRと同じ課金モデルで提供予定とのこと
  • Github Actionをつかってdockerイメージをビルド、およびGHCRへイメージをpushするならdocker/build-push-actionを使うのが簡単

ここで今一度、takeawayを要約すると

  • Github Container Registry (GHCR)に置いたprivateコンテナイメージならk3sから利用できる
  • docker/build-push-action@v2を使ってGHCRにコンテナイメージをpushするには、(1)事前に機能の有効化を行い、(2)tagにはghcr.io/接頭辞としてつける

ということになる。

それでは次から具体的な方法を示していく。

ちなみにk3sはARMでも動くと謳っているが、本記事ではx86系のプロセッサで動く環境を前提とする*2。 本記事で利用したコードをこちらのリポジトリにまとめた。

github.com

1. Github Actionを用いたGHCRへのコンテナイメージ登録

流れとしては以下の4ステップ。

  • 1-a. 適当なプログラムとDockerイメージを生成するDockerfileの用意
  • 1-b. GHCRの有効化
  • 1-c. GHCRの認証用にPAT(Private Access Token)の発行
  • 1-d. GithubActionでdocker/build-push-action@v2を使うための設定

基本的にはリファレンスを張りつつ注意書き程度の補足を記すが、サンプルコードと合わせて確認してもらいたい。

1-a. 適当なプログラムとDockerイメージを生成するDockerfileの用意

Hello!の文字列と現在時刻を4秒ごとに履き続けるjsスクリプトをDockerイメージとして用意する。 長々と述べるよりもcommitログを見るほうがわかりやすいように思う。

1-b. GHCRの有効化

オフィシャルドキュメントにて画像添付で解説されているので参考にされたい。PersonalとOrganizationとで有効化ボタンが2箇所あるので注意。

なお、これを忘れると後述するGHCRへのコンテナイメージpushが失敗する。

1-c. GHCRの認証用にPAT(Private Access Token)の発行

オフィシャルでGITHUB_TOKENではなくPATを利用するように書かれている。

If you want to authenticate to GitHub Container Registry in a GitHub Actions workflow, then you must use a personal access token (PAT).

PATの作成方法はこちらから。

作成したPATをセキュアにGithub Actionに渡すには secrets という仕組みを利用する。

PATとは異なり、secretsはリポジトリごとあるいはOrganizationごとに登録する。登録した値をGithub Actionから${{ secrets.SuperSecret }} のようにして参照する。

1-d. GithubActionでdocker/build-push-action@v2を使うための設定

v1 から v2 へとアップデートした際に大きな仕様変更があり、v2では レジストリログインビルド環境セットアップが別actionに切り出された。 とはいえ設定項目が難しくなった訳でもないので素直にREADME.mdに従ってworkflowを書けば良い。

なお、v1にあった便利な tag_with_ref が使えなくなってしまった。そのためコンテナイメージへのタグ付けは他のアクションで賄う必要がある*3

name: deploy
on:
  push:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_PAT }} # secret経由でPATを渡す
      - name: Get smart tag  # このstepはオプショナル。v1の`tag_with_ref`と同等の機能
        id: prepare
        uses: Surgo/docker-smart-tag-action@v1
        with:
          docker_image: ghcr.io/${{ github.repository }}/test  # `ghcr.io`を接頭辞にすること
      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          push: true
          context: .
          tags: ${{ steps.prepare.outputs.tag }}  # 前ステップ(prepare)で用意したタグ名を使う
          build-args: NODEJS_VERSION=14.10.0-slim
          cache-from: type=registry,ref=ghcr.io/${{ github.repository }}/test:develop
          cache-to: type=inline

2. Privateレジストリの設定とk3sのインストール

k3sでプライベートレジストリを利用するには、事前に所定のパスに設定ファイルを配備する*4。具体的には /etc/rancher/k3s/registries.yaml に以下の内容でファイルを置く。

$ cat /etc/rancher/k3s/registries.yaml
mirrors:
  ghcr:
    endpoint:
      - "https://ghcr.io"
configs:
  "ghcr.io":
    auth:
      username: sat0yu
      password: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

username にはGithubアカウント、passwordにはPATを指定する。ここで使うPATは read:packages 権限があれば十分で、GHCRにコンテナイメージをPushするときのものと異なっていても良い(むしろ別にしたほうが安全。)

オフィシャルドキュメントを見るとhttpsならTLSに関する設定が必須のように誤解しそうだが、上記のようにTLSフィールドを省略しても正常にイメージをpullできた。

registries.yaml が用意できたらk3sのインストールを行う。k3sのインストールはワンライナーで済む。

$ curl -sfL https://get.k3s.io | sh -
[INFO]  Finding release for channel stable
[INFO]  Using v1.20.0+k3s2 as release
[INFO]  Downloading hash https://github.com/rancher/k3s/releases/download/v1.20.0+k3s2/sha256sum-amd64.txt
[INFO]  Downloading binary https://github.com/rancher/k3s/releases/download/v1.20.0+k3s2/k3s
[INFO]  Verifying binary download
[INFO]  Installing k3s to /usr/local/bin/k3s
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  Skipping /usr/local/bin/ctr symlink to k3s, command exists in PATH at /usr/bin/ctr
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s

k3sはkubectlが同梱していて、正常にインストール完了するとkubectlコマンドにsymlinkがはられる。 kubectl get nodeで動作を確認しておく。

$ ls -al /usr/local/bin/kubectl
lrwxrwxrwx 1 root root 3 Jan 18 14:06 /usr/local/bin/kubectl -> k3s

$ sudo kubectl get node
NAME   STATUS   ROLES                  AGE   VERSION
xxxx   Ready    control-plane,master   32s   v1.20.0+k3s2

3. k3sクラスタへのpodデプロイ

全ステップまでですでにk3sクラスタ*5を立ち上げた状態になっている。

マニフェストファイルに特別な記述は必要なく、適切なimageを指定すれば良い。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      containers:
        - name: test
          image: ghcr.io/sat0yu/k3s-with-private-container-images-in-gcr/test:master
          imagePullPolicy: Always
          command: ["yarn", "start"]

あとはマニフェストファイルをapplyしてk8sがコンテナイメージをpullしてデプロイが完了させる。

$ sudo kubectl apply -f deployment.yaml
deployment.apps/test configured

最後に期待通りコンテナが動いているか確認する

$ sudo kubectl get po
NAME                    READY   STATUS    RESTARTS   AGE
test-85b8b88944-grmsk   1/1     Running   0          2m22s

$ sudo kubectl logs -f -l app=test
hello! Mon Jan 18 2021 23:44:58 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:02 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:06 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:10 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:14 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:18 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:22 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:26 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:30 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:34 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:38 GMT+0000 (Coordinated Universal Time)

*1:GCRだとContainer Registry  |  Google Cloudと紛らわしい

*2:確認していないがDockerfileにあるnodeのベースイメージを適切なものに変更すればARMでも動くと思う

*3:サンプルコードではSurgo/docker-smart-tag-actionを利用した

*4:マニュアルによればk3s起動時に--private-registryオプションでパスを指定することも可能らしい

*5:k3sで立ち上げるk8sクラスタ?