07_Dockerfile

参考资料

Dockerfile是什么?

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令。

大白话:可用通过代码构建镜像,写的代码文件就是Dockerfile。

  • Dockerfile构建镜像流程:
    • 准备构建镜像需要的材料 -> 编写Dockerfile文件 -> 使用docker build构建镜像。

注:编写Dockerfile需要一定的Linux基础,或者知道一些常用的Linux指令。

常用指令

常用指令表

分类 指令 说明
构建指令 FROM 设置基础镜像
WORKDIR 指定工作目录(类似cd到指定目录)
LABEL 设置元数据
VOLUME 定义匿名数据卷
运行指令 RUN 编译时运行后面的命令(类似在shell中执行命令)
CMD 启动时运行后面的命令,会被启动参数覆盖
ENTRYPOINT 与CMD类似,但不会被启动时参数覆盖
参数指令 ENV 设置环境变量
ARG 设置局部变量
复制指令 COPY 拷贝文件,不解压
ADD 添加文件,默认解压

常用指令详解

构建指令

FROM

设置基础镜像。有效的Dockerfile必须从FROM指令开始。

  • 语法
FROM [--platform=<platform>] <image> [AS <name>]
# 或
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
# 或
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
  • 示例
# 以centos:latest作为基础镜像
FORM centos
WORKDIR

指定工作目录。

  • 语法
WORKDIR <容器内目录>

说明:WORKDIR可以在Dockerfile中多次使用,如果提供了相对路径,则它将相对于上一条WORKDIR指令的路径。例如:

WORKDIR /a
WORKDIR b
WORKDIR c
# 输出:/a/b/c
RUN pwd
  • 示例
# 设置工作目录
WORKDIR /path/to/workdir

# 输出:/path/to/workdir
RUN pwd
LABEL

标签。将元数据添加到镜像,一个镜像可以包含多个标签。

要查看镜像的标签,请使用docker image inspect命令。可以使用该--format选项仅显示标签。

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

说明:在Docker 1.10之前的版本中,为减小镜像大小,尽可能的把多个标签写到一行,新版本会自动合并。

  • 示例
# 添加维护者信息
LABEL maintainer="vv_lin<vv_lin@123.com>"

# 查看标签
docker image inspect --format='' myimage
VOLUME

定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

在启动容器 docker run的时候,我们可以通过 -v 参数修改挂载点。

  • 语法
VOLUME ["<路径1>", "<路径2>"]
# 或
VOLUME <路径1> <路径2>
  • 示例
# 在容器内部创建myvol目录
RUN mkdir /myvol

# 将myvol目录以匿名方式挂载到宿主机
VOLUME /myvol

注意:这里说的匿名挂载是站在宿主机角度看为匿名,容器内部的目录还是具名的。

运行指令

RUN

用于执行后面跟着的命令,只在编译时生效。

  • 语法
# 执行命令,类似打开Linux的/bin/sh(或Window的cmd),并执行命令。
RUN <command>
# 或
RUN ["可执行文件", "参数1", "参数2"]
  • 示例
# 1.使用yum安装wget
RUN yum install wget
# 2.下载tomcat包
RUN wget https://mirrors.cnnic.cn/apache/tomcat/tomcat-8/v8.5.57/bin/apache-tomcat-8.5.57.tar.gz
# 3.解压tomcat
RUN tar -xvf apache-tomcat-8.5.57.tar.gz

# 或
# 执行HelloWorld.sh
# 类似 RUN ./HelloWorld.sh aaa bbb
RUN ["./HelloWorld.sh", "aaa", "bbb"]

注意:Dockerfile 的指令每执行一次都会在 Docker 镜像上新建一层。过多无意义的层,会造成镜像膨胀过大。以 && 符号连接命令,这样执行后,只会创建 1 层镜像。如下:

RUN yum install wget \
  && wget https://mirrors.cnnic.cn/apache/tomcat/tomcat-8/v8.5.57/bin/apache-tomcat-8.5.57.tar.gz\
  && tar -xvf apache-tomcat-8.5.57.tar.gz
CMD

类似于 RUN 指令,用于运行程序。

RUNCMD的区别:

  • RUN 是在docker build时运行。
  • CMD 在docker run时运行。

说明:

  • Dockerfile中只能有一条CMD指令,如果有多条,只有最后一条生效
  • CMD 指令参数可被 docker run命令行参数覆盖。
  • 语法
CMD ["可执行文件", "参数1", "参数2"]
# 或
# 该方式作为ENTRYPOINT的默认参数
CMD ["参数1", "参数2"]
# 或
CMD <命令> "参数1", "参数2"

示例

