为什么个人站长也该了解 GitOps
很多个人站长的"部署"是这样的:本地改完代码,scp 或 rsync 传上去,或者登录服务器 git pull,然后重启一下 PHP-FPM / 重载 Nginx。单机、单项目的时候这没问题,但只要你手上超过一台机器、或者同时维护博客 + 论坛 + 工具站,就会开始出现"这台改了那台忘了""三台机器配置不一致""回滚不知道回到哪个版本"的问题。
GitOps 的核心思想只有一句话:Git 仓库是系统的唯一事实来源,集群的实际状态必须始终向 Git 里的期望状态收敛。 你不再登录服务器执行命令去"推"变更,而是把期望状态写进 Git,让一个常驻的 Agent 自己"拉"下来并对齐。ArgoCD 就是 Kubernetes 生态里最流行的 GitOps 工具,它把"部署"变成了"Git 提交"。
本文面向有 Docker 基础、想给个人项目引入规范化部署流程的站长,从零搭一套 ArgoCD,讲清楚它怎么工作、怎么和 Git 联动、以及几个容易踩的坑。
推式部署与拉式部署的本质区别
先厘清概念。Jenkins、GitHub Actions 这类 CI/CD 属于推式(Push-based):流水线跑完,主动 SSH 到目标服务器执行部署命令。它的问题在于:目标服务器必须对 CI 机器开放 SSH 权限,CI 一旦被攻破,等于所有服务器裸奔;而且"服务器现在是什么状态"没人知道,只能靠流水线脚本记。
ArgoCD 属于拉式(Pull-based):Agent 跑在集群内部,主动去 Git 仓库拉取清单,再和集群当前状态比对。好处是:
- 集群不需要对外暴露任何部署端口,攻击面小;
- 任何人都能通过 Git 提交发起变更,权限就是 Git 权限;
- 集群状态由 Agent 持续守护,被人手动改乱也能自动纠偏(自愈);
- 回滚 = git revert,历史即审计日志。
对个人站长来说,最实用的价值是可回溯:任何时刻集群里的配置都能对应到某一条 Git 提交,出问题直接看 diff。
准备一台单节点 Kubernetes
ArgoCD 运行在 Kubernetes 里(它本身也是以 Pod 形式部署的)。个人站没必要上多节点集群,一台 2 核 4G 的 VPS 跑 k3s 绰绰有余。k3s 是经过裁剪的 Kubernetes,单二进制、内存占用低,非常适合小机器。
# 安装 k3s(会自带 containerd 作为容器运行时)
curl -sfL https://get.k3s.io | sh -
# 确认节点就绪
sudo k3s kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# myserver Ready control-plane,master 30s v1.28.x
# 让普通用户也能用 kubectl(k3s 的 kubeconfig 权限是 600)
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
export KUBECONFIG=~/.kube/config
kubectl get nodes注意:k3s 默认会把 6443(API Server)只监听在本地,安全性没问题。绝对不要把 6443 直接暴露到公网,那等于把整个集群控制权送人。
安装 ArgoCD
官方提供了一份安装清单,直接 apply 即可:
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# 等待所有 Pod 就绪(首次拉镜像会慢一点)
kubectl -n argocd get pods -w装完后会得到一堆组件,最核心的是 argocd-server(提供 Web UI 和 API)、argocd-repo-server(负责拉 Git 仓库并渲染清单)、argocd-application-controller(负责比对与同步)。
拿到初始密码并访问界面
ArgoCD 默认安装后,admin 用户密码是随机生成并存在一个 Secret 里的:
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d; echo访问方式推荐 端口转发,不要直接给 nodePort 或 LoadBalancer:
kubectl port-forward svc/argocd-server -n argocd 8080:443
# 浏览器打开 https://127.0.0.1:8080,用户名 admin,密码用上面查到的如果一定要从公网访问,请务必在前面加一层带 HTTPS 和身份认证的 Nginx 反向代理,并且启用 ArgoCD 自身的 SSO,别把裸界面挂公网。
登录命令行 argo CLI
Web UI 适合看状态,但配置还是命令行顺手。安装 argo CLI 后:
# 因为用了 port-forward,用 --insecure 跳过证书校验
argocd login 127.0.0.1:8080 --insecure
# 建议改掉初始密码
argocd account update-password准备一个 Git 仓库存放期望状态
把要部署的应用清单(Deployment、Service、Ingress 等 YAML)放进一个 Git 仓库,目录结构建议按环境或应用分开:
# 仓库结构示例
gitops-repo/
├── apps/
│ ├── blog/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── ingress.yaml
│ └── forum/
│ └── ...
└── README.md这里有个关键取舍:仓库可以是公开的(ArgoCD 支持匿名拉取公开仓库),也可以是私有的。私有仓库需要配置访问凭据。个人站建议私有仓库 + SSH 部署密钥,避免把服务器配置暴露在公开仓库里。
创建 Application:把 Git 和集群连起来
ArgoCD 的核心对象叫 Application,它描述"哪个 Git 仓库的哪个路径,应该同步到哪个集群的哪个命名空间"。可以直接 apply 一个 YAML:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: blog
namespace: argocd
spec:
project: default
source:
repoURL: git@github.com:yourname/gitops-repo.git
targetRevision: main
path: apps/blog # 只同步这个子目录
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated: # 开启自动同步
prune: true # Git 里删掉的资源,集群里也删
selfHeal: true # 集群被手改后自动纠回
syncOptions:
- CreateNamespace=true把这段保存成 blog-app.yaml 然后 kubectl apply -f blog-app.yaml,ArgoCD 就会开始工作:拉取仓库 → 渲染清单 → 和集群比对 → 发现差异就同步。
理解 Sync Status 与 Health Status
界面上每个应用有两个状态,很多新手会混淆:
- Sync Status(同步状态):Git 期望状态 vs 集群实际状态。Synced 表示一致,OutOfSync 表示有差异(比如有人手动改了副本数)。
- Health Status(健康状态):资源本身是否正常运行。Healthy 表示 Pod 都起来了、探针通过。
一个应用可能 Synced 但 Progressing(刚同步完还在拉起 Pod),也可能 OutOfSync 但 Healthy(比如刚才手动扩了副本,实际跑得好好的但和 Git 不符)。两个维度要分开看。
自动同步与自愈:把手工操作关进笼子
上面 syncPolicy.automated 里两个开关很重要:
- selfHeal:有人偷偷
kubectl edit改了副本数或镜像 tag,ArgoCD 检测到 OutOfSync 后会自动改回来。这保证 Git 永远是唯一事实来源。 - prune:Git 里删掉的资源,同步时集群里也一并删除。注意这个开关有风险——如果误删了 Git 里的一个文件,集群里的资源会跟着消失,所以务必配合下面的审批流程。
也可以不开自动同步,改为手动同步:ArgoCD 只提示 OutOfSync,由你在界面上点 Sync 按钮确认。生产环境比较稳妥的做法是"关键应用手动、非关键自动"。
几个必须知道的坑
1. 渲染方式和 Helm/Kustomize 的关系。 上面的例子是纯 YAML。如果清单里有环境差异(dev/prod 不同镜像 tag),用 Kustomize 或 Helm 做参数化,ArgoCD 原生支持。直接在 Application 里写 source.helm 或 source.kustomize 即可,不要用 sed 改 YAML 再提交,那样会制造无意义的提交历史。
2. 私有仓库凭据别写进 YAML。 用 argocd repo add 或 Web UI 配置仓库凭据,ArgoCD 会存进 Secret。把私钥硬编码进 Application 清单等于把密钥提交进 Git。
3. 别让 ArgoCD 管理它自己。 ArgoCD 自身也是跑在集群里的,如果用一个 Application 去管理 argocd 命名空间,升级时可能把自己同步没了。要么单独用官方的 bootstrap 流程,要么明确排除。
4. namespaces 与 cluster-scoped 资源。 像 ClusterRole、Namespace 本身这类资源不属于某个命名空间,用 CreateNamespace=true 让 ArgoCD 自动建 ns 很方便,但要注意权限边界。
5. 数据库不要放进 GitOps 管理的 Deployment 里裸跑。 无状态应用(Web 前端、API)用 GitOps 管理很舒服;有状态数据库(MySQL/PostgreSQL)涉及数据持久化,别让 prune 把 PVC 一起删了。数据库建议独立管理,或者用 StatefulSet + 保留策略(Prevent deletion)。
给个人站的落地建议
如果你的站只是单机 Docker Compose,其实不必强上 ArgoCD——Docker Compose 已经够用。但如果你符合以下任一情况,GitOps 收益就很明显:
- 同时管理多个站点/服务,希望配置集中在一个仓库;
- 经常需要在多台机器之间保持环境一致;
- 希望任何一次变更都可回溯、可一键回滚;
- 正在学习 Kubernetes,想用真实项目练手。
起步阶段的推荐路径:先在一台 k3s 上把一个无状态应用(比如静态博客的 Nginx 容器)跑通 ArgoCD 流程,理解 Application、Sync、Health 三个概念,再逐步把有状态服务分离出去单独管理。不要一上来就把数据库交给 GitOps,那是给自己埋雷。
小结
ArgoCD 把"部署"从"登录服务器敲命令"变成了"往 Git 提交清单",这背后是推送式到拉取式的思维转变。它的价值不在于炫技,而在于让系统状态始终有据可查、有人守护。对个人站长而言,哪怕只用一个单节点 k3s + ArgoCD 管理一两个无状态服务,也能切实体会到"回滚 = git revert"的安心感。记住三条底线:Git 是唯一事实来源、私有凭据不进仓库、有状态数据库不交给 prune。