【k8s】containerd を使った Kubernetes 環境で Docker Registry からイメージを pull できなくて困った話

こんにちは。お久しぶりです。
私事で恐縮ですが、12 月より新しい会社様にお世話になることとなりました。
学生の頃からずっと憧れていた企業様ですので、精一杯頑張ろうと思います。

さて、本題ですが、ローカルの VM に構築した Kubernetes ( CR: containerd ) 環境にて、同じくローカルに用意した Docker Registry に保存されているイメージから Pod を起動しようとしたところ、下記のようなエラーが発生しました。

Failed to pull image "xxx.xxx.xxx.xxx:5000/hoge": failed to pull and unpack image "xxx.xxx.xxx.xxx:5000/hoge:latest": failed to resolve reference "xxx.xxx.xxx.xxx:5000/hoge:latest:latest": failed to do request: Head "xxx.xxx.xxx.xxx:5000/hoge:latest:5000/v2/hoge/manifests/latest": http: server gave HTTP response to HTTPS client
Error: ErrImagePull

どうも、TLS 通信周りの問題から、イメージの pull に失敗しているようです。
コンテナランタイムに containerd を使っているのもあり、中々参照できる情報がなかったため、ココに備忘録を残しておきます。

TL;DR

単純に Docker Registry が HTTPS での通信に対応していないだけ。
containerd の設定ファイルを下記のように書き換えて、containerd を再起動すれば OK。
※ xxx.xxx.xxx.xxx は Docker Registry の IP に差し替える。

/etc/containerd/config.toml

[plugins."io.containerd.grpc.v1.cri".registry.configs]
  [plugins."io.containerd.grpc.v1.cri".registry.configs."xxx.xxx.xxx.xxx:5000".tls]
    insecure_skip_verify = true

[plugins."io.containerd.grpc.v1.cri".registry.headers]

[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."xxx.xxx.xxx.xxx:5000"]
    endpoint = ["http://xxx.xxx.xxx.xxx:5000"]

/etc/containerd/config.toml 差分

[root@verienv ~]# diff /etc/containerd/config.toml /etc/containerd/config.toml.org
150,151d149
<         [plugins."io.containerd.grpc.v1.cri".registry.configs."xxx.xxx.xxx.xxx:5000".tls]
<           insecure_skip_verify = true
156,157d153
<         [plugins."io.containerd.grpc.v1.cri".registry.mirrors."xxx.xxx.xxx.xxx:5000"]
<           endpoint = ["http://xxx.xxx.xxx.xxx:5000"]

やってみる ( 構築 )

・環境

RoleIP addressOSsupplement
k8s Control192.168.33.11AlmaLinux 9Name: verienv01
Docker Registry
k8s Node192.168.33.12AlmaLinux 9Name: verienv02

k8s Control と Docker Registry は同じ VM に同居させるとします。

・構築 ( k8s )

k8s の主要な導入は Ansible Playbook の Role にまとめました。
Playbook を流し終えた後は、README の Memo.Initialization ( Commands after provisioning ) と Node での kubeadm join を実行すれば完了するかと思います。

[root@verienv01 ~]# kubectl get nodes
NAME        STATUS   ROLES           AGE    VERSION
verienv01   Ready    control-plane   162m   v1.28.4
verienv02   Ready    <none>          159m   v1.28.4

・構築 ( Docker Registry )

Docker Registry はコンテナが用意されているので、公式の通りに導入。
※ nerdctl で入れてしまってもいいかもしれません。

dnf -y install docker-ce docker-ce-cli
systemctl enable --now docker
docker run -d -p 5000:5000 --name registry registry:2

やってみる ( 検証 )

※ verienv01 で実施します。
※ TL;DR に記載の containerd の設定を全 Node で実施済みとします。

・検証用のコンテナイメージを作成

簡単な HTTP レスポンスを返すコンテナイメージを hello_k8s という名称でビルドします。

cat > ./Dockerfile << EOF
FROM python:slim

WORKDIR /app
EXPOSE 8000
COPY index.html .

CMD ["python", "-m", "http.server", "8000"]
EOF

cat > ./index.html << EOF
"Hello from k8s!"
EOF

docker build -t hello_k8s .

・コンテナイメージを Docker Registry に登録

localhost:5000 のタグを付与してイメージを push します。
※ 他 PC 等から Docker Registry の IP を指定して push する場合は、Docker に insecure-registries の設定が必要です。

docker tag hello_k8s:latest localhost:5000/hello_k8s
docker push localhost:5000/hello_k8s

・マニフェスト作成 & apply

Pod を起動する Deployment と NodePort 30080 で接続を受け付ける Service を定義して apply します。

cat > ./deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-k8s
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-k8s
  template:
    metadata:
      labels:
        app: hello-k8s
    spec:
      containers:
      - name: hello-k8s
        image: 192.168.33.11:5000/hello_k8s
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8000
EOF

