2022年11月6日日曜日

docker-registry-proxy でコンテナイメージをキャッシュする

色々実験していると、各種 Container Registry のレートリミットが怖くなってくるので、ローカルにキャッシュサーバーを建てることにした。

ただ、Dockerfile や Manifest 内のイメージ URL を変更するのは辛いので、透過的にキャッシュを取得する方法を実現したい。

apt-cacher-ng を知っている方は、「それのコンテナイメージ版が欲しい」という感じで理解していただければ OK。

rpardini/docker-registry-proxy が、Man in the Middle 方式でキャッシュしてくれるようなので、それを試す。

前提

  • OS: Windows 11 Pro 21H2 ビルド 22000.1042
  • Docker Desktop: 4.12.0 (85629)

docker-registry-proxy のコンテナを起動

今回は、Docker Compose で建てることにした。 以下 yaml ファイルを作成し、 docker compose up -d する。

version: '3'

services:
  docker-registry-proxy:
    image: rpardini/docker-registry-proxy:0.6.4
    restart: always
    environment:
      # k8s のマニフェストキャッシュ機能を有効化
      ENABLE_MANIFEST_CACHE: true
      # `registry-1.docker.io` 以外にキャッシュしたいレジストリをスペース区切りで記載
      REGISTRIES: "k8s.gcr.io gcr.io quay.io"
    volumes:
      # キャッシュデータ格納用ディレクトリ
      - docker-registry-proxy:/docker_mirror_cache
      # 証明書関連データ格納ディレクトリ
      - docker-registry-proxy-ca:/ca
    ports:
      - 0.0.0.0:3128:3128
volumes:
  # キャッシュデータ格納用ボリューム
  docker-registry-proxy:
    name: docker-registry-proxy
  # 証明書関連データ格納ボリューム
  docker-registry-proxy-ca:
    name: docker-registry-proxy-ca

Windows の設定

ファイアウォールに穴をあける

今回は、同じネットワークの他ホストからも参照させたいので、 TCP 3128 の受信を許可する。

docker-registry-proxy の証明書インストール

docker-registry-proxy の Web サーバーから取得できるので、 Invoke-WebRequest で取得して、 Import-Certificate でインストールする。

# docker-registry-proxy の証明書を取得
Invoke-WebRequest -Uri http://localhost:3128/ca.crt -OutFile ca.crt

# 取得した証明書を Windows に、 `信頼されたルート証明機関` としてインストール。
Import-Certificate -FilePath .\ca.crt -CertStoreLocation Cert:\CurrentUser\Root

セキュリティ警告 のダイアログが出て、 この証明書をインストールしますか? と聞かれるので はい(Y) を押下。

証明書のインストール後、 Docker Desktop を再起動する。

Docker Engine の設定

プロキシ設定

  1. タスクトレイの Docker アイコン右クリック -> Settings -> Resources -> Proxies と選択し、必要な設定を入力後、 Apply & Restart ボタンを押下
    • Manual proxy configuration: オンにする
    • Web Server (HTTP):http://localhost:3128`
    • Web Server (HTTPS):http://localhost:3128`

docker info コマンドで、プロキシ設定が有効になっていることを確認できる。

Docker Desktop 再起動

Apply & Restart では、証明書の再読み込みまでしてくれないらしいので、 Docker Desktop の再起動を行う。

タスクトレイの Docker アイコン右クリック -> Restart 押下。

動作確認

Measure-Commanddocker pull の時間を計ってみる。

docker image rmi hello-world
docker image prune

Measure-Command {docker pull hello-world}
# => TotalSeconds      : 5.4094275

docker image rmi hello-world
docker image prune

Measure-Command {docker pull hello-world}
# => TotalSeconds      : 2.0318131

docker compose logs にも、ミスキャッシュしてからヒットしたというログが残っている。

