从零搭建 Jenkins + Docker + Gitee 自动化部署流水线,实现“代码一推、飞书即报”
本文分享了作者从手动部署到自动化部署的实践过程。通过Jenkins和DockerCompose搭建了一套完整的自动化部署流程,包括:1)使用DockerCompose编排Flask+MySQL服务;2)配置Jenkins流水线自动拉取代码、构建镜像并部署;3)实现飞书机器人构建结果通知。文章详细记录了Dockerfile编写、Jenkins容器配置、Gitee集成等关键步骤,并总结了环境配置、权限
说实话,以前我觉得自动化部署这词儿挺高端,离我很远。直到前阵子,我被折腾得快崩溃了:
每天改个小 Bug,都要经历“改代码、传代码、进 FinalShell、重启容器”这一套枯燥的动作。最离谱的是,有时候 Windows 的 OpenSSH 服务莫名其妙抽风,连都连不上,时间全浪费在修环境上了。
后来我照着文档,借助claude,研究了几天 Jenkins 和 Docker Compose。虽然中间也踩了不少坑,但跑通的那一刻,看着飞书群里跳出来的“构建成功”通知,还是成就感满满的。
现在的流程:我只管写代码,推到 Gitee,Jenkins 帮我把 Python 环境和 MySQL 数据库打包成镜像并原地重启。
这篇文章就把我这几天实操的完整路径和踩过的所有坑,一次性全掏给你。
一、自动化部署流程

构建的镜像及容器情况:

jenkins:

飞书机器人通知:

二、python文件结构

