生产环境 DevOps 实战 | 第 7 天:三件套集成总览——Tekton 构建 → Harbor 存储 → Argo CD 部署的端到端流程

第 7/60 天

引言

过去六天,我们从技术选型(第 1 天)、工具全景对比(第 2 天)、全链路架构设计(第 3 天)出发,分别深入了 Tekton(第 4 天)、Argo CD(第 5 天)、Harbor(第 6 天)的核心概念。三个组件单独看都很强大,但生产环境的真正价值在于把它们串成一条端到端的自动化链路

代码提交 → Tekton 构建镜像 → Harbor 存储制品 → GitOps 仓库更新 → Argo CD 同步部署 → 生产集群运行

这一篇是第 1 周的收官之作,我们不再逐个组件讲解,而是把三件套拼成一张完整的”全链路地图”:数据流怎么走、每个环节的职责边界在哪里、集成点有哪些、一次完整的发布旅程长什么样。同时给出最小可运行的三件套联动 YAML 示例,为第 2 ~ 5 周的深入实战建立整体认知框架。

核心概念

三件套的分工与边界

组件 定位 职责 核心资源
Tekton CI(持续集成) 拉代码、跑测试、构建镜像、推送制品、产出构建结果 Task / Pipeline / PipelineRun / Trigger
Harbor 制品仓库 存储 OCI 制品、权限隔离、漏洞扫描、复制分发 Project / Robot Account / Replication / Scan
Argo CD CD(持续交付) 声明式 GitOps 同步、漂移检测、多环境/多集群交付 Application / Project / Sync Policy

关键原则:Tekton 负责”把代码变成制品”、Harbor 负责”把制品安全地存起来并发出去”、Argo CD 负责”把制品按 Git 声明部署到集群”。三者之间尽量只通过制品和 Git 进行松耦合交互,不互相调用内部 API。

端到端数据流(Pull 模型)

   [开发者] ──git push──▶ [应用仓库 GitHub/GitLab]
                              │ webhook 事件
                              ▼
                  [Tekton EventListener] ──▶ TriggerTemplate
                              │              生成 PipelineRun
                              ▼
                  [Tekton Pipeline: 拉码 → 测试 → kaniko 构建 → 推送]
                              │  docker login (Harbor Robot Account)
                              ▼
                       [Harbor 镜像仓库]
                       (扫描/签名/复制)
                              │  CI 更新 GitOps 仓库中的镜像 tag(提交)
                              ▼
                    [GitOps 配置仓库 deployment.yaml]
                              │  Argo CD 检测到与集群状态不一致(漂移)
                              ▼
                    [Argo CD Application 执行 Sync]
                              │  kube-apiserver 下发滚动更新
                              ▼
                 [K8s 集群 Deployment] ──pull──▶ [Harbor 拉取新镜像]

注意 Argo CD 是 Pull 模型:它主动从 Git 仓库拉取期望状态与集群实际状态对比,发现不一致就收敛(Sync/自愈),而不是由 Harbor 或 CI 主动”推”给集群。这是 GitOps 与传统 CD 工具最本质的区别。

集成点与松耦合解耦原则

集成点 交互方式 避免的耦合
Tekton ↔ Harbor 推送镜像(robot account 认证) 不要用管理员密码
Tekton ↔ GitOps 仓库 更新镜像 tag 并提交 不要直接调用 Argo CD API
Harbor ↔ Argo CD 仅通过”镜像名+tag 约定” Argo CD 不感知 Harbor 内部结构
Git ↔ Argo CD 监听仓库变化 不需要 webhook 也能跑(轮询)

镜像不可变标签:衔接 CI 与 CD 的契约

三件套联动的核心契约是镜像 tag 策略。生产环境强烈建议使用不可变、可追溯的标签:

harbor.example.com/team-a/order-service:main-3f2a9c1-20260818-0715
                └── 项目 ──┘└──── 应用 ────┘└── 分支-SHA-时间戳 ──┘

每个构建产物有唯一身份,CI 产出它、Harbor 保存它、Argo CD 通过 GitOps 仓库引用它——任何一个环节出问题,都能从 tag 精准回溯到某一次 commit。

实战步骤

