nginx-proxyでSSL/TLS化するのに、普通はdocker-composeするので特に問題は無いのだけれど、これをpodmanでやろうとするとなかなかに難しいという話。環境としてはRHEL9.3のpodman-4.6.1-7.el9_3.x86_64。事前にpodman.socketを有効化してあり、docker.sockはシンボリックリンクになっている。
# systemctl enable --now podman.socket
# ll /var/run/docker.sock
lrwxrwxrwx. 1 root root 23 1月 25 10:19 /var/run/docker.sock -> /run/podman/podman.sock
どういうエラーか
”Error: can’t get my container ID !”がログに出力される。
# podman pod logs app-pod
6f8df1ce5fe1 Info: running acme-companion version v2.2.10-5-g013005a
6f8df1ce5fe1 2024/01/28 01:05:16, Error: can't get my container ID !
6f8df1ce5fe1 Warning: can't check if '/etc/nginx/certs' is a mounted volume without self container ID.
6f8df1ce5fe1 2024/01/28 01:05:16, Error: can't get my container ID !
6f8df1ce5fe1 Warning: can't check if '/etc/nginx/vhost.d' is a mounted volume without self container ID.
6f8df1ce5fe1 2024/01/28 01:05:16, Error: can't get my container ID !
fab5b0cf58eb Info: running nginx-proxy version 1.4.0-71-gd46881f
6f8df1ce5fe1 Warning: can't check if '/etc/acme.sh' is a mounted volume without self container ID.
6f8df1ce5fe1 2024/01/28 01:05:16, Error: can't get my container ID !
podに入れない場合のhostname
手順としては以下。hostnameはコンテナIDの上位12桁が設定される。
# podman run \
--detach \
--name nginx-proxy \
--volume nginx-certs:/etc/nginx/certs \
--volume nginx-vhost:/etc/nginx/vhost.d \
--volume nginx-html:/usr/share/nginx/html \
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
docker.io/nginxproxy/nginx-proxy
e2fb454034da541ce00b0dc8f4d3471359458dca9326914328091c0353fb5421
# podman run \
--detach \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume nginx-certs:/etc/nginx/certs:rw \
--volume acme:/etc/acme.sh \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
-e NGINX_PROXY_CONTAINER=nginx-proxy \
-e DEFAULT_EMAIL=rio@rio.st \
docker.io/nginxproxy/acme-companion
1cb364360e42af3dbd911ec5249f7b577692f4e4773e3e4cb50cf45e5327ab87
# podman exec -it 1cb hostname
1cb364360e42
podに入れる場合のhostname
手順は以下。pod createして、podman runに–podオプションを追加して、podにコンテナを入れる。hostnameはpodの名前が設定される。
# podman pod create -p 80:80 -p 443:443 --name app-pod
e32b3fd7c38052a7780d340d6bd622071d940ad14dd6c1c466da81ae65758217
# podman run \
--detach \
--pod app-pod \
--name nginx-proxy \
--volume nginx-certs:/etc/nginx/certs \
--volume nginx-vhost:/etc/nginx/vhost.d \
--volume nginx-html:/usr/share/nginx/html \
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
docker.io/nginxproxy/nginx-proxy
74aeab47336c14387e224cbac083ad9447135cc5273382e4f493ddeecf7bce74
# podman run \
--detach \
--pod app-pod \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume nginx-certs:/etc/nginx/certs:rw \
--volume acme:/etc/acme.sh \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
-e NGINX_PROXY_CONTAINER=nginx-proxy \
-e DEFAULT_EMAIL=rio@rio.st \
docker.io/nginxproxy/acme-companion
0ba98878fa3e6c1110a30cb0fe619083846e9cfdb5162ca0c8e94a17682ffe93
# podman exec -it 0ba hostname
app-pod
エラーになるフロー
podに入れた場合にエラーになるのは、nginxproxy/acme-companionの/app/functions.shの以下のコード。
/proc/1/cpusetと/proc/self/cgroupはcgroups v1でしか利用できず、Docker APIを叩いている。
function get_self_cid {
local self_cid=""
# Try the /proc files methods first then resort to the Docker API.
if [[ -f /proc/1/cpuset ]]; then
self_cid="$(grep -Eo '[[:alnum:]]{64}' /proc/1/cpuset)"
fi
if [[ ( ${#self_cid} != 64 ) && ( -f /proc/self/cgroup ) ]]; then
self_cid="$(grep -Eo -m 1 '[[:alnum:]]{64}' /proc/self/cgroup)"
fi
if [[ ( ${#self_cid} != 64 ) ]]; then
self_cid="$(docker_api "/containers/$(hostname)/json" | jq -r '.Id')"
fi
# If it's not 64 characters long, then it's probably not a container ID.
if [[ ${#self_cid} == 64 ]]; then
echo "$self_cid"
else
echo "$(date "+%Y/%m/%d %T"), Error: can't get my container ID !" >&2
return 1
fi
}
## Docker API
function docker_api {
local scheme
local curl_opts=(-s)
local method=${2:-GET}
# data to POST
if [[ -n "${3:-}" ]]; then
curl_opts+=(-d "$3")
fi
if [[ -z "$DOCKER_HOST" ]];then
echo "Error DOCKER_HOST variable not set" >&2
return 1
fi
if [[ $DOCKER_HOST == unix://* ]]; then
curl_opts+=(--unix-socket "${DOCKER_HOST#unix://}")
scheme='http://localhost'
else
scheme="http://${DOCKER_HOST#*://}"
fi
[[ $method = "POST" ]] && curl_opts+=(-H 'Content-Type: application/json')
curl "${curl_opts[@]}" -X "${method}" "${scheme}$1"
}
podを作成しない場合、もし/proc/1/cpusetと/proc/self/cgroupからコンテナIDを取得できなくても、functions.shのget_self_cid()はdocker_api()は以下を実行することでコンテナIDを取得できるため、エラーにならない。
curl -s --unix-socket /tmp/docker.sock -X GET http://localhost/containers/1cb364360e42/json
podを作成する場合、実行されるcurlは以下のようになる。
curl -s --unix-socket /tmp/docker.sock -X GET http://localhost/containers/app-pod/json
すると、docker.sock、つまりpodman.sockは以下のエラーを返す。
# curl -s --unix-socket /tmp/docker.sock -X GET http://localhost/containers/app-pod/json
{"cause":"no such container","message":"\"app-pod\" is a pod, not a container: no such container","response":404}
「そんなコンテナは無ぇ」。それはそう…。
hostnameにコンテナID(の一部)が設定されていることを期待しているnginxproxy/acme-companionがよろしくないっぽい。というのは、OCIのコンテナランタイムの仕様ではhostnameはUTS namespaceとなっているから。
ではどうするか
2つの方法が考えられる。1つは/proc/self/mountinfoから取得すること。”cgroups v2″のコメントから3行がそれ。busyboxのgrepではPerl拡張が使えないのでとりあえずこう書いたが、他に良い書き方があれば知りたい…。
function get_self_cid {
local self_cid=""
# Try the /proc files methods first then resort to the Docker API.
if [[ -f /proc/1/cpuset ]]; then
self_cid="$(grep -Eo '[[:alnum:]]{64}' /proc/1/cpuset)"
fi
if [[ ( ${#self_cid} != 64 ) && ( -f /proc/self/cgroup ) ]]; then
self_cid="$(grep -Eo -m 1 '[[:alnum:]]{64}' /proc/self/cgroup)"
fi
# cgroups v2
if [[ ( ${#self_cid} != 64 ) && ( -f /proc/self/mountinfo ) ]]; then
self_cid="$(grep '/userdata/hostname' /proc/self/mountinfo | grep -Eo '[[:alnum:]]{64}')"
fi
if [[ ( ${#self_cid} != 64 ) ]]; then
self_cid="$(docker_api "/containers/$(hostname)/json" | jq -r '.Id')"
fi
# If it's not 64 characters long, then it's probably not a container ID.
if [[ ${#self_cid} == 64 ]]; then
echo "$self_cid"
else
echo "$(date "+%Y/%m/%d %T"), Error: can't get my container ID !" >&2
return 1
fi
}
もう1つは、podman run –cidfile /tmp/acme-companion.cidなどとして、runした後にマウントして内部で読み取ること。ただしrunが完了しないとコンテナIDは確定しないため、runに-vでマウントするように指示してもfile not foundになってしまう。上記get_self_cid()の実行をwaitさせるようにしないとならないため、いまいちである。
とりまPRはしておいたが、acme-companionにマージされるか否かは分からんw