cat > ./service.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: hello-k8s-service
spec:
  selector:
    app: hello-k8s
  type: NodePort
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000
      nodePort: 30080  
EOF
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

・確認

Pod が正常に起動し、レスポンスが返ってくれば成功です!

[root@verienv01 ~]# kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
hello-k8s-77b86d6c8-4mbm2   1/1     Running   0          6m52s
[root@verienv01 ~]# curl http://192.168.33.11:30080/
"Hello from k8s!"

【redash】dumpファイルからの復旧/データ移行【Docker】

【redash】Dockerコンテナ内部からdumpデータを取得【シェルスクリプト】


↑dumpデータの取得はこちらで。

dumpデータから redash のレストア/データ移行手順になります。

# コンテナ停止
docker-compose -f docker-compose-redash.yml down --remove-orphans

# PostgreSQLのコンテナのみ起動
docker container run -d -v /path/to/postgres-data:/var/lib/postgresql/data -p 5432:5432 postgres:9.5.6-alpine

# PostgreSQLのコンテナIDを確認
docker ps | grep postgres | awk '{print $1}'

# dumpファイルをコンテナに転送
docker container cp /path/to/redash-backup_$(date +"%Y%m%d").gz [コンテナID]:/usr/local/redash-backup_$(date +"%Y%m%d").gz

# DBの削除
docker container exec [コンテナID] /bin/bash -c 'psql -c "drop database if exists postgres" -U postgres template1'

# DBの作成
docker container exec [コンテナID] /bin/bash -c 'psql -c "create database postgres" -U postgres template1'

# dumpデータのインポート
docker container exec [コンテナID] /bin/bash -c 'zcat /usr/local/redash-backup_$(date +"%Y%m%d").gz | psql -U postgres -d postgres'

# PostgreSQLのコンテナ停止
docker container stop [コンテナID]

# PostgreSQLのコンテナ削除
docker container rm [コンテナID]

# コンテナ起動
docker-compose -f docker-compose-redash.yml up -d

↓redash の構築方法はこちら

【Docker】redashの構築【CentOS7】

【redash】Dockerコンテナ内部からdumpデータを取得【シェルスクリプト】

そういえば redash のバックアップを取得していなかったなと気づきまして。
どうやらコンテナ内部の PostgreSQL の dumpデータを取得するればいいようです。

↓こんな感じのシェルスクリプトをcronで定期実行すればいいですね!

#!/bin/sh

# バックアップディレクトリを定義
BACKUP_DIR=/var/backup
# バックアップファイルを保持する期間を定義
TIME=3

# DBのコンテナIDを取得
CID=$(docker container ls | grep postgres | awk '{print $1}')

# dumpを取得
docker container exec ${CID} /bin/bash -c 'pg_dump -U postgres postgres | gzip > /usr/local/redash-backup.gz'

# ホストにdumpを転送
docker container cp ${CID}:/usr/local/redash-backup.gz ${BACKUP_DIR}/redash-backup_$(date +"%Y%m%d").gz

# dumpのローテート設定
find ${BACKUP_DIR}/ -daystart -name '*.gz' -mtime +${TIME} -delete

↓redashの構築方法はこちら

【Docker】redashの構築【CentOS7】

【Docker】redashの構築【CentOS7】

担当した案件で CentOS7 に redash を導入したんでその時の備忘録になります。
なんか色々と嵌った気がします。

# 必要なパッケージの導入
yum install epel-release -y
yum install docker docker-compose -y

# docker操作用の権限を一般ユーザに追加
sudo groupadd docker
sudo gpasswd -a $USER docker
sudo systemctl restart docker


# docker-compose.ymlファイルを用意
cat <<EOF > docker-compose-redash.yml
version: '2'
services:
  redis:
    image: redis:3.0-alpine
    restart: always

  server:
    image: redash/redash:latest
    command: server
    depends_on:
      - postgres
      - redis
    ports:
      - "5000:5000"
    environment:
      PYTHONUNBUFFERED: 0
      REDASH_LOG_LEVEL: "INFO"
      REDASH_REDIS_URL: "redis://redis:6379/0"
      REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
      REDASH_COOKIE_SECRET: veryverysecret
      REDASH_WEB_WORKERS: 4
    restart: always

  worker:
    image: redash/redash:latest
    command: scheduler
    environment:
      PYTHONUNBUFFERED: 0
      REDASH_LOG_LEVEL: "INFO"
      REDASH_REDIS_URL: "redis://redis:6379/0"
      REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
      QUEUES: "queries,scheduled_queries,celery"
      WORKERS_COUNT: 2
    restart: always

  postgres:
    image: postgres:9.5.6-alpine
    volumes:
      - ~/postgres-data:/var/lib/postgresql/data
    restart: always
EOF

# docker上に環境構築
docker-compose -f docker-compose-redash.yml run --rm server create_db

# docker上のredash起動
docker-compose -f docker-compose-redash.yml up -d

5000ポートで listen するんで、フロントに Nginx を置いてリバプロするのがいいですね。