🚀 GitLab + CI/CD 自动化部署完全指南
适用场景:Win11 电脑 + VMware 虚拟机 + Ubuntu Server 24.04,从零搭建 GitLab 并实现 Java + Vue 全栈项目的自动化 CI/CD 部署。
阅读提示:本文档为"傻瓜式"教程,请严格按照顺序执行,遇到报错先看"排错大全"。如果看不懂,不是你的问题,是我写得还不够傻瓜!😄
📑 导航目录
- 🎯 写在前面:为什么要折腾这个?
- 📦 环境准备
- 🖥️ 第一步:Ubuntu 虚拟机初始化
- 🐳 第二步:安装 Docker
- 🦊 第三步:部署 GitLab
- 🏃 第四步:注册 GitLab Runner
- 🌐 第五步:安装并配置 Nginx
- ☕ 第六步:配置 Java 后端服务
- 📝 第七步:
.gitlab-ci.yml配置完全指南 - 💻 第八步:Win11 客户端配置
- 🎉 第九步:实战部署(完整示例)
- 🐛 第十步:排错大全(30+ 常见问题)
- 📋 附录:运维命令速查表
🎯 写在前面:为什么要折腾这个?
想象一下这个场景:
你写了一段代码,提交到 Git,然后——
- 手动打开服务器
- 手动拉代码
- 手动编译 Java
- 手动构建 Vue
- 手动复制到 Nginx 目录
- 手动重启服务
- 发现报错了,回滚,再来一遍…
累不累? 😫
而有了 CI/CD(持续集成/持续部署),流程变成:
你写了一段代码,
git push→ 自动编译 → 自动测试 → 自动部署 → 浏览器刷新,新功能上线了!🎉
这就是本文档要带你实现的目标。 跟着做,保证你能搭起来。如果搭不起来……来,先看看是不是哪一步偷偷跳过了!👀
📦 环境准备
硬件要求
| 资源 | 最低要求 | 推荐配置 | 备注 |
|---|---|---|---|
| CPU | 2 核 | 4 核 | GitLab 很能吃 |
| 内存 | 4 GB | 8 GB | 低于 4G 启动极慢 |
| 磁盘 | 40 GB | 80 GB | GitLab 初始化就要几十 G |
| 网络 | 能上网 | 能上网 | 需要拉镜像 |
⚠️ 血泪教训:磁盘一定要给够!GitLab 首次初始化就像个饕餮,空间不够直接罢工给你看。
软件清单
| 软件 | 版本 | 作用 |
|---|---|---|
| Win11 | 任意 | 你的主力开发机 |
| VMware Workstation | 最新版 | 跑 Ubuntu 虚拟机 |
| Ubuntu Server | 24.04 LTS | 服务器系统 |
| GitLab CE | latest | 代码仓库 + CI/CD 平台 |
| Docker | docker.io | 容器化部署 |
| Nginx | 最新版 | 反向代理 + 静态文件服务 |
网络架构
┌─────────────────────────────────────────────────────────────┐
│ Win11 开发机 │
│ ┌──────────────┐ git push ┌──────────────────────┐ │
│ │ VS Code │ ─────────────→ │ GitLab Web (8080) │ │
│ │ Git Bash │ │ 查看流水线状态 │ │
│ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ 桥接模式(同局域网)
▼
┌─────────────────────────────────────────────────────────────┐
│ Ubuntu Server 24.04 (VMware 虚拟机) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ GitLab CE │ │ Nginx │ │ GitLab Runner │ │
│ │ (Docker) │ │ (80端口) │ │ (Docker + Shell) │ │
│ │ 8080:80 │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Java 后端 │ │ Vue 前端 │ │
│ │ 8081端口 │ │ /var/www │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
🖥️ 第一步:Ubuntu 虚拟机初始化
1.1 查看 IP 地址
开机后,在虚拟机控制台登录,执行:
ip addr show
找到你的网卡(通常是 ens33 或 eth0)下面的 inet,例如:
inet 172.19.90.78/20
📝 拿出小本本,记下这个 IP!后面全部用它代替。
1.2 用 SSH 连接(强烈推荐!)
在 Win11 的 CMD / PowerShell 中执行:
ssh 你的用户名@你的IP
# 例如:ssh duke@172.19.90.78
第一次连接会提示:
Are you sure you want to continue connecting (yes/no/[fingerprint])?
⚠️ 注意!这里必须输入完整的 yes,按回车。
输入
Y?不行!
输入y?也不行!
输入yes?对了!SSH 就是这么傲娇。😤
然后输入密码登录。
💡 为什么强烈建议用 SSH?
- VMware 控制台里复制粘贴不生效(Ubuntu Server 没有图形界面)
- SSH 窗口可以正常复制粘贴,命令直接贴,爽歪歪
- 字体可以调大,保护视力
1.3 更新系统
sudo apt update && sudo apt upgrade -y
这就像给系统打补丁,虽然无聊,但必须做。
1.4 安装常用工具
sudo apt install -y curl wget git vim net-tools ca-certificates gnupg
1.5 设置主机名(可选,但推荐)
sudo hostnamectl set-hostname gitlab-server
给服务器起个名字,以后登录提示符显示 gitlab-server,比一串数字亲切多了。
🐳 第二步:安装 Docker
2.1 安装 docker.io(Ubuntu 官方源,最稳)
不要折腾 Docker 官方仓库,Ubuntu 自带的 docker.io 完全够用。那些配置 GPG 密钥、添加源列表的教程,看着就头大,咱不整那些虚的:
# 更新包索引
sudo apt update
# 安装 Docker(就一行,简单粗暴)
sudo apt install -y docker.io
# 启动 Docker 服务
sudo systemctl start docker
sudo systemctl enable docker
# 将当前用户加入 docker 组(以后不用 sudo 也能跑 docker)
sudo usermod -aG docker $USER
newgrp docker
# 验证安装
docker --version
⚠️ 执行
newgrp docker后,如果还是提示docker: permission denied,请重新 SSH 连接一次。别问为什么,问就是 Linux 的权限缓存机制在搞事情。
2.2 安装 Docker Compose
Ubuntu 24.04 安装的是 docker-compose-v2,命令格式是 docker compose(中间空格,不是横杠):
sudo apt install -y docker-compose
验证:
docker compose version
如果你习惯敲
docker-compose,系统会温柔地告诉你:unknown command。记住:空格,空格,空格!
2.3 配置国内镜像加速器(如果拉取镜像慢)
国内网络你懂的,Docker Hub 有时候像蜗牛爬。配置加速器,让镜像飞起来:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.1panel.live",
"https://hub.rat.dev",
"https://docker.m.daocloud.io"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
这些镜像站是"公益节点",可能会变动。如果某天失效了,网上搜"Docker 国内镜像"换一批就行。
🦊 第三步:部署 GitLab
3.1 创建数据目录
GitLab 是个"数据大户",需要三个目录分别存配置、日志和数据:
sudo mkdir -p /srv/gitlab/{config,logs,data}
sudo mkdir -p /srv/gitlab-runner/config
sudo chmod 777 /srv/gitlab/{config,logs,data}
sudo chmod 777 /srv/gitlab-runner/config
chmod 777意思是"所有人都能读写执行"。生产环境不建议这么奔放,但咱们是本地测试,先让权限不成为拦路虎。
3.2 创建 docker-compose.yml
不要用 vim!不要用 vim!不要用 vim!
新手用 vim 容易进去出不来(按 Esc 然后 :q! 可以强制退出,但何必呢)。用 tee 命令直接写入,优雅又安全:
sudo tee /srv/gitlab/docker-compose.yml << 'EOF'
version: '3.8'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
container_name: gitlab
restart: always
hostname: 'gitlab-server'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://172.19.90.78:8080'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
nginx['listen_https'] = false
nginx['redirect_http_to_https'] = false
nginx['listen_port'] = 80
ports:
- '8080:80'
- '8443:443'
- '2222:22'
volumes:
- '/srv/gitlab/config:/etc/gitlab'
- '/srv/gitlab/logs:/var/log/gitlab'
- '/srv/gitlab/data:/var/opt/gitlab'
shm_size: '256m'
gitlab-runner:
image: 'gitlab/gitlab-runner:latest'
container_name: gitlab-runner
restart: always
volumes:
- '/srv/gitlab-runner/config:/etc/gitlab-runner'
- '/var/run/docker.sock:/var/run/docker.sock'
depends_on:
- gitlab
EOF
⚠️ 超级重点:
nginx['listen_port'] = 80必须写!如果不写,GitLab 内部的 Nginx 会误以为要监听 8080,但 Puma(Rails 内置服务器)默认也监听 8080。两个程序抢一个端口,结果就是——同归于尽,容器状态显示
unhealthy。⚠️ IP 地址:把
172.19.90.78换成你虚拟机的实际 IP!
3.3 启动 GitLab
cd /srv/gitlab
docker compose up -d
首次启动会拉取镜像并初始化数据库,需要 5~15 分钟。这段时间你可以:
- 泡杯咖啡 ☕
- 刷会儿手机 📱
- 盯着终端看日志(推荐,有种看着孩子出生的感觉)
docker logs -f gitlab
当看到日志不再频繁滚动,或出现 gitlab Reconfigured!,按 Ctrl+C 退出日志查看。
3.4 获取 root 初始密码
docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
输出示例:
Password: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
⚠️ 这个文件会在 24 小时后自动删除!拿到密码后立刻登录修改,否则 24 小时后你就得重置密码了(虽然也能重置,但麻烦啊)。
3.5 浏览器登录
在 Win11 浏览器访问:
http://你的IP:8080
# 例如:http://172.19.90.78:8080
- 用户名:
root - 密码:上一步获取的初始密码
登录后,点击右上角 root 头像 → Edit profile → Password,设置一个你能记住的新密码。
3.6 验证 GitLab 服务
# 检查容器状态
docker ps
# 本地测试端口
curl -I http://localhost:8080
预期返回 HTTP/1.1 302 Found 或 200 OK。
🏃 第四步:注册 GitLab Runner
Runner 是 CI/CD 的"打工仔",负责执行 .gitlab-ci.yml 里的任务。我们需要两种 Runner:
| Runner 类型 | 作用 | 执行环境 |
|---|---|---|
| Docker Runner | 编译代码(Maven、npm) | Docker 容器内 |
| Shell Runner | 部署到宿主机(复制文件、重启服务) | 直接在 Ubuntu 上 |
为什么需要两个?因为 Docker Runner 运行在容器里,默认看不到宿主机的
/var/www和/opt/backend。让 Docker Runner 去部署,就像让鱼缸里的鱼去浇花——有心无力。
4.1 注册 Docker Runner
获取 Token
在 GitLab 网页:
- 点击左上角 Menu → Admin
- 左侧 CI/CD → Runners
- 点击右上角 New instance runner → Create runner
- 复制 Token(格式如
glrt-xxxxxxxxxx)
执行注册
docker exec -it gitlab-runner gitlab-runner register
按提示输入:
| 提示 | 输入 |
|---|---|
| GitLab instance URL | http://你的IP:8080 |
| Registration token | 粘贴你复制的 Token |
| Description | my-docker-runner |
| Tags | docker |
| Maintenance note | 直接回车 |
| Executor | docker |
| Default Docker image | ubuntu:24.04 |
验证
docker exec -it gitlab-runner gitlab-runner verify
在 GitLab 网页 Admin → Runners 中,应该看到绿色的 Online 状态。🟢
4.2 安装并注册 Shell Runner
Shell Runner 需要直接安装在宿主机上(不是 Docker 里):
# 添加 GitLab Runner 官方仓库
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
# 安装 Runner
sudo apt install -y gitlab-runner
# 启动并设置开机自启
sudo systemctl enable gitlab-runner
sudo systemctl start gitlab-runner
获取项目级 Token
这次要从项目级别获取 Token(不是 Admin 级别):
- 在 GitLab 网页进入你的项目(如
hello-cicd) - Settings → CI/CD → Runners → New project runner
- 复制 Token
💡 为什么用 Project Runner 而不是 Instance Runner?
Instance Runner 是全局的,所有项目都能用,但默认项目看不到它。Project Runner 是项目专属的,注册后立刻出现在项目里,省心!
执行注册
sudo gitlab-runner register
按提示输入:
| 提示 | 输入 |
|---|---|
| URL | http://你的IP:8080 |
| Token | 粘贴项目级 Token |
| Description | deploy-shell |
| Tags | shell |
| Executor | shell |
验证
sudo gitlab-runner list
sudo gitlab-runner verify
在 GitLab 网页 项目 → Settings → CI/CD → Runners 中,应该能看到 deploy-shell 显示为绿色 Online。
🌐 第五步:安装并配置 Nginx
5.1 安装 Nginx
sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
5.2 创建前端部署目录
sudo mkdir -p /var/www/frontend
sudo chown -R www-data:www-data /var/www/frontend
sudo chmod 775 /var/www/frontend
5.3 配置 Nginx 站点
单页应用(Vue/React)专用配置,包含后端 API 反向代理:
# 禁用默认站点,避免冲突(默认站点的欢迎页会抢戏)
sudo rm -f /etc/nginx/sites-enabled/default
# 创建新站点配置
sudo tee /etc/nginx/sites-available/fullstack << 'EOF'
server {
listen 80;
server_name _;
# Vue 前端(所有请求先找静态文件,找不到回退到 index.html)
location / {
root /var/www/frontend;
index index.html;
try_files $uri $uri/ /index.html;
}
# Java 后端 API 代理(解决跨域问题)
location /api/ {
proxy_pass http://127.0.0.1:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
EOF
# 启用站点
sudo ln -sf /etc/nginx/sites-available/fullstack /etc/nginx/sites-enabled/
# 测试配置并重启
sudo nginx -t
sudo systemctl reload nginx
💡
try_files $uri $uri/ /index.html;是 Vue 单页应用的救命稻草!Vue 是前端路由(URL 变化但不请求新页面),直接刷新
/about时,Nginx 会去找/about/index.html,当然找不到,返回 404。try_files的意思是:“找不到文件?别慌,回退到index.html,让 Vue 自己处理路由。”
☕ 第六步:配置 Java 后端服务
6.1 创建后端部署目录
sudo mkdir -p /opt/backend
sudo chown -R gitlab-runner:gitlab-runner /opt/backend
6.2 创建 systemd 服务文件
systemd 是 Ubuntu 的"服务管家",让 Java 程序像 Nginx 一样开机自启、崩溃自动重启:
sudo tee /etc/systemd/system/backend.service << 'EOF'
[Unit]
Description=Java Backend
After=network.target
[Service]
Type=simple
User=gitlab-runner
WorkingDirectory=/opt/backend
ExecStart=/usr/bin/java -jar /opt/backend/app.jar --server.port=8081
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
6.3 给 Shell Runner sudo 权限
Shell Runner 默认用户是 gitlab-runner,执行 systemctl 需要 sudo。但咱们不能给它全部权限(安全第一),只给必要的命令:
sudo tee /etc/sudoers.d/gitlab-runner << 'EOF'
gitlab-runner ALL=(ALL) NOPASSWD: /bin/rm, /bin/cp, /bin/mkdir, /bin/systemctl stop backend, /bin/systemctl start backend, /bin/systemctl status backend, /bin/systemctl daemon-reload, /bin/systemctl restart backend
EOF
sudo chmod 440 /etc/sudoers.d/gitlab-runner
sudo visudo -c
⚠️ 只给必要的命令权限,不要给
ALL!gitlab-runner要是能sudo rm -rf /,那画面太美不敢看。
📝 第七步:.gitlab-ci.yml 配置完全指南
7.1 这是什么?
.gitlab-ci.yml 是 CI/CD 的"剧本",告诉 GitLab:
“代码推送后,先干嘛,再干嘛,最后干嘛。”
必须放在 Git 仓库的根目录,文件名必须是 .gitlab-ci.yml(注意前面有个点)。
my-project/ ← 项目根目录
├── .gitlab-ci.yml ← 放这里!
├── backend/
├── frontend/
└── README.md
⚠️ 文件名写错(如
gitlab-ci.yml少了点)、放错位置(如放在backend/里),流水线都不会触发。
7.2 基础结构速览
stages: # ← 定义阶段顺序
- build
- test
- deploy
build_job: # ← Job 名字,随便取
stage: build # ← 属于哪个阶段
tags: # ← 用哪个 Runner 执行
- docker
image: node:20 # ← 用什么 Docker 镜像
script: # ← 真正执行的命令
- echo "开始构建"
- npm install
- npm run build
执行顺序:
build 阶段的所有 Job(并行)
↓
test 阶段的所有 Job(并行)
↓
deploy 阶段的所有 Job(并行)
7.3 核心字段详解
stages — 定义流水线阶段
stages:
- build-backend
- build-frontend
- test
- deploy
同一阶段的 Job 并行执行,不同阶段 串行执行。
💡 技巧:把耗时的操作放在前面并行执行,比如前后端同时构建,省时间。
tags — 指定 Runner(关键!)
build_job:
tags:
- docker # ← 只有标签含 docker 的 Runner 会执行
| 场景 | tags 设置 |
|---|---|
| Maven 编译 Java | tags: [docker] |
| npm 构建 Vue | tags: [docker] |
| 复制文件到 Nginx 目录 | tags: [shell] |
| 执行 systemctl 重启服务 | tags: [shell] |
⚠️ 如果 tags 不匹配,Job 会永远 pending(橙色),不会执行。 就像你叫了个外卖但地址写错了,骑手永远找不到你。
image — Docker 镜像
build_job:
image: maven:3.9-eclipse-temurin-21-alpine
| 技术栈 | 推荐镜像 |
|---|---|
| Java + Maven | maven:3.9-eclipse-temurin-21-alpine |
| Java + Gradle | gradle:8.5-jdk21-alpine |
| Vue/React | node:20-alpine |
| Python | python:3.11-alpine |
| Go | golang:1.22-alpine |
💡 带
alpine的版本体积小(约 5MB 基础),拉取快,省时间。
script — 执行命令列表
script:
- echo "开始构建"
- cd backend
- mvn clean package -DskipTests
- ls -la target/
每行是一个独立的 shell 命令,按顺序执行。如果某一行报错,后面的命令不会执行。
多行脚本写法(更整洁):
script:
- |
echo "======== 开始构建 ========"
cd backend
mvn clean package -DskipTests
echo "======== 构建完成 ========"
artifacts — 产物传递(跨 Job 共享文件)
build_job:
script:
- mvn package
artifacts:
paths:
- backend/target/*.jar # ← 把 jar 包保存下来
expire_in: 1 week # ← 1 周后自动删除
为什么需要 artifacts?
build_job 在 Docker 容器里编译出 jar 包,容器一销毁文件就没了。deploy_job 需要这个 jar 包才能部署。
artifacts 的作用:把文件"暂存"起来,让后面的 Job 能下载使用。
cache — 缓存加速
build_job:
cache:
paths:
- backend/.m2/repository # ← Maven 依赖缓存
- frontend/node_modules/ # ← npm 依赖缓存
key: "$CI_COMMIT_REF_SLUG" # ← 按分支隔离缓存
artifacts vs cache 区别:
artifacts:同一条流水线的不同 Job 之间传递文件(如 build → deploy)cache:跨流水线复用文件(如下次构建直接用上次下载的依赖)
dependencies — 指定依赖哪个 Job 的产物
deploy_job:
dependencies:
- build-backend # ← 只拿 build-backend 的 artifacts
rules / only — 触发条件
旧写法(不推荐新项目用):
deploy_job:
only:
- main # ← 只有推送到 main 分支才触发
新写法(功能更强,推荐):
deploy_job:
rules:
- if: '$CI_COMMIT_BRANCH == "main"' # main 分支才部署
when: always
- if: '$CI_COMMIT_BRANCH == "develop"' # develop 分支只构建不部署
when: never
- when: manual # 其他分支手动触发
when 值 | 含义 |
|---|---|
on_success | 前面阶段成功才执行(默认) |
on_failure | 前面阶段失败才执行(用于报错通知) |
always | 无论成败都执行(用于清理) |
manual | 手动触发(用于部署到生产环境) |
生产环境手动审批示例:
deploy-prod:
stage: deploy
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual # ← 必须人工点击"执行"按钮
script:
- echo "✅ 生产环境已确认上线!"
needs — 跳过阶段,直接依赖(DAG 流水线)
默认流水线是阶段顺序执行,用 needs 可以让前后端并行部署:
deploy-backend:
stage: deploy
needs: [build-backend] # ← 只要 build-backend 完成就开始
dependencies: [build-backend]
deploy-frontend:
stage: deploy
needs: [build-frontend] # ← 只要 build-frontend 完成就开始
dependencies: [build-frontend]
environment — 环境配置(部署追踪)
deploy_job:
environment:
name: production
url: http://172.19.90.78
效果:GitLab 网页会显示 “Deployed to production” 按钮,点击直接跳转到部署地址。
7.4 实战配置模板(Java + Vue 全栈)
stages:
- build-backend
- build-frontend
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
# ========== 构建 Java 后端 ==========
build-backend:
stage: build-backend
tags: [docker]
image: maven:3.9-eclipse-temurin-21-alpine
cache:
paths: [backend/.m2/repository]
key: "mvn-$CI_COMMIT_REF_SLUG"
script:
- cd backend
- mvn -s settings.xml clean package -DskipTests
- ls -la target/
artifacts:
paths: [backend/target/*.jar]
expire_in: 1 day
# ========== 构建 Vue 前端 ==========
build-frontend:
stage: build-frontend
tags: [docker]
image: node:20-alpine
cache:
paths: [frontend/node_modules/]
key: "npm-$CI_COMMIT_REF_SLUG"
script:
- cd frontend
- npm config set registry https://registry.npmmirror.com
- npm install
- npm run build
- ls -la dist/
artifacts:
paths: [frontend/dist/]
expire_in: 1 day
# ========== 部署后端 ==========
deploy-backend:
stage: deploy
tags: [shell]
needs: [build-backend]
dependencies: [build-backend]
script:
- sudo systemctl stop backend || true
- cp backend/target/*.jar /opt/backend/app.jar
- sudo systemctl start backend
- sleep 5
- sudo systemctl status backend
# ========== 部署前端 ==========
deploy-frontend:
stage: deploy
tags: [shell]
needs: [build-frontend]
dependencies: [build-frontend]
script:
- rm -rf /var/www/frontend/*
- cp -r frontend/dist/* /var/www/frontend/
- ls -la /var/www/frontend/
7.5 多环境部署模板(测试自动 + 生产手动)
stages:
- build
- deploy-staging
- deploy-prod
build:
stage: build
tags: [docker]
image: maven:3.9-eclipse-temurin-21-alpine
script:
- mvn clean package -DskipTests
artifacts:
paths: [target/*.jar]
# 测试环境:自动部署
deploy-staging:
stage: deploy-staging
tags: [shell]
dependencies: [build]
environment:
name: staging
url: http://172.19.90.78:8082
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
script:
- cp target/*.jar /opt/backend-staging/app.jar
- sudo systemctl restart backend-staging
# 生产环境:手动审批
deploy-prod:
stage: deploy-prod
tags: [shell]
dependencies: [build]
environment:
name: production
url: http://172.19.90.78
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual # ← 必须人工点击"执行"
script:
- cp target/*.jar /opt/backend/app.jar
- sudo systemctl restart backend
7.6 常用内置变量
| 变量 | 示例值 | 说明 |
|---|---|---|
$CI_COMMIT_BRANCH | main | 当前分支名 |
$CI_COMMIT_SHA | a1b2c3d... | 当前 commit 完整哈希 |
$CI_COMMIT_SHORT_SHA | a1b2c3d | 当前 commit 短哈希 |
$CI_PROJECT_NAME | my-project | 项目名 |
$CI_JOB_NAME | build_job | 当前 Job 名 |
$CI_REGISTRY | 172.19.90.78:8080 | GitLab 镜像仓库地址 |
💻 第八步:Win11 客户端配置
8.1 安装 Git
访问 https://git-scm.com/download/win 下载安装,一路默认即可。
验证:
git --version
8.2 配置 Git 身份
git config --global user.email "duke@example.com"
git config --global user.name "Duke"
不配置的话,Git 会傲娇地报错:
fatal: unable to auto-detect email address
8.3 配置 SSH 密钥
ssh-keygen -t ed25519 -C "duke@gitlab"
一路回车。然后复制公钥:
type %USERPROFILE%\.ssh\id_ed25519.pub
复制输出的内容,在 GitLab 网页:
- 点击右上角 root 头像 → Preferences → SSH Keys
- 粘贴公钥,Title 填
Win11-PC - 点击 Add key
如果
git push提示Permission denied (publickey),99% 是这步没做,或者公钥复制错了。
🎉 第九步:实战部署(完整示例)
9.1 在 GitLab 创建项目
- 点击 Create new project → Create blank project
- Project name:
hello-cicd - Visibility:
Public - 点击 Create project
9.2 克隆项目到本地
cd D:\demo\gitlab
git clone ssh://git@你的IP:2222/root/hello-cicd.git
cd hello-cicd
9.3 创建示例文件
创建 index.html:
<!DOCTYPE html>
<html>
<head>
<title>Hello GitLab CI/CD</title>
</head>
<body>
<h1>🎉 部署成功!</h1>
<p>Build Time: BUILD_TIME_PLACEHOLDER</p>
</body>
</html>
创建 .gitlab-ci.yml:
stages:
- build
- deploy
build_job:
stage: build
tags:
- docker
image: node:20-alpine
script:
- echo "开始构建..."
- mkdir -p dist
- cp index.html dist/index.html
- sed -i "s/BUILD_TIME_PLACEHOLDER/$(date)/g" dist/index.html
- echo "构建完成!"
artifacts:
paths:
- dist/
deploy_job:
stage: deploy
tags:
- shell # 关键:指定 Shell Runner
script:
- echo "清理旧文件..."
- rm -rf /var/www/frontend/*
- echo "复制新文件到 Nginx..."
- cp -r dist/* /var/www/frontend/
- echo "✅ 部署完成!"
- ls -la /var/www/frontend/
dependencies:
- build_job
only:
- main
- master
9.4 推送代码触发流水线
git add .
git commit -m "init: add CI/CD pipeline"
git push origin main
推送后,在 GitLab 网页进入项目 → CI/CD → Pipelines,可以看到流水线正在运行。
等两个 Job 都变绿 ✅ 后,在 Win11 浏览器访问:
http://你的IP
# 例如:http://172.19.90.78
你应该能看到 “🎉 部署成功!” 的页面,而且 Build Time 是刚才构建的时间。
以后每次改代码 → git push → 自动构建 → 自动部署到 Nginx! 🚀
☁️ 第十步:部署到云端(阿里云 / 腾讯云 / AWS)
前置要求:你已经跟着前面的步骤,在本地 VMware 上成功跑通了 GitLab + CI/CD。
现在你想把这套玩意儿搬到云上,让全世界(或者至少你的团队)都能访问。恭喜你,从"单机版玩家"进阶为"联网版玩家"了!🌐
10.1 本地 vs 云端:有啥不一样?
| 对比项 | 本地 VMware | 云端服务器 |
|---|---|---|
| 网络模式 | NAT / 桥接,折腾死人 | 公网 IP,开箱即用 |
| 访问方式 | 只有局域网能访问 | 全球任何地方都能访问 |
| 防火墙 | ufw 命令搞定 | 安全组(云端特有,坑很多) |
| 域名 | 直接用 IP | 推荐绑定域名 + SSL |
| 备案 | 不需要 | 国内服务器必须备案(否则 80/443 被封) |
| 磁盘扩容 | 关虚拟机 → 改设置 → 扩分区 | 云控制台点几下,重启即可 |
| 数据备份 | 自己搞 | 云快照 / 云盘备份,一键恢复 |
| 成本 | 电费 + 电脑折旧 | 按月付费,约 50~200 元/月 |
💡 一句话总结:云端部署的核心流程和本地几乎一样,差异主要在网络安全和域名备案。
10.2 云服务器选型推荐
GitLab 是个"资源饕餮",云服务器配置不能抠门:
| 用途 | CPU | 内存 | 磁盘 | 带宽 | 月租参考 |
|---|---|---|---|---|---|
| 个人学习 / 小团队 | 2 核 | 4 GB | 80 GB SSD | 3 Mbps | ~50 元 |
| 推荐配置 | 4 核 | 8 GB | 100 GB SSD | 5 Mbps | ~100 元 |
| 团队正式使用 | 4 核 | 16 GB | 200 GB SSD | 10 Mbps | ~200 元 |
平台选择:
- 阿里云:生态完善,文档齐全,学生有优惠
- 腾讯云:性价比高,新用户折扣大
- 华为云:政企首选,稳定性好
- AWS / Azure:国际业务首选,国内访问略慢
⚠️ 避坑指南:
- 不要买"突发性能实例"(T5/T6),CPU 性能被限制,GitLab 启动慢到怀疑人生
- 一定要选 SSD 云盘,机械硬盘跑 GitLab 就是灾难
- 带宽建议 3Mbps 起步,低于 1Mbps 拉镜像能急死人
10.3 购买和初始化云服务器
步骤 1:购买服务器
以阿里云为例:
- 登录 阿里云控制台
- 点击 创建实例
- 地域:选离你近的(如华东 1 杭州)
- 镜像:选择 Ubuntu 24.04 LTS 64位
- 实例规格:至少 2 核 4G(推荐 4 核 8G)
- 存储:系统盘 80GB+,建议加一块数据盘 100GB(放 GitLab 数据)
- 带宽:按固定带宽 3~5 Mbps
- 安全组:先选默认的,后面再改
- 登录凭证:设置 root 密码 或上传 SSH 公钥
- 确认订单,付款,等待 1~2 分钟创建完成
步骤 2:获取公网 IP
创建完成后,在 ECS 控制台找到你的实例,记录:
- 公网 IP:如
47.102.123.45 - 私有 IP:如
172.17.0.1(后面配置用不到,但知道一下)
📝 拿出小本本,记下公网 IP!后面全部用它代替。
步骤 3:SSH 连接云服务器
在 Win11 CMD / PowerShell 中:
ssh root@你的公网IP
# 例如:ssh root@47.102.123.45
第一次连接同样要输入 yes(完整的,不是 Y)。
10.4 配置安全组(云端特有,重点!)
安全组是云端的"数字防火墙",默认只开放 22 端口(SSH)。如果不配置,你访问 GitLab 会被无情拒绝。
阿里云安全组配置
- 进入 ECS 控制台 → 网络与安全 → 安全组
- 找到你的实例关联的安全组,点击 配置规则
- 点击 入方向 → 手动添加,添加以下规则:
| 类型 | 端口范围 | 授权对象 | 说明 |
|---|---|---|---|
| SSH | 22/22 | 0.0.0.0/0 | 远程连接 |
| HTTP | 80/80 | 0.0.0.0/0 | Nginx 前端 |
| 自定义 TCP | 8080/8080 | 0.0.0.0/0 | GitLab Web |
| 自定义 TCP | 8081/8081 | 0.0.0.0/0 | Java 后端 |
| 自定义 TCP | 2222/2222 | 0.0.0.0/0 | GitLab SSH |
| HTTPS | 443/443 | 0.0.0.0/0 | SSL(后面配) |
⚠️ 0.0.0.0/0 表示允许任何 IP 访问。学习阶段可以这么设,生产环境建议限制为公司 IP 段。
腾讯云安全组配置
类似,进入 CVM 控制台 → 安全组 → 入站规则,添加上述端口。
验证端口是否通
# 在云服务器上启动一个临时服务测试
python3 -m http.server 8080 &
然后在 Win11 浏览器访问 http://你的公网IP:8080,如果能看到目录列表,说明安全组配置成功。
# 测试完关掉临时服务
kill %1
10.5 数据盘挂载(推荐)
如果你购买时加了一块数据盘(如 100GB),需要挂载到 /srv/gitlab 目录,这样即使系统盘出问题,GitLab 数据还在。
# 查看磁盘
lsblk
# 假设数据盘是 /dev/vdb(阿里云通常是 vdb,腾讯云是 vdc)
# 格式化(首次使用,会清空数据!)
sudo mkfs.ext4 /dev/vdb
# 创建挂载点
sudo mkdir -p /srv/gitlab
# 挂载
sudo mount /dev/vdb /srv/gitlab
# 设置开机自动挂载
sudo tee -a /etc/fstab << 'EOF'
/dev/vdb /srv/gitlab ext4 defaults 0 0
EOF
# 验证
df -h | grep /srv/gitlab
💡 把 GitLab 数据放在独立云盘上,就像把鸡蛋放在不同篮子里,系统盘崩了数据还在。
10.6 部署 GitLab(云端版)
与本地部署的差异点
云端部署的核心流程和本地 几乎一样,只有几个地方需要改:
- IP 地址:换成公网 IP
- external_url:建议用域名(后面讲)
- 安全组:已经配好了(见 10.4)
- 防火墙:云服务器通常没开 ufw,不用管
创建 docker-compose.yml(云端版)
sudo mkdir -p /srv/gitlab/{config,logs,data}
sudo mkdir -p /srv/gitlab-runner/config
sudo chmod 777 /srv/gitlab/{config,logs,data}
sudo chmod 777 /srv/gitlab-runner/config
sudo tee /srv/gitlab/docker-compose.yml << 'EOF'
version: '3.8'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
container_name: gitlab
restart: always
hostname: 'gitlab-server'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://47.102.123.45:8080'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
nginx['listen_https'] = false
nginx['redirect_http_to_https'] = false
nginx['listen_port'] = 80
ports:
- '8080:80'
- '8443:443'
- '2222:22'
volumes:
- '/srv/gitlab/config:/etc/gitlab'
- '/srv/gitlab/logs:/var/log/gitlab'
- '/srv/gitlab/data:/var/opt/gitlab'
shm_size: '256m'
gitlab-runner:
image: 'gitlab/gitlab-runner:latest'
container_name: gitlab-runner
restart: always
volumes:
- '/srv/gitlab-runner/config:/etc/gitlab-runner'
- '/var/run/docker.sock:/var/run/docker.sock'
depends_on:
- gitlab
EOF
⚠️ 把
47.102.123.45换成你的真实公网 IP!
启动 GitLab
cd /srv/gitlab
docker compose up -d
同样等待 5~15 分钟初始化。期间可以:
docker logs -f gitlab
获取 root 密码
docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
浏览器访问
http://你的公网IP:8080
# 例如:http://47.102.123.45:8080
如果能看到 GitLab 登录页,恭喜你,云端 GitLab 部署成功!🎉
10.7 域名绑定 + 备案(国内服务器必须)
为什么要域名?
- IP 地址难记,域名好记(如
git.yourcompany.com) - 配置 SSL 证书需要域名
- 看起来更专业(毕竟
47.102.123.45:8080像黑客攻击目标)
国内备案流程
如果你用的是国内云服务器(阿里云、腾讯云等),必须备案才能使用 80/443 端口:
- 购买域名(阿里云万网、腾讯云 DNSPod,约 30~70 元/年)
- 实名认证(域名持有者身份证照片)
- ICP 备案(在云平台提交备案申请)
- 填写网站信息、负责人信息
- 上传身份证、手持身份证照片
- 阿里云/腾讯云初审(1~2 天)
- 管局审核(7~20 天,不同省份速度不同)
- 备案通过后,80/443 端口才能正常访问
⚠️ 备案期间,网站不能访问,或者只能显示"网站建设中"。建议备案完成后再正式使用。
域名解析配置
备案通过后:
- 进入域名管理控制台(如阿里云 DNS 解析)
- 添加 A 记录:
- 主机记录:
git(表示git.yourdomain.com) - 记录值:你的公网 IP(如
47.102.123.45)
- 主机记录:
- 等待 5~10 分钟生效(DNS 传播)
修改 GitLab 使用域名
sudo tee /srv/gitlab/docker-compose.yml << 'EOF'
version: '3.8'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
container_name: gitlab
restart: always
hostname: 'git.yourdomain.com'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://git.yourdomain.com:8080'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
nginx['listen_https'] = false
nginx['redirect_http_to_https'] = false
nginx['listen_port'] = 80
ports:
- '8080:80'
- '8443:443'
- '2222:22'
volumes:
- '/srv/gitlab/config:/etc/gitlab'
- '/srv/gitlab/logs:/var/log/gitlab'
- '/srv/gitlab/data:/var/opt/gitlab'
shm_size: '256m'
gitlab-runner:
image: 'gitlab/gitlab-runner:latest'
container_name: gitlab-runner
restart: always
volumes:
- '/srv/gitlab-runner/config:/etc/gitlab-runner'
- '/var/run/docker.sock:/var/run/docker.sock'
depends_on:
- gitlab
EOF
cd /srv/gitlab
docker compose down
docker compose up -d
⚠️ 把
git.yourdomain.com换成你的真实域名!
10.8 配置 HTTPS(SSL 证书)
为什么要 HTTPS?
- 数据加密传输,防止中间人攻击
- GitLab 某些功能(如 Container Registry)强制要求 HTTPS
- 浏览器不再显示"不安全"的警告,用户体验好
使用 Let’s Encrypt 免费证书
Let’s Encrypt 提供免费的 SSL 证书,有效期 90 天,可以自动续期。
前置条件:
- 域名已备案(国内)
- 域名已解析到服务器 IP
- 80 端口可访问(Let’s Encrypt 验证需要)
步骤:
# 安装 certbot
sudo apt update
sudo apt install -y certbot
# 获取证书(替换为你的域名)
sudo certbot certonly --standalone -d git.yourdomain.com
# 按提示输入邮箱,同意协议
# 证书会生成在 /etc/letsencrypt/live/git.yourdomain.com/
修改 docker-compose.yml 启用 HTTPS:
sudo tee /srv/gitlab/docker-compose.yml << 'EOF'
version: '3.8'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
container_name: gitlab
restart: always
hostname: 'git.yourdomain.com'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://git.yourdomain.com'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
nginx['listen_https'] = true
nginx['redirect_http_to_https'] = true
nginx['ssl_certificate'] = "/etc/gitlab/ssl/fullchain.pem"
nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/privkey.pem"
ports:
- '80:80'
- '443:443'
- '2222:22'
volumes:
- '/srv/gitlab/config:/etc/gitlab'
- '/srv/gitlab/logs:/var/log/gitlab'
- '/srv/gitlab/data:/var/opt/gitlab'
- '/etc/letsencrypt/live/git.yourdomain.com/fullchain.pem:/etc/gitlab/ssl/fullchain.pem'
- '/etc/letsencrypt/live/git.yourdomain.com/privkey.pem:/etc/gitlab/ssl/privkey.pem'
shm_size: '256m'
gitlab-runner:
image: 'gitlab/gitlab-runner:latest'
container_name: gitlab-runner
restart: always
volumes:
- '/srv/gitlab-runner/config:/etc/gitlab-runner'
- '/var/run/docker.sock:/var/run/docker.sock'
depends_on:
- gitlab
EOF
⚠️ 把
git.yourdomain.com换成你的真实域名!
重启 GitLab:
cd /srv/gitlab
docker compose down
docker compose up -d
设置自动续期:
Let’s Encrypt 证书 90 天过期,需要自动续期:
# 创建续期脚本
sudo tee /usr/local/bin/renew-ssl.sh << 'EOF'
#!/bin/bash
certbot renew --quiet
cd /srv/gitlab
docker compose restart gitlab
EOF
sudo chmod +x /usr/local/bin/renew-ssl.sh
# 添加到 crontab,每月 1 号凌晨 3 点执行
(crontab -l 2>/dev/null; echo "0 3 1 * * /usr/local/bin/renew-ssl.sh >> /var/log/renew-ssl.log 2>&1") | crontab -
验证 HTTPS
在浏览器访问:
https://git.yourdomain.com
应该能看到绿色的小锁 🔒,表示 HTTPS 配置成功!
10.9 云端 Runner 注册
云端 Runner 注册和本地完全一样,只是 URL 换成公网 IP 或域名:
Docker Runner:
docker exec -it gitlab-runner gitlab-runner register
| 提示 | 输入 |
|---|---|
| URL | http://你的公网IP:8080 或 https://你的域名 |
| Token | 从 GitLab Admin → Runners 获取 |
| Tags | docker |
| Executor | docker |
| Default image | ubuntu:24.04 |
Shell Runner:
sudo gitlab-runner register
| 提示 | 输入 |
|---|---|
| URL | http://你的公网IP:8080 或 https://你的域名 |
| Token | 从项目 Settings → CI/CD → Runners 获取 |
| Tags | shell |
| Executor | shell |
10.10 Nginx + Java 后端(云端版)
和本地部署完全一样,直接复制粘贴:
# 安装 Nginx
sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
# 创建前端目录
sudo mkdir -p /var/www/frontend
sudo chown -R www-data:www-data /var/www/frontend
sudo chmod 775 /var/www/frontend
# 配置 Nginx(和本地一样)
sudo rm -f /etc/nginx/sites-enabled/default
sudo tee /etc/nginx/sites-available/fullstack << 'EOF'
server {
listen 80;
server_name _;
location / {
root /var/www/frontend;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
EOF
sudo ln -sf /etc/nginx/sites-available/fullstack /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
# Java 后端服务(和本地一样)
sudo mkdir -p /opt/backend
sudo chown -R gitlab-runner:gitlab-runner /opt/backend
sudo tee /etc/systemd/system/backend.service << 'EOF'
[Unit]
Description=Java Backend
After=network.target
[Service]
Type=simple
User=gitlab-runner
WorkingDirectory=/opt/backend
ExecStart=/usr/bin/java -jar /opt/backend/app.jar --server.port=8081
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
# sudo 权限(和本地一样)
sudo tee /etc/sudoers.d/gitlab-runner << 'EOF'
gitlab-runner ALL=(ALL) NOPASSWD: /bin/rm, /bin/cp, /bin/mkdir, /bin/systemctl stop backend, /bin/systemctl start backend, /bin/systemctl status backend, /bin/systemctl daemon-reload, /bin/systemctl restart backend
EOF
sudo chmod 440 /etc/sudoers.d/gitlab-runner
10.11 数据备份策略(云端优势)
云端最大的好处之一就是备份方便,一定要利用起来!
方案 A:云快照(最简单)
- 阿里云:ECS 控制台 → 实例详情 → 本实例快照 → 创建快照
- 腾讯云:CVM 控制台 → 实例详情 → 快照 → 创建快照
建议:
- 每周自动快照一次
- 重大升级前手动快照一次
- 快照保留 2~4 个版本
方案 B:GitLab 内置备份
# 创建备份(包含仓库、数据库、配置)
docker exec -it gitlab gitlab-backup create
# 备份文件在 /srv/gitlab/data/backups/
ls -la /srv/gitlab/data/backups/
# 下载到本地(Win11 用 scp)
scp root@你的公网IP:/srv/gitlab/data/backups/xxx_gitlab_backup.tar .
方案 C:云盘备份到 OSS / COS
# 安装 ossutil(阿里云)
wget http://gosspublic.alicdn.com/ossutil/1.7.15/ossutil64
chmod +x ossutil64
sudo mv ossutil64 /usr/local/bin/
# 配置(按提示输入 AccessKey)
ossutil64 config
# 每天自动备份到 OSS
(crontab -l 2>/dev/null; echo "0 2 * * * cd /srv/gitlab/data/backups && ossutil64 cp *.tar oss://your-bucket/gitlab-backups/ >> /var/log/oss-backup.log 2>&1") | crontab -
💡 备份三原则:3-2-1 法则——至少 3 份备份,2 种不同介质,1 份异地存储。
10.12 云端特有排错
❌ 37. 安全组端口没开放,访问超时
现象:浏览器访问 http://公网IP:8080 一直转圈,最后超时。
原因:安全组没有开放 8080 端口。
解决:去云平台控制台 → 安全组 → 添加入方向规则,开放 8080 端口。
❌ 38. 备案未完成,80/443 端口被阻断
现象:访问 http://你的域名 显示"未备案"或无法访问,但 http://公网IP:8080 可以。
原因:国内服务器未备案,运营商封了 80/443 端口。
解决:
- 临时方案:用非标准端口(如 8080、8443)
- 根本方案:完成 ICP 备案
❌ 39. Let’s Encrypt 证书申请失败
现象:certbot 报错 Connection refused 或 Timeout。
原因:
- 80 端口被占用(GitLab 容器占用了 80:80)
- 安全组没开放 80 端口
- 域名解析还没生效
解决:
# 1. 临时停止 GitLab,释放 80 端口
cd /srv/gitlab
docker compose down
# 2. 申请证书
sudo certbot certonly --standalone -d git.yourdomain.com
# 3. 启动 GitLab
docker compose up -d
❌ 40. 公网 IP 暴露,被扫描攻击
现象:服务器 CPU 突然飙升,日志里大量陌生 IP 的登录尝试。
原因:GitLab 暴露在公网,被黑客扫描。
解决:
# 1. 修改 SSH 默认端口(可选)
sudo sed -i 's/#Port 22/Port 22222/' /etc/ssh/sshd_config
sudo systemctl restart sshd
# 2. 安装 fail2ban 自动封禁暴力破解
sudo apt install -y fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# 3. GitLab 设置强密码 + 关闭注册
# GitLab 网页 → Admin → Settings → Sign-up restrictions → 取消勾选 Sign-up enabled
❌ 41. 云服务器被限速,GitLab 极慢
现象:GitLab 页面加载慢,Runner 构建超时。
原因:云服务器带宽太小(如 1Mbps),或遇到网络高峰限速。
解决:
- 升级带宽到 5Mbps+
- 配置国内镜像加速(Docker、Maven、npm)
- 非工作时间执行大构建任务
❌ 42. 数据盘没挂载,GitLab 又满了
现象:df -h 显示根分区满了,但明明买了 100GB 数据盘。
原因:数据盘买了但没挂载,GitLab 数据还是写在系统盘。
解决:见 10.5 节,挂载数据盘到 /srv/gitlab。
10.13 成本优化建议
| 策略 | 说明 | 节省 |
|---|---|---|
| 按量付费转包年包月 | 长期用选包年包月,便宜 30~50% | ~50 元/月 |
| 使用抢占式实例 | 阿里云/腾讯云的竞价实例,价格低 70% | ~70 元/月 |
| 关闭时释放公网 IP | 不用的时候解绑公网 IP,省带宽费 | ~20 元/月 |
| 使用轻量应用服务器 | 阿里云/腾讯云轻量服务器,性价比更高 | ~30 元/月 |
| 对象存储存备份 | 备份放 OSS/COS,比云盘便宜 | ~10 元/月 |
10.14 云端部署 checklist
部署完成后,逐项检查:
- 云服务器已购买,配置满足最低要求
- 安全组已开放 22、80、443、8080、8081、2222 端口
- 数据盘已挂载到
/srv/gitlab - Docker 和 Docker Compose 已安装
- GitLab 容器已启动,浏览器可访问
- Docker Runner 已注册,状态 Online
- Shell Runner 已注册,状态 Online
- Nginx 已安装并配置
- Java 后端 systemd 服务已配置
- 域名已解析(可选)
- SSL 证书已配置(可选)
- 自动备份已配置(云快照或 ossutil)
- fail2ban 已安装(安全)
- GitLab 注册已关闭(安全)
- root 密码已修改为强密码(安全)
🐛 第十一步:排错大全(30+ 常见问题)
恭喜你来到本文档最实用的章节!这里汇集了部署过程中可能遇到的所有坑,以及填坑方法。建议收藏,遇事不慌。
🔴 环境准备阶段
❌ 1. SSH 连接时输入 Y 无效
现象:
Are you sure you want to continue connecting (yes/no/[fingerprint])? Y
Please type 'yes', 'no' or the fingerprint:
原因:SSH 要求输入完整的 yes,不能简写为 Y 或 y。
解决:输入完整的 yes,按回车。SSH 就是这么严格,不接受缩写。
❌ 2. VMware NAT 模式导致 Win11 无法访问虚拟机
现象:浏览器访问虚拟机 IP 显示 “拒绝连接”。
原因:NAT 模式下虚拟机 IP 只在 VMware 内部网络有效,宿主机无法直接访问。
解决:
- 关闭虚拟机
- VMware → 虚拟机 → 设置 → 网络适配器
- 改为 桥接模式,勾选 “复制物理网络连接状态”
- 重新开机,获取新 IP
❌ 3. 虚拟机重启后 IP 变化
现象:桥接模式下 IP 随网络环境变化。
解决:使用 netplan 固定静态 IP(见第一步)。
❌ 4. 虚拟机控制台复制粘贴不生效
现象:在 VMware 黑框控制台里无法复制粘贴命令。
原因:Ubuntu Server 无图形界面,VMware 控制台不支持剪贴板。
解决:用 Win11 的 CMD/PowerShell 通过 SSH 连接虚拟机。SSH 窗口复制粘贴丝般顺滑。
🔴 Docker 安装阶段
❌ 5. Docker 官方仓库配置失败
现象:
Package docker-ce-cli is not available...
Package 'docker-ce' has no installation candidate
原因:Docker 官方 APT 仓库配置失败,系统找不到 docker-ce 包。
解决:直接用 Ubuntu 官方源安装:
sudo apt update
sudo apt install -y docker.io
❌ 6. docker compose 命令不存在
现象:
docker: unknown command: docker compose
原因:Ubuntu 24.04 安装的是 docker-compose-v2,命令格式是 docker compose(空格),不是 docker-compose(横杠)。
解决:
sudo apt install -y docker-compose
# 装完后用 docker compose(空格)
❌ 7. 拉取镜像失败 connection refused
现象:
failed to resolve reference ... dial tcp ... connect: connection refused
原因:无法访问 Docker Hub(国际网络问题)。
解决:配置国内镜像加速器(见第二步 2.3)。
🔴 GitLab 部署阶段
❌ 8. 磁盘空间不足
现象:
ERROR: could not extend file ... No space left on device
原因:GitLab 首次初始化需要大量空间(建议 50GB+),虚拟机磁盘不足。
解决:
- VMware 中关闭虚拟机 → 设置 → 硬盘 → 扩展(至少 80GB)
- Ubuntu 中扩展 LVM 分区:
sudo apt install -y cloud-guest-utils
sudo growpart /dev/sda 3
sudo pvresize /dev/sda3
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
❌ 9. GitLab 初始化被中断导致配置损坏
现象:
curl -I http://localhost:8080返回Empty reply from server- 容器状态
unhealthy - 删除
/srv/gitlab/config/*后容器内找不到配置文件
原因:在容器运行时删除了 config 目录,导致内部进程崩溃。
解决:
# 1. 彻底停止容器
cd /srv/gitlab
docker compose down
# 2. 清空所有数据(壮士断腕)
sudo rm -rf /srv/gitlab/config/*
sudo rm -rf /srv/gitlab/data/*
sudo rm -rf /srv/gitlab/logs/*
# 3. 重新启动(会重新初始化)
docker compose up -d
⚠️ 警告:这步会清空所有 GitLab 数据!如果是生产环境,请先备份!
❌ 10. Nginx 和 Puma 端口冲突
现象:
Address already in use - bind(2) for "127.0.0.1" port 8080
原因:external_url 'http://172.19.90.78:8080' 让 Nginx 误以为要监听 8080,但 Puma(Rails Web 服务器)默认也监听 8080。
解决:在 docker-compose.yml 的 GITLAB_OMNIBUS_CONFIG 中强制指定:
nginx['listen_port'] = 80
❌ 11. Win11 浏览器访问不了 GitLab
原因:
- GitLab 还在初始化(首次启动需要 5~15 分钟)
- 防火墙拦截
- 虚拟机网络模式不对(NAT 而不是桥接)
解决:
# 开放端口
sudo ufw allow 8080
sudo ufw allow 80
sudo ufw allow 2222
# 检查容器状态
docker ps
curl -I http://localhost:8080
🔴 GitLab Runner 阶段
❌ 12. Docker Runner 容器停止运行
现象:
Error response from daemon: container ... is not running
解决:
cd /srv/gitlab
docker compose up -d gitlab-runner
❌ 13. Runner 标签不匹配导致 Job 无法执行
现象:
This job has not started yet
No project runners found
原因:
.gitlab-ci.yml里写了tags: - docker,但项目只有 Shell Runner- 或者 Runner 是 Instance Runner,但项目没有启用
解决:
- 方案 A:在项目中创建 Project Runner(推荐)
- 方案 B:修改
.gitlab-ci.yml的 tags 匹配实际 Runner 标签 - 方案 C:给 Runner 勾选 “Run untagged jobs”
❌ 14. Shell Runner 项目里看不到
现象:my-project → Settings → CI/CD → Runners 里看不到 deploy-shell。
原因:Runner 注册成了 Instance Runner,但项目没有启用它。
解决:在项目里直接创建 New project runner,用项目级别的 Token 注册。
❌ 15. Docker Runner 无法访问宿主机目录
现象:deploy 阶段用 Docker Runner 执行 cp 到 /var/www/frontend/ 失败。
原因:Docker Runner 运行在容器里,默认无法访问宿主机的文件系统。
解决:
- 推荐:deploy 阶段改用 Shell Runner(在宿主机上执行)
- 或者给 Docker Runner 配置 volume 映射
/var/www/frontend
🔴 CI/CD 配置阶段
❌ 16. .gitlab-ci.yml 格式错误
现象:
jobs:deploy_job:script config should be a string or a nested array of strings up to 10 levels deep
原因:
- Windows 记事本保存的 UTF-8 文件带 BOM 头(隐藏字符)
- 使用了 Tab 键缩进,YAML 要求用空格
- 缩进层级不对
解决:
- 不要用记事本,用 VS Code 或 GitLab 网页编辑器
- 确保右下角显示 “Spaces: 2” 和 “UTF-8”
- 在 GitLab 网页直接编辑
.gitlab-ci.yml
❌ 17. 分支不匹配
现象:流水线只有 build_job,没有 deploy_job。
原因:.gitlab-ci.yml 写了 only: - main,但 Git 分支是 master。
解决:
only:
- main
- master # 加上这行,两个分支都能触发
❌ 18. Git 身份未配置
现象:
fatal: unable to auto-detect email address
解决:
git config user.email "duke@example.com"
git config user.name "Duke"
❌ 19. SSH 公钥认证失败
现象:
git@172.19.90.78: Permission denied (publickey).
解决:
ssh-keygen -t ed25519 -C "duke@gitlab"
type %USERPROFILE%\.ssh\id_ed25519.pub
复制输出到 GitLab Preferences → SSH Keys 中添加。
❌ 20. Maven 依赖下载慢/超时
现象:build-backend 运行 30+ 分钟后失败,显示 Connection reset。
原因:从 Maven 中央仓库下载依赖,国际网络不稳定。
解决:在 backend/ 目录创建 settings.xml,配置阿里云镜像:
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<mirrors>
<mirror>
<id>aliyun</id>
<name>Aliyun Maven</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
然后在 .gitlab-ci.yml 中使用:
script:
- mvn -s settings.xml clean package -DskipTests
❌ 21. npm 下载慢
现象:build-frontend 中 npm install 卡住或极慢。
原因:默认从 npmjs.org(国外源)下载。
解决:在构建脚本中切换淘宝镜像:
script:
- npm config set registry https://registry.npmmirror.com
- npm install
- npm run build
🔴 Nginx 部署阶段
❌ 22. 403 Forbidden(Nginx 目录为空)
现象:访问 http://你的IP/ 显示 403 Forbidden。
原因:
/var/www/frontend/目录为空(deploy 没把文件复制过来)- Nginx 配置错误
解决:
# 检查目录
ls -la /var/www/frontend/
# 修复权限
sudo chown -R www-data:www-data /var/www/frontend
sudo chmod 755 /var/www/frontend
# 重启 Nginx
sudo systemctl reload nginx
❌ 23. Shell Runner 权限不足
现象:
cp: cannot create regular file '/var/www/frontend/': Permission denied
sudo: I'm sorry gitlab-runner. I'm afraid I can't do that
原因:gitlab-runner 用户没有 sudo 权限执行 rm、cp、systemctl 等命令。
解决:
sudo tee /etc/sudoers.d/gitlab-runner << 'EOF'
gitlab-runner ALL=(ALL) NOPASSWD: /bin/rm, /bin/cp, /bin/mkdir, /bin/systemctl stop backend, /bin/systemctl start backend, /bin/systemctl status backend, /bin/systemctl daemon-reload
EOF
sudo chmod 440 /etc/sudoers.d/gitlab-runner
sudo visudo -c
❌ 24. Nginx 默认站点冲突
现象:访问 IP 显示的是 Nginx 默认欢迎页,而不是自己的项目。
解决:
# 禁用默认站点
sudo rm -f /etc/nginx/sites-enabled/default
# 启用自己的站点
sudo ln -sf /etc/nginx/sites-available/fullstack /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
🔴 Java + Vue 全栈部署阶段
❌ 25. 8081 端口被旧项目占用
现象:后端启动成功,但访问 http://你的IP:8081/hello 返回的是旧项目的页面或 404。
原因:之前部署的 java-demo 项目还在运行,占用了 8081 端口。
解决:
# 查看所有 Java 进程
ps aux | grep java
# 查看所有 Java 相关的 systemd 服务
sudo systemctl list-units --type=service | grep -i java
# 停止并禁用旧服务
sudo systemctl stop java-demo
sudo systemctl disable java-demo
# 杀掉所有残留进程
sudo pkill -9 java
# 只启动新服务
sudo systemctl start backend
❌ 26. 后端接口 404
现象:curl http://localhost:8081/hello 返回 404,但 curl http://localhost:8081/ 能返回内容。
原因:Controller 代码里映射的是根路径 /,不是 /hello。
解决:修改 HelloController.java:
@RestController
@RequestMapping("/hello") // 加上这行
public class HelloController {
@GetMapping
public String hello() {
return "Hello from Java Backend! 🎉";
}
}
❌ 27. 前端调用后端跨域
现象:前端点击按钮调用后端接口,浏览器控制台报跨域错误。
原因:前端和后端在不同端口,浏览器安全策略限制。
解决:
- 生产环境:通过 Nginx 反向代理,前端和后端走同域(
http://你的IP/api/→ 后端) - 开发环境:
vue.config.js配置 proxy:
devServer: {
proxy: {
'/api': {
target: 'http://你的IP:8081',
changeOrigin: true
}
}
}
❌ 28. Vue 刷新页面 404
现象:点击前端路由正常,但直接刷新页面显示 Nginx 404。
原因:Vue 是单页应用(SPA),路由由前端处理,Nginx 找不到对应的物理文件。
解决:Nginx 配置 try_files:
location / {
root /var/www/frontend;
index index.html;
try_files $uri $uri/ /index.html; # 关键:找不到文件回退到 index.html
}
🔴 其他问题
❌ 29. 路径嵌套问题
现象:解压 zip 后路径变成 D:\demo\gitlab\my-project\my-project\。
解决:
cd D:\demo\gitlab
rmdir /S /Q my-project
# 重新解压,确保路径是 D:\demo\gitlab\my-project\.gitlab-ci.yml
❌ 30. vim 卡死
现象:粘贴内容后 vim 无法退出。
解决:
- 按
Esc键,然后输入:q!回车,强制退出不保存 - 推荐:用
tee命令直接写文件,不用 vim
❌ 31. docker compose up -d 报错 no configuration file provided
原因:当前目录没有 docker-compose.yml。
解决:
cd /srv/gitlab
docker compose up -d
❌ 32. curl -I http://localhost:8080 返回 Connection reset by peer
原因:Nginx 和 Puma 端口冲突。
解决:在 docker-compose.yml 的 GITLAB_OMNIBUS_CONFIG 中加上:
nginx['listen_port'] = 80
然后重启容器。
❌ 33. sudo systemctl start backend 报错 Failed to start backend.service
原因:jar 包不存在,或服务文件配置错误。
解决:
# 检查 jar 包是否存在
ls -la /opt/backend/app.jar
# 检查服务文件语法
sudo systemctl daemon-reload
# 手动测试启动
sudo java -jar /opt/backend/app.jar --server.port=8081
❌ 34. cp target/*.jar /opt/backend/app.jar 报错 Permission denied
原因:gitlab-runner 用户没有写入权限。
解决:
sudo chown -R gitlab-runner:gitlab-runner /opt/backend
sudo chmod 755 /opt/backend
❌ 35. 虚拟机重启后 GitLab 没自动启动
原因:容器没有设置开机自启,或 Docker 服务没启动。
解决:
# 启动 Docker
sudo systemctl start docker
# 启动 GitLab
cd /srv/gitlab
docker compose up -d
💡 如果 Docker 已设置
restart: always,容器会自动启动,但需要 Docker 服务先起来。
❌ 36. nginx['listen_port'] has been deprecated
原因:GitLab 新版语法变了,但不影响使用。
解决:可以忽略,或改成新语法:
gitlab_rails['nginx']['listen_port'] = 80
📋 附录一:运维命令速查表
GitLab 容器
| 场景 | 命令 |
|---|---|
| 启动 GitLab | cd /srv/gitlab && docker compose up -d |
| 停止 GitLab | cd /srv/gitlab && docker compose down |
| 查看 GitLab 日志 | docker logs -f gitlab |
| 查看容器状态 | docker ps |
| 查看 GitLab 内部服务 | docker exec -it gitlab gitlab-ctl status |
| 重启 GitLab 配置 | docker exec -it gitlab gitlab-ctl reconfigure |
| 获取 root 密码 | docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password |
Nginx
| 场景 | 命令 |
|---|---|
| 测试配置 | sudo nginx -t |
| 重载配置 | sudo systemctl reload nginx |
| 重启 Nginx | sudo systemctl restart nginx |
| 查看 Nginx 状态 | sudo systemctl status nginx |
Java 后端
| 场景 | 命令 |
|---|---|
| 启动服务 | sudo systemctl start backend |
| 停止服务 | sudo systemctl stop backend |
| 重启服务 | sudo systemctl restart backend |
| 查看状态 | sudo systemctl status backend |
| 查看端口监听 | sudo ss -tlnp | grep 8081 |
| 查看 Java 进程 | ps aux | grep java |
GitLab Runner
| 场景 | 命令 |
|---|---|
| 查看 Runner 列表 | sudo gitlab-runner list |
| 验证 Runner | sudo gitlab-runner verify |
| 重启 Runner | sudo systemctl restart gitlab-runner |
| Docker Runner 验证 | docker exec -it gitlab-runner gitlab-runner verify |
系统
| 场景 | 命令 |
|---|---|
| 查看磁盘 | df -h |
| 查看内存 | free -h |
| 查看 CPU | nproc |
| 查看 IP | ip addr show |
| 查看进程 | ps aux | grep java |
| 查看端口占用 | sudo ss -tlnp |
📋 附录二:最佳实践总结
| 场景 | 建议 |
|---|---|
| 编辑配置文件 | 用 VS Code 或 GitLab 网页编辑器,不要用记事本 |
| SSH 连接 | 用 Win11 Terminal SSH,不要用 VMware 控制台 |
| Docker 安装 | 直接用 sudo apt install docker.io,不要折腾官方仓库 |
| GitLab 初始化 | 不要中途删除 config 目录,等容器完全启动 |
| 磁盘空间 | 虚拟机至少分配 80GB,GitLab 非常吃空间 |
| Runner 注册 | 在项目里创建 Project Runner,不要用 Instance Runner |
| CI/CD 调试 | deploy 脚本里加 echo $(pwd) 和 ls -la 查看实际路径 |
| Nginx 部署 | 确保 /var/www/ 目录权限正确,www-data 用户可读写 |
| 多项目部署 | 旧项目停用时务必 systemctl disable 并 pkill 清理残留进程 |
| 网络加速 | Maven 用阿里云镜像,npm 用淘宝镜像,Docker 用国内加速器 |
| 分支管理 | .gitlab-ci.yml 同时写 main 和 master,避免分支名不匹配 |
| 权限最小化 | sudoers 只给必要命令,不要给 ALL |
🎉 恭喜你!
如果你跟着本文档一步一步做到了这里,那么你已经:
- ✅ 在 VMware 上跑起了 Ubuntu Server 24.04
- ✅ 安装了 Docker 和 Docker Compose
- ✅ 部署了 GitLab CE 私有化代码仓库
- ✅ 注册了 Docker Runner + Shell Runner
- ✅ 配置了 Nginx 反向代理
- ✅ 搭建了 Java + Vue 全栈自动化 CI/CD 流水线
- ✅ 掌握了 30+ 个常见问题的排查方法
以后你的开发流程变成:
写代码 → git add → git commit → git push → 自动构建 → 自动部署 → 刷新浏览器看效果
再也不用手动登录服务器、手动编译、手动复制文件了。把时间省下来,喝杯咖啡,摸摸鱼,不香吗?☕😎
🖥️ 环境:Win11 + VMware / 阿里云 / 腾讯云 + Ubuntu Server 24.04 + GitLab CE 19.2
📝 共收录 42 个常见问题 及解决方案
🚀 祝你部署顺利,流水线永远绿灯!

6156


被折叠的 条评论
为什么被折叠?



