Dockerの置き換えを目指す ルートレス&デーモンレスの Podman 入門

OpenShiftのマニュアルには、dockerコマンドと共に podmanコマンド が利用できるとされている。OpenShift を触る様になるまで、Podmanの存在を知らなかったので、調べたことをメモとして残しておく。


## Podman とは？

このpodmanという名前は、コンテナだけでなく、Kubernetesのポッドの形態でコンテナを起動できることを期待させてくれる。プロジェクトのウェブページから特徴を拾うと次の7つがある。そして、プロジェクトのページの前面には出ていないが、Podmanには、ポッドのマニフェストを生成したり、コンテナを起動する機能もある。**Podmanロゴ**は、可愛いもの路線で、複数のコンテナが内部で動くポッドを実行できることを感じさてくれる。
![podman_logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42829/e93d5a0d-f402-3ae6-8801-5a23b034a193.png)


## Podman の特徴

#### (1) Docker互換のコマンドラインフロントエンドを提供

最も簡単にいうと、`alias docker = podman`　ということだ。つまり、docker コマンドの代わりに、podmanで代用できることを意味する。

#### (2) オープンソースプロジェクトであり、GitHub でコードが公開されている

このプロジェクトは Red Hat によって運営されており、Fedora、Red Hat Enterprise Linuxの最新リリースバージョン、および Ubuntuに対して、ビルドをテストしている。

* ホームページ https://podman.io/
* GitHub https://github.com/containers/libpod

podmanのバンドル状況について、調べたところ、2019年9月現在で、次の表の状況がわかった。問題なく動作するのは Fedora 30 だけで、RHEL8はバージョンが古くルートレスモードが実装されていないなどの問題があった。RHEL7/CentOS7 でも動作するとされているが、ルートレスで動作させることができなかった。  

|Distribution  | Status | Podman version  | Remarks                 |
|:-------------|:------:|:---------------:|:------------------------|
|Fedora 30     | GA     |1.5.1 | sudo なしの rootlessモードで実行可能 |
|RHEL 8.0      | GA     |1.0.5 | rootlessモード未実装 |
|RHEL 7.6      | GA     |1.4.4 | sudo podman で実行  |
|CentOS 7.6    | GA     |1.4.4 | 同上                |
|Ubuntu 18.04  | GA     |1.5.1 | sudo なしの rootlessモードで実行可能 |

このことから、podman は Red Hat がオープンソース・プロジェクトとして、発展途上であると見なされ、dockerコマンドを置き換えるまでに熟成されるには、もう少し時間が必要と考えられる。

#### (3) OCIに準拠するコンテナイメージの開発、管理、および、コンテナとして実行

Docker Hubに登録されたコンテナを実行すること、podmanでビルドして、レジストリに登録したイメージを、dockerコマンドで実行することも可能であり、互換性に問題はないと見られる。

#### (4) デーモンレスのコンテナエンジン

podman は、Dockerデーモンの様な root で動作するデーモンを必要としない。つまり、podman コマンドだけで、デーモンの助けを必要とせずにコンテナを実行できる。


#### (5) コンテナはルートレスモードで実行可能

ルートレスのコンテナは、それらを起動したユーザーよりも多くの特権を持つことはない。もちろん、root権限で実行されるDockerデーモンでも、コンテナが起動したユーザー以上の権限を持つことが無い様に考慮されているが、もしも、悪意のあるプログラムに隔離が突破された場合のリスクは大きい。その点で、ルートレスは明白なため安心感がある。

次は、podman で実行した場合のコンテナホスト上のプロセスのリストで、この中でコンテナのプロセスは、PID 21710 とその子プロセスである。 これらのプロセスの USER は、rootではなく、vagrant という一般ユーザーになっている。

~~~console:
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
vagrant  21729  0.0  0.1   4336  2760 pts/0    S    13:21   0:00 /usr/bin/slirp4netns --api-socket /run/user/1000/libpod/tmp/35de2a668cad2
vagrant  21710  0.0  0.0  80348  2012 ?        Ssl  13:21   0:00 /usr/libexec/podman/conmon --api-version 1 -c 35de2a668cad28fa366c233a641
vagrant  21721  0.0  0.1  11688  2624 ?        Ss   13:21   0:00  \_ bash -c while true; do /usr/bin/nc -l -p 3306 < /srv/hello.txt; done
vagrant  21740  0.0  0.1  43988  3364 ?        S    13:21   0:00      \_ /usr/bin/nc -l -p 3306
vagrant  21707  0.0  0.1   5756  3208 ?        Ss   13:21   0:00 /usr/bin/fuse-overlayfs -o lowerdir=/home/vagrant/.local/share/containers
vagrant  21478  0.0  1.8  80508 37540 ?        S    13:19   0:00 podman
~~~

対比のために、Docker で同じコンテナを実行した場合のコンテナホスト上のプロセスのリストである。コンテナは PID番号 5339 のプロセスとその子プロセスですが、USER は全て root になっている。

