文章目录
- 前言
- 一、前期的准备工作
- 二、上手构建一个简单的镜像
- 三、DcokerFile
- 1 指令总览
- 2 指令详情
- 四、Dockerfile文件规范
- 五、docker运行build时发生了什么?
- 六、调试手段
- 1. 修改镜像打包后,如何验证新内容已更新至镜像
- 七、Dockerfile优化方案
前言
docker构建镜像有三种方式 Dockerfile
文件名首字母约定要大写。大多数工具(包括Docker CLI和Docker Compose)都会默认查找名为"Dockerfile"的文件来构建镜像。
使用命令行构建
适合临时构建,一般就是在项目开发初期,环境没有搭建完善,这时候临时需要一个镜像包,可以直接通过命令打给他。使用Dockerfile文件
官方推荐这种方式,配置好dockerfile文件之后,docker根据Dockerfile文件构建使用脚本调用docker 的 api构建
一般在CI/CD的时候都会使用脚本,调用Docker api配合dockerfile文件实现一键部署,这种方式比较灵活可以配合脚本文件自由实现自己的定制操作,但前期准备工作比较大容器构建
通过在容器中执行commit命令构建
一、前期的准备工作
- 一台contos操作机器
cat /etc/os-release # 查看操作系统的版本信息# out system info NAME="OpenCloudOS" # 这个是腾讯的兼容contos8操作系统,可以看作contos8 VERSION="8.8" ID="opencloudos"
- 安装好docker,这里我的docker版本为
如果你还不了解如何安装docker,我的这篇文章可能会对你有帮助Docker version 25.0.4, build 1a576c5
二、上手构建一个简单的镜像
-
初始化一个docker项目
mkdir /demo cd /demo npm init -y
初始化项目结构
demo |-src | |-index.js |-Dockerfile |-package.json
-
index.js 内容
const { createServer } = require("http"); const server = createServer(); server.listen(8080, () => {console.log("启动成功!"); });
-
Dockerfile文件内容
FROM node WORKDIR /src COPY . . RUN npm install --production CMD ["node", "src/index.js"]
-
执行命令打包
# 进入目录 dockerfile同级目录 cd /demo# 执行构建镜像 docker build -t "cs:1.0.0" . # -t:为镜像打上标签 cs:为镜像名 .:为当前目录
-
查看打包结果
docker images
三、DcokerFile
Dockerfile是本质来讲是一个文本文件,内部包含了一条条指令,能够使docker根据上面的指令定制构建镜像(Image)
1 指令总览
命令 | 列子 |
---|---|
FROM | 指定镜像的依赖 可以有多条。但多个相连的FROM指令只会执行最后一个。详情在 #指令详情 中说明 |
RUN | 指定运行命令 RUN ls ./ 相当于在镜像 构建目录下的shell窗口中执行ls ./ 命令 |
COPY | 将本地文件copy到镜像 支持正则表达式拷贝( COPY /local? . )所有匹配local? 的文件目录都会拷贝 |
ADD | 同COPY一样,但是更强大(支持更多的文件类型的拷贝 如网络文件的下载后拷贝, ADD http://xxxx/test.tar.gz /test/ )拷贝归档文件( .tar .gz 等)文件会在复制到镜像的过程中自动解压 |
USER | 指定Dockerfile后续命令使用哪个用户身份执行,默认root。 |
WORKDIR | 目录切换指令,类似shell的cd |
ENV | 指定运行容器时的环境变量 |
CMD | 用来指定由镜像创建的容器启动后执行的命令,比如你想让容器启动服务,就可以通过它设置 只能有一条出现多条则最后一条生效 |
ENTRYPOINT | 作用CMD类似,但与CMD同时使用时可能会将CMD的值最为参数 |
2 指令详情
-
FROM
当遇到FROM指令时,docker会在本地库寻找对应的镜像库
如果没有发现,则通过docker pull拉取,如果存在则使用本地下载的镜像库
版本号不一致的镜像是不同的node:16.16.20
和node:18.18.1
是两个不同镜像FROM node; # 当前镜像依赖node镜像
FROM可以多条使用,但不能连续使用。多条使用一般用于Docker多阶段构建
# 第一阶段 FROM node:14 AS build # 使用node:14镜像 设置构建阶段别名提供给COPY --from 访问 WORKDIR /app COPY . . RUN npm install RUN npm run build # 操作完成产生打包结果文件# 第二阶段 FROM node:14-alpine # 使用node:14-alpine镜像此时将会替换掉之前node:14镜像 WORKDIR /app COPY --from=build /app . # 复制 build构建阶段下的/app目录到镜像目录 CMD ["node", "index.js"]
连续使用,只有最后一条生效
FROM node:16.16.1 FROM node:18.18.1 # 此条生效
-
RUN
设置镜像执行命令RUN npm i # 相当于在镜像目录的shell窗口中执行npm i
-
COPY
将本地文件活目录拷贝至镜像目录- 只能将本地文件拷贝到镜像,不能将镜像文件拷贝到本地
- 拷贝的源路径只能处在当前构建镜像的上下文的目录下,通过
docker build
命令的url
参数指定构建上下文 - 语法:
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY /local /app # 将本地/local 下的所有文件、目录拷贝到镜像/app目录下COPY /local1 /local2 /app # 可以由多个源路径,它会将本地/local1 和 /local2下的所有文件、目录拷贝到镜像/app下COPY /local* /app # 支持通配符,将本地/local1 /local2 /local3 /local...等符合匹配规则的路径复制到镜像/app下COPY --chown=user1:group1 /local /app # 本地文件复制到镜像后,将它归属设置为user1用户和group1组
-
ADD
也是用于将本地文件拷贝到镜像中,但是它与COP有些不同- 可以COPY远程文件,类似于 自动下载远程文件-》移动至镜像
- 在拷贝归档文件
(.tar等压缩文件)
时会在拷贝至镜像的过程中自动解压
ADD http://www.test.com/test.tar . # 会自动自动解压到镜像文件
请注意!即便ADD命令能够自动解压
tar
等压缩文件,官方的Dockerfile使用规范仍推荐使用COPY复制压缩文件,配合解压命令解压。这样在语法上更加明确// Dockerfile COPY test.tar . RUN tar -vxf ./test.tar # 执行tar -vxf解压
-
USER
USER [用户名]:[用户组]
-
WORKDIR
修改镜像当前上下文目录,类似linux cd命令。WORKDIR ./src # 之后的指令上下文都是./src RUN ls ./
-
CMD
和ENTRYPOINT
CMD和ENTRYPOINT指令都是用来指定镜像创建的容器启动时执行的指令,相当于在容器启动时开启了一个shell窗口,然后运行CMD和ENTRYPOINT的指令参数。它们有相同的语法:
# CMD <shell 命令> CMD echo 11;// 容器启动执行 echo 11 ENTRYPOINT echo 11; // 同上# CMD ["<可执行文件或命令>","<param1>","<param2>",...] CMD ["node","-inspect","index.js"] # 相当于shell窗口执行 node -inspect index.js ENTRYPONT ["node","-inspect","index.js"] // 同上
如果
CMD
和ENTRYPONT
同时使用ENTRYPONT
是数组CMD ["--inspect","index.js"] # or CMD echo 11ENTRYPONT ["node"]
CMD
会作为ENTRYPONT
的参数,相当于容器启动时执行node --inspect index.js # or node echo 11
ENTRYPONT
不是数组,ENTRYPONT
无法接受参数最终只执行ENTRYPONT
最终容器执行CMD echo 或者 CMD ["hello"] ENTRYPONT echo
echo
-
ENV
定义环境变量,定义之后就可以在后面的任何命令中使用。
并且连续多次定义ENV的话,它将会叠加
而不是覆盖
,下面的例子可以访问3个
的环境变量
ENV $var1 value1; # 定义单个 ENV $var2=value2 $var3=$value3 # 定义多个RUN echo $var1 COPY $var1 /app ADD ./src/$var1 /app CMD echo $var1 WORKDIR $var1
它会在容器内部存在,你可以在代码中访问环境变量
console.log(process.env.$var1);
四、Dockerfile文件规范
- Dockerfile文件名首字母最好约定大写,因为大多数工具(包括Docker CLI和Docker Compose)都会默认查找名为"Dockerfile"的文件来构建镜像。如果你想指定文件名
docker build -f myDockerfile -t my-image .
- Dockerfile指令不区分大小写,但是为方便和参数做区分,通常指令使用大写字母。
from node # 这样也可以FROM node # 这样更清晰
- Dockerfile中指令按顺序从上至下依次执行。
- Dockerfile中第一个非注释行必须是FROM指令,用来指定制作当前镜像依据的是哪个基础镜像。
- Dockerfile中需要调用的文件必须跟Dockerfile文件在同一目录下,或者在其子目录下,父目录或者其它路径无效
五、docker运行build时发生了什么?
当你运行docker build命令构建Docker镜像时,Docker会执行以下步骤:
-
读取Dockerfile
:Docker首先读取Dockerfile,解析其中的指令。 -
加载构建上下文
:Docker将Dockerfile所在的目录(或者你在docker build命令中指定的其他目录)作为构建上下文,将其所有文件和目录发送到Docker守护进程。 -
执行Dockerfile中的指令
:Docker按照Dockerfile中的顺序,一条一条地执行指令。每执行一条指令,Docker都会创建一个新的容器,运行指令,然后提交这个容器为一个新的镜像层。这个过程会重复,直到所有指令都被执行。 -
对于FROM指令
,Docker会加载指定的基础镜像。如果基础镜像不存在,Docker会尝试从Docker Hub或其他配置的镜像仓库下载它。 -
对于RUN指令
,Docker会在当前的镜像上启动一个新的容器,然后在这个容器中运行指令。完成后,Docker会提交这个容器为一个新的镜像层。 -
对于COPY和ADD指令
,Docker会从构建上下文中复制文件或目录到镜像中。完成后,Docker会创建一个新的镜像层。 -
对于CMD和ENTRYPOINT指令
,Docker会设置镜像的默认命令和/或入口点。这些指令不会创建新的镜像层。
保存最终的镜像:当所有指令都被执行后,Docker会保存最终的镜像。如果你在docker build命令中指定了镜像的标签,Docker会将这个标签应用到最终的镜像。
以上就是docker build命令构建Docker镜像的基本过程。这个过程可能会根据你的Dockerfile和构建上下文的具体内容有所不同。
六、调试手段
1. 修改镜像打包后,如何验证新内容已更新至镜像
通过镜像创建容器
$ docker create --name containerName imageName
进入容器
docker exec -it containerName /bin/bash # 请注意是容器名不是容器id
在容器内部查看对应的目录结构是否存在
ls ./ # 检查更新的相应的目录结构是否存在
七、Dockerfile优化方案
- Dockerfile中的指令没执行一条就会多构建一个镜像,所以尽量合并执行的执行(run、copy比较多)
RUN npm i; RUN npm run dev; # MERGE RUN npm i && npm run dev
- 多阶段构建,最后生成的镜像包会比较小
最后的镜像包中只包含node:14-alpone镜像# 阶段1:构建阶段 FROM node:14 AS build WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build# 阶段2:运行阶段 FROM node:14-alpine WORKDIR /app COPY --from=build /app/dist ./dist COPY package*.json ./ RUN npm install --only=production CMD ["node", "dist/index.js"]