Jenkins + Docker + GitLab 实现 CI/CD 自动化:从手动部署到自动构建的进化

本文记录了使用 Jenkins + Docker + GitLab 搭建 CI/CD 自动化流水线的完整过程,包括 7 个踩坑点及解决方案。

一、前言

刚学运维的时候,每次改完代码都要手动打包、上传、部署,一套流程下来十几分钟,还容易出错。

后来学了 Docker,部署方便了,但还是要登录服务器手动操作。

直到用了 Jenkins + Docker + GitLab,终于实现了"代码提交即部署"的自动化流程。

最终效果:代码 push 到 GitLab 后,Jenkins 自动拉取代码、构建 Docker 镜像、部署到服务器,全程无需手动操作。

在这里插入图片描述

流程图

在这里插入图片描述


二、技术选型

组件选择原因
CI/CD 工具Jenkins业界主流,插件丰富
代码托管GitLab(本地部署)内网访问快,不依赖外网
容器化Docker轻量级,部署方便
Web 服务器Nginx轻量级,性能好

为什么选择本地 GitLab 而不是 Gitee?

最初计划用 Gitee,但配置 Webhook 时发现 Gitee 服务器在公网,无法访问内网 IP。所以改用本地部署的 GitLab,同在内网,直接访问。


三、环境准备

3.1 服务器配置

项目配置
操作系统Kylin Linux V10
IP 地址192.168.1.5(内网环境)
内存8GB(Jenkins 需要 512MB+,GitLab 需要 4GB+)
Docker已安装

3.2 项目结构

jenkins-cicd/
├── jenkins/
│   └── docker-compose.yml    # Jenkins 容器配置
├── gitlab/
│   └── docker-compose.yml    # GitLab 容器配置
├── demo-app/
│   ├── Dockerfile            # 应用镜像定义
│   ├── index.html            # 应用网页
│   └── Jenkinsfile           # 流水线定义
└── README.md                 # 项目说明

四、部署 Jenkins

4.1 编写 docker-compose.yml

version: '3'

services:
  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    restart: always
    network_mode: host
    volumes:
      - jenkins_data:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker
      - /usr/lib64/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7
    environment:
      - JAVA_OPTS=-Duser.timezone=Asia/Shanghai

volumes:
  jenkins_data:

关键配置解释

配置作用
network_mode: host使用宿主机网络,方便访问其他容器
/var/run/docker.sock让 Jenkins 能调用宿主机的 Docker
/usr/bin/docker挂载 Docker 二进制文件
/usr/lib64/libltdl.so.7Docker 依赖库(踩坑后发现必须挂载)
JAVA_OPTS=-Duser.timezone=Asia/Shanghai设置时区为中国时间

4.2 启动 Jenkins

cd /root/user/jenkins-cicd
docker-compose -f jenkins/docker-compose.yml up -d

4.3 初始化 Jenkins

  1. 浏览器访问:http://192.168.1.5:8080
  2. 获取初始密码:
    docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
    
  3. 安装推荐插件
  4. 创建管理员账号

4.4 安装必要插件

在 Jenkins 首页 → Manage Jenkins → Manage Plugins → Available:

  • Docker Pipeline:让 Jenkins Pipeline 能执行 docker 命令
  • GitLab Plugin:让 Jenkins 能识别 GitLab 的 Webhook 请求

五、部署 GitLab

5.1 编写 docker-compose.yml

version: '3'

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: always
    hostname: gitlab.local
    ports:
      - "8929:8929"
      - "443:443"
      - "2222:22"
    volumes:
      - ./config:/etc/gitlab
      - ./logs:/var/log/gitlab
      - ./data:/var/opt/gitlab
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://192.168.1.5:8929'
        gitlab_rails['gitlab_shell_ssh_port'] = 2222

关键配置解释

配置作用
8929:8929GitLab Web 界面端口
2222:22SSH 克隆端口(避免和宿主机 SSH 冲突)
external_urlGitLab 访问地址
gitlab_shell_ssh_port让 GitLab 页面显示正确的 SSH 地址

5.2 启动 GitLab

cd /root/user/jenkins-cicd
docker-compose -f gitlab/docker-compose.yml up -d

注意:GitLab 第一次启动需要 5-10 分钟初始化,耐心等待。

检查是否就绪:

curl -s -o /dev/null -w "%{http_code}" http://192.168.1.5:8929

返回 200 或 302 说明就绪了。

5.3 初始化 GitLab

  1. 获取初始密码:

    docker exec gitlab cat /etc/gitlab/initial_root_password
    
  2. 浏览器访问:http://192.168.1.5:8929

  3. 登录(用户名:root,密码:刚才获取的密码)

  4. 修改密码(初始密码太长难记)

5.4 创建项目

在 GitLab 页面点击 New project

字段
Project namejenkins-cicd-demo
Project descriptionJenkins CI/CD 演示应用
Visibility LevelPrivate 或 Internal

在这里插入图片描述


六、创建演示应用

