引言
- 前情提要:上一篇文章介绍了如何用Docker运行一个Node.js + MySQL 后端和两个 Vue.js 前端项目。但是项目上传到服务器还是使用原始的文件上传。
- 选择Jenkins:比较知名的CI/CD方案有Jenkins和GitLab CI/CD、GitHub Actions,我们先选择用Jenkins,后续再尝试GitLab CI/CD等方案。
方案设计
1,创建Jenkins容器,Jenkins容器可以控制宿主机docker。
2,创建3个构建任务。分别是cms-web-vue任务、cms-front-vue任务、cms-server-node任务。
3,3个构建任务构建5个容器,如下图展示。
4,容器启动后协同工作。工作方式如下所示。
Jenkins 安装与配置
- 安装 Jenkins:
javascript
// 拉取jenkins镜像
sudo docker pull jenkins/jenkins:lts
// 创建卷目录,这会是jenkins的工作目录
sudo mkdir -p /var/jenkins_home // 创建卷目录
sudo chmod 755 /var/jenkins_home // 更改目录权限
// 创建 Jenkins 容器
sudo docker run -d \
-p 8080:8080 \
-p 50000:50000 \
--name jenkins \
-v /var/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkins/jenkins:lts
// 获取初始管理员密码
sudo docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
// 可以通过服务器ip:8080访问到jenkins,输入初始管理员密码,就可以看到jenkins管理页面了
解释创建 Jenkins 容器命令:
docker run -d
:以守护进程模式(后台)运行容器。-p 8080:8080
:将主机的 8080 端口映射到容器的 8080 端口,这是 Jenkins Web 界面的默认端口。-p 50000:50000
:将主机的 50000 端口映射到容器的 50000 端口,用于连接 Jenkins 从节点(如果你配置了分布式构建)。--name jenkins
:为容器指定一个名称 "jenkins"。-v /var/jenkins_home:/var/jenkins_home
:将主机的/var/jenkins_home
目录挂载到容器的/var/jenkins_home
目录,确保 Jenkins 的数据持久化,即使容器被删除或重启,数据仍然保留。-v /var/run/docker.sock:/var/run/docker.sock
:将主机的 Docker 套接字文件挂载到容器中,这允许 Jenkins 容器内的 Jenkins 实例直接与 Docker 引擎通信。这通常用于在 Jenkins 中运行 Docker 命令,例如构建 Docker 镜像或启动 Docker 容器。jenkins/jenkins:lts
:指定要运行的 Jenkins 镜像和版本。
创建Jenkins构建任务
- 在 Jenkins 的主页上,点击左侧菜单中的"新建任务"或"New Item"。
- 在弹出的窗口中,输入任务名称,比如 cms-web-vue。
- 选择"Pipeline"作为项目类型,然后点击"确定"按钮。
- 滚动到页面底部,找到"Pipeline"选项卡。
- 在"Definition"下拉菜单中选择"Pipeline script"。
- 将你提供的 Pipeline 脚本粘贴到"Script"文本框中。
下面是我的三个任务的Pipeline 脚本。
cms-web-vue任务:任务会执行pipeline脚本,脚本执行了三个阶段,拉取代码、构建镜像、启动容器。
typescript
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://gitee.com/DaBuChen/cms-web-vue.git'
}
}
stage('Build Docker Image') {
steps {
script {
// 构建 Docker 镜像
def imageName = 'cms-server-node-frontend-web:latest' // 替换为您的镜像名称
sh "docker build -t ${imageName} ."
}
}
}
stage('Run Docker Container') {
steps {
script {
// 停止并删除旧容器(如果存在)
try {
sh "docker stop cms-server-node-frontend-web" // 替换为您的容器名称
sh "docker rm cms-server-node-frontend-web"
} catch (Exception e) {
echo "No existing container to stop or remove."
}
// 运行新的 Docker 容器
sh "docker run -d --name cms-server-node-frontend-web --network my-custom-network cms-server-node-frontend-web:latest" // 替换为您的镜像名称
}
}
}
}
post {
always {
// 可选:清理工作区
cleanWs()
}
}
}
提示:我的项目原先托管再github,但是服务器访问外网经常访问不到,所以将项目托管到了gitee。
cms-front-vue任务:与cms-web-vue任务类似。
typescript
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://gitee.com/DaBuChen/cms-front-vue.git'
}
}
stage('Build Docker Image') {
steps {
script {
// 构建 Docker 镜像
def imageName = 'cms-server-node-frontend-manage:latest' // 替换为您的镜像名称
sh "docker build -t ${imageName} ."
}
}
}
stage('Run Docker Container') {
steps {
script {
// 停止并删除旧容器(如果存在)
try {
sh "docker stop cms-server-node-frontend-manage" // 替换为您的容器名称
sh "docker rm cms-server-node-frontend-manage"
} catch (Exception e) {
echo "No existing container to stop or remove."
}
// 运行新的 Docker 容器
sh "docker run -d --name cms-server-node-frontend-manage --network my-custom-network cms-server-node-frontend-manage:latest" // 替换为您的镜像名称
}
}
}
}
post {
always {
// 可选:清理工作区
cleanWs()
}
}
}
cms-server-node任务:除了启动nodejs服务外,还需要启动mysql和nginx容器。
python
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://gitee.com/DaBuChen/cms-server-node.git'
}
}
stage('Build and Run Nginx') {
steps {
script {
// 检查是否存在名为 my-nginx 的容器,如果存在则停止并删除
sh """
if [ $(docker ps -a -q -f name=my-nginx) ]; then
docker stop my-nginx
docker rm my-nginx
fi
"""
// 构建并运行 Nginx 容器,挂载 nginx.conf
sh """
docker run -d --name my-nginx \
-p 80:80 \
-v /var/lib/docker/volumes/jenkins_home/_data/workspace/cms-server-node/nginx.conf:/etc/nginx/nginx.conf:ro \
--network my-custom-network \
nginx:latest
"""
}
}
}
stage('Build and Run MySQL') {
steps {
script {
// 检查是否存在名为 my-mysql 的容器,如果存在则停止并删除
sh """
if [ $(docker ps -a -q -f name=my-mysql) ]; then
docker stop my-mysql
docker rm my-mysql
fi
"""
// 构建并运行 MySQL 容器,初始化数据库
sh """
docker run -d --name my-mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=12345678 \
-e MYSQL_DATABASE=cms \
-e MYSQL_USER=admin \
-e MYSQL_PASSWORD=12345678 \
-v mysql-data:/var/lib/mysql \
-v /var/lib/docker/volumes/jenkins_home/_data/workspace/cms-server-node/db/backup.sql:/docker-entrypoint-initdb.d/backup.sql:ro \
--network my-custom-network \
mysql:8.0
"""
}
}
}
stage('Build Docker Image') {
steps {
script {
// 构建 Docker 镜像
def imageName = 'cms-server-node-backend:latest' // 替换为您的镜像名称
sh "docker build -t ${imageName} ."
}
}
}
stage('Run Docker Container') {
steps {
script {
// 停止并删除旧容器(如果存在)
try {
sh "docker stop cms-server-node-backend" // 替换为您的容器名称
sh "docker rm cms-server-node-backend"
} catch (Exception e) {
echo "No existing container to stop or remove."
}
// 运行新的 Docker 容器
sh "docker run -d --name cms-server-node-backend --network my-custom-network cms-server-node-backend:latest" // 替换为您的镜像名称
}
}
}
}
post {
always {
// 可选:清理工作区
cleanWs()
}
}
}
三个任务如下展示:
配置Jenkins Webhook
配置Webhook可以让我们在提交代码时触发Jenkins进行部署。我以 cms-web-vue 项目为例配置。
1. 在 Jenkins 中配置 Gitee 插件
首先,确保您的 Jenkins 安装了 Gitee 相关的插件,例如 "Generic Webhook Trigger" 插件。您可以通过以下步骤安装插件:
- 在 Jenkins 的管理界面中,点击 "Manage Jenkins"。
- 选择 "Manage Plugins"。
- 在 "Available" 标签页中搜索 "Generic Webhook Trigger" 并安装它。
2. 配置 Jenkins Job 以响应 Webhook
- 在配置页面,找到 "Build Triggers" 部分,勾选 "Generic Webhook Trigger"。
- 设置token。这里我设置为cms-web-vue。
3. 配置 Webhook URL
在 Jenkins Job 中,您需要配置一个 Webhook URL。通常这个 URL 格式如下:
ini
http://<your-jenkins-server>:<port>/generic-webhook-trigger/invoke?token=cms-web-vue
请确保您的 Jenkins 服务器可以通过公网访问,或者您可以使用工具如 ngrok 来创建一个临时的公网 URL。
4. 在 Gitee 上设置 Webhook
- 登录到您的 Gitee 帐户并导航到您的项目。
- 点击 "管理"。
- 在左侧菜单中选择 "Webhook"。
- 点击 "添加 Webhook"。
- 在 "请求地址" 中输入 Jenkins 的 Webhook URL。
- 选择触发事件,例如 "Push events" 或 "Tag push events"。
- 保存 Webhook。
5. 测试 Webhook
在 Gitee 中进行一次代码推送或其他触发事件,查看 Jenkins 是否正确响应并开始构建。
总结
这篇文章中我们介绍了用Jenkins方案来实现我们全栈项目的CI/CD。但是我的方案应该和公司正规方案还有区别。Jenkins应该会独立的在一个服务器,将镜像打包好后在传输到生产服务器上。同样我的这次实践页足够的简陋,后续希望自己能不断的完善自己的部署方案。同时尝试其他的CI/CD方案。