はじめに
Kubernetesクラスタを運用する際、特にCI/CDパイプラインやプロトタイピング中に、Docker Hubのレートリミットに遭遇することがあります。この制限は、イメージのプル失敗(ImagePullBackOff
)などを引き起こし、非常にうっとうしいです。
これを回避するために、mirror.gcr.io をDocker Hubイメージのプルスルーキャッシュとして利用できます。
本稿では、特にCluster API を用いた管理クラスタとワークロードクラスタの構成で、全てのクラスターでmirror.gcr.ioをレジストリミラーとして設定し、Docker Hubのレートリミットを回避する方法を解説していきます。
なぜmirror.gcr.ioを使うのか?
mirror.gcr.ioはDocker Hubイメージをキャッシュしてくれているので、クラスタからは直接Docker HubではなくGoogleのミラーからイメージを取得するようにすると、Docker Hubのレートリミットに悩まされなくなります!
前提条件
- Docker のインストール
- kind(Kubernetes IN Docker)のインストール
- kubectl のインストール
- clusterctl のインストール
- yq のインストール(YAML処理用)
ステップ1: レジストリミラー設定ファイルの準備
kindで直接起動したクラスター(管理クラスター)のcontainerdがDocker Hubのミラーとしてmirror.gcr.ioを利用できるよう、ディレクトリ構造と設定ファイルを作成します。
mkdir -p certs.d/docker.io
cat > certs.d/docker.io/hosts.toml <<EOF
server = "https://registry-1.docker.io"
[host."https://mirror.gcr.io"]
capabilities = ["pull"]
[host."https://registry-1.docker.io"]
capabilities = ["pull", "resolve"]
EOF
このファイルは、containerdにDocker Hubイメージの取得時に、
- registry-1.docker.ioからマニフェストを取得 (resolve)
- mirror.gcr.ioからpull
というステップを踏むような設定になっています。
このファイルはkindを実行するホストマシン(macOS)に作成しておきます。
resolveもひとつのAPIコールになるため、Docker Hubのレートリミットの消費につながります。「一切Docker Hubとやりとりしたくない」という場合は次のようにmirror.gcr.ioにresolveも任せることもできます。
server = "https://registry-1.docker.io"
[host."https://mirror.gcr.io"]
capabilities = ["pull", "resolve"]
注意点として、latest
タグなど頻繁に更新されるイメージが、mirror.gcr.ioでは古いままといったことが起りうるので注意してください。
ステップ2: kindクラスタのミラー設定
以下の内容で kind-cluster.yaml
ファイルを作成します。これにより、kind内部のcontainerdがミラーを利用し、デバッグログも有効化されます。
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
ipFamily: dual
nodes:
- role: control-plane
image: kindest/node:v1.33.1
extraMounts:
# Cluster API Docker Provider(CAPD)がホストのDockerを操作できるようにDockerソケットをマウント
- hostPath: /var/run/docker.sock
containerPath: /var/run/docker.sock
# ミラー設定をcontainerdの設定ディレクトリにマウント
- hostPath: ./certs.d
containerPath: /etc/containerd/certs.d
# containerdにdocker.ioとregistry.k8s.ioのイメージ取得時にmirror.gcr.ioを優先させる
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
# mirror.gcr.ioの利用状況を確認するためデバッグモードを有効化
# ログ確認例: `docker exec -it <cluster名>-control-plane bash -c 'journalctl -u containerd -f & crictl pull nginx'`
[debug]
level = "debug"
これもホストマシンに作成しておきます。
各項目の詳細解説:
-
extraMounts
:- CAPDがDockerを操作するためのソケットマウント。
- ミラー設定(certs.d)をノード内のcontainerd設定ディレクトリにマウント。
-
containerdConfigPatches
:- containerdにカスタムレジストリ設定パスを指定。
- デバッグログを有効化し、イメージプルやミラー利用がはっきりと解るようにする。
ステップ3: 管理クラスタの作成
kind create cluster --name management --config kind-cluster.yaml
このコマンドで、カスタムレジストリ設定とデバッグログが有効な管理クラスタが作成されます。ミラーが利用されているかは、以下のコマンドで確認できます:
docker exec -it management-control-plane bash -c 'crictl pull hello-world && journalctl -u containerd' | grep fetch | grep mirror.gcr.io
mirror.gcr.io
からイメージが取得されているログが表示されれば成功です。
ステップ4: 管理クラスタへのCluster APIインストール
export CLUSTER_TOPOLOGY=true
clusterctl init --infrastructure docker
これで管理クラスタにCluster APIコンポーネントがインストールされます。
ステップ5: ワークロードクラスタテンプレートへのミラーパッチ適用
Cluster APIでワークロードクラスタを作成する際、ノードにもミラー設定が反映されるよう、クラスタテンプレートに事前コマンドを注入します。
以下の内容で patch.yaml
を作成します:
- name: containerd-customization
description: "containerdのミラー・デバッグ・再起動設定"
definitions:
- selector:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlaneTemplate
matchResources:
controlPlane: true
jsonPatches:
- op: add
path: /spec/template/spec/kubeadmConfigSpec/preKubeadmCommands
value:
- |
set -e
# 1. ミラー用hosts.toml作成
mkdir -p /etc/containerd/certs.d/docker.io
cat <<EOF > /etc/containerd/certs.d/docker.io/hosts.toml
server = "https://registry-1.docker.io"
[host."https://mirror.gcr.io"]
capabilities = ["pull"]
[host."https://registry-1.docker.io"]
capabilities = ["pull", "resolve"]
EOF
# 2. デバッグ設定を冪等に追加
grep -qF '[debug]' /etc/containerd/config.toml || cat <<'TOML' >> /etc/containerd/config.toml
[debug]
level = "debug"
TOML
# 3. registry config_pathを冪等に追加
grep -qF '[plugins."io.containerd.grpc.v1.cri".registry]' /etc/containerd/config.toml || cat <<'TOML' >> /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
TOML
# 4. containerdを再起動して反映
systemctl restart containerd
- selector:
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
matchResources:
machineDeploymentClass:
names: ["default-worker"]
jsonPatches:
- op: add
path: /spec/template/spec/preKubeadmCommands
value:
- |
set -e
# 1. ミラー用hosts.toml作成
mkdir -p /etc/containerd/certs.d/docker.io
cat <<EOF > /etc/containerd/certs.d/docker.io/hosts.toml
server = "https://registry-1.docker.io"
[host."https://mirror.gcr.io"]
capabilities = ["pull"]
[host."https://registry-1.docker.io"]
capabilities = ["pull", "resolve"]
EOF
# 2. デバッグ設定を冪等に追加
grep -qF '[debug]' /etc/containerd/config.toml || cat <<'TOML' >> /etc/containerd/config.toml
[debug]
level = "debug"
TOML
# 3. registry config_pathを冪等に追加
grep -qF '[plugins."io.containerd.grpc.v1.cri".registry]' /etc/containerd/config.toml || cat <<'TOML' >> /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
TOML
# 4. containerdを再起動して反映
systemctl restart containerd
各項目の詳細解説:
-
name
とdescription
: パッチの目的を示すメタデータ。 -
definitions
: ノード種別ごとのパッチ定義リスト。 - 各
selector
は特定のCluster APIテンプレート種別(コントロールプレーンまたはワーカーノード)をターゲット。 -
jsonPatches
: ノードの起動プロセスにコマンドを注入するJSONパッチ操作。 -
preKubeadmCommands
内のシェルスクリプトは:- ミラー設定作成: containerd用のhosts.tomlを作成。
- デバッグログ有効化: containerd設定にdebugセクションを追加。
- registry config_path設定: containerdにミラー設定パスを認識させる。
- containerd再起動: 設定反映のためサービス再起動。
- このパッチはコントロールプレーン・ワーカーノード両方に適用され、全ノードでミラーが利用されます。
なんだか込み入ってますが、kindで行った設定をCluster API版として焼き直したものです。
ステップ6: ワークロードクラスタマニフェストの生成と適用
このステップでは、ワークロードクラスタのマニフェストを生成し、containerdカスタマイズパッチを適用してから管理クラスタに適用します。
clusterctl generate cluster workload \
--flavor development \
--kubernetes-version v1.33.1 \
--control-plane-machine-count=1 \
--worker-machine-count=1 \
| yq \
| tee workload.original.yaml \
| yq 'select(.kind == "ClusterClass").spec.patches += load("patch.yaml")' \
| tee workload.patched.yaml \
| kubectl apply -f -
各コマンドの意味と流れは以下の通りです:
-
クラスタマニフェスト生成:
clusterctl generate cluster workload ...
で新しいワークロードクラスタのYAMLマニフェストを生成します(トポロジー、マシンテンプレート、設定など)。 -
yqでYAML整形: 最初の
yq
でYAMLをパースし、後続処理しやすくします。 -
オリジナル保存:
tee workload.original.yaml
で未パッチのマニフェストを保存。 -
ClusterClassへパッチ適用: 2つ目の
yq
でClusterClass
のspec.patches
にpatch.yaml
の内容を追加。これによりクラスタ作成時にpreKubeadmCommands(ミラー設定)がノード起動時に注入されます。 -
パッチ済みマニフェスト保存:
tee workload.patched.yaml
でパッチ済みマニフェストを保存。 -
クラスタへ適用:
kubectl apply -f -
でパッチ済みマニフェストを管理クラスタに適用し、ワークロードクラスタが作成されます。
workload.original.yamlとworkload.patched.yamlは、パッチ適用の前後をご確認いただきやすくするために保存しています。
ステップ7: ワークロードクラスタでミラー利用を確認
ワークロードクラスタのノードがミラーを利用しているか確認するには、以下を実行します:
for container in $(kubectl --context=kind-management get machines -o custom-columns=NAME:.metadata.name --no-headers); do
echo
echo === $container ===
docker exec -it $container bash -c 'crictl pull hello-world && journalctl -u containerd' | grep fetch | grep mirror.gcr.io
done
各ノードでmirror.gcr.io
からイメージが取得されているログが表示されれば成功です。
ステップ8: (オプション) CNIプラグインのインストール
例:Calicoをインストールする場合
kubectl --context=kind-workload apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
まとめ
以上のようにすると、管理クラスタ・ワークロードクラスタの両方でmirror.gcr.ioをDocker Hubイメージのプルスルーキャッシュとして利用できます。これによりDocker Hubのレートリミットを回避し、Kubernetesワークロードのイメージプルが安定します。是非試してみてください! 最後までお読みいただき、ありがとうございました!