kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Argo CD で kustomize プロジェクトを GitOps 化する

Argo CDkustomize をサポートしているため,簡単に kustomize プロジェクトを GitOps 化できる.さっそく試していく!

argo-cd.readthedocs.io

検証環境

前回書いた記事で使った kustomize プロジェクトを使う. Namespace + Service + Deployment (nginx) という構成で,replicasimageskustomize で変更している.詳しくは以下に置いてある.

github.com

Argo CD Application

今回は Argo CD v2.0.5+4c94d88 を使う.まずは GitHub リポジトリと紐付けるために Argo CD Application を作る.Argo CDWeb UI を使っても作れるし,argocd app create コマンドを使っても作れるけど,今回はマニフェストで宣言をする.以下のように application.yaml を作った.ポイントは大きく2点ある.

  • spec.source.path : overlays/dev など kustomize で作る環境ディレクトリを指定する
  • spec.syncPolicy : automated.selfHeal: true を指定して「自動同期」「自己回復 (Self-Healing)」を有効化する
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: sandbox-kustomize-argocd
  namespace: argocd
spec:
  destination:
    namespace: kakakakakku-dev
    server: https://kubernetes.default.svc
  project: default
  source:
    repoURL: https://github.com/kakakakakku/sandbox-kustomize.git
    path: overlays/dev
    targetRevision: HEAD
  syncPolicy:
    automated:
      selfHeal: true

リソース確認

次に application.yaml を適用すると,Argo CD によって自動的にリソースが作られる.なお,Argo CD には Tool Detection という機能があり自動的にプロジェクトを判別する.具体的には kustomization.yaml / kustomization.yml / Kustomization ファイルを発見した場合に kustomize プロジェクトと判別する.

argoproj.github.io

実際に Argo CD の実装をザッと読むと,ドキュメントと同様に Tool Detectionkustomize build コマンドを実行している感じだった.

github.com

適用後に kubectl コマンドを実行すると NamespaceServiceDeployment が作られている.

$ kubectl get namespaces kakakakakku-dev
NAME              STATUS   AGE
kakakakakku-dev   Active   3m30s

$ kubectl get services -n kakakakakku-dev kakakakakku-dev-nginx
NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kakakakakku-dev-nginx   ClusterIP   10.98.69.104   <none>        80/TCP    3m40s

$ kubectl get deployments -n kakakakakku-dev kakakakakku-dev-nginx
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
kakakakakku-dev-nginx   5/5     5            5           4m20s

Web UI でもリソースを確認できる.

f:id:kakku22:20210819170100p:plain

f:id:kakku22:20210819170414p:plain

レプリカ数を強制的に変更してみる

意図しない操作として kustomization.yaml に設定しているレプリカ数を 7 に変更してみる.一瞬 7 Pods になったけど,直後に Argo CD によって削除されていた.「自己回復 (Self-Healing)」も含めて GitOps を実現できている.Argo CDkustomize の連携は簡単だった!

$ kustomize build overlays/dev | kubectl apply -f -

$ kubectl get pods -n kakakakakku-dev
NAME                                     READY   STATUS    RESTARTS   AGE
kakakakakku-dev-nginx-7c7bbd5d59-2tgww   2/2     Running   0          5s
kakakakakku-dev-nginx-7c7bbd5d59-8rfsj   2/2     Running   0          8m30s
kakakakakku-dev-nginx-7c7bbd5d59-cq4vt   2/2     Running   0          5s
kakakakakku-dev-nginx-7c7bbd5d59-dptvl   2/2     Running   0          8m30s
kakakakakku-dev-nginx-7c7bbd5d59-mb5xs   2/2     Running   0          8m30s
kakakakakku-dev-nginx-7c7bbd5d59-p6w9n   2/2     Running   0          8m30s
kakakakakku-dev-nginx-7c7bbd5d59-wqpm8   2/2     Running   0          8m30s

