Chapter 8: Argo Project Integration Practice
Learn how to integrate Argo CD, Argo Workflows, Argo Rollouts and Argo Events to build a complete cloud-native CI/CD platform
作者
30min
Argo Project Integration Practice
Chapter 8: Building a Complete Cloud-Native CI/CD Platform
This chapter will introduce how to integrate various components of the Argo ecosystem together to build a complete cloud-native CI/CD platform.
8.1 Integration Architecture Overview
8.1.1 Overall Architecture
🔄 正在渲染 Mermaid 图表...
8.1.2 Component Interaction Flow
🔄 正在渲染 Mermaid 图表...
8.2 Infrastructure Preparation
8.2.1 Install All Argo Components
#!/bin/bash
# install-argo-stack.sh
# Create namespaces
kubectl create namespace argo
kubectl create namespace argo-events
kubectl create namespace argo-rollouts
# Install Argo Workflows
kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/latest/download/install.yaml
# Install Argo CD
kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Install Argo Events
kubectl apply -n argo-events -f https://raw.githubusercontent.com/argoproj/argo-events/stable/manifests/install.yaml
kubectl apply -n argo-events -f https://raw.githubusercontent.com/argoproj/argo-events/stable/manifests/install-validating-webhook.yaml
# Install Argo Rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
# Wait for all Pods to be ready
echo "Waiting for Argo components to be ready..."
kubectl wait --for=condition=Ready pods --all -n argo --timeout=300s
kubectl wait --for=condition=Ready pods --all -n argo-events --timeout=300s
kubectl wait --for=condition=Ready pods --all -n argo-rollouts --timeout=300s
echo "All Argo components installed successfully!"
8.2.2 Configure ServiceAccount and RBAC
# argo-rbac.yaml
# Argo Workflows ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: argo-workflow
namespace: argo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: argo-workflow-role
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "secrets", "configmaps"]
verbs: ["*"]
- apiGroups: ["argoproj.io"]
resources: ["workflows", "workflowtemplates", "cronworkflows"]
verbs: ["*"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: argo-workflow-binding
subjects:
- kind: ServiceAccount
name: argo-workflow
namespace: argo
roleRef:
kind: ClusterRole
name: argo-workflow-role
apiGroup: rbac.authorization.k8s.io
---
# Argo Events ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: argo-events-sa
namespace: argo-events
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: argo-events-role
rules:
- apiGroups: ["argoproj.io"]
resources: ["workflows", "workflowtemplates"]
verbs: ["*"]
- apiGroups: [""]
resources: ["pods", "configmaps", "secrets"]
verbs: ["*"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: argo-events-binding
subjects:
- kind: ServiceAccount
name: argo-events-sa
namespace: argo-events
roleRef:
kind: ClusterRole
name: argo-events-role
apiGroup: rbac.authorization.k8s.io
8.2.3 Configure EventBus
# eventbus.yaml
apiVersion: argoproj.io/v1alpha1
kind: EventBus
metadata:
name: default
namespace: argo-events
spec:
nats:
native:
replicas: 3
auth: token
persistence:
storageClassName: standard
accessMode: ReadWriteOnce
volumeSize: 10Gi
8.3 CI Pipeline Configuration
8.3.1 GitHub EventSource
# github-eventsource.yaml
apiVersion: argoproj.io/v1alpha1
kind: EventSource
metadata:
name: github
namespace: argo-events
spec:
service:
ports:
- port: 12000
targetPort: 12000
github:
app-repo:
repositories:
- owner: myorg
names:
- myapp
events:
- push
- pull_request
webhook:
endpoint: /push
port: "12000"
method: POST
apiToken:
name: github-access
key: token
webhookSecret:
name: github-webhook
key: secret
insecure: false
active: true
contentType: json
---
# Ingress for webhook
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: github-webhook
namespace: argo-events
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: webhook.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: github-eventsource-svc
port:
number: 12000
8.3.2 CI WorkflowTemplate
# ci-workflow-template.yaml
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: ci-pipeline
namespace: argo
spec:
entrypoint: ci-pipeline
arguments:
parameters:
- name: repo
- name: branch
- name: commit
- name: registry
value: docker.io/myorg
volumeClaimTemplates:
- metadata:
name: workspace
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
templates:
- name: ci-pipeline
dag:
tasks:
- name: checkout
template: git-checkout
arguments:
parameters:
- name: repo
value: "{{workflow.parameters.repo}}"
- name: branch
value: "{{workflow.parameters.branch}}"
- name: build
dependencies: [checkout]
template: docker-build
arguments:
parameters:
- name: image
value: "{{workflow.parameters.registry}}/myapp"
- name: tag
value: "{{workflow.parameters.commit}}"
- name: unit-test
dependencies: [checkout]
template: run-tests
arguments:
parameters:
- name: test-type
value: unit
- name: lint
dependencies: [checkout]
template: run-lint
- name: security-scan
dependencies: [build]
template: trivy-scan
arguments:
parameters:
- name: image
value: "{{workflow.parameters.registry}}/myapp:{{workflow.parameters.commit}}"
- name: integration-test
dependencies: [build, unit-test]
template: run-tests
arguments:
parameters:
- name: test-type
value: integration
- name: push-image
dependencies: [security-scan, integration-test, lint]
template: docker-push
arguments:
parameters:
- name: image
value: "{{workflow.parameters.registry}}/myapp"
- name: tag
value: "{{workflow.parameters.commit}}"
- name: update-manifest
dependencies: [push-image]
template: update-gitops
arguments:
parameters:
- name: image
value: "{{workflow.parameters.registry}}/myapp:{{workflow.parameters.commit}}"
- name: git-checkout
inputs:
parameters:
- name: repo
- name: branch
container:
image: alpine/git:latest
command: ["/bin/sh", "-c"]
args:
- |
git clone https://github.com/{{inputs.parameters.repo}}.git /workspace/source
cd /workspace/source
git checkout {{inputs.parameters.branch}}
volumeMounts:
- name: workspace
mountPath: /workspace
- name: docker-build
inputs:
parameters:
- name: image
- name: tag
container:
image: docker:dind
command: ["/bin/sh", "-c"]
args:
- |
cd /workspace/source
docker build -t {{inputs.parameters.image}}:{{inputs.parameters.tag}} .
docker save {{inputs.parameters.image}}:{{inputs.parameters.tag}} > /workspace/image.tar
volumeMounts:
- name: workspace
mountPath: /workspace
env:
- name: DOCKER_HOST
value: tcp://localhost:2375
sidecars:
- name: docker
image: docker:dind
securityContext:
privileged: true
mirrorVolumeMounts: true
- name: run-tests
inputs:
parameters:
- name: test-type
container:
image: node:18
command: ["/bin/sh", "-c"]
args:
- |
cd /workspace/source
npm ci
npm run test:{{inputs.parameters.test-type}}
volumeMounts:
- name: workspace
mountPath: /workspace
- name: run-lint
container:
image: node:18
command: ["/bin/sh", "-c"]
args:
- |
cd /workspace/source
npm ci
npm run lint
volumeMounts:
- name: workspace
mountPath: /workspace
- name: trivy-scan
inputs:
parameters:
- name: image
container:
image: aquasec/trivy:latest
command: ["/bin/sh", "-c"]
args:
- |
trivy image --input /workspace/image.tar --severity HIGH,CRITICAL --exit-code 1
volumeMounts:
- name: workspace
mountPath: /workspace
- name: docker-push
inputs:
parameters:
- name: image
- name: tag
container:
image: docker:latest
command: ["/bin/sh", "-c"]
args:
- |
docker load < /workspace/image.tar
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker push {{inputs.parameters.image}}:{{inputs.parameters.tag}}
volumeMounts:
- name: workspace
mountPath: /workspace
env:
- name: DOCKER_USERNAME
valueFrom:
secretKeyRef:
name: docker-credentials
key: username
- name: DOCKER_PASSWORD
valueFrom:
secretKeyRef:
name: docker-credentials
key: password
- name: update-gitops
inputs:
parameters:
- name: image
container:
image: alpine/git:latest
command: ["/bin/sh", "-c"]
args:
- |
# Clone config repository
git clone https://$GIT_TOKEN@github.com/myorg/gitops-config.git /tmp/config
cd /tmp/config
# Update image tag
sed -i "s|image:.*|image: {{inputs.parameters.image}}|g" apps/myapp/deployment.yaml
# Commit changes
git config user.email "ci@example.com"
git config user.name "CI Bot"
git add .
git commit -m "Update image to {{inputs.parameters.image}}"
git push
env:
- name: GIT_TOKEN
valueFrom:
secretKeyRef:
name: github-access
key: token
8.3.3 CI Sensor
# ci-sensor.yaml
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: ci-sensor
namespace: argo-events
spec:
template:
serviceAccountName: argo-events-sa
dependencies:
- name: github-push
eventSourceName: github
eventName: app-repo
filters:
data:
- path: body.ref
type: string
value:
- refs/heads/main
- refs/heads/develop
- path: headers.X-GitHub-Event
type: string
value:
- push
triggers:
- template:
name: trigger-ci
argoWorkflow:
operation: submit
source:
resource:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: ci-
namespace: argo
spec:
workflowTemplateRef:
name: ci-pipeline
arguments:
parameters:
- name: repo
- name: branch
- name: commit
parameters:
- src:
dependencyName: github-push
dataKey: body.repository.full_name
dest: spec.arguments.parameters.0.value
- src:
dependencyName: github-push
dataTemplate: "{{ .Input.body.ref | replace \"refs/heads/\" \"\" }}"
dest: spec.arguments.parameters.1.value
- src:
dependencyName: github-push
dataKey: body.after
dest: spec.arguments.parameters.2.value
8.4 CD Configuration
8.4.1 Argo CD Application
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argo
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-config.git
targetRevision: HEAD
path: apps/myapp
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ApplyOutOfSyncOnly=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
8.4.2 GitOps Configuration Repository Structure
gitops-config/
├── apps/
│ └── myapp/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── rollout.yaml
│ └── analysis-template.yaml
├── base/
│ ├── namespace.yaml
│ └── secrets.yaml
└── overlays/
├── development/
│ └── kustomization.yaml
├── staging/
│ └── kustomization.yaml
└── production/
└── kustomization.yaml
8.4.3 Rollout Configuration
# apps/myapp/rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp
namespace: production
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: docker.io/myorg/myapp:latest
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
strategy:
canary:
canaryService: myapp-canary
stableService: myapp-stable
trafficRouting:
nginx:
stableIngress: myapp-ingress
analysis:
templates:
- templateName: success-rate
startingStep: 2
args:
- name: service-name
value: myapp-canary
steps:
- setWeight: 5
- pause: {duration: 1m}
- setWeight: 20
- pause: {duration: 2m}
- setWeight: 50
- pause: {duration: 5m}
- setWeight: 80
- pause: {duration: 5m}
---
apiVersion: v1
kind: Service
metadata:
name: myapp-stable
namespace: production
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: myapp-canary
namespace: production
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: production
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-stable
port:
number: 80
8.4.4 Analysis Template
# apps/myapp/analysis-template.yaml
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
namespace: production
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 30s
count: 10
successCondition: result[0] >= 0.95
failureCondition: result[0] < 0.90
failureLimit: 3
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{
service="{{args.service-name}}",
status=~"2.."
}[2m])) /
sum(rate(http_requests_total{
service="{{args.service-name}}"
}[2m]))
- name: latency-p95
interval: 30s
count: 10
successCondition: result[0] < 200
failureCondition: result[0] > 500
failureLimit: 3
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
histogram_quantile(0.95,
sum(rate(http_request_duration_seconds_bucket{
service="{{args.service-name}}"
}[2m])) by (le)
) * 1000
8.5 Complete Example: Microservices CI/CD
8.5.1 Microservices Architecture
🔄 正在渲染 Mermaid 图表...
8.5.2 ApplicationSet Multi-Service Deployment
# applicationset-microservices.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: microservices
namespace: argo
spec:
generators:
- list:
elements:
- name: frontend
path: apps/frontend
namespace: production
- name: api-gateway
path: apps/api-gateway
namespace: production
- name: user-service
path: apps/user-service
namespace: production
- name: order-service
path: apps/order-service
namespace: production
- name: product-service
path: apps/product-service
namespace: production
template:
metadata:
name: '{{name}}'
namespace: argo
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-config.git
targetRevision: HEAD
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{namespace}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
8.5.3 Multi-Service CI WorkflowTemplate
# multi-service-ci.yaml
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: microservice-ci
namespace: argo
spec:
entrypoint: ci-pipeline
arguments:
parameters:
- name: services
value: '[]'
- name: commit
templates:
- name: ci-pipeline
dag:
tasks:
- name: detect-changes
template: detect-changes
- name: build-services
dependencies: [detect-changes]
template: build-service
arguments:
parameters:
- name: service
value: "{{item}}"
- name: commit
value: "{{workflow.parameters.commit}}"
withParam: "{{tasks.detect-changes.outputs.parameters.changed-services}}"
- name: update-manifests
dependencies: [build-services]
template: update-manifests
arguments:
parameters:
- name: commit
value: "{{workflow.parameters.commit}}"
- name: detect-changes
outputs:
parameters:
- name: changed-services
valueFrom:
path: /tmp/changed.json
container:
image: alpine/git:latest
command: ["/bin/sh", "-c"]
args:
- |
git clone https://github.com/myorg/microservices.git /workspace
cd /workspace
# Detect which services have changes
CHANGED=$(git diff --name-only HEAD~1 | grep -E "^services/" | cut -d'/' -f2 | sort -u | jq -R . | jq -s .)
echo $CHANGED > /tmp/changed.json
- name: build-service
inputs:
parameters:
- name: service
- name: commit
dag:
tasks:
- name: build
template: docker-build
arguments:
parameters:
- name: service
value: "{{inputs.parameters.service}}"
- name: commit
value: "{{inputs.parameters.commit}}"
- name: test
template: run-tests
arguments:
parameters:
- name: service
value: "{{inputs.parameters.service}}"
- name: scan
dependencies: [build]
template: security-scan
arguments:
parameters:
- name: service
value: "{{inputs.parameters.service}}"
- name: commit
value: "{{inputs.parameters.commit}}"
- name: push
dependencies: [build, test, scan]
template: docker-push
arguments:
parameters:
- name: service
value: "{{inputs.parameters.service}}"
- name: commit
value: "{{inputs.parameters.commit}}"
- name: docker-build
inputs:
parameters:
- name: service
- name: commit
container:
image: docker:dind
command: ["/bin/sh", "-c"]
args:
- |
cd /workspace/services/{{inputs.parameters.service}}
docker build -t myorg/{{inputs.parameters.service}}:{{inputs.parameters.commit}} .
- name: run-tests
inputs:
parameters:
- name: service
container:
image: node:18
command: ["/bin/sh", "-c"]
args:
- |
cd /workspace/services/{{inputs.parameters.service}}
npm ci && npm test
- name: security-scan
inputs:
parameters:
- name: service
- name: commit
container:
image: aquasec/trivy:latest
command: ["/bin/sh", "-c"]
args:
- |
trivy image myorg/{{inputs.parameters.service}}:{{inputs.parameters.commit}} \
--severity HIGH,CRITICAL --exit-code 1
- name: docker-push
inputs:
parameters:
- name: service
- name: commit
container:
image: docker:latest
command: ["/bin/sh", "-c"]
args:
- |
docker push myorg/{{inputs.parameters.service}}:{{inputs.parameters.commit}}
- name: update-manifests
inputs:
parameters:
- name: commit
container:
image: alpine/git:latest
command: ["/bin/sh", "-c"]
args:
- |
git clone https://$GIT_TOKEN@github.com/myorg/gitops-config.git /tmp/config
cd /tmp/config
# Update all changed service images
for service in $(cat /tmp/changed.json | jq -r '.[]'); do
sed -i "s|image: myorg/$service:.*|image: myorg/$service:{{inputs.parameters.commit}}|g" \
apps/$service/deployment.yaml
done
git add .
git commit -m "Update images to {{inputs.parameters.commit}}"
git push
8.6 Monitoring and Alerting Integration
8.6.1 Prometheus Monitoring Configuration
# prometheus-servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: argo-workflows
namespace: monitoring
spec:
selector:
matchLabels:
app: argo-workflow-controller
endpoints:
- port: metrics
interval: 30s
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: argo-rollouts
namespace: monitoring
spec:
selector:
matchLabels:
app: argo-rollouts
endpoints:
- port: metrics
interval: 30s
8.6.2 Alert Rules
# argo-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: argo-alerts
namespace: monitoring
spec:
groups:
- name: argo-workflows
rules:
- alert: WorkflowFailed
expr: |
argo_workflows_count{status="Failed"} > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Workflow failed"
description: "Workflow {{ $labels.name }} has failed"
- alert: WorkflowStuck
expr: |
argo_workflows_count{status="Running"} > 0 and
time() - argo_workflow_start_time > 3600
for: 10m
labels:
severity: warning
annotations:
summary: "Workflow stuck"
description: "Workflow {{ $labels.name }} has been running for over 1 hour"
- name: argo-rollouts
rules:
- alert: RolloutFailed
expr: |
kube_rollout_status_phase{phase="Failed"} == 1
for: 1m
labels:
severity: critical
annotations:
summary: "Rollout failed"
description: "Rollout {{ $labels.rollout }} has failed"
- alert: RolloutDegraded
expr: |
kube_rollout_status_phase{phase="Degraded"} == 1
for: 5m
labels:
severity: warning
annotations:
summary: "Rollout degraded"
description: "Rollout {{ $labels.rollout }} is degraded"
8.6.3 Slack Notification Configuration
# notification-sensor.yaml
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: notification-sensor
namespace: argo-events
spec:
dependencies:
- name: workflow-status
eventSourceName: resource-events
eventName: workflow
filters:
data:
- path: body.status.phase
type: string
value:
- Failed
- Succeeded
triggers:
- template:
name: slack-notification
http:
url: https://hooks.slack.com/services/xxx/yyy/zzz
method: POST
payload:
- src:
dependencyName: workflow-status
dataTemplate: |
{
"attachments": [{
"color": "{{ if eq .Input.body.status.phase \"Succeeded\" }}good{{ else }}danger{{ end }}",
"title": "Workflow {{ .Input.body.status.phase }}",
"fields": [
{"title": "Name", "value": "{{ .Input.body.metadata.name }}", "short": true},
{"title": "Namespace", "value": "{{ .Input.body.metadata.namespace }}", "short": true},
{"title": "Duration", "value": "{{ .Input.body.status.duration }}", "short": true}
]
}]
}
dest: body
8.7 Chapter Summary
This chapter detailed how to integrate the Argo ecosystem into a complete CI/CD platform:
🔄 正在渲染 Mermaid 图表...
Key Points:
- Event-Driven: Use Argo Events to listen for Git events and trigger CI
- CI Pipeline: Use Argo Workflows to execute build, test, and security scans
- GitOps CD: Use Argo CD to sync Git configuration to cluster
- Progressive Delivery: Use Argo Rollouts for safe production releases
- Full Observability: Integrate Prometheus monitoring and Slack notifications
In the next chapter, we will learn about Argo security and permission management.