CI/CD平台搭建:从GitLab到K8s全链路实践
搞了这么多年的持续交付我一直觉得“CI/CD平台搭建”是个被严重低估的工程活。很多团队手里的工具其实都不差代码仓库、构建机、镜像仓库、编排系统样样都有但把它们串成一条能自动跑的流水线就总会卡在某个环节Jenkins连不上GitLab、镜像推不进Harbor、K8s拉不到私有仓库的包、Rancher导入集群后节点全是NotReady。这些坑我基本都踩过一遍所以每次看到有人问“GitLabJenkinsDockerHarborK8sRancher怎么搭一套CICD”我都想把完整的思路和细节直接扔过去。这篇文章不是官方文档式的罗列而是我实际从头搭建这套平台、并把一个Java微服务应用自动发布到K8s集群的经验复盘。文里会讲清楚每个组件为什么选它、全链路数据是怎么流转的、关键配置该怎么写、以及那些不翻文档根本发现不了的坑。目标读者很明确想在公司内部落地一套自建DevOps平台或者准备考K8s相关认证、需要亲手搭一套完整环境来练手的人。如果你是这种人这篇文章应该能省下你至少一周的摸索时间。1. 整链路设计与组件选型思路1.1 为什么偏偏是这六个组件先聊一个最容易被忽略的问题为什么是GitLab、Jenkins、Docker、Harbor、K8s、Rancher这六样而不是别的先说GitLab。代码仓库的选择其实很多GitHub、Gitea、Bitbucket都行但GitLab在企业内网落地有一个非常大的优势它自带完整的DevOps生命周期管理从代码托管、Merge Request评审到CI/CD、容器镜像仓库全都有。虽然我们这里不用它的内置CI但代码托管、分支权限、Webhook这些能力非常成熟社区版免费功能也够用。更重要的是很多公司已经有现成的GitLab实例这套链路可以无缝接上去。Jenkins是我在这套链路里最坚持的选择。有人会问“GitLab自带CI为什么还要单独搞Jenkins”我的回答是Jenkins的插件生态和Pipeline自由度目前依然是所有CI引擎里最强的。你可以用Groovy写任意复杂的构建逻辑几百个插件覆盖你能想象到的所有构建场景。而GitLab CI更适合轻量级、以YAML为主的流水线一旦遇到复杂的多分支策略、跨项目编排或者需要和大量内部系统对接时Jenkins的可控性会明显更好。Docker不用多说它是整个容器化链路的地基。需要特别区分的是Docker和K8s的关系Docker负责把应用打包成标准化的镜像并负责单台机器上容器的运行K8s负责的是跨多台机器的容器编排、调度、伸缩和故障恢复。按生活化类比来说Docker是“打包工”K8s是“调度中心”一个是生产标准件一个是管理标准件怎么摆放和替换。Harbor则是解决镜像存储和分发的问题。你可能觉得“Docker Registry不也够用吗”但Harbor在企业场景下多提供了几个关键能力基于角色的访问控制、镜像复制同步、漏洞扫描、审计日志。特别是多环境部署时Harbor的镜像复制功能可以让你把镜像从测试环境同步到生产环境这个在合规审计里几乎是刚需。最后是Rancher。它在K8s之上做了一层统一管理面板就像给你的K8s集群装了套“带界面的控制台”。对于团队里不习惯整天敲kubectl命令的运维和开发Rancher的UI操作非常友好而且它支持一个界面同时管理多套K8s集群这在测试环境、预生产、生产多套集群并行时非常实用。引入Rancher不是为了替代K8s而是为了降低K8s的使用门槛。这套组合的本质是一条完整的“代码到应用发布”流水线每个组件负责一个不可替代的环节环环相扣。1.2 全链路工作流程拆解先把整条链路的数据流讲透后续配置你才能理解“为什么这个参数要这么填”。当一个开发者在本地把代码push到GitLab的指定分支后GitLab会根据仓库里预先配置的Webhook往Jenkins发送一个HTTP请求内容大概是“某分支有新提交了”。Jenkins收到请求后会根据触发规则找到对应的流水线任务开始执行构建。构建阶段通常包括从GitLab拉取最新代码在构建机或Jenkins容器内执行Maven或Gradle打包生成可执行文件然后读取项目里的Dockerfile把可执行文件打成Docker镜像。这里的镜像不是推送到Docker Hub而是推送到公司内部部署的Harbor私有仓库。镜像推送到Harbor之后K8s集群并不会自动感知。实际上还需要再走一步Jenkins在镜像推送成功后会执行kubectl命令修改K8s集群里的Deployment配置里的镜像版本号然后触发滚动更新。如果Harbor是私有的K8s节点在拉取镜像时还需要预先配置一个imagePullSecret否则会出现永远ImagePullBackOff的报错。如果你问“为什么不让K8s那边定时去Harbor拉最新镜像”这个问题问得很好。一个镜像tag如果永远是latest你就永远不知道线上跑的到底是哪个版本。所以正确做法是给镜像打上唯一tag比如build-20250317-001然后通过修改Deployment的镜像tag来触发更新。这既保证了可追溯性也让回滚变得简单改回旧的镜像tag即可。1.3 资源规划与部署架构开始动手前先规划机器资源。最低配置和推荐配置差别很大我建议按下面的标准来最小可用架构个人学习/小型团队试用一台8核16G的服务器将所有组件通过Docker Compose方式部署在同一台机器上K8s使用单节点集群minikube或者kubeadm单节点都可以。8G内存其实很紧张实际跑起来后K8s系统组件大概要占2G多Jenkins和GitLab各占1到2GHarbor也要1G左右。所以16G是底线再多几个微服务就很容易打满。推荐生产架构正式环境至少3台4核8G的节点组成K8s集群再单独准备两台4核8G的机器分别跑GitLabJenkins和Harbor。生产环境不建议把GitLab和Jenkins塞进K8s集群里除非你已经有成熟的K8s运维能力否则一旦集群出问题CI系统也跟着挂就违背了“稳定”的初衷。架构上再强调一个容易犯的错误不要在生产环境把Harbor和K8s集群混部。因为Harbor挂了K8s节点虽然不会立刻停止运行但一旦Pod需要重新调度到新节点、或者镜像需要重新拉取时就会卡死。这是我在一次故障演练后深刻体会到的。2. 环境准备与基础组件部署2.1 系统初始化与前置配置我实际部署用的操作系统是Ubuntu 22.04 LTS和CentOS 7.9都跑过。如果你用CentOS 7需要注意内核版本对K8s的支持建议先升级内核Ubuntu则省心很多。开始前先做几项通用初始化更新系统源并安装基础工具yum install -y vim net-tools wget或apt install -y vim net-tools wget关闭swapK8s默认要求关闭swap分区否则kubelet无法启动。swapoff -a并注释掉/etc/fstab里的swap行加载内核模块并调整系统参数/etc/sysctl.d/k8s.conf里至少要有net.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 vm.swappiness 0这是K8s网络通信的基础不配置的话Pod网络会有问题。修改主机名并保证每台机器的主机名能互相解析。很多节点NotReady的坑就是主机名解析失败导致的。最简单的做法是编辑/etc/hosts把三台机器的IP和主机名写进去。时间同步。K8s对时间同步要求很严格证书校验、事件时间戳都依赖准确的系统时间。chronyc sources确认时间源正常。2.2 GitLab部署与初始化GitLab的部署方式最简单的就是用官方Docker镜像跑单容器。虽然GitLab官方更推荐用Omnibus包直接装在宿主机上但既然我们整套链路都容器化用Docker部署也更便于迁移和备份。我的部署命令大致如下docker run -d \ --name gitlab \ --restart always \ -p 2222:22 -p 80:80 -p 443:443 \ -v /data/gitlab/config:/etc/gitlab \ -v /data/gitlab/logs:/var/log/gitlab \ -v /data/gitlab/data:/var/opt/gitlab \ -e GITLAB_OMNIBUS_CONFIGexternal_url http://gitlab.example.com; gitlab_rails[gitlab_shell_ssh_port] 2222 \ gitlab/gitlab-ce:latest注意三个细节。第一external_url必须设置为你希望用户访问的地址如果后面用户通过git clone http://主机的IP/xxx.git去拉代码这个URL就会显示成你配置的地址。热词里有人问“clone with http怎么clone设置为域名 不是机器id”答案就在这里在GitLab管理后台把External URL改成域名或者改/etc/gitlab/gitlab.rb里的external_url配置后重新配置。第二宿主机22端口如果被占用就映射成2222但GitLab克隆SSH地址时要记得带上这个端口否则连不上。第三GitLab容器首次启动耗时比较久需要几分钟初始化不能急。可以通过docker logs -f gitlab监控日志看到“GitLab is ready”或类似提示后再访问。首次访问时浏览器打开GitLab地址系统会要求设置root密码。很多人忽略了这个细节导致后面积累了数据后忘了root密码。更推荐的做法是直接查看容器内生成的初始密码docker exec gitlab cat /etc/gitlab/initial_root_password用这个密码登录后立刻修改。日常使用中开发者在GitLab上最好配置SSH Key这样拉取和推送代码时不用每次输密码。在GitLab页面上头像菜单里找到Settings → SSH Keys把本地生成的~/.ssh/id_rsa.pub内容粘贴进去。这个动作看着简单但在整个CI/CD链路里非常重要因为后面Jenkins要拉取GitLab代码用SSH凭据方式是最稳妥的。2.3 Harbor私有镜像仓库部署Harbor的部署方式和GitLab类似但步骤会更繁琐一些因为它还依赖PostgreSQL、Redis等组件。官方提供了在线和离线安装包企业内部网络环境建议下载离线包避免安装过程中拉取外部镜像超时。下载并解压后会得到一个harbor.yml文件这个文件需要改几个关键项hostname: harbor.internal.com http: port: 8080 harbor_admin_password: Admin12345 database: password: Db12345这里有一个特别容易踩坑的地方Harbor对密码强度有要求必须包含大小写字母和数字否则启动时会在配置校验阶段报错。热词里有人遇到“harbor happened in config validation”十有八九就是这个原因。我一开始就吃了这个亏设置了一个太简单的密码结果./install.sh跑起来后看到一堆validation error翻了好一会儿日志才定位到问题。hostname字段很关键它会被写入镜像tag里。比如你配置的是harbor.internal.com那么推送镜像时就必须写成harbor.internal.com/library/myservice:latest。如果你在/etc/hosts里把这个域名映射到Harbor所在机器的IP那么本地测试也能正常推送。这里要提醒如果Harbor不启用HTTPS那Docker客户端必须在/etc/docker/daemon.json里配置{ insecure-registries: [harbor.internal.com:8080] }否则执行docker login harbor.internal.com:8080时会报“certificate signed by unknown authority”或“http: server gave HTTP response to HTTPS client”。配置好后重启Docker服务登录Harbor才能成功。安装时我通常会执行./install.sh --with-trivy把漏洞扫描组件也带上虽然会多占一些内存但扫描镜像漏洞功能对生产环境非常重要可以在镜像进入K8s前就拦截掉高危漏洞。在Harbor页面里创建一个名为library的项目把它设为公开或私有都行。建议公开这样K8s集群拉取时就不用每次都配置凭据但前提是Harbor只在内网可达。3. Jenkins流水线实现CI3.1 Jenkins安装与插件准备Jenkins的安装方式我推荐直接用官方war包配合systemd管理或者用Docker跑都行。我习惯用Docker方式因为可以单独隔离环境不污染宿主机。但要注意一点如果用Docker方式跑Jenkins而且你还想在Jenkins里直接用宿主机的Docker来构建镜像就需要把宿主机/var/run/docker.sock挂载进Jenkins容器否则Jenkins内部没法调用Docker命令。这一点很多人没注意到导致流水线里执行docker build时报“Cannot connect to the Docker daemon”。我的Jenkins启动命令大致是docker run -d \ --name jenkins \ -p 8080:8080 -p 50000:50000 \ -v /data/jenkins_home:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/bin/docker:/usr/bin/docker \ --restart always \ jenkins/jenkins:lts首次启动后浏览器访问http://jenkins_ip:8080会要求输入解锁密码。这个密码在Jenkins容器里的/var/jenkins_home/secrets/initialAdminPassword文件里可以直接用docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword获取。插件选择上建议在安装向导里直接选“Install suggested plugins”然后在系统管理里再额外安装几个关键插件Git plugin拉取GitLab代码的基础插件默认会带上Docker Pipeline在流水线里执行docker build、docker push需要使用Kubernetes CLI用于在流水线里执行kubectl命令后面CD阶段会用到GitLab插件用于和GitLab Webhook集成同时支持在pipeline中调用GitLab API插件安装完成后第一件事就是配置“系统管理 → Manage Credentials”里的凭据。Jenkins连接GitLab我踩过的坑是在凭据类型上选错了。如果你用SSH方式拉代码凭据类型选“SSH Username with private key”填入GitLab部署用户的私钥。如果你用HTTP方式那就用GitLab的Access Token作为密码用户名可以写任意值。热词里有人遇到“login failed. check api token or gitlab version”这个报错通常是GitLab的Access Token权限不足或已过期需要在GitLab里重新生成一个具有api和read_repository权限的token再回Jenkins更新凭据。3.2 构建Java微服务Pipeline示例这里给出一个典型的Java Maven项目流水线片段用Jenkins Pipeline语法编写。这个示例涵盖了拉代码、构建、打镜像、推镜像四个阶段是整条CI流水线的核心逻辑pipeline { agent any environment { DOCKER_REGISTRY harbor.internal.com:8080 IMAGE_REPO library/my-service GITLAB_CRED credentials(gitlab-ssh-key) } stages { stage(Checkout) { steps { checkout([$class: GitSCM, branches: [[name: */main]], userRemoteConfigs: [[url: gitgitlab.internal.com:dev/my-service.git, credentialsId: gitlab-ssh-key]] ]) } } stage(Build) { steps { sh mvn clean package -DskipTests } } stage(Build Docker Image) { steps { script { def image ${DOCKER_REGISTRY}/${IMAGE_REPO}:build-${BUILD_NUMBER} sh docker build -t ${image} . sh docker push ${image} } } } } }这段代码里值得注意的有几点。第一镜像的tag我用了build-${BUILD_NUMBER}而不是latest。这在前面已经解释过原因为了可追溯和可回滚。BUILD_NUMBER是Jenkins内置的环境变量每次任务执行都会自动递增天然是唯一的。如果你想同时保留多个特征比如分支名、Commit Hash可以继续拼接比如${BRANCH_NAME}-${GIT_COMMIT.take(7)}-${BUILD_NUMBER}但要注意tag里不能有斜杠分支名如果带斜杠要处理一下。第二流水线里用了credentials(gitlab-ssh-key)引用的是之前创建的凭据ID。Jenkins会用这个凭据去拉取GitLab代码。如果你遇到“Host key verification failed”的报错说明Jenkins容器里没有把GitLab的主机公钥加入known_hosts。解决办法是在Jenkins容器里执行ssh-keyscan gitlab.internal.com ~/.ssh/known_hosts或者更稳妥的方式是在流水线里加一行sh GIT_SSH_COMMANDssh -o StrictHostKeyCheckingno git pull。第三执行docker build时有个经常被忽视的细节Dockerfile里如果基于的base image也要从Harbor拉取那构建时会提示找不到镜像。这时候的解决办法和K8s节点拉镜像一样在Jenkins所在的宿主机Docker配置里加入insecure-registries并且在构建前先执行docker login。3.3 GitLab Webhook联动排查CI配置好之后还需要把GitLab和Jenkins联动起来当代码push时GitLab自动触发Jenkins构建。在GitLab项目页面的“Settings → Webhooks”里填写Jenkins的构建触发地址格式是http://jenkins_ip:8080/project/你的任务名同时添加一个Secret Token这个token就是之前我们创建的GitLab API Token。GitLab会把这个token放到请求头里Jenkins确认后才执行。如果你用了反向代理或域名访问Jenkins务必在系统管理里把Jenkins URL配置成对外可访问的地址否则Webhook回调会失败。我遇到过的一个典型问题是Webhook配置时点“Test”显示成功但代码push就是不会触发构建。后来发现是Jenkins任务配置里只选了“Build when a change is pushed to GitLab”却没有勾选对应的分支过滤规则。在Jenkins任务配置的GitLab触发面板里要明确填写触发分支名如mainGitLab的push事件才会被正确匹配。还有一次踩坑是出于安全考虑给Jenkins加了一层Nginx反向代理导致GitLab的Webhook走HTTP回调到Jenkins时Jenkins拿到的是代理IP而不是GitLab请求的真实来源IP。这不影响构建触发但影响Jenkins的权限控制日志。如果不想深究这个直接用IP加端口的方式访问Jenkins是最省心的。4. K8s集群搭建与Rancher接入实现CD4.1 K8s集群搭建方式对比从CI阶段进入CD阶段核心就是把构建好的镜像真正跑起来。这一步的关键是K8s集群本身。K8s集群的搭建方式我用过kubeadm手动搭、也用过sealos一键部署。kubeadm是官方标准方式适合学习原理但步骤多、容易出错尤其在网络组件和证书配置上。sealos则是傻瓜式操作一行命令能拉起来一个高可用集群适合生产快速部署。如果你的目的是搭建一套可用的CICD平台而不是深入学习K8s集群原理强烈建议用sealos或类似工具节省时间。用kubeadm手动搭集群的大致步骤包括在所有节点安装kubelet、kubeadm、kubectl版本要一致在master节点执行kubeadm init --apiserver-advertise-address主IP --pod-network-cidr10.244.0.0/16按输出提示配置kubectl的kubeconfig文件安装Pod网络插件我常用的Calico或Flannel将worker节点用kubeadm join命令加入集群这里需要强调“kubectl配置文件”是什么。很多新手会在热词里搜“gitlab 怎么设置kubectl 配置文件”其实这个文件不是只给GitLab用的而是K8s的访问凭据默认路径是~/.kube/config。里面包含了集群地址、证书、用户信息。后面Jenkins要用kubectl操作K8s集群就必须把这个config文件内容放到Jenkins的凭据里或者直接挂载到Jenkins容器中。热词里还有“单节点 k8s 上的若依微服务整套环境”这说明很多人想在单机学习环境里跑完整的微服务。这里有个关键点单节点集群默认control-plane节点是不允许调度业务Pod的需要用一条命令去掉这个限制kubectl taint nodes --all node-role.kubernetes.io/master-否则你部署应用时Pod会一直卡在Pending状态。4.2 Rancher部署与集群导入K8s集群起来后我不会急着直接用命令行部署应用而是先把Rancher装上。Rancher有两种部署方式一种是docker run直接跑单容器另一种是Helm方式部署到K8s集群里。如果你想用Rancher监控和管理现有集群直接在任意一台有Docker的机器上跑docker run -d \ --name rancher \ --restart unless-stopped \ -p 80:80 -p 443:443 \ --privileged \ rancher/rancher:latestRancher首次启动后访问UI会让你设置管理员密码和服务器URL。服务器URL填写Rancher服务对外访问的地址整个后续的操作都基于这个地址做跳转。Rancher UI里的核心操作是“导入集群”。点“添加集群 → 现有集群”Rancher会生成一个用于导入的kubectl命令在你的K8s master节点上执行这个命令Rancher会自动安装一些管理组件并在几分钟之内把集群状态同步到UI。导入后你可以在Rancher上直接看到集群的节点信息、工作负载、服务发现这些内容完全不用再去记复杂的kubectl命令。Rancher本身就相当于K8s的一个“驾驶舱”。但这里要特别提醒一个点Rancher和K8s集群的版本兼容性问题。太新的Rancher版本可能不支持老的K8s版本反之亦然。我第一次搭的时候Rancher用最新版K8s用的却是1.20结果导入后一堆组件状态异常。后来把K8s升级到和Rancher兼容的版本问题才解决。建议在搭建前查一下官方兼容性表格别用太古老或太新的组合。4.3 应用部署与私有仓库拉取应用部署到K8s本质上就是创建Deployment和Service资源。以我们前面构建推送的harbor.internal.com:8080/library/my-service:build-123镜像为例最小可用的Deployment YAML如下apiVersion: apps/v1 kind: Deployment metadata: name: my-service namespace: dev spec: replicas: 2 selector: matchLabels: app: my-service template: metadata: labels: app: my-service spec: containers: - name: my-service image: harbor.internal.com:8080/library/my-service:build-123 ports: - containerPort: 8080如果你直接执行kubectl apply -f deploy.yaml大概率会遇到ImagePullBackOff因为K8s节点尝试去Harbor拉镜像但Harbor是私有的没有登录凭据。解决办法是先在K8s集群里创建一个docker-registry类型的Secretkubectl create secret docker-registry harbor-secret \ --docker-serverharbor.internal.com:8080 \ --docker-usernameadmin \ --docker-passwordAdmin12345 \ --namespacedev然后在Deployment的模板里加上imagePullSecrets: - name: harbor-secret加了这一段的Deployment才能成功从私有仓库拉取镜像。这是整个CD流程里最容易被忽略、也是报错率最高的一个步骤。很多人在Rancher的UI里直接部署却忘了配置Image Pull Secret结果页面一直显示拉取失败。如果你用Rancher UI部署可以在工作负载页面里配置“镜像拉取凭据”原理和命令行完全一样。应用跑起来后你可以用kubectl get pods确认状态再用kubectl logs查看日志。如果要让外部访问还需要创建Service类型可以是NodePort、LoadBalancer或者Ingress这个根据你的网络环境来选。在Rancher里操作更加直观创建Service时选择端口映射几秒钟就能完成。4.4 Jenkins到K8s的CD打通有了Deployment YAML和集群访问凭据后最后一步就是把Jenkins构建完镜像之后的操作串起来镜像推送到Harbor后自动更新K8s里的Deployment镜像版本。我用的方案是Jenkins里新增一个“Deploy to K8s”阶段用kubectl命令直接更新Deployment的镜像tag。第一步在Jenkins里配置K8s集群的kubeconfig凭据。系统管理 → Manage Credentials凭据类型选择“Secret file”上传~/.kube/config文件的内容。然后在Pipeline里引用这个凭据把它写到工作目录stage(Deploy to K8s) { steps { withCredentials([file(credentialsId: k8s-kubeconfig, variable: KUBECONFIG)]) { sh export KUBECONFIG${KUBECONFIG} kubectl set image deployment/my-service my-service${DOCKER_REGISTRY}/${IMAGE_REPO}:build-${BUILD_NUMBER} -n dev } } }这一步直接通过kubectl set image命令触发Deployment滚动更新K8s会自动拉取新镜像并逐步替换旧Pod。好处是不用写复杂的YAML一行命令就能完成发布。如果你的Deployment配置比较复杂比如有环境变量、配置映射、健康检查更稳妥的方式是在GitLab仓库里维护一份deploy.yaml模板然后在Jenkins里用sed或模板工具把镜像tag替换进去再执行kubectl apply -f deploy.yaml。替换命令大概长这样sed -i s|image: harbor.internal.com:8080/library/my-service:.*|image: ${DOCKER_REGISTRY}/${IMAGE_REPO}:build-${BUILD_NUMBER}|g deploy.yaml kubectl apply -f deploy.yaml这里的正则要点是.*用来匹配旧的tag而不关心它具体是什么所以每构建一次Deployment都会指向新镜像。而且这种方式能顺带更新Service、ConfigMap等其他资源适合微服务数量多的情况。还要注意权限问题Jenkins通过kubeconfig访问K8s集群时用的是kubeconfig里内置的用户通常是admin用户。这么做在测试环境没问题但生产环境强烈建议创建一个专用ServiceAccount只授予需要的命名空间权限避免Jenkins误操作其他资源。这个操作可以通过RBAC来实现kubectl create serviceaccount jenkins -n dev kubectl create rolebinding jenkins-dev-binding \ --clusterroleedit --serviceaccountdev:jenkins -n dev然后把这个ServiceAccount对应的token配置到Jenkins的Kubeconfig里。权限最小化是生产安全的基本要求我实际在正式环境就是这么做的。5. 常见问题与排查技巧实录5.1 高频报错速查表整理了这套平台搭建过程中我遇到的和身边朋友问得最多的问题做一个速查表方便你按图索骥现象常见原因排查与解决思路GitLab页面无法访问容器未启动完成或external_url配置错误docker logs看omibus日志确认initial_root_password出现后再访问推送Harbor报x509证书错误Docker未配置insecure-registries或Harbor证书问题检查/etc/docker/daemon.json重启Docker再执行docker login验证Harbor初始化报config validation错误密码强度不够或hostname填写错误确保hostname不包含协议密码包含大小写字母和数字Jenkins无法连接GitLabToken权限不足或Jenkins插件版本过旧在GitLab生成新Token勾选api和read_repository权限更新Jenkins凭据Jenkins执行docker build无权限Jenkins容器没挂载docker.sock启动Jenkins时挂载/var/run/docker.sock并将jenkins用户加入docker组K8s Pod一直Pending单节点master有污点未去除或资源不足kubectl describe pod看事件执行去除taint命令或检查节点CPU/内存Pod状态ImagePullBackOff没有配置imagePullSecrets或Harbor地址不通创建docker-registry Secret在Deployment里引用用docker pull在节点上验证地址可达性Rancher导入集群后节点NotReady主机名解析失败或网络插件异常检查/etc/hosts、Pod网络Pod是否Running查看kubelet日志Webhook测试成功但Jenkins不触发任务没配置分支过滤规则或Webhook URL不正确检查GitLab Webhook里填的URL和Jenkins任务名是否精确一致分支过滤里是否写了正确分支5.2 低配置机器下的资源调优心得很多朋友用一台8G内存的机器想跑完整套平台经常卡到怀疑人生。我说说实际优化经验。先说K8s集群本身的资源占用。单节点K8s跑起来后光系统组件kubelet、etcd、kube-apiserver、kube-scheduler、kube-controller-manager、Pod网络加起来就要占2GB左右内存这还没算分布式存储。如果你测试环境对存储要求不高可以考虑不用StorageClass减少相关组件的开销。Jenkins也是个内存大户。默认JVM参数可能让它尝试吃满系统内存必须在启动参数里限制JAVA_OPTS-Xms512m -Xmx1024m加入/etc/default/jenkins或Docker启动命令的环境变量里否则Jenkins会拖垮整台机器。GitLab也是内存消耗大户默认配置会使用Unicorn和Sidekiq在docker启动命令里可以加GITLAB_OMNIBUS_CONFIGunicorn[worker_processes]2; sidekiq[max_concurrency]5来降低内存占用。还有一个细节是Docker在Windows或macOS上启动报“virtualization support wasnt detected”。这个报错其实是宿主机BIOS里没开虚拟化或者Windows的Hyper-V没有被启用。解决办法是进入BIOS开启VT-x/AMD-V并在Windows功能里勾选Hyper-V或Windows Hypervisor Platform。在国内很多人用Docker Desktop时遇到这个问题通常就是电脑的虚拟化被安全软件或系统策略关了。5.3 提高流水线稳定性与团队协作当整套平台能跑通之后更进一步的问题是如何让流水线更稳定、更好用。这里分享几个我认为很有效的实践。第一Docker镜像构建一定要利用缓存。写Dockerfile时把依赖下载层放在源码复制之前。比如Java项目常见的分层写法FROM openjdk:11 WORKDIR /app COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java,-jar,app.jar]这个写法的缺点是每次代码变更整个镜像层都重新构建因为target/*.jar的内容变了。而更优的写法是先把pom.xml复制进去执行mvn dependency:go-offline把依赖下载缓存成一两层再复制源码打jar。这样依赖没有变更的情况下Docker能直接复用缓存层构建速度能快一倍以上。第二多环境部署时的镜像复用策略。我建议在测试环境构建出的镜像直接复用为预生产和生产环境的镜像不要再各自构建一次。也就是说同一个构建产物通过修改Deployment里的配置数据库连接、注册中心地址等打在不同的环境里。这样能最大化保证“测试通过的产物就是上线运行的产物”。Harbor的镜像复制功能可以帮你把镜像同步到生产环境的Harbor仓库避免不同环境之间网络不通的问题。第三团队协作上的一个建议粗粒度流水线拆分成多个任务。如果所有微服务都放在一个Jenkins流水线里每次构建都全量执行会很慢也很乱。可以按服务维度拆分Jenkins多分支流水线任务或者用GitLab的Group Jenkins的Organization Folder来自动发现仓库。这个方案一开始配置起来麻烦但长期维护会轻松很多。5.4 热词背后的典型需求解读浏览这组热搜词的时候我发现几个出现频率很高的词背后其实对应着不同的需求层次这里集中回应一下。“gitlab使用教程”“jenkins配置gitlab connection”“gitlab拉取代码到本地”这类词说明很多人卡在最基础的账号接入和网络配置上。核心就是三类操作SSH key配置、HTTPS克隆地址、Jenkins凭据配置。把这三件事理顺GitLab这一环基本就通了。“k8s和docker区别”“k8s部署教程”“k8s常用命令”这类词说明不少人是第一次接触容器编排。我的建议是先用Docker Compose跑通单体应用再学K8s跑同样的应用最后再上微服务。跳过基础直接玩微服务遇到问题时完全不知道是网络问题、存储问题还是编排问题排查成本极高。热词里“单节点 k8s 上的若依微服务整套环境”就是这么个吃力不讨好的场景不是不能跑但一定要先弄懂单节点K8s的限制和调试手段。“准不停服、不丢数据地迁移到阿里云ecs”这个词比较特别它可能来自某个具体问题。但从DevOps平台的角度看数据迁移和滚动更新本质上是一回事你需要保证服务在替换节点或集群时数据不丢、请求不断。在K8s里这取决于你的应用是否做到无状态化数据放到外部存储实例不保存本地状态以及Deployment的strategy是否配置了RollingUpdate而不是Recreate。所以别急着看迁移工具先认识到底层的工作机制。停服迁移一定不是因为工具不行而是架构没有支持滚动更新。“dubbo mesh(k8s service mesh)”和“idea 打包docker镜像”这两个词很有趣一个代表K8s生态的进阶方向服务网格一个代表开发环境的日常操作。前者说明有人已经在考虑微服务治理和流量治理的问题建议先扎实掌握K8s原生能力再上Service Mesh。后者说明开发者希望IDE一体化操作在IDEA里安装Docker插件配置Docker Host地址后就能直接右键镜像构建和推送这对开发阶段做本地验证很有帮助能有效减少和运维联调的等待时间。最后说一个很实际的经验。每次搭建这套平台的过程本质上是一次对企业软件交付流程的重新梳理。很多团队不是缺工具而是缺一条让代码变更快速、安全、可靠地抵达生产环境的路径。单靠某一个组件解决不了这个问题只有把GitLab的代码管理、Jenkins的自动构建、Docker的标准化打包、Harbor的镜像管理、K8s的容器编排、Rancher的集群管理这六层串起来形成一个闭环才能真正意义上实现“提交代码后剩下的事情交给流水线”。我踩过最大的坑就是在资源不足时硬上整套平台结果排查定位问题时反而更混乱。如果你只有一台机器先别上K8s用Docker Compose把GitLab、Jenkins、Harbor跑通先实现手动触发构建和推送再单独找一台机器装K8s手动kubectl apply部署等这些都熟了再引入Rancher和自动化CD。一步步来比一次全上然后花一周排错要靠谱得多。这一套平台搭建完之后后续的演进方向也值得留个心眼Jenkins的流水线可以逐渐标准化成模板库微服务的部署文件可以搬进GitOps的仓库里管镜像漏洞扫描和审批流也可以接进流水线。工具会不断迭代但“代码、构建、镜像、部署”这条主线不会变。把主线想清楚工具只是实现方式的选择问题。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →