# 3.6dockerfile语法梳理及最佳实践

在本节中，我们对常用的Dockerfile语法进行梳理。

## FROM

```
FROM scratch # 制作base image
FROM centos     # 使用base image
```

> 注意：尽量使用官方的image作为base image

## LABEL

```
LABEL maintainer="moluo@163.com"        # 镜像作者
LABEL version="1.0"                        # 镜像版本
LABEL description="this is description"    # 镜像描述
```

> 建议：Metadata不可少

## ENV

```
ENV MYSQL_VERSION 5.6
RUN apt-get install -y mysql-server="&{MYSQL_VERSION}" \
    && rm -rf/var/lib/apt/lists/*    # 引用常量
```

> 建议：尽量使用ENV增加可维护性

## WORKDIR

```
WORKDIR /root # 用于切换文件夹，如果没有会自动创建root目录
```

> 强制：用WORKDIR ，不要使用RUN cd
>
> 建议：为了清晰，尽量使用绝对目录

## ADD and COPY

```
ADD hello /            # 添加hello文件到根目录
ADD hello test/     # 添加hello文件到test目录
ADD test.tar.gz /     # 添加到根目录并解压

COPY hello /        # 添加hello文件到根目录
```

> 注意：大部分情况，copy优于ADD
>
> 注意：ADD除了COPY还有额外功能（解压）
>
> 提示：添加远程文件/目录请使用crul或者wget

## RUN

执行命令并创建新的Image Layer

```
RUN yum update \
    && yum install -y vim python-dev    # 反斜线换行

RUN apt-get update \
    && apt-get install -y perl pwgen --no-install-ecommends
    && rm -rf /var/lib/apt/lists/*        # 注意清理缓存
```

> 建议一：为了美观，复杂的RUN请使用反斜线换行
>
> 建议二：每运行一次RUN，image都会新建一层，为了避免无用分层，合并多条命令成一行

## CMD

CMD：设置容器启动后`默认`执行的命令和参数

```
FROM centos
CMD echo "hello Docker"
```

> 注意一：如果docker run指定了其他命令，CMD命令被忽略。
>
> ```bash
> docker run -it moluo/hello-cmd /bin/bash     # 将不会执行CMD打印hello Docker
> ```
>
> 注意二：如果定义了多个CMD，只有最后一个会执行
>
> ```
> FROM centos
> CMD echo "hello world"                        # CMD echo "hello world"将不会执行
> CMD echo "hello Docker"
> ```

## ENTRYPOINT

ENTRYPOINT：设置容器启动时运行的命令

让容器以应用程序或者服务的形式运行

不会被忽略，一定会执行

```
COPY docker-entrypoint.sh /user/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 27017
CMD ["mongod"]
```

## VOLUME and EXPOSE

储存和网络

后期补充

## Shell格式和Exec格式

Shell格式

```bash
RUN apt-get install -y vim
CMD echo "hello docker"
ENTRYPOINT echo "hello docker"
```

Exec格式

```bash
RUN [ "apt-get","install","-y","vim"]
CMD ["/bin/echo","hello docker"]
ENTRYPOINT["/bin/echo","hello docker"]
```

Shell格式示例

```
FROM centos
ENV name Docker
ENTRYPOINT echo "hello $name"
```

Exec格式正确示例

```
FROM centos
ENV name Docker
ENTRYPOINT ["/bin/bash","-c","echo hello $name"]
```

> Exec格式错误示例一
>
> ```
> FROM centos
> ENV name Docker
> ENTRYPOINT ["/bin/echo","hello $name"]
> ```
>
> > 错误原因：$name不会被视为一个变量。若想要将其识别为变量，可采用`["/bin/bash","-c","..."]`
>
> Exec格式错误示例二
>
> ```
> FROM centos
> ENV name Docker
> ENTRYPOINT ["/bin/bash","-c","echo","hello $name"]
> ```
>
> > 错误原因：使用”/bin/bash“时，”echo hello $name“应该视为一个参数，而不是两个

## 补充

官方Image Dockerfile：[Docker-library](https://github.com/docker-library/docker)

## 参考

[Dockerfile reference](https://docs.docker.com/v17.09/engine/reference/builder/#escape)
