kakakakakku blog

Weekly Tech Blog: Keep on Learning!

textlint-rule-prh で複数のルールをインポートできる imports 構文

textlint「表記揺れ」を検出するときに prh (proofreading-helper) を組み合わせて使うと思う.例えば .textlintrcrules.prh.rulePaths を以下のように書くと,ルールを複数設定できる.長くなりがちなルールを分割できるため,よく使っている👌

{
    "rules": {
        "prh": {
            "rulePaths" :["./prh-rules/rule1.yml", "./prh-rules/rule2.yml"]
        }
    }
}

最近 textlint-rule-prhREADME.md を読み直していたら,imports を書けばインポートできることを知った💡

github.com

例えば prh-rules/prh.yml のようなルール設定に imports を書くと rule1.ymlrule2.yml をインポートできる.

version: 1

imports:
  - rule1.yml
  - rule2.yml

最終的に以下のような構成になる.実際に試したら期待通りに検出できた!

$ tree
.
├── .textlintrc
└── prh-rules
    ├── prh.yml
    ├── rule1.yml
    └── rule2.yml

まとめると textlint-rule-prh でルールを複数設定するときは .textlintrc に設定することもできるし,ルール設定に imports を書くこともできる.個人的な使い分けとしては「異なるコンテキスト(目的)のルールを複数設定するなら .textlintrc「特定のコンテキスト(目的)のルールを管理上分割するならルール設定に imports を書く」で,実際には併用すると良さそう.実際に併用して試してみるぞ!

Dockerfile で新しく使えるようになった構文「ヒアドキュメント」で複数行の RUN をシュッと書く

2021年7月30日に Docker Blog に公開された以下の記事を参考にDockerfile で新しく使えるようになった構文 "heredocs"」を試す.Dockerfile「ヒアドキュメント」を使うと,今まで RUN&& \ を組み合わせて複数コマンドを 1 レイヤーにまとめていた Tips を使わずにシュッと書けるようになる.なんと!

www.docker.com

なお RUN&& \ を組み合わせる Tips は Intro Guide to Dockerfile Best Practices「Tip #3: Identify cacheable units such as apt-get update & install」として紹介されていたり,とてもよく知られていると思う.

www.docker.com

検証用 Dockerfile

今回は以下のように3種類の検証用 Dockerfile を作った.あくまで検証として Amazon Linux 2 をベースに yum コマンドで適当にパッケージをインストールをしている.ヒアドキュメントを使うと 3-heredocs/Dockerfile のようにシュッと Dockerfile を書けるようになる.大きく差はなく見えるかもしれないけど,可読性は高くなるし,今まで \ を書き忘れるとエラーになっていたし,とても便利だと思う!

  • 1-base/Dockerfile : コマンドごとに RUN を書く
  • 2-backslash/Dockerfile : RUN&& \ を組み合わせる
  • 3-heredocs/Dockerfile : ヒアドキュメントを使う
$ tree .
.
├── 1-base
│   └── Dockerfile
├── 2-backslash
│   └── Dockerfile
└── 3-heredocs
    └── Dockerfile

3 directories, 3 files

🐳 1-base/Dockerfile

FROM amazonlinux:2

RUN yum update -y
RUN yum install -y git
RUN yum install -y tree
RUN yum install -y jq
RUN yum install -y wget
RUN rm -rf /var/cache/yum

🐳 2-backslash/Dockerfile

FROM amazonlinux:2

RUN yum update -y && \
    yum install -y git && \
    yum install -y tree && \
    yum install -y jq && \
    yum install -y wget && \
    rm -rf /var/cache/yum

🐳 3-heredocs/Dockerfile

# syntax = docker/dockerfile:1.3-labs
FROM amazonlinux:2

RUN <<EOF
  yum update -y
  yum install -y git
  yum install -y tree
  yum install -y jq
  yum install -y wget
  rm -rf /var/cache/yum
EOF

BuildKit を使ってビルドをする

「ヒアドキュメント」を使った Dockerfile をビルドするためには BuildKit を使う必要がある.以下のように DOCKER_BUILDKIT=1 環境変数を有効化して docker build コマンドを実行したり,docker buildx build コマンドを実行する.またデフォルトで BuildKit を有効化するなら /etc/docker/daemon.json に設定を追加することもできる.詳しくは以下のサイトに載っている!

