Dockerfile详解

2021/07/12 914点热度 0人点赞 0条评论

什么是Dockerfile?

Dockerfile是一个用于构建镜像的文本文件,文本文件中包含了一条条构建镜像所需的指令和说明。

  • 固定格式;
  • 顺序执行(从上到下);
  • 通过docker build -f 来执行文件;
  • ‘#’为Dockerfile中的注释
  • Dockerfile镜像由只读层堆叠起来,每个层代表一个Dockerfile指令;
  • RUN 执行一条指令就新建一层,过多容易导致镜像过大

构建方式

经典构建

# 语法
docker build -f  具体dockerfile文件位置
docker build -t  指定存储新镜像的存储库和标签,-t 可以多个

示例:
# 使用当前目录的Dockerfile文件创建镜像,并打标签为yxkong/ubuntu:v1 
docker build -t yxkong/ubuntu:v1 .

# 使用远程的Dockerfile构建镜像
docker build github.com/creack/docker-firefox 

# 
docker build -f ./Dockerfile .

buildkit构建

18.09开始,docker添加的新的构建指令
构建方式

#在构建前必须设置环境变量
export DOCKER_BUILDKIT=1
# 然后执行build
docker build -f  具体dockerfile文件位置

带来的好处

  • 构建步骤优化(跳过已构建,直接执行未构建的)
  • 高效灵活的缓存(优先考虑缓存)
  • 并行构建独立的构建阶段;
  • 在构建之间仅增量传输构建上下文中已更改的文件;
  • 扩展了Dockerfile语法;

Dockerfile指令与结构

Dockerfile中支持的修改环境变量的指令

  • FROM 指定基准环境
  • ADD 将宿主的某个文件或目录挂到新建的镜像中(支持tar和url)
  • COPY 从宿主复制文件到新创建的镜像文件(主要是本地)
  • ENV 为新建的镜像指定环境变量
  • EXPOSE 设置对外开发的端口
  • LABEL 为新建的镜像添加元数据标签
  • STOPSIGNAL 发送容器退出的调用信号
  • USER 指定执行后续命令的用户和用户组
  • VOLUME 定义匿名数据卷
  • WORKDIR 设置工作目录
  • ONBUILD 用于延迟构建命令的执行

解析器指令

# 解析器指令(可选)主要用来提示解析器进行特殊处理,如果使用必须在第一行,不能重复,与后续必须空一格
# directive=value

#用于定义Dockerfile中的转义符,在windows中推荐用别的替换
#escape=\

#18.09版本后使用
# 构建时使用什么样的版本镜像构建
# syntax=[remote image reference]
#表示不断更新最新的1.x.x次要和布丁版本
#syntax=docker/dockerfile:1 
#表示不断更新1.2.x的最新吧版本,一但1.3.0版本发布就停止更新
#syntax=docker/dockerfile:1.2
#表示使用固定的1.2.1版本
#syntax=docker/dockerfile:1.2.1

FROM 指令

表示定制的镜像都是以FROM指令指定的镜像作为基准镜像,后续的构建指令运行于此基准镜像。

语法:
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

示例
FROM ubuntu:18.04

ADD 指令

ADD指令从文件、目录或远程文件 URL 复制文件,并将它们添加新构建的镜像中

  • 可以支持远程操作
  • tar 包的话,会解压
  • src可以指定多个资源
  • 支持Go的filepaht.Match规则;
语法:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

示例:
# 将以hom开头的文件复制到镜像中的mydir目录中
ADD hom* /mydir/
# 将所有匹配到的hom*.txt文件复制到镜像中的mydir目录
ADD hom?.txt /mydir/
# 将test.txt复制到相对路径<WORDDIR>/relativeDir中
ADD test.txt relativeDir/

# 复制到镜像的文件增加权限组或用户,可以是数字也可以是具体的名称
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/

#从远程复制foobar 到容器的/下的foobar目录
ADD http://example.com/foobar /
#如果ADD 后面跟目录,则将这个目录拷贝到容器中
ADD  myapp  /myapp

COPY 指令

COPY指令从文件或目录中复制内容到新镜像指定的中。

  • 本地
  • src 可指定多个资源
  • 支持Go的filepaht.Match规则;
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

和ADD类似

ENV 指令

ENV指令将环境变量设置为 value

#语法
ENV <key>=<value> ...

# 示例:
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy

# run的时候可以更改
docker run --env <key>=<value>。

EXPOSE 指令

EXPOSE指令通知 Docker 容器在运行时侦听指定的网络端口。可以指定端口是监听TCP还是UDP,如果不指定协议,默认为TCP。

#语法
EXPOSE <port> [<port>/<protocol>...]

示例:
EXPOSE 80/tcp
EXPOSE 80/udp
EXPOSE 80 443

docker run -p 80:80/tcp -p 80:80/udp

LABEL 指令

LABEL指令新构建的镜像添加元数据。lable是键值对。要在LABEL值中包含空格,可以在命令行解析中一样使用引号和反斜杠

语法:
LABEL <key>=<value> <key>=<value> <key>=<value> ...

示例:1.10之前可以多个label
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

LABEL multi.label1="value1" multi.label2="value2" other="value3"

LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

USER 指令

USER指令用于为打包的镜像,运行时指定用户名和用户组,主要使用USER场景的指令有RUN/CMD/ENTRYPOINT

语法:
USER <user>[:<group>]
USER <UID>[:<GID>]

示例:
USER www:www

WORKDIR

主要为以下指令(RUN/CMD/ENTRYPOINT/COPY/ADD)设置工作目录,可以是相对路径或绝对路径,如果多个,将继承

语法:
WORKDIR /path/to/workdir

示例:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
最终的工作目录为/a/b/c

ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
最终的工作目录是/path/$DIRNAME

ARG 指令

ARG指令用于定义变量,用户可以在docker build时通过--build-arg =传递给构建器。

语法:
ARG <name>[=<default value>]
示例:

FROM ubuntu
ARG CONT_IMG_VER #(这块可以给默认值)
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER

docker build --build-arg CONT_IMG_VER=v2.0.1 

docker中有一些预定义的ARG变量

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy

VOLUME 指令

用于在镜像中创建一个挂载点目录,以挂载宿主机上的目录

  • 在windows中,必须是一个不存在或空的目录,也不能在C盘
  • 挂载可以是JSON格式的
  • 主机目录依赖于主机
语法:
VOLUME ["/data"]

示例:
FROM ubuntu
RUN mkdir /myvol #创建宿主机中挂载的目录
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol #挂载目录

ONBUILD 指令

通过onbuild指令,将一个触发器指令添加到镜像中,以便该镜像作为基础构建下一层时执行。

执行流程

  • 遇到onbuild指令时,build会向正在构建的image的元数据中添加触发器;
  • 构建结束,可以用docker inspect命令检查
  • 当FROM该image时,会触发onbuild添加的触发器,并按照注册顺序执行
  • 执行一次后就清除了
    
    ONBUILD <INSTRUCTION>

示例:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src


#### RUN 指令
RUN执行命令,并生成一层镜像,生成image提交并用于下一次构建。
- 每条RUN都是在当前基础镜像执行,并提交新镜像
- 尽量减少RUN,一个RUN创建一层;
- 如果有多个RUN,使用 && 符号链接命令,只会创建一层
- RUN在docker build阶段执行

语法:
RUN <command>(shell形式,命令在 shell 中运行,默认/bin/sh -c在 Linux 或cmd /S /CWindows 上)
RUN ["executable", "param1", "param2"](执行形式,必须用双引号)

示例:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
RUN ["/bin/bash", "-c", "echo hello"]


#### CMD 指令
类似于RUN命令
- CMD 在docker run 阶段执行
- 存在多个CMD,只有最后一个生效

语法:
CMD ["executable","param1","param2"](exec形式,这是首选形式)
CMD ["param1","param2"](作为ENTRYPOINT 的默认参数)
CMD command param1 param2(外壳形式)


