DockerからPodmanへの置き換えを調査してみる。

# はじめに

DockerからPodmanへの置き換えの流れがある。
Docker for MacではMac上から透過的にDockerを操作できたり、docker-composeなどで便利な操作ができたのだが、podmanだと現状どうなのか。
当記事ではその辺りをまとめてみる。

# 環境情報

```
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.6
BuildVersion:   19G2021

$ VBoxManage -v
6.0.8r130520

$ vagrant -v
Vagrant 2.2.9
```


# Setup

やることは以下の2つ。

- Podman環境のVMのSetup
- Mac側でのpodman-remoteのSetup

## Podman環境のVMのSetup

VMの構築はboot2podmanとかpodman-machineとかpodman専用のものがあるが、使い慣れたVagrantで作ろうと思う。
まずVagrantfileを用意する。
private networkの固定IPを割り振っておくことがポイント

```ruby:Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "fedora/32-cloud-base"

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "2048"
  end

  config.vm.network :private_network, ip: "192.168.33.11"

end
```

そして起動してlogin

```
$ vagrant up
...

$ vagrant ssh
[vagrant@localhost ~]$
```

vagrant login後はpodmanの設定をしておく。

```
[vagrant@localhost ~]$ sudo dnf -y install podman

[vagrant@localhost ~]$ systemctl --user enable podman.socket
Created symlink /home/vagrant/.config/systemd/user/sockets.target.wants/podman.socket → /usr/lib/systemd/user/podman.socket.

[vagrant@localhost ~]$ sudo loginctl enable-linger $USER

[vagrant@localhost ~]$ systemctl --user status podman.socket
● podman.socket - Podman API Socket
     Loaded: loaded (/usr/lib/systemd/user/podman.socket; enabled; vendor preset: disabled)
     Active: inactive (dead)
   Triggers: ● podman.service
       Docs: man:podman-system-service(1)
     Listen: /run/user/1000/podman/podman.sock (Stream)

[vagrant@localhost ~]$ systemctl --user start podman.socket

[vagrant@localhost ~]$ systemctl --user status podman.socket
● podman.socket - Podman API Socket
     Loaded: loaded (/usr/lib/systemd/user/podman.socket; enabled; vendor preset: disabled)
     Active: active (listening) since Fri 2020-09-11 06:29:56 UTC; 3s ago
   Triggers: ● podman.service
       Docs: man:podman-system-service(1)
     Listen: /run/user/1000/podman/podman.sock (Stream)
     CGroup: /user.slice/user-1000.slice/user@1000.service/podman.socket

Sep 11 06:29:56 localhost.localdomain systemd[2509]: Listening on Podman API Socket.

[vagrant@localhost ~]$ podman --remote info
host:
  arch: amd64
  buildahVersion: 1.15.1
  cgroupVersion: v2
...
```

以上で完了。


## Mac側でpodman remoteのsetup

```
$ brew install podman

$ podman --version
podman version 2.0.6

$ podman system connection add vagrant --identity $HOME/podman_env/.vagrant/machines/default/virtualbox/private_key ssh://vagrant@192.168.33.11/run/user/1000/podman/podman.sock

$ podman system connection list
Name      Identity                                                                      URI
vagrant*  /Users/seijitada/podman_env/.vagrant/machines/default/virtualbox/private_key  ssh://vagrant@192.168.33.11:22/run/user/1000/podman/podman.sock

$ podman info
host:
  arch: amd64
  buildahVersion: 1.15.1
  cgroupVersion: v2
...
```

以上で完了。


# Operation

dockerでよく使っていたのは以下の操作でこれらをpodmanでもいい感じで行いたい。

- docker-compose likeな操作
- Container間の通信
- Volumeの使用
- Imageのbuild
- Container外部から通信

## docker compose likeな操作

podmanでもpodman-composeというdocker-compose likeな非公式ツールがあるが、
podmanに内蔵されているKubernetes yamlを読み込む機能が良さげなのでこれを使う。
Kubernetes yamlは慣れた人なら一から作れそうだが、ここではpodmanから自動生成してみる。

```
$ podman pod create -n pod01
06267e4e35e53ff1c7cf843fd74c617601870a7b82e5eb2404053fdc2f54f1ce

$ podman run --pod pod01 -d --name postgres -e POSTGRES_PASSWORD=password postgres:13
f54a6902a3e0d7b62ec3a1b4c92f637b14c2ea6dd8fbd8856804e9692b870f50

$ podman pod ls
POD ID        NAME    STATUS   CREATED             # OF CONTAINERS  INFRA ID
06267e4e35e5  pod01   Running  About a minute ago  2                2a9e30d39f67

$ podman ps
CONTAINER ID  IMAGE                          COMMAND   CREATED             STATUS             PORTS   NAMES
2a9e30d39f67  k8s.gcr.io/pause:3.2                     About a minute ago  Up 21 seconds ago          06267e4e35e5-infra
f54a6902a3e0  docker.io/library/postgres:13  postgres  21 seconds ago      Up 20 seconds ago          postgres

$ podman generate kube pod01 > pod01.yaml

$ cat pod01.yaml
# 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-2.0.6
apiVersion: v1
kind: Pod
```

こんな感じでpodman generate kubeコマンドで今動いているpodやcontainerからKubernetes yamlを生成することができる。
続いて生成したKubenetes yamlを読み込ませてみる。

```
$ podman pod rm pod01 -f
06267e4e35e53ff1c7cf843fd74c617601870a7b82e5eb2404053fdc2f54f1ce

$ podman pod ls
POD ID  NAME    STATUS  CREATED  # OF CONTAINERS  INFRA ID

$ podman play kube pod01.yaml
Pod:
4203ed53e966d6c0f8c2ae8008efe9fb9cc42fb73d9a1b06831de664cdfa5843
Container:
f62bf71b3f8ea19b0c2084bf21cd2d7dbfa9f18097d14c213a49fb9c562d3b6e

$ podman pod ls
POD ID        NAME    STATUS   CREATED         # OF CONTAINERS  INFRA ID
4203ed53e966  pod01   Running  11 seconds ago  2                4058387dce44

$ podman ps
CONTAINER ID  IMAGE                 COMMAND  CREATED         STATUS             PORTS   NAMES
4058387dce44  k8s.gcr.io/pause:3.2           22 seconds ago  Up 21 seconds ago          4203ed53e966-infra

$ podman ps -a
CONTAINER ID  IMAGE                          COMMAND   CREATED         STATUS                     PORTS   NAMES
4058387dce44  k8s.gcr.io/pause:3.2                     27 seconds ago  Up 26 seconds ago                  4203ed53e966-infra
f62bf71b3f8e  docker.io/library/postgres:13  postgres  27 seconds ago  Exited (1) 26 seconds ago          pod01-postgres
```

podman play kubeコマンドでKubernetes yamlを使うことができるのだが、何やらpostgresが正しく動いていない。
logを見てみるとなにやらpostgresをrootであげようとしているようだ。

```
$ podman logs pod01-postgres
"root" execution of the PostgreSQL server is not permitted.
The server must be started under an unprivileged user ID to prevent
possible system security compromise.  See the documentation for
more information on how to properly start the server.
```

調べてみると、どうやらKubernetes yamlではImageのEntrypointをspec.containers.commandの指定で上書きする仕様のようだが、
podman kube generateで生成されたKubernetes yamlではこれが指定されてしまっている模様。
したがってここを修正すればok

```
$ cp pod01.yaml pod01.yaml.bk

# vim pod01.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2020-09-11T06:56:41Z"
  labels:
    app: pod01
  name: pod01
spec:
  containers:
  - command:               <---- ここが不要
    - postgres             <---- ここが不要
    env:

$ diff pod01.yaml.bk pod01.yaml
16,18c16
<   - command:
<     - postgres
<     env:
---
>   - env:

$ podman pod rm pod01 -f
4203ed53e966d6c0f8c2ae8008efe9fb9cc42fb73d9a1b06831de664cdfa5843

$ podman play kube pod01.yaml
Pod:
3cd402c2bc67b07fc31f845de84169ba574d8171c263c6a74aeec816fbf0e234
Container:
f6278b9af26d956185618b5424792f240d814cca3ed34d02ca0df63cc2c73fcb

$ podman ps
CONTAINER ID  IMAGE                          COMMAND               CREATED         STATUS             PORTS   NAMES
12f881ec8125  k8s.gcr.io/pause:3.2                                 11 seconds ago  Up 10 seconds ago          3cd402c2bc67-infra
f6278b9af26d  docker.io/library/postgres:13  docker-entrypoint...  10 seconds ago  Up 10 seconds ago          pod01-postgres
```

これでpodman play kubeでkubernetes yamlから起動できることが確認できた。

## Container間の通信

Container間の通信はdocker-composeの場合はnetworkを作成して、container名でのアクセスをできるようにしてくれていた。
podmanの場合はpod(ipc,net,utsの共有)なので、localhostでの通信やinter-process communicationが可能。

## Volumeの使用

docker-composeではbind mountとvolume mountが可能であったが、podman playでは今のところはhostPath(bind mount)のみのサポートのようだ。
hostPathを使用する上では事前にvm側でのpathの作成が必要になることと、SELinuxの関連の指定に注意が必要となる。

```
[vagrant@localhost ~]$ mkdir -p /tmp/pg_data


$ cat pod01.yaml
...
spec:
  containers:
  ...
    securityContext:
      allowPrivilegeEscalation: true
      capabilities: {}
      privileged: false
      readOnlyRootFilesystem: false
      seLinuxOptions:
        type: spc_t              <---- SELinuxでホストアクセスの特権が付与される。
    workingDir: /
    volumeMounts:
    - mountPath: /var/lib/postgresql/data
      name: pg_data
  volumes:
  - name: pg_data
    hostPath:
      path: "/tmp/pg_data"
```

## Imageのbuild

buildに関してはdocker-composeのようにはできないので、podmanコマンドから行う。

```
$ podman build -t node_app:latest -f Dockerfile ./
```

## Container外部から通信

Kubernetes yaml的にはspec.containers.portsの追加で可能。
PCからはVagrantで設定した固定IP宛でアクセスできる。

```
    ports:
    - containerPort: 5432
      hostPort: 5432
      protocol: TCP
```


# まとめ

Docker互換とはいかないものの、今の段階でも実用上困らないくらいには使うことはできそうだ。
今後のさらなる拡張に期待。


# 参考資料

[Podman remote clients for macOS and Windows](https://www.redhat.com/sysadmin/podman-clients-macos-windows)