news 2026/2/9 11:44:35

不搭 Jenkins 也能专业上线:Shell + Docker 一键部署(带回滚)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
不搭 Jenkins 也能专业上线:Shell + Docker 一键部署(带回滚)

适用人群 ✅

  • 个人项目 / 小团队 / 接私活交付
  • 不想上 Jenkins / GitLab CI / GitHub Actions
  • 服务器只有一台或几台,追求简单可控

目标 🎯
一条命令完成:拉代码 → 构建镜像 → 停旧起新 → 健康检查 → 回滚兜底


🧠 先讲清楚:为什么不是“反对 CI/CD”?

CI/CD 很强,但也有成本:

  • 需要平台、Runner、权限、网络、配置维护
  • 小项目上线频率不高,反而 “搭平台 > 写业务”
  • 最痛的不是构建,而是上线可重复、可回滚

所以这篇做的方案是:

保留工程化能力(版本、回滚、健康检查)
不引入 CI/CD 平台依赖
👉 让部署回到最朴素的:脚本 + Docker


🗺️ 部署流程总览(看懂这张图就能用)

Yes

No

本地/服务器执行 deploy.sh

拉取/更新代码 git pull

Docker build 构建镜像

停止旧容器

启动新容器(带版本Tag)

健康检查通过?

清理旧镜像/旧容器

自动回滚到上一个版本


✅ 你将得到什么(这篇文章的交付物)

  • 🧱 一个生产可用的Dockerfile(支持多阶段构建)

  • 🔥 一个deploy.sh

    • 自动打版本 Tag(时间戳/commit)
    • 停旧起新
    • 健康检查失败自动回滚
    • 可选:Nginx/反代不动,容器热切换
  • 🗂️ 一个最小目录结构(直接套到你的项目)


0)目录结构(建议直接照抄)

Go 服务为例(Python/Java 同理):

myapp/ cmd/myapp/main.go Dockerfile deploy.sh .env.prod

1)Dockerfile(多阶段构建,镜像小、上线快)

这是 Go 版本,其他语言我后面给替换模板。

# -------- build stage -------- FROM golang:1.22 AS builder WORKDIR /app # 1) 依赖缓存 COPY go.mod go.sum ./ RUN go mod download # 2) 拷贝源码并构建 COPY . . RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o myapp ./cmd/myapp # -------- runtime stage -------- FROM gcr.io/distroless/static:nonroot WORKDIR /app COPY --from=builder /app/myapp /app/myapp # 服务端口(按你项目改) EXPOSE 8080 # 健康检查(推荐你在服务里提供 /health) # distroless 没有 curl,健康检查放在 deploy.sh 里做(更通用) USER nonroot:nonroot ENTRYPOINT ["/app/myapp"]

2)一键部署脚本 deploy.sh(核心)

特点:
✅ 不依赖 CI 平台
✅ 只要服务器有 Docker + Git
✅ 失败自动回滚(评分加分项)

把下面脚本保存为deploy.sh,并chmod +x deploy.sh

