ArgoCD · GitOps 引擎

ArgoCD 是这套自托管集群的核心:它持续 watch yldm-tech/k8s-config 仓库的 main 分支,把仓库里 ~1300 份 YAML manifest 自动 reconcile 进集群。合并到 main 几分钟后改动就会落地,没有手动 kubectl apply 这一步。完整的开发者流程见 GitOps 工作流

组件构成

ArgoCD 部署在 argocd namespace,由 Helm chart argo-cd-9.1.0 渲染后落到仓库,所有核心组件镜像统一为 quay.io/argoproj/argocd:v3.4.3

  • Name
    application-controller
    Description
    StatefulSet,1 副本。负责 reconcile 所有 Application —— 比对 git 期望状态与集群实际状态,执行同步与自愈。
  • Name
    repo-server
    Description
    Deployment,1 副本。拉取 git 仓库、渲染 kustomize/Helm,把 manifest 交给 controller。监听 argocd-repo-server:8081
  • Name
    server
    Description
    Deployment,1 副本。提供 Web UI / API / gRPC。server.insecure: "true"(TLS 在 Traefik ingress 终结),ingress 后端走 80 端口。
  • Name
    applicationset-controller
    Description
    Deployment,1 副本。展开 ApplicationSet(如微服务的 yldm-services)为一组具体 Application。enable.leader.election 关闭(单副本无需选主)。
  • Name
    notifications-controller
    Description
    Deployment,1 副本。按 argocd-notifications-cm 的触发器推送同步/健康通知。
  • Name
    redis
    Description
    Deployment,1 副本,镜像 redis:8.8.0-alpine。作为 controller / repo-server 的缓存。配套一个 argocd-redis-watchdog CronJob。

reconcile 周期由 argocd-cm 控制:timeout.reconciliation: 180s(软周期 3 分钟),timeout.hard.reconciliation: 0s(关闭硬性强制刷新)。exec.enabled: "false" 禁掉了 UI 里的 pod exec 终端。

两层编排

ArgoCD 把仓库变成运行中应用靠两层结构,这是理解整个 repo 的关键。详细机制见 GitOps 工作流

第一层是 app-of-apps 根应用bootstrap/applications/root-app.yaml 定义的 Application root-app,source 指向 bootstrap/applicationsdirectory.recurse: true(排除自身 root-app.yaml)。任何放进 bootstrap/applications/Application / ApplicationSet YAML 都会被自动发现 —— 不需要手动注册。这一层覆盖单例组件:infrastructure、databases、monitoring、networking、storage、cluster/ 配置等。

第二层是 微服务 ApplicationSetbootstrap/applications/orchestration/production-services.yaml 里的 ApplicationSet yldm-services,用 List generator + goTemplate: true。它是一个静态列表,不是 git 目录生成器 —— 列表里每个元素(service / serviceType / namespace / replicas)生成一个名为 <serviceType>-<service> 的 Application,source 指向 applications/<serviceType>/<service>

- service: fluxa
  serviceType: platform
  namespace: platform
  replicas: 2
  category: service
# → 生成 Application platform-fluxa,path: applications/platform/fluxa

模板侧给每个生成的 Application 打上 yldm.tech/deploy-tracking: 'true' 注解,接入 deploy-tracker,把 rollout 状态镜像成 GitHub Deployment status。

AppProject 边界

