我们来学习 Docker 容器化部署。这是现代 DevOps 的核心实践之一——将应用及其依赖打包成镜像,在任何支持 Docker 的环境中一致运行。我们将在现有的 CI/CD 流水线中集成 Docker,让 Jenkins 构建镜像并部署容器。


🎯 学习目标

  • 编写 Dockerfile 将 Flask 应用容器化。

  • 在 Jenkins Pipeline 中增加 构建 Docker 镜像 和 运行容器 阶段。

  • 学会管理容器(停止旧容器、启动新容器)。

  • 验证容器化部署的效果。


📌 前提准备

  • Jenkins 服务器(192.168.88.135)已安装 Docker。

  • 确保 Jenkins 用户有权限运行 Docker 命令。

如果尚未安装 Docker,我会在下面的步骤中给出安装命令。


🔧 第一步:在 Jenkins 服务器上安装 Docker

SSH 登录到 Jenkins 服务器(192.168.88.135),以 root 身份执行:

bash

# 1. 卸载旧版本(如果有)
sudo dnf remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine

# 2. 安装必要依赖
sudo dnf install -y dnf-utils device-mapper-persistent-data lvm2

# 3. 添加 Docker 官方仓库
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 4. 安装 Docker
sudo dnf install -y docker-ce docker-ce-cli containerd.io

# 5. 启动 Docker 并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

# 6. 将 Jenkins 用户加入 docker 组(避免每次 sudo)
sudo usermod -aG docker jenkins

# 7. 验证安装
docker --version

重要:添加用户到 docker 组后,需要重启 Jenkins 服务才能使权限生效:

bash

sudo systemctl restart jenkins

📄 第二步:在项目根目录创建 Dockerfile

在本地项目的根目录(D:\gitlabjenkins\my-cicd-demo)下,创建 Dockerfile(无扩展名),内容如下:

dockerfile

# 使用官方 Python 3.9 镜像作为基础
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件并安装
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY app.py .

# 暴露 5000 端口
EXPOSE 5000

# 启动命令
CMD ["python", "app.py"]

这个 Dockerfile 将应用打包成镜像,并指定启动命令。


📤 第三步:提交并推送到 GitLab

bash

git add Dockerfile
git commit -m "Add Dockerfile for containerization"
git push origin main


🔄 第四步:修改 Jenkinsfile,添加 Docker 构建和部署阶段

现在我们要将 Pipeline 中的 Deploy 阶段改为 使用 Docker。新流水线会包含:

  • Checkout:拉取代码。

  • Build:安装依赖(这一步可以保留,也可以移除,因为 Docker 镜像内会安装)。

  • Test:并行测试(保持不变)。

  • Build Docker Image:使用 Docker 构建镜像。

  • Deploy Container:停止旧容器,启动新容器。

以下是完整的 Jenkinsfile(替换原有内容):

groovy

