jenkins嵌套问题
问题解析
你提到 Jenkins 本身是通过 Docker 容器启动的,然后在 Jenkins 的 Docker 容器中再运行 Docker 命令来构建 Node.js 项目。这种情况下,确实会涉及嵌套 Docker 容器(即 Docker-in-Docker,简称 DinD)和嵌套挂载的问题。以下是详细分析和解决方案:
1. 嵌套 Docker 容器(DinD)的问题
- 问题描述:
- Jenkins 容器本身运行在 Docker 中,而你在 Jenkins 容器内又运行 Docker 命令(如
docker build
)。 - 这会导致 Docker 守护进程在 Jenkins 容器内运行,形成嵌套的 Docker 环境。
- Jenkins 容器本身运行在 Docker 中,而你在 Jenkins 容器内又运行 Docker 命令(如
- 潜在问题:
- 资源消耗:嵌套 Docker 会增加资源占用(CPU、内存)。
- 复杂性:需要额外配置 Docker-in-Docker(如共享 Docker 套接字或启动 DinD 服务)。
- 安全性:嵌套 Docker 可能带来安全风险(如权限提升)。
2. 嵌套挂载的问题
- 问题描述:
- 你想将 Jenkins 容器内构建的静态文件挂载到宿主机目录(如
./dist
)。 - 由于 Jenkins 容器本身已经挂载了宿主机目录(通过
-v
参数),嵌套挂载可能导致路径混乱或权限问题。
- 你想将 Jenkins 容器内构建的静态文件挂载到宿主机目录(如
- 潜在问题:
- 路径冲突:宿主机目录和 Jenkins 容器内的目录可能不一致。
- 权限问题:Jenkins 容器内的用户可能没有权限写入宿主机目录。
3. 解决方案
以下是几种可行的解决方案,避免嵌套 Docker 和嵌套挂载:
方案 1:直接在 Jenkins 容器中运行 Docker 命令(共享 Docker 套接字)
- 原理:
- 将宿主机的 Docker 套接字(
/var/run/docker.sock
)挂载到 Jenkins 容器中。 - Jenkins 容器内的 Docker 命令会直接调用宿主机的 Docker 守护进程,而不是嵌套的 Docker。
- 将宿主机的 Docker 套接字(
- 步骤:
- 启动 Jenkins 容器时,挂载 Docker 套接字:
1
2
3
4
5
6docker run -d \
-p 8080:8080 \
-p 50000:50000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts - 在 Jenkins Pipeline 中直接运行 Docker 命令:
1
2
3
4
5
6
7
8
9
10
11
12
13pipeline {
agent any
stages {
stage('Build') {
steps {
sh '''
docker build -t my-node-app .
docker run --rm -v $(pwd)/dist:/app/dist my-node-app bash -c 'cp -r /app/build/* /app/dist'
'''
}
}
}
}
- 启动 Jenkins 容器时,挂载 Docker 套接字:
- 优点:
- 无需嵌套 Docker,性能更好。
- 构建的静态文件可以直接挂载到宿主机目录。
- 缺点:
- 安全性较低(Jenkins 容器可以访问宿主机的 Docker 守护进程)。
方案 2:使用 Jenkins 的 Docker 插件(推荐)
- 原理:
- 使用 Jenkins 的
Docker Pipeline
插件,通过 Jenkins 节点(Agent)运行 Docker 命令。 - Jenkins 节点可以是宿主机或其他容器,避免嵌套。
- 使用 Jenkins 的
- 步骤:
- 安装 Jenkins 的
Docker Pipeline
插件。 - 配置 Jenkins 节点(Agent)为宿主机或其他容器。
- 在 Jenkinsfile 中使用
docker
步骤:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18pipeline {
agent any
stages {
stage('Build') {
steps {
script {
docker.image('node:18-alpine').inside('-v ${WORKSPACE}/dist:/app/dist') {
sh '''
npm install -g pnpm
pnpm install
pnpm run build:prod
'''
}
}
}
}
}
}
- 安装 Jenkins 的
- 优点:
- 无需嵌套 Docker,逻辑清晰。
- 插件管理方便,安全性较高。
- 缺点:
- 需要配置 Jenkins 节点。
方案 3:在宿主机上运行 Docker 命令(通过 Jenkins 脚本)
- 原理:
- Jenkins 容器通过
ssh
或直接调用宿主机命令来运行 Docker 命令。
- Jenkins 容器通过
- 步骤:
- 确保 Jenkins 容器可以访问宿主机(如通过
ssh
或共享脚本)。 - 在 Jenkins Pipeline 中调用宿主机命令:
1
2
3
4
5
6
7
8
9
10
11
12pipeline {
agent any
stages {
stage('Build') {
steps {
sh '''
ssh user@host "cd /path/to/project && docker build -t my-node-app . && docker run --rm -v /path/to/dist:/app/dist my-node-app bash -c 'cp -r /app/build/* /app/dist'"
'''
}
}
}
}
- 确保 Jenkins 容器可以访问宿主机(如通过
- 优点:
- 完全避免嵌套 Docker。
- 缺点:
- 配置复杂,依赖宿主机环境。
4. 推荐方案
- 推荐方案 1 或 方案 2:
- 如果 Jenkins 容器和宿主机在同一台机器上,推荐方案 1(共享 Docker 套接字),简单直接。
- 如果需要更灵活的节点管理,推荐方案 2(Docker Pipeline 插件),安全性更高。
- 避免嵌套 Docker:
- 嵌套 Docker 会增加复杂性和资源消耗,应尽量避免。
5. 静态文件挂载的注意事项
- 挂载宿主机目录:
- 确保 Jenkins 容器或 Docker 命令有权限写入宿主机目录。
- 使用绝对路径(如
/path/to/dist
),避免路径混淆。
- 示例:
1
2
3
4
5
6
7docker.image('node:18-alpine').inside('-v /host/path/to/dist:/app/dist') {
sh '''
npm install -g pnpm
pnpm install
pnpm run build:prod
'''
}
总结
- 避免嵌套 Docker:通过共享 Docker 套接字或使用 Jenkins 插件实现。
- 静态文件挂载:使用绝对路径,确保权限正确。
- 推荐方案:
- 简单场景:共享 Docker 套接字(方案 1)。
- 复杂场景:Docker Pipeline 插件(方案 2)。
通过以上方法,你可以高效地在 Jenkins Docker 容器中构建 Node.js 项目,并避免嵌套构建和挂载的问题。
目标
- 在 Jenkins 流水线中拉取 Git 仓库中的 Node.js 应用代码。
- 根据项目根目录中的
Dockerfile
构建镜像,并将构建的静态文件输出到工作目录/app/dist
。 - 基于构建的静态文件启动一个 Web 服务器(如 Nginx)。
1. Dockerfile 示例
假设你的项目是一个 Vite + Vue 应用,Dockerfile
可以如下编写:
1 | # 第一阶段:构建静态文件 |
说明
- 第一阶段:使用
node:18-alpine
构建项目,生成静态文件到/app/dist
。 - 第二阶段:使用
nginx:alpine
作为 Web 服务器,将静态文件复制到/usr/share/nginx/html
。 - 暴露端口:Nginx 默认监听
80
端口。
2. Jenkins Pipeline 脚本(Jenkinsfile)
以下是一个完整的 Jenkinsfile
示例,展示如何在 Jenkins 中拉取 Git 代码、构建 Docker 镜像并启动 Web 服务器:
1 | pipeline { |
说明
Checkout
阶段:- 使用
git
步骤拉取 Git 仓库代码。 - 替换
https://github.com/your-username/your-repo.git
为你的仓库地址。
- 使用
Build Docker Image
阶段:- 使用
docker.build
根据Dockerfile
构建镜像。 - 镜像名称为
my-node-app
。
- 使用
Run Web Server
阶段:- 使用
docker.image(...).run
启动容器,并将宿主机的8080
端口映射到容器的80
端口。 - 访问
http://localhost:8080
即可查看静态文件。
- 使用
post
阶段:cleanWs()
:清理工作空间(可选)。
3. 运行和验证
构建和运行:
- 将
Jenkinsfile
和Dockerfile
提交到 Git 仓库。 - 在 Jenkins 中配置流水线任务,选择 “Pipeline script from SCM” 并指定 Git 仓库地址。
- 运行流水线任务,Jenkins 会自动拉取代码、构建镜像并启动 Web 服务器。
- 将
验证:
- 访问
http://localhost:8080
(或 Jenkins 容器映射的端口),查看静态文件是否正确加载。
- 访问
4. 注意事项
Docker 权限:
- 确保 Jenkins 用户有权限运行 Docker 命令(参考前面的权限配置)。
端口映射:
- 如果宿主机
8080
端口被占用,可以修改-p 8080:80
为其他端口(如-p 8081:80
)。
- 如果宿主机
静态文件路径:
- 确保
vite.config.js
或vue.config.js
中的base
配置正确(如设置为./
或空字符串),以避免资源路径问题。
- 确保
通过以上配置,你可以在 Jenkins 中实现完整的 Node.js 应用构建和 Web 服务器启动流程。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 adong的博客!
评论