下面给出三件套联动的最小可运行骨架。整体上我们在同一集群中:Tekton 建在 tekton-pipelinescicd 命名空间,Harbor 部署在 harbor 命名空间(假设域名 harbor.example.com),Argo CD 在 argocd 命名空间,应用部署到 production 命名空间。

步骤 1:创建 Harbor Robot Account(一次性准备)

Robot Account(机器人账号)是给 Tekton CI 用的自动化凭证。权限最小化:仅对 team-a 项目有推送权。用 Harbor API 创建:

# 先登录获取会话,管理员身份创建 robot account
ADMIN_PASS='your-admin-password'
curl -s -u "admin:${ADMIN_PASS}" -H "Content-Type: application/json" 
  -X POST https://harbor.example.com/api/v2.0/robots 
  -d '{
    "name": "tekton-ci",
    "duration": -1,
    "level": "project",
    "permissions": [{
      "kind": "project",
      "namespace": "team-a",
      "access": [
        {"resource": "repository", "action": "push"},
        {"resource": "repository", "action": "pull"}
      ]
    }]
  }'

# 响应中的 secret 即为推送密码(只显示一次,保存好!)
# 格式: robot$team-a+tekton-ci / <secret>

步骤 2:把 Robot Credential 做成 K8s Secret(dockerconfigjson)

Tekton 推送镜像时使用标准的 docker login 凭证。把 robot account 转成 dockerconfigjson Secret,并关联到 Tekton 使用的 ServiceAccount:

apiVersion: v1
kind: Secret
metadata:
  name: harbor-robot-push
  namespace: cicd
  annotations:
    tekton.dev/docker-0: https://harbor.example.com   # Tekton 识别的 docker 凭证注解
type: kubernetes.io/dockerconfigjson
stringData:
  .dockerconfigjson: |
    {
      "auths": {
        "https://harbor.example.com": {
          "username": "robot$team-a+tekton-ci",
          "password": "REPLACE_WITH_ROBOT_SECRET",
          "auth": "REPLACE_WITH_base64(user:pass)"
        }
      }
    }
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: pipeline-runner
  namespace: cicd
secrets:
  - name: harbor-robot-push

步骤 3:Tekton Pipeline——构建并推送镜像到 Harbor

一个完整的 CI Pipeline:clone 代码 → 单元测试 → kaniko 构建 → 推送到 Harbor。注意以 Git commit SHA 生成不可变 tag 并通过 results 传递给下游:

apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: build-and-push
  namespace: cicd
spec:
  workspaces:
    - name: source          # 共享源码目录
    - name: dockerconfig    # 挂载 Harbor 凭证
  params:
    - name: git-url
      type: string
    - name: git-revision
      type: string
    - name: image
      type: string
      default: harbor.example.com/team-a/order-service
  results:
    - name: image-tag       # 供下游(更新 GitOps 仓库)使用
  tasks:
    - name: clone
      taskRef:
        name: git-clone
      params:
        - name: url
          value: $(params.git-url)
        - name: revision
          value: $(params.git-revision)
      workspaces:
        - name: output
          workspace: source
    - name: test
      runAfter: [clone]
      taskSpec:
        steps:
          - name: go-test
            image: golang:1.22
            workingDir: $(workspaces.source.path)
            script: |
              go test ./... -race -cover
              go vet ./...
      workspaces:
        - name: source
          workspace: source
    - name: build-push
      runAfter: [test]
      workspaces:
        - name: source
          workspace: source
        - name: dockerconfig
          workspace: dockerconfig
      params:
        - name: image
          value: $(params.image)
        - name: git-revision
          value: $(params.git-revision)
      taskSpec:
        params:
          - name: image
          - name: git-revision
        workspaces:
          - name: source
          - name: dockerconfig
        results:
          - name: image-tag
            description: 完整镜像引用
        steps:
          - name: build-and-push
            image: gcr.io/kaniko-project/executor:v1.20.0
            env:
              - name: DOCKER_CONFIG
                value: /builder/home/.docker
            args:
              - --context=$(workspaces.source.path)
              - --dockerfile=$(workspaces.source.path)/Dockerfile
              - --destination=$(params.image):$(params.git-revision)
              - --cache=true
              - --cache-repo=harbor.example.com/team-a/cache
              - --push-retry=3
            volumeMounts:
              - name: docker-config
                mountPath: /builder/home/.docker
        volumes:
          - name: docker-config
            secret:
              secretName: harbor-robot-push
              items:
                - key: .dockerconfigjson
                  path: config.json
      results:
        - name: image-tag-description