在我上一篇文章的基础上修改:这里
app.py:
from flask import Flask, Response
import mysql.connector
import os
app = Flask(__name__)
@app.route('/')
def hello():
conn = mysql.connector.connect(
host=os.environ.get('DB_HOST', 'db'),
user=os.environ.get('DB_USER', 'root'),
password=os.environ.get('DB_PASSWORD', '123456'),
database=os.environ.get('DB_NAME', 'testdb'),
charset='utf8mb4'
)
cursor = conn.cursor()
cursor.execute("SELECT message FROM greetings LIMIT 1")
row = cursor.fetchone()
conn.close()
return Response(
f"数据库说:{row[0]},这条数据来自MySQL!",
content_type='text/html; charset=utf-8'
)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
作为一个“中间人”,从 MySQL 数据库里取一句话,然后显示在网页上
host='0.0.0.0': 极其重要! 在 Docker 环境下必须设为
0.0.0.0,否则你在容器外(比如浏览器里)是访问不到这个服务的
Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
这个文件告诉 Docker 如何把你的 Python 代码封装成一个集装箱。
FROM python:3.12-slim:指定基础镜像。用
slim版本是为了减小体积,只保留最核心的运行环境。WORKDIR /app:设置容器内的工作目录,就像进入了容器里的“D盘”。
COPY . .:把本地电脑当前文件夹下的所有文件(包括
app.py)一股脑拷贝到容器里。RUN pip install...:在容器构建时执行,把代码运行需要的依赖装好。
CMD ["python", "app.py"]:这是容器启动后的第一条指令,相当于手动输入命令运行程序。
Dockerfile_db:
FROM mysql:8.0
COPY init.sql /docker-entrypoint-initdb.d/init.sql
dockerfile_db和init.sql这两个文件配合使用,让MySQL 容器在启动时就自带数据。
Dockerfile_db:
FROM mysql:8.0:指定使用官方的 MySQL 8.0 镜像。
COPY init.sql /docker-entrypoint-initdb.d/:这是 MySQL 官方提供的“特权路径”,只要把 SQL 脚本扔进去,容器第一次启动时就会自动执行它。
init.sql:
CREATE TABLE...:自动创建一个名为
greetings的表,并指定utf8mb4编码防止中文乱码。INSERT INTO...:提前插入一条
'Hello from MySQL'的原始数据,方便我们一会儿测试。
一开始我把dockerfile_db里的代码写进了dockerfile,因为我看他们结构差不多,应该能写到一起吧,但是答案是不能,必须分开写。
docker-compose.yml:
name: dockerapp
services:
web:
build: .
ports:
- "8080:5000"
depends_on:
- db
environment:
DB_HOST: db
DB_USER: root
DB_PASSWORD: 123456
DB_NAME: testdb
db:
build:
context: .
dockerfile: Dockerfile_db
environment:
MYSQL_ROOT_PASSWORD: "123456"
MYSQL_DATABASE: testdb
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
这是最重要的文件,它负责把 Python 和 MySQL 两个孤岛连接起来。
services:定义了两个服务,一个是
web(Python),一个是db(MySQL)。build:告诉 Compose 去哪里找刚才那两个
Dockerfile来构建镜像。ports: "8080:5000":端口映射。你在浏览器访问服务器的
8080端口,就会转发到容器内部的5000端口。depends_on: - db:非常关键! 确保
db容器先启动,web容器后启动,防止 Python 因为找不到数据库而报错崩溃。environment:环境变量。这里填写的配置会被刚才的
app.py通过os.environ.get读取到,实现了代码和配置的解耦。
Jenkinsfile:
pipeline {
agent any
stages {
stage('1. 拉取代码') {
steps {
// 这里其实可以留空,因为你选了 "from SCM",Jenkins 会自动先拉取代码
echo '代码已由 Jenkins 自动拉取完毕'
}
}
stage('2. 部署容器') {
steps {
// 使用 -p 指定项目名,--remove-orphans 清理孤儿容器
// -v 确保每次数据库都是根据最新的 init.sql 重新生成的
sh 'docker-compose -p dockerapp down -v --remove-orphans'
// 重新启动
sh 'docker-compose -p dockerapp up -d --build'
// 留出时间让 MySQL 建表,避免 500 错误
echo '正在初始化数据库,请等待...'
sleep 25
}
}
}
post {
success {
sh "curl -X POST 'https://open.feishu.cn/open-apis/bot/v2/hook/1ae5c408-4818' \
-H 'Content-Type: application/json' \
-d '{\"msg_type\":\"text\",\"content\":{\"text\":\"🚀 Jenkins 构建成功!\\n项目:${env.JOB_NAME}\\n编号:#${env.BUILD_NUMBER}\\n状态:部署已完成。\"}}'"
}
failure {
sh "curl -X POST 'https://open.feishu.cn/open-apis/bot/v2/hook/1ae5c408-4818' \
-H 'Content-Type: application/json' \
-d '{\"msg_type\":\"text\",\"content\":{\"text\":\"❌ Jenkins 构建失败!\\n项目:${env.JOB_NAME}\\n编号:#${env.BUILD_NUMBER}\\n请登录 Jenkins 查看控制台日志。\"}}'"
}
}
}
这段代码采用了 声明式流水线 (Declarative Pipeline) 语法,主要分为三个部分:
1. 核心部署阶段 (Stages)
stage('1. 拉取代码'):由于你在 Jenkins 任务配置里选了 "Pipeline script from SCM",Jenkins 会在流水线开始前自动把 Gitee 仓库的代码拉到本地。这一步主要起到一个日志记录的作用。
stage('2. 部署容器'):
docker-compose -p dockerapp down -v:先清理现场。-v参数非常关键,它会删除旧的数据卷,确保你的init.sql能够重新执行,保证环境始终是最新的。
up -d --build:后台启动并强制重新构建镜像,确保你刚刚提交的代码改动能立即生效。
sleep 25:因为 MySQL 启动和初始化表结构需要时间,如果不等这 25 秒,Python 程序连过去就会报错。2. 消息通知机制 (Post)
这利用了 Jenkins 的
post钩子,根据构建结果自动触发:
success (构建成功):当上面的部署步骤全部跑通时,通过
curl命令调用飞书机器人的 Webhook 接口。failure (构建失败):如果任何一步报错(比如代码写错了,或者端口被占用),飞书会立刻弹窗告诉你具体哪个任务、哪次构建出了问题。
3. 动态变量引用
代码中的
${env.JOB_NAME}和${env.BUILD_NUMBER}是 Jenkins 内置的变量:
它们会自动把任务名称(如
dockerapp)和构建序号(如#12)填入消息内容中,让通知看起来非常专业。
1)这里的部署阶段stage本来第二步是“构建镜像”:
但是后来发现起来的容器没有用这个镜像的啊,原因如下:


