通过 Jenkins 自动部署到 K8S 集群
📋 目录
- 前置准备
- 环境要求
- 资源准备
- Jenkins 流水线配置
- K8S 部署配置
- 一键安装脚本
- 验证与测试
- 故障排查
🚨 前置准备
1. 环境检查清单
2. 权限准备
💻 环境要求
硬件要求
| 组件 |
CPU |
内存 |
存储 |
| K8S 集群 |
4 核 + |
8GB+ |
50GB+ |
| Jenkins |
2 核 + |
4GB+ |
20GB+ |
| OpenClaw |
2 核 + |
4GB+ |
10GB+ |
软件要求
| 软件 |
版本 |
说明 |
| Kubernetes |
v1.20+ |
K8S 集群 |
| Jenkins |
v2.553+ |
CI/CD 平台 |
| Docker |
v20.10+ |
容器引擎 |
| kubectl |
v1.20+ |
K8S 命令行工具 |
| Helm |
v3.0+ |
K8S 包管理(可选) |
| Git |
v2.30+ |
版本控制 |
📦 资源准备
1. GitLab 仓库
1 2 3 4 5 6 7 8
| OpenClaw/ ├── openclaw-core/ # 核心代码 ├── openclaw-gateway/ # 网关服务 ├── openclaw-agent/ # Agent 服务 ├── openclaw-ui/ # 前端界面 ├── k8s-manifests/ # K8S 部署配置 └── jenkins/ # Jenkins 流水线 └── Jenkinsfile
|
2. Docker 镜像
1 2 3 4 5 6
| 镜像仓库:hb.test/openclaw 镜像列表: - openclaw-core:latest - openclaw-gateway:latest - openclaw-agent:latest - openclaw-ui:latest
|
3. K8S 资源配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
| apiVersion: v1 kind: Namespace metadata: name: openclaw labels: name: openclaw
---
apiVersion: apps/v1 kind: Deployment metadata: name: openclaw-core namespace: openclaw spec: replicas: 2 selector: matchLabels: app: openclaw-core template: metadata: labels: app: openclaw-core spec: containers: - name: openclaw-core image: hb.test/openclaw/openclaw-core:latest ports: - containerPort: 8080 resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" env: - name: DB_HOST value: "mysql.openclaw.svc.cluster.local" - name: DB_PORT value: "3306" - name: DB_NAME value: "openclaw" - name: DB_USER valueFrom: secretKeyRef: name: openclaw-db-secret key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: openclaw-db-secret key: password
---
apiVersion: v1 kind: Service metadata: name: openclaw-core namespace: openclaw spec: selector: app: openclaw-core ports: - port: 80 targetPort: 8080 type: ClusterIP
---
apiVersion: apps/v1 kind: Deployment metadata: name: openclaw-gateway namespace: openclaw spec: replicas: 2 selector: matchLabels: app: openclaw-gateway template: metadata: labels: app: openclaw-gateway spec: containers: - name: openclaw-gateway image: hb.test/openclaw/openclaw-gateway:latest ports: - containerPort: 3000 resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "200m"
---
apiVersion: v1 kind: Service metadata: name: openclaw-gateway namespace: openclaw spec: selector: app: openclaw-gateway ports: - port: 80 targetPort: 3000 type: NodePort externalTrafficPolicy: Local nodePort: 32080
---
apiVersion: apps/v1 kind: Deployment metadata: name: openclaw-agent namespace: openclaw spec: replicas: 3 selector: matchLabels: app: openclaw-agent template: metadata: labels: app: openclaw-agent spec: containers: - name: openclaw-agent image: hb.test/openclaw/openclaw-agent:latest ports: - containerPort: 5000 resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "2Gi" cpu: "1000m" env: - name: CORE_URL value: "http://openclaw-core.openclaw.svc.cluster.local"
---
apiVersion: v1 kind: Service metadata: name: openclaw-agent namespace: openclaw spec: selector: app: openclaw-agent ports: - port: 5000 targetPort: 5000 type: ClusterIP
|
4. 数据库配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc namespace: openclaw spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi
---
apiVersion: apps/v1 kind: Deployment metadata: name: mysql namespace: openclaw spec: replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.0 ports: - containerPort: 3306 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: openclaw-db-secret key: root-password - name: MYSQL_DATABASE value: "openclaw" - name: MYSQL_USER valueFrom: secretKeyRef: name: openclaw-db-secret key: username - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: openclaw-db-secret key: password volumeMounts: - name: mysql-storage mountPath: /var/lib/mysql volumes: - name: mysql-storage persistentVolumeClaim: claimName: mysql-pvc
---
apiVersion: v1 kind: Service metadata: name: mysql namespace: openclaw spec: selector: app: mysql ports: - port: 3306 targetPort: 3306 type: ClusterIP
|
5. K8S Secrets
1 2 3 4 5 6
| kubectl create secret generic openclaw-db-secret \ --from-literal=root-password='Root@123456' \ --from-literal=username='openclaw_user' \ --from-literal=password='User@123456' \ -n openclaw
|
🔧 Jenkins 流水线配置
Jenkinsfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
| pipeline { agent any environment { KUBECONFIG = credentials('k8s-kubeconfig') DOCKER_REGISTRY = 'hb.test' DOCKER_IMAGE_PREFIX = 'openclaw' K8S_NAMESPACE = 'openclaw' } parameters { string(name: 'VERSION', defaultValue: 'latest', description: '部署版本') booleanParam(name: 'FORCE_REBUILD', defaultValue: false, description: '强制重新构建镜像') } stages { stage('Checkout') { steps { echo '📦 拉取代码...' git branch: 'main', url: 'http://192.168.100.191:9910/tech-platfom/openclaw.git', credentialsId: 'gitlab-credentials' } } stage('Build & Push Images') { when { expression { return params.FORCE_REBUILD } } parallel { stage('Build Core') { steps { echo '🔨 构建 Core 镜像...' dir('openclaw-core') { sh """ docker build -t ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-core:${VERSION} . docker push ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-core:${VERSION} """ } } } stage('Build Gateway') { steps { echo '🔨 构建 Gateway 镜像...' dir('openclaw-gateway') { sh """ docker build -t ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-gateway:${VERSION} . docker push ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-gateway:${VERSION} """ } } } stage('Build Agent') { steps { echo '🔨 构建 Agent 镜像...' dir('openclaw-agent') { sh """ docker build -t ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-agent:${VERSION} . docker push ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-agent:${VERSION} """ } } } stage('Build UI') { steps { echo '🔨 构建 UI 镜像...' dir('openclaw-ui') { sh """ docker build -t ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-ui:${VERSION} . docker push ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-ui:${VERSION} """ } } } } } stage('Deploy to K8S') { steps { echo '🚀 部署到 K8S 集群...' script { sh """ kubectl apply -f k8s-manifests/openclaw-namespace.yaml """ sh """ kubectl create secret generic openclaw-db-secret \\ --from-literal=root-password='Root@123456' \\ --from-literal=username='openclaw_user' \\ --from-literal=password='User@123456' \\ -n ${K8S_NAMESPACE} \\ --dry-run=client -o yaml | kubectl apply -f - """ sh """ kubectl apply -f k8s-manifests/ -n ${K8S_NAMESPACE} """ sh """ kubectl set image deployment/openclaw-core \\ openclaw-core=${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-core:${VERSION} \\ -n ${K8S_NAMESPACE} kubectl set image deployment/openclaw-gateway \\ openclaw-gateway=${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-gateway:${VERSION} \\ -n ${K8S_NAMESPACE} kubectl set image deployment/openclaw-agent \\ openclaw-agent=${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-agent:${VERSION} \\ -n ${K8S_NAMESPACE} kubectl set image deployment/openclaw-ui \\ openclaw-ui=${DOCKER_REGISTRY}/${DOCKER_IMAGE_PREFIX}/openclaw-ui:${VERSION} \\ -n ${K8S_NAMESPACE} """ } } } stage('Health Check') { steps { echo '⏳ 等待部署完成...' script { timeout(time: 5, unit: 'MINUTES') { sh """ kubectl rollout status deployment/openclaw-core -n ${K8S_NAMESPACE} kubectl rollout status deployment/openclaw-gateway -n ${K8S_NAMESPACE} kubectl rollout status deployment/openclaw-agent -n ${K8S_NAMESPACE} kubectl rollout status deployment/openclaw-ui -n ${K8S_NAMESPACE} """ } } echo '✅ 健康检查...' script { timeout(time: 2, unit: 'MINUTES') { waitForURL( url: 'http://openclaw-gateway.openclaw.svc.cluster.local/health', timeout: 120000 ) } } } } } post { success { echo '✅ 部署成功!' sh """ NODE_PORT=\$(kubectl get svc openclaw-gateway -n ${K8S_NAMESPACE} -o jsonpath='{.spec.ports[0].nodePort}') NODE_IP=\$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}') echo "====================================" echo "✅ OpenClaw 部署成功!" echo "====================================" echo "访问地址:http://\\${NODE_IP}:\\${NODE_PORT}" echo "====================================" """ } failure { echo '❌ 部署失败!' sh """ echo "=== Core Logs ===" kubectl logs -l app=openclaw-core -n ${K8S_NAMESPACE} --tail=50 echo "=== Gateway Logs ===" kubectl logs -l app=openclaw-gateway -n ${K8S_NAMESPACE} --tail=50 echo "=== Agent Logs ===" kubectl logs -l app=openclaw-agent -n ${K8S_NAMESPACE} --tail=50 """ } } }
|
📝 一键安装脚本
install-openclaw.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| #!/bin/bash set -e
echo "======================================" echo " OpenClaw 一键安装脚本" echo "======================================"
K8S_NAMESPACE="openclaw" DOCKER_REGISTRY="hb.test" DOCKER_IMAGE_PREFIX="openclaw" VERSION="latest"
RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m'
check_prerequisites() { echo -e "${YELLOW}📋 检查前置条件...${NC}" if ! command -v kubectl &> /dev/null; then echo -e "${RED}❌ kubectl 未安装${NC}" exit 1 fi if ! kubectl cluster-info &> /dev/null; then echo -e "${RED}❌ 无法连接到 K8S 集群${NC}" exit 1 fi if ! command -v docker &> /dev/null; then echo -e "${RED}❌ Docker 未安装${NC}" exit 1 fi echo -e "${GREEN}✅ 前置检查通过${NC}" }
create_namespace() { echo -e "${YELLOW}📦 创建命名空间...${NC}" kubectl create namespace ${K8S_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f - echo -e "${GREEN}✅ 命名空间创建完成${NC}" }
create_secrets() { echo -e "${YELLOW}🔐 创建 Secrets...${NC}" kubectl create secret generic openclaw-db-secret \\ --from-literal=root-password='Root@123456' \\ --from-literal=username='openclaw_user' \\ --from-literal=password='User@123456' \\ -n ${K8S_NAMESPACE} \\ --dry-run=client -o yaml | kubectl apply -f - echo -e "${GREEN}✅ Secrets 创建完成${NC}" }
deploy_k8s() { echo -e "${YELLOW}🚀 部署 K8S 资源...${NC}" kubectl apply -f k8s-manifests/ -n ${K8S_NAMESPACE} echo -e "${GREEN}✅ K8S 资源部署完成${NC}" }
wait_deployment() { echo -e "${YELLOW}⏳ 等待部署完成...${NC}" kubectl rollout status deployment/openclaw-core -n ${K8S_NAMESPACE} kubectl rollout status deployment/openclaw-gateway -n ${K8S_NAMESPACE} kubectl rollout status deployment/openclaw-agent -n ${K8S_NAMESPACE} kubectl rollout status deployment/openclaw-ui -n ${K8S_NAMESPACE} echo -e "${GREEN}✅ 所有部署完成${NC}" }
health_check() { echo -e "${YELLOW}⏳ 执行健康检查...${NC}" sleep 10 kubectl get pods -n ${K8S_NAMESPACE} kubectl get svc -n ${K8S_NAMESPACE} echo -e "${GREEN}✅ 健康检查通过${NC}" }
get_access_info() { echo -e "${YELLOW}📊 获取访问信息...${NC}" NODE_PORT=$(kubectl get svc openclaw-gateway -n ${K8S_NAMESPACE} -o jsonpath='{.spec.ports[0].nodePort}') NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}') echo "" echo -e "${GREEN}======================================" echo " ✅ OpenClaw 部署成功!" echo "======================================${NC}" echo "访问地址:http://${NODE_IP}:${NODE_PORT}" echo "命名空间:${K8S_NAMESPACE}" echo "======================================" echo "" }
main() { check_prerequisites create_namespace create_secrets deploy_k8s wait_deployment health_check get_access_info echo -e "${GREEN}🎉 安装完成!${NC}" }
main "$@"
|
✅ 验证与测试
1. 检查 Pod 状态
1
| kubectl get pods -n openclaw
|
预期输出:
1 2 3 4 5 6
| NAME READY STATUS RESTARTS AGE openclaw-core-xxxxx-xxxxx 1/1 Running 0 5m openclaw-gateway-xxxxx-xxxxx 1/1 Running 0 5m openclaw-agent-xxxxx-xxxxx 1/1 Running 0 5m openclaw-ui-xxxxx-xxxxx 1/1 Running 0 5m mysql-xxxxx-xxxxx 1/1 Running 0 5m
|
2. 检查服务状态
1
| kubectl get svc -n openclaw
|
预期输出:
1 2 3 4 5
| NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE openclaw-core ClusterIP 10.x.x.x <none> 80/TCP 5m openclaw-gateway NodePort 10.x.x.x <none> 80:32080/TCP 5m openclaw-agent ClusterIP 10.x.x.x <none> 5000/TCP 5m mysql ClusterIP 10.x.x.x <none> 3306/TCP 5m
|
3. 测试访问
1 2 3 4 5 6
| NODE_PORT=$(kubectl get svc openclaw-gateway -n openclaw -o jsonpath='{.spec.ports[0].nodePort}') NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}')
curl http://${NODE_IP}:${NODE_PORT}/health
|
预期输出:
1
| {"status":"ok","message":"OpenClaw Gateway is running"}
|
4. 查看日志
1 2 3 4 5 6 7 8
| kubectl logs -l app=openclaw-core -n openclaw --tail=50
kubectl logs -l app=openclaw-gateway -n openclaw --tail=50
kubectl logs -l app=openclaw-agent -n openclaw --tail=50
|
🔧 故障排查
问题 1:Pod 无法启动
症状:
1 2 3
| kubectl get pods -n openclaw NAME READY STATUS RESTARTS AGE openclaw-core-xxxxx-xxxxx 0/1 ImagePullBackOff 0 5m
|
解决方案:
1 2 3 4 5 6 7 8
| kubectl describe pod openclaw-core-xxxxx-xxxxx -n openclaw
kubectl get secrets -n openclaw
docker pull hb.test/openclaw/openclaw-core:latest
|
问题 2:数据库连接失败
症状:
1
| Error: connect ECONNREFUSED 10.x.x.x:3306
|
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12
| kubectl get pods -l app=mysql -n openclaw
kubectl logs -l app=mysql -n openclaw
kubectl get secret openclaw-db-secret -n openclaw -o yaml
kubectl run -it --rm mysql-client --image=mysql:8.0 --restart=Never -n openclaw -- \\ mysql -h mysql -u openclaw_user -p
|
问题 3:服务无法访问
症状:
1
| curl: (7) Failed to connect to <IP> port <PORT>: Connection refused
|
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12
| kubectl get svc openclaw-gateway -n openclaw -o yaml
kubectl get endpoints openclaw-gateway -n openclaw
kubectl get pods --show-labels -n openclaw
kubectl run -it --rm test --image=curlimages/curl --restart=Never -n openclaw -- \\ curl http://openclaw-gateway/health
|
问题 4:Jenkins 构建失败
症状:
1
| ERROR: Failed to pull image
|
解决方案:
1 2 3 4 5 6 7 8
|
docker login hb.test
|
📖 参考链接
一键安装,自动部署,快速上线!🚀
所有配置已脱敏,实际部署时请替换为真实配置!✅
AI Agent / AI Coding 实战
需要企业级 AI Agent / OpenClaw 落地方案?
围绕 AI Agent 架构、多 Agent 协作、OpenClaw、MCP 和企业知识库接入,提供咨询、PoC、内训与私有化部署。
合作咨询:chartvip@hotmail.com