$ DOCKER_BUILDKIT=1 docker build .
$ docker buildx build .

docs.docker.com

実際にビルドをすると 2-backslash3-heredocsRUN&& \ を組み合わせなくても同じイメージサイズになっていた.docker history コマンドの結果も参考までに載せておく.レイヤー数もまとまっている!

$ docker build -t my-image:1-base 1-base/.

$ docker build -t my-image:2-backslash 2-backslash/.

$ DOCKER_BUILDKIT=1 docker build -t my-image:3-heredocs 3-heredocs/.

$ docker images my-image
REPOSITORY   TAG           IMAGE ID       CREATED             SIZE
my-image     1-base        16997f0cd75b   About an hour ago   1.62GB
my-image     2-backslash   de739b8af746   About an hour ago   330MB
my-image     3-heredocs    c0ec07c5bc12   About an hour ago   330MB

$ docker history my-image:2-backslash
IMAGE          CREATED             CREATED BY                                      SIZE      COMMENT
de739b8af746   About an hour ago   /bin/sh -c yum update -y &&     yum install …   167MB
d85ab0980c91   5 days ago          /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>      5 days ago          /bin/sh -c #(nop) ADD file:4cbe5850096b1ae39…   163MB

$ docker history my-image:3-heredocs
IMAGE            CREATED             CREATED BY   SIZE      COMMENT
c0ec07c5bc12     About an hour ago   RUN /bin/sh -c   yum update -y
  yum install…   167MB               buildkit.dockerfile.v0
<missing>        5 days ago          /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>        5 days ago          /bin/sh -c #(nop) ADD file:4cbe5850096b1ae39…   163MB

ヒアドキュメントを活用して Dockerfile でファイルを作る

Docker Blog の記事を読んでいたら「ヒアドキュメント」を活用して「インラインファイル」を作れると書いてあった.具体的には以下のように nginx に置く index.html をファイルとして COPY するのではなく,直接 Dockerfile に書くことができる.ようするに「ヒアドキュメント」RUN だけでなく COPY にも使える.過剰に使うと読みにくくなるだろうけど,簡単に試す場面なら良さそう.

# syntax = docker/dockerfile:1.3-labs
FROM nginx:1.21-alpine

COPY <<EOF /usr/share/nginx/html/index.html
  <html>
    <body>
      <h1>kakakakakku blog</h1>
    </body>
  </html>
EOF

まとめ

Dockerfile で新しく使えるようになった構文「ヒアドキュメント」を試した.今まで RUN&& \ を組み合わせて複数コマンドを 1 レイヤーにまとめていた Tips を使わずにシュッと書けて便利💡

関連記事

レイヤーを減らすために docker build コマンドで --squash オプションを使うこともできる.2019年に紹介したけど,現在も「Experimental(実験的機能)」のままだった.参考に載せておく!

kakakakakku.hatenablog.com

Kubernetes と OPA を組み合わせて Deployment の spec.replicas に対して「最低個数」を制限する

前回の記事では Kubernetes と OPA (Open Policy Agent) を組み合わせて「ラベル強制」を試した.OPA を使ってできることをより把握するために,今回は Deploymentspec.replicas に対して「最低個数」を制限できるようにする.なお OPA のインストール手順などは前回の記事を参照してもらえればと!Kubernetes v1.20 を使う.

kakakakakku.hatenablog.com

CRD k8sminreplicas.constraints.gatekeeper.sh を追加する

さっそく「レプリカ最低個数」のために必要な設定を適用する.まず,ConstraintTemplate からレプリカ数を判定する CRD として K8sMinReplicas を作る.強制するためには Rego を使って実装をする必要があり,今回は以下の k8sminreplicas.yaml を使う.簡単に言うと,制限する parameters.min とマニフェストの spec.replicas を比較している.

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8sminreplicas
spec:
  crd:
    spec:
      names:
        kind: K8sMinReplicas
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          properties:
            min:
              type: integer
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sminreplicas
        violation[{"msg": msg, "details": {"missing_replicas": missing}}] {
          provided := input.review.object.spec.replicas
          required := input.parameters.min
          missing := required - provided
          missing > 0
          msg := sprintf("you must provide %v more replicas", [missing])
        }

kubectl apply コマンドで k8sminreplicas.yaml を適用すると,新しく CRD として k8sminreplicas.constraints.gatekeeper.sh が追加された.準備 OK👌