# 执行HelloWorld.sh
CMD ["./HelloWorld.sh", "aaa", "bbb"]
ENTRYPOINT

类似CMD指令,但正常情况下不会被docker run 的命令行参数所覆盖。

如果非要覆盖,可以使用docker run--entrypoint 选项强制覆盖。

说明:如果 Dockerfile 中如果存在多个ENTRYPOINT 指令,仅最后一个生效

  • 语法
ENTRYPOINT ["可执行文件", "参数1", "参数2"]
# 或
ENTRYPOINT <命令> "参数1", "参数2"
  • 示例
# 假设通过Dockerfile 构建了nginx:test镜像。
# Dockerfile如下:
FROM nginx

# 定参
ENTRYPOINT ["nginx", "-c"]
# 变参
CMD ["/etc/nginx/nginx.conf"]
  1. 不传参运行
# 命令
docker run nginx:test

# =>
# 容器内会默认运行以下命令(从CMD设定的参数取值)
nginx -c /etc/nginx/nginx.conf
  1. 传参运行
# 命令
docker run nginx:test -c /etc/nginx/nginx-custom.conf

# =>
# 容器内会默认运行以下命令(使用外部传递的参数)
nginx -c /etc/nginx/nginx-custom.conf

参数指令

ENV

设置环境变量。

若定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

  • 语法
ENV <key> <value>
# 或
ENV <key>=<value> ...
  • 示例
# 设置JAVA_HOME
ENV JAVA_HOME=/usr/lib/jvm/jdk8

# 使用
ENV PATH=$JAVA_HOME/bin:$PATH
ARG

构建参数,与 ENV类似。

ENVARG对比:

  • ENV在Dockerfile和镜像中均可用;
  • ARG只在Dockerfile中可用。

说明:构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

  • 语法
ARG <name>[=<default value>]
  • 示例
# 设置参数
ARG hello_world=helloWorld!!

# 使用
RUN echo $hello_world

复制指令

COPY

复制,从上下文目录中复制文件或者目录到容器里指定路径。

  • 语法
COPY [--chown=<user>:<group>] <源路径1>... <目标路径>
#或
# 包含空格的路径需要使用该形式
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

如果<源路径1>是目录,则将复制目录的整个内容,目录本身不被复制,仅其内容被复制。

<src>可以包含通配符。

注意:--chown仅在用于构建Linux容器的Dockerfiles上受支持,而在Windows容器上不起作用。

  • 示例
# 1. 复制所有以“hom”开头的文件到容器的/mydir/目录下
COPY hom* /mydir/

# 2. "?"被替换为任何单个字符,例如“ home.txt”。
COPY hom?.txt /mydir/

# 3. 复制到相对路径中:<WORKDIR>/relativeDir/
COPY test.txt relativeDir/
ADD

ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY),功能也类似。

不同之处如下:

  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。

  • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

  • 语法