$ kubectl get pods -n kakakakakku-dev
NAME                                     READY   STATUS    RESTARTS   AGE
kakakakakku-dev-nginx-7c7bbd5d59-8rfsj   2/2     Running   0          8m50s
kakakakakku-dev-nginx-7c7bbd5d59-dptvl   2/2     Running   0          8m50s
kakakakakku-dev-nginx-7c7bbd5d59-mb5xs   2/2     Running   0          8m50s
kakakakakku-dev-nginx-7c7bbd5d59-p6w9n   2/2     Running   0          8m50s
kakakakakku-dev-nginx-7c7bbd5d59-wqpm8   2/2     Running   0          8m50s

Argo CD 側で kustomize の設定を変更する

ドキュメントを読んでいたら Argo CD は以下の kustomize 設定をサポートしていると書いてあったけど,ドキュメントを読むだけでは正直よくわからなかった.試した結果,正しく表現するなら Argo CD Application の設定で一部の kustomize 設定を変更できるという意味だった.

  • namePrefix
  • nameSuffix
  • images
  • commonLabels
  • commonAnnotations

f:id:kakku22:20210830121332p:plain

今回はサンプルとして commonAnnotations を使って Annotation を追加する.Argo CD Application の YAML は以下のようになり spec.source.kustomizecommonAnnotations を設定している.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: sandbox-kustomize-argocd
  namespace: argocd
spec:
  destination:
    namespace: kakakakakku-dev
    server: https://kubernetes.default.svc
  project: default
  source:
    repoURL: https://github.com/kakakakakku/sandbox-kustomize.git
    path: overlays/dev
    targetRevision: HEAD
    kustomize:
      commonAnnotations:
        my-key: my-value
  syncPolicy:
    automated:
      selfHeal: true

Argo CD Application を更新する前後の Annotation は以下のようになった.ちゃんと my-key: my-value が追加されている.どういうときに使うと便利なのか今のところは思い付かないけど,kustomize の一部設定を kustomization.yaml ではなく Argo CD Application で管理したいときに使えそう!

$ kubectl describe pod -n kakakakakku-dev | grep Annotations
Annotations:  <none>
Annotations:  <none>
Annotations:  <none>
Annotations:  <none>
Annotations:  <none>

$ kubectl describe pod -n kakakakakku-dev | grep Annotations
Annotations:  my-key: my-value
Annotations:  my-key: my-value
Annotations:  my-key: my-value
Annotations:  my-key: my-value
Annotations:  my-key: my-value

kustomize で patchesStrategicMerge を使わずに簡単に replicas と images を変更する

今まで kustomize を使って Deploymentreplicas を変更するときなどは kustomization.yamlpatchesStrategicMerge を設定していた.例えば,1年前に kustomize に入門したときに書いた以下の記事でもそういう手順になっている.

kakakakakku.hatenablog.com

最近 kustomize のドキュメントを読み直していたら replicasimages など,簡単に kustomization.yaml に設定できる機能があることに気付いた💡今まで知らなかった!検証も兼ねて試していく.

kubectl.docs.kubernetes.io

kubectl.docs.kubernetes.io

kustomize v4.2.0

なお,今回は kustomize v4.2.0 を使う.kubectl kustomize コマンドだとバンドルされているバージョンが古い可能性があり,正確には kubectl v1.20 までは kustomize v2.0.3 がバンドルされている.詳しくは GitHub に載っている.

$ brew install kustomize
$ kustomize version --short
{kustomize/v4.2.0  2021-07-01T00:44:28+01:00  }

github.com

replicasimages を試す

まず,以下のようなディレクトリ構成で base/overlays/ を準備する.Namespace + Service + Deployment (nginx) という基本構成で,今回は overlays/dev/ に設定している通り dev 環境を kustomize で作る.

.
├── README.md
├── base
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   ├── namespace.yaml
│   └── service.yaml
└── overlays
    └── dev
        └── kustomization.yaml

以下に base/ の代表的なファイルを載せておく.ポイントは base/deployment.yamlreplicas: 2image: nginx:1.20-alpine で,今回は kustomizereplicas: 5image: nginx:1.21-alpine に変更していく.