$ kubectl apply -f k8sminreplicas.yaml
constrainttemplate.templates.gatekeeper.sh/k8sminreplicas created

$ kubectl get crds
NAME                                                 CREATED AT
configs.config.gatekeeper.sh                         2021-08-02T05:00:00Z
constraintpodstatuses.status.gatekeeper.sh           2021-08-02T05:00:00Z
constrainttemplatepodstatuses.status.gatekeeper.sh   2021-08-02T05:00:00Z
constrainttemplates.templates.gatekeeper.sh          2021-08-02T05:00:00Z
k8sminreplicas.constraints.gatekeeper.sh             2021-08-02T05:10:00Z

Deploymet で「レプリカ最低個数」を試す

Deploymet「レプリカ最低個数」の制限を試す.以下のように K8sMinReplicas でマニフェストを作る.Rego で実装した通り parameters.min に最低個数 2 を設定している.よって spec.replicas = 1 だとエラーになる.

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sMinReplicas
metadata:
  name: deployment-must-have-min-replicas
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
  parameters:
    min: 2

実際に deployment-must-have-min-replicas.yaml を適用する.

$ kubectl apply -f deployment-must-have-min-replicas.yaml
k8sminreplicas.constraints.gatekeeper.sh/deployment-must-have-min-replicas created

さっそく以下の2種類のマニフェストを適用する.

  • 🆖 my-nginx1.yaml ... spec.replicas = 1
  • 🆗 my-nginx2.yaml ... spec.replicas = 2

📁 my-nginx1.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21-alpine

📁 my-nginx2.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21-alpine

結果として my-nginx1.yaml「レプリカ最低個数」の制限でエラーになる.violation に設定した you must provide 1 more replicas というエラーも出ている.

$ kubectl apply -f my-nginx1.yaml
Error from server ([deployment-must-have-min-replicas] you must provide 1 more replicas): error when creating "nginx.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [deployment-must-have-min-replicas] you must provide 1 more replicas

$ kubectl apply -f my-nginx2.yaml
deployment.apps/my-nginx created

まとめ

前回に引き続き OPA (Open Policy Agent) を試した.Deploymentspec.replicas に対して「最低個数」を制限できるようにした.実践的に使えそうなポリシーだと思う.現状まだ Rego をスラスラと書くことができず,サンプルなどをコピーしているため,より複雑なポリシーなどを書けるように勉強を続けていくぞ!

Kubernetes と OPA を組み合わせて Namespace と Pod の「ラベル強制」を試した

今回は Kubernetes と OPA (Open Policy Agent) を組み合わせて「ラベル強制」を試す.

OPA Gatekeeper をインストールする

まず,Kubernetes クラスターに OPA Gatekeeper をインストールする.今回は OPA のドキュメントに載っている kubectl apply コマンドを使う.他にも Helm を使うこともできる.

open-policy-agent.github.io

kubectl apply コマンドでインストールをすると,多くの Kubernetes オブジェクトが適用される.重要なのは CRD (Custom Resource Definition) で,以下の4種類の CRD が適用されている.

  • configs.config.gatekeeper.shkind: Config
  • constraintpodstatuses.status.gatekeeper.shkind: ConstraintPodStatus
  • constrainttemplatepodstatuses.status.gatekeeper.shkind: ConstraintTemplatePodStatus
  • constrainttemplates.templates.gatekeeper.shkind: ConstraintTemplate
$ kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.5/deploy/gatekeeper.yaml
namespace/gatekeeper-system created
resourcequota/gatekeeper-critical-pods created
customresourcedefinition.apiextensions.k8s.io/configs.config.gatekeeper.sh created
customresourcedefinition.apiextensions.k8s.io/constraintpodstatuses.status.gatekeeper.sh created
customresourcedefinition.apiextensions.k8s.io/constrainttemplatepodstatuses.status.gatekeeper.sh created
customresourcedefinition.apiextensions.k8s.io/constrainttemplates.templates.gatekeeper.sh created
serviceaccount/gatekeeper-admin created
podsecuritypolicy.policy/gatekeeper-admin created
role.rbac.authorization.k8s.io/gatekeeper-manager-role created
clusterrole.rbac.authorization.k8s.io/gatekeeper-manager-role created
rolebinding.rbac.authorization.k8s.io/gatekeeper-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/gatekeeper-manager-rolebinding created
secret/gatekeeper-webhook-server-cert created
service/gatekeeper-webhook-service created
deployment.apps/gatekeeper-audit created
deployment.apps/gatekeeper-controller-manager created
poddisruptionbudget.policy/gatekeeper-controller-manager created
validatingwebhookconfiguration.admissionregistration.k8s.io/gatekeeper-validating-webhook-configuration created