bootstrap/projects/*.yaml 定义 AppProject,给 Application 划权限边界。共 6 个:yldm-appyldm-platformyldm-gameinfrastructureci-cdmonitoring。微服务 ApplicationSet 模板里 spec.project: '{{.serviceType}}',即 app/platform/game 服务分别落进同名 project。

app / platform / game 是 namespace-scoped 的;infrastructure / ci-cd / monitoring 通过显式 clusterResourceWhitelist 允许集群级资源。例如 infrastructure project 放行 Namespace、ClusterRole(Binding)、CRD、Validating/Mutating WebhookConfiguration、IngressClass、StorageClass、PriorityClass、cert-manager ClusterIssuer,并对 namespace 级资源全放行('*'/'*')。所有 project 的 sourceRepos 都锁定 k8s-config 仓库的 SSH/HTTPS 两种 URL。

automated prune / selfHeal

绝大多数 Application 都开了自动同步:

syncPolicy:
  automated:
    prune: true # git 里删掉的资源,集群里也删
    selfHeal: true # 集群被手动改动会被拉回 git 状态
  • selfHeal 意味着直接 kubectl edit 改 live 资源是徒劳的 —— 下一次 reconcile(≤180s)会把它改回 git 里的样子。要改就改仓库。
  • prune 意味着从仓库删掉一个 manifest,集群里对应资源会被删除。微服务 ApplicationSet 额外用 PrunePropagationPolicy=foreground + PruneLast=true,并带 retry(5 次,指数退避到 3m),prune 的 Application 还设了 allowEmpty: false 防止误删空目录把整个 app 抹掉。
  • 微服务模板对 Deployment 的 /spec/replicas 设了 ignoreDifferences,让 HPA 调整副本数时不会被 selfHeal 拉回。

sync-wave 排序

bootstrap/applications/ 里被 root-app 一次性发现的 Application 用 argocd.argoproj.io/sync-wave 注解控制相对顺序,wave 数字小的先同步。这保证依赖在被依赖者之后上线,例如:

wave组件(示例)
0nfs-provisioner
1kube-vip、cluster-config、reloader、argocd-image-updater
2external-dns、nodelocaldns
3vault
5kyverno、各 runner
6minio
8loki、tempo、prometheus、registry(晚于 minio,依赖其 bucket)
9grafana、velero
15cloudflare-tunnel

同样的机制也用在单个组件内部排序资源。argocd-image-updater 的 ImageUpdater CR 用 sync-wave: "2"SkipDryRunOnMissingResource=true,确保它们在 CRD(wave 0)建立之后才同步,否则首次同步会报 no matches for kind ImageUpdater

配合 argocd-image-updater

镜像 tag 不靠手改,由 argocd-image-updaterquay.io/argoprojlabs/argocd-image-updater:v1.2.1,部署在 argocd namespace)管理。它扫描镜像仓库发现新版本,把新 tag git commit 回写到对应 app 的 kustomization images: 块(仓库历史里的 build: automatic update of … commit),再由 ArgoCD 同步上线。

每个被管理的服务在 applications/infrastructure/argocd-image-updater/imageupdaters/<app>.yaml 有一份 ImageUpdater CR(当前 24 份)。以 fluxa 为例:

spec:
  writeBackConfig:
    method: 'git:secret:argocd/git-creds' # SSH 写回
    gitConfig:
      repository: 'git@github.com:yldm-tech/k8s-config.git'
      branch: 'main'
      writeBackTarget: 'kustomization:/applications/platform/fluxa'
  applicationRefs:
    - namePattern: 'platform-fluxa'
      commonUpdateSettings:
        updateStrategy: 'semver'
        allowTags: "regexp:^[0-9]+\\.[0-9]+\\.[0-9]+$" # 只认 X.Y.Z

控制器配置(config-patch.yaml):watch.namespaces: argocd(CR 与 Application 同 ns),扫描间隔 interval: 2m,写回身份 argocd-image-updater@yldm.tech。镜像仓库定义两个 —— ghcr.io(复用从 Vault 同步的 argocd/ghcr-secret pull secret 列 private tag)和集群内 registry.dunaifen.games(DNF 镜像,匿名只读)。写回用的 SSH key 来自 ExternalSecret git-creds,从 Vault argocd/repo/golang-serversshPrivateKey,与 ArgoCD repo 凭据同一把写权限 key。

访问与认证

ArgoCD Web UI 通过 ingress 暴露在 argocd.yldm.techingressClassName: traefik,TLS 证书由 cert-manager letsencrypt-prod-dns01 ClusterIssuer 签发(secret argocd-server-tls),并经 Cloudflare Tunnel 代理。

认证走独立的 Dex 做 OIDC(issuer: https://dex.yldm.techclientID: argocd-cli,请求 openid/profile/email/groups);admin.enabled: "true" 保留本地 admin 兜底。argocd-secret 由 ExternalSecret 从 Vault argocd/argocd-secret 同步。会话有效期 timeout.session: 168h(7 天)。

评论