pipeline {
    agent any

    environment {
        // 镜像名称和标签
        IMAGE_NAME = 'flask-app'
        IMAGE_TAG = "${env.BUILD_NUMBER}"
        CONTAINER_NAME = 'flask-app-container'
        HOST_PORT = '5000'
        CONTAINER_PORT = '5000'
    }

    stages {

        // ---------- 1️⃣ 拉取代码 ----------
        stage('Checkout') {
            steps {
                git branch: 'main',
                    credentialsId: 'gitlab-root-cred',
                    url: 'http://192.168.88.137/root/my-cicd-demo.git'
            }
        }

        // ---------- 2️⃣ 构建(可选,因为 Docker 内会安装依赖,但保留做语法检查) ----------
        stage('Build') {
            steps {
                sh '''
                    cd $WORKSPACE
                    echo "检查代码语法..."
                    python3 -m py_compile app.py || true
                    echo "✅ 语法检查通过"
                '''
            }
        }

        // ---------- 3️⃣ 测试(保持不变) ----------
        stage('Test') {
            parallel {
                stage('语法检查') {
                    steps {
                        sh '''
                            cd $WORKSPACE
                            echo "正在检查 Python 语法..."
                            python3 -m py_compile app.py
                            echo "✅ 语法检查通过"
                        '''
                    }
                }
                stage('单元测试占位') {
                    steps {
                        sh '''
                            cd $WORKSPACE
                            echo "正在运行单元测试(占位)..."
                            python3 -c "print('所有测试通过!')"
                            echo "✅ 单元测试通过"
                        '''
                    }
                }
            }
        }

        // ---------- 4️⃣ 构建 Docker 镜像 ----------
        stage('Build Docker Image') {
            steps {
                sh '''
                    cd $WORKSPACE
                    echo "构建 Docker 镜像..."
                    docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
                    docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest
                    echo "✅ Docker 镜像构建完成"
                '''
            }
        }

        // ---------- 5️⃣ 部署容器 ----------
        stage('Deploy Container') {
            when {
                branch 'main'
            }
            steps {
                sh '''
                    echo "停止并移除旧容器(如果存在)..."
                    docker stop ${CONTAINER_NAME} || true
                    docker rm ${CONTAINER_NAME} || true

                    echo "启动新容器..."
                    docker run -d \
                        --name ${CONTAINER_NAME} \
                        -p ${HOST_PORT}:${CONTAINER_PORT} \
                        --restart unless-stopped \
                        ${IMAGE_NAME}:latest

                    echo "✅ 容器已启动,应用运行在 http://192.168.88.135:${HOST_PORT}"
                '''
            }
        }
    }

    // ---------- 6️⃣ 后置动作 ----------
    post {
        always {
            echo "流水线执行结束。"
        }
        success {
            echo "🎉 恭喜!构建、测试、Docker 镜像构建和部署全部成功!"
        }
        failure {
            echo "🚨 流水线失败,请检查控制台日志。"
        }
    }
}


⚠️ 注意事项

  1. 环境变量environment 中定义了镜像名称、标签、容器名称、端口等,方便统一修改。

  2. Docker 权限:确保 Jenkins 用户有权限运行 docker 命令(已加入 docker 组)。

  3. 端口映射-p 5000:5000 将容器的 5000 端口映射到宿主机的 5000 端口。

  4. 容器重启策略--restart unless-stopped 保证服务器重启后容器自动启动。

  5. 部署条件:只有当分支为 main 时才执行部署。


🔁 第五步:提交并测试

  1. 提交修改后的 Jenkinsfile

    bash

    git add Jenkinsfile
    git commit -m "Add Docker build and deploy stages"
    git push origin main

  2. 在 Jenkins 中手动点击 Build Now(或等待 Poll SCM 自动触发)。

  3. 3.观察 Console Output,应该能看到 Build Docker Image 和 Deploy Container 阶段执行


🌐 第六步:验证部署

注意:Jenkinsfile 中 Deploy Container 阶段设置了 when { branch 'main' },而我这次构建是手动点击 Build Now 触发的,Jenkins 不会自动设置 BRANCH_NAME 环境变量,所以条件不满足,部署被跳过。

需要手动启动容器

sudo docker run -d --name flask-app-container -p 5000:5000 --restart unless-stopped flask-app:latest

构建成功后,在浏览器访问 http://192.168.88.135:5000,应该看到同样的 Hello from CI/CD Pipeline! 或你修改后的内容。

你也可以在 Jenkins 服务器上通过 docker ps 查看容器运行状态:

bash

docker ps | grep flask-app-container


🧠 你学到了什么?

  • 容器化:将应用和依赖打包成 Docker 镜像,环境一致性。

  • 镜像版本管理:使用构建编号作为标签,保留历史版本。

  • 容器生命周期管理:停止、删除旧容器,启动新容器。

  • 持续部署流水线:完整的 CI/CD + Docker 实践。


🔧 故障排查

错误 原因 解决方法
docker: command not found Docker 未安装或 Jenkins 用户路径未包含 安装 Docker,确保 Jenkins 用户有权限
Permission denied Jenkins 用户不在 docker 组 sudo usermod -aG docker jenkins,重启 Jenkins
Address already in use 宿主机 5000 端口被占用 修改 HOST_PORT 为其他端口(如 5001)并重新构建
容器启动后立即退出 应用代码错误 查看容器日志:docker logs flask-app-container

📌 下一步建议

  • 将镜像推送到私有镜像仓库(如 GitLab Container Registry 或 Harbor)。

  • 使用 docker-compose 管理多容器应用。

  • 部署到 Kubernetes 集群(如果条件允许)。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