Podmanをさわってみる

時間があったので、前々から気になっていたPodmanをさわってみることにしました。
こちらに自分用のメモを残します。

Podmanの話をする前にコンテナがどういう動きをしているかを把握するため、コンテナランタイムについて調べていきます。

# コンテナランタイム
わかりやすかったのは、[こちらの方](https://www.publickey1.jp/blog/20/firecrackergvisorunikernel_container_runtime_meetup_2.html)と[こちらの方](https://qiita.com/cfg17771855/items/0c0f5df26ffcfe18a74d)の記事です。
コンテナランタイムは、コンテナがホストOS上で動くためのあらゆる機能を提供するソフトウェアを指します。
コンテナランタイムには大きく分けて、高レベルランタイムと低レベルランタイムの２つがあり、
高レベルランタイムは、DockerやKubernetesからの命令を受け取り、低レベルランタイムに渡します。
低レベルランタイムは、高レベルランタイムからの命令を受け取り、コンテナの実行・停止などを行います。
コンテナといえばDockerですが、Dockerでは高レベルランタイムはcontainerd、低レベルランタイムはrunCで、ユーザからの入力をdockerdで受けてcontainerd→runcという流れで動いているようです。
（詳細は記事参照）

また、本筋と若干ずれますが、このDockerのコンテナランタイムには問題があり、Dockerの低レベルコンテナランタイムのrunCは、ホストのカーネル上で直接するアプリケーションを実行する仕組みのため、高速で動く代わりに、脆弱性があると危険という問題があります。

そのため、AWSのFirecrackerでは仮装OSをかませたり、GoogleのgVisorではゲストカーネルをかませたりして、直接ホストのカーネル上で実行しないようにしているようです。

なお、このrunCでの脆弱性の対策として、現在は非rootユーザでプロセスが実行されるようになったようです。

# Podmanとは
やっと[Podman](https://docs.podman.io/en/latest/index.html)の話ですが、[こちらの方の記事](https://medium.com/nttlabs/docker-podman-28ced4f7cb90)が非常にわかりやすかったです。
Podmanは、Dockerのdockeredやcontainerdのようなデーモンプロセスは無く、コンテナ度に独立したプロセスを立てるようで、あるコンテナでメモリリークなどの問題が起きても、他コンテナに影響が出にくいようになっています。
dockerコマンドをaliasでpodmanコマンドに変えて使うみたいなことも書いてある通り、立ち位置としては、Dockerと同じなのかなと思います。

# 使ってみる
まずはインストールから。Podman公式の[install](https://podman.io/getting-started/installation)を参考に行います。

## インストールとコンテナ起動
私の環境はMacなので、brewでインストールします。

```bash
$ brew install podman
```

Podmanをインストールしたのち、下記でPodmanを動かすための環境を作ります。
（PodmanはRedhat社が作っており、Macで動かすためにfedoraのVMを作っていました。docker machineコマンドとまんまいっしょですね。Linuxではこの操作はなさそうです。）
`podman machine init`の時にオプションでCPUのコア数も指定できるようです。

```bash
$ podman machine init
$ podman machine start
```

上記のコマンドののち、下記のコマンドでインストールできたことを確認します。

```bash
$ podman info

host:
  arch: amd64
  buildahVersion: 1.23.1
  cgroupControllers:
  - memory
  - pids
  cgroupManager: systemd
  cgroupVersion: v2
  conmon:
    package: conmon-2.0.30-2.fc35.x86_64
    path: /usr/bin/conmon
    version: 'conmon version 2.0.30, commit: '
  cpus: 1
  distribution:
    distribution: fedora
    variant: coreos
    version: "35"
  eventLogger: journald
  hostname: localhost.localdomain
  idMappings:
    gidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
    uidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
  kernel: 5.15.7-200.fc35.x86_64
  linkmode: dynamic
  logDriver: journald
  memFree: 1340944384
  memTotal: 2061385728
  ociRuntime:
    name: crun
    package: crun-1.3-1.fc35.x86_64
    path: /usr/bin/crun
    version: |-
      crun version 1.3
      commit: 8e5757a4e68590326dafe8a8b1b4a584b10a1370
      spec: 1.0.0
      +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +YAJL
  os: linux
  remoteSocket:
    exists: true
    path: /run/user/1000/podman/podman.sock
  security:
    apparmorEnabled: false
    capabilities: CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT
    rootless: true
    seccompEnabled: true
    seccompProfilePath: /usr/share/containers/seccomp.json
    selinuxEnabled: true
  serviceIsRemote: true
  slirp4netns:
    executable: /usr/bin/slirp4netns
    package: slirp4netns-1.1.12-2.fc35.x86_64
    version: |-
      slirp4netns version 1.1.12
      commit: 7a104a101aa3278a2152351a082a6df71f57c9a3
      libslirp: 4.6.1
      SLIRP_CONFIG_VERSION_MAX: 3
      libseccomp: 2.5.3
  swapFree: 0
  swapTotal: 0
  uptime: 9m 44.86s
plugins:
  log:
  - k8s-file
  - none
  - journald
  network:
  - bridge
  - macvlan
  volume:
  - local
registries:
  search:
  - docker.io
store:
  configFile: /var/home/core/.config/containers/storage.conf
  containerStore:
    number: 0
    paused: 0
    running: 0
    stopped: 0
  graphDriverName: overlay
  graphOptions: {}
  graphRoot: /var/home/core/.local/share/containers/storage
  graphStatus:
    Backing Filesystem: xfs
    Native Overlay Diff: "true"
    Supports d_type: "true"
    Using metacopy: "false"
  imageStore:
    number: 0
  runRoot: /run/user/1000/containers
  volumePath: /var/home/core/.local/share/containers/storage/volumes
version:
  APIVersion: 3.4.2
  Built: 1636748737
  BuiltTime: Fri Nov 12 20:25:37 2021
  GitCommit: ""
  GoVersion: go1.16.8
  OsArch: linux/amd64
  Version: 3.4.2

```

出力を見ると、fedora上で動いているっぽいです。
確認のため、さきほど作ったVMの稼働を見ます。コマンドは`podman machine --help`で確認できます。

```bash
$ podman machine list

NAME                     VM TYPE     CREATED         LAST UP            CPUS        MEMORY      DISK SIZE
podman-machine-default*  qemu        18 minutes ago  Currently running  1           2.147GB     10.74GB
```

※VM TYPEのqemuは、仮想化ソフトウェアで、同じレイヤーの他のものではVirtualBoxなどがあります。

Podmanを使うためのVMは起動できていることが確認できたので次にコンテナを立てます。

```bash
# githubからalpine_nginxのDockerfileを取得
$ git clone http://github.com/baude/alpine_nginx && cd alpine_nginx

# ビルド
$ podman build -t alpine_nginx .

# コンテナを立ち上げる
$ podman run -dt -p 9999:80 alpine_nginx

# コンテナの稼働を確認
$ podman ps

CONTAINER ID  IMAGE                          COMMAND               CREATED         STATUS             PORTS                 NAMES
931f3f56c318  localhost/alpine_nginx:latest  nginx -g daemon o...  27 seconds ago  Up 27 seconds ago  0.0.0.0:9999->80/tcp  objective_murdock

# httpサーバにアクセス
$ curl http://localhost:9999

<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
```

コンテナの起動が確認できました。

また、せっかくなので、conmonプロセス（コンテナごとの）が立ち上がっていることを確認します。
コンテナが動いているのはVM上なので、VMに入ってからプロセスを見ます。

```
# sshで接続
$ podman machine ssh podman-machine-default  

$ ps aux | grep conmon

core        6933  0.0  0.1  82076  2440 ?        Ssl  15:00   0:00 /usr/bin/conmon --api-version 1 -c 931f3f56c318ed41001fa92ebccb93aec82934f2308ce2face3ef9ce5878e876 -u 931f3f56c318ed41001fa92ebccb93aec82934f2308ce2face3ef9ce5878e876 -r /usr/bin/crun -b /var/home/core/.local/share/containers/storage/overlay-containers/931f3f56c318ed41001fa92ebccb93aec82934f2308ce2face3ef9ce5878e876/userdata -p /run/user/1000/containers/overlay-containers/931f3f56c318ed41001fa92ebccb93aec82934f2308ce2face3ef9ce5878e876/userdata/pidfile -n objective_murdock --exit-dir /run/user/1000/libpod/tmp/exits --full-attach -s -l journald --log-level info --runtime-arg --log-format=json --runtime-arg --log --runtime-arg=/run/user/1000/containers/overlay-containers/931f3f56c318ed41001fa92ebccb93aec82934f2308ce2face3ef9ce5878e876/userdata/oci-log -t --conmon-pidfile /run/user/1000/containers/overlay-containers/931f3f56c318ed41001fa92ebccb93aec82934f2308ce2face3ef9ce5878e876/userdata/conmon.pid --exit-command /usr/bin/podman --exit-command-arg --root --exit-command-arg /var/home/core/.local/share/containers/storage --exit-command-arg --runroot --exit-command-arg /run/user/1000/containers --exit-command-arg --log-level --exit-command-arg info --exit-command-arg --cgroup-manager --exit-command-arg systemd --exit-command-arg --tmpdir --exit-command-arg /run/user/1000/libpod/tmp --exit-command-arg --runtime --exit-command-arg crun --exit-command-arg --storage-driver --exit-command-arg overlay --exit-command-arg --events-backend --exit-command-arg journald --exit-command-arg container --exit-command-arg cleanup --exit-command-arg 931f3f56c318ed41001fa92ebccb93aec82934f2308ce2face3ef9ce5878e876
```

/usr/bin/conmonというコマンドが実行されていることが確認できました。
このconmonプロセスは、OCI container runtime monitorとあり、コンテナの管理をしているようです。

Podmanのコマンド一覧は[こちら](https://docs.podman.io/en/latest/Commands.html)で見ることができ、dockerコマンドと同じような感じですね。

# 所感
Dockerとほぼ同じ使い勝手のようです。
参考にした記事を見ましたところ、Podmanの利点はデーモン無しというところより、どちらかというと改修のサイクルが早いというところらしいです。
本筋とずれますが、コンテナランタイムについて少し知れたのはよかったです。

# 参考
[コンテナランタイムの仕組みと、Firecracker、gVisor、Unikernelが注目されている理由。 Container Runtime Meetup #2](https://www.publickey1.jp/blog/20/firecrackergvisorunikernel_container_runtime_meetup_2.html)

[コンテナランタイム事情を整理してみる](https://qiita.com/cfg17771855/items/0c0f5df26ffcfe18a74d)

[DockerとPodmanの比較 [Container Runtime Meetup #3]]
(https://medium.com/nttlabs/docker-podman-28ced4f7cb90)

[コンテナでプログラムをrootとして実行することがなぜ問題なのか KubernetsのCVE-2019-11245を例に考える](https://unit42.paloaltonetworks.jp/non-root-containers-kubernetes-cve-2019-11245-care/)