所以这一步直接删了,反正会自动构建和命名嘛。
2)第二个问题就是success和failure里的格式改了好几次,一开始是,sh 后面用三引号括住,报错,改成单引号也报错,后面改成了双引号,加了些斜杠才对。
init.sql:
CREATE TABLE IF NOT EXISTS greetings (
id INT AUTO_INCREMENT PRIMARY KEY,
message VARCHAR(255)
) CHARACTER SET utf8mb4;
INSERT INTO greetings (message) VALUES ('Hello from MySQL');
requirements.txt:
flask
mysql-connector-python
三、代码提交到Gitee
推代码之前,得先在gitee里建好仓库:
登录 Gitee → 右上角
+→ 新建仓库:
- 仓库名:flask_
docker- 是否开源:私有(学习项目建议私有)
- 不要勾选初始化仓库(因为你本地已经有文件了)
- 点创建
这时候页面上会有个url地址,复制它
回到咱们pycharm,在刚才那个项目里打开pycharm终端,依次输入以下命令:
# 初始化 git
git init
# 把所有文件加入暂存区
git add .
# 提交,引号里是描述
git commit -m "flask + mysql + docker compose 学习项目"
# 关联 Gitee 仓库(换成你刚才复制的url)
git remote add origin https://gitee.com/你的用户名/dockerapp.git
# 推送
git push -u origin master
```
推送时会弹出登录框,输入你的 Gitee 账号密码就行。
---
刷新一下Gitee页面就能看到推送成功。
四、起Jenkins容器
可以先把Docker desktop里的没用的容器都先删了,避免页面太乱。
在docker desktop右下角终端里执行即可,\ 是为了换行,你如果想一行写完也可以的:
docker run -d \
--name jenkins \
-p 8090:8080 \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts
第一次启动jenkins需要一串密码,执行这个命令来获取密码:
记得复制啊!!!
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
然后去浏览器打开网址:
输入你刚才的那一串密码吧。
然后一步步地往下走,安装它推荐的插件,不安装的话后面自己再安装就比较麻烦,这里我觉得jenkins版本必须比较高才行,否则后面问题多得很,让你头疼。咱上面起jenkins容器的时候用的镜像是jenkins:lts,稳定版,就很好。我之前全部都安装失败了,可能是版本太老了,这次就很顺滑了,不过也需要等待几分钟。
五、配置Gitee流水线(pipeline)
5.1
进入 Jenkins 主界面后,点击新建一个任务:
-----任务名称:flask-docker-test
-----选择流水线 (Pipeline)
-----点击确定
5.2
安装Gitee专用插件:
-----Jenkins 主页点击右上角齿轮(系统管理)
-----点击插件管理
-----在available plugins里下载 Gitee Plugin
-----install完刷新一下jenkins页面/重新打开一下这个网址
5.3
配置Gitee凭据:
-----点击齿轮
-----安全模块里的“凭据管理”
-----add一个凭据,选择 Username with password,填入你的Gitee账号用户名(非昵称)
-----密码别填gitee密码,去gitee个人设置里生成一个“私人令牌”,自己保存好!密码就填这个
-----ID你就起个比较好记的名字,比如gitee-auth,或者别的
5.4
从首页点击任务名字:
-----点击左侧配置
-----选择流水线,配置如下:

url就是最开始你建仓库时复制的那个url,下方就是刚才配置的gitee凭据。
5.5
由于上一步我们在流水线配置里选择了“pipeline script from SCM”,所以脚本就必须写到gitee里的项目文件里,jenkinsfile相当于菜谱,python文件相当于做菜的原材料,jenkins是厨师:
-----在python项目里新建一个文件jenkinsfile(无后缀),填入代码
-----这个jenkins是咱们起的容器,并没有执行docker命令的资格,我们需要重新启动一下 Jenkins 容器,通过**挂载 Docker 守护进程(Socket)**的方式,让容器里的 Jenkins 能直接调用咱们Windows 上的 Docker,去docker desktop终端执行:
#删除现有容器,jenkins容器id每个人不一样,看docker desktop
docker rm -f dbb57c9dbe28
#重启,加入挂载参数
docker run -d \
-p 8090:8080 -p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
--name jenkins \
jenkins/jenkins:lts
再去powershell(管理者身份)执行:
docker exec -u 0 -it jenkins bash
apt-get update && \
apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release && \
curl -fsSL https://download.docker.com/linux/debian/gpg | 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/debian $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list && \
apt-get update && \
apt-get install -y docker-ce-cli
chmod 666 /var/run/docker.sock
exit
#能输出docker版本信息即可
docker exec jenkins docker version
OK,现在是可执行docker命令了,但是docker-compose命令还是不行,继续在这里执行:
docker exec -u 0 -it jenkins bash
curl -L "https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
exit
#输出版本就ok
docker exec jenkins docker-compose version
回到jenkins主页面,任务后面有个运行键,点击进行构建:
我第一次第二次都失败了,原因如下,在上面几个步骤里已经规避了。

构建成功,打开http://localhost:8080能看到:
六、jenkins感知gitee代码变动,触发构建
先说一下,这个我整了半天也没成功,配置步骤不难,但是就是有各种问题,给我整无语了,所以我就先放弃这一步了。如果成功的话,就是会在你每次提交代码到gitee的时候直接触发构建,就不用你手动点那个构建了。我也只是想体验一下,不重要。我们公司用的是gitlab,那个应该比较容易成功,大家可以试试,我用gitee是因为之前就把代码托管到这里了,懒得再弄gitlab了,后面有机会试试它能成功不。
七、构建结束,飞书通知
-----先在飞书建个群,添加个机器人,复制生成的 Webhook 地址
-----回到jenkins下载个插件:Notification
-----将复制的webhook地址填入jenkinsfile文件:

任务配置里可以配置触发器:

代表每小时构建一次,成功失败都会飞书通知,通知的样式就看你jenkinsfile咋写的了。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)