小型开发团队这样工作才最爽:代码管理、自动部署、服务器监控、远程运维一套工作流全搞定
本文以 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 Web8929代码仓库与项目管理GitLab SSH2424SSH方式拉取与推送代码Jenkins8080自动构建与部署示例网站8088验证自动部署结果Prometheus9090服务器指标查询Node Exporter9100采集Linux主机指标cpolar Web UI9200管理公网隧道先更新系统并安装基础工具sudoaptupdatesudoaptupgrade-ysudoaptinstall-yca-certificatescurlgnupggitvimwget二、从Docker官方软件源安装Docker EngineUbuntu软件仓库提供的docker.io可以用于简单测试但为了方便后续升级并与Docker官方文档保持一致本文使用Docker官方APT仓库安装Docker Engine和Compose插件。若服务器曾经安装过发行版提供的Docker软件包先清理可能产生冲突的旧包forpkgindocker.io docker-docdocker-composedocker-compose-v2 podman-docker containerd runc;dosudoapt-getremove-y$pkg2/dev/null||truedone添加Docker官方签名密钥和软件源sudoinstall-m0755-d/etc/apt/keyringssudocurl-fsSLhttps://download.docker.com/linux/ubuntu/gpg-o/etc/apt/keyrings/docker.ascsudochmodar /etc/apt/keyrings/docker.ascechodeb [arch$(dpkg --print-architecture)signed-by/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu$(./etc/os-releaseecho${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$USERnewgrpdocker验证安装结果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.ymlservices: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:22volumes:-./config:/etc/gitlab-./logs:/var/log/gitlab-./data:/var/opt/gitlabshm_size:256m将192.168.1.50替换为服务器自己的局域网IP。示例使用latest便于实验正式环境建议在验证后固定明确的GitLab版本标签并在升级前备份config、logs和data目录。启动GitLabdockercompose up-ddockerlogs-fgitlabGitLab第一次启动需要初始化数据库和内部组件耗时通常明显长于普通容器。初始化完成后访问http://服务器局域网IP:8929查看初始管理员密码dockerexec-itgitlabgrepPassword:/etc/gitlab/initial_root_password默认管理员用户名为root。首次登录后应立即修改密码并为团队建立独立Group、Project和成员账号不要让所有成员共用管理员账号。初始密码文件不会永久保留因此应在首次启动后及时完成登录。3.3 创建并推送测试项目在开发电脑上配置Git身份gitconfig--globaluser.namedevelopergitconfig--globaluser.emaildeveloperexample.com进入项目目录后执行gitinitgitadd.gitcommit-minitial commitgitbranch-Mmaingitremoteaddorigin http://服务器IP:8929/团队名称/项目名称.gitgitpush-uorigin mainGitLab页面中能够看到代码与提交记录后说明私有仓库已经可以正常工作。代码管理问题解决后下一步是把手动登录服务器发布程序的过程变成固定流水线。四、准备一个安全的示例网站项目为了验证Jenkins自动部署可以准备一个简单的Nginx静态页面。项目目录如下team-demo/ ├── index.html ├── Dockerfile ├── .dockerignore └── Jenkinsfileindex.html示例!DOCTYPEhtmlhtmllangzh-CNheadmetacharsetUTF-8title团队自动部署测试/title/headbodyh1代码已经通过 Jenkins 自动部署成功/h1/body/htmlDockerfile只复制真正需要发布的网页文件避免将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 21sudoaptupdatesudoaptinstall-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.keyechodeb [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安装并启动Jenkinssudoaptupdatesudoaptinstall-yjenkinssudosystemctlenable--nowjenkinssudosystemctl status jenkins浏览器访问http://服务器局域网IP:8080查看初始化密码sudocat/var/lib/jenkins/secrets/initialAdminPassword完成初始化后安装推荐插件并确认已安装Git、Pipeline、GitLab和Credentials Binding等常用插件。5.3 让Jenkins能够调用Dockersudousermod-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{shdocker 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-madd Jenkins pipelinegitpush在Jenkins中新建“流水线”任务流水线定义选择Pipeline script from SCMSCM选择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-mupdate homepagegitpushGitLab应自动通知JenkinsJenkins随后拉取最新代码、构建新镜像并替换示例容器。八、使用Prometheus与Node Exporter监控服务器本文的监控范围是Linux服务器的CPU、内存、磁盘和网络等基础状态并不等同于完整的应用可用性监控。若要进一步确认8088端口的网页是否能正常访问可以继续增加Blackbox Exporter若要观察Docker容器资源可以增加cAdvisor。8.1 创建配置文件sudomkdir-p/srv/monitorsudochown-R$USER:$USER/srv/monitorcd/srv/monitor创建prometheus.ymlglobal:scrape_interval:15sscrape_configs:-job_name:prometheusstatic_configs:-targets:[127.0.0.1:9090]-job_name:node-exporterstatic_configs:-targets:[127.0.0.1:9100]8.2 使用宿主机网络运行监控组件创建compose.ymlservices:prometheus:image:prom/prometheus:latestcontainer_name:prometheusrestart:unless-stoppednetwork_mode:hostvolumes:-./prometheus.yml:/etc/prometheus/prometheus.yml:ro-prometheus-data:/prometheuscommand:---config.file/etc/prometheus/prometheus.yml---storage.tsdb.path/prometheusnode-exporter:image:quay.io/prometheus/node-exporter:latestcontainer_name:node-exporterrestart:unless-stoppednetwork_mode:hostpid:hostcommand:---path.rootfs/hostvolumes:-/:/host:ro,rslavevolumes: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{modeidle}[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 安装cpolarsudocurlhttps://get.cpolar.sh|sh检查并启动服务cpolar versionsudosystemctlenable--nowcpolarsudosystemctl status cpolar浏览器访问http://服务器局域网IP:9200使用cpolar账号登录Web UI。9.2 建议创建的隧道隧道协议本地地址建议公网类型用途GitLab WebHTTP8929固定二级子域名远程访问代码仓库GitLab SSHTCP2424固定TCP地址SSH方式拉取和推送JenkinsHTTP8080固定二级子域名管理平台与WebhookPrometheusHTTP9090临时或受保护地址远程排查服务器状态固定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在公网侧提供HTTPSGitLab容器内部仍通过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并推送到GitLabGitLab收到Push事件后通过Webhook触发JenkinsJenkins自动拉取代码、构建镜像并替换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事件能够自动触发JenkinsPrometheus中的目标均显示UPCPU、内存和磁盘使用率查询正常cpolar固定地址能够稳定访问GitLab和JenkinsPrometheus公网访问已增加保护或只在排查时临时开启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 Ubuntuhttps://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 Exporterhttps://prometheus.io/docs/guides/node-exporter/https://prometheus.io/docs/guides/basic-auth/