$ kubectl get crds
NAME                                                 CREATED AT
configs.config.gatekeeper.sh                         2021-07-05T07:00:00Z
constraintpodstatuses.status.gatekeeper.sh           2021-07-05T07:00:00Z
constrainttemplatepodstatuses.status.gatekeeper.sh   2021-07-05T07:00:00Z
constrainttemplates.templates.gatekeeper.sh          2021-07-05T07:00:00Z

CRD k8srequiredlabels.constraints.gatekeeper.sh を追加する

さっそく「ラベル強制」のために必要な設定を適用する.まず,ConstraintTemplate から「ラベル強制」をする CRD として K8sRequiredLabels を作る.強制するためには Rego を使って実装をする必要があるけど,詳細は割愛して,今回は OPA の GitHub リポジトリに公開されているサンプルとして,k8srequiredlabels_template.yaml をそのまま使う.

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels
        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("you must provide labels: %v", [missing])
        }

kubectl apply コマンドで k8srequiredlabels_template.yaml を適用すると,新しく CRD として k8srequiredlabels.constraints.gatekeeper.sh が追加された.

$ kubectl apply -f k8srequiredlabels_template.yaml

$ kubectl get crds
NAME                                                 CREATED AT
configs.config.gatekeeper.sh                         2021-07-05T00:00:00Z
constraintpodstatuses.status.gatekeeper.sh           2021-07-05T00:00:00Z
constrainttemplatepodstatuses.status.gatekeeper.sh   2021-07-05T00:00:00Z
constrainttemplates.templates.gatekeeper.sh          2021-07-05T00:00:00Z
k8srequiredlabels.constraints.gatekeeper.sh          2021-07-05T00:01:00Z

Namespace で「ラベル強制」を試す

まず,Namespace「ラベル強制」を試す.以下のように K8sRequiredLabels でマニフェストを作る.ポイントは Namespace に対して app ラベルを強制するため,もし app ラベルが付いていない場合はリソースの作成に失敗する.

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: namespace-app-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["app"]

実際に namespace-app-label.yaml を適用する.

$ kubectl apply -f namespace-app-label.yaml

$ kubectl get k8srequiredlabels.constraints.gatekeeper.sh 
NAME                  AGE
namespace-app-label   30s

さっそく以下の3種類のマニフェストを適用する.

  • 🆖 ns1.yaml ... app ラベルなし
  • 🆖 ns2.yaml ... app ラベルなし(env ラベルあり)
  • 🆗 ns3.yaml ... app ラベルあり(env ラベルあり)

📁 ns1.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: kakakakakku-ns

📁 ns2.yaml

apiVersion: v1
kind: Namespace
metadata:
  labels:
    env: dev
  name: kakakakakku-ns

📁 ns3.yaml

apiVersion: v1
kind: Namespace
metadata:
  labels:
    app: my-app
    env: dev
  name: kakakakakku-ns

結果として ns1.yamlns2.yaml「ラベル強制」の結果エラーになる.you must provide labels: {"app"} というエラーも出ている.

$ kubectl apply -f ns1.yaml
Error from server ([namespace-app-label] you must provide labels: {"app"}): error when creating "ns1.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [namespace-app-label] you must provide labels: {"app"}

$ kubectl apply -f ns2.yaml
Error from server ([namespace-app-label] you must provide labels: {"app"}): error when creating "ns2.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [namespace-app-label] you must provide labels: {"app"}

$ kubectl apply -f ns3.yaml
namespace/kakakakakku-ns created

Pod で「ラベル強制」を試す

次に,Pod「ラベル強制」を試す.同じく K8sRequiredLabels でマニフェストを作る.ポイントは Pod に対して app ラベルを強制するため,もし app ラベルが付いていない場合はリソースの作成に失敗する.

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: pod-app-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    labels: ["app"]

実際に pod-app-label.yaml を適用する.

$ kubectl apply -f pod-app-label.yaml