说明:+ 号在 YAML/Shell 中建议加引号;kaniko 通过 DOCKER_CONFIG 指向挂载的 dockerconfig.json,不需要集群内 Docker daemon。这是第 15 天将要深度展开的内容。

步骤 4:CI 更新 GitOps 仓库(把新 tag 写进配置仓库)

镜像构建完成并推送到 Harbor 后,CI 需要把新的 image tag 提交进 GitOps 配置仓库,Argo CD 才会知道”期望状态变了”。使用 Git clone + sed + commit 的方式:

# 在 Tekton pipeline 的 gitops-update task 中执行
git clone https://x-access-token:${GIT_TOKEN}@github.com/example/gitops-repo.git /tmp/gitops
cd /tmp/gitops

# 更新 kustomize 的 images 段(也可以是 helm values 或纯 deployment.yaml)
sed -i "s|harbor.example.com/team-a/order-service:.*|harbor.example.com/team-a/order-service:${NEW_TAG}|g" 
  apps/order-service/overlays/production/kustomization.yaml

git config user.name "tekton-ci"
git config user.email "ci@example.com"
git add apps/order-service/overlays/production/kustomization.yaml
git commit -m "chore(order-service): bump image to ${NEW_TAG} [skip ci]"
git push origin main

步骤 5:Argo CD Application——声明式交付到生产集群

Argo CD 每 3 分钟(默认)轮询 GitOps 仓库,一旦检测到配置变更就自动同步:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: order-service
  namespace: argocd
spec:
  project: production
  source:
    repoURL: https://github.com/example/gitops-repo.git
    targetRevision: main
    path: apps/order-service/overlays/production
    kustomize: {}
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:                # 自动同步 + 自愈(第 23 天详解)
      prune: true             # 删除 Git 中已移除的资源
      selfHeal: true          # 手动改动会被 Git 状态覆盖
    syncOptions:
      - CreateNamespace=true
  # 使用 Git 中的镜像引用;生产建议结合 argocd-image-updater(第 31 天)

步骤 6:配置仓库里的目标状态(引用来自 Harbor 的镜像)

Argo CD 同步时最终会通过 K8s 下发这样一个 Deployment——注意 image 不是硬编码,而是从 Kustomize overlay 注入

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: production
  labels:
    app: order-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      # 通过 imagePullSecrets 从 Harbor 拉私有镜像
      imagePullSecrets:
        - name: harbor-pull-secret
      containers:
        - name: order-service
          image: harbor.example.com/team-a/order-service:main-3f2a9c1-20260818-0715
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 512Mi

步骤 7:全链路验证与观察

# 1) 查看 Tekton 流水线运行状态
tkn pipelinerun list -n cicd
tkn pipelinerun logs $(tkn pipelinerun list -n cicd --no-headers | head -1 | awk '{print $1}') -n cicd

# 2) 确认镜像已进入 Harbor(用 robot account 查询制品清单)
curl -s -u "robot$team-a+tekton-ci:${ROBOT_SECRET}" 
  "https://harbor.example.com/api/v2.0/projects/team-a/repositories/order-service/artifacts?with_tag=true" 
  | python3 -m json.tool | head -40

# 3) 观察 Argo CD Application 健康状态
kubectl -n argocd port-forward svc/argocd-server 8080:443 >/dev/null 2>&1 &
argocd app get order-service --grpc-web
argocd app sync order-service      # 手动触发一次同步(自动同步下通常不需要)

# 4) 验证生产 Pod 是否滚动更新成功
kubectl rollout status deployment/order-service -n production
kubectl get pods -n production -l app=order-service

常见问题