ADD [--chown=<user>:<group>] <源路径1>... <目标路径>
# 或
# 包含空格的路径需要使用该形式
ADD [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

如果<源路径1>是目录,则将复制目录的整个内容,目录本身不被复制,仅其内容被复制。

<src>可以包含通配符。

注意:

  • --chown仅在用于构建Linux容器的Dockerfiles上受支持,而在Windows容器上不起作用。
  • ADD可以通过远程文件URL添加文件。
  • 示例
# 1. 复制所有以“hom”开头的文件到容器的/mydir/目录下
ADD hom* /mydir/

# 2. "?"被替换为任何单个字符,例如“ home.txt”。
ADD hom?.txt /mydir/

# 3. 复制到相对路径中:<WORKDIR>/relativeDir/
ADD test.txt relativeDir/

小试牛刀

通过Dockerfile构建一个最简单的镜像

  1. 通过Dockerfile构建一个最简单的镜像。
# 1. 编写Dockerfile文件
vim Dockerfile

# Dockerfile
FROM centos
RUN echo "Hello World!!" > hello_world.txt
  1. 根据Dockerfile构建镜像。
# 参数说明(注意后面有个“.”):
# -t mycentos:test 指定构建镜像的名称和标签
# . 指定镜像构建过程中的上下文环境的目录
docker build -t mycentos:test .

# 输出
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM centos
 ---> 0d120b6ccaa8
Step 2/2 : RUN echo "Hello World!!" > hello_world.txt
 ---> Running in f78e091a6c89
Removing intermediate container f78e091a6c89
 ---> efa3df984c1c
Successfully built efa3df984c1c
Successfully tagged mycentos:test
  1. 启动镜像。
# 查看所有镜像
docker images
# 使用交互模式运行镜像
docker run -it --name mycentos mycentos:test bash

# 在容器中查看目录结构
ls
# 输出结果,能看到hellow_world.txt
bin  dev  etc  hello_world.txt  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

# 查看文件内容
[root@a5b50d7fa748 /]# cat hello_world.txt
# 输出结果
Hello World!!

构建镜像参数后面的".",的意思

参考资料

当构建的时候,由用户指定构建镜像的上下文路径,而docker build会将这个路径下所有的文件都打包上传给 Docker引擎,引擎内将这些内容展开后,就能获取到所有指定上下文中的文件了。

比如:

Dockerfile中的COPY ./package.json /project,其实拷贝的并不是本机目录下的package.json文件,而是Docker引擎中展开的构建上下文中的文件,所以如果拷贝的文件超出了构建上下文的范围,Docker引擎 是找不到那些文件的。

.dockerignore 文件的作用

参考资料:

《.dockerignore 文件的作用》

.dockerignore 文件中指定在传递给 docker引擎 时需要忽略掉的文件或文件夹。

比如:

在前端项目中,node_modules 文件夹在构建镜像过程中如果用不到,但是又异常庞大,那么向 docker引擎 传递其实是并没有必要的,这个时候就可以将 node_modules 文件夹加入 .dockerignore 文件中。

通过Dockerfile构建Tomcat

  1. 使用vim编写Dockerfile
vim Dockerfile
# Dockerfile
# jdk + tomcat
FROM centos

# 添加标记
LABEL maintainer="vv_lin<vv_lin@123.com>"

# 创建目录
RUN mkdir -p /usr/local/java \
    && mkdir -p /usr/local/tomcat

# 设置工作空间
WORKDIR /usr/local

# 添加jdk
ADD jdk-8u202-linux-x64.tar.gz /usr/local/java/

# 添加tomcat
ADD apache-tomcat-8.5.57.tar.gz /usr/local/tomcat/

# 设置环境变量
# 设置java环境变量
ENV JAVA_HOME=/usr/local/java/jdk1.8.0_202
ENV CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
# 设置tomcat环境变量
ENV CATALINA_HOME=/usr/local/tomcat/apache-tomcat-8.5.57
# 追加到PATH中
ENV PATH=$CATALINA_HOME/bin:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH

# 挂载匿名磁盘
VOLUME ["/usr/local/tomcat/apache-tomcat-8.5.57/logs", "/usr/local/tomcat/apache-tomcat-8.5.57/webapps"]

# 声明端口
EXPOSE 8080

# 启动
ENTRYPOINT ["catalina.sh", "run"]
  1. 编译成镜像
# 注意后面的“.”
docker build -t mytomcat .
# 或
docker build -f Dockerfile -t mytomcat:latest .
# 或
docker build -f Dockerfile -t mytomcat .

说明:docker build默认读取名为Dockerfile,如果是其他名称,则需要使用-f <Dockerfile文件名>指定文件名。

  1. 启动镜像
docker run -it --name mytomcat -p 8080:8080 mytomcat bash
  1. 通过浏览器访问,发现正常
  2. 查看挂载的目录
# 1. 查看所有挂载的目录
docker volume ls

# 输出
DRIVER              VOLUME NAME
local               0ea11ee5a377263e6a62a61afdd554f12f3f61e0e69fb603794363637668f270
local               1c2c7f656fdea4a16471e9c066353d7b3423614f4dc6b9abfd3e8a0e6f163b5b
local               28ad647f7dc23b3a04c96f8ba8eeca08c0766a1b2669bc42162e43ddcd585164
local               85a5ba99cf1ca1a52e745685bede94be81f6d79e44ef108192d2d27817460331
local               9438df2f42126d5ac47a63916ae888f6c2d0dfb65e65634df6916c55ad231e0d
local               26817b63add34160593e512468510fbfa0e1d3326ef826c01f8d4a1a3d10ce8c
local               testvolume

# 2. 查看挂载目录的元数据
docker volume inspect 1c2c7f656fdea4a16471e9c066353d7b3423614f4dc6b9abfd3e8a0e6f163b5b

# 输出
[
    {
        "CreatedAt": "2020-08-19T17:00:07+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/1c2c7f656fdea4a16471e9c066353d7b3423614f4dc6b9abfd3e8a0e6f163b5b/_data",
        "Name": "1c2c7f656fdea4a16471e9c066353d7b3423614f4dc6b9abfd3e8a0e6f163b5b",
        "Options": null,
        "Scope": "local"
    }
]

# 4. 查看挂载目录的内容
ls /var/lib/docker/volumes/1c2c7f656fdea4a16471e9c066353d7b3423614f4dc6b9abfd3e8a0e6f163b5b/_data

# 输出
docs  examples  host-manager  manager  ROOT
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容