$ kubectl get k8srequiredlabels.constraints.gatekeeper.sh
NAME                  AGE
namespace-app-label   15m
pod-app-label         30s

さっそく以下の3種類のマニフェストを適用する.

  • 🆖 pod1.yaml ... app ラベルなし
  • 🆖 pod2.yaml ... app ラベルなし(env ラベルあり)
  • 🆗 pod3.yaml ... app ラベルあり(env ラベルあり)

📁 pod1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: kakakakakku-pod
spec:
  containers:
    - name: nginx
      image: nginx:1.21

📁 pod2.yaml

apiVersion: v1
kind: Pod
metadata:
  name: kakakakakku-pod
  labels:
    env: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.21

📁 pod3.yaml

apiVersion: v1
kind: Pod
metadata:
  name: kakakakakku-pod
  labels:
    app: my-app
    env: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.21

結果として pod1.yamlpod2.yaml「ラベル強制」の結果エラーになる.you must provide labels: {"app"} というエラーも出ている.

$ kubectl apply -f pod1.yaml
Error from server ([pod-app-label] you must provide labels: {"app"}): error when creating "pod1.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [pod-app-label] you must provide labels: {"app"}

$ kubectl apply -f pod2.yaml
Error from server ([pod-app-label] you must provide labels: {"app"}): error when creating "pod2.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [pod-app-label] you must provide labels: {"app"}

$ kubectl apply -f pod3.yaml
pod/kakakakakku-pod created

まとめ

今回は Kubernetes と OPA (Open Policy Agent) を組み合わせて「ラベル強制」を試した.NamespacePod に対して,もし app ラベルが付いていない場合はリソースの作成に失敗するようにできた.引き続き OPA を試していくぞ!

docker run コマンドの --pid オプションで PID namespace を共有する

docker run コマンドで使える --pid オプションを試す.Docker では以下のドキュメントに書いてある通り,デフォルトでは PID namespace でコンテナ同士を隔離する.よって,コンテナ同士でプロセスを共有することはできず,各コンテナでは PID 1 を含む「プロセス ID」を再利用できるようになっている.今回は一歩一歩確認しながら進めていく.

docs.docker.com

PID 1 を確認する

まず,デフォルトの挙動を確認する.以下のように docker run コマンドで nginx コンテナを起動する.そして docker exec コマンドでコンテナに接続をしてプロセス一覧を確認すると,nginx プロセスが PID 1 になっていることがわかる.なお,Alpine Linux なので ps コマンドを実行できるようにしておく必要がある.

$ docker run --rm -d -it nginx:1.21
9313d4a4fc73cf2a173569d3f5f76595169c1577b242b9cf7b019afe32785df0

$ docker exec -it 9313d4a4fc73 /bin/sh

# apt update
# apt install -y procps

# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:34 pts/0    00:00:00 nginx: master process nginx -g daemon off;
nginx       32     1  0 01:34 pts/0    00:00:00 nginx: worker process
nginx       33     1  0 01:34 pts/0    00:00:00 nginx: worker process
nginx       34     1  0 01:34 pts/0    00:00:00 nginx: worker process
nginx       35     1  0 01:34 pts/0    00:00:00 nginx: worker process
root        36     0  0 01:35 pts/1    00:00:00 /bin/sh
root       364    36  0 01:36 pts/1    00:00:00 ps -ef

次に docker run コマンドで新しく Ubuntu コンテナを起動する(今回は sleep コマンドでプロセスを常駐させておく).同じく docker exec コマンドでコンテナに接続をしてプロセス一覧を確認すると,sleep プロセスが PID 1 になっていることがわかる.よって,nginxUbuntu もコンテナ同士で隔離されていると言える.

$ docker run --rm -d -it ubuntu:21.10 /bin/sh -c 'sleep 300'
5c754ed1f167ece55849ac5760d902782f562b6815c2fff05dc472c1cb54bc35

$ docker exec -it 5c754ed1f167 /bin/sh

# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:31 pts/0    00:00:00 /bin/sh -c sleep 300
root         8     1  0 01:31 pts/0    00:00:00 sleep 100
root         9     0  0 01:32 pts/1    00:00:00 /bin/sh
root        17     9  0 01:32 pts/1    00:00:00 ps -ef

--pid オプション

次に docker run コマンドの --pid オプションを試す.既に起動している nginx コンテナ(9313d4a4fc73)の PID namespace を共有した Ubuntu コンテナを起動するために --pid=container:9313d4a4fc73 のようにオプションを指定する.

$ docker run --rm -d --pid=container:9313d4a4fc73 -it ubuntu:21.10 /bin/sh -c 'sleep 300'

同じく docker exec コマンドで Ubuntu に接続をしてプロセス一覧を確認すると,nginx コンテナのプロセスを確認できるようになった.そして Ubuntu コンテナの sleep プロセスが PID 372 となり,PID 1 ではなくなっている.よって,コンテナ同士で PID namespace を共有できていると言える.

$ docker exec -it 06bbc0fb87f3 /bin/sh

# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:34 pts/0    00:00:00 nginx: master process nginx -g daemon off;
101         32     1  0 01:34 pts/0    00:00:00 nginx: worker process
101         33     1  0 01:34 pts/0    00:00:00 nginx: worker process
101         34     1  0 01:34 pts/0    00:00:00 nginx: worker process
101         35     1  0 01:34 pts/0    00:00:00 nginx: worker process
root       372     0  0 02:03 pts/0    00:00:00 /bin/sh -c sleep 300
root       379   372  0 02:03 pts/0    00:00:00 sleep 300
root       380     0  0 02:04 pts/1    00:00:00 /bin/sh
root       386   380  0 02:04 pts/1    00:00:00 ps -ef

--pid オプションの活用例

では --pid オプションをどのように活用すれば良いかと言うと「サイドカー」のように使える.書籍「分散システムデザインパターン」「2章 : サイドカー」brendanburns/topz イメージを使った例が載っているので,参考にしながら試していく.

既に起動している nginx コンテナ(9313d4a4fc73)の PID namespace を共有した topz コンテナを起動する.そして http://localhost:8080/topz にリクエストを送ると,nginx コンテナのプロセス一覧とリソース使用量を確認できる.このように「プロセスのモニタリング機能」--pid オプションで「サイドカー」として追加できるという活用に繋がる.

$ docker run --rm -d --pid=container:9313d4a4fc73 -p 8080:8080 brendanburns/topz:db0fa58 /server --addr=0.0.0.0:8080

$ curl http://localhost:8080/topz
1  0 0.21145721 nginx: master process nginx -g daemon off;
32 0 0.11457208 nginx: worker process
33 0 0.11457208 nginx: worker process
34 0 0.11457208 nginx: worker process
35 0 0.11457208 nginx: worker process
36 0 0.17175984 /server --addr=0.0.0.0:8080

Docker Compose と pid オプション

今度は Docker Compose の場合にどうなるんだろう?と気になるため PID namespace を確認する.以下のように nginx コンテナと Ubuntu コンテナを含めた docker-compose.yml を作る.

version: '3.9'
services:
  nginx:
    image: nginx:1.21
  ubuntu:
    image: ubuntu:21.10
    command: sleep 300

そして docker compose up コマンドでコンテナを起動して docker compose exec コマンドで Ubuntu コンテナに接続をしてプロセス一覧を確認すると sleep プロセスが PID 1 になっている.よって,デフォルトでは PID namespace でコンテナ同士を隔離していると言える.

なお,docker-compose.ymlpid: "host" を追加すると「ホスト側」PID namespace を共有できる.実際に使う機会は少なそうだけど,ドキュメントに載っていた.そしてコンテナ同士での PID namespace 共有は Compose file version 2 reference のドキュメントには載っていたけど,Compose file version 3 reference では消えていた.

$ docker compose up

$ docker compose exec ubuntu /bin/sh

# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 02:51 ?        00:00:00 sleep 300
root         7     0  0 02:52 pts/0    00:00:00 /bin/sh
root        14     7  0 02:52 pts/0    00:00:00 ps -ef

docs.docker.com

まとめ

docker run コマンドで使える --pid オプションを試した.デフォルトでは PID namespace でコンテナ同士を隔離するけど,--pid オプションで PID namespace を共有できるようになる.そして,書籍「分散システムデザインパターン」にも載っている通り,「サイドカー」のように活用することができる.

関連記事

書籍「分散システムデザインパターン」の書評記事は以下にある.合わせて読んでもらえると!

kakakakakku.hatenablog.com

また docker run コマンドには多くのオプションがある.今月公開した以下の記事では --security-opt オプションを紹介している.

kakakakakku.hatenablog.com