avatar
tkat0.dev
Published on

argocd-image-updaterがLocal Docker Registryで動かなかった

Table of Contents

Summary

argocd-image-updaterは特定の条件下で Local Docker Registry(e.g. 192.168.0.100:32000)の複数のイメージを Application を更新できない問題があります。 この記事は問題の概要と解決策の簡単なメモです。

結論としては、:を含むレジストリ名を使ってはいけません。

もし詳細を知りたい人はコメントで教えて下さい。尚、私は kubernetes を勉強中なので誤りがある可能性があります。

Background

  • 趣味の kubernetes 環境をローカルマシンで動かしている
  • Docker Registry も、ECR や GCR ではなく、ローカルで動かしている
  • アプリケーションの CI には tekton、CD には argocd を利用している
  • argocd で使う manifest の images の更新を自動化するために、argocd-image-updater を使いたかった

Environment

  • kubernetes
    • created by microk8s
    • work on local machine
  • Container Registry
    • Use local registry created by microk8s (microk8s enable registry)
    • work on NodePort of kubernetes like 192.168.0.100:32000 (default configurations)
  • argocd
    • v2.5.5+fc3eaec
  • argocd-image-updater
    • v0.12.1

Issue

2 つ以上の Local Registry のイメージを指定した Kustomize を利用する Application で、1 つのイメージしか更新されない問題があります。 ただしイメージが Local Registry(192.168.0.100.32000)である点に注意してください。

この manifest は、以下の 2 つのイメージを最新に自動更新する設定です。

  • 192.168.0.100:32000/api-server
  • 192.168.0.100:32000/app

CI がイメージを Push すると、argocd-image-updater が自動的に最新のイメージをデプロイすることが期待されます。 argocd-image-updater のログや Event では2つのイメージが期待通り更新されていましたが、結果は 192.168.0.100:32000/app のみ更新されました。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
  annotations:
    argocd-image-updater.argoproj.io/image-list: 192.168.0.100:32000/api-server,192.168.0.100:32000/app

    argocd-image-updater.argoproj.io/force-update: 'true'
    argocd-image-updater.argoproj.io/update-strategy: latest
    argocd-image-updater.argoproj.io/write-back-method: git
    argocd-image-updater.argoproj.io/git-branch: main
    argocd-image-updater.argoproj.io/write-back-target: kustomization
spec:
  project: myapp
  source:
    repoURL: git@github.com:tkat0/xxx.git
    targetRevision: HEAD
    path: kubernetes/myapp/development
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
  destination:
    server: https://kubernetes.default.svc
    namespace: default

Solution

:を含むレジストリ名を使ってはいけません。 そのため、以下の解決案があります。

  • Option1: 192.168.0.101 のようなポートを含まない URI で Local Registry を公開する
    • 今回はこちらを採用
  • Option2: GCR, ECR などを利用する

Option1 では、単純に Local Registry の Service を LoadBalancer にして、192.168.0.101 のようなアドレスで直接アクセスできるようにするだけでも十分だと思いますが、私は Ingress を利用して Local Docker Registry を以下のように設定しました。

  • (required) Ingress で/で Service を公開
  • (required) nginx.ingress.kubernetes.io/proxy-body-size: "0"
  • (optional) host には自分のドメインを設定(cr.cluster.tkat0.dev)
    • 利便性のため(尚、このホストは LAN 内のクラスタを解決するように設定していて、public ではない)
    • https://sslip.io/ のような wild card DNS でも問題ないはず
  • (optional) microk8s の registry の利用を辞め、helm で registry をデプロイ
    • できる限り manifest を Git 管理したいため

以下が Local Registry のマニフェストです。argocd 経由で helm の twuni/docker-registry をインストールし、Ingress を追加しています。(helm chart の設定次第では、すべて Application 内の記述で完結できるかもしれません)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cr
  namespace: ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: 'false'
    # NOTE: 413 Request Entity Too Large:
    # https://github.com/kubernetes/ingress-nginx/issues/4825#issuecomment-721911893
    # https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#custom-max-body-size
    # 0 means no upper bound
    nginx.ingress.kubernetes.io/proxy-body-size: '0'
spec:
  ingressClassName: nginx
  rules:
    - host: cr.cluster.tkat0.dev
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: docker-registry
                port:
                  number: 5000
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: docker-registry
  namespace: argocd
spec:
  project: default
  source:
    chart: docker-registry
    # https://github.com/twuni/docker-registry.helm
    repoURL: https://helm.twun.io
    targetRevision: 2.2.2
    helm:
      releaseName: docker-registry
      parameters:
        - name: service.type
          value: ClusterIP
        - name: garbageCollect.enabled
          value: 'true'
        - name: persistence.size
          value: 20Gi
        - name: ingress.enabled
          value: 'false'
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
  destination:
    server: https://kubernetes.default.svc
    namespace: docker-registry

Cause

原因は、argocd の実装に起因します。

argocd は、Kustomize のイメージを比較するときに最初の:までをイメージの識別子として扱うため、192.168.0.100:32000/api-server と 192.168.0.100:32000/app を同じイメージ(192.168.0.100)として誤って認識します。

具体的には、argocd-image-updater は、func SetKustomizeImage の中で Application を更新しています。 このメソッドが呼ばれるたびに、1 つずつイメージを Application に追加することが期待されます。

しかし、func (*ApplicationSourceKustomize) MergeImagefunc (KustomizeImage) Matchでイメージを追加するのではなく、上書きしていることがわかりました。

なぜなら、前述の通り:までが同じであればを同じイメージだと判断するためです。

一般的には、gcr.io/project/image-1:v1.0 と gcr.io/project/image-2:v1.0 のようなイメージは別物と判断されますが、ポートを含む Local Registry では、この問題が起こります。

レジストリの指定にコロンを含むことは特に production 環境では一般的ではないことは理解できますが、仕様として含んではいけない、という記述は見つけることはできませんでした(もし知っている方がいれば教えて下さい)。 少なくとも、以下では:8080 を含んでも良いように見えます。

https://docs.docker.com/engine/reference/commandline/tag/

The hostname must comply with standard DNS rules, but may not contain underscores. If a hostname is present, it may optionally be followed by a port number in the format :8080

Conclusion

この記事では、私が直面した argocd-image-updater が Local Registry で動かない問題について説明しました。

情報を整理して近日中にコミュニティに報告する予定です。

update(Jan 7): 報告済み https://github.com/argoproj-labs/argocd-image-updater/issues/516