~~~console:
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root      5339  0.0  1.8 880444 37108 ?        Ssl  13:46   0:00 /usr/bin/containerd
root      7684  0.0  0.2  10744  4724 ?        Sl   13:49   0:00  \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.conta
root      7707  0.2  0.1  11692  2560 ?        Ss   13:49   0:00      \_ bash -c while true; do /usr/bin/nc -l -p 3306 < /srv/hello.txt; d
root      7757  0.0  0.1  43992  3452 ?        S    13:49   0:00          \_ /usr/bin/nc -l -p 3306
root      5701  4.4  4.4 1114504 90648 ?       Ssl  13:46   0:10 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root      7678  0.0  0.1 305136  2964 ?        Sl   13:49   0:00  \_ /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 3306 -co
~~~

Docker 19.03 から rootless のコンテナが実行できる様になったが、podman では、よりシンプルに、デーモンレス ＋ ルートレス である。


#### (6) ほとんどのLinuxプラットフォームで利用できる

パッケージマネージャーでインストールするだけで、簡単に動くかは解らないが、Arch Linux & Manjaro Linux, Fedora, CentOS, Gentoo, MacOS, openSUSE のインストール手順が https://podman.io/getting-started/installation に公開されている。


#### (7) Kubernetes の Pod を扱える

前面に出されていないが、GitHub の Docs には、マニュアルが整備されていて、その中にはポッドに関するものが多数ある。
ポッド関連のサブコマンドのリストは、https://github.com/containers/libpod/blob/master/docs/podman-pod.1.md でみることができる。


## Podmanの今後の計画（ロードマップ)

podmanは、バージョン 1.0 に到達しているものの、更なる開発を計画されている。

* Podman CLIが Varlinkバックエンドを使用して、MacおよびWindowsから、リモートPodmanインスタンスに接続を実装
* libpodをCRI-Oに統合して、既存のコンテナー管理バックエンドを置き換え
* podman podコマンドのさらなる機能追加
* ルートレスコンテナのさらなる改善

https://github.com/containers/libpod


## Podman の Pod 関連の機能を試してみる

Podman のドキュメントを見ると、Kubernetes の pod のマニフェスト生成機能、 podマニフェストからpodの起動機能がある。https://github.com/containers/libpod/tree/master/docs

これは、試してみたくなるので、好奇心のままに、少し遊んでみる。
まずは、普通にnginxのコンテナを起動する。このコンテナから Kubernetesのマニフェストを生成してみたい。

~~~
[vagrant@fedora30 ~]$ podman run -d -p 8080:80 nginx
Trying to pull docker.io/library/nginx...
Getting image source signatures
Copying blob b8f262c62ec6 done
Copying blob 22a7aa8442bf done
Copying blob a6639d774c21 done
Copying config ab56bba913 done
Writing manifest to image destination
Storing signatures
e65c774464616011c27d419eb15ef38a7f95b9807bf19e81de926a2bb48bf18c

[vagrant@fedora30 ~]$ podman ps
CONTAINER ID  IMAGE                                                                  COMMAND               CREATED        STATUS            PORTS                   NAMES
e65c77446461  docker.io/library/nginx:latest                                         nginx -g daemon o...  5 seconds ago  Up 4 seconds ago  0.0.0.0:8080->80/tcp    elated_taussig

[vagrant@fedora30 ~]$ curl http://localhost:8080/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
~~~


## podmanコマンド で コンテナからポッドのマニフェストを生成

実行中のコンテナの名前、または、CONTIAINER ID を指定して、YAML形式のマニフェストを生成できる。

~~~console:
vagrant@fedora30 ~]$ podman ps
CONTAINER ID  IMAGE                           COMMAND               CREATED        STATUS            PORTS                 NAMES
e65c77446461  docker.io/library/nginx:latest  nginx -g daemon o...  6 minutes ago  Up 6 minutes ago  0.0.0.0:8080->80/tcp  elated_taussig

[vagrant@fedora30 ~]$ podman generate kube elated_taussig
# Generation of Kubernetes YAML is still under development!
#
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-1.5.1
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2019-09-14T18:00:49Z"
  labels:
    app: elatedtaussig
  name: elatedtaussig
spec:
  containers:
  - command:
    - nginx
    - -g
    - daemon off;
    env:
    - name: PATH
      value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    - name: TERM
      value: xterm
    - name: HOSTNAME
    - name: container
      value: podman
    - name: NGINX_VERSION
      value: 1.17.3
    - name: NJS_VERSION
      value: 0.3.5
    - name: PKG_RELEASE
      value: 1~buster
    image: docker.io/library/nginx:latest
    name: elatedtaussig
    ports:
    - containerPort: 80
      hostPort: 8080
      protocol: TCP
    resources: {}
    securityContext:
      allowPrivilegeEscalation: true
      capabilities: {}
      privileged: false
      readOnlyRootFilesystem: false
    workingDir: /
status: {}
~~~

