LoginSignup
6
2

More than 3 years have passed since last update.

ええっ!?Podman と Kubernetes では マニフェストファイルの解釈が違うのかい!?(at version 2.0.2)

Last updated at Posted at 2020-07-20

※ 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

ipc.yml
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"] が無視されていないんじゃ?

allingeek/ch6_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 no args for a Container, only the supplied command 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 が実行されているってことでしょうか……?

とりあえず回避策

マニフェストファイルを以下の通り編集します。

ipc2.yml
# 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 貢献できることはあるんだということが分かったのは嬉しかったです。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2