ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Jenkins部署集成方式对比:主机直装、Docker与Kubernetes选型指南

Jenkins部署集成方式对比:主机直装、Docker与Kubernetes选型指南 刚接手CI/CD基础设施那段时间我翻遍了各种Jenkins安装文档脑子里冒出来的问题特别统一Jenkins到底应该装在哪是老老实实跑在服务器上还是塞进Docker容器又或者直接托管到Kubernetes里后来我想明白了这个问题本身就值得拿来做一次Jenkins部署集成方式与使用场景对比。今天我不打算只贴安装命令而是把这些年自己用过、也帮别人排查过的几种部署路线全部摊开讲清楚它们的适用边界、配置要点和真正容易踩坑的地方。无论你是刚接触CI/CD的新人还是在为团队选型做评估的工程师这篇文章的目标都是帮你建立一套决策框架先搞清楚控制器和Agent的承载形态再谈插件、镜像、权限和后续的自动部署集成。1. 总要回答的第一个问题Jenkins装在哪里决定了后面所有集成方式很多人在选型时只盯着哪个方式安装快实际上忽略了一个更关键的事实部署形态会直接决定你后面所有的集成路径。Jenkins作为CI/CD中枢它不仅要连代码仓库、制品库还要管Agent、产物和目标环境。控制器装在哪决定了别人怎么连它Agent以什么方式跑起来决定了构建任务的种类和并发能力。1.1 三种主流部署载体的形态差异按我接触过的团队Jenkins的部署形态基本可以归为三类主机直装、Docker容器、Kubernetes集群。这三者的关系不是谁取代谁而是各自对应不同阶段的资源条件和运维能力。部署载体运行方式Agent扩展方式核心运维依赖典型使用场景物理机/虚拟机WAR包或系统安装包由系统服务托管SSH固定节点、Java Web StartJDK版本、系统环境变量、进程管理小团队、内网隔离环境、快速验证Docker容器docker run或docker composeDocker Cloud动态容器、固定容器Agent镜像拉取、数据卷、socket权限中小团队、服务器环境整洁有要求Kubernetes集群Deployment/StatefulSet或Helm ChartKubernetes插件动态创建PodPVC、RBAC、Service/Ingress微服务较多、并发波动大、已有K8s直装方式本质上是把Jenkins当一个常驻进程来养Docker方式把它包装成环境无关的镜像而Kubernetes方式则更进一步让Jenkins的控制器和Agent都变成可编排的实例。这个差异直接决定了后续集成方式中Agent的调配逻辑和资源隔离能力。1.2 部署方式与集成方式的联动关系很多人会把部署和集成当成两件独立的事其实它们是一条链上的两个环节。部署方式决定的是控制器的出口形态包括监听端口、对外URL、是否需要Ingress以及JENKINS_HOME数据放在哪个存储上。集成方式决定的是执行器的来源Agent到底是固定几台老服务器还是按队列动态拉起的容器又或者是随时创建销毁的Pod。举个实际场景你如果直接在个人电脑上跑了WAR包那后面接GitLab、配Webhook、让外部节点连接回来都会受本机网络形态的限制。反过来如果你用Kubernetes部署Jenkins那天然就能享受动态Pod带来的并发红利但也要承担Service、PVC、RBAC这一整套K8s概念的运维成本。所以选部署方式之前先想清楚三件事访问Jenkins的入口是哪里的构建任务要跑在什么环境里数据要持久化到哪里这三个问题的答案基本就框定了你的部署选型。2. 主机直装的排除法WAR包和系统包适合谁坑在哪里我最早接触Jenkins就是从WAR包开始的。那时候服务器数量少团队也就五六个人在虚拟机里直接跑Jenkins是最省事的选择。到今天主机直装依然有它的生存空间但你需要清楚它的边界在哪。2.1 从WAR包到系统服务最小可用路径主机直装有两种常见形式一种是直接下载jenkins.war用java -jar启动另一种是使用各发行版的软件源安装生成系统服务。前者的好处是版本控制非常直接想换版本就换文件适合快速验证后者的好处是系统接管了开机自启和日志。如果你用java -jar方式建议至少写一个systemd服务来托管而不是在终端里挂个前台进程。一个典型的unit文件长这样[Unit] DescriptionJenkins Server Afternetwork.target [Service] Userjenkins EnvironmentJENKINS_HOME/data/jenkins ExecStart/usr/bin/java -Xms512m -Xmx2048m -jar /opt/jenkins/jenkins.war --httpPort8080 Restartalways [Install] WantedBymulti-user.target这里有几个容易忽略的点User要单独建一个jenkins账号JENKINS_HOME明确指向独立数据目录Xmx根据机器内存来调别默认不管。我见过太多直接把Jenkins跑在root下的案例后来插件执行脚本或Agent连接时权限混乱排查起来特别痛苦。2.2 会被反复踩的环境变量与JENKINS_HOME问题主机直装模式下Jenkins和操作系统的交互最直接所以环境变量问题被放到了最大。首先是JENKINS_HOME。它默认在用户目录的.jenkins下但生产环境一定要把配置、构建记录、插件这些数据独立出去。原因很简单系统盘容易满重装系统会丢数据。我习惯把JENKINS_HOME挂到大容量磁盘路径下比如/data/jenkins然后通过环境变量或者启动参数固定住。其次是JAVA_HOME和PATH。Jenkins以系统服务运行时的环境变量和你SSH登录后看到的环境变量不是一回事。你明明在shell里配好了Maven、Docker、kubectl但Pipeline里执行命令时却提示找不到多半就是服务进程没继承你shell里配置的PATH。解决办法是把构建工具路径显式写进Jenkins系统设置里的全局属性中或者干脆在Pipeline脚本里用绝对路径。关于环境变量我把日常排查中用得最多的几个整理出来。变量名含义使用场景JENKINS_HOMEJenkins数据目录定位配置、构建记录、插件JENKINS_URL控制器对外访问地址Agent回连、Webhook回调BUILD_NUMBER当前构建序号制品版本号、部署标签BUILD_URL本次构建的完整URL在通知、制品中回链构建GIT_COMMIT代码提交ID回滚定位、镜像tagWORKSPACE本次构建的工作目录脚本里定位文件路径这些变量在Pipeline和系统脚本里都能直接用属于真正开箱即用的集成基础不管后续迁移到Docker还是K8s都不会变。2.3 汉化、插件安装与升级体验主机直装的汉化也一直是个槽点。Jenkins官方汉化包依赖Locale插件装完之后还要在管理Jenkins - 全局配置 - Locale里把语言设置为zh_US界面才会变成中文。很多人装完插件发现还是英文就是少了这一步。升级方面主机直装升级最方便但也最危险。直接把WAR包替换掉、重启服务就行了但对旧插件的兼容性不做检查的话经常会出现某个关键插件在新版本下失效的情况。我的原则是升级前备份JENKINS_HOME升级后先跑一遍核心流水线再观察插件报错日志。如果不是必须修复安全漏洞别在业务高峰期碰升级。主机直装适合什么人我的判断是团队规模小、服务器资源有限、对快速上手要求高、网络环境内网隔离。一旦并发构建上来了或者多语言多环境隔离要求出现直装方式的短板就会慢慢暴露。3. Docker化部署绕不开的镜像、数据卷与Docker握手Docker化部署现在已经是中小团队的主流选择。它把Jenkins运行时和宿主机隔离升级回滚都方便也顺带解决了一台机器上装多套JDK互相打架的脏乱差问题。但Docker部署真正的难点不在启动容器而在你要怎么让容器里的Jenkins和宿主机的Docker、目标服务器协作。3.1 基础部署与国内镜像源配置最基础的单容器启动命令是这样的docker run -d \ --name jenkins \ -p 8080:8080 \ -p 50000:50000 \ -v /data/jenkins:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ jenkins/jenkins:lts-jdk17这里有两个端口需要解释8080是Web入口50000是JNLP Agent用来回连控制器的端口。如果你后面要接固定Agent或云端Agent50000必须映射出去而且不能被防火墙挡住。拉镜像慢是很多人部署时遇到的第一个问题。这个问题和网络环境有关配置镜像加速器能明显改善拉取速度。做法是在Docker的daemon.json里添加registry-mirrors配置{ registry-mirrors: [https://你的加速器地址] }配置完重启Docker服务再拉jenkins/jenkins镜像就会快很多。这里顺便提一句镜像Tag的选择。现在官方镜像主要分lts-jdk11和lts-jdk17等标签新装环境我建议直接选lts-jdk17因为新版本插件对旧JDK的兼容性越来越差。别再用不带tag的latest你都不知道今晚重启后会变成什么版本。3.2 容器内执行Docker命令的两种方案这是Docker部署Jenkins下面最热门的问题热词里对应的就是jenkins容器内使用docker命令。常见做法有两种但很多人只知道第一种。第一种是Docker outside Docker也就是把宿主机的/var/run/docker.sock挂载进Jenkins容器。这样Jenkins容器里执行docker命令时实际上是在调宿主机上的Docker守护进程。version: 3.8 services: jenkins: image: jenkins/jenkins:lts-jdk17 container_name: jenkins ports: - 8080:8080 - 50000:50000 volumes: - jenkins_home:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock - /usr/bin/docker:/usr/bin/docker第二种是真正的Docker in Docker也就是在Jenkins容器内部再跑一个完整的Docker守护进程通常用官方提供的docker-in-docker镜像再加一层Jenkins镜像。这么做隔离性更好但映射关系复杂构建缓存也容易丢实际维护起来更累。我的实践经验是如果没有特殊安全要求DooD足够用但必须接受一个事实——构建产物和容器会直接在宿主机上留下痕迹。如果你介意这一点还是老老实实上K8s动态Pod别在Docker这一层死磕隔离性。两种方案的核心差异可以看这张表方案Docker命令执行者隔离程度构建缓存维护成本挂载socketDooD宿主机Docker守护进程弱共享宿主机保留在宿主机低容器内DinDJenkins容器内独立守护进程强独立环境容易随容器丢失高3.3 动态AgentDocker Cloud的Host URI与root细节Docker部署的另一个常见诉求是让Agent也动态化这就涉及到Jenkins里的Cloud配置。在管理Jenkins - 系统配置 - Clouds中新增一个Docker Cloud核心要填的就是Docker Host URI。配置Docker Cloud时我最常被问到的字段是Docker Host URI、Credentials和容器运行参数。热词里那个jenkins new cloud docker host uri root我拆解一下它基本对应的是下面这个操作路径进入Cloud配置后Docker Host URI可以填下面两种格式之一unix:///var/run/docker.sock用于Jenkins容器和Docker守护进程在同一台宿主机上tcp://192.168.x.x:2375或2376用于连接远程Docker主机。如果选择TCP方式一定不要裸奔2375端口。这个端口一旦暴露到公网等于把服务器的Docker控制权交给别人。我见过不止一次因为测试环境开着2375最后被拿去挖矿的真实案例。生产环境要么加TLS证书要么用内网防火墙限制来源IP。接着要处理的是以什么用户运行Agent容器。Docker Cloud创建的Agent容器默认很多是root用户Jenkins官方镜像里面默认用户是jenkins。如果你填了root意味着在Agent容器里执行任何命令都拥有最高权限这在某些需要装系统包的构建场景下很省事但安全审计的时候会非常难看。我的习惯是构建环境可控的用root方便一旦涉及多团队共享就换普通用户配合sudo规则做权限收敛。3.4 容器部署的权限与升级陷阱Docker部署最经典的一个坑是数据卷权限。jenkins/jenkins镜像里的jenkins用户UID是1000如果你宿主机上的/data/jenkins目录是root创建的容器启动时会因为写权限失败而初始化异常。解决办法很简单提前执行一条chown -R 1000:1000 /data/jenkins另一个坑出现在升级时。如果你直接docker pull最新镜像再重启容器插件目录里那些旧插件很可能因为Java版本或API变动直接罢工。我通常的做法是先备份jenkins_home再拉一个指定LTS版本镜像启动后立即进插件管理里做一次批量升级然后跑一条最小流水线验证。还有一个细节是容器内的时间同步。很多容器基础镜像默认不带tzdata构建日志里的时间显示UTC排查问题的时候总得换算时区。建议在docker run或compose里加上环境变量TZAsia/Shanghai顺手解决。4. Kubernetes部署与动态Pod规模化交付的配置要点如果你们团队的微服务多、并发构建波动大、而且已经有K8s集群那直接在Kubernetes里跑Jenkins控制器、用Kubernetes插件拉动态Agent是收益最明显的方案。4.1 为什么上K8s弹性Agent才是核心诉求很多人一听到K8s部署就紧张觉得复杂度高。但你要想清楚上K8s的真正动力不是把Jenkins控制器容器化那个用Docker一样能做到。核心动力是动态Agent。传统方式下你要么固定几台Jenkins Agent机器要么靠Docker Cloud临时起容器。前者的问题是并发高峰期CPU打满、平时又闲置后者的问题是要在一台Docker主机上堆积所有构建环境。而在K8s里每个构建任务都可以对应一个临时Pod任务结束Pod销毁资源真正按需分配。再加上Kubernetes本身有调度器和节点池构建任务可以很自然地分布到集群里不需要人为维护每一台Agent。4.2 2.541.3版本配置Kubernetes Cloud的关键字段我以Jenkins 2.541.3版本为例说下配置Kubernetes Cloud时几个关键字段怎么填。入口在管理Jenkins - System - Clouds - New Cloud选择Kubernetes。核心配置项如下Kubernetes URL集群API Server地址。如果Jenkins控制器也在集群内通常直接填https://kubernetes.default.svc.cluster.local这个地址走集群内部DNS比填外部IP稳定得多。Kubernetes server certificate需要上传API Server证书或者用Kubeconfig文件里的证书内容。测试环境如果图省事可以勾选跳过证书验证但生产环境别这么干。Credentials选Kubeconfig填入一个有权限创建Pod的ServiceAccount或用户凭证。注意权限范围能给到指定Namespace就行别给集群管理员。Namespace指定动态Pod创建在哪个命名空间建议单独建一个cicd。Jenkins URL这个非常关键Agent Pod要回连控制器必须能访问到它。控制器在集群内的一般填Service地址比如http://jenkins.cicd.svc.cluster.local:8080。如果从集群外访问就得填Ingress或者NodePort地址。Jenkins Tunnel如果Agent不想走50000端口可以指定控制器的hostname:port。有的团队把Agent连接单独走一个Service端口能有效避开负载均衡超时。保存之后记得点一下Test Connection我看到很多人配置完了不测试然后Agent一直连接不上绕了一大圈才发现是URL填错了。4.3 Helm部署与PVC、Service的联动配置控制器本身部署到K8s我更推荐用Helm而不是手写一堆YAML。Jenkins社区维护的Helm Chart已经比较成熟可以省掉不少重复配置工作。添加Chart仓库并安装helm repo add jenkinsci https://charts.jenkins.io helm repo update helm install jenkins jenkinsci/jenkins -n cicd --create-namespace \ --set controller.imageTag2.541.3-lts-jdk17 \ --set controller.persistence.size50Gi \ --set controller.serviceTypeClusterIP \ --set ingress.enabledfalse这里面有几个联动关系值得注意。persistence.size决定JENKINS_HOME的PVC大小这个值不要拍脑袋填先看看历史构建产物占多大留出1.5倍余量。serviceType默认是ClusterIP如果只有集群内访问就够用如果有外部Webhook、外部Agent要连进来就需要换成NodePort或Ingress。还有一个我没说但很容易踩的点如果你用StatefulSet模式或有多个副本JENKINS_HOME必须用ReadWriteMany的存储。单副本用ReadWriteOnce没问题想扩副本或者做控制器高可用存储不换是会直接卡死的。4.4 动态Agent的Pod模板设计配置完Cloud之后剩下的大头是Pod模板。Pod模板本质上定义了每次构建时动态创建的Agent长什么样。我的建议是一个Pod模板里放多个容器而不是一个Pod一个构建工具。比如前端构建需要Node后端构建需要Maven你可以定义一个叫build-base的Pod模板里面同时包含jnlp、node、maven三个容器。构建时通过容器名称选择在哪个容器里执行。Pod模板里注意这几点标签Labels给Pod模板打标签Pipeline里用agent { label build-base }关联别靠默认的任意标签否则调度到你自己的代码里容易乱。容器镜像拉取尽量走内网镜像仓库别每次都上外网拉既慢又有被限流的风险。资源限制在Pod模板里为容器设置requests和limits防止某个构建把整个节点内存吃满。环境变量可以在Pod模板里定义统一的环境变量比如MAVEN_OPTS、NODE_OPTIONS。我见过很多团队在Pod模板里只放一个jnlp容器构建工具链靠Agent容器手动装。这样做不是不行但每加一个工具就要改镜像重建非常繁。把常用工具拆成独立容器放在一个Pod模板里才是K8s Agent的正确姿势。5. 集成方式的对比与选型建议聊完三种部署形态最后把它们放在同一张横向对比表里下结论。注意我这里说的集成方式不光是部署载体还包含Agent的调配方式因为二者在实践中根本拆不开。5.1 一张表说清差异对比维度主机直装Docker部署Kubernetes部署部署复杂度低中高环境隔离性弱中强Agent弹性差靠固定节点中靠Docker Cloud强按需Pod资源利用率低中高升级回滚能力一般好好学习成本低中高迁移成本迁移JENKINS_HOME迁移数据卷迁移PVC配置典型团队规模5人以内5-30人30人以上或并发波动大这个表格里我最想强调的是Agent弹性这一行。固定节点的方式表面上省钱但一到发布日并发上来队列堆积比什么都急人。Docker Cloud能缓解一部分但一台Docker主机的资源始终有上限。只有K8s把构建任务打散到多个节点上才算真正摆脱单机瓶颈。5.2 按团队规模选型我按常见的三类团队规模给一套参考方案。5人以内、服务器一两台、业务单一的小团队直接主机直装或者docker run都行。这个阶段的核心目标是先把流水线跑通别在基础设施上投入太多精力。我建议选Docker因为后续迁移到集群时JENKINS_HOME数据卷可以整块带走比系统目录干净得多。5到30人、项目多、并发有波动的中等团队上docker-compose Docker Cloud动态Agent。Jenkins控制器保持在容器里Agent用Docker Cloud按队列拉起兼顾灵活性和运维复杂度。这一步要把构建工具链尽量镜像化减少人工干预。已经有稳定K8s集群的团队不要犹豫直接Helm部署控制器 Kubernetes插件动态Pod。控制器放在集群里用PVC保存数据Agent用Pod模板按需创建构建任务量再大也不会把某个节点打瘫。5.3 脚本拉代码与自动部署不管哪种方式都通用的集成姿势最后说一个几乎所有团队都会问到的点脚本怎么拉代码怎么实现自动部署。先纠正一个常见误区在Jenkins里拉代码能用SCM插件就用SCM插件别自己在shell里裸敲git clone。原因很简单SCM插件会替你处理凭据、分支和变更集还能自动触发多分支流水线。但有些场景确实需要手动拉比如你想在同一个工作空间里拉多个仓库这时候可以在Pipeline里这样写stage(拉取代码) { steps { git credentialsId: my-git-credentials, url: ssh://gitgitlab.example.com/group/project.git, branch: main } } stage(自动部署) { steps { sh scp target/app.jar deploy10.0.0.5:/opt/app/ ssh deploy10.0.0.5 systemctl restart app } }这段脚本里credentialsId对应Jenkins里保存的SSH密钥或账号密码部署动作通过SSH到目标服务器执行。如果你的目标环境是K8s把scp和systemctl换成kubectl set image或者helm upgrade就行。自动部署的关键在于把环境差异收敛到脚本之外。我习惯把目标主机地址、镜像仓库地址、部署目录这些信息提取为Jenkins的字符串参数或环境变量Pipeline里只引用变量。这样换环境时不用改脚本只改参数。一点个人体会看了这么多对比你可能会问到底哪种部署集成方式最正确我的答案是没有最正确只有最匹配你当前团队资源和运维能力的方案。从我自己的经验看Jenkins最怕被人当成宠物来养。所谓宠物就是所有人都不敢动它升级怕炸、迁移怕丢、扩容怕搞挂。正确的态度是把它当成可替换的零件——无论它今天跑在虚拟机里、容器里还是K8s集群里只要JENKINS_HOME数据和Jenkins URL这两个核心资产是清晰的随时都能一键重建一个同等能力的服务。最后分享一个被很多人忽略的小技巧不管选哪种部署方式先把JENKINS_HOME的备份和恢复流程在测试环境完整演练一遍。选型本身花不了多少时间真正让团队痛到彻骨的是数据丢失之后的抢救。把这条底线守住Jenkins从单机迁到Docker、再从Docker迁到K8s你会发现并没有想象中那么可怕。
返回列表