> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/devops/container/docker/dockerfile-xiang-jie.md).

# Dockerfile详解

### FROM

1. 指定基础镜像，必须是第一条指令
2. 如果没有基础镜像，则以 `FROM scratch` 开头
3. 这条指令写入的东西为镜像首层

**写法**： `FROM <images>:<tag>`

### USER

1. 设置容器启动用户，可以是用户名或UID
2. 如果容器以daemon用户运行，那么`RUN`, `CMD`, `ENTRYPOINT` 都会以这个用户运行

**语法**：

```
USER daemo
USER UID
```

### ENV 指令

1. 设置容器环境变量

**语法**：

```
ENV <key> <value>
ENV <key>=<value> ... // 可以设置多个
```

### WORKDIR

1. 设置工作目录，对 `RUN`, `CMD`, `ENTRYPOINT`, `COPY`, `ADD` 生效。
2. 如果不存在则会创建， **也可以设置多次**
3. 可以指定绝对路径和相对路径，这里的路径是指容器中的路径
4. `WORKDIR` 会解析环境变量
5. 可以理解为cd 命令

**语法**：

```
WORKDIR /path/to/workdir
WORKDIR path
WORKDIR /$PATH
```

### RUN

1. RUN命令在容器的可写入层执行命令，并commit容器为新的镜像。
2. 上一步RUN命令生成的镜像被接下来RUN使用，每次RUN命令都会重新生成一个新的镜像
3. 可以通过链式输入命令以减少创建镜像层的数量
4. 镜像层过多，会导致镜像过大
5. 可以通过`docker history 镜像名称` 查看镜像层
6. CMD 命令通常在文件最末尾处

**写法**： `RUN <command>` 或者 `RUN ["executable", "param1", "param2"]`

第二种方式类似函数调用，在linux下，自动使用脚本解析器： `/bin/sh -c` ，windows下则是 `cmd /S /C`

为了减少镜像层数，可以使用链式命令 `RUN command1&& command2` 并且，可以使用 `\反斜线` 换行。

### CMD

1. CMD 命令用于指定启动容器时的命令
2. 如果不指定CMD命令，则将使用基础镜像提供的默认命令
3. 因为是启动命令，所以在创建容器时不会执行，只有启动容器时才会执行
4. 也可以使用exec形式指定要执行的命令
5. 可以使用`docker run -ti centos:7 echo "hello world"` 的方式，替换默认命令
6. 写法：`CMD ["echo", "hello"]`
7. 只可以写一条，如果写多条，只有最后一条有效

### ENTRYPOINT

功能是启动时默认命令，与CMD类似

**语法**：

```
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
```

**相同点**：

1. 只可以写一条，如果有多条，只有最后一条有效
2. 容器启动时相同

**不同点**：

1. ENTRYPOINT 指令不会被运行时command覆盖

### COPY

1. COPY 指令从build上下文复制文件或者文件夹到容器文件系统
2. 例如： `COPY xxx.jar /data/jar/`

### ADD

1. ADD指令和COPY指令类似，可以将文件复制到容器中
2. 也可以从Internet下载文件并复制到容器中
3. 并且，ADD命令可以自动解压压缩文件
4. 通常使用COPY指令，除非明确需要ADD指令

```
ADD test relativeDir/
ADD test /relativeDir
ADD http://example.com/foobar /
```

### VOLUME

1. 可实现挂载
2. 容器使用的是AUFS，这种文件系统不能持久化数据，当容器关闭后，所有的更改都会丢失
3. 数据资源推荐挂载

**语法**：

```
VOLUME ["/data"]
VOLUME /var/log
VOLUME /var/log /var/db
```

### LABEL

LABEL 指令为镜像指定标签， 一个镜像可以拥有多个标签

**语法**：

```
LABEL <key>=<value> <key>=<value>

或

LABEL <key>=<value>
LABEL <key>=<value>
```

> 可以使用 反斜杠换行，或者多个LABEL 标签

### MAINTAINER

指定作者：

```
MAINTAINER <name>
```

### ARG

1. 设置变量命令
2. ARG命令定义一个变量，在docker build 使用Dockerfile构建镜像时，使用 `--build-arg <varname>=<value>` 来指定变量值
3. 可以给变量设置默认值
4. 如果用户在build镜像时指定了一个参数没有定义在Dockerfile中，那么将有一个Warning`[Warning] One or more build-args [foo] were not consumed.`

**语法**：

```
FROM busybox
ARG user1=someuser
ARG buildno # 给默认值
```

### HEALTHCHECK

**语法**：

```
# 在容器内部运行一个命令进行健康检查
HEALTHCHECK [OPTIONS] CMD command

# 在基础镜像中取消健康检查命令
HEALTHCHECK NONE
```

OPTIONS 支持如下三个选项：

```
--interval=DURATION 两次检查默认的时间间隔为30秒
--timeout=DURATION 健康检查命令运行超时时长，默认30秒
--retries=N 当连续失败指定次数后，则容器被认为是不健康的，状态为unhealthy，默认次数是3
```