1. Argo CD 为什么不直接监听 Harbor 的新镜像?
GitOps 的铁律是 Git 是唯一事实来源(Source of Truth)。如果 Argo CD 直接盯着 Harbor 的 tag 变化部署,就绕过了 Git,既无法审计也无法回滚(状态不可收敛到 Git 声明)。正确姿势是:CI 把新 tag 提交进 GitOps 仓库,Argo CD 检测到 Git 变化后再同步。需要”镜像一更新就立即部署”的场景,用 argocd-image-updater(第 31 天)自动提交 Git,仍然是”先进 Git 再同步”。

2. 镜像 tag 用 latest 可以吗?
不可以(生产环境)。latest 是可变、不可追溯的:同一个 tag 在不同时间指向不同内容,回滚时你不知道跑的是哪个版本,K8s 的 imagePullPolicy 还可能因 tag 相同而跳过拉取。务必使用 commit SHA / SemVer + SHA 拼接的不可变 tag(详见第 45 天)。

3. Tekton 推送镜像到 Harbor 报 401 Unauthorized?
大概率是 robot account 问题:① robot account 已过期(创建时 duration=-1 才是永不过期,默认会有有效期);② 权限只给了 push 没给项目读权限;③ dockerconfigjson 里 base64 的 auth 写错。先用 curl -u 'robot$team-a+tekton-ci:<secret>' https://harbor.example.com/v2/ 验证凭证本身是否可用。

4. Argo CD 同步成功了,但 Pod 还在跑旧镜像?
检查三点:① GitOps 仓库里的 image tag 是否真的更新并 push 成功(git log 确认);② Deployment 的 imagePullPolicy——tag 不变时即使模型不一致也可能不拉新镜像,用不可变 tag 天然规避;③ Argo CD 轮询间隔默认 3 分钟,selfHeal 只修配置漂移,不负责”发现新 tag”(新 tag 必须来自 Git 提交)。

5. Webhook 触发不稳定,Tekton EventListener 收不到事件?
排查顺序:① EventListener Service 是否被 Ingress/NodePort 正确暴露、Harbor 或 Git 平台能否访问到;② TriggerBinding/TriggerTemplate 的 event 字段与平台 webhook payload 字段是否匹配;③ 是否配置了 interceptor 的 secret 校验,签名不匹配会被静默丢弃;④ 事件投递失败可先用 curl 手动 POST 一个模拟 payload 验证链路(第 12~13 天详解)。

6. 谁负责更新 GitOps 仓库?CI 里提交还是人工 PR?
两种都可以,取决于团队的变更审批要求:小团队/实验环境用 Tekton 里 git-clone + commit + push 全自动;有审批要求的团队建议 Tekton 只创建 PR,由负责人 review 合并后再触发 Argo CD 同步。关键是一旦引入自动化提交,务必在 commit message 加 [skip ci] 防止触发死循环。

总结

  1. 一条主线:代码提交 → Tekton 构建 → Harbor 存储 → 更新 GitOps 仓库 → Argo CD 同步 → 生产集群,三件套通过”镜像 + Git”两个契约松耦合联动。
  2. Pull 模型:Argo CD 主动从 Git 收敛集群状态,而非被推送部署——这是 GitOps 与 Jenkins 式 CD 的本质区别,也是可审计、可回滚的根基。
  3. Robot Account 是 CI 与 Harbor 之间的凭证桥梁:最小权限、可独立管理、可轮换,绝不能在流水线里写死管理员密码。
  4. 不可变 tag 是全链路的可追溯性契约:CI 产出唯一制品、Harbor 安全存储、GitOps 精确引用,任何一个环节出问题都能靠 tag 回溯。
  5. 骨架已就绪:今天的最小联动 YAML 是后续 53 天的”地图”——第 2 周深入 Tekton CI、第 3 周深入镜像构建与 Harbor、第 4 周深入 Argo CD、第 5 周做三件套完整实战。

下期预告

第 8 天:在 Kubernetes 上安装 Tekton Pipelines——kubectl 与 Operator 两种方式

从第 2 周开始我们进入 Tekton CI 深入专题。第 8 天将手把手带你完成 Tekton Pipelines 的两种安装方式(kubectl apply 快速安装 vs Tekton Operator 管理安装),并验证安装结果、了解版本升级与卸载注意事项,为后续所有 Tekton 实战打好地基。

📖 系列目录

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容