### 编写Dockerfile
指南和建议
- Use multi-stage builds 使用多阶段构建
- 不要安装不必要的包
- 解耦应用程序
- 尽量减少层数 (RUN/COPY/ADD会创建image层)
- 对多行参数进行排(避免重复)
- 利用构建缓存

#### v1版本

https://hub.docker.com/_/centos?tab=tags&page=1&ordering=last_updated

基于centos7安装nginx

Base images 基础镜像

FROM centos:7

MAINTAINER 维护者信息

MAINTAINER yxkong 5ycode@sina.com

ENV 设置环境变量

ENV PATH /usr/local/nginx/sbin:$PATH

ADD 文件放在当前目录下,拷过去会自动解压

ADD nginx-1.16.1.tar.gz /usr/local/
ADD epel-release-latest-7.noarch.rpm /usr/local/

RUN 执行以下命令

RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm && yum install -y wget lftp gcc gcc-c++ make openssl-devel pcre-devel pcre && yum clean all && useradd -s /sbin/nologin -M www

WORKDIR 相当于cd

WORKDIR /usr/local/nginx-1.8.0

RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install && echo "daemon off;" >> /etc/nginx.conf

可以设置环境变量,对外可以直接使用nginx

ENV PATH /usr/local/nginx/sbin:$PATH

EXPOSE 映射端口

EXPOSE 80

CMD 运行以下命令

CMD ["nginx"]


执行目录文件
Dockerfile                              
epel-release-latest-7.noarch.rpm        
nginx-1.16.1.tar.gz 

docker yxk$ docker build -f ./Dockerfile -t yxkong/nginx:v1 .
[+] Building 31.3s (11/11) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.08kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:7 2.8s
=> [1/6] FROM docker.io/library/centos:7@sha256:0f4ec88e21daf75124b8a9e5ca03c37a5e937e0e108a255d890492430789b60e 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 444B 0.0s
=> CACHED [2/6] ADD nginx-1.16.1.tar.gz /usr/local/ 0.0s
=> CACHED [3/6] ADD epel-release-latest-7.noarch.rpm /usr/local/ 0.0s
=> CACHED [4/6] RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm && yum install -y wget lftp gcc gcc-c++ make 0.0s
=> [5/6] WORKDIR /usr/local/nginx-1.16.1 0.0s
=> [6/6] RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make 27.5s
=> exporting to image 0.8s
=> => exporting layers 0.8s
=> => writing image sha256:df0f9bd5c26a2c825e610db1c19cd157afe7bb78bc0cfd926b93855a0402df1c 0.0s
=> => naming to docker.io/yxkong/nginx:v1 0.0s

通过images查看

docker yxk$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
yxkong/nginx v1 df0f9bd5c26a 10 minutes ago 369MB

通过inspect命令查看

docker yxk$ docker inspect df0f9bd5c26a
[
{
"Id": "sha256:df0f9bd5c26a2c825e610db1c19cd157afe7bb78bc0cfd926b93855a0402df1c",
"RepoTags": [
"yxkong/nginx:v1"
],
"RepoDigests": [],
"Parent": "",
"Comment": "buildkit.dockerfile.v0",
"Created": "2021-07-12T07:48:37.0883337Z",
"Container": "",
"ContainerConfig": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": null,
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"DockerVersion": "",
"Author": "yxkong 5ycode@sina.com",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/nginx/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"nginx"
],
"ArgsEscaped": true,
"Image": "",
"Volumes": null,
"WorkingDir": "/usr/local/nginx-1.16.1",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20201113",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"org.opencontainers.image.created": "2020-11-13 00:00:00+00:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
"Architecture": "amd64",
"Os": "linux",
"Size": 369358118,
"VirtualSize": 369358118,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/jvg0utv0k2o8a6g8xkc3s1vzb/diff:/var/lib/docker/overlay2/wk2x19rfkes9mfbysl8s9r2kj/diff:/var/lib/docker/overlay2/91w8e42eapjzhhj0fvj65tf5z/diff:/var/lib/docker/overlay2/zkdux7by7x4sfj7jiud88frwm/diff:/var/lib/docker/overlay2/8429756e5d996a34977fd2bef5936a34b25c3afffd98b51d62f16081ec9d8f4f/diff",
"MergedDir": "/var/lib/docker/overlay2/ukaikrf2j0hz2z7n956x40wgt/merged",
"UpperDir": "/var/lib/docker/overlay2/ukaikrf2j0hz2z7n956x40wgt/diff",
"WorkDir": "/var/lib/docker/overlay2/ukaikrf2j0hz2z7n956x40wgt/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02",
"sha256:ebad2bdc79dce1588044a87b646407fb40e6ed4ffb92a6aae3b1c45a8679123a",
"sha256:0bfcb95f76d8fb541c874ca3d205a37075bcf0c3dce65e80b976e38364a41ba7",
"sha256:69dcde0bdd1ee35d1b18fa2c83772db09af04282f113867bcd507c346f2e2310",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:af7571d28fc377a90dabf6f400675b7e8a30292dbfafd658dd6157e894d0b7b7"
]
},
"Metadata": {
"LastTagTime": "2021-07-12T07:48:37.932162Z"
}
}
]

启动容器:
docker run -d -p9000:80 yxkong/nginx:v1 nginx -g "daemon off;"

查看nginx,启动成功了

docker yxk$ curl 127.0.0.1:9000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>;
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>;

<p><em>Thank you for using nginx.</em></p>
</body>
</html>


#### v2版本

基于centos7安装nginx

Base images 基础镜像

FROM centos:7

MAINTAINER 维护者信息

MAINTAINER yxkong 5ycode@sina.com

ENV 设置环境变量

ENV PATH /usr/local/nginx/sbin:$PATH

ADD 文件放在当前目录下,拷过去会自动解压

ADD nginx-1.16.1.tar.gz /usr/local/
ADD epel-release-latest-7.noarch.rpm /usr/local/

RUN 执行以下命令

RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm && yum install -y wget lftp gcc gcc-c++ make openssl-devel pcre-devel pcre && yum clean all && useradd -s /sbin/nologin -M www

WORKDIR 相当于cd

WORKDIR /usr/local/nginx-1.8.0

RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install && echo "daemon off;" >> /etc/nginx.conf

可以设置环境变量,对外可以直接使用nginx

ENV PATH /usr/local/nginx/sbin:$PATH

EXPOSE 映射端口

EXPOSE 80

CMD 运行以下命令

CMD /bin/sh -c 'nginx -g "daemon off;"'

重新构建

docker yxk$ docker build -t yxkong/nginx:v2 .
[+] Building 2.9s (11/11) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.13kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:7 2.7s
=> [internal] load build context 0.0s
=> => transferring context: 444B 0.0s
=> [1/6] FROM docker.io/library/centos:7@sha256:0f4ec88e21daf75124b8a9e5ca03c37a5e937e0e108a255d890492430789b60e 0.0s
=> CACHED [2/6] ADD nginx-1.16.1.tar.gz /usr/local/ 0.0s
=> CACHED [3/6] ADD epel-release-latest-7.noarch.rpm /usr/local/ 0.0s
=> CACHED [4/6] RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm && yum install -y wget lftp gcc gcc-c++ make 0.0s
=> CACHED [5/6] WORKDIR /usr/local/nginx-1.16.1 0.0s
=> CACHED [6/6] RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:0db1941cd714d545807ab3a8b9ffa22901f846a568d6f04c89c3ddb792dff598 0.0s
=> => naming to docker.io/yxkong/nginx:v2 0.0s

docker yxk$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
yxkong/nginx v2 0db1941cd714 3 hours ago 369MB
yxkong/nginx v1 df0f9bd5c26a 3 hours ago 369MB

docker yxk$ docker run -d -p:9002:80 yxkong/nginx:v2
a81aad49e53c2c4342d781ef29f1f1447dafec5f7a87f83d257e7997e6ea4a2c
docker yxk$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a81aad49e53c yxkong/nginx:v2 "/bin/sh -c '/bin/sh…" 6 seconds ago Up 5 seconds 0.0.0.0:9002->80/tcp interesting_kalam

yxkong

这个人很懒,什么都没留下