C#/.NET 微服务架构:从入门到精通(六):Docker部署与 Jenkins 自动构建发布
前一篇我们完成了。但随着微服务数量增多、迭代节奏加快,环境不一致、部署效率低、发布风险高成为了制约团队交付速度的核心瓶颈 —— 传统手动打包、FTP 上传的方式,不仅耗时费力,还极易出现 “开发能跑、生产报错” 的环境问题。
本篇将聚焦单服务独立 Docker 容器化与单服务独立 Jenkins 自动化流水线的全流程落地,严格遵循微服务 “独立构建、独立部署” 的核心原则,实现 “代码提交→自动构建→镜像打包→一键部署” 的 CI/CD 闭环,让微服务发布从 “手工操作” 升级为 “自动化、标准化、可追溯” 的工业级流程。
一、核心理念:为什么必须 “每个微服务单独构建 + 容器化部署”?
1. 传统单体构建的致命问题
- 构建效率极低:修改用户服务的一行代码,需要编译整个解决方案的所有服务,数十个服务的构建时间可能长达数小时;
- 发布完全耦合:A 服务的紧急 Bug 修复,必须等待 B 服务的功能开发完成才能一起上线;
- 回滚风险巨大:一次发布包含多个服务的变更,出问题时难以定位根因,回滚会影响所有服务;
- 团队协作阻塞:不同团队负责不同服务,共用一条流水线会导致发布冲突、互相等待
2. Docker + 独立构建的核心价值
- 环境绝对一致:Docker 将应用及其所有依赖(.NET 运行时、配置、依赖库)打包成一个不可变的镜像,一次构建,到处运行;
- 构建完全解耦:每个服务有独立的构建流水线,修改哪个服务就只构建哪个服务,构建时间从小时级缩短到分钟级;
- 发布风险可控:每次只部署变更的服务,出问题只回滚单个服务,影响范围最小化;
- 扩缩容秒级完成:容器是进程级隔离,启动速度远快于虚拟机,完美适配微服务动态扩缩容的需求。
二、前期准备:在服务器中安装Docker与Jenkins
这里不做具体展示,详情可查看之前的文章:https://www.cnblogs.com/lantingxu/p/19859131
三、实战步骤 1:单服务 Docker 容器化(以商品服务为例)
我们将所有精力聚焦在单个服务的容器化上,订单服务只需重复完全相同的步骤。

1. 编写.NET 8 优化版 Dockerfile
采用多阶段构建 + Alpine 基础镜像策略,最终镜像体积可控制在 100MB 以内,比默认镜像小 70% 以上。
在ProductApi项目根目录创建Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
# 设置中文编码
ENV LANG=zh_CN.UTF-8
ENV LC_ALL=zh_CN.UTF-8
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["ProductApi/ProductApi.csproj", "ProductApi/"]
RUN dotnet restore "ProductApi/ProductApi.csproj"
COPY . .
# 构建
WORKDIR "/src/ProductApi"
RUN dotnet build "ProductApi.csproj" -c Release -o /app/build
# 发布
FROM build AS publish
RUN dotnet publish "ProductApi.csproj" -c Release -o /app/publish /p:UseAppHost=false
# 最终运行
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ProductApi.dll"]
四、实战步骤 2:单服务独立 Jenkins 自动化流水线(以商品服务为例)
1. 编写单服务 Jenkinsfile
Jenkinsfile,这条流水线只负责商品服务的全生命周期,与其他服务完全无关:pipeline {
agent any
options {
timeout(time: 30, unit: 'MINUTES')
disableResume() // 符合你的禁用Resume要求
skipStagesAfterUnstable()
}
environment {
// 基础配置
GIT_URL = 'https://gitee.com/kb248/e-commerce-demo.git'
GIT_CRED_ID = 'gitee-auth'
GIT_BRANCH = '*/master'
SERVICE_NAME = 'productapi'
DOCKERFILE_PATH = 'ProductApi/Dockerfile'
HOST_PORT = '5002'
CONTAINER_PORT = '80'
DOCKER_IMAGE_TAG = "${SERVICE_NAME}:${BUILD_NUMBER}"
}
stages {
stage('1. 拉取全量代码') {
steps {
echo "正在拉取代码..."
checkout scmGit(
branches: [[name: GIT_BRANCH]],
extensions: [[$class: 'CleanBeforeCheckout']], // 构建前清理工作空间
userRemoteConfigs: [[
credentialsId: GIT_CRED_ID,
url: GIT_URL
]]
)
}
}
stage('2. 验证 Dockerfile') {
steps {
script {
if (!fileExists(DOCKERFILE_PATH)) {
error "❌ 未找到 Dockerfile: ${DOCKERFILE_PATH},请在ProductApi目录下创建Dockerfile"
}
echo "✅ Dockerfile 验证通过"
}
}
}
stage('3. 构建 Docker 镜像') {
steps {
echo "构建镜像: ${DOCKER_IMAGE_TAG}"
sh "docker build -t ${DOCKER_IMAGE_TAG} -f ${DOCKERFILE_PATH} ."
echo "✅ 镜像构建完成"
}
}
stage('4. 清理旧容器和镜像') {
steps {
echo "清理旧容器和悬空镜像..."
sh """
docker stop ${SERVICE_NAME} || true
docker rm ${SERVICE_NAME} || true
docker image prune -f
"""
}
}
stage('5. 启动新容器') {
steps {
echo "启动容器: ${SERVICE_NAME}"
sh "docker run -d --network dorm-network -p ${HOST_PORT}:${CONTAINER_PORT} -e ASPNETCORE_ENVIRONMENT=Development -e ASPNETCORE_URLS=http://0.0.0.0:${CONTAINER_PORT} -e LANG=zh_CN.UTF-8 -e LC_ALL=zh_CN.UTF-8 --name ${SERVICE_NAME} ${DOCKER_IMAGE_TAG}"
// 等待容器启动并验证健康状态
sleep 10
sh "docker ps | grep ${SERVICE_NAME}"
echo "✅ 容器启动成功"
}
}
}
post {
always {
cleanWs() // 构建完成后清理工作空间
echo "工作空间清理完成"
}
success {
script {
def containerId = sh(
script: "docker ps -qf name=${SERVICE_NAME}",
returnStdout: true
).trim()
echo "========================================="
echo "✅ 产品服务发布成功!"
echo "📖 访问地址: http://你的服务器IP:${HOST_PORT}/swagger"
echo "🐳 容器ID: ${containerId}"
echo "========================================="
}
}
failure {
echo "❌ 构建失败!请查看详细日志"
// 输出容器日志帮助排查(容器可能已停止,使用docker logs -f 会卡住,去掉-f)
sh "docker logs ${SERVICE_NAME} 2>&1 || true"
}
}
}
五、实战步骤 3:使用Jenkins创建Pipeline任务(以商品服务为例)
上述文件添加后,提交到代码仓库中。
1. 创建凭证
- 进入 Jenkins 首页,点击「设置」;
- 找到「安全」,选择「凭证」,点击「新增凭证」,选择「用户名与密码」模式
- 配置凭证:
- 「范围」选择「Global (Jenkins, nodes, items, all child items, etc)」;
- 「用户名」输入你的Gitee用户名;
- 「密码」输入你的Gitee密码;
- 「ID」填写的要和Jenkinsfile文件中GIT_CRED_ID一致;
- 「描述」填写凭证描述;
- 点击「Save」保存凭证。