6.1 创建 index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>My App</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            padding: 50px;
            background-color: #f0f0f0;
        }
        h1 { color: #333; }
        .version { color: #666; font-size: 18px; }
    </style>
</head>
<body>
    <h1>Hello from Jenkins CI/CD!</h1>
    <p class="version">Version: 3.0</p>
    <p>This page is automatically deployed by Jenkins Pipeline.</p>
</body>
</html>

6.2 创建 Dockerfile

FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
RUN chmod 644 /usr/share/nginx/html/index.html
EXPOSE 80

注意RUN chmod 644 这行很重要!不加会导致 nginx 返回 403 Forbidden。

6.3 创建 Jenkinsfile

pipeline {
    agent any

    environment {
        IMAGE_NAME = "demo-app"
        IMAGE_TAG = "${env.BUILD_NUMBER}"
        CONTAINER_NAME = "demo-app"
        HOST_PORT = "8090"
        CONTAINER_PORT = "80"
    }

    stages {
        stage('拉取代码') {
            steps {
                checkout scm
                echo "代码拉取完成"
            }
        }

        stage('构建镜像') {
            steps {
                echo "开始构建 Docker 镜像..."
                sh "docker build -t ${IMAGE_NAME}:${IMAGE_TAG} ."
                sh "docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest"
                echo "镜像构建完成:${IMAGE_NAME}:${IMAGE_TAG}"
            }
        }

        stage('部署') {
            steps {
                echo "开始部署..."
                sh "docker rm -f ${CONTAINER_NAME} || true"
                sh "docker run -d --name ${CONTAINER_NAME} -p ${HOST_PORT}:${CONTAINER_PORT} ${IMAGE_NAME}:latest"
                echo "部署完成"
            }
        }

        stage('验证') {
            steps {
                echo "开始验证..."
                sh "sleep 3"
                sh "curl -s http://localhost:${HOST_PORT} | grep 'Hello from Jenkins' || exit 1"
                echo "验证通过!"
            }
        }
    }

    post {
        success {
            echo "=========================================="
            echo "  流水线执行成功!"
            echo "  访问地址:http://192.168.1.5:${HOST_PORT}"
            echo "=========================================="
        }
        failure {
            echo "=========================================="
            echo "  流水线执行失败,请检查日志"
            echo "=========================================="
        }
    }
}

6.4 推送到 GitLab

cd /root/user/jenkins-cicd/demo-app
git init
git add .
git commit -m "Initial commit: demo app"
git remote add origin http://192.168.1.5:8929/root/jenkins-cicd-demo.git
git push -u origin master

注意:GitLab 默认禁止密码认证,需要使用 Personal Access Token。

生成 Token:

  1. GitLab → 头像 → Settings → Access Tokens
  2. Token name:git-push
  3. Scopes:勾选 apiwrite_repository
  4. 点击 Create personal access token
  5. 复制 Token,推送时用 Token 代替密码

七、配置 Jenkins Pipeline

7.1 创建 Pipeline 任务

  1. Jenkins 首页 → New Item
  2. 输入任务名:demo-app-pipeline
  3. 选择 Pipeline
  4. 点击 OK

7.2 配置 Pipeline

进入任务 → Configure:

Pipeline 配置

字段
DefinitionPipeline script from SCM
SCMGit
Repository URLhttp://192.168.1.5:8929/root/jenkins-cicd-demo.git
Credentialsgitlab-credentials
Branch Specifier*/master
Script PathJenkinsfile

在这里插入图片描述

凭据配置

  1. Jenkins → Manage Jenkins → Manage Credentials
  2. 点击 Jenkins → Global credentials (unrestricted)
  3. 点击 Add Credentials
  4. 填写:
字段
KindUsername with password
ScopeGlobal
Usernameroot
Password你的 GitLab 密码
IDgitlab-credentials
DescriptionGitLab 登录凭据

八、配置 Webhook(自动触发构建)

8.1 配置 Jenkins 触发器

  1. Jenkins → demo-app-pipeline → Configure
  2. 找到 Build Triggers
  3. 勾选 Build when a change is pushed to GitLab
  4. 点击 高级(Advanced)
  5. 找到 Secret Token
  6. 点击 Generate 生成 Token
  7. 复制生成的 Token

8.2 配置 GitLab Webhook

  1. GitLab → 项目 → Settings → Webhooks
  2. 填写:
字段
URLhttp://192.168.1.5:8080/project/demo-app-pipeline
Secret token粘贴刚才生成的 Token
Trigger勾选 Push events
  1. 点击 Add webhook

在这里插入图片描述

8.3 允许本地网络 Webhook

GitLab 默认会阻止对本地网络的 Webhook 请求,需要修改配置:

  1. GitLab → Admin Area → Settings → Network
  2. 找到 Outbound requests
  3. 勾选 Allow requests to the local network from webhooks and integrations
  4. 点击 Save changes

九、测试自动构建

9.1 手动触发第一次构建

  1. Jenkins → demo-app-pipeline → Build Now
  2. 等待构建完成(1-2 分钟)

9.2 查看构建日志

点击构建编号 → Console Output:

在这里插入图片描述

9.3 验证部署结果

浏览器访问:http://192.168.1.5:8090

在这里插入图片描述

9.4 测试自动触发

修改 index.html,把版本改成 4.0:

<p class="version">Version: 4.0</p>

推送到 GitLab:

cd /root/user/jenkins-cicd/demo-app
git add .
git commit -m "Update to v4.0"
git push

观察 Jenkins 是否自动开始构建!


十、踩坑记录

10.1 libltdl.so.7 缺失

问题:Jenkins 容器执行 docker build 报错:

docker: error while loading shared libraries: libltdl.so.7: cannot open shared object file: No such file or directory

原因:只挂载了 docker 二进制文件,没有挂载依赖库。

解决:在 docker-compose.yml 添加库文件挂载:

volumes:
  - /usr/lib64/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7

10.2 docker.sock 权限问题

问题

permission denied while trying to connect to the Docker daemon socket

原因:Jenkins 用户没有权限访问 docker.sock。

解决:在宿主机执行:

chmod 666 /var/run/docker.sock

10.3 index.html 403 Forbidden

问题:部署后访问返回 403 Forbidden。

原因:nginx 以 nginx 用户运行,没有权限读取 index.html。

解决:在 Dockerfile 添加权限设置:

RUN chmod 644 /usr/share/nginx/html/index.html

10.4 Jenkins 容器无法访问 localhost:8090

问题:Jenkins 验证阶段 curl localhost:8090 失败。

原因:Jenkins 容器和 demo-app 容器在不同网络,无法互通。

解决:改用 host 网络模式:

services:
  jenkins:
    network_mode: host

10.5 Gitee Webhook 无法访问内网

问题:Gitee 无法访问内网 IP 的 Jenkins。

原因:Gitee 服务器在公网,无法访问 192.168.1.5 内网地址。

解决:改用本地 GitLab,同在内网,直接访问。

10.6 GitLab Webhook 返回 403

问题:GitLab 测试 Webhook 返回:

HTTP ERROR 403 anonymous is missing the Job/Build permission

原因:Jenkins 需要验证 Webhook 请求的身份。

解决

  1. Jenkins 任务配置中设置 Secret Token(高级选项里)
  2. GitLab Webhook 配置相同的 Secret Token

10.7 GitLab 推送需要 Token

问题git push 时提示:

HTTP Basic: Access denied. If a password was provided for Git authentication, the password was incorrect or you're required to use a token instead of a password.

原因:GitLab 默认禁止密码认证,要求使用 Personal Access Token。

解决

  1. GitLab → Settings → Access Tokens → 生成 Token
  2. 推送时用 Token 代替密码

十一、核心概念解析

11.1 Docker Outside of Docker(DooD)

Jenkins 容器通过挂载宿主机的 Docker 来执行构建:

Jenkins 容器内
    │
    ▼
执行 docker build . (用挂载进来的 docker CLI)
    │
    ▼
通过 sock 把命令发给宿主机 daemon
    │
    ▼
宿主机 daemon 解析并执行构建
    │
    ▼
镜像构建好,存在宿主机仓库
    │
    ▼
结果通过 sock 返回给 Jenkins 容器

关键点

  • Docker CLI(容器里)= 遥控器,只发命令
  • Docker Daemon(宿主机)= 干活的人,真正执行
  • Socket = 电话线,连接 CLI 和 Daemon

11.2 Webhook 认证机制

GitLab Webhook 发送请求
    ↓
带上 Secret Token
    ↓
Jenkins 收到请求
    ↓
验证 Secret Token 是否匹配
    ↓
├── 匹配 → 执行构建
└── 不匹配 → 403 错误

两个认证方向

方向作用认证方式
Jenkins → GitLab拉代码、访问 APIGitLab API Token
GitLab → Jenkins发送 Webhook 通知Secret Token

11.3 构建上下文

Jenkins workspace(/var/jenkins_home/workspace/demo-app-pipeline/)
    │
    ├── Dockerfile
    ├── index.html
    └── Jenkinsfile
    │
    ▼ 执行 Jenkinsfile 里的命令
    │
docker build -t demo-app:1 .
                │
                └── 这个 "." 就是当前目录(Jenkinsfile 所在目录)
                    Dockerfile 和 index.html 都在这个目录下
                    所以能正确构建

十二、项目总结

12.1 实现功能

  • ✅ 代码 push 到 GitLab 后自动触发构建
  • ✅ 自动构建 Docker 镜像
  • ✅ 自动部署容器
  • ✅ 自动验证部署结果
  • ✅ 流程可版本控制(Jenkinsfile)

12.2 技术亮点

  • Pipeline as Code:用 Jenkinsfile 定义流水线,代码可版本控制
  • Docker Outside of Docker:让 Jenkins 容器调用宿主机 Docker
  • Webhook 自动触发:代码推送后自动构建部署
  • 本地 GitLab:内网访问快,不依赖外网

十三、参考资料


十四、项目代码

本文基于实际项目编写,所有步骤都在内网环境(192.168.1.5)中验证通过。

由于是内网环境,无法提供在线演示,但所有代码和配置都已详细列出,读者可以按照步骤在自己的环境中复现。

项目代码https://gitee.com/pengjia12345/jenkins-cicd

更多推荐