docker desktop の代わりに windows10 wsl2 で podman をつかう

[docker desktop の有料化に対する猶予期間が終了する、1月31日が近づいてきました。](https://www.docker.com/blog/updating-product-subscriptions/)皆様いかがお過ごしでしょうか。

さて、今日は windows10 の wsl2 環境で docker desktop の代替として podman が使えそうか検証していきたいと思います！

## 用意するもの :briefcase:

wsl2 

```shell
> wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop         Stopped         2
  docker-desktop-data    Stopped         2
```

ubuntu

```shell
❯ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.3 LTS
Release:        20.04
Codename:       focal
```

まだ 18.04 使ってるよーという方が[このあたりを参考に](https://www.nextofwindows.com/how-to-upgrade-existing-wsl-wsl2-ubuntu-18-04-to-20-04)、20.04 にされることをお勧めします。

なお、`sudo do-release-upgrade` コマンドが `Reading state information... Done` の後でfailしてしまう場合は、[こちら](https://askubuntu.com/questions/1344069/cant-upgrade-to-ubuntu-21-04-restoring-original-system-state-aborting)に書かれているように、 `sudo apt-get purge snapd` してあげると進みます（ました）。

## Install :comet:

install と書きましたが、まずは docker desktop を uninstall します。
いつでも戻せるという甘えを断ち切ります。

うおー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221423/2e0a81e4-5507-f3f1-6727-5df6ce188a82.png)

```shell
>wsl -l -v
  NAME      STATE           VERSION
* Ubuntu    Running         2
```
docker いなくなりました :cry:

では、install していきます。
公式Docを見ると下記blogポストがリンクされています。

https://www.redhat.com/sysadmin/podman-windows-wsl2

これによると、install コマンドはこう。

```shell
. /etc/os-release
sudo sh -c "echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/x${NAME}_${VERSION_ID}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list"
wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/x${NAME}_${VERSION_ID}/Release.key -O Release.key
sudo apt-key add - < Release.key
sudo apt-get update -qq
sudo apt-get -qq -y install podman
sudo mkdir -p /etc/containers
echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf
```

ubuntu 20.10 以降だと

```shell
# Ubuntu 20.10 and newer
sudo apt-get -y update
sudo apt-get -y install podman
sudo mkdir -p /etc/containers
echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf
```

でいけんのかな？未確認。

上記の blog によると、構成オプションの変更が必要そうなのですが、

```shell
--cgroup-manager cgroupfs --event-logger file
```

`podman info` してみると、すでに該当の構成で設定されており、必要なさそうです。

```shell
❯ podman info
...
  cgroupManager: cgroupfs
...
  eventLogger: file
...
```

version

```shell
❯ podman version
Version:      3.4.2
API Version:  3.4.2
Go Version:   go1.16.6
Built:        Thu Jan  1 09:00:00 1970
OS/Arch:      linux/amd64
```

## podman run :basketball_player_tone1:

とりあえず run してみます。

```
 ❯ podman run -dt -p 8080:80/tcp httpd
✔ docker.io/library/httpd:latest
Trying to pull docker.io/library/httpd:latest...
Getting image source signatures
Copying blob 0d58b11d2867 done
Copying blob e88da7cb925c done
Copying blob ba1caf8ba86c done
Copying blob eff15d958d66 done
Copying blob ab86dc02235d done
Copying config ad17c88403 done
Writing manifest to image destination
Storing signatures
7ae5b62981563f779be7af3c2210841d70ac513a0941f76ead7fffe7e7f2a350
```

```shell
❯ podman ps
CONTAINER ID  IMAGE                           COMMAND           CREATED         STATUS             PORTS                 NAMES
7ae5b6298156  docker.io/library/httpd:latest  httpd-foreground  17 seconds ago  Up 18 seconds ago  0.0.0.0:8080->80/tcp  jolly_mayer
```

localhost:8080 にアクセスしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221423/7e2bd3e1-c74d-6703-1f63-4a9cc1ae942c.png)

イケてる！

止めます。

```shell 
❯ podman stop jolly_mayer
jolly_mayer
```

```shell
❯ podman ps -a
CONTAINER ID  IMAGE                           COMMAND           CREATED        STATUS
    PORTS                 NAMES
7ae5b6298156  docker.io/library/httpd:latest  httpd-foreground  6 minutes ago  Exited (0) 5 seconds ago  0.0.0.0:8080->80/tcp  jolly_mayer
```

restart してみたり、 rm してみたり。このあたりの使用感は docker と全然変わらないですね。

## volume mount :package:

マウントしてみます。

```shell
echo "test page" > index.html
podman run -d --rm -p 8080:80 -v $PWD:/usr/share/nginx/html --name web nginx
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221423/a861eba7-900a-ec5f-1b37-26ade01d42f9.png)

うむ、普通にできました。

## external network :spider_web:

default 状態の確認。

```shell
❯ podman network ls
NETWORK ID    NAME        VERSION     PLUGINS
2f259bab93aa  podman      0.4.0       bridge,portmap,firewall,tuning
```

ここにネットワークを作ります。

```shell
❯ podman network create ex-network
/etc/cni/net.d/ex-network.conflist
```

できた。

```shell
❯ podman network ls
NETWORK ID    NAME        VERSION     PLUGINS
2f259bab93aa  podman      0.4.0       bridge,portmap,firewall,tuning
34f160cc6cae  ex-network  0.4.0       bridge,portmap,firewall,tuning,dnsname
```

nginx をたててみます。

```shell
❯ podman run -d --rm -p 8080:80 -v $PWD:/usr/share/nginx/html --name web --net ex-network nginx
7759d6c9199f29891b1fed4fc7543e9bf01054698710c41392bedb927e619411
```

busybox の debug コンテナから web コンテナにアクセスしてみます。

```shell
podman run -it --rm --net ex-network busybox sh
Resolved "busybox" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/busybox:latest...
Getting image source signatures
Copying blob e685c5c858e3 done
Copying config 7138284460 done
Writing manifest to image destination
Storing signatures
/ # wget -q -O - web:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
```

問題なくイケてますね！

### docker と podman の network 実装の違いの話

ちなみに、[こちらの記事で](https://medium.com/nttlabs/docker-podman-28ced4f7cb90)述べられている、

> docker network create コマンドと同様に podman network create コマンドでカスタムネットワークを作成できますが、Podman (3.0.0-rc1) では複数のネットワーク間での通信が禁止されていません。

について検証してみました。
こちらの issue ですね。

https://github.com/containers/podman/issues/5805

もう1つ network を作成して、

```shell
❯ podman network create ex-network-alt
/etc/cni/net.d/ex-network-alt.conflist
```

コンテナ名でのアクセスはできませんが、

```shell
❯ podman run -it --rm --net ex-network-alt busybox sh
/ # wget -q -O - web:80
wget: bad address 'web:80'
/ # exit
```

IP だとアクセスできてしまいました。

```shell
 podman run -it --rm --net ex-network-alt busybox sh
/ # wget -q -O - 10.89.0.2:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
```

なるほど。

## k8s yaml :ship:

podman は（公式には）docker-compose をサポートしていません。
その代わり、k8s yaml をサポートしています。
そんな podman の醍醐味（?）である k8s yaml での動作を見てみます。

### container から pod yaml をつくる

まず手始めに、起動している container から k8s pod yaml を生成してみます。
単一の container から pod yaml をつくる方法です。

```shell
❯ podman generate kube web > web-kube.yaml
```

おぉ。見慣れたやつ。

```yaml:web-kube.yaml
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-3.4.2
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2021-11-24T13:49:13Z"
  labels:
    app: web
  name: web
spec:
  containers:
  - args:
    - nginx
    - -g
    - daemon off;
    image: docker.io/library/nginx:latest
    name: web
    ports:
    - containerPort: 80
      hostPort: 8080
    securityContext:
      capabilities:
        drop:
        - CAP_MKNOD
        - CAP_NET_RAW
        - CAP_AUDIT_WRITE
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: root-workspace-host-0
  volumes:
  - hostPath:
      path: /root/workspace
      type: Directory
    name: root-workspace-host-0
```

では次に複数の container から pod yaml を作ってみたいと思います。
が、そのためにまず pod そのものをコマンドで作ります。

空のpodを作るコマンド

```shell
podman pod create --name my-pod
```

お、pod ができている！

```shell
❯ podman pod list
POD ID        NAME        STATUS      CREATED        INFRA ID      # OF CONTAINERS
70976cb582e2  my-pod      Created     4 minutes ago  8bdec75f2026  1
❯ podman ps -a
CONTAINER ID  IMAGE                 COMMAND     CREATED        STATUS      PORTS       NAMES
8bdec75f2026  k8s.gcr.io/pause:3.5              8 minutes ago  Created                 70976cb582e2-infra
```

この pod に対して、次のように、podman run コマンドを `--pod` で実行すると container が pod に割り当てられると言うわけです。
（この時、`--pod new:my-pod2` のように new を付けるとpodの作成もやってくれるらしいです。）


```shell
podman run -d --rm -p 8080:80 -v $PWD:/usr/share/nginx/html --name web --net ex-network --pod my-pod nginx
```

おー container 増えた。

```shell
❯ podman pod ls
POD ID        NAME        STATUS      CREATED         INFRA ID      # OF CONTAINERS
70976cb582e2  my-pod      Running     10 minutes ago  8bdec75f2026  2
❯ podman ps
CONTAINER ID  IMAGE                           COMMAND               CREATED         STATUS             PORTS                 NAMES
8bdec75f2026  k8s.gcr.io/pause:3.5                                  10 minutes ago  Up 33 seconds ago                        70976cb582e2-infra
aef5792650c1  docker.io/library/nginx:latest  nginx -g daemon o...  32 seconds ago  Up 32 seconds ago  0.0.0.0:8080->80/tcp  web
```

例があまりよくないけど、さっきの busybox コンテナも pod に入れて、30s 毎に web container にアクセスするようにしてみます。

```shell
❯ podman run -d --rm --name debug --net ex-network --pod my-pod busybox sh -c "while true; do wget -q -O - web:80; sleep 30; done;"
87cd836c39135dc59bb4bbd3414bd03dc0ec5aa5cf6ef8b475ae2011dcf56562
❯ podman logs debug
test page
test page
```

うむうむ。

さてさて、これらをいよいよ k8s pod yaml に変換します。
コマンドは単一 container の時と同じですが、今度は pod 名で。

```shell
❯ podman generate kube my-pod > my-pod-kube.yaml
```

できてるできてる。

```yaml:my-pod-kube.yaml
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-3.4.2
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2021-11-24T16:12:52Z"
  labels:
    app: my-pod
  name: my-pod
spec:
  containers:
  - args:
    - nginx
    - -g
    - daemon off;
    image: docker.io/library/nginx:latest
    name: web
    resources: {}
    securityContext:
      capabilities:
        drop:
        - CAP_MKNOD
        - CAP_NET_RAW
        - CAP_AUDIT_WRITE
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: root-workspace-host-0
  - command:
    - sh
    - -c
    - while true; do wget -q -O - web:80; sleep 30; done;
    image: docker.io/library/busybox:latest
    name: debug
    resources: {}
    securityContext:
      capabilities:
        drop:
        - CAP_MKNOD
        - CAP_NET_RAW
        - CAP_AUDIT_WRITE
  restartPolicy: Never
  volumes:
  - hostPath:
      path: /root/workspace
      type: Directory
    name: root-workspace-host-0
status: {}
```

ex-networkを指定している要素は yaml には表れてこないですね :thinking: 
必要ないからかな。

というわけで、podman だと、ローカル環境などで（試行錯誤しながら）複数 container によるオーケストレーション環境を作り、それを k8s yaml に吐きだして管理できることが分かりました。

docker-compose には compose yaml を ganerate する仕組みはなかった（気がする）から新鮮 :sparkles:

pod yaml を作ってしまえば、一度 pod も container も消して、

```shell 
❯ podman pod stop my-pod
70976cb582e2758ecf8f3f9900cfc96919fc31b5f40c66edbfb0905a74f4c998
❯ podman pod rm my-pod
70976cb582e2758ecf8f3f9900cfc96919fc31b5f40c66edbfb0905a74f4c998
❯ podman ps -a --pod
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES       POD ID      PODNAME
```

何もない状態から、`podman play` コマンドで、`docker-compose up` のように先ほどの環境が作れます。

```shell
❯ podman play kube my-pod-kube.yaml
Pod:
b9ccabbc6312534340feef2ee4f37b86f306bc0a0e509801ab1f52aec68eefd1
Containers:
89579f9b1b615a6ddab8b86eb7d0c8950fb5e8a4341e0e8976d1956140b34feb
fc1ec0acc05c246c7eff078dd9fe0e90bba100d553c25e129962bbb31aad3a08
❯ podman pod ls
POD ID        NAME        STATUS      CREATED         INFRA ID      # OF CONTAINERS
b9ccabbc6312  my-pod      Degraded    18 seconds ago  732642163dc8  3
❯ podman ps -a --pod
CONTAINER ID  IMAGE                             COMMAND               CREATED         STATUS                     PORTS       NAMES               POD ID        PODNAME
732642163dc8  k8s.gcr.io/pause:3.5                                    22 seconds ago  Up 15 seconds ago                      b9ccabbc6312-infra  b9ccabbc6312  my-pod
89579f9b1b61  docker.io/library/nginx:latest    nginx -g daemon o...  19 seconds ago  Up 15 seconds ago                      my-pod-web          b9ccabbc6312  my-pod
fc1ec0acc05c  docker.io/library/busybox:latest                        15 seconds ago  Exited (0) 12 seconds ago              my-pod-debug        b9ccabbc6312  my-pod

```

と思ったらあれ？

```shell
❯ podman logs my-pod-debug
wget: bad address 'web:80'
```

yamlを少し修正する必要がありそうです。

```yaml
...
spec:
  containers:
  - args:
    - nginx
    - -g
    - daemon off;
    image: docker.io/library/nginx:latest
    ports:                 # ここ追記
      - containerPort: 80  # ここ追記
    name: web
...
  - command:
    - sh
    - -c
    - while true; do wget -q -O - localhost:80; sleep 30; done; # ここ web -> localhost
    image: docker.io/library/busybox:latest
    name: debug
...
```

```shell
❯ podman logs my-pod-debug
test page
test page
test page
```

でたでた。

ちなみに、podman generate command の

```shell
 ❯ podman generate systemd --files --name my-pod
/root/workspace/pod-my-pod.service
/root/workspace/container-my-pod-web.service
/root/workspace/container-my-pod-debug.service
```

を実行すると、systemd 用の unit file が作られて、これを `/usr/lib/systemd/system` などに配置すると daemon のいない podman でもサービスを永続化できるっぽい。
けど今回は docker desktop の代替を目指すので、ここでは触れません。

## resource limit :ghost:

k8s yaml で行けるとなると、contaier 毎の resource request/limit も yaml 通りに設定されるんでしょうか :thinking: 
少し見てみたいと思います。

何も制限を設けず、負荷をかけてみます。
debug コンテナのコマンドを少し修正して、

```shell
sleep 5; while true; do wget -q -O - localhost:80 > /dev/null; done;
```

```shell
❯ podman top my-pod-debug
USER        PID         PPID        %CPU        ELAPSED      TTY         TIME        COMMAND
root        1           0           22.060      36.2640195s  ?           8s          sh -c while true; do wget -q -O - localhost:80 > /dev/null; done;
```

では、cpu に limit を追加します。

```yaml
...
  - command:
    - sh
    - -c
    - sleep 5; while true; do wget -q -O - localhost:80 > /dev/null; done;
    image: docker.io/library/busybox:latest
    name: debug
    resources:
      limits:       # ここだよー
        cpu: "200m" # ここだよー
    securityContext:
      capabilities:
        drop:
        - CAP_MKNOD
        - CAP_NET_RAW
        - CAP_AUDIT_WRITE
...
```

おーだいぶ控えめになりました。

```shell
❯ podman top my-pod-debug
USER        PID         PPID        %CPU        ELAPSED        TTY         TIME        COMMAND
root        1           0           4.034       1m39.1670656s  ?           4s          sh -c sleep 5; while true; do wget -q -O - localhost:80 > /dev/null; done;
root        21940       1           0.000       1.1671397s     ?           0s          wget -q -O - localhost:80
```

## registry login 

`docker login` command と同じでした。

```shell
❯ podman login registry.gitlab.com -u yo_C_ta
Password:
Login Succeeded!
```

## 小ネタ :coffee:

調べてて見つけた小ネタです。

- [コマンド補完の有効化](https://github.com/containers/podman/blob/main/docs/source/markdown/podman-completion.1.md)
- [`podman auto-update` でコンテナー自動更新！すげー](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/building_running_and_managing_containers/proc_auto-updating-containers-using-podman_assembly_porting-containers-to-systemd-using-podman)

## 所感 :xmas-tree:

なんとなく個人の感想ですが、k8s 化を見据えて何らかのサービスを作っている場合には、初期の段階から k8s yaml の構成を考えていけるので、docker で始めるよりも開発者体験はいいのかなーと思いました。

あと、docker desktop の代替検討として考えると、podman は daemonless らしいので何もしていない時に手元の PC リソースを圧迫しないのもいいのかも（程度は未確認）。

podman 自体のリリースサイクルも早いらしいので、今後さらに期待できそうです :heart_eyes:
乗り換え候補としては十分ありだと思いますが、もう少し他も見てみよう :eyes: