DockerとPodman/Skopeo/Buildahは何が違うのか？

# はじめに

コンテナ初学にあたって，Red Hat Enterprise Linux 8 からDockerの代わりに導入されたPodman，Skopeo，Buildahについてまとめています．
コンテナの勉強を始めたい方，DockerからPodmanへ移行しようか悩んでいる方の助けになればと思います．

それぞれの住み分けは次の通り．

| 名称 | 機能 |
|----|----|
| Podman | コンテナとコンテナイメージの管理 |
| Skopeo | イメージ検証，コピー，削除，署名の機能 |
| Buildah | 新しいコンテナイメージの作成 |

# Red Hat がPodmanに移行した背景

Dockerではコンテナランタイムの構造上，デーモンプロセスが全てのサービスを管理するため単一障害点となる可能性が示唆されています．
デーモンプロセスとして常駐する高レベルランタイムに障害が発生した場合，クライアントはレジストリ・イメージ・カーネルと疎通が取れなくなり，高レベルランタイムからの子プロセスにあたる低レベルランタイムとコンテナは停止してしまいます．

![スライド1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997806/330f0079-dcee-1f1e-165e-bdcaf31f7c36.png)

![スライド2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997806/fa7c67a7-3aba-4d44-0df1-46e32776bd1c.png)

これに対してRed Hat社のPodmanではコンテナランタイムにデーモンプロセスを起動しなくても動作し，障害の影響が波及してシステムが停止することがないように変更されました．
また，root権限がなくても実行できるようにしてsudoをばらまかないようにしたり，Kubernetesの利用を補助する機能を追加するなどの変更が加えられました．

![スライド3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997806/123827d4-8dab-6e93-3721-395dead11bb6.png)

ところで，最近KubernetesとDockerの関係性の見直しやDocker Desktopの有償化が話題に上がっていました．

https://jaco.udcp.info/entry/2020/12/03/172843

調べてみるとDockerの代替としてPodmanに行き着く方がいらっしゃるようです．
しかし，これらの影響でDockerが使えなくなるわけではありません．
KubernetesではこれからもDockerは利用できますし，Docker Desktopを使わなくてもWSLで`docker`コマンドを叩くことができます．

Podmanの導入は，Dockerの代替を探す方にとって一つの解決策となり得ますが，そのままDockerを使い続ける選択肢は依然として存在します．
そもそもPodmanはDockerの完全互換というわけでなく，機能的な違いも含んでいるので，あくまでも選択して導入する形になります．
もちろんですが，ドキュメント類はDockerのほうが充実しています．

# 検証環境

| 名称 | バージョン |
|----|----|
| Ubuntu | 20.04LTS |
| Docker | 20.10.8 |
| Docker-compose | 1.29.2 |
| Podman | 3.3.1 |
| Skopeo | 1.3.0 |
| Buildah | 1.21.3 |

Oracle VM VirtualBoxを使ってDocker環境とPodman環境を準備しました．
調べたところPodmanはCentOSに導入する記事が多いのですが，CentOS 8は2021/12に，Cent OS 7は2024/6にサポートが終了することもありUbuntuで検証を行いました．
結論だけ述べると，PodmanやBuildahを使うにはRed Hat系OSを使った方が楽です．
Podman3.0からはDocker-composeがサポートされるようになっているので，Podman環境でも同じバージョンを利用しました．

https://www.redhat.com/sysadmin/podman-docker-compose

# 比較事項
* 導入のしやすさ
* 基本コマンド
* 特有機能
    - ルートレス
    - Skopeoの特徴
    - Buildahの特徴
    - PodmanとDocker-compose
    - Podの管理

## 導入のしやすさ

### Docker Engine & Docker-compose

インストール方法はこちらの公式ページに従います．

https://docs.docker.com/engine/install/ubuntu/

```shell
# Docker Engineのインストール
sudo apt-get update
sudo apt-get install \
  apt-transport-https \
  ca-certificates \
  curl \
  gnupg \
  lsb-release

# curlは必要に応じて-xオプションでプロキシ情報を追加．
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

# Docker-composeのインストール
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

### Podman & Skopeo & Buildah

こちらも公式ページに従ってインストールを進めます．

https://podman.io/getting-started/installation.html

```shell
# Podmanのインストール
sudo apt-get update
sudo apt-get install \
  apt-transport-https \
  ca-certificates \
  curl \
  gnupg \
  lsb-release

# レジストリを追加します．Ubuntu 20.10以降ではaptのレポジトリにPodmanが含まれるので不要になります．
. /etc/os-release
echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get upgrade

sudo apt-get install podman

# Skopeoのインストール
sudo apt-get install skopeo

# Buildahのインストール
sudo apt-get install buildah

# Docker-composeのインストール
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

どちらも公式ページに従えば簡単に導入でき，この2つの間で難易度的な差はほとんどありませんでした．
ただし，```yum```を使っている場合は```container-tools```をインストールして有効化するだけでPodman，Skopeo等の環境を一括で整えられます．

## 基本コマンド
```run```, ```start```, ```images```など基本的なコマンドは```docker```を```podman```と書き換えるだけで使用できます．

```shell
podman pull centos:latest
podman images
> quay.io/centos/centos

podman run -it quay.io/centos/centos
> #
```

Dockerで利用していたイメージもそのまま使えるので，PodmanをインストールしてDockerを停止し，```docker=podman```のエイリアスを設定して利用(sudoは不要)しても特に困ることはありませんでした．

## 特有機能

### ルートレス

Red Hat がPodmanに移行した背景で紹介したように，Podman, Skopeo, Buildahは導入時からroot権限不要(ルートレス)で動作します．
ホームディレクトリのリポジトリ```~/.local/share/containers```を利用することで，誤って削除するなどのユーザ間の干渉がなくなります．
もちろんroot権限付与をしないので，不正操作防止策の一つにもなります．
コンテナ内にコンテナを作成したい場合でもルートレスを選択することができます．
一方で，コンテナホストの特権ポート(1024未満)を介してネットワークサービスに公開できないなど，若干使い勝手に困る場合もあるかもしれません．

Dockerでは，設定作業を行うことでルートレスモードが使用可能になります．
設定手順は以下の通り．

```shell
# 前準備としてuidmapをインストールします．
sudo apt install uidmap

# Ubuntuの場合は，Dockerを止めてインストールを開始します．
sudo systemctl disable --now docker
curl -fsSL https://get.docker.com/rootless | sh
> DOCKER_HOST=unix:///run/user/1000/docker.sock

# インストール時に指定されたDOCKER_HOSTを登録します．
export DOCKER_HOST=unix:///run/user/1000/docker.sock

# 試してみます．
docker run hello-world
> Hello from Docker!
```

DOCKER_HOSTはユーザごとに設定しなければなりません．
永続化させる場合はこちらの記事を参考にできます．

https://qiita.com/yasthon/items/26f943bd5f1b8fab87e0

ルートレスで使いたいならPodmanの方が断然楽だと感じました．

### Skopeoの特徴

SkopeoもPodman同様にデーモンレスで使用できます．
イメージの検証やコピーなどを機能として持ちます．

```skopeo copy```ではコンテナイメージをレジストリ間でコピーが可能です．宛先レジストリへの書き込みに署名が必要であれば```--sign-by=```オプションを使います．
コピー先にはイメージに加えて```manifest.json```と```version```ファイルが作成されます．

```shell
skopeo copy docker://quay.io/skopeo/stable:latest dir://home/user/test
ls ./test
> 42a318...
> ...
> manifest.json
> version
```

```skopeo layers```は現在のディレクトリにlayersから始まるディレクトリを作成してコピーを格納します．
実行しようすると
```DEPRECATED: skopeo layers is deprecated in favor of skopeo copy```
と出るように，現在は非推奨です．

```skopeo inspect```では`docker pull`を行うことなくイメージを検証できます．
つまり余計な通信や削除作業を抑制することができます．
一方で`podman(docker) inspect`はローカルに保存されているイメージのみを対象とするので，Skopeo特有の非常に便利な機能と言えます．

### Buildahの特徴

BuildahもPodman同様にデーモンレスです．
実行環境に最適なコンテナイメージを作成します．
作成されたイメージはOCI準拠の任意のコンテナランタイムで動作します．
公式としてはBuildahでコンテナイメージを作成し，Podmanで管理することを想定しているようです．

実行環境に最適なコンテナイメージとは，スクラッチな状態から必要最小限な設定を含んだコンテナイメージを作成することを指しています．
スクラッチなコンテナイメージとは，空もしくは少数のメタデータのみ保持する新規イメージです．
余計な作業(悪用とか誤操作とか)をさせたくない，イメージはできるだけ軽量にしたいなどの目的で使用されるようです．
(実はDockerfileに`FROM scratch`と記述することでスクラッチイメージを作成できますが...)

WindowsとMacではまだ対応していないなど，現在整備が進められている状態です．

Buildahにはチュートリアルが準備されていますので，そちらをまとめます．

https://github.com/containers/buildah/tree/main/docs/tutorials

#### スクラッチ以外からイメージを作成

`buildah run`はDockerfileにおけるRUNコマンドに相当します．

```shell
# buildah fromによってイメージを取得
buildah from fedora

# シェル変数としておくと後の記述が楽になります．
# デフォルトでコンテナイメージ名をworking-containerがついたものに設定します．
container=$(buildah from fedora)

# 作成されたコンテナイメージを確認
echo $container
> fedora-working-container 

# コンテナイメージにコマンドを追加．並べる場合は -- をつける．
buildah run $container bash -- dnf install java
```

#### スクラッチからイメージを作成

マウント作業はrootで実行しますが，ルートレス環境からは`buildah unshare`コマンドによってrootとして実行されている名前空間でシェルコマンドを実行できるようにします．
`dnf`，`yum`でしか動作確認できていない箇所があります．

```shell
# 空のコンテナイメージを作成
newcontainer=$(buildah from scratch)

# buildah mountによってマウントし，Buildahパッケージをインストールできるようにします．
# scratchmntはオーバーレイイメージのパスを格納
scratchmnt=$(buildah mount $newcontainer)

# ルートレス環境からマウントするには
buildah unshare
scratchmnt=$(buildah mount $newcontainer)

# RPMデータベースを初期化する．追加インストールしたいものは末尾に記載します．
# Red Hat系(dnf, yum)でしか動作確認をできていません．
dnf install --installroot $scratchmnt --releasever 33 bash coreutils --setopt install_weak_deps=false -y

buildah run $newcontainer bash
```

#### Dockerfileからコンテナイメージを作成

Buildahを使う利点はあまりないように思います．

【追記 2021/12/2】
まれに`podman build`が実行できないバグが出ることもあるそうなので，困ったらBuildahを使ってみるのも良いかもしれません．

```shell
# dockerfileを作っておく
vi Dockerfile
> FROM alpine:3.6
> RUN apk update
> CMD ["pwd"]

# コンテナイメージを作成
buildah bud -t test .

# 作成されたイメージを確認
# testのタグが付いたイメージを確認できる
buildah images
> localhost/test
```

#### イメージの実行

イメージの実行は`podman run`であり，`buildah run`ではないことに注意します．

```shell
# イメージの実行
podman run localhost/test

# コンテナの検証
buildah inspect localhost/test
```

#### マウント/アンマウント

作業コンテナのルートファイルシステムをマウントして，ホストから作業を行えるようにします．
ルートレス環境からは`buildah unshare`コマンド実行後に行います．

```shell
# マウントポイントを変更
# シェル変数に設定しておくとコンテナに修正を加える際に楽になる．
mymnt=$(buildah mount test-working-container)

# アンマウント
buildah umount test-working-container
```

### PodmanとDocker-compose

Docker-composeは複数のコンテナを一斉に起動・管理するために利用します．
Red Hatはしばらくの間Docker-composeをサポートしておらず，複数のコンテナの管理はPodなるものに任せていました．
PodmanでもDocker-composeを利用したいサードパーティによってPodman-composeが作成されましたが，これは実はPodに関するコマンドを実行しているにすぎず，動作が不安定と言われていました．
Podman3.0からはAPIが充実し，満を持してDocker-composeがサポートされるようになりました．

#### PodmanでDocker-composeを利用する．

設定を最低限にする場合はroot権限を使って実行することになります．こちらの記事を参考にしました．

https://rheb.hatenablog.com/entry/podman3_docker_compose

```shell
# コンテナ通信のための名前解決を行うdnsnameが含まれたpodman-pluginsをインストール
apt install podman-plugins

# Podman APIソケットを起動
systemctl enable podman.socket
systemctl start podman.socket

# podman.socketで起動したPodman APIソケットをDOCKER_HOST環境変数に登録
export DOCKER_HOST=unix:/run/podman/podman.sock

# docker-compose.ymlの作成
mkdir wordpress && cd wordpress
vi docker-compose.yml
> version: '3.1'
> services:
>   wordpress:
>     image: wordpress
>     restart: always
>     ports:
>       - 8080:80
>     environment:
>       WORDPRESS_DB_HOST: db
>       WORDPRESS_DB_USER: exampleuser
>       WORDPRESS_DB_PASSWORD: examplepass
>       WORDPRESS_DB_NAME: exampledb
>     volumes:
>       - wordpress:/var/www/html
>   db:
>     image: mysql:5.7
>     restart: always
>     environment:
>       MYSQL_DATABASE: exampledb
>       MYSQL_USER: exampleuser
>       MYSQL_PASSWORD: examplepass
>       MYSQL_RANDOM_ROOT_PASSWORD: '1'
>     volumes:
>       - db:/var/lib/mysql
> volumes:
>   wordpress:
>   db:

# docker-composeを実行します．
docker-compose up -d
```

Docker-composeを使うとなると，今度はPodmanのほうが設定を含み若干面倒になってしまいました．

#### PodでDocker-composeの動作を再現する．

(とりあえずPodとは何かを考えず...)
ここではDocker-composeで利用した例を，Podmanで実装されているPodを作成して実行してみます．
複数のコンテナを設定したYAMLファイルを作成し，YAMLから再び起動を試みます．

```shell
# 中身が空のPodを作成
podman pod create --name testPod

# 中身を指定してPodを作成
podman run -d --pod new:testPod --name wordpress -e WORDPRESS_DB_HOST=db \
  -e WORDPRESS_DB_USER=exampleuser -e WORDPRESS_DB_PASSWORD=examplepass \
  -e WORDPRESS_DB_NAME=exampledb -p 8080:80 wordpress

# Podへコンテナの追加
podman run -d --pod testPod --name db -e MYSQL_DATABASE=exampledb \
  -e MYSQL_USER=exampleuser -e MYSQL_PASSWORD=examplepass \
  -e MYSQL_RANDOM_ROOT_PASSWORD='1' mysql:5.7

# Podの検証
podman pod ps
podman pod inspect testPod

# Podの停止
podman pod stop testPod

# YAMLファイルを作成
podman generate kube testPod > testPod.yaml

# Pod削除
podman pod rm testPod

# YAMLファイルからPodおよびコンテナを起動
podman play kube testPod.yaml
```

Docker-composeと似たような取り回しが可能となっています．
ところが実際は，Docker-composeと全く同じ挙動を想定していると失敗することもあります．
より詳細に検討している記事がありましたのでご紹介します．

https://qiita.com/ijiest/items/287bf6ac3eb2853a18fb

### Podの管理

ここでPodとは何だったのかの話に入ります．

PodとはKubernetesにおける複数コンテナをまとめたアプリケーション実行単位の呼称で，それぞれが1つのコンテナとしてサーバのような振る舞いをするものです．
Podmanでは次のようなPodを管理する追加機能を持ちます．

* `podman generate kube`
    - ローカルで起動済みのPodからKubernetes YAMLを作成できます．
* `podman play`
    - ローカルでYAMLからPodおよびコンテナの作成をテストできます．
    - KubernetesまたはOpenShiftで作成されたPodおよびコンテナをローカルで再作成できます．

https://developers.redhat.com/blog/2019/01/29/podman-kubernetes-yaml#

Podmanには今の環境をKubernetes/OpenShiftへ移動する補助のための機能が含まれていました．
今後Kubernetesを利用したいひとが，まず勉強がてら触ってみるなどの用途に向いていると言えます．

PodによるDocker-composeの動作の再現は，上記の機能を流用したものでした．
しかし，Docker-composeを再現したPodが，Kubernetesで想定されているPodの使い方と異なる(例えばAPとDBを一つのPodにまとめたりは本来しない)可能性がある点については注意しなければいけないかもしれません．
厳密にいえば，KubernetesのPodとPodmanのPodは異なります．

https://kubernetes.io/ja/docs/concepts/workloads/pods/pod-overview/