> docker compose logs
...(略)
tmp-docker-registry-proxy-1  | {"access_time":"02/Oct/2022:18:46:34 +0000","upstream_cache_status":"MISS","method":"HEAD
","uri":"/v2/library/hello-world/manifests/latest","request_type":"manifest-default","status":"200","bytes_sent":"0","up
stream_response_time":"0.720","host":"registry-1.docker.io","proxy_host":"registry-1.docker.io","upstream":"34.205.13.15
4:443"}
...(略)
tmp-docker-registry-proxy-1  | {"access_time":"02/Oct/2022:18:47:20 +0000","upstream_cache_status":"HIT","method":"HEAD"
,"uri":"/v2/library/hello-world/manifests/latest","request_type":"manifest-default","status":"200","bytes_sent":"0","ups
tream_response_time":"","host":"registry-1.docker.io","proxy_host":"registry-1.docker.io","upstream":""}
...(略)

キャッシュディレクトリにデータも入っている。

> docker compose exec docker-registry-proxy find /docker_mirror_cache
/docker_mirror_cache
/docker_mirror_cache/a
/docker_mirror_cache/a/47
/docker_mirror_cache/a/47/d0862d346f039b9a238728e97237d47a
/docker_mirror_cache/7
/docker_mirror_cache/7/c4
/docker_mirror_cache/7/c4/d2c014ee5630f39c4a0fb8a1ce8d5c47
/docker_mirror_cache/5
/docker_mirror_cache/5/cf
/docker_mirror_cache/5/cf/e44914286ca296d3a45dc06185d03cf5
/docker_mirror_cache/d
/docker_mirror_cache/d/36
/docker_mirror_cache/d/36/7a4ad43a77fb3fc1a5cb477b7e2f136d
/docker_mirror_cache/c
/docker_mirror_cache/c/71
/docker_mirror_cache/c/71/27629938b7c2102ec0b9185900e4871c

良さそう、以上。

Windows 以外でのプロキシ設定方法

systemd でマシンごとに設定する方法と、 ~/.docker/config.json でユーザーごとに設定する方法がある。

systemd でマシンごとに設定する方法

systemd の docker.service 向けの設定ファイルを作成し、その中で環境変数を渡す。

docker.service 用の設定ファイル格納ディレクトリを作成

sudo mkdir /etc/systemd/system/docker.service.d

作成したディレクトリ内に設定ファイルを作成

今回は http-proxy.conf という名前で作成。

[Service]
Environment=HTTP_PROXY=http://192.168.2.101:3128/
Environment=HTTPS_PROXY=http://192.168.2.101:3128/

Docker サービスリスタート

sudo systemctl daemon-reload
sudo systemctl restart docker.service

~/.docker/config.json でユーザーごとに設定する方法

~/.docker/config.json を作成し、以下のように設定する。

{
 "proxies":
 {
   "default":
   {
     "httpProxy": "http://192.168.1.12:3128",
     "httpsProxy": "http://192.168.1.12:3128",
     "noProxy": "*.test.example.com,.example2.com,127.0.0.0/8"
   }
 }
}

参考資料

C プログラムを WASI 向けの Wasm としてビルドし、 WasmEdge で実行する

そのうち Docker で Wasm コンテナが実行できるようになる(Docker DesktopがWebAssemblyランタイムを統合。コンテナと同様にWebAssemblyイメージを実行可能に - Publickey)らしいので、Wasm のビルドと実行を試してみる。

前提

  • OS: Windows 11 Pro 22H2 ビルド: 22621.755
  • Docker: Docker Desktop 4.13.1 (90346)
  • 使用イメージ: debian:bullseye-slim

イメージ起動

docker run -it --rm debian:bullseye-slim

環境構築

前提ツール類インストール

apt-get update
apt-get install -y curl git libxml2 file
  • curl : WasmEdge のインストールに必要
  • git : WasmEdge のインストールに必要
  • libxml2 : Wasm バイナリのビルドに必要なライブラリ
  • file : ビルドしたバイナリが Wasm バイナリになっているかを確認するために使用

wasi-sdk のインストール

Wasm のコンパイルに必要な SDK をインストールする。

curl -LO https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-16/wasi-sdk_16.0_amd64.deb
apt-get install -f ./wasi-sdk_16.0_amd64.deb

/opt/wasi-sdk/ にインストールされる。

WasmEdge のインストール

Wasm 実行に必要なランタイムをインストールする。

curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash

~/.wasmedge にインストールされる。

ビルド

C のソースコードを作成

cat << EOF >> main.c
#include <stdio.h>

int main(int argc, char **argv) {
    printf("Hello, World!\n");
    return 0;
}
EOF

clang によるビルド

/opt/wasi-sdk/bin 内に clang があるので、それを使ってビルド。

/opt/wasi-sdk/bin/clang main.c --sysroot=/opt/wasi-sdk/share/wasi-sysroot/ -o ./main.wasm

バイナリの確認

file コマンドで確認すると、 WebAssembly であることが確認できる。

# file main.wasm
main.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)

実行

wasmedge に、ビルドしたバイナリを渡す。

# ~/.wasmedge/bin/wasmedge ./main.wasm
Hello, World!

実行できた。以上。

参考資料