2. 在 Jenkins 创建独立任务
- 进入 Jenkins 首页,点击「新建 Item」;
- 输入任务名称:
商品服务,选择「流水线」,点击「确定」; - 配置 流水线:
- 「Definition」选择「Pipeline script from SCM」;
- 「SCM」选择「Git」,填写用户服务的仓库 URL;
- 「Credentials」选择之前配置的凭证;
- 「Branch Specifier」填写
*/master; - 「Script Path」填写
Jenkinsfile;
- 点击「Save」保存配置。




3.构建
构建前,记得在部署服务器上将ProductApi使用的5002端口在防火墙打开,要不然即使构建成功,也访问不了接口文档

点击构建

构建成功

然后通过你部署的服务器IP+5002端口访问,出现swagger文档就部署成功了

六、常见问题解决
1. Docker 构建慢,NuGet 包还原失败
- 配置 Docker 镜像加速器(如阿里云加速器),加快基础镜像拉取速度;
- 在 Dockerfile 中配置国内 NuGet 源(如 Azure NuGet 源),避免官方源访问慢;
- 利用 Docker 缓存机制,将
COPY .csproj和dotnet restore放在前面,代码不变时无需重复还原依赖。
2. 容器内时间与本地不一致
- 在 Dockerfile 中安装
tzdata包并配置时区,如本文示例所示; - 不要通过挂载宿主机
/etc/localtime的方式解决,这在 Alpine 镜像中会导致问题。
3. Jenkins 没有权限执行 Docker 命令
- 将 Jenkins 用户添加到 docker 用户组:
sudo usermod -aG docker jenkins sudo systemctl restart jenkins
- 重启 Jenkins 服务后生效。
4. 镜像推送失败,提示认证错误
- 确认 Jenkins 凭据中的镜像仓库用户名和密码正确;
- 如果使用阿里云 ACR,密码是访问密钥,不是登录密码;
- 确认镜像名称格式正确:
仓库地址/命名空间/镜像名:标签。
七、总结
- 理解了传统单体构建的致命问题,以及 Docker + 独立构建在微服务架构中的核心价值;
- 实现了.NET 8 微服务的优化版 Docker 容器化,镜像体积控制在 100MB 以内;
- 搭建了企业级单服务独立 Jenkins 流水线,实现了代码提交、构建、测试、打包、部署的全自动化;
- 梳理了版本号管理、共享代码处理、部署策略、镜像安全等最佳实践。

浙公网安备 33010602011771号