# まとめ

Podmanでは，Dockerからのアーキテクチャ変更に伴ってルートレスを始めとした機能が追加されていました．
SkopeoやBuildahはPodmanのアーキテクチャに即しており，本来は機能分割を目的として作成されたものですが，Dockerを元々利用している方は追加機能としての使用感を得ることができます．
こうして見ると結局Dockerの機能をPodmanにも対応させる形で落ち着くこともあり，Dockerの偉大さを再確認する結果でもありました．
特にBuildahなどは開発段階にあり，非Red Hat系環境でも本格的に導入されていくのはまだ先な印象です．

今回の比較結果をまとめると次のようになります．

#### Dockerを採用する立場

* 現在利用者が多く，わざわざ移行をしなくてもまだまだ使える．
* 日本語でも英語でもドキュメントが豊富なので，とっつきやすくトラブルシューティングしやすい．
* ルートレス機能は設定すれば使える．
* Docker-composeを既に導入済みであったり使い慣れている場合は，Docker採用に分がある．

#### Podman/Skopeo/Buildahを採用する立場

* デーモンレスとしたことで，単一障害点が排除されている．
* 導入時からルートレスなので，セキュリティ設定の面で分がある．
* CIなどでDinD(Docker in Docker)を使いたい場合，代替手段として機能する．
* Red Hat系OSだと特に楽に環境を整えられる．
* Dockerとコマンドがほぼ同じかつイメージの共有もできるので，Docker環境から移行がスムーズ．
* Docker-composeは設定すれば使える．
* Kubernetesをこれから勉強したい人がまず触ってみるのもいいかもしれない．
* Skopeo : 検証機能を担当．pullをしなくてもイメージ検証できるなどの便利機能を備える．
* Buildah : コンテナイメージ作成を担当．デーモンレスでスクラッチコンテナを作成できる．

個人的には，`Skopeo inspect`を使うだけでもSkopeo採用はありかなぁと思いました．
PodmanとDockerのコンテナ起動速度の比較やコンテナ間通信についても今後追加で調査できたらと思います．

# 参考

* [Building a Buildah container image for Kubernetes | buildah.io](https://buildah.io/blogs/2018/03/01/building-buildah-container-image-for-kubernetes.html)

Buildahが作成された経緯が書かれています．

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

Podmanのコンテナ起動はDockerの起動よりも少し遅い傾向，また起動済みコンテナ数が増えるにつれてより遅くなっていく傾向があるという調査記事があります．

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

コンテナランタイムについて詳しくまとめてある記事です．コンテナランタイムはコンテナ操作を行う実態です．高レベルランタイムと低レベルランタイムがあります．

| 分類 | 特徴 |
|----|----|
| 高レベルランタイム(containerd...) | daemonプロセスとして常駐してDockerやKubernetesと対話 <br> コンテナイメージを管理 |
| 低レベルランタイム(runC...) | 高レベルランタイムによって実行される(常駐しない) <br> リソースの隔離やアプリケーションの実行 |

* [コンテナーの実行及び管理 | Red Hat Customer Portal](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/building_running_and_managing_containers/index)

Red Hat Customer Portalに今回まとめた内容がもう少し詳しくまとめられています．ただし，GitHubページに対して内容が少し古かったりします．

* [How to use Podman inside of a container | Red Hat Enable Sysadmin]
(https://www.redhat.com/sysadmin/podman-inside-container)

PINP(Podman in Podman)などのPodmanによるコンテナ内コンテナの実行について説明されています．
ルートレスで実行する方法についても言及されています．

* [DockerユーザーのためのPodmanとBuildahの紹介 | 赤帽エンジニアブログ](https://rheb.hatenablog.com/entry/2020/07/16/podman_buidah_for_docker_users)

Red Hat DeveloperによるPodman，Buildah導入記事が日本語訳されたものです．

* [(翻訳) Kubernetes Podcast from Google: Episode 164 - Podman, with Daniel Walsh and Brent Baude | 赤帽エンジニアブログ]
(https://rheb.hatenablog.com/entry/k8s-podcast-164-podman-jp)

Kubernetes Podcast エピソード164の日本語訳が掲載されています．
Red HatのエンジニアによってPodman/Buildah/Skopeoが開発されていった経緯が語られています．