本文以 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 管理公网隧道

整体架构与端口规划

先更新系统并安装基础工具:

sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg git vim wget

二、从Docker官方软件源安装Docker Engine

Ubuntu软件仓库提供的docker.io可以用于简单测试,但为了方便后续升级并与Docker官方文档保持一致,本文使用Docker官方APT仓库安装Docker Engine和Compose插件。若服务器曾经安装过发行版提供的Docker软件包,先清理可能产生冲突的旧包:

for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
  sudo apt-get remove -y "$pkg" 2>/dev/null || true
done

添加Docker官方签名密钥和软件源:

sudo install -m 0755 -d /etc/apt/keyrings

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg   -o /etc/apt/keyrings/docker.asc

sudo chmod a+r /etc/apt/keyrings/docker.asc

echo   "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" |   sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

安装Docker Engine、Buildx和Compose插件:

sudo apt update

sudo apt install -y   docker-ce   docker-ce-cli   containerd.io   docker-buildx-plugin   docker-compose-plugin

sudo systemctl enable --now docker

为了让当前登录用户可以直接执行Docker命令,可以将其加入docker用户组:

sudo usermod -aG docker "$USER"
newgrp docker

验证安装结果:

docker --version
docker compose version
docker run --rm hello-world

需要注意的是,加入docker用户组相当于授予较高的宿主机权限,只应给可信任的运维和开发账号使用。

三、使用GitLab解决代码散落和版本混乱

从压缩包混乱到GitLab统一管理

小型团队最早经常通过压缩包传递代码,文件名可能逐渐变成“最终版”“最终版2”“最终版-今天修改”。成员增加后,这种方式很快就会出现代码相互覆盖、修改记录无法追踪和旧版本无法恢复的问题。GitLab的作用,是把项目代码、分支、提交记录和成员权限统一放入一套私有平台。

3.1 创建GitLab目录

sudo mkdir -p /srv/gitlab
sudo chown -R "$USER":"$USER" /srv/gitlab
cd /srv/gitlab

3.2 编写Docker Compose配置

创建compose.yml

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: unless-stopped
    hostname: gitlab.local

    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://192.168.1.50:8929'
        gitlab_rails['gitlab_shell_ssh_port'] = 2424

    ports:
      - "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版本标签,并在升级前备份configlogsdata目录。

GitLab部署配置示意

启动GitLab:

docker compose up -d
docker logs -f gitlab

GitLab第一次启动需要初始化数据库和内部组件,耗时通常明显长于普通容器。初始化完成后访问:

http://服务器局域网IP:8929

查看初始管理员密码:

docker exec -it gitlab   grep "Password:" /etc/gitlab/initial_root_password

默认管理员用户名为root。首次登录后应立即修改密码,并为团队建立独立Group、Project和成员账号,不要让所有成员共用管理员账号。初始密码文件不会永久保留,因此应在首次启动后及时完成登录。

3.3 创建并推送测试项目

在开发电脑上配置Git身份:

git config --global user.name "developer"
git config --global user.email "developer@example.com"

进入项目目录后执行:

git init
git add .
git commit -m "initial commit"
git branch -M main

git remote add origin   http://服务器IP:8929/团队名称/项目名称.git

git push -u origin main

GitLab页面中能够看到代码与提交记录后,说明私有仓库已经可以正常工作。代码管理问题解决后,下一步是把手动登录服务器发布程序的过程变成固定流水线。

四、准备一个安全的示例网站项目

为了验证Jenkins自动部署,可以准备一个简单的Nginx静态页面。项目目录如下:

team-demo/
├── index.html
├── Dockerfile
├── .dockerignore
└── Jenkinsfile

index.html示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="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负责把拉取代码、构建镜像和启动容器等重复操作固化为流水线。本文使用Jenkins官方Debian软件源安装LTS版本,并使用Java 21作为运行环境。

5.1 安装Java 21

sudo apt update
sudo apt install -y fontconfig openjdk-21-jre
java -version

5.2 添加Jenkins LTS软件源

