podman で docker-compose の代わりに Kubernetes YAML を使ってみた

create
2021年09月17日
update
2021年09月28日

はじめに

PodmanDocker Compose の代わりに Kubernetes YAML を使うことが推奨されているみたい。
そこで Kubernetes YAML から起動することを試してみた。

追記

Podman v3 では(ルートレスの場合は v3.2.3 以降から) docker-compose に公式対応してるみたい。
よって docker-compose.yml がすでに有るなら、本記事のように Kubernetes YAML への変換は必要なさそう。

Table 1. 環境
App Version

podman

3.0.1

概略

podman コマンドで起動中の Pod から Kubernetes YAML を生成できるため、それを利用する。

  1. 通常通り podman コマンドから Pod を作成。

  2. 作成した Pod から Kubernetes YAML ファイルを生成。

    • この Kubernetes YAML ファイルは修正する必要があるかもしれない。

  3. Kubernetes YAML ファイルから Pod を起動。

概略図

podman による Kubernetes YAML 利用の手順

  1. 通常通り podman コマンドから Pod を作成。

  2. 作成した Pod から Kubernetes YAML ファイルを生成。

    • この Kubernetes YAML ファイルは修正する必要があるかもしれない。

  3. Kubernetes YAML ファイルから Pod を起動。

コマンドで Pod を作成

まずは podmanPod を作成およびコンテナを起動する。

Example 1. Pod を作成してみる例
Pod を作成
podname="web-demo"
podman pod create \
  --name "${podname}" \   (1)
  --hostname "${podname}" \
  --publish 8080:80
1 コンテナ起動時に参照するので、Pod 名を設定しておくことを推奨。
コンテナを Pod に所属させて起動
podman run --rm -d \
  --pod "${podname}" \  (1)
  --name "some-nginx" \
  --expose 80 \
  docker.io/library/nginx:1.21-alpine
1 所属させる Pod を指定。

Kubernetes YAML ファイルを生成

podman generate kube コマンドにより、現在起動中の Pod から Kubernetes YAML ファイルが生成できる。

podman v3.0.1 現在ではまだ開発途中であるため完全な変換はできていない(名前付き Volume のマウントなどは無視される)。
そのため手作業での修正が必要となるかもしれない。
Example 2. podman で Kubernetes YAML を生成する
対象の Pod から YAML ファイルを生成する
podman generate kube --filename demo-pod.yml "${podname}"    (1)
1 Pod から Kubernetes YAML を生成して出力する。
ここでは出力される内容を demo-pod.yml ファイルに保存している。
名前付き Volume をマウントしている場合、その設定は書き出してくれなかったので後述の修正を行う必要がある。

Kubernetes YAML から Pod 起動

上記で生成した Kubernetes YAML から Pod を起動してみる。

Example 3. Kubenters YAML ファイルから Pod 起動
Pod 起動
podman play kube demo-pod.yml
Pod 削除
podman pod rm --force <pod name>
Kubernetes YAML については podman で対応していない書式も多いため、
podman generate kube により生成されたもの以外を使う場合は注意が必要。

Kubernetes YAML についての補足

podman から利用するにあたって必要そうな部分だけ補足。

書式サンプル

Kubernetes YAML ファイルの書式については Kubernetes API ドキュメントとにらめっこするしかなさそう?
元々はユーザー側が記述する予定ではなかったらしいので、つらい。

基本的には podman generate kube で生成するので、ユーザーが追記するのは metadata フィールド部分になるはず。

Example 4. Pod の YAML サンプル
sample-kubernetes.yml
kind: Pod   (1)
metadata:   (2)
  name: demo-nginx  (3)
  creationTimestamp: "2021-09-11T12:00:00Z"
  labels:       (4)
    app: nginx
  annotations:  (5)
    version: "0.1.2"
    update_at: "2021-09-14"
    homepage: "https://example.com"

spec:   (6)
  containers:
  - image: docker.io/library/nginx:1.21-alpine
    name: some-nginx
    ports:
    - containerPort: 80
      hostPort: 8080
      protocol: TCP
1 Podman で利用する場合は Pod にしておけばいい。
2 ObjectMeta 型。
作成する Pod の名前やラベル、アノテーションなどを指定する。
3 作成する Pod の名前を指定する。
4 文字列のキーと値のハッシュ(Map)。
この値を使って検索ができる。
5 文字列のキーと値のハッシュ(Map)。
ラベルと違って検索用途には使えない。
6 Pod の仕様設定。
基本的に podman generate kube からの自動生成にまかせておけばいい。

ラベルとアノテーション

ラベルとアノテーションの違い
ラベル

