From e1641cd3cfae81cb8aa0bfc9c370cb4ea0bddf94 Mon Sep 17 00:00:00 2001 From: Mayne0213 Date: Wed, 7 Jan 2026 14:27:51 +0900 Subject: [PATCH] FEAT(ci): add ArgoCD Image Updater and CI/CD pipelines - ArgoCD Image Updater for Zot registry polling - Tekton Tasks: git-clone, buildah-build-push - Pipelines: nextjs, fastapi, python - ExternalSecrets for Zot and GitHub credentials --- argocd-image-updater/argocd.yaml | 35 +++++++++ argocd-image-updater/helm-values.yaml | 28 ++++++++ argocd-image-updater/kustomization.yaml | 6 ++ argocd-image-updater/manifests/secret.yaml | 27 +++++++ kustomization.yaml | 9 +++ tekton/ci-cd/argocd.yaml | 28 ++++++++ tekton/ci-cd/manifests/kustomization.yaml | 14 ++++ .../manifests/pipelines/fastapi-pipeline.yaml | 59 +++++++++++++++ .../manifests/pipelines/nextjs-pipeline.yaml | 65 +++++++++++++++++ .../manifests/pipelines/python-pipeline.yaml | 59 +++++++++++++++ .../manifests/secrets/github-credentials.yaml | 18 +++++ .../secrets/zot-registry-secret.yaml | 27 +++++++ .../manifests/tasks/buildah-build-push.yaml | 71 +++++++++++++++++++ tekton/ci-cd/manifests/tasks/git-clone.yaml | 57 +++++++++++++++ 14 files changed, 503 insertions(+) create mode 100644 argocd-image-updater/argocd.yaml create mode 100644 argocd-image-updater/helm-values.yaml create mode 100644 argocd-image-updater/kustomization.yaml create mode 100644 argocd-image-updater/manifests/secret.yaml create mode 100644 tekton/ci-cd/argocd.yaml create mode 100644 tekton/ci-cd/manifests/kustomization.yaml create mode 100644 tekton/ci-cd/manifests/pipelines/fastapi-pipeline.yaml create mode 100644 tekton/ci-cd/manifests/pipelines/nextjs-pipeline.yaml create mode 100644 tekton/ci-cd/manifests/pipelines/python-pipeline.yaml create mode 100644 tekton/ci-cd/manifests/secrets/github-credentials.yaml create mode 100644 tekton/ci-cd/manifests/secrets/zot-registry-secret.yaml create mode 100644 tekton/ci-cd/manifests/tasks/buildah-build-push.yaml create mode 100644 tekton/ci-cd/manifests/tasks/git-clone.yaml diff --git a/argocd-image-updater/argocd.yaml b/argocd-image-updater/argocd.yaml new file mode 100644 index 0000000..37f0814 --- /dev/null +++ b/argocd-image-updater/argocd.yaml @@ -0,0 +1,35 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: argocd-image-updater + namespace: argocd +spec: + project: default + sources: + - repoURL: https://argoproj.github.io/argo-helm + chart: argocd-image-updater + targetRevision: 0.11.0 + helm: + valueFiles: + - $values/argocd-image-updater/helm-values.yaml + - repoURL: https://github.com/K3S-HOME/platform.git + targetRevision: main + ref: values + - repoURL: https://github.com/K3S-HOME/platform.git + targetRevision: main + path: argocd-image-updater/manifests + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m diff --git a/argocd-image-updater/helm-values.yaml b/argocd-image-updater/helm-values.yaml new file mode 100644 index 0000000..dbe0596 --- /dev/null +++ b/argocd-image-updater/helm-values.yaml @@ -0,0 +1,28 @@ +# ArgoCD Image Updater Helm Values + +# Configuration for Zot private registry +config: + logLevel: debug + registries: + - name: zot + prefix: zot0213.kro.kr + api_url: https://zot0213.kro.kr + credentials: pullsecret:argocd/zot-registry-credentials + insecure: false + +# Resource limits +resources: + requests: + cpu: 10m + memory: 64Mi + limits: + memory: 256Mi + +# Tolerations for master node +tolerations: + - key: "node-role.kubernetes.io/master" + operator: "Exists" + effect: "NoExecute" + - key: "node-role.kubernetes.io/control-plane" + operator: "Exists" + effect: "NoSchedule" diff --git a/argocd-image-updater/kustomization.yaml b/argocd-image-updater/kustomization.yaml new file mode 100644 index 0000000..86a1e27 --- /dev/null +++ b/argocd-image-updater/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - argocd.yaml + - manifests/secret.yaml diff --git a/argocd-image-updater/manifests/secret.yaml b/argocd-image-updater/manifests/secret.yaml new file mode 100644 index 0000000..80e8283 --- /dev/null +++ b/argocd-image-updater/manifests/secret.yaml @@ -0,0 +1,27 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: zot-registry-credentials + namespace: argocd +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault-backend + target: + name: zot-registry-credentials + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: | + {"auths":{"zot0213.kro.kr":{"username":"{{ .USERNAME }}","password":"{{ .PASSWORD }}","auth":"{{ printf "%s:%s" .USERNAME .PASSWORD | b64enc }}"}}} + data: + - secretKey: USERNAME + remoteRef: + key: zot + property: USERNAME + - secretKey: PASSWORD + remoteRef: + key: zot + property: PASSWORD diff --git a/kustomization.yaml b/kustomization.yaml index 42c2986..9fd0d8a 100644 --- a/kustomization.yaml +++ b/kustomization.yaml @@ -7,5 +7,14 @@ resources: - cert-manager/argocd.yaml - traefik/argocd.yaml + # Tekton CI/CD Platform + - tekton/pipeline/argocd.yaml + - tekton/triggers/argocd.yaml + - tekton/dashboard/argocd.yaml + - tekton/ci-cd/argocd.yaml + + # ArgoCD Image Updater + - argocd-image-updater/argocd.yaml + # ArgoCD configuration (no Application, just configuration) - argocd diff --git a/tekton/ci-cd/argocd.yaml b/tekton/ci-cd/argocd.yaml new file mode 100644 index 0000000..2e98365 --- /dev/null +++ b/tekton/ci-cd/argocd.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: tekton-ci-cd + namespace: argocd + annotations: + argocd.argoproj.io/compare-options: IgnoreExtraneous +spec: + project: default + source: + repoURL: https://github.com/K3S-HOME/platform.git + targetRevision: main + path: tekton/ci-cd/manifests + destination: + server: https://kubernetes.default.svc + namespace: tekton-pipelines + syncPolicy: + automated: + prune: false + selfHeal: true + syncOptions: + - CreateNamespace=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m diff --git a/tekton/ci-cd/manifests/kustomization.yaml b/tekton/ci-cd/manifests/kustomization.yaml new file mode 100644 index 0000000..0d11a8e --- /dev/null +++ b/tekton/ci-cd/manifests/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + # Secrets + - secrets/zot-registry-secret.yaml + - secrets/github-credentials.yaml + # Tasks + - tasks/git-clone.yaml + - tasks/buildah-build-push.yaml + # Pipelines + - pipelines/nextjs-pipeline.yaml + - pipelines/fastapi-pipeline.yaml + - pipelines/python-pipeline.yaml diff --git a/tekton/ci-cd/manifests/pipelines/fastapi-pipeline.yaml b/tekton/ci-cd/manifests/pipelines/fastapi-pipeline.yaml new file mode 100644 index 0000000..f4c6ac6 --- /dev/null +++ b/tekton/ci-cd/manifests/pipelines/fastapi-pipeline.yaml @@ -0,0 +1,59 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: fastapi-build-deploy + namespace: tekton-pipelines +spec: + description: Build FastAPI app and push to Zot registry + params: + - name: git-url + description: Git repository URL + type: string + - name: git-revision + description: Git revision (branch/tag/sha) + type: string + default: main + - name: app-name + description: Application name + type: string + - name: context-dir + description: Docker build context directory + type: string + default: ./fastapi + workspaces: + - name: shared-workspace + description: Shared workspace for all tasks + - name: docker-credentials + description: Docker registry credentials + tasks: + - name: clone + taskRef: + name: git-clone + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.git-revision) + - name: deleteExisting + value: "true" + workspaces: + - name: output + workspace: shared-workspace + + - name: build-push + taskRef: + name: buildah-build-push + runAfter: + - clone + params: + - name: IMAGE + value: zot0213.kro.kr/$(params.app-name):$(params.git-revision) + - name: DOCKERFILE + value: ./Dockerfile + - name: CONTEXT + value: $(params.context-dir) + workspaces: + - name: source + workspace: shared-workspace + - name: dockerconfig + workspace: docker-credentials diff --git a/tekton/ci-cd/manifests/pipelines/nextjs-pipeline.yaml b/tekton/ci-cd/manifests/pipelines/nextjs-pipeline.yaml new file mode 100644 index 0000000..c22f110 --- /dev/null +++ b/tekton/ci-cd/manifests/pipelines/nextjs-pipeline.yaml @@ -0,0 +1,65 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: nextjs-build-deploy + namespace: tekton-pipelines +spec: + description: Build Next.js app and push to Zot registry + params: + - name: git-url + description: Git repository URL + type: string + - name: git-revision + description: Git revision (branch/tag/sha) + type: string + default: main + - name: app-name + description: Application name + type: string + - name: context-dir + description: Docker build context directory + type: string + default: ./nextjs + - name: build-args + description: Build arguments (key=value format, one per line) + type: string + default: "" + workspaces: + - name: shared-workspace + description: Shared workspace for all tasks + - name: docker-credentials + description: Docker registry credentials + tasks: + - name: clone + taskRef: + name: git-clone + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.git-revision) + - name: deleteExisting + value: "true" + workspaces: + - name: output + workspace: shared-workspace + + - name: build-push + taskRef: + name: buildah-build-push + runAfter: + - clone + params: + - name: IMAGE + value: zot0213.kro.kr/$(params.app-name):$(params.git-revision) + - name: DOCKERFILE + value: ./Dockerfile + - name: CONTEXT + value: $(params.context-dir) + - name: BUILD_ARGS + value: $(params.build-args) + workspaces: + - name: source + workspace: shared-workspace + - name: dockerconfig + workspace: docker-credentials diff --git a/tekton/ci-cd/manifests/pipelines/python-pipeline.yaml b/tekton/ci-cd/manifests/pipelines/python-pipeline.yaml new file mode 100644 index 0000000..fc22dd7 --- /dev/null +++ b/tekton/ci-cd/manifests/pipelines/python-pipeline.yaml @@ -0,0 +1,59 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: python-build-deploy + namespace: tekton-pipelines +spec: + description: Build Python app (LangGraph/Chainlit) and push to Zot registry + params: + - name: git-url + description: Git repository URL + type: string + - name: git-revision + description: Git revision (branch/tag/sha) + type: string + default: main + - name: app-name + description: Application name + type: string + - name: context-dir + description: Docker build context directory + type: string + default: ./langgraph + workspaces: + - name: shared-workspace + description: Shared workspace for all tasks + - name: docker-credentials + description: Docker registry credentials + tasks: + - name: clone + taskRef: + name: git-clone + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.git-revision) + - name: deleteExisting + value: "true" + workspaces: + - name: output + workspace: shared-workspace + + - name: build-push + taskRef: + name: buildah-build-push + runAfter: + - clone + params: + - name: IMAGE + value: zot0213.kro.kr/$(params.app-name):$(params.git-revision) + - name: DOCKERFILE + value: ./Dockerfile + - name: CONTEXT + value: $(params.context-dir) + workspaces: + - name: source + workspace: shared-workspace + - name: dockerconfig + workspace: docker-credentials diff --git a/tekton/ci-cd/manifests/secrets/github-credentials.yaml b/tekton/ci-cd/manifests/secrets/github-credentials.yaml new file mode 100644 index 0000000..2ef14d6 --- /dev/null +++ b/tekton/ci-cd/manifests/secrets/github-credentials.yaml @@ -0,0 +1,18 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: github-credentials + namespace: tekton-pipelines +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault-backend + target: + name: github-credentials + creationPolicy: Owner + data: + - secretKey: token + remoteRef: + key: github + property: PAT diff --git a/tekton/ci-cd/manifests/secrets/zot-registry-secret.yaml b/tekton/ci-cd/manifests/secrets/zot-registry-secret.yaml new file mode 100644 index 0000000..4553fd2 --- /dev/null +++ b/tekton/ci-cd/manifests/secrets/zot-registry-secret.yaml @@ -0,0 +1,27 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: zot-registry-credentials + namespace: tekton-pipelines +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault-backend + target: + name: zot-registry-credentials + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: | + {"auths":{"zot0213.kro.kr":{"username":"{{ .USERNAME }}","password":"{{ .PASSWORD }}","auth":"{{ printf "%s:%s" .USERNAME .PASSWORD | b64enc }}"}}} + data: + - secretKey: USERNAME + remoteRef: + key: zot + property: USERNAME + - secretKey: PASSWORD + remoteRef: + key: zot + property: PASSWORD diff --git a/tekton/ci-cd/manifests/tasks/buildah-build-push.yaml b/tekton/ci-cd/manifests/tasks/buildah-build-push.yaml new file mode 100644 index 0000000..b8dc5b0 --- /dev/null +++ b/tekton/ci-cd/manifests/tasks/buildah-build-push.yaml @@ -0,0 +1,71 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: buildah-build-push + namespace: tekton-pipelines +spec: + description: Build container image with Buildah and push to registry + params: + - name: IMAGE + description: Full image reference (registry/repo:tag) + type: string + - name: DOCKERFILE + description: Path to Dockerfile + type: string + default: ./Dockerfile + - name: CONTEXT + description: Build context directory + type: string + default: . + - name: BUILD_ARGS + description: Build arguments (key=value format, one per line) + type: string + default: "" + workspaces: + - name: source + description: Source code workspace + - name: dockerconfig + description: Docker config for registry auth + optional: true + results: + - name: IMAGE_DIGEST + description: Digest of built image + - name: IMAGE_URL + description: Full URL of pushed image + steps: + - name: build-and-push + image: quay.io/buildah/stable:v1.33 + securityContext: + privileged: true + workingDir: $(workspaces.source.path) + env: + - name: BUILD_ARGS + value: $(params.BUILD_ARGS) + script: | + #!/usr/bin/env bash + set -ex + + REGISTRY=$(echo "$(params.IMAGE)" | cut -d'/' -f1) + DOCKER_CONFIG="$(workspaces.dockerconfig.path)/.dockerconfigjson" + + # Login to registry + if [ -f "$DOCKER_CONFIG" ]; then + USER=$(sed -n 's/.*"username":"\([^"]*\)".*/\1/p' "$DOCKER_CONFIG") + PASS=$(sed -n 's/.*"password":"\([^"]*\)".*/\1/p' "$DOCKER_CONFIG") + buildah login -u "$USER" -p "$PASS" "$REGISTRY" + fi + + # Parse build args + BUILD_ARGS_FLAGS="" + while IFS= read -r line; do + [ -n "$line" ] && BUILD_ARGS_FLAGS="$BUILD_ARGS_FLAGS --build-arg $line" + done <<< "$BUILD_ARGS" + + # Build and push + buildah bud --platform linux/arm64 --format docker \ + -f $(params.DOCKERFILE) -t $(params.IMAGE) $BUILD_ARGS_FLAGS $(params.CONTEXT) + buildah push --digestfile /tmp/image-digest $(params.IMAGE) + + # Output results + cat /tmp/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$(params.IMAGE)" | tee $(results.IMAGE_URL.path) diff --git a/tekton/ci-cd/manifests/tasks/git-clone.yaml b/tekton/ci-cd/manifests/tasks/git-clone.yaml new file mode 100644 index 0000000..4c7a912 --- /dev/null +++ b/tekton/ci-cd/manifests/tasks/git-clone.yaml @@ -0,0 +1,57 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: git-clone + namespace: tekton-pipelines + labels: + app.kubernetes.io/version: "1.0" +spec: + description: Clone a git repository using standard git + workspaces: + - name: output + description: The git repo will be cloned onto the volume backing this Workspace. + params: + - name: url + description: Repository URL to clone from. + type: string + - name: revision + description: Revision to checkout (branch, tag, sha, ref). + type: string + default: "main" + - name: depth + description: Perform a shallow clone, fetching only the most recent N commits. + type: string + default: "1" + - name: deleteExisting + description: Clean out the contents of the destination directory if it already exists. + type: string + default: "true" + results: + - name: commit + description: The precise commit SHA that was fetched by this Task. + - name: url + description: The precise URL that was fetched by this Task. + steps: + - name: clone + image: alpine/git:latest + script: | + #!/bin/sh + set -ex + + CHECKOUT_DIR="$(workspaces.output.path)" + + if [ "$(params.deleteExisting)" = "true" ] && [ -d "${CHECKOUT_DIR}" ]; then + rm -rf "${CHECKOUT_DIR:?}/"* || true + rm -rf "${CHECKOUT_DIR}"/.[!.]* || true + fi + + cd "${CHECKOUT_DIR}" + + git clone --depth="$(params.depth)" --branch="$(params.revision)" \ + "$(params.url)" . + + RESULT_SHA="$(git rev-parse HEAD)" + printf "%s" "${RESULT_SHA}" > "$(results.commit.path)" + printf "%s" "$(params.url)" > "$(results.url.path)" + + echo "Cloned $(params.url) at ${RESULT_SHA}"