本文以 Ubuntu 24.04 LTS 为基础环境,使用 GitLab、Jenkins、Docker、Prometheus、Node Exporter 与 cpolar,搭建一套适合小型开发团队的代码管理、自动部署、服务器监控和远程运维工作流。
本版采用“实景场景图 + 技术信息图”交替排版:实景图展示痛点和使用结果,技术图解释部署结构与关键配置。涉及命令与参数时请以正文为准。
一个五六人的小型开发团队,项目不多时通常不会投入太多精力建设流程。代码可以通过聊天软件传递,测试版本打包后放进共享文件夹,程序上线时由某位开发人员登录服务器手动替换文件。只要团队成员彼此熟悉,这种方式看起来简单、直接,也不会立即暴露出太多问题。
当项目数量、成员数量和发布频率逐渐增加后,原来的办法就会开始失效。不同开发人员手里保存着不同版本的代码,测试环境与正式环境经常不一致;每次发布都需要重复登录服务器、复制文件和重启服务,任何一步遗漏都可能造成线上异常;程序上线后又缺少统一监控,往往要等到客户反馈页面打不开,团队才知道服务器早已出现问题。即使代码仓库、部署平台和监控页面都搭建好了,成员出差或居家办公时仍可能因为局域网限制而无法使用。
本文将这几个问题串成一条完整工作流:GitLab负责代码和版本管理,Jenkins负责自动构建与部署,Docker负责承载示例应用,Prometheus与Node Exporter负责服务器基础监控,最后使用cpolar解决异地访问和公网Webhook触发问题。流程跑通后,开发人员只需提交代码,后续构建、部署、监控和远程处理便能沿着固定路径完成。
一、部署前先规划服务器资源与端口
将GitLab、Jenkins、Docker构建任务和Prometheus部署在同一台服务器上时,测试环境最低可以从4核CPU、8GB内存起步,但内存会比较紧张,建议同时配置Swap。准备长期运行时,更适合使用4—8核CPU、16GB内存和120GB以上SSD空间。GitLab仓库、Docker镜像、Jenkins构建记录和Prometheus时序数据都会持续增长,因此磁盘容量不宜只按照刚安装时的占用量估算。
本文采用以下端口规划。正式部署前应确认这些端口没有被其他程序占用,并根据实际网络环境调整服务器防火墙。
| 服务 | 本地端口 | 作用 |
|---|---|---|
| GitLab Web | 8929 | 代码仓库与项目管理 |
| GitLab SSH | 2424 | SSH方式拉取与推送代码 |
| Jenkins | 8080 | 自动构建与部署 |
| 示例网站 | 8088 | 验证自动部署结果 |
| Prometheus | 9090 | 服务器指标查询 |
| Node Exporter | 9100 | 采集Linux主机指标 |
| cpolar Web UI | 9200 | 管理公网隧道 |
先更新系统并安装基础工具:
sudoaptupdatesudoaptupgrade-ysudoaptinstall-yca-certificatescurlgnupggitvimwget二、从Docker官方软件源安装Docker Engine
Ubuntu软件仓库提供的docker.io可以用于简单测试,但为了方便后续升级并与Docker官方文档保持一致,本文使用Docker官方APT仓库安装Docker Engine和Compose插件。若服务器曾经安装过发行版提供的Docker软件包,先清理可能产生冲突的旧包:
forpkgindocker.io docker-docdocker-composedocker-compose-v2 podman-docker containerd runc;dosudoapt-getremove-y"$pkg"2>/dev/null||truedone添加Docker官方签名密钥和软件源:
sudoinstall-m0755-d/etc/apt/keyringssudocurl-fsSLhttps://download.docker.com/linux/ubuntu/gpg-o/etc/apt/keyrings/docker.ascsudochmoda+r /etc/apt/keyrings/docker.ascecho"deb [arch=$(dpkg --print-architecture)signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu$(./etc/os-release&&echo"${UBUNTU_CODENAME:-$VERSION_CODENAME}")stable"|sudotee/etc/apt/sources.list.d/docker.list>/dev/null安装Docker Engine、Buildx和Compose插件:
sudoaptupdatesudoaptinstall-ydocker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginsudosystemctlenable--nowdocker为了让当前登录用户可以直接执行Docker命令,可以将其加入docker用户组:
sudousermod-aGdocker"$USER"newgrpdocker验证安装结果:
docker--versiondockercompose versiondockerrun--rmhello-world需要注意的是,加入docker用户组相当于授予较高的宿主机权限,只应给可信任的运维和开发账号使用。
三、使用GitLab解决代码散落和版本混乱
小型团队最早经常通过压缩包传递代码,文件名可能逐渐变成“最终版”“最终版2”“最终版-今天修改”。成员增加后,这种方式很快就会出现代码相互覆盖、修改记录无法追踪和旧版本无法恢复的问题。GitLab的作用,是把项目代码、分支、提交记录和成员权限统一放入一套私有平台。
3.1 创建GitLab目录
sudomkdir-p/srv/gitlabsudochown-R"$USER":"$USER"/srv/gitlabcd/srv/gitlab3.2 编写Docker Compose配置
创建compose.yml:
services:gitlab:image:gitlab/gitlab-ce:latestcontainer_name:gitlabrestart:unless-stoppedhostname:gitlab.localenvironment:GITLAB_OMNIBUS_CONFIG:|external_url 'http://192.168.1.50:8929' gitlab_rails['gitlab_shell_ssh_port'] = 2424ports:-"8929:8929"-"2424:22"volumes:-"./config:/etc/gitlab"-"./logs:/var/log/gitlab"-"./data:/var/opt/gitlab"shm_size:"256m"将192.168.1.50替换为服务器自己的局域网IP。示例使用latest便于实验,正式环境建议在验证后固定明确的GitLab版本标签,并在升级前备份config、logs和data目录。
启动GitLab:
dockercompose up-ddockerlogs-fgitlabGitLab第一次启动需要初始化数据库和内部组件,耗时通常明显长于普通容器。初始化完成后访问:
http://服务器局域网IP:8929查看初始管理员密码:
dockerexec-itgitlabgrep"Password:"/etc/gitlab/initial_root_password默认管理员用户名为root。首次登录后应立即修改密码,并为团队建立独立Group、Project和成员账号,不要让所有成员共用管理员账号。初始密码文件不会永久保留,因此应在首次启动后及时完成登录。
3.3 创建并推送测试项目
在开发电脑上配置Git身份:
gitconfig--globaluser.name"developer"gitconfig--globaluser.email"developer@example.com"进入项目目录后执行:
gitinitgitadd.gitcommit-m"initial commit"gitbranch-Mmaingitremoteaddorigin http://服务器IP:8929/团队名称/项目名称.gitgitpush-uorigin mainGitLab页面中能够看到代码与提交记录后,说明私有仓库已经可以正常工作。代码管理问题解决后,下一步是把手动登录服务器发布程序的过程变成固定流水线。
四、准备一个安全的示例网站项目
为了验证Jenkins自动部署,可以准备一个简单的Nginx静态页面。项目目录如下:
team-demo/ ├── index.html ├── Dockerfile ├── .dockerignore └── Jenkinsfileindex.html示例:
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>团队自动部署测试</title></head><body><h1>代码已经通过 Jenkins 自动部署成功</h1></body></html>Dockerfile只复制真正需要发布的网页文件,避免将Git仓库元数据和流水线配置暴露到Nginx目录:
FROM nginx:alpine COPY index.html /usr/share/nginx/html/index.html创建.dockerignore:
.git .gitignore Jenkinsfile Dockerfile五、安装Jenkins并赋予构建权限
Jenkins负责把拉取代码、构建镜像和启动容器等重复操作固化为流水线。本文使用Jenkins官方Debian软件源安装LTS版本,并使用Java 21作为运行环境。
5.1 安装Java 21
sudoaptupdatesudoaptinstall-yfontconfig openjdk-21-jrejava-version5.2 添加Jenkins LTS软件源
sudowget-O/etc/apt/keyrings/jenkins-keyring.asc https://pkg.jenkins.io/debian-stable/jenkins.io-2026.keyecho"deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/"|sudotee/etc/apt/sources.list.d/jenkins.list>/dev/null安装并启动Jenkins:
sudoaptupdatesudoaptinstall-yjenkinssudosystemctlenable--nowjenkinssudosystemctl status jenkins浏览器访问:
http://服务器局域网IP:8080查看初始化密码:
sudocat/var/lib/jenkins/secrets/initialAdminPassword完成初始化后安装推荐插件,并确认已安装Git、Pipeline、GitLab和Credentials Binding等常用插件。
5.3 让Jenkins能够调用Docker
sudousermod-aGdockerjenkinssudosystemctl restart jenkins验证权限:
sudo-ujenkinsdockerps如果仍然提示权限不足,可以重启服务器后再次测试。让Jenkins访问Docker套接字意味着其拥有较高宿主机权限,因此正式生产环境更适合使用独立构建节点、受限Runner或隔离构建环境。
六、编写不会重复检出代码的Jenkins流水线
Declarative Pipeline默认可能自动检出一次代码,如果流水线中又显式执行checkout scm,就会产生重复操作。下面通过skipDefaultCheckout(true)关闭默认检出,同时使用disableConcurrentBuilds()避免两个构建同时争抢同一个容器名称和端口。
项目根目录中的Jenkinsfile如下:
pipeline{agent any options{skipDefaultCheckout(true)disableConcurrentBuilds()timestamps()}stages{stage('拉取代码'){steps{checkout scm}}stage('构建镜像'){steps{sh'docker build --pull -t team-demo:${BUILD_NUMBER} .'}}stage('部署应用'){steps{sh''' docker rm -f team-demo || true docker run -d --name team-demo --restart unless-stopped -p 8088:80 team-demo:${BUILD_NUMBER} '''}}}post{success{echo'项目构建与部署成功'}failure{echo'构建失败,请检查控制台日志'}}}提交项目文件:
gitadd.gitcommit-m"add Jenkins pipeline"gitpush在Jenkins中新建“流水线”任务,流水线定义选择Pipeline script from SCM,SCM选择Git并填写GitLab项目地址。私有仓库应在Jenkins凭据管理中保存GitLab用户名与Personal Access Token,然后在任务中选择对应凭据。
分支填写:
*/main脚本路径填写:
Jenkinsfile保存后点击“立即构建”。构建成功后访问:
http://服务器局域网IP:8088如果页面显示测试内容,说明代码拉取、镜像构建和容器部署已经跑通。
七、配置GitLab Webhook自动触发Jenkins
手动点击“立即构建”只能证明流水线可用,真正顺畅的流程应当由代码提交自动触发。安装GitLab插件后,在Jenkins任务的“构建触发器”中勾选GitLab Push事件触发选项,并记录页面生成的Webhook URL和Secret Token。
GitLab与Jenkins处在同一局域网时,可以先使用:
http://服务器局域网IP:8080/project/team-demo进入GitLab项目的Settings → Webhooks,填写URL与Secret Token并勾选Push Events。如果自建GitLab默认阻止Webhook访问局域网地址,需要用管理员账号进入:
Admin Area → Settings → Network → Outbound requests开启允许Webhook和集成访问本地网络的选项。
如果GitLab位于外部网络,或者需要长期从公网稳定触发Jenkins,则应在cpolar中为Jenkins保留固定HTTP地址,并将该地址配置为Webhook URL。随机地址发生变化后,GitLab中的Webhook也必须同步修改,因此不适合长期自动化流程。
修改index.html后推送代码:
gitadd.gitcommit-m"update homepage"gitpushGitLab应自动通知Jenkins,Jenkins随后拉取最新代码、构建新镜像并替换示例容器。
八、使用Prometheus与Node Exporter监控服务器
本文的监控范围是Linux服务器的CPU、内存、磁盘和网络等基础状态,并不等同于完整的应用可用性监控。若要进一步确认8088端口的网页是否能正常访问,可以继续增加Blackbox Exporter;若要观察Docker容器资源,可以增加cAdvisor。
8.1 创建配置文件
sudomkdir-p/srv/monitorsudochown-R"$USER":"$USER"/srv/monitorcd/srv/monitor创建prometheus.yml:
global:scrape_interval:15sscrape_configs:-job_name:"prometheus"static_configs:-targets:["127.0.0.1:9090"]-job_name:"node-exporter"static_configs:-targets:["127.0.0.1:9100"]8.2 使用宿主机网络运行监控组件
创建compose.yml:
services:prometheus:image:prom/prometheus:latestcontainer_name:prometheusrestart:unless-stoppednetwork_mode:hostvolumes:-"./prometheus.yml:/etc/prometheus/prometheus.yml:ro"-"prometheus-data:/prometheus"command:-"--config.file=/etc/prometheus/prometheus.yml"-"--storage.tsdb.path=/prometheus"node-exporter:image:quay.io/prometheus/node-exporter:latestcontainer_name:node-exporterrestart:unless-stoppednetwork_mode:hostpid:hostcommand:-"--path.rootfs=/host"volumes:-"/:/host:ro,rslave"volumes:prometheus-data:启动并检查状态:
dockercompose up-ddockerps访问:
http://服务器局域网IP:9090 http://服务器局域网IP:9100/metrics在Prometheus的Status → Targets页面中,Prometheus和Node Exporter目标都应显示UP。
8.3 使用更直观的PromQL查询
目标在线状态:
up整机CPU使用率:
100 * ( 1 - avg by (instance) ( rate(node_cpu_seconds_total{mode="idle"}[5m]) ) )内存使用率:
100 * ( 1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes )根目录磁盘使用率:
100 * ( 1 - node_filesystem_avail_bytes{ mountpoint="/", fstype!~"tmpfs|overlay" } / node_filesystem_size_bytes{ mountpoint="/", fstype!~"tmpfs|overlay" } )九、使用cpolar解决公网访问与远程Webhook问题
当GitLab、Jenkins和Prometheus都能在局域网中运行后,最后一个问题是异地成员无法访问。cpolar可以为本地HTTP或TCP服务建立公网入口,使成员在公司外部访问代码仓库、部署平台和监控页面,同时也能为Jenkins提供稳定的公网Webhook地址。
9.1 安装cpolar
sudocurlhttps://get.cpolar.sh|sh检查并启动服务:
cpolar versionsudosystemctlenable--nowcpolarsudosystemctl status cpolar浏览器访问:
http://服务器局域网IP:9200使用cpolar账号登录Web UI。
9.2 建议创建的隧道
| 隧道 | 协议 | 本地地址 | 建议公网类型 | 用途 |
|---|---|---|---|---|
| GitLab Web | HTTP | 8929 | 固定二级子域名 | 远程访问代码仓库 |
| GitLab SSH | TCP | 2424 | 固定TCP地址 | SSH方式拉取和推送 |
| Jenkins | HTTP | 8080 | 固定二级子域名 | 管理平台与Webhook |
| Prometheus | HTTP | 9090 | 临时或受保护地址 | 远程排查服务器状态 |
固定HTTP二级子域名和固定TCP地址涉及相应套餐,免费套餐提供的随机地址可能发生变化。Jenkins Webhook需要长期稳定地址,因此更适合使用固定二级子域名。
9.3 GitLab公网地址的两种使用方式
如果只是临时从外网查看GitLab页面,可以保留原来的局域网external_url,直接把8929端口映射到公网,但GitLab页面显示的部分克隆链接仍可能是局域网地址。
如果准备把固定cpolar地址作为GitLab主要入口,可以将GitLab配置改为:
environment:GITLAB_OMNIBUS_CONFIG:|external_url 'https://你的固定子域名.cpolar.top' nginx['listen_port'] = 8929 nginx['listen_https'] = false gitlab_rails['gitlab_shell_ssh_port'] = 2424更新后重新创建容器:
cd/srv/gitlabdockercompose up-d这里由cpolar在公网侧提供HTTPS,GitLab容器内部仍通过HTTP监听8929端口。若需要公网SSH克隆,还应创建TCP隧道,并根据cpolar提供的公网主机和端口连接。
9.4 Jenkins公网Webhook
为Jenkins创建固定HTTP隧道后,在Jenkins中进入:
Manage Jenkins → System → Jenkins Location将Jenkins URL修改为固定公网地址,再把新的Webhook地址填写到GitLab项目中并执行测试Push。
9.5 Prometheus公网安全
Prometheus默认不适合长期匿名暴露在公网。更安全的做法包括增加Basic Auth反向代理、设置访问白名单,或者只在临时排查问题时开启隧道。Jenkins同样应关闭匿名权限,并使用明确的认证与授权策略。
十、完整流程测试
完成配置后,可以按照以下顺序进行验收:开发人员在本地修改index.html并推送到GitLab;GitLab收到Push事件后,通过Webhook触发Jenkins;Jenkins自动拉取代码、构建镜像并替换8088端口上的示例容器;随后在Prometheus中确认Node Exporter仍然处于UP状态,并检查CPU、内存与磁盘指标;最后断开公司Wi-Fi,使用手机流量访问GitLab、Jenkins和经过保护的Prometheus公网地址。
当这条链路能够连续跑通时,团队的工作方式便从“群里传代码、手动登录发布、客户反馈后排查”,变成了“提交代码、自动构建、自动部署、持续监控、随时远程处理”。
十一、常见问题
GitLab启动很久仍无法访问
GitLab首次启动会初始化多个内部组件。先查看日志:
dockerlogs-fgitlab再检查内存和容器状态:
free-hdockerps-a如果服务器只有8GB内存,建议配置Swap,并尽量避免同时运行大量Jenkins构建任务。
Jenkins无法执行Docker命令
groupsjenkinssudo-ujenkinsdockerps若jenkins不在docker组中,重新添加并重启服务:
sudousermod-aGdockerjenkinssudosystemctl restart jenkinsJenkins无法拉取私有GitLab项目
先在服务器终端使用相同仓库地址和凭据执行一次git clone,确认网络、用户名和Personal Access Token正常,再回到Jenkins检查Credentials与分支配置。
GitLab Webhook测试失败
同一局域网内使用私有地址时,应检查GitLab是否允许Webhook访问本地网络;跨网络或外部GitLab触发时,应检查cpolar隧道是否在线、地址是否变化,以及Jenkins任务是否开启对应的Push触发器。
Prometheus中的Node Exporter显示DOWN
先访问:
http://服务器IP:9100/metrics如果无法打开,检查Node Exporter容器和9100端口;如果页面可打开,再检查prometheus.yml中的目标地址,以及Prometheus容器是否确实使用了宿主机网络。
十二、部署验收清单
- GitLab能够正常登录、创建Group和Project并推送代码;
- 团队成员使用独立账号,不共用
root管理员; - Jenkins能够从GitLab拉取私有项目;
- Jenkins流水线不会重复检出代码;
- 同一任务不会发生并发部署冲突;
- Docker镜像只包含需要发布的网页文件;
- GitLab Push事件能够自动触发Jenkins;
- Prometheus中的目标均显示
UP; - CPU、内存和磁盘使用率查询正常;
- cpolar固定地址能够稳定访问GitLab和Jenkins;
- Prometheus公网访问已增加保护,或只在排查时临时开启;
- GitLab、Jenkins与Prometheus数据均纳入备份计划。
十三、真正的爽点,是开发人员只需要专心提交代码
这套工作流真正带来的变化,并不是服务器上多安装了几个软件,而是每个环节终于有了明确职责。GitLab负责代码、版本和权限,Jenkins负责把构建与发布动作自动执行,Prometheus与Node Exporter负责持续观察服务器状态,cpolar则让团队在公司之外仍然能够访问这些工具。
以前一次上线,需要先在群里确认谁手中的代码最新,再由某个人打包、登录服务器、替换文件并重启服务;程序出现问题后,团队还要临时连接服务器逐项排查。现在开发人员完成修改后只需提交代码,系统就会沿着既定流程完成构建和部署;服务器是否健康,可以通过监控指标快速确认;即使成员不在办公室,仍然能够进入GitLab、Jenkins和监控平台处理问题。
对于小型开发团队来说,这才是自动化工作流最实际的价值:它不一定让每一项工作完全无人参与,却能把最容易出错、不断重复的操作变成稳定流程,让团队把更多时间用于开发产品,而不是反复处理版本、发布和远程访问问题。
完整教程可参考
CentOS 7环境下的GitLab部署与公网访问参考:
https://www.cpolar.com/blog/centos7-private-gitlab-cpolar-internal-network-penetration-to-achieve-public-network-access-tutorialJenkins自动部署与cpolar公网触发:
https://www.cpolar.com/blog/jenkins-automatic-deployment-in-practice-combining-cpolar-to-achieve-public-network-triggeringPrometheus、Node Exporter与Alertmanager监控告警:
https://www.cpolar.com/blog/say-goodbye-to-downtime-build-a-server-monitoring-and-alarm-system-from-scratch-even-beginners-can-learn-itNode Exporter与cpolar远程监控:
https://www.cpolar.com/blog/no-public-ip-address-required-remote-monitoring-of-server-status-is-achieved-by-using-node_exporter-and-cpolarcpolar安装与配置文档:
https://www.cpolar.com/docs
技术校对依据
Docker Engine on Ubuntu:
https://docs.docker.com/engine/install/ubuntu/GitLab Docker安装与配置:
https://docs.gitlab.com/install/docker/installation/
https://docs.gitlab.com/install/docker/configuration/Jenkins Linux安装:
https://www.jenkins.io/doc/book/installing/linux/Prometheus与Node Exporter:
https://prometheus.io/docs/guides/node-exporter/
https://prometheus.io/docs/guides/basic-auth/