No.2 Dockerfile的基本使用

792 阅读6分钟

前言

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战

本文主要讲述Dockerfile的基本使用,简单地说,DockerFile其实就是一个脚本,把我们需要执行的指令都汇总到一起而已。

基本示例

在讲解Dockerfile的基本命令前,我先以 使用Flask框架构建Http服务器 为例,说明Dockerfile的基本用法。

1.目录结构:

.
├── Dockerfile
└── docker-test
    ├── flask_server.py
    └── requirements.txt

2.Dockerfile文件内容:

# 指定基础镜像
FROM python:3

# 标明作者
LABEL author="yangan"

# 设置工作目录,即进入容器后的默认路径
WORKDIR /data/docker-test

# 将宿主机的docker-test目录的内容拷贝到容器
ADD ./docker-test /data/docker-test

# 安装Python依赖
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

# 暴露端口
EXPOSE 10086

# 设置环境变量
ENV PYTHONIOENCODING=utf-8

# 执行Python脚本
CMD [ "python", "./flask_server.py" ]

3.flask_server.py 内容:

from flask import Flask

app = Flask(__name__)

@app.route('/', methods=['GET'])
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=10086, debug=True)

4.requirements.txt内容:

Click==7.0
Flask==1.1.1
itsdangerous==1.1.0
Jinja2==2.10.3
MarkupSafe==1.1.1
Werkzeug==0.16.0

5.根据Dockerfile创建镜像(注意有个点,代表当前目录):

docker build -t docker-test:v1 .

打包时的效果图 2019-11-12-21-50-47.png

打包成功效果图 2019-11-12-21-53-13.png

Dockerfile命令

FROM

语法:FROM <name>
该指令的作用是指定基础镜像。

在看其他人的Dockerfile时,我们可能会看到alpine版的镜像。为什么使用这个版本呢?以Ubuntu镜像为例,如果是普通的镜像,比如ubuntu:16.04,通常需要占几百M;而alpine版的镜像,比如ubuntu:alpine,只需要几十M,从镜像大小的角度看,alpine版的镜像是非常有优势的,但是凡事还是要看适用场景,如果你构建的镜像只运行单个服务,则使用 alpine版镜像 可能更好,如果需要运行多个服务,则使用普通版镜像更好,因为库更全,安装更顺手,具体区别可参见这里

MAINTAINER

语法:MAINTAINER <name>
该指令的作用是说明镜像制作者

WORKDIR

语法:WORKDIR <dir>
指定工作目录,即进入容器后的路径,相当于进入容器后执行 cd <dir>

ENV

语法:ENV <key>=<value>
指定环境变量,以此镜像创建的容器都会应用这些环境变量。

创建普通环境变量:ENV PYTHONIOENCODING=utf-8,相当于 export PYTHONIOENCODING=utf-8

创建PATH环境变量:ENV PATH="/root/.local/bin:${PATH}",相当于 export PATH="/root/.local/bin:${PATH}",在安装JDK时比较常用到。

注意这里有个陷阱,即 /root/.local/bin 必须使用绝对路径,且不能掺杂环境变量,如 $HOME/.local/bin,这样设置是无效的。

另一种设置环境变量的方式(不推荐):

# 1.首先写入到文件
echo 'export PATH="$PATH:/root/.local/bin"' >> ~/.bashrc

# 2.执行命令时先使用source命令使环境变量生效
docker exec -it ubuntu_test bash -c "source ~/.bashrc && make proto"

EXPOSE

语法:EXPOSE <port> [<port>...]
声明运行时容器提供服务端口

声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。 这个命令并不能像docker-compose一样绑定宿主机端口,因为docker-compose是直接生成容器,Dockerfile只是生成一个镜像,端口的绑定需在启动容器时进行。

VOLUME

语法:VOLUME ["<mountpoint>"]
在镜像内创建挂载点,并不会在宿主机创建挂载点,宿主机的挂载点是自动生成的。

既然需要在启动时才能绑定宿主机端口或者挂载宿主机某个目录, 那么还有必要在Dockerfile指定吗? www.cnblogs.com/51kata/p/52…

ADD & COPY

# 语法
ADD [--chown=<user>:<group>] <src>... <dest>   
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] (这种方式路径间需要有空格)

COPY命令:   
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] (这种方式路径间需要有空格)

1.如果<src>是文件且<dest>中不使用斜杠结束,则会将<dest>视为文件,<src>的内容会写入<dest>
2.如果<src>是文件且<dest>中使用斜杠结束,则会<src>文件拷贝到<dest>目录下。
3.如果<src>是目录, 则会将src目录的文件到容器dest目录,注意并不会拷贝src这个目录。

共同点:

  • 1.拷贝文件(只能拷贝上下文内的文件)
  • 2.可通过chown控制文件属性,如果没有该选项,拷贝的文件或文件夹默认属于root用户(uid和gid为0)

不同点:

  • COPY能把前一阶段构建的产物拷贝到另一个镜像中(multistage 场景)
  • ADD命令能解压压缩文件并把它们添加到镜像中
  • ADD命令能将远程文件拷贝到镜像中,类似wget
# 解压举例
WORKDIR /app
ADD test.tar.gz .

# 远程拷贝举例
ADD http://example.com/big.tar.xz /app

注意点:
1.绝对路径和相对路径

COPY test relativeDir/ # adds "test" to WORKDIR/relativeDir/ COPY test /absoluteDir/ # adds "test" to /absoluteDir/

2.chown选项只对Linux系统有效

RUN

语法:
RUN <command> (在Linux下默认在/bin/sh执行)(shell form)
RUN ["executable", "param1", "param2"]   (exec form)

执行命令的指令,后面接执行的命令,比如 RUN apt-get update

1.每次执行RUN命令,都会新建一个新的镜像层,为了减少镜像层的数量,一般都会将多条命令写在一起。

2.使用其他shell

RUN /bin/bash -c 'echo hello'
RUN ["/bin/bash", "-c", "echo hello"]

3.RUN ["executable", "param1", "param2"] 这种形式的RUN命令,参数必须用双引号包裹,因为解析的时候是被解析成JSON数组

4.RUN ["executable", "param1", "param2"] 这种形式的RUN命令, 注意 RUN [ "echo", "$HOME" ]RUN [ "sh", "-c", "echo $HOME" ]并不一样, RUN [ "echo", "$HOME" ]并不会调用shell,所以不会寻找环境变量。

CMD

语法:
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)

1.容器启动时执行的命令,但是一个Dockerfile中只能有一条CMD命令,多条则只执行最后一条CMD。

2.CMD主要用于容器时启动指定的服务,当Docker run command的命令匹配到CMD command时,会替换CMD执行的命令。

3.CMD 与 RUN并不一样, RUN命令执行时会生成一个新的镜像层,而CMD只是单纯执行命令而已

4.一个Dockerfile中只能有一条CMD命令,多条则只执行最后一条CMD.

5.exec form 形式的CMD命令,依然不会调用shell,与RUN命令一样。

参考

Dokcerfile官方文档
Docker Dockerfile 定制镜像
docker之Dockerfile实践
Docker设置PATH环境变量