24 KiB
Jenkins CI/CD迁移实施计划
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: 将novalon-website项目从Woodpecker CI迁移到Jenkins,实现稳定可靠的CI/CD流程。
Architecture: 在生产服务器(139.155.109.62)上使用Docker部署Jenkins Master,通过Shell模式直接在宿主机执行构建和部署,避免创建临时Docker容器,实现零网络传输的高效构建流程。
Tech Stack: Jenkins LTS, Docker, Nginx, Node.js 20, npm, Git, Shell Script
前置条件
- 生产服务器:139.155.109.62
- 域名:ci.f.novalon.cn
- 项目路径:/home/novalon/docker-app/novalon-website
- SSH访问权限:root用户
- Docker已安装并运行
Phase 1: 环境准备(第1天)
Task 1: 创建Jenkins部署目录结构
Files:
- Create:
/home/novalon/docker-app/jenkins/docker-compose.yml - Create:
/home/novalon/docker-app/jenkins/.env - Create:
/home/novalon/docker-app/jenkins/backup.sh
Step 1: SSH连接到生产服务器
ssh root@139.155.109.62
Expected: 成功登录到服务器
Step 2: 创建Jenkins目录
mkdir -p /home/novalon/docker-app/jenkins
cd /home/novalon/docker-app/jenkins
Expected: 目录创建成功
Step 3: 创建docker-compose.yml文件
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
restart: unless-stopped
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /home/novalon/docker-app:/home/novalon/docker-app
- /root/.ssh:/root/.ssh:ro
environment:
- JENKINS_OPTS=--prefix=/jenkins
- JAVA_OPTS=-Xmx2g -Xms512m -Duser.timezone=Asia/Shanghai
user: root
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/jenkins/login"]
interval: 30s
timeout: 10s
retries: 5
volumes:
jenkins_home:
driver: local
Step 4: 创建环境变量文件
cat > .env << 'EOF'
JENKINS_VERSION=lts
JAVA_OPTS=-Xmx2g -Xms512m -Duser.timezone=Asia/Shanghai
EOF
Step 5: 创建备份脚本
cat > backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/home/novalon/backups/jenkins"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
docker exec jenkins tar czf - /var/jenkins_home > $BACKUP_DIR/jenkins_backup_$DATE.tar.gz
find $BACKUP_DIR -name "jenkins_backup_*.tar.gz" -mtime +7 -delete
echo "Backup completed: jenkins_backup_$DATE.tar.gz"
EOF
chmod +x backup.sh
Step 6: 提交配置
git add /home/novalon/docker-app/jenkins/
git commit -m "feat: add Jenkins deployment configuration"
Task 2: 启动Jenkins容器
Files:
- Modify:
/home/novalon/docker-app/jenkins/docker-compose.yml
Step 1: 启动Jenkins容器
cd /home/novalon/docker-app/jenkins
docker-compose up -d
Expected:
Creating jenkins ... done
Step 2: 检查容器状态
docker ps | grep jenkins
Expected:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
<container_id> jenkins/jenkins:lts "/usr/local/bin/jenk…" 1 minute ago Up 1 minute 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp jenkins
Step 3: 查看Jenkins初始密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
Expected: 输出一个32位的初始管理员密码
Step 4: 保存初始密码
INITIAL_PASSWORD=$(docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword)
echo "Jenkins Initial Password: $INITIAL_PASSWORD" > /home/novalon/docker-app/jenkins/initial_password.txt
Step 5: 验证Jenkins启动
curl -I http://localhost:8080/jenkins/
Expected:
HTTP/1.1 302 Found
Location: http://localhost:8080/jenkins/login
Task 3: 配置Nginx反向代理
Files:
- Create:
/etc/nginx/sites-available/ci.f.novalon.cn.conf - Modify:
/etc/nginx/sites-enabled/(创建软链接)
Step 1: 创建Nginx配置文件
cat > /etc/nginx/sites-available/ci.f.novalon.cn.conf << 'EOF'
upstream jenkins {
server localhost:8080;
}
server {
listen 80;
server_name ci.f.novalon.cn;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name ci.f.novalon.cn;
# SSL证书配置(假设已有证书)
ssl_certificate /etc/nginx/ssl/novalon.cn.crt;
ssl_certificate_key /etc/nginx/ssl/novalon.cn.key;
# SSL优化配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 安全头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Jenkins反向代理
location / {
proxy_pass http://jenkins;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
# 增大上传限制
client_max_body_size 100m;
# 超时配置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# WebSocket支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 访问日志
access_log /var/log/nginx/jenkins-access.log;
error_log /var/log/nginx/jenkins-error.log;
}
EOF
Step 2: 测试Nginx配置
nginx -t
Expected:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Step 3: 启用站点配置
ln -sf /etc/nginx/sites-available/ci.f.novalon.cn.conf /etc/nginx/sites-enabled/
Step 4: 重载Nginx
systemctl reload nginx
Expected:
Job completed successfully
Step 5: 验证HTTPS访问
curl -I https://ci.f.novalon.cn/jenkins/
Expected:
HTTP/2 302
location: https://ci.f.novalon.cn/jenkins/login
Task 4: 初始化Jenkins
Files:
- None (通过Web界面操作)
Step 1: 访问Jenkins Web界面
打开浏览器访问:https://ci.f.novalon.cn/jenkins/
Expected: 显示Jenkins解锁页面
Step 2: 输入初始密码
cat /home/novalon/docker-app/jenkins/initial_password.txt
复制密码并粘贴到Web界面
Step 3: 安装推荐插件
在Web界面选择"Install suggested plugins"
Expected: 自动安装以下插件:
- Git Plugin
- Pipeline
- Workspace Cleanup
- Timestamper
- Credentials Binding
- SSH Build Agents
- Docker Pipeline
Step 4: 创建管理员账户
在Web界面填写:
- Username: admin
- Password: <设置强密码>
- Full name: Jenkins Admin
- E-mail address: admin@novalon.cn
Step 5: 配置Jenkins URL
确认Jenkins URL为:https://ci.f.novalon.cn/jenkins/
Step 6: 保存配置
点击"Save and Finish",然后点击"Start using Jenkins"
Expected: 进入Jenkins主界面
Task 5: 安装必要插件
Files:
- None (通过Web界面操作)
Step 1: 进入插件管理
导航到:Manage Jenkins → Manage Plugins → Available
Step 2: 搜索并安装以下插件
勾选以下插件:
- NodeJS Plugin
- Pipeline Utility Steps
- Git Parameter
- Build Timeout
- Email Extension
- AnsiColor
Step 3: 点击"Install without restart"
Expected: 插件安装完成
Step 4: 验证插件安装
导航到:Manage Jenkins → Manage Plugins → Installed
确认所有插件都已安装
Task 6: 配置全局工具
Files:
- None (通过Web界面操作)
Step 1: 配置Node.js
导航到:Manage Jenkins → Global Tool Configuration
找到"NodeJS"部分,点击"Add NodeJS":
- Name: NodeJS-20
- Install automatically: 勾选
- Version: NodeJS 20.x (选择最新的20.x版本)
Step 2: 配置Git
找到"Git"部分:
- Name: Default
- Path to Git executable: git (使用系统Git)
Step 3: 配置Maven(可选)
如果需要Java项目支持:
- Name: Maven-3.9
- Install automatically: 勾选
- Version: 3.9.x
Step 4: 保存配置
点击"Save"
Phase 2: 配置CI/CD流水线(第2天)
Task 7: 创建Jenkins凭据
Files:
- None (通过Web界面操作)
Step 1: 进入凭据管理
导航到:Manage Jenkins → Credentials → System → Global credentials
Step 2: 添加Git凭据
点击"Add Credentials":
- Kind: Username with password
- Scope: Global
- Username: <Git用户名>
- Password: <Git密码或Token>
- ID: git-credentials
- Description: Git Repository Credentials
Step 3: 添加SSH凭据
点击"Add Credentials":
- Kind: SSH Username with private key
- Scope: Global
- Username: root
- Private Key: Enter directly
- Key: <粘贴SSH私钥内容>
- ID: ssh-credentials
- Description: SSH Key for Deployment
Step 4: 验证凭据
确认两个凭据都已出现在列表中
Task 8: 创建Jenkinsfile
Files:
- Create:
/home/novalon/docker-app/novalon-website/Jenkinsfile
Step 1: 切换到项目目录
cd /home/novalon/docker-app/novalon-website
Step 2: 创建Jenkinsfile
pipeline {
agent any
environment {
NODE_ENV = 'production'
PROJECT_PATH = '/home/novalon/docker-app/novalon-website'
BACKUP_PATH = '/home/novalon/backups/novalon-website'
}
tools {
nodejs 'NodeJS-20'
}
options {
timeout(time: 30, unit: 'MINUTES')
timestamps()
ansiColor('xterm')
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Checkout') {
steps {
echo '📥 Checking out code...'
checkout scm
sh 'git log -1 --pretty=format:"%h - %an, %ar : %s"'
}
}
stage('Install Dependencies') {
steps {
echo '📦 Installing dependencies...'
sh '''
npm ci --legacy-peer-deps --prefer-offline || \
npm install --legacy-peer-deps
'''
}
}
stage('Quality Gates') {
parallel {
stage('Lint') {
steps {
echo '🔍 Running linter...'
sh 'npm run lint'
}
}
stage('Type Check') {
steps {
echo '🔍 Running type checker...'
sh 'npm run type-check'
}
}
}
}
stage('Build') {
steps {
echo '🏗️ Building production artifacts...'
sh '''
npm run build
ls -la dist/
'''
}
}
stage('Backup Current Version') {
steps {
echo '💾 Backing up current version...'
sh '''
mkdir -p ${BACKUP_PATH}
if [ -d "${PROJECT_PATH}/dist" ]; then
tar czf ${BACKUP_PATH}/backup_$(date +%Y%m%d_%H%M%S).tar.gz \
-C ${PROJECT_PATH} dist/ public/ package.json
echo "✅ Backup created"
else
echo "⚠️ No existing dist to backup"
fi
'''
}
}
stage('Deploy') {
steps {
echo '🚀 Deploying to production...'
sh '''
cd ${PROJECT_PATH}
# 停止现有容器
docker-compose down || true
# 启动新容器
docker-compose -f docker-compose.server.yml up -d --build
# 等待容器启动
sleep 10
# 检查容器状态
docker-compose -f docker-compose.server.yml ps
'''
}
}
stage('Health Check') {
steps {
echo '🏥 Checking application health...'
sh '''
# 等待应用启动
sleep 5
# 检查HTTP响应
for i in {1..10}; do
if curl -f http://localhost:3000 > /dev/null 2>&1; then
echo "✅ Application is healthy"
exit 0
fi
echo "Waiting for application to start... ($i/10)"
sleep 3
done
echo "❌ Application health check failed"
exit 1
'''
}
}
}
post {
success {
echo '''
✅ ========================================
✅ Deployment Successful!
✅ ========================================
✅ Project: novalon-website
✅ Branch: ${GIT_BRANCH}
✅ Commit: ${GIT_COMMIT}
✅ Build: #${BUILD_NUMBER}
✅ ========================================
'''
}
failure {
echo '''
❌ ========================================
❌ Deployment Failed!
❌ ========================================
❌ Project: novalon-website
❌ Branch: ${GIT_BRANCH}
❌ Commit: ${GIT_COMMIT}
❌ Build: #${BUILD_NUMBER}
❌ ========================================
❌ Check the logs for details
❌ ========================================
'''
}
always {
cleanWs()
}
}
}
Step 3: 提交Jenkinsfile
git add Jenkinsfile
git commit -m "feat: add Jenkinsfile for CI/CD pipeline"
git push origin main
Task 9: 创建Jenkins Pipeline Job
Files:
- None (通过Web界面操作)
Step 1: 创建新Job
在Jenkins主界面点击"New Item"
Step 2: 配置Job
- Enter an item name: novalon-website
- Select: Pipeline
- Click: OK
Step 3: 配置General
- Description: Novalon Website CI/CD Pipeline
- Discard old builds: 勾选
- Max # of builds to keep: 10
Step 4: 配置Build Triggers
- Poll SCM: 勾选
- Schedule:
H/5 * * * *(每5分钟检查一次)
- Schedule:
Step 5: 配置Pipeline
- Definition: Pipeline script from SCM
- SCM: Git
- Repository URL: https://git.f.novalon.cn/novalon/novalon-website.git
- Credentials: 选择之前创建的git-credentials
- Branch Specifier:
*/main - Script Path: Jenkinsfile
Step 6: 保存配置
点击"Save"
Task 10: 配置Git Webhook
Files:
- None (通过Web界面操作)
Step 1: 获取Jenkins Webhook URL
https://ci.f.novalon.cn/jenkins/git/notifyCommit?url=https://git.f.novalon.cn/novalon/novalon-website.git
Step 2: 在Git仓库配置Webhook
访问Git仓库设置页面:
- Webhook URL:
https://ci.f.novalon.cn/jenkins/git/notifyCommit?url=https://git.f.novalon.cn/novalon/novalon-website.git - Content type: application/json
- Trigger: Push events
- Active: 勾选
Step 3: 测试Webhook
在Git仓库页面点击"Test Webhook" → "Push"
Expected: Jenkins触发新的构建
Phase 3: 测试和优化(第3天)
Task 11: 执行完整构建测试
Files:
- None (通过Web界面操作)
Step 1: 手动触发构建
在Jenkins Job页面点击"Build Now"
Step 2: 观察构建过程
点击构建编号,然后点击"Console Output"
Expected:
- 所有阶段依次执行
- 没有错误信息
- 最终显示"Deployment Successful!"
Step 3: 验证部署
访问生产网站:https://novalon.cn
Expected: 网站正常运行,显示最新内容
Step 4: 检查容器状态
ssh root@139.155.109.62
cd /home/novalon/docker-app/novalon-website
docker-compose -f docker-compose.server.yml ps
Expected:
NAME COMMAND SERVICE STATUS PORTS
novalon-website "node dist/server" app running 0.0.0.0:3000->3000/tcp
Task 12: 性能优化
Files:
- Modify:
/home/novalon/docker-app/novalon-website/Jenkinsfile
Step 1: 添加npm缓存配置
在Jenkinsfile的"Install Dependencies"阶段添加:
stage('Install Dependencies') {
steps {
echo '📦 Installing dependencies...'
sh '''
npm config set cache /tmp/npm-cache --global
npm ci --legacy-peer-deps --prefer-offline --cache /tmp/npm-cache || \
npm install --legacy-peer-deps --cache /tmp/npm-cache
'''
}
}
Step 2: 配置构建缓存
在Jenkinsfile的options部分添加:
options {
timeout(time: 30, unit: 'MINUTES')
timestamps()
ansiColor('xterm')
buildDiscarder(logRotator(numToKeepStr: '10'))
// 添加缓存配置
skipDefaultCheckout(false)
disableConcurrentBuilds()
}
Step 3: 提交优化
git add Jenkinsfile
git commit -m "perf: optimize Jenkins pipeline with caching"
git push origin main
Step 4: 测试优化效果
再次触发构建,观察构建时间
Expected: 构建时间缩短20-30%
Task 13: 配置邮件通知
Files:
- None (通过Web界面操作)
Step 1: 配置邮件服务器
导航到:Manage Jenkins → Configure System
找到"Extended E-mail Notification":
- SMTP server: smtp.novalon.cn
- Default Content Type: HTML
- Default Recipients: team@novalon.cn
Step 2: 在Jenkinsfile添加邮件通知
post {
success {
emailext (
subject: "✅ Jenkins Build Success: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
<h2>Build Successful</h2>
<p><strong>Project:</strong> ${env.JOB_NAME}</p>
<p><strong>Build Number:</strong> ${env.BUILD_NUMBER}</p>
<p><strong>Branch:</strong> ${env.GIT_BRANCH}</p>
<p><strong>Commit:</strong> ${env.GIT_COMMIT}</p>
<p><a href="${env.BUILD_URL}">View Build Details</a></p>
""",
to: 'team@novalon.cn'
)
}
failure {
emailext (
subject: "❌ Jenkins Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
<h2>Build Failed</h2>
<p><strong>Project:</strong> ${env.JOB_NAME}</p>
<p><strong>Build Number:</strong> ${env.BUILD_NUMBER}</p>
<p><strong>Branch:</strong> ${env.GIT_BRANCH}</p>
<p><strong>Commit:</strong> ${env.GIT_COMMIT}</p>
<p><a href="${env.BUILD_URL}console">View Console Output</a></p>
""",
to: 'team@novalon.cn'
)
}
}
Step 3: 测试邮件通知
触发一次构建,检查邮件是否正常发送
Task 14: 编写操作文档
Files:
- Create:
/home/novalon/docker-app/novalon-website/docs/jenkins/JENKINS_GUIDE.md
Step 1: 创建文档目录
mkdir -p docs/jenkins
Step 2: 编写操作指南
# Jenkins CI/CD 操作指南
## 访问Jenkins
- URL: https://ci.f.novalon.cn/jenkins/
- 用户名: admin
- 密码: <存储在安全位置>
## 触发构建
### 方式1: 自动触发
- 代码推送到main分支时自动触发
- Webhook配置在Git仓库设置中
### 方式2: 手动触发
1. 登录Jenkins
2. 选择Job: novalon-website
3. 点击"Build Now"
## 查看构建日志
1. 点击构建编号
2. 点击"Console Output"
## 回滚操作
如果部署失败,可以回滚到上一个版本:
\`\`\`bash
cd /home/novalon/docker-app/novalon-website
# 查看备份列表
ls -lt /home/novalon/backups/novalon-website/
# 恢复备份
tar xzf /home/novalon/backups/novalon-website/backup_YYYYMMDD_HHMMSS.tar.gz -C ./
# 重启容器
docker-compose -f docker-compose.server.yml restart
\`\`\`
## 故障排查
### 构建失败
1. 检查Console Output查看错误信息
2. 检查依赖是否正确安装
3. 检查代码是否有语法错误
### 部署失败
1. 检查容器日志: `docker-compose logs`
2. 检查端口占用: `netstat -tlnp | grep 3000`
3. 检查磁盘空间: `df -h`
### Webhook不触发
1. 检查Git仓库Webhook配置
2. 检查Jenkins是否可达
3. 查看Jenkins系统日志
## 维护操作
### 备份Jenkins数据
\`\`\`bash
cd /home/novalon/docker-app/jenkins
./backup.sh
\`\`\`
### 更新Jenkins
\`\`\`bash
cd /home/novalon/docker-app/jenkins
docker-compose pull
docker-compose up -d
\`\`\`
### 查看Jenkins日志
\`\`\`bash
docker logs jenkins -f
\`\`\`
Step 3: 提交文档
git add docs/jenkins/
git commit -m "docs: add Jenkins operation guide"
git push origin main
Phase 4: 清理和收尾
Task 15: 停用Woodpecker CI
Files:
- Modify:
/home/novalon/docker-app/novalon-website/.woodpecker.yml
Step 1: 备份Woodpecker配置
cp .woodpecker.yml .woodpecker.yml.backup
Step 2: 重命名Woodpecker配置
mv .woodpecker.yml .woodpecker.yml.disabled
Step 3: 提交更改
git add .woodpecker.yml.disabled
git rm .woodpecker.yml
git commit -m "chore: disable Woodpecker CI, switch to Jenkins"
git push origin main
Step 4: 验证Woodpecker不再触发
推送代码后,确认Woodpecker不再创建新的Pipeline
Task 16: 最终验证
Files:
- None
Step 1: 执行完整构建流程
# 在本地修改代码
echo "<!-- Test Jenkins CI/CD -->" >> src/app/layout.tsx
# 提交并推送
git add .
git commit -m "test: verify Jenkins CI/CD pipeline"
git push origin main
Step 2: 验证自动触发
- 检查Jenkins是否自动触发构建
- 检查构建是否成功
- 检查网站是否更新
Step 3: 验证邮件通知
检查是否收到构建成功邮件
Step 4: 验证回滚功能
# 恢复之前的代码
git reset --hard HEAD~1
git push -f origin main
确认Jenkins触发构建并成功回滚
风险评估和缓解措施
风险1: Jenkins容器故障
缓解措施:
- 定期备份Jenkins数据(每天自动备份)
- 使用Docker Volume持久化数据
- 监控容器健康状态
风险2: 构建失败
缓解措施:
- 自动备份当前版本
- 提供快速回滚机制
- 详细的错误日志和通知
风险3: 网络问题
缓解措施:
- Jenkins部署在同一台服务器,避免网络传输
- 配置超时和重试机制
- 使用本地缓存加速构建
风险4: 安全问题
缓解措施:
- 使用HTTPS加密通信
- 配置强密码和访问控制
- 定期更新Jenkins和插件
- 限制网络访问(仅允许必要IP)
成功标准
- ✅ Jenkins成功部署并运行
- ✅ CI/CD流水线正常工作
- ✅ 代码推送自动触发构建
- ✅ 构建成功率 > 95%
- ✅ 平均构建时间 < 10分钟
- ✅ 部署成功率 = 100%
- ✅ 回滚功能正常
- ✅ 邮件通知正常
- ✅ 文档完善
- ✅ 团队成员培训完成
后续优化建议
-
性能优化
- 配置npm缓存加速依赖安装
- 使用并行构建减少总时间
- 优化Docker镜像构建
-
功能增强
- 添加单元测试和集成测试
- 配置代码质量扫描
- 添加性能测试
-
监控告警
- 集成Prometheus监控
- 配置Grafana可视化
- 设置告警规则
-
安全加固
- 配置RBAC权限控制
- 启用审计日志
- 定期安全扫描