IP 定向灰度发布:ArgoCD + GitLab CLI 方案
要实现基于ArgoCD + GitLab CLI的IP定向灰度发布,核心思路是:通过GitLab存储应用配置(基线/灰度版本)、ArgoCD同步K8s资源、NGINX Ingress基于IP范围分流流量,结合GitLab CLI自动化配置变更和发布流程。以下是完整落地方案:
一、核心组件与原理
| 组件 | 作用 |
|---|---|
| K8s集群 | 运行应用和Ingress,提供容器编排能力 |
| NGINX Ingress | 基于IP地址范围做流量分流(灰度IP访问v2,其他IP访问v1) |
| GitLab | 存储应用的K8s配置(基线/灰度分支),通过GitLab CLI自动化版本管理 |
| GitLab CLI (glab) | 自动化分支创建、配置修改、提交推送,触发ArgoCD同步 |
| ArgoCD | 同步GitLab中的配置到K8s集群,管理基线/灰度应用的生命周期 |
| Argo Rollouts(可选) | 替代原生Deployment,支持更精细的灰度策略(如按比例发布、自动回滚) |
二、环境准备
1. 基础环境部署
Bash
# 1. 部署K8s(示例用kind,生产用EKS/AKS/GKE)
kind create cluster --name canary-demo
# 2. 部署ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.11.0/manifests/install.yaml
# 获取ArgoCD密码(admin用户)
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d && echo
# 3. 部署NGINX Ingress
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/kind/deploy.yaml
# 等待Ingress就绪
kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=120s
# 4. 安装GitLab CLI (glab)
# 参考:https://gitlab.com/gitlab-org/cli/-/blob/main/docs/installation.md
# 配置glab登录(关联你的GitLab仓库)
glab auth login
2. GitLab仓库初始化
创建GitLab仓库(如argocd-canary-demo),并克隆到本地:
Bash
git clone https://gitlab.com/你的用户名/argocd-canary-demo.git
cd argocd-canary-demo
三、配置文件编写(Kustomize + ArgoCD)
采用Kustomize分层管理基线/灰度配置,目录结构如下:
Plain
argocd-canary-demo/
├── base/ # 基础配置(通用部分)
│ ├── deployment.yaml # 基础Deployment模板
│ ├── service.yaml # 基础Service
│ └── kustomization.yaml
├── overlays/ # 环境覆盖层
│ ├── production/ # 基线版本(v1)
│ │ ├── deployment-patch.yaml # v1镜像/环境变量
│ │ ├── ingress.yaml # 流量分流核心配置(IP匹配)
│ │ └── kustomization.yaml
│ └── canary/ # 灰度版本(v2)
│ ├── deployment-patch.yaml # v2镜像/环境变量
│ └── kustomization.yaml
└── argocd/ # ArgoCD Application配置
├── production.yaml
└── canary.yaml
1. 基础配置(base/)
base/deployment.yaml
YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
replicas: 3
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: demo-app
image: nginx:1.21 # 基础镜像(后续patch修改)
ports:
- containerPort: 80
# 简单的版本标识(页面返回版本信息)
command: ["/bin/sh", "-c"]
args: ["echo 'Version: ${VERSION}' > /usr/share/nginx/html/index.html && nginx -g 'daemon off;'"]
base/service.yaml
YAML
apiVersion: v1
kind: Service
metadata:
name: demo-app
spec:
selector:
app: demo-app
ports:
- port: 80
targetPort: 80
type: ClusterIP
base/kustomization.yaml
YAML
resources:
- deployment.yaml
- service.yaml
2. 基线版本(overlays/production/)
overlays/production/deployment-patch.yaml(v1版本)
YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
template:
spec:
containers:
- name: demo-app
image: nginx:1.21
env:
- name: VERSION
value: "v1"
overlays/production/ingress.yaml(核心:IP分流)
通过NGINX Ingress的server-snippet实现IP正则匹配,灰度IP(如192.168.1.0/24、10.0.0.0/24)访问v2,其他访问v1:
YAML
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app
annotations:
kubernetes.io/ingress.class: "nginx"
# 核心:IP匹配规则 + 流量分流
nginx.ingress.kubernetes.io/server-snippet: |
# 定义灰度IP范围(正则匹配)
set $is_canary 0;
if ($remote_addr ~* "^192\.168\.1\.[0-9]+$|^10\.0\.0\.[0-9]+$") {
set $is_canary 1;
}
# 灰度IP转发到canary Service,其他转发到production Service
if ($is_canary = 1) {
proxy_pass http://demo-app-canary.demo-app.svc.cluster.local:80;
break;
}
spec:
rules:
- host: demo-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app-production
port:
number: 80
overlays/production/kustomization.yaml
YAML
bases:
- ../../base
patches:
- deployment-patch.yaml
resources:
- ingress.yaml
nameSuffix: -production # 资源名后缀:demo-app-production
3. 灰度版本(overlays/canary/)
overlays/canary/deployment-patch.yaml(v2版本)
YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
replicas: 1 # 灰度副本数减少
template:
spec:
containers:
- name: demo-app
image: nginx:1.25
env:
- name: VERSION
value: "v2"
overlays/canary/kustomization.yaml
YAML
bases:
- ../../base
patches:
- deployment-patch.yaml
nameSuffix: -canary # 资源名后缀:demo-app-canary
4. ArgoCD Application配置
argocd/production.yaml(基线应用)
YAML
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo-app-production
namespace: argocd
spec:
project: default
source:
repoURL: https://gitlab.com/你的用户名/argocd-canary-demo.git # 替换为你的GitLab仓库
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: demo-app
syncPolicy:
automated:
prune: true # 自动删除多余资源
selfHeal: true # 配置漂移自动修复
syncOptions:
- CreateNamespace=true # 自动创建demo-app命名空间
argocd/canary.yaml(灰度应用)
YAML
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo-app-canary
namespace: argocd
spec:
project: default
source:
repoURL: https://gitlab.com/你的用户名/argocd-canary-demo.git
targetRevision: canary # 灰度分支
path: overlays/canary
destination:
server: https://kubernetes.default.svc
namespace: demo-app
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
四、GitLab CLI自动化灰度发布流程
1. 初始化基线版本
Bash
# 提交基线配置到main分支
git add .
git commit -m "Init: production v1"
git push origin main
# 应用ArgoCD配置,同步基线版本
kubectl apply -f argocd/production.yaml
argocd app sync demo-app-production
2. 创建灰度分支并修改配置
Bash
# 1. 创建灰度分支
glab checkout -b canary main
# 2. (可选)调整灰度IP范围(比如新增172.16.0.0/24)
sed -i 's/^192\.168\.1\.[0-9]+$|^10\.0\.0\.[0-9]+$/^192\.168\.1\.[0-9]+$|^10\.0\.0\.[0-9]+$|^172\.16\.0\.[0-9]+$/' overlays/production/ingress.yaml
# 3. (可选)升级灰度版本镜像
sed -i 's/nginx:1.25/nginx:1.26/' overlays/canary/deployment-patch.yaml
# 4. 提交并推送灰度分支
glab add .
glab commit -m "Canary: add 172.16.0.0/24 IP range, upgrade to v2.1"
glab push origin canary
3. 同步灰度应用
Bash
# 应用ArgoCD灰度配置
kubectl apply -f argocd/canary.yaml
# 手动同步(或等待自动同步)
argocd app sync demo-app-canary
4. 验证灰度效果
骤1:获取Ingress IP
c
INGRESS_IP=$(kubectl get ingress demo-app-production -n demo-app -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
# Kind环境下Ingress IP为集群节点IP,端口为80
echo "Ingress IP: $INGRESS_IP"
步骤2:模拟不同IP访问
- 灰度IP(<192.168.1.10>):
c
# 用X-Forwarded-For模拟灰度IP(需Ingress配置proxy-real-ip-cidr)
curl -H "Host: demo-app.example.com" -H "X-Forwarded-For: 192.168.1.10" http://$INGRESS_IP
# 预期输出:Version: v2
- 非灰度IP(<172.17.0.1>):
c
curl -H "Host: demo-app.example.com" -H "X-Forwarded-For: 172.17.0.1" http://$INGRESS_IP
# 预期输出:Version: v1
5. 全量发布(灰度验证通过后)
Bash
# 1. 合并灰度分支到main
glab checkout main
glab merge canary -m "Merge canary to main: full release v2"
glab push origin main
# 2. 同步基线应用(全量升级为v2)
argocd app sync demo-app-production
# 3. (可选)清理灰度资源
glab branch delete canary -y
argocd app delete demo-app-canary -y
kubectl delete deployment demo-app-canary -n demo-app
五、进阶优化(可选)
1. 集成Argo Rollouts
原生Deployment无法实现"按比例发布、自动回滚",可替换为Argo Rollouts:
YAML
# overlays/production/rollout.yaml(替代deployment.yaml)
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: demo-app-production
spec:
replicas: 3
selector:
matchLabels:
app: demo-app-production
template: # 同Deployment模板
strategy:
canary:
steps:
- setWeight: 20 # 先发布20%流量
- pause: {} # 暂停发布,手动验证
- setWeight: 100 # 全量发布
2. GitLab CI/CD自动化
编写.gitlab-ci.yml,实现"提交代码→自动同步ArgoCD→验证→全量发布":
YAML
stages:
- sync-canary
- validate
- sync-production
sync-canary:
stage: sync-canary
script:
- glab auth login --token $GITLAB_TOKEN
- argocd app sync demo-app-canary
only:
- canary
validate:
stage: validate
script:
- curl -H "Host: demo-app.example.com" -H "X-Forwarded-For: 192.168.1.10" http://$INGRESS_IP | grep "v2"
only:
- canary
sync-production:
stage: sync-production
script:
- glab merge canary main -m "Full release v2"
- argocd app sync demo-app-production
only:
- canary
when: manual # 手动触发全量发布
3. IP匹配优化
如果Ingress部署在代理(如CDN、LB)后,需配置真实IP透传:
YAML
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "10.0.0.0/8,192.168.0.0/16" # 代理IP段
nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
六、常见问题排查
-
IP分流不生效:
-
检查NGINX Ingress日志:
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller -
确认
remote_addr是否为真实客户端IP(代理场景需配置proxy-real-ip-cidr)。
-
-
ArgoCD同步失败:
-
检查GitLab仓库权限(ArgoCD需能拉取仓库)。
-
验证配置文件语法:
kubectl apply --dry-run=client -f overlays/production/。
-
-
灰度版本无法访问:
-
检查Service/Deployment标签匹配:
kubectl get svc -n demo-app。 -
验证Pod状态:
kubectl get pods -n demo-app。
-
总结
本方案通过「GitLab存储配置 + GitLab CLI自动化 + ArgoCD同步资源 + NGINX Ingress IP分流」实现了IP定向灰度发布,核心优势:
-
配置即代码:所有发布配置存储在GitLab,可追溯、可回滚。
-
自动化:GitLab CLI/CI替代手动操作,减少人为错误。
-
灵活分流:基于IP范围精准控制灰度群体,支持逐步扩大灰度范围。
-
可扩展:集成Argo Rollouts可实现更复杂的灰度策略(如按比例、A/B测试)。