sudo wget -O /etc/apt/keyrings/jenkins-keyring.asc   https://pkg.jenkins.io/debian-stable/jenkins.io-2026.key
echo "deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/" | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null

安装并启动Jenkins:

sudo apt update
sudo apt install -y jenkins

sudo systemctl enable --now jenkins
sudo systemctl status jenkins

浏览器访问:

http://服务器局域网IP:8080

查看初始化密码:

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

完成初始化后安装推荐插件,并确认已安装Git、Pipeline、GitLab和Credentials Binding等常用插件。

5.3 让Jenkins能够调用Docker

sudo usermod -aG docker jenkins
sudo systemctl restart jenkins

验证权限:

sudo -u jenkins docker ps

如果仍然提示权限不足,可以重启服务器后再次测试。让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 '构建失败,请检查控制台日志'
        }
    }
}

Jenkins安装与流水线配置示意

提交项目文件:

git add .
git commit -m "add Jenkins pipeline"
git push

在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 Webhook自动触发Jenkins

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后推送代码:

git add .
git commit -m "update homepage"
git push

GitLab应自动通知Jenkins,Jenkins随后拉取最新代码、构建新镜像并替换示例容器。

八、使用Prometheus与Node Exporter监控服务器

Prometheus与Node Exporter服务器监控实景

本文的监控范围是Linux服务器的CPU、内存、磁盘和网络等基础状态,并不等同于完整的应用可用性监控。若要进一步确认8088端口的网页是否能正常访问,可以继续增加Blackbox Exporter;若要观察Docker容器资源,可以增加cAdvisor。

8.1 创建配置文件

sudo mkdir -p /srv/monitor
sudo chown -R "$USER":"$USER" /srv/monitor
cd /srv/monitor

创建prometheus.yml

global:
  scrape_interval: 15s

scrape_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:latest
    container_name: prometheus
    restart: unless-stopped
    network_mode: host

    volumes:
      - "./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:latest
    container_name: node-exporter
    restart: unless-stopped
    network_mode: host
    pid: host

    command:
      - "--path.rootfs=/host"

    volumes:
      - "/:/host:ro,rslave"

volumes:
  prometheus-data:

启动并检查状态:

docker compose up -d
docker ps

访问:

http://服务器局域网IP:9090
http://服务器局域网IP:9100/metrics

在Prometheus的Status → Targets页面中,Prometheus和Node Exporter目标都应显示UP

Prometheus与Node Exporter配置示意

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问题

通过cpolar在外部访问团队工具

当GitLab、Jenkins和Prometheus都能在局域网中运行后,最后一个问题是异地成员无法访问。cpolar可以为本地HTTP或TCP服务建立公网入口,使成员在公司外部访问代码仓库、部署平台和监控页面,同时也能为Jenkins提供稳定的公网Webhook地址。

9.1 安装cpolar

sudo curl https://get.cpolar.sh | sh

检查并启动服务:

cpolar version
sudo systemctl enable --now cpolar
sudo systemctl 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 临时或受保护地址 远程排查服务器状态

cpolar隧道与端口规划

固定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/gitlab
docker compose 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首次启动会初始化多个内部组件。先查看日志:

docker logs -f gitlab

再检查内存和容器状态:

free -h
docker ps -a

如果服务器只有8GB内存,建议配置Swap,并尽量避免同时运行大量Jenkins构建任务。

Jenkins无法执行Docker命令

groups jenkins
sudo -u jenkins docker ps

jenkins不在docker组中,重新添加并重启服务:

sudo usermod -aG docker jenkins
sudo systemctl restart jenkins

Jenkins无法拉取私有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-tutorial

  • Jenkins自动部署与cpolar公网触发:
    https://www.cpolar.com/blog/jenkins-automatic-deployment-in-practice-combining-cpolar-to-achieve-public-network-triggering

  • Prometheus、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-it

  • Node Exporter与cpolar远程监控:
    https://www.cpolar.com/blog/no-public-ip-address-required-remote-monitoring-of-server-status-is-achieved-by-using-node_exporter-and-cpolar

  • cpolar安装与配置文档:
    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/

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