📁 base/kustomization.yaml

resources:
- namespace.yaml
- deployment.yaml
- service.yaml

📁 base/deployment.yaml

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

次に overlays/dev 環境用の kustomization.yaml を載せる.ポイントは以下の2点となる.

  • replicas : Deployment のレプリカ数を count として設定できる
  • images : Deployment のイメージ名とタグ名を newNamenewTag として設定できる

📁 overlays/dev/kustomization.yaml

resources:
  - ../../base
namespace: kakakakakku-dev
namePrefix: kakakakakku-dev-
commonLabels:
  app: kakakakakku-dev
replicas:
- name: kakakakakku-dev-nginx
  count: 5
images:
  - name: nginx
    newName: nginx
    newTag: 1.21-alpine

さっそく kustomize build overlays/dev コマンドを実行すると,期待通りに replicas: 5image: nginx:1.21-alpine に変更できている💡簡単!これなら patchesStrategicMerge を使わずに kustomization.yaml だけでシンプルに設定できる.

apiVersion: v1
kind: Namespace
metadata:
  labels:
    app: kakakakakku-dev
  name: kakakakakku-dev
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: kakakakakku-dev
  name: kakakakakku-dev-nginx
  namespace: kakakakakku-dev
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: kakakakakku-dev
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: kakakakakku-dev
  name: kakakakakku-dev-nginx
  namespace: kakakakakku-dev
spec:
  replicas: 5
  selector:
    matchLabels:
      app: kakakakakku-dev
  template:
    metadata:
      labels:
        app: kakakakakku-dev
    spec:
      containers:
      - image: nginx:1.21-alpine
        name: nginx

patchesStrategicMerge と組み合わせる

とは言え replicasimages では複雑な設定はできないため,要件によってはやはり patchesStrategicMerge と組み合わせることになる.例えば nginx に加えて fluentbit をサイドカーとして追加する場合,images ではイメージの追加には対応していなかったりする.以下のように overlays/dev/kustomization.yamlpatchesStrategicMerge を追加した.

📁 overlays/dev/kustomization.yaml

resources:
  - ../../base
namespace: kakakakakku-dev
namePrefix: kakakakakku-dev-
commonLabels:
  app: kakakakakku-dev
replicas:
- name: kakakakakku-dev-nginx
  count: 5
images:
  - name: nginx
    newName: nginx
    newTag: 1.21-alpine
patchesStrategicMerge:
  - deployment.yaml

そして overlays/dev/deployment.yaml に追加する fluentbit を差分として設定する.

📁 overlays/dev/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    spec:
      containers:
      - name: fluentbit
        image: fluent/fluent-bit:1.8

もう1度 kustomize build overlays/dev コマンドを実行すると,今度は fluentbit も追加できている.

apiVersion: v1
kind: Namespace
metadata:
  labels:
    app: kakakakakku-dev
  name: kakakakakku-dev
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: kakakakakku-dev
  name: kakakakakku-dev-nginx
  namespace: kakakakakku-dev
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: kakakakakku-dev
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: kakakakakku-dev
  name: kakakakakku-dev-nginx
  namespace: kakakakakku-dev
spec:
  replicas: 5
  selector:
    matchLabels:
      app: kakakakakku-dev
  template:
    metadata:
      labels:
        app: kakakakakku-dev
    spec:
      containers:
      - image: fluent/fluent-bit:1.8
        name: fluentbit
      - image: nginx:1.21-alpine
        name: nginx

まとめ

今まで kustomize で知らなかった replicasimages を使って,patchesStrategicMerge を使わずに簡単にマニフェストを変更できた.要件次第では patchesStrategicMerge と組み合わせることになるだろうけど,基本的な変更なら kustomization.yaml だけで完結できそう.引き続き kustomize も勉強していくぞ!今回検証に使ったプロジェクトは以下で確認できる.

github.com

関連記事

kakakakakku.hatenablog.com

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 をスラスラと書くことができず,サンプルなどをコピーしているため,より複雑なポリシーなどを書けるように勉強を続けていくぞ!