#!/usr/bin/env bashset-euo pipefail# =========================# 配置区:按需修改# =========================APP_NAME="myapp"PORT="8080"# 容器内服务端口HOST_PORT="8080"# 宿主机映射端口HEALTH_URL="http://127.0.0.1:${HOST_PORT}/health"HEALTH_RETRY=20# 健康检查重试次数HEALTH_INTERVAL=1# 每次间隔秒ENV_FILE=".env.prod"# 环境变量文件(可选)DOCKERFILE="Dockerfile"NETWORK=""# e.g. "my-net" 如果你有自建网络可填# =========================# 自动生成版本号(时间戳 + git commit)# =========================TS="$(date+%Y%m%d%H%M%S)"GIT_SHA="$(gitrev-parse --short HEAD2>/dev/null||echo'nogit')"VERSION="${TS}-${GIT_SHA}"IMAGE="${APP_NAME}:${VERSION}"CONTAINER="${APP_NAME}"echo"🚀 Deploy start:${APP_NAME}"echo"📌 Version:${VERSION}"echo"📦 Image:${IMAGE}"# =========================# 记录上一个版本(用于回滚)# =========================PREV_IMAGE=""ifdockerps-a --format'{{.Names}}'|grep-qx"${CONTAINER}";thenPREV_IMAGE="$(dockerinspect -f'{{.Config.Image}}'"${CONTAINER}"2>/dev/null||true)"fiecho"🕰️ Prev image:${PREV_IMAGE:-<none>}"# =========================# 1) 更新代码(可按你的习惯:服务器拉代码 or scp 上传)# =========================echo"🔄 Git pull..."gitpull --rebase# =========================# 2) 构建镜像# =========================echo"🧱 Docker build..."dockerbuild -f"${DOCKERFILE}"-t"${IMAGE}".# =========================# 3) 停旧容器# =========================ifdockerps--format'{{.Names}}'|grep-qx"${CONTAINER}";thenecho"🛑 Stop running container..."dockerstop"${CONTAINER}"fiifdockerps-a --format'{{.Names}}'|grep-qx"${CONTAINER}";thenecho"🧹 Remove old container..."dockerrm"${CONTAINER}"fi# =========================# 4) 启新容器# =========================echo"▶️ Run new container..."RUN_ARGS=(-d --name"${CONTAINER}"-p"${HOST_PORT}:${PORT}"--restart=always)if[[-f"${ENV_FILE}"]];thenRUN_ARGS+=(--env-file"${ENV_FILE}")fiif[[-n"${NETWORK}"]];thenRUN_ARGS+=(--network"${NETWORK}")fidockerrun"${RUN_ARGS[@]}""${IMAGE}"# =========================# 5) 健康检查(失败自动回滚)# =========================echo"🩺 Health check:${HEALTH_URL}"ok=0foriin$(seq1"${HEALTH_RETRY}");doifcurl-fsS"${HEALTH_URL}">/dev/null2>&1;thenok=1breakfisleep"${HEALTH_INTERVAL}"doneif[["${ok}"-ne1]];thenecho"❌ Health check failed. Rolling back..."echo"🧾 Logs (last 120 lines):"dockerlogs --tail120"${CONTAINER}"||truedockerrm-f"${CONTAINER}"||trueif[[-n"${PREV_IMAGE}"]];thenecho"⏪ Rollback to:${PREV_IMAGE}"dockerrun"${RUN_ARGS[@]}""${PREV_IMAGE}"# 再验一次ok2=0foriin$(seq1"${HEALTH_RETRY}");doifcurl-fsS"${HEALTH_URL}">/dev/null2>&1;thenok2=1breakfisleep"${HEALTH_INTERVAL}"doneif[["${ok2}"-ne1]];thenecho"💥 Rollback also failed. Please check service manually."exit2fiecho"✅ Rolled back successfully."exit1elseecho"⚠️ No previous image to rollback."exit1fifiecho"✅ Deploy success!"# =========================# 6) 清理旧镜像(保留最近 5 个版本)# =========================echo"🧽 Cleanup old images..."KEEP=5dockerimages"${APP_NAME}"--format'{{.Repository}}:{{.Tag}} {{.CreatedAt}}'\|awk'{print $1}'\|tail-n +$((KEEP+1))\|xargs-rdockerrmi -f||trueecho"🎉 Done."

3)环境变量文件 .env.prod(示例)

APP_ENV=prod LOG_LEVEL=info DB_DSN=mysql://user:pass@tcp(127.0.0.1:3306)/db

--env-file的好处:配置不进镜像、不进代码库(更安全)


4)服务器部署方式(两种最常用)

方式 A:服务器直接拉代码部署(最省事)

服务器上:

gitclone<你的仓库>cdmyappchmod+x deploy.sh ./deploy.sh

方式 B:本地打包上传到服务器部署(更安全)

本地rsync/scp上传源码或构建产物,然后服务器执行deploy.sh


5)为什么这个方案“更容易稳定”?

可重复:每次执行同样流程
可追溯:镜像 Tag 带版本号(时间戳 + commit)
可回滚:健康检查失败自动回退上一个版本
可部署:只依赖 Docker + Shell + Git


6)常见坑位

⚠️ 1)健康检查要写对

  • 不要只返回200 ok
  • 至少检查:依赖连通(DB/Redis)/ 主服务线程状态

⚠️ 2)端口占用/反代冲突

  • 你用 Nginx 反代的话,尽量固定宿主机端口
  • 容器内服务端口可变,但宿主机端口建议固定

⚠️ 3)日志别写到容器里

  • 建议 stdout/stderr 打日志,让 Docker 管
  • 或挂载卷到宿主机(-v /var/log/myapp:/logs

⚠️ 4)别把密钥写进 Dockerfile

  • .env.prod或 Secret 管理
  • 不要COPY .env.prod进镜像

✅ 结尾总结

CI/CD 不是必须,但可重复、可回滚、可追溯是必须。
对个人项目/小团队来说:
Shell + Dockerfile足够构建一条轻量但可靠的上线链路。
你少维护平台,多维护业务,效率反而更高。


👉 后续工具会持续补充进《程序员自动化工具箱》,喜欢的朋友别忘了点个关注订阅此专栏。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/4 1:07:49

机器学习:强化学习算法

摘要&#xff1a;强化学习是机器学习的一个分支&#xff0c;通过智能体与环境的交互来学习最优策略。核心要素包括智能体、环境、状态、动作、奖励和策略。智能体通过试错过程&#xff0c;根据环境反馈的奖励调整策略&#xff0c;目标是最大化长期累积奖励。主要算法包括基于价…

作者头像 李华
网站建设 2026/2/8 22:04:41

es连接工具在日志分析系统中的核心作用:一文说清

日志系统里的“搬运工”没那么简单&#xff1a;揭秘 es连接工具的实战价值你有没有遇到过这种情况——线上服务突然报错&#xff0c;你急着查日志定位问题&#xff0c;结果发现Kibana里半天刷不出数据&#xff1f;或者好不容易查到了日志&#xff0c;字段全是乱的&#xff0c;s…

作者头像 李华
网站建设 2026/2/6 12:15:38

2026.1.9

加密技术PKI&#xff08;公钥基础设施&#xff09;通过使用公钥技术和数字签名来确保信息安全PKI体系能够实现的功能身份验证数据完整性数据机密性操作的不可否认性对称加密&#xff1a;用相同的密钥进行加密和解密。不安全&#xff0c;但处理速度快非对称加密&#xff1a;使用…

作者头像 李华
网站建设 2026/2/8 10:21:40

PyTorch 2.5零基础教程:云端GPU免配置,1小时1块快速上手

PyTorch 2.5零基础教程&#xff1a;云端GPU免配置&#xff0c;1小时1块快速上手 引言&#xff1a;为什么选择云端PyTorch&#xff1f; 作为一名大学生&#xff0c;当你需要完成深度学习课程项目时&#xff0c;最头疼的往往不是算法本身&#xff0c;而是环境配置。传统PyTorch…

作者头像 李华
网站建设 2026/2/7 1:08:28

es客户端工具全文检索图解说明:match与multi_match用法

从零搞懂 Elasticsearch 全文检索&#xff1a;match和multi_match到底怎么用&#xff1f;你有没有遇到过这种情况——用户在搜索框里输入“苹果手机”&#xff0c;结果系统却把卖水果的页面排到了前面&#xff1f;或者搜“自动驾驶特斯拉”时&#xff0c;标题明明写着相关内容的…

作者头像 李华
网站建设 2026/2/3 5:50:20

超详细版AUTOSAR OS任务调度机制:深度剖析原理

深入AUTOSAR OS任务调度&#xff1a;从原理到实战的系统性解析在现代汽车电子控制单元&#xff08;ECU&#xff09;中&#xff0c;一个小小的控制器可能同时运行着数十个任务——从读取传感器信号、执行发动机喷油逻辑&#xff0c;到处理CAN通信、响应紧急制动请求。这些任务必…

作者头像 李华