識別値として、検索やグルーピングに使う任意のハッシュを設定する。

アノテーション

ラベルとして使わない情報なら何でも。
外部ツールがここの情報を利用したりする。

例: Gitブランチ名、ハッシュ値、使用しているライブラリやツール…​ など。

値の型について

metadata.labels および metadata.annotations で許容されるのは平坦なハッシュのみ。
うっかりネストさせてしまうと次のようなエラーが発生する。

wrong-metadata.yml
kind: Pod
metadata:
  name: wrong-metadata
  annotations:
    parent:
      child: "hoge"
spec: # ...
Error!
podman play kube wrong-metadata.yml
Error: unable to read YAML "wrong-metadata.yml" as Kube Pod:
  error unmarshaling JSON:
    json: cannot unmarshal object into Go struct field ObjectMeta.metadata.annotations of type string

生成された Kubernetes YAML ファイルの修正

podman で自動生成したあと、下記のような事柄では手直しが必要かもしれない。

不要な環境変数の削除

podman generate で生成された Kubernetes YAML ファイルには、コンテナに存在するすべての環境変数が含まれている。
つまり、コンテナにあらかじめ定義されている環境変数も含まれる。

このままだとコンテナ運用時のメンテナンスがめんどくさくなるので、不要な環境変数の指定については削除しておく。

名前付き Volume を利用したい

podman play kube でサポートされている VolumehostPathpersistentVolumeClaim の2つのみ(参考)。
このうち persistentVolumeClaim が名前付き Volume に対応する。

Example 5. Kubernetes YAML にて名前付き Volume を利用する書き方
with-named-volume.yml
# Kubernetes YAML
---
kind: Pod
metadata:
  name: demo-nginx
  creationTimestamp: "2021-09-11T12:00:00Z"
  labels:
    app: nginx

spec:
  containers:
  - image: docker.io/library/nginx:1.21-alpine
    name: some-nginx
    ports:
    - containerPort: 80
      hostPort: 8080
      protocol: TCP
    volumeMounts:   (1)
    - mountPath: /usr/share/nginx/html
      name: contents
  volumes:
  - name: contents
    persistentVolumeClaim:    (1) (2)
      claimName: nginx-contents   (3)
1 Pod において persistentVolumeClaimVolume として扱うため、このような書式となる。
2 podman において persistentVolumeClaim 型が名前付き Volume に相当する。
3 名前付き Volume の指定。存在しなければ新規作成される。

別ファイルで値を定義したい

ConfigMap を利用して環境変数に値を注入できる。

ConfigMap は機密性や暗号化の機能をもたないので、パスワードなどの機密情報については Secret を使うべき。
ただ、残念ながら podman play kube では Secret が未実装。
Example 6. ConfigMap ファイルから値を注入する
ConfigMap の例
# ConfigMap
# demo-nginx-configmap1.yml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config1   (1)
data:   (2)
  NGINX_HOST: foobar.com  (3)
  NGINX_PORT: 80          (3)

# demo-nginx-configmap2.yml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config2   (1)
data:   (2)
  template_dir: /etc/nginx/templates
1 必須: 参照されるときに使われるキー。
2 参照される値。文字列のキーと値のハッシュ(Map)を格納。
3 環境変数として指定。後述する envFrom から参照されることを想定している。
demo-nginx.yml
# Kubernetes YAML
---
kind: Pod
metadata:
  name: demo-nginx
  creationTimestamp: "2021-09-11T12:00:00Z"
  labels:
    app: nginx
spec:
  containers:
  - image: docker.io/library/nginx:1.21-alpine
    name: some-nginx
    env:
    - name: NGINX_ENVSUBST_TEMPLATE_DIR
      valueFrom:  (1)
        configMapKeyRef:
          name: nginx-config2 (2)
          key: template_dir   (3)
    envFrom:  (4)
    - configMapRef:
        name: nginx-config1
    ports:
    - containerPort: 80
      hostPort: 8080
      protocol: TCP
1 環境変数の値について ConfigMap を参照する。
2 参照したい ConfigMap ファイルの metadata.name を指定。
3 参照する data のキーを指定。
4 ConfigMapdata をすべて環境変数として取り込みたい場合は envFrom を利用する。
ConfigMapを利用して起動
podman play kube demo-nginx.yml \
  --configmap demo-nginx-configmap1.yml \   (1)
  --configmap demo-nginx-configmap2.yml     (1)
1 --configmap オプションは複数回指定できる。

おわりに

podmanKubernetes YAML ファイルを自動生成できるので、使うだけなら簡単でうれしい。
名前付き Volume の設定は自動生成されなかったので、そこだけ注意しておく。