※ Podman version 2.0.2 にて確認、2020/7/17 時点で既に修正に着手されている問題です。
問題の概要
podman play kube
コマンドでポッドを作成すると、 Podman は Kubernetes マニフェストファイル中の command パラメータを 「イメージの ENTRYPOINT の上書き」ではなく、事実上の「追記」として処理しており、どうもこれは Kubernetes とは異なる挙動のよう。Podman と Kubernetes の間でマニフェストファイルの互換性が保てなくなるはずなので不具合なのでは?
再現方法
同一ポッド内での IPC 通信のデモを Podman で行うために、こちらのマニフェストファイルをお借りしました。
https://qiita.com/mamorita/items/15437a1dbcc00919fa4e
apiVersion: v1
kind: Pod
metadata:
name: mc2
spec:
containers:
- name: producer
image: allingeek/ch6_ipc
command: ["./ipc", "-producer"]
- name: consumer
image: allingeek/ch6_ipc
command: ["./ipc", "-consumer"]
restartPolicy: Never
この Kubernetes マニフェストに基づき、Podman にポッドを作ってもらいます。
# podman play kube ipc.yml
実行結果
すると producer コンテナが送信したメッセージを consumer コンテナが受け取るはず……あれ?
# podman logs mc2-producer
Either the producer has not been started or maybe I cannot access the same memory...
# podman logs mc2-consumer
Either the producer has not been started or maybe I cannot access the same memory...
受信どころか送信さえされていません。ちゃんと意図したコマンドが実行されているんでしょうか。
producer コンテナでは ./ipc -producer
が、 consumer コンテナでは ./ipc -consumer
が実行されているか確かめます。
# podman container ls -a --no-trunc --format "{{.Names}} {{.Command}}"
NAMES COMMAND
82c3852f621c-infra
mc2-producer ./ipc ./ipc -producer
mc2-consumer ./ipc ./ipc -consumer
んん? ./ipc ./ipc -producer
と ./ipc ./ipc -consumer
が実行されてますね……。
command パラメータの指定は command: ["./ipc", "-producer"]
と command: ["./ipc", "-consumer"]
だったにも関わらず ./ipc が 2 回入っています。
もしかして、allingeek/ch6_ipc イメージの ENTRYPOINT ["./ipc"]
が無視されていないんじゃ?
FROM ubuntu:latest
RUN apt-get update && apt-get -y install gcc libc-dev
COPY . /work/ipc
WORKDIR /work/ipc
RUN gcc -o ipc ipc.c -lrt
ENTRYPOINT ["./ipc"]
有り得ます。でもその動きって Kubernetes 的にはどうなんでしょう。
Podman で動作するマニフェストファイルと、 Kubernetes で動作するマニフェストファイルの間で互換性を保つためには、マニフェストファイルの解釈が Podman と Kubernetes で異なっていてはならないはずです。とりあえず Kubernetes において command パラメータがどのように処理されるのか確認してみましょう。
期待していた結果
Kubernetes Documentation には以下の記述があります。
Notes
This table summarizes the field names used by Docker and Kubernetes.
|Description |Docker field name |Kubernetes field name|
|---------------------------------------|-----------------------|---------------------|
|The command run by the container |Entrypoint |command |
|The arguments passed to the command |Cmd |args |
- If you supply a
command
but noargs
for a Container, only the suppliedcommand
is used. The default EntryPoint and the default Cmd defined in the Docker image are ignored.
つまり……
- Kubernetes マニフェストファイルの
command
は dockerfile の Entrypoint に相当し、同じくargs
は Cmd に相当する。 - マニフェストファイルの中に command パラメータが設定されている時、コンテナイメージ側の Entrypoint は無視され command パラメータに設定したコマンドが実行される。
したがって、Podman と Kubernetes の間で マニフェストファイルの互換性を保つのであれば、やはりコンテナで実行されるべきコマンドは ./ipc -producer
と ./ipc -consumer
だったということになります。そもそも、 command
が Entrypoint に相当するのであれば、 podman container ls
の COMMAND 列に command
パラメータで指定したコマンドが含まれるのも、おそらく正しい挙動ではないですね。
更に詳しく調査
inspect
コマンドで作成されたコンテナで実行されているコマンドを調べてみます。
# podman inspect mc2-producer | grep Cmd -A4
"Cmd": [
"./ipc",
"./ipc",
"-producer"
],
Cmd で ./ipc がダブってる……。
では Entrypoint はどうなっているんでしょう。
# podman inspect mc2-producer | grep Entrypoint
"Entrypoint": "./ipc",
!?
……つまりこれコンテナ起動時に ./ipc ./ipc ./ipc -producer
が実行されているってことでしょうか……?
とりあえず回避策
マニフェストファイルを以下の通り編集します。
# removed "./ipc" from command param.
apiVersion: v1
kind: Pod
metadata:
name: mc2v2
spec:
containers:
- name: producer
image: allingeek/ch6_ipc
command: ["-producer"]
- name: consumer
image: allingeek/ch6_ipc
command: ["-consumer"]
restartPolicy: Never
command: 行から ./ipc を削除しました。ここまでの仮説が正しければ、実行コマンドから ./ipc が1個消えるはずです。
それでもあと 1 個余分なのが残るので動かない気はしますが……。
実行結果
予想に反して、これだとプロセス間通信が正常に行われました。
# podman logs mc2v2-Producer
Produced: c6
Produced: 22
Produced: a2
Produced: bf
Produced: 44
# podman logs mc2v2-consumer
Consumed: c6
Consumed: 22
Consumed: a2
Consumed: bf
Consumed: 44
Consumed: done
でもやっぱり Entrypoint と Cmd で ./ipc がダブっています。
# podman inspect mc2v2-producer | grep Cmd -A4
"Cmd": [
"./ipc",
"-producer"
],
# podman inspect mc2-producer | grep Entrypoint
"Entrypoint": "./ipc",
この出力結果と実際の動作に辻褄の合うシナリオを考えるなら……
おそらく Podman version 2.0.2 での podman play kube
は command パラメータについて以下のような動作をしているのではないでしょうか。
- Cmd にイメージ側の ENTRYPOINT と Kubernetes マニフェストの command パラメータを取り込み、最終的なコマンドとして実行する。
- この時、 イメージ側の ENTRYPOINT を無視する。
- しかし ENTRYPOINT のコマンドは結局 Cmd として実行されているため、command パラメータがある場合には本来無視されるべきコマンドが実行されてしまっており、結果 Podman は Kubernetes とは異なる動作をする。
結論「version 2.0.2 未満の Podman に Kubernetes YAML を食わせたかったら command は ENTRYPOINT への追記だと思って使うべし。」
ちなみに RHEL8.2.1 には Podman 1.9.3 がリリースされるそうなので、Kubernetes マニフェストを Podman で使う場合はしばらく留意しておく必要がありそうです。
https://github.com/containers/podman/issues/7007#issuecomment-660174231
その後の話
既知の問題を軽く漁ってみましたが、同様の問題が見つからなかったので issue として投げたところ 1 日経たないうちに修正されてしまいました。すごいですね。
go 言語未履修の自分でも、公式ドキュメント読んでいるだけで OSS 貢献できることはあるんだということが分かったのは嬉しかったです。