CMD命令的返回值，决定了本次健康检查是否成功：

```
0: success - 表示容器是健康的
1: unhealthy - 表示容器已经不能工作了
2: reserved - 保留值
```

比如：

```
HEALTHCHECK --interval=5m --timeout=3s \CMD curl -f http://localhost/ || exit 1
```

HEALTHCHECK 指令只可以出现一次，如果出现多次，最后一个生效。

### ONBUILD

1. 只对当前镜像的子镜像生效

比如当前镜像为A，在Dockerfile种添加： `ONBUILD RUN ls -al` 这个 `ls -al` 命令不会在A镜像构建或启动的时候执行

此时有一个镜像B是基于A镜像构建的，那么这个ls -al 命令会在B镜像构建的时候被执行。

**语法**：

```
ONBUILD [INSTRUCTION]
```

### STOPSIGNAL 指令

**语法**：

```
STOPSIGNAL signalSTOPSIGNAL
```

命令是的作用是当容器推出时给系统发送什么样的指令

### 使用Dockerfile构建image

cd 到 Dockerfile 文件所在路径执行 `docker build -t 镜像名称:版本 .` 即可构建镜像

#### 镜像构建缓存

![file](/files/2wIxK0kUOylKv1fzbHNG)

如果新的构建版本中，某些指令与之前的版本一致，则会使用缓存。

**造成的问题**：

1. 如果是`yum update -y` 命令，则不会执行，导致yum仓库源不是最新的

**解决办法**：

1. 保证命令前后不一致，可以使用链式命令
2. 使用 –no-cache=true`docker build -t centos . --no-cache=true`

#### 上传镜像至 DockerHub

1. docker login
2. docker push \[OPTIONS] NAME\[:TAG]

### 构建多CPU架构image

**创建以下dockerfile：**

```dockerfile
FROM alpine:latest

LABEL maintainer="yangsx.yangsx95@qq.com"

RUN apk update && \
        apk add git curl
```

**安装buildx：**

在mac与win的docker-desktop自带buildx，而linux需要安装：<https://github.com/docker/buildx>

**创建并使用一个新的builder（默认builder可构建的cpu架构类型少）：**

```shell
$ docker buildx create --use --name mybuilder
mybuilder
```

**查看所有的builder列表：**

```shell
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS  PLATFORMS
default * docker
  default default         running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
```

**查看当前使用的builder：**

```shell
$ docker buildx inspect --bootstrap
docker buildx inspect --bootstrap
Name:   mybuilder
Driver: docker-container

Nodes:
Name:      mybuilder0
Endpoint:  unix:///var/run/docker.sock
Status:    running
Platforms: linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
```

**使用buildx构建多cpu架构的镜像：**

```shell
# --platform 指定支持的cpu架构
docker buildx build --push --platform linux/arm/v7,linux/arm64/v8,linux/amd64 -t <image_name> .

=> 

docker buildx build --push --platform linux/arm/v7,linux/arm64/v8,linux/amd64 -t yangsx95/gitclient . 
```

\*\*成果：\*\*<https://hub.docker.com/repository/docker/yangsx95/gitclient>

### 多阶段构建

可以使用Dockerfile拉取环境镜像，分阶段构建最终镜像。比如一个java应用，依次经过三个阶段：

1. `git clone` 拉取代码
2. `mvn clean package` 打包
3. 放入tomcat容器

```dockerfile
FROM yangsx95/gitclient:latest as cloner
MAINTAINER "yangsx.yangsx95@qq.com"
WORKDIR /opt
# 拉取代码
RUN git clone https://github.com/lizhenliang/tomcat-java-demo.git

FROM maven:3.8-openjdk-8 as builder
MAINTAINER "yangsx.yangsx95@qq.com"
WORKDIR /opt
# 将cloner阶段的opt/tomcat-java-demo这个文件拷贝到当前阶段的/opt/路径下
COPY --from=cloner /opt/tomcat-java-demo/ /opt/
# 添加阿里云maven镜像
RUN echo '<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd"><localRepository>/usr/share/maven/ref/repository</localRepository><mirrors><mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>阿里云公共仓库</name><url>https://maven.aliyun.com/repository/public</url></mirror></mirrors></settings>' > /opt/settings.xml
# 执行构建
RUN mvn clean package --settings=/opt/settings.xml -Dmaven.test.skip=true

FROM tomcat:8-jdk8 as runner
MAINTAINER "yangsx.yangsx95@qq.com"
WORKDIR /usr/local/tomcat/webapps/
COPY --from=cloner /opt/  /opt/test
COPY --from=builder /opt/target/*.war /usr/local/tomcat/webapps/ROOT.war
```

> \--from可以指定名称，也可以指定索引，比如引用第一个阶段的结果，使用`--from=0`

将上述内容保存到`Dockerfile`文件中，使用下面的命令构建镜像：

```
docker build -t yangsx95/hello-girl .
```

测试：

```
docker run -p 8080:8080 --rm  yangsx95/hello-girl
```