## Kubernetesマニフェストから、コンテナを実行

マニフェストを生成して、既存のコンテナを停止させる。

~~~console:
[vagrant@fedora30 ~]$ podman generate kube elated_taussig > pod.yaml

[vagrant@fedora30 ~]$ podman kill elated_taussig
e65c774464616011c27d419eb15ef38a7f95b9807bf19e81de926a2bb48bf18c

[vagrant@fedora30 ~]$ podman ps
CONTAINER ID  IMAGE  COMMAND  CREATED  STATUS  PORTS  NAMES
~~~

生成されたマニフェストを指定して、コンテナを実行する

~~~console:
[vagrant@fedora30 ~]$ podman play kube pod.yaml
a container exists with the same name (elatedtaussig) as the pod in your YAML file; changing pod name to elatedtaussig_pod
Pod:
475a420c00e777182de6e294f83d68e48e646bc3c3b8912ebdcf8aa19f014ec7
Container:
adbb48def6a8ed85db124f54e5b7d249006f6b1bf57fe303b5b8fa444d882fde
~~~

以下の様に、コンテナとして起動していることを確認できる。

~~~console:
[vagrant@fedora30 ~]$ podman ps
CONTAINER ID  IMAGE                           COMMAND               CREATED         STATUS             PORTS                 NAMES
adbb48def6a8  docker.io/library/nginx:latest  nginx -g daemon o...  25 seconds ago  Up 25 seconds ago  0.0.0.0:8080->80/tcp  elatedtaussig
~~~

ポッドとして扱うコマンドも用意されている。

~~~console:
[vagrant@fedora30 ~]$ podman pod ps
POD ID         NAME                STATUS    CREATED          # OF CONTAINERS   INFRA ID
475a420c00e7   elatedtaussig_pod   Running   37 seconds ago   2                 3659edba15a5
~~~

## Podmanは サイドカーを実行できるか？

サイドカーを持つポッドのマニフェストを利用して、podman のロゴが象徴する複数のコンテナを一まとめにして動作させることができるか、確認してみる。
GitHubからサイドカーを持つポッドのマニフェストをクローンして、`podman play kube filename`　で実行してみる。

~~~console:
[vagrant@fedora30 ~]$ git clone https://github.com/takara9/codes_for_lessons
[vagrant@fedora30 ~]$ cd codes_for_lessons/step07/sidecar/
[vagrant@fedora30 sidecar]$ ls
Dockerfile  contents-cloner  webserver.yml
~~~

次は、マニフェストのリストである。二つのコンテナ間は、emptyDir というストレージで連携する。

~~~file:webserver.yml
## サイドカーポッドのサンプル
#
apiVersion: v1
kind: Pod
metadata:
  name: webserver
spec:
  containers:          ## メイン コンテナ
  - name: nginx
    image: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: contents-vol
      readOnly: true
      
  - name: cloner       ## サイドカー コンテナ
    image: maho/c-cloner:0.1
    env:
    - name: CONTENTS_SOURCE_URL
      value: "https://github.com/takara9/web-contents"
    volumeMounts:
    - mountPath: /data
      name: contents-vol
      
  volumes:             ## 共有ボリューム
  - name: contents-vol
    emptyDir: {}
~~~

次は、`podman play kube webserver.yml` で実行した結果である。 
残念ながら、サイドカーの中のコンテナを連携させるための 共有ボリュームで エラーが発生した。まだ、これからという感じだ。

~~~console:
[vagrant@fedora30 sidecar]$ podman play kube webserver.yml
Error: HostPath is currently the only supported VolumeSource
~~~

おそらく、共有ボリュームを必要としないタイプのサイドカーであれば、問題なく動作するであろう。しかし、その様な例は稀だと思う。


## 感想

Podman は、dockerコマンドを置き換える目的で、開発されている ルートレス、デーモンレスのコンテナ開発と実行環境である。また、その名前が示す様に、Kubernetesのポッドを実行できることも目指している。Red Hat が立ち上げたオープンソースプロジェクトであり、コミッターのリストをみると、Red Hatの社員が牽引している。そのためか、RHELよりも、Fedoraで最新のバージョンが利用できる。 また、様々な Linux Distribution にも対応すると宣言して、業界でよく知られたメジャーな存在となることを目指していることが伺われる。

個人的には、Dockerはコンテナの世界を幅広く世の中に知らしめ、コンテナの流通の仕組みを作るなど、IT業界への多大な貢献があるにも関わらず、どうして、ここまで置き換え様とされるのか、少々不敏な印象を受ける。 Dockerが非常に価値があることの裏返しでもあると思う。

これからのpodman の動向は注視していきたい。


## その他の参考資料
* Red HatがCRI-OとPodmanに投資している理由(英語), https://www.redhat.com/en/blog/why-red-hat-investing-cri-o-and-podman
* Podman and Buildah for Docker users, https://developers.redhat.com/blog/2019/02/21/podman-and-buildah-for-docker-users/