如果你还在手动打包、scp上传、登录服务器重启服务那这篇文章就是给你准备的。我前两年在团队里落地过一套基于 GitLab Jenkins Docker Harbor K8s Rancher 的 CICD 平台从代码提交到服务更新中间全自动开发在群里喊一声就能部署运维基本不用碰生产环境。这套方案覆盖了代码托管、持续集成、镜像构建、私有仓库、集群调度和可视化运维六个环节等于把一整套 DevOps 工具链端到端跑通。这几年容器化已经不是新鲜事了但真正把链路完整搭起来、能稳定跑生产环境的团队并没有想象中那么多。很多人卡在单点工具的使用上比如会装 GitLab、会用 Jenkins但对 K8s 集群怎么编排、Rancher 怎么纳管、Harbor 怎么跟流水线打通缺乏一套完整的落地思路。这篇文章会从零开始把整套平台的搭建过程、组件协作逻辑、常见坑位全部过一遍适合中小团队的技术负责人、运维工程师以及想系统了解 CICD 全链路的新手开发者。文章里涉及的步骤和配置都是我实测跑过的你照着操作基本能复现一套可用的平台。1. 整体方案设计与组件选型思路1.1 六个组件各管哪一段先说清楚这套平台里每个组件的定位避免后续操作时一头雾水。GitLab 负责代码托管和代码审查。团队里的所有代码、分支、Merge Request、Tag 都在这上面管理它自带的 CI/CD 配置能力其实也不错但我们最终还是把流水线主体放在了 Jenkins 上原因后面会说。Jenkins 做持续集成和持续交付负责拉取代码、执行构建、跑测试、生成制品、触发部署。它的插件生态非常成熟几乎什么场景都能找到对应插件这也是它至今没有被大量替代的核心原因。Docker 是整套体系的“打包机”。不管你的项目是 Java、Go 还是 Node最终都会被打成一个 Docker 镜像。镜像的好处是环境完全一致开发本地能跑生产环境就一定能跑彻底告别“在我机器上是好的”这种问题。Harbor 是私有 Docker 镜像仓库镜像构建好之后推到 HarborK8s 部署时从 Harbor 拉取。相比 Docker 自带的 RegistryHarbor 多了镜像复制、漏洞扫描、回收策略、项目级权限控制这些功能在团队场景里好用太多。K8s 是容器编排平台负责容器的调度、伸缩、负载均衡、自愈。简单说你告诉它“我要跑 3 个副本”它会自动帮你把 Pod 分布到合适的节点上某个节点挂了它会自动在其他节点拉起新的实例。Rancher 则是 K8s 集群的可视化管理平台解决“多个集群怎么看、怎么管”的问题。如果没有 Rancher你操作 K8s 只能对着 kubectl 敲命令查看资源状态全靠人脑记忆效率低不说还容易误操作。Rancher 天然适合多集群管理尤其生产环境和测试环境同时存在的时候它的价值就非常明显。1.2 组件间怎么协作一条流水线的完整走向把工具选好之后你会发现它是一条完整的流水线。开发本地写完代码推送到 GitLab 的某个分支。GitLab 检测到 push 事件通过 Webhook 通知 Jenkins。Jenkins 收到通知后触发对应的流水线任务先拉取代码然后根据项目类型执行构建比如 Maven 打包、npm build构建产物通过 Dockerfile 打包成镜像push 到 Harbor 仓库。镜像推送完成后Jenkins 再调用 kubectl 命令更新 K8s 集群中对应 Deployment 的镜像版本触发滚动更新。更新过程中Rancher 界面可以直接看到 Pod 的新旧交替状态、日志、事件有问题可以立即回滚。这条链路里有个关键点Jenkins 一定要能访问到 K8s 集群的 API Server。很多人在这一步卡住因为权限控制没做好。后面实操部分会详细讲怎么配置 kubeconfig 和 ServiceAccount 权限。1.3 先用 Docker Compose 能不能起步如果你的团队规模很小服务量也在个位数以内业务流量也不大我其实推荐先用 Docker Compose 过渡。在一台服务器上用 Compose 管十几个容器比直接上 K8s 简单太多运维成本低排障也快。但一旦服务数超过 20 个、或者需要多副本扩缩容、或者涉及到滚动发布和故障自愈Compose 就会非常吃力。这时候再迁移到 K8s前期搭建的成本就值得了。我们当初是直接一步到位上了 K8s回头来看有一定试错成本。如果你时间宽裕可以先跑 Compose 跑半年把 Docker 层面的东西吃透再上 K8s这样理解的深度会很不一样。但如果你属于“必须短期内搞定一套平台给团队用”的情况那按这篇文章的路径走就可以每一步的操作都是实测过的能少走很多弯路。2. 基础环境部署Docker、GitLab、Harbor 一次搞定2.1 服务器规划与 Docker 安装要点先说服务器规划。以我们团队为例最开始只有 4 台机器一台 8C16G 跑 GitLab一台 4C8G 跑 Jenkins一台 4C8G 跑 Harbor另外三台节点组 K8s 集群。K8s 集群规模根据业务量调整最小编排是 1 个 master 2 个 worker但我建议如果条件允许master 节点至少给到 4C8Gworker 节点跑业务应用配置看业务负载。Docker 是整套环境的地基所有组件都会以容器方式运行。Ubuntu 系统下安装比较顺滑核心命令如下sudo apt update sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-pluginCentOS 的安装方式有些区别yum 源配置好之后执行yum install -y docker-ce docker-ce-cli containerd.io即可这里不展开。这里有一个非常典型的坑Windows 用户使用 Docker Desktop 时经常报virtualization support wasnt detected的错误。这个问题的根源是电脑没有开启硬件虚拟化。解决方法是重启进入 BIOS/UEFI找到“VT-x”、“Intel Virtualization Technology”或“SVM Mode”选项开启后保存退出。另外 Docker Desktop 依赖 Windows 的 Hyper-V 或 WSL2需要在“启用或关闭 Windows 功能”中勾选对应的选项。Linux 服务器环境如果遇到了虚拟化相关的报错检查一下 BIOS 设置和/proc/cpuinfo中是否有 vmx/svm 标志。Docker 安装完成后记得配置镜像加速器。国内环境的镜像拉取速度会直接影响后面的构建效率我建议至少配置一个可用的加速源配置文件在/etc/docker/daemon.json。注意修改了 daemon.json 后需要执行sudo systemctl restart docker才能生效。如果是新装 Docker这一步越早做越好。2.2 部署 GitLab 社区版并设置域名访问GitLab 社区版功能足够团队使用。直接用 Docker 跑最省事命令如下sudo mkdir -p /srv/gitlab export GITLAB_HOME/srv/gitlab sudo docker run --detach \ --hostname gitlab.example.com \ --publish 443:443 --publish 80:80 --publish 22:22 \ --name gitlab \ --restart always \ --volume $GITLAB_HOME/config:/etc/gitlab \ --volume $GITLAB_HOME/logs:/var/log/gitlab \ --volume $GITLAB_HOME/data:/var/opt/gitlab \ --shm-size 256m \ gitlab/gitlab-ce:latest启动后需要配置 external_url。这个配置直接决定了 GitLab 页面里所有 Clone 地址的显示。很多人在浏览器里访问 IP 地址没问题但仓库的 clone 地址却是容器 ID 或内网主机名就是因为 external_url 没配对。修改方法很简单sudo docker exec -it gitlab vim /etc/gitlab/gitlab.rb找到external_url改成你的域名或 IP比如external_url http://gitlab.example.com然后执行sudo docker exec -it gitlab gitlab-ctl reconfigure让它生效。GitLab 首次启动初始化比较慢一般要等 3 到 5 分钟可以用docker logs -f gitlab观察日志。初始化完成后访问页面第一次进入会让你设置 root 密码。这里有个细节GitLab 的内存占用比较大默认配置下 8G 内存很可能吃紧。如果你只是测试环境可以通过GITLAB_OMNIBUS_CONFIG环境变量禁用一些不用的组件比如 Prometheus、Grafana能省出不少内存。GitLab 使用过程中第二个高频操作是配置 SSH Key。用户在“用户设置”中粘贴自己的公钥之后就可以用 SSH 方式 clone/push。同时团队落地后建议开启“用户注册验证”或者在初始阶段用管理员账号统一创建团队成员账号避免乱七八糟的账号混进来权限管理会清爽很多。2.3 用 Docker Compose 搭建 Harbor 私有镜像仓库Harbor 官方提供了在线安装包和离线安装包离线包体积比较大但安装成功率高推荐使用离线包。下载解压后进入目录拷贝配置文件模板cp harbor.yml.tmpl harbor.ymlharbor.yml 里必须改三个地方hostname、port、harbor_admin_password。hostname 可以写成 IP 或域名比如harbor.example.comhttp.port 默认是 80如果服务器 80 端口被占了改成 8080 或 8000。修改完之后执行./install.sh。如果你没有现成的 HTTPS 证书最简单的做法是只启用 HTTP把 harbor.yml 里的https:部分注释掉。但要注意Docker 客户端默认只用 HTTPS 拉取镜像要让 Docker 认这个 HTTP 仓库必须配置 insecure-registries。编辑/etc/docker/daemon.json{ insecure-registries: [harbor.example.com:80] }然后sudo systemctl restart docker。Harbor 部署完成后在页面里创建一个项目比如叫prod。之后所有构建的镜像都会推到类似harbor.example.com/prod/appname:v1.0.0这样的地址。Harbor 的镜像复制功能强烈建议了解一下比如你可以把生产仓库的镜像同步到备份机房或者从公网镜像源同步基础镜像到本地仓库这样构建时从本地拉基础镜像速度快、稳定性也高。2.4 常见环境坑虚拟机虚拟化未开启、Harbor 配置校验失败老规矩把两个高频错误单拎出来讲。第一个是 Docker Desktop 在 Windows 上的虚拟化问题。Docker Desktop failed to start because virtualisation support wasnt detected这个报错基本就是 BIOS 没开虚拟化或者 Hyper-V/WSL2 功能没启用。处理顺序是先开 BIOS 虚拟化再启用 Windows 功能最后安装 WSL2 内核。不要想着绕过绕不过去的。第二个是 Harbor 的配置校验失败报错内容是harbor happened in config validation。这个错误十有八九是 harbor.yml 里的字段格式问题。常见原因有三个hostname 写了http://前缀导致校验失败hostname 应该只写域名或 IPhttp.port 写了字符串类型比如port: 80YAML 解析器会报错配置了 https 证书路径但文件真实路径不对或权限不足。检查一下这几个地方问题基本都能解决。3. Jenkins 持续集成从拉代码到构建镜像3.1 Jenkins 安装与 GitLab 连接配置Jenkins 的安装我还是推荐 Docker 方式。但这里有一个极其关键的点Jenkins 容器里需要调用 Docker 命令来构建镜像所以必须把宿主机的 Docker socket 挂载进去让 Jenkins 容器内的 Docker 客户端能和宿主机 Docker 守护进程通信。sudo docker run -d --name jenkins --restartalways \ -p 8080:8080 -p 50000:50000 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v jenkins_home:/var/jenkins_home \ -v $(which kubectl):/usr/local/bin/kubectl \ -v ~/.kube:/root/.kube \ jenkins/jenkins:lts这个命令同时把 kubectl 和 kubeconfig 也挂了进去。挂载 kubectl 时要注意宿主机上which kubectl的路径要真实存在而且二进制文件需要能支持容器内的系统架构。初始化后访问http://server_ip:8080首次进入需要输入管理员密码。密码在容器里的/var/jenkins_home/secrets/initialAdminPassword执行docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword就能看到。插件方面核心必装的是GitLab、Pipeline、Docker Pipeline、Kubernetes CLI、DingTalk。其他插件按需安装。装完插件后在“Manage Jenkins” - “System” 里配置 GitLab 连接。这里需要一个 GitLab API Token在 GitLab 的 User Settings - Access Tokens 中生成勾选api权限。Token 生成后回到 Jenkins添加为凭据填写 GitLab 地址测试连接能看到Success就说明通了。3.2 凭据与 Token 的坑login failed. check api token接入 GitLab 时很多人会碰到一个报错login failed. check api token or gitlab version。别慌这个报错有以下几类原因。Token 权限不足是最常见的。生成 GitLab Access Token 时默认权限可能没勾选 api导致 Jenkins 调 GitLab API 时被拒绝。去 GitLab 重新生成一个勾上 read_api 或 api再试一次。URL 填写错误也会导致同样的问题。GitLab 的 API 路径是http://gitlab.example.com/api/v3或.../api/v4老版本是 v3新版本基本都是 v4。如果版本填错Jenkins 无法正确发起 API 请求就会报这个错。排查方法很简单浏览器直接访问http://gitlab.example.com/api/v4/version如果能正常返回 JSON就说明地址没问题。还有一种情况是 GitLab 版本过旧。Jenkins 的 GitLab 插件迭代很快新版本插件对旧版 GitLab 的兼容性往往不好。如果 GitLab 版本确实旧了要么升级 GitLab要么降低 Jenkins 插件版本。实话说长期运行的团队建议把 GitLab 保持在小版本内滚动升级不要一直停在老版本越往后出现兼容性问题的概率越高。3.3 编写 Jenkinsfile构建、推送、触发更新这一步是整个流水线的核心。我直接给一个 Java 项目的完整 Jenkinsfile 示例。注意里边的镜像 tag 使用了 Git 提交哈希的前 7 位保证每次构建的镜像都有唯一标识方便回滚。pipeline { agent any environment { APP_NAME demo HARBOR_REGISTRY harbor.example.com HARBOR_PROJECT prod // 使用 Git 提交哈希缩短作为镜像版本号 IMAGE_TAG ${GIT_COMMIT.take(7)} K8S_NAMESPACE prod } stages { stage(Checkout) { steps { checkout scm } } stage(Maven Build) { steps { sh mvn clean package -DskipTests } } stage(Docker Build Push) { steps { script { def fullImageName ${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${APP_NAME}:${IMAGE_TAG} docker.withRegistry(http://${HARBOR_REGISTRY}, harbor-credentials) { def customImage docker.build(${APP_NAME}:${IMAGE_TAG}, -f Dockerfile .) customImage.push() customImage.push(latest) } } } } stage(Deploy to K8s) { steps { sh kubectl set image deployment/${APP_NAME} \ ${APP_NAME}${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${APP_NAME}:${IMAGE_TAG} \ -n ${K8S_NAMESPACE} } } } }这个流水线的执行逻辑是push 代码到 GitLab 后Webhook 触发 Jenkins 流水线依次执行拉代码、Maven 构建、Docker 镜像构建并推送到 Harbor最后通过 kubectl 更新 K8s 里 deployment 的镜像版本触发滚动更新。构建镜像时Dockerfile 建议写成多阶段构建以 Java 项目为例FROM maven:3.8-openjdk-11 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests FROM openjdk:11-jre-slim WORKDIR /app COPY --frombuild /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, /app/app.jar]多阶段构建的好处是最终镜像里不带编译器、不包含完整依赖源码体积能小很多同时构建过程还能利用 Docker 层缓存。我见过有的团队把整个 maven 仓库目录塞进镜像里镜像体积高达 2GB部署和回滚都极其痛苦。环境变量方面Jenkins 内置了非常多的变量可以直接引用。常用的几个BRANCH_NAME是当前构建的分支名BUILD_NUMBER是构建序号GIT_COMMIT是完整提交哈希WORKSPACE是工作目录。在流水线里可以通过env.XXX或者在双引号字符串里直接读比如${env.BUILD_NUMBER}。我用得最多的组合是BRANCH_NAME GIT_COMMIT 前缀 BUILD_NUMBER拼出镜像版本号既能定位代码版本又能保证唯一性。3.4 通知与可视化钉钉/飞书消息怎么做发布流程自动化之后通知也得跟上。团队里最常用的方案是钉钉群机器人或者飞书群机器人。虽然消息平台不同但思路一致机器人通过 Webhook 地址接收 POST 请求Jenkins 在流水线最后一步调用 Webhook 推送构建状态。推荐两个实现方式。第一个是装钉钉/飞书官方插件配置相对简单填 Webhook 地址和关键词就行但消息格式固定想自定义内容比较费劲。第二个方式更灵活用 HTTP Request 插件直接 Post 一个 JSON 到机器人 Webhook内容完全可控。比如我常用的一段是stage(Notify) { steps { httpRequest( httpMode: POST, url: https://oapi.dingtalk.com/robot/send?access_tokenYOUR_TOKEN, requestBody: { msgtype: markdown, markdown: { title: ${env.JOB_NAME} 构建结果, text: ### ${env.JOB_NAME} #${env.BUILD_NUMBER}\n- 分支: ${env.BRANCH_NAME}\n- 结果: ${currentBuild.currentResult}\n- 镜像: ${env.HARBOR_REGISTRY}/${env.HARBOR_PROJECT}/${env.APP_NAME}:${env.IMAGE_TAG} } } .stripIndent(), contentType: APPLICATION_JSON, timeout: 3000 ) } }注意机器人安全设置里如果设了关键词消息里就要包含对应关键词否则会被拒收。这个配合发布频率很顺手构建失败时群里立刻弹消息开发自己就能看到问题不用等运维通知。4. K8s 集群与 Rancher 可视化运维4.1 先理清 K8s 和 Docker 的关系很多刚接触的人会问有了 Docker 为什么还要 K8s这个疑问很自然。Docker 解决的是“怎么把应用打包成标准镜像、怎么运行容器”而 K8s 解决的是“几十上百个容器怎么调度、怎么保证服务不中断”。用个生活化的类比Docker 是集装箱解决货物标准化的问题K8s 是港口调度系统负责集装箱的装卸、分配位置和故障处理。港口可以没有集装箱吗可以但效率极低。集装箱可以没有港口调度吗也可以但规模一大就乱套。所以 Docker 和 K8s 不是二选一而是配合关系。K8s 的最小调度单位是 PodPod 里面跑的容器就是 Docker 容器理论上也支持其他容器运行时比如 containerd、CRI-O。K8s 的 Deployment 控制器管理 Pod 的副本数量Service 负责负载均衡Ingress 负责外部访问路由。理解了这几个概念后面的操作就不会懵。4.2 单节点/多节点 K8s 集群搭建实录搭建 K8s 集群我推荐 kubeadm 方案虽然步骤多一点但对底层理解更清晰。所有节点先做基础准备# 关闭 swap sudo swapoff -a sudo sed -i / swap / s/^/#/ /etc/fstab # 加载内核模块 cat EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter # 配置系统参数 cat EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 net.bridge.bridge-nf-call-ip6tables 1 EOF sudo sysctl --system然后安装 kubelet、kubeadm、kubectl。以 Ubuntu 为例官方源配置好后安装即可。安装完成后在 master 节点初始化集群sudo kubeadm init \ --apiserver-advertise-address192.168.1.10 \ --pod-network-cidr10.244.0.0/16安装完成后会输出一段kubeadm join命令保存好之后 worker 节点加入时要使用。如果你是单节点想让 master 节点也调度 Pod执行kubectl taint nodes --all node-role.kubernetes.io/control-plane-注意这个命令会把控制平面节点的污点去掉master 节点上也能跑业务 Pod。测试环境这么做没问题生产环境还是建议让 master 只做管理业务 Pod 跑在 worker 上。网络插件这一步不能省。集群初始化后节点状态会一直是 NotReady直到装好 CNI 插件。我用的是 Calicokubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26/manifests/calico.yaml装完后kubectl get nodes就能看到所有节点 Ready。关于单节点 K8s 上跑若依微服务整套环境这里有一个实测建议。若依微服务包含 Nacos、Gateway、Auth、System、File、Job 等多个服务服务间依赖关系复杂单节点部署时首先要保证内存充足建议 16G 起步。其次 Nacos 的数据存储依赖 MySQLMySQL 要用 PVC 做持久化否则重建 Pod 后配置丢失。最后是服务访问统一走 Ingress把网关暴露到外部内部服务通过 Service 名互相调用不要用 Pod IP因为 Pod 重建后 IP 会变。这套套路应用到你自己的微服务项目上也是通用的。4.3 Rancher 部署与集群导入Rancher 部署非常简单单机方式一个容器就搞定sudo docker run -d --restartunless-stopped \ -p 80:80 -p 443:443 \ --privileged \ rancher/rancher:latest首次访问https://server_ip会提示设置 admin 密码和 Rancher Server URL。Rancher 本身也吃资源4G 内存起步比较稳。部署完 Rancher 之后我们要把现有的 K8s 集群导入进去。在 Rancher UI 里选择“导入已有集群”它会生成一段 kubectl 命令在你的 K8s master 节点上执行集群就会被纳管。之后你在 Rancher 界面上可以直接看到节点状态、Pod 运行情况、日志还能直接在界面上执行 kubectl 等效操作非常方便。这里特别提一下 kubectl 配置文件的设置。很多人在 Jenkins 里想执行 kubectl 命令但是发现 kubectl not found 或者报权限错误。原因就是 kubeconfig 文件没配置好。在 master 节点上kubeadm init 之后会在~/.kube/config生成管理员配置文件。你在 Rancher UI 的集群管理页面也可以直接下载 kubeconfig 文件。把这个文件放到 Jenkins 容器对应的路径下比如/root/.kube/configJenkins 里的 kubectl 命令就能正常工作了。记得检查一下文件权限权限太开放系统会拒绝加载。4.4 高频 K8s 命令与 YAML 关键点K8s 命令系统很庞大但日常高频用的其实就那么几十条。这里整理一个速查表我平时排障基本就是靠这些操作场景命令查看节点状态kubectl get nodes查看某命名空间下 Podkubectl get pods -n prod查看 Pod 详细状态kubectl describe pod podname -n prod查看 Pod 日志kubectl logs -f podname -n prod进入 Pod 容器kubectl exec -it podname -n prod -- /bin/sh调整副本数kubectl scale deployment/demo --replicas5 -n prod更新镜像kubectl set image deployment/demo demoharbor.example.com/prod/demo:v1.0.1 -n prod滚动更新回滚kubectl rollout undo deployment/demo -n prod查看滚动更新状态kubectl rollout status deployment/demo -n prod写 YAML 时三个关键点Deployment 里必须配置资源限制requests 和 limits不然某个服务出问题会把整个节点搞挂Service 的类型内部服务用 ClusterIP 即可外部访问用 Ingress工作负载的存活探针和就绪探针必须配否则程序卡死但是容器还在流量打到故障实例上服务雪崩就是这么产生的。5. 全链路打通一次代码提交触发自动发布5.1 流水线全流程演示设施都就位了我们把整条流水线串一遍。开发完成代码后执行git push origin devGitLab 收到 push 事件通过 Webhook 触发 Jenkins 流水线。这个 Webhook 是预先在 GitLab 项目设置里配好的地址填 Jenkins 的/project/你的任务名触发规则选 Push events 即可。Jenkins Pipeline 启动后走的流程就是前面写的 JenkinsfileCheckout 拉代码、Maven Build 打包、Docker Build and Push 构建镜像推到 Harbor、Deploy to K8s 执行 kubectl set image。每一步都执行完后Jenkins 发出钉钉机器人通知群里弹出消息“构建成功镜像版本 xxx”。此时你再看 Rancher 界面Deployment 对应的 Pod 正在滚动更新新 Pod 先创建等它就绪之后旧的 Pod 才会被销毁。这个过程对用户无感服务始终在线。我经常用这个画面给团队演示效果非常直观。这里要强调一个细节Jenkins 里执行kubectl set image时K8s 不会区分镜像 tag 是latest还是v1.0.0。但如果更新命令里的 tag 没变K8s 会认为镜像没变化不会触发滚动更新。所以镜像 tag 一定不能写死某个值我们当初就是吃了这个亏每次构建的镜像都打 latest 标签导致kubectl set image更新后 Pod 不重建新代码死活不生效。后来全部改用 commit 哈希作为 tag问题彻底解决。5.2 版本回滚与多环境发布自动发布能力有了回滚能力必须跟上。我们当前的策略是每次构建的镜像都保留最近 N 个版本Harbor 里配置了镜像保留策略只保留最近 20 个版本避免仓库被撑爆。当线上出问题时最简单的回滚方式是直接执行kubectl rollout undo回滚到上一次部署的版本。如果要回滚到更早的版本可以先通过kubectl rollout history deployment/demo -n prod查看历史版本再用kubectl rollout undo deployment/demo --to-revision版本号精确回滚。整个操作用时几十秒基本不影响线上用户。多环境发布我们也做了拆分。开发环境、测试环境、预发布环境、生产环境分别用不同的 K8s 命名空间Jenkins 里建了对应的 Jenkinsfile 分支比如 dev 分支触发构建后部署到 dev 命名空间master 分支触发后部署到 prod 命名空间。通过环境变量区分镜像地址、数据库地址、配置中心地址避免不同环境互相干扰。5.3 上线半年遇到的典型问题速查表平台跑起来之后各种问题会不断冒出来。这里整理一份高频问题速查表基本覆盖了最常踩的坑。症状根因解决方案Jenkins 构建时 docker 命令失败容器内 docker socket 未挂载或权限不足确认启动时加了-v /var/run/docker.sock:/var/run/docker.sockJenkins 执行 kubectl 报 not found容器内没有 kubectl 二进制挂载宿主机的 kubectl或者用 Kubernetes CLI 插件kubectl 权限错误kubeconfig 未正确配置或权限过宽检查~/.kube/config路径和权限文件权限设为 600Push 镜像到 Harbor 报 http 错误Docker 未配置 insecure-registries在/etc/docker/daemon.json添加仓库地址并重启 dockerKubernetes 节点 NotReadyCNI 网络插件未安装或状态异常执行kubectl get pods -n kube-system排查 calico/flannelPod 一直 ContainerCreating镜像拉取失败或存储卷挂载失败kubectl describe pod查看具体事件更新镜像后 Pod 不重建镜像 tag 未变化不要用 latest使用 commit 哈希或时间戳作为 tagHarbor 镜像越来越多未配置清理策略在 Harbor 项目里配置镜像保留策略滚动更新卡住readinessProbe 未配置或配置错误给 Deployment 添加正确的就绪探针GitLab clone 地址是容器 IDexternal_url 未配置修改 gitlab.rb 中的 external_url 并 reconfigure还有一个我特别想分享的坑Jenkins 集群跑久了/var/jenkins_home目录会越来越大尤其是构建日志和旧的 workspace 文件。如果不定期清理磁盘会被打满导致构建频繁失败。我们后来在 Jenkins 里配置了“丢弃旧构建”的策略保留最近 30 次构建记录同时写了个定时任务清理过期的 workspace这个问题才算彻底解决。另外IDEA 打包 Docker 镜像这个需求也经常有人问。如果你开发本地想快速构建镜像看效果可以通过 Maven 的 dockerfile-maven-plugin 或者 Jib 插件直接打包推送不需要走完整的 Jenkins 流水线。但注意这种本地构建方式不能替代正式流水线否则会出现“本地能跑、线上失败”的不可控情况。开发期可以随意上线前一切以 Jenkins 构建为准。回滚这个动作很关键我对团队的要求是“任何人、任何时候都可以一键回滚”。Rancher 界面里选中对应 Deployment点“回滚”按钮就能执行这对运维同学非常友好不需要每次都在终端里敲一堆命令。监控这块建议后续再加上。平台能跑起来只是第一步真正长期稳定运行需要 Prometheus Grafana 做监控告警Harbor 的镜像复制可以做到跨机房同步Rancher 的多集群管理功能可以在业务扩展时直接纳管新集群。这些是后续扩展的方向但基础框架搭好之后往上叠加组件都会非常快。这套平台从落地到现在最大的收益不是“部署快了”而是整个团队的研发节奏被重塑了。以前每次发布都是一场战役现在发布就是日常操作。开发、测试、运维之间的协作流畅了很多线上问题能够快速发现、快速回滚风险敞口小了很多。如果你正在犹豫要不要搭这么一套全链路平台我的建议是只要团队规模超过五个人、服务数量超过三五个就值得投入成本去搭。前期搭建确实繁琐但粘性一旦建立起来你就再也不想回到手动发布的日子了。