引言
在现代软件开发中,持续集成和持续部署(CI/CD)已经成为提升团队交付效率、保证代码质量的关键实践。本文将详细介绍如何使用Docker、Kubernetes和Jenkins构建一个完整的CI/CD自动化部署流水线,帮助开发者快速实现从代码提交到生产环境部署的全流程自动化。
什么是CI/CD
CI/CD(Continuous Integration/Continuous Delivery)是一套软件开发实践,通过自动化流程来提高软件交付的质量和速度。其中:
- 持续集成(CI):开发者频繁地将代码变更合并到主分支,并通过自动化测试确保代码质量
- 持续部署(CD):在代码通过测试后,自动将其部署到生产环境或预发布环境
技术栈概述
Docker容器化技术
Docker是容器化技术的代表,它允许我们将应用程序及其依赖打包成轻量级、可移植的容器。Docker的优势包括:
- 环境一致性
- 快速启动和部署
- 资源隔离和优化
- 版本控制和镜像管理
Kubernetes集群编排
Kubernetes(简称k8s)是容器编排平台,用于自动化部署、扩展和管理容器化应用程序。其核心功能包括:
- 自动化部署和回滚
- 服务发现和负载均衡
- 弹性伸缩
- 存储编排
- 自我修复能力
Jenkins自动化构建工具
Jenkins是一个开源的持续集成和持续交付工具,提供了丰富的插件生态系统。它支持:
- 多种构建触发方式
- 灵活的流水线配置
- 丰富的报告和监控功能
- 集成各种开发工具
环境准备
系统要求
在开始之前,请确保满足以下环境要求:
# Docker版本要求
docker --version
# 推荐版本:Docker CE 20.10+
# Kubernetes集群要求
kubectl version
# 推荐版本:Kubernetes 1.20+
# Jenkins要求
jenkins --version
# 推荐版本:Jenkins 2.300+
基础环境搭建
1. Docker安装
# Ubuntu/Debian系统安装Docker
sudo apt update
sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 启动Docker服务
sudo systemctl start docker
sudo systemctl enable docker
2. Kubernetes集群搭建
# 使用kubeadm搭建单节点集群(测试环境)
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
# 配置kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 安装Flannel网络插件
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
3. Jenkins安装
# 使用Docker运行Jenkins
docker run -d \
--name jenkins \
--restart=unless-stopped \
-p 8080:8080 \
-p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts
# 查看初始密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
Docker容器化应用
创建示例应用
我们以一个简单的Node.js应用为例来演示整个流程:
// app.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({
message: 'Hello from Docker + Kubernetes + Jenkins CI/CD!',
timestamp: new Date().toISOString(),
version: '1.0.0'
});
});
app.get('/health', (req, res) => {
res.status(200).json({ status: 'healthy' });
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// package.json
{
"name": "docker-k8s-jenkins-demo",
"version": "1.0.0",
"description": "Demo application for CI/CD pipeline",
"main": "app.js",
"scripts": {
"start": "node app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"express": "^4.18.0"
},
"engines": {
"node": ">=12.0.0"
}
}
编写Dockerfile
# Dockerfile
FROM node:16-alpine
# 设置工作目录
WORKDIR /app
# 复制package.json和package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制应用代码
COPY . .
# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 启动应用
CMD ["npm", "start"]
构建和推送Docker镜像
# 构建Docker镜像
docker build -t myapp:latest .
# 登录到Docker Hub(如果使用)
docker login
# 标签镜像
docker tag myapp:latest your-username/myapp:latest
# 推送到仓库
docker push your-username/myapp:latest
Kubernetes部署配置
创建命名空间
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ci-cd-demo
labels:
name: ci-cd-demo
部署应用配置
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
namespace: ci-cd-demo
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: your-username/myapp:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
服务配置
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: ci-cd-demo
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 3000
protocol: TCP
type: LoadBalancer
配置文件应用
# 应用配置
kubectl apply -f namespace.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# 查看部署状态
kubectl get pods -n ci-cd-demo
kubectl get svc -n ci-cd-demo
Jenkins流水线配置
安装必要插件
在Jenkins中安装以下核心插件:
- Docker Pipeline
- Kubernetes Continuous Deploy
- Git Plugin
- Pipeline: GitHub Groovy Libraries
创建流水线项目
1. 简单的Pipeline脚本
// Jenkinsfile
pipeline {
agent any
environment {
DOCKER_IMAGE = 'your-username/myapp'
KUBE_NAMESPACE = 'ci-cd-demo'
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/your-username/myapp.git'
}
}
stage('Build Docker Image') {
steps {
script {
docker.build("${DOCKER_IMAGE}:${env.BUILD_NUMBER}")
}
}
}
stage('Push to Registry') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', 'docker-hub-credentials') {
docker.image("${DOCKER_IMAGE}:${env.BUILD_NUMBER}").push()
}
}
}
}
stage('Deploy to Kubernetes') {
steps {
script {
withKubeConfig([credentialsId: 'k8s-config']) {
sh """
kubectl set image deployment/myapp-deployment myapp=${DOCKER_IMAGE}:${env.BUILD_NUMBER} -n ${KUBE_NAMESPACE}
"""
}
}
}
}
stage('Verify Deployment') {
steps {
script {
withKubeConfig([credentialsId: 'k8s-config']) {
sh """
kubectl rollout status deployment/myapp-deployment -n ${KUBE_NAMESPACE}
"""
}
}
}
}
}
post {
success {
echo 'Pipeline completed successfully!'
}
failure {
echo 'Pipeline failed!'
}
}
}
2. 增强版Pipeline脚本
// Jenkinsfile - 增强版
pipeline {
agent any
environment {
DOCKER_IMAGE = 'your-username/myapp'
KUBE_NAMESPACE = 'ci-cd-demo'
REGISTRY_URL = 'https://index.docker.io/v1/'
GIT_COMMIT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
}
stages {
stage('Checkout') {
steps {
checkout scm
echo "Building commit: ${GIT_COMMIT}"
}
}
stage('Unit Tests') {
steps {
script {
sh 'npm install'
sh 'npm test'
}
}
post {
success {
echo 'Unit tests passed!'
}
failure {
error 'Unit tests failed!'
}
}
}
stage('Build Docker Image') {
steps {
script {
def dockerImage = docker.build("${DOCKER_IMAGE}:${GIT_COMMIT}")
echo "Docker image built: ${DOCKER_IMAGE}:${GIT_COMMIT}"
}
}
}
stage('Push to Registry') {
steps {
script {
docker.withRegistry(REGISTRY_URL, 'docker-hub-credentials') {
docker.image("${DOCKER_IMAGE}:${GIT_COMMIT}").push()
docker.image("${DOCKER_IMAGE}:latest").push()
}
}
}
}
stage('Deploy to Staging') {
steps {
script {
withKubeConfig([credentialsId: 'k8s-staging-config']) {
sh """
kubectl set image deployment/myapp-deployment myapp=${DOCKER_IMAGE}:${GIT_COMMIT} -n ${KUBE_NAMESPACE}
"""
sh "kubectl rollout status deployment/myapp-deployment -n ${KUBE_NAMESPACE}"
}
}
}
}
stage('Integration Tests') {
steps {
script {
// 这里可以添加集成测试逻辑
echo 'Running integration tests...'
// 示例:等待应用启动后进行测试
sh 'sleep 30'
}
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
script {
withKubeConfig([credentialsId: 'k8s-prod-config']) {
sh """
kubectl set image deployment/myapp-deployment myapp=${DOCKER_IMAGE}:${GIT_COMMIT} -n ${KUBE_NAMESPACE}
"""
sh "kubectl rollout status deployment/myapp-deployment -n ${KUBE_NAMESPACE}"
}
}
}
}
}
post {
always {
echo 'Pipeline execution completed'
}
success {
echo 'Pipeline completed successfully!'
slackSend channel: '#deployments', message: "✅ Deployment successful for ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
failure {
echo 'Pipeline failed!'
slackSend channel: '#deployments', message: "❌ Deployment failed for ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
}
}
Jenkins配置说明
1. 凭据管理
在Jenkins中配置必要的凭据:
- Docker Hub凭据(用于推送镜像)
- Kubernetes配置凭据(用于访问集群)
# 创建Jenkins凭据的示例命令
# 在Jenkins界面中通过Manage Jenkins -> Manage Credentials进行配置
2. 系统配置
在Jenkins系统配置中添加:
- Docker Daemon URL
- Kubernetes API URL
- 集群认证信息
自动化测试集成
单元测试配置
// test/app.test.js
const request = require('supertest');
const app = require('../app');
describe('API Tests', () => {
test('should return welcome message', async () => {
const response = await request(app)
.get('/')
.expect(200)
.expect('Content-Type', /json/);
expect(response.body.message).toBe('Hello from Docker + Kubernetes + Jenkins CI/CD!');
});
test('should return health status', async () => {
const response = await request(app)
.get('/health')
.expect(200);
expect(response.body.status).toBe('healthy');
});
});
// package.json - 添加测试脚本
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch"
},
"devDependencies": {
"jest": "^28.0.0",
"supertest": "^6.0.0"
}
}
测试覆盖率报告
// jest.config.js
module.exports = {
collectCoverageFrom: [
'src/**/*.{js,jsx}',
'!src/index.js',
'!src/server.js'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov'],
testEnvironment: 'node'
};
监控和日志管理
Prometheus监控配置
# prometheus-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: ci-cd-demo
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
- job_name: 'myapp'
static_configs:
- targets: ['myapp-service.ci-cd-demo.svc.cluster.local:80']
日志收集配置
# fluentd-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
namespace: ci-cd-demo
data:
fluent.conf: |
<source>
@type tail
path /var/log/containers/*.log
pos_file /var/log/fluentd-containers.log.pos
tag kubernetes.*
read_from_head true
<parse>
@type json
</parse>
</source>
<match **>
@type stdout
</match>
安全最佳实践
容器安全扫描
# 使用Trivy进行安全扫描
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image your-username/myapp:latest
# 或者使用Clair进行扫描
docker run -d --name clair -p 6060:6060 quay.io/coreos/clair:v2.1.0
权限控制
# rbac.yaml - Kubernetes RBAC配置
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-sa
namespace: ci-cd-demo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: ci-cd-demo
name: jenkins-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: jenkins-role-binding
namespace: ci-cd-demo
subjects:
- kind: ServiceAccount
name: jenkins-sa
namespace: ci-cd-demo
roleRef:
kind: Role
name: jenkins-role
apiGroup: rbac.authorization.k8s.io
性能优化建议
镜像优化
# 优化后的Dockerfile
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
# 使用非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CMD ["npm", "start"]
资源限制优化
# deployment.yaml - 优化资源配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
namespace: ci-cd-demo
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: your-username/myapp:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
故障排除和调试
常见问题排查
# 检查Pod状态
kubectl get pods -n ci-cd-demo
# 查看Pod详细信息
kubectl describe pod <pod-name> -n ci-cd-demo
# 查看日志
kubectl logs <pod-name> -n ci-cd-demo
# 检查部署状态
kubectl rollout status deployment/myapp-deployment -n ci-cd-demo
# 检查服务状态
kubectl get svc -n ci-cd-demo
Jenkins流水线调试
// 添加调试信息到Pipeline
stage('Debug Info') {
steps {
script {
sh 'echo "Current branch: ${BRANCH_NAME}"'
sh 'echo "Build number: ${BUILD_NUMBER}"'
sh 'echo "Workspace: ${WORKSPACE}"'
sh 'pwd'
sh 'ls -la'
}
}
}
高级功能扩展
蓝绿部署策略
# blue-green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-blue
namespace: ci-cd-demo
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: myapp
image: your-username/myapp:v1.0.0
ports:
- containerPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-green
namespace: ci-cd-demo
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: myapp
image: your-username/myapp:v2.0.0
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: ci-cd-demo
spec:
selector:
app: myapp
version: green # 默认指向green版本
ports:
- port: 80
targetPort: 3000
回滚机制
stage('Rollback') {
steps {
script {
withKubeConfig([credentialsId: 'k8s-config']) {
sh """
kubectl rollout undo deployment/myapp-deployment -n ${KUBE_NAMESPACE}
"""
}
}
}
}
总结
通过本文的详细演示,我们成功构建了一个完整的CI/CD自动化部署流水线,涵盖了从代码提交到生产环境部署的全流程。这个流水线具有以下特点:
- 容器化部署:使用Docker实现应用的标准化打包和部署
- 集群编排:利用Kubernetes实现高可用、可扩展的应用部署
- 自动化构建:通过Jenkins实现完整的CI/CD流程自动化
- 安全可靠:包含安全扫描、权限控制等最佳实践
- 监控告警:集成监控和日志收集功能
这个自动化流水线能够显著提升团队的开发效率,减少人为错误,确保软件交付的质量和速度。通过持续优化和完善,可以进一步扩展功能,满足更复杂的业务需求。
在实际使用中,建议根据具体的项目需求和环境特点进行相应的调整和优化,同时建立完善的监控和告警机制,确保系统的稳定运行。

评论 (0)