制作docker镜像的dockerfile编写规则汇总

简介: 制作docker镜像的dockerfile编写规则汇总

Dokcerfile

使用 Dockerfile 定制镜像:

镜像的定制实际上就是定制每一层所添加的配置、文件。

如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜

像,

那么无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就Dockerfile。

1、为什么要使用Dockerfile

问题:在dockerhub中官方提供很多镜像已经能满足我们的所有服务了,为什么还需要自定义镜像

核心作用:日后用户可以将自己应用打包成镜像,这样就可以让我们应用进行容器运行.还可以对官方镜像做扩展,以打包成我们生产应用的镜像。

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,

因此每一条指令的内容,就是描述该层应当如何构建。

此处以定制 nginx 镜像为例,使用 Dockerfile 来定制。

2、dockerfile构建过程:

从基础镜像运行一个容器

执行一条指令,对容器做出修改

执行类似docker commit 的操作,提交一个新的镜像层

再基于刚提交的镜像运行一个新的容器

执行dockerfile中的下一条指令,直至所有指令执行完毕

基于Dockerfile构建镜像可以使用docker build命令。docker build命令中使用-f可以指定具体的dockerfile文件

如果不指定Dockerfile文件,Dockerfile的命名必须为Dockerfile。大小写不能变。

若不是这个名字,需要 -f 指定文件路径

[root@master01 kubernetes ]#docker build --help

-f, --file string Name of the Dockerfile (Default is ‘PATH/Dockerfile’)

典型用法

docker build -t ImageName:TagName dir

1

选项

-t 给镜像加一个Tag

ImageName 给镜像起的名称

TagName 给镜像的Tag名

Dir Dockerfile所在目录

根据目录下的 Dockerfile 文件构建镜像

例子

docker build -t chinaskill-redis:v1.1 .

1

chinaskill-redis 是镜像名

v1.1 是 tag 标签

. 表示当前目录,即Dockerfile所在目录

docker build -f dockerfiles/Dockerfile.debug -t myapp_debug .

在一个空白目录中,建立一个文本文件,并命名为 Dockerfile :

$ mkdir mynginx

$ cd mynginx

$ touch Dockerfile

其内容为:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

这个 Dockerfile 很简单,一共就两行。涉及到了两条指令, FROM 和 RUN 。

FROM 指定基础镜像

所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。而 FROM 就是指定基础镜像,

因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。

在 Docker Store 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,

如nginx 、 redis 、 mongo 、mysql 等;也有一些方便开发、构建、运行各种语言应用的镜像,

如 node 、 openjdk 、 python 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。

如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,

如ubuntu 、 debian 、 centos 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。

这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

FROM scratch

如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。

不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,

比如 swarm 、 coreos/etcd 。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,

所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。

使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,

这也是为什么有人认为 Go是特别适合容器微服务架构的语言的原因之一。

RUN 执行命令

 RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最常用的指令之一。其格式有两种:

shell 格式: RUN <命令> ,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。

RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

3、dockerfile的规则

格式:

#为注释

指令(大写) 内容(小写)

尽管指令大小写不敏感,但是我们强烈建议指令用大写,内容用小写表示

docker是按顺序执行dockerfile里的指令集的(从上到下一次执行)

每一个dockerfile的第一个非注释行指令,必须是"FROM" 指令,用于为镜像文件构建过程中,指定基准镜像。后续的指令运行

于此基准镜像提供的运行环境中

实践中,基准镜像可以是任何可用镜像文件,

默认情况下docker build会在docker主机上(本地)查找指定的镜像文件,当其不存在时,则会从远端(Docker registry)上拉取所需镜像文件

MAINTAINER: 维护者信息

格式:

MAINTAINER

示例:

MAINTAINER Jasper Xu

MAINTAINER sorex@163.com

MAINTAINER Jasper Xu sorex@163.com

4、核心的dockerfile指令:

USER/WORKDIR指令

ADD/EXPOSE指令

RUN/ENV指令

CMD/ENTRYPOINT指令

1、USER/WORKDIR指令

USER指定进程使用哪个用户运行,WORKDIR指定进程的工作目录,进去docker时所在目录

[root@223 ~ ]# cat Dockerfile

FROM docker.io/littefun91/nginx_with_ailiyunyuan:v1.0

USER nginx

WORKDIR /usr/share/nginx/html

[root@223 ~ ]# docker run -it littlefun91/nginx_with_aliyunyuan:v1.0_with_user_workdir /bin/bash

nginx@9328a34b97cf:/usr/share/nginx/html$

nginx@9328a34b97cf:/usr/share/nginx/html$

nginx@9328a34b97cf:/usr/share/nginx/html$ whoami

nginx

nginx@9328a34b97cf:/usr/share/nginx/html$

nginx@9328a34b97cf:/usr/share/nginx/html$

nginx@9328a34b97cf:/usr/share/nginx/html$ pwd

/usr/share/nginx/html

切换到镜像中的指定路径,设置工作目录

在 WORKDIR 中需要使用绝对路径,如果镜像中对应的路径不存在,会自动创建此目录

一般用 WORKDIR 来替代 RUN cd && 切换目录进行操作的指令

WORKDIR 指令为 Dockerfile 中跟随它的任何 RUN、CMD、ENTRYPOINT、COPY、ADD 指令设置工作目录

如果 WORKDIR 不存在,即使它没有在任何后续 Dockerfile 指令中使用,它也会被创建

2、ADD/EXPOSE指令

ADD指令的功能是将主机构建环境(上下文)目录中的文件和目录、以及一个URL标记的文件 拷贝到镜像中。

其格式是: ADD 源路径 目标路径

把当前config目录下所有文件拷贝到/config/目录下

ADD config/ /config/

EXPOSE指令:指的是让容器哪个端口暴露出来

有如下注意事项:

1、如果源路径是个文件,且目标路径是以 / 结尾, 则docker会把目标路径当作一个目录,会把源文件拷贝到该目录下。

如果目标路径不存在,则会自动创建目标路径。

2、如果源路径是个文件,且目标路径是不是以 / 结尾,则docker会把目标路径当作一个文件。

如果目标路径不存在,会以目标路径为名创建一个文件,内容同源文件;

如果目标文件是个存在的文件,会用源文件覆盖它,当然只是内容覆盖,文件名还是目标文件名。

如果目标文件实际是个存在的目录,则会源文件拷贝到该目录下。 注意,这种情况下,最好显示的以 / 结尾,以避免混淆。

3、如果源路径是个目录,且目标路径不存在,则docker会自动以目标路径创建一个目录,把源路径目录下的文件拷贝进来。

如果目标路径是个已经存在的目录,则docker会把源路径目录下的文件拷贝到该目录下。

4、如果源文件是个归档文件(压缩文件),则docker会自动帮解压。

ADD指令可以让你使用URL作为参数。当遇到URL时候,可以通过URL下载文件并且复制到。

ADD http://foo.com/bar.go /tmp/main.go

COPY和ADD相似,但是功能少一些。

在Docker 1.0发布时候,包括了新指令COPY。不像是ADD,COPY 更加直接了当,只复制文件或者目录到容器里。

COPY不支持URL,也不会特别对待压缩文件。如果build 上下文件中没有指定解压的话,那么就不会自动解压,只会复制压缩文件到容器中。

COPY是ADD的一种简化版本,目的在于满足大多数人“复制文件到容器”的需求。

Docker 团队的建议是在大多数情况下使用COPY。拷贝文件的原则:使用COPY(除非你明确你需要ADD)

如果单纯复制文件,dockerfile推荐使用COPY

[root@220 ~ ]# ll

-rw-r–r-- 1 root root 59 Jun 9 10:46 Dockerfile

-rw-r–r-- 1 root root 2380 Jun 9 10:46 index.html

COPY…

COPY[“”…“”]

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

格式:

COPY [–chown=:] <源路径1>… <目标路径>

COPY [–chown=:] [“<源路径1>”,… “<目标路径>”]

[–chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。

<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

COPY hom* /mydir/

COPY hom?.txt /mydir/

<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

例;替换/usr/share/nginx下的index.html

cd /root/dockerfile/test1

cat dockerfile

FROM centos

MAINTAINER xianchao

RUN yum install wget -y

RUN yum install nginx -y

COPY index.html /usr/share/nginx/html/

EXPOSE 80

ENTRYPOINT [“/usr/sbin/nginx”,“-g”,“daemon off;”]

[root@220 ~ ]# cat Dockerfile

FROM nginx

ADD index.html /usr/share/nginx/html/

EXPOSE 80

[root@220 ~ ]# docker build . -t nginx_with_expose:v2.0

Sending build context to Docker daemon 468.8MB

Step 1/3 : FROM nginx

—> 0e901e68141f

Step 2/3 : ADD index.html /usr/share/nginx/html/

—> ef87faa6f741

Step 3/3 : EXPOSE 80

—> Running in 617141822bba

Removing intermediate container 617141822bba

—> fd6a2aeaf727

Successfully built fd6a2aeaf727

Successfully tagged nginx_with_expose:v2.0

[root@220 ~ ]# docker run -itd -p 8865:80 nginx_with_expose:v2.0

[root@220 ~ ]# docker ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

9a988daab135 nginx_with_expose:v2.0 “/docker-entrypoint.…” 3 seconds ago Up 2 seconds 0.0.0.0:8865->80/tcp, :::8865->80/tcp youthful_chandrasekhar

若是通过centos镜像,然后通过dockerfile安装nginx,默认nginx是没起来的

docker run通过/bin/bash 进入容器后。nginx进程是没起来的

需要进去容器,或者docker run时 使用nginx -g “daemon off” 启动nginx

3、RUN/ENV指令:

RUN是构建镜像时,执行的操作

ENV是将环境变量固化到镜像中

RUN:指定在当前镜像构建过程中要运行的命令

包含两种模式

1、Shell

RUN (shell模式,这个是最常用的,需要记住)

RUN echo hello

2、exec模式

RUN “executable”,“param1”,“param2”

RUN [“/bin/bash”,”-c”,”echo hello”]

等价于/bin/bash -c echo hello

[root@220 ~ ]# cat Dockerfile

FROM nginx

ENV jinghao=“jing_hao”

RUN touch $jinghao.sh

[root@220 ~ ]# docker build -t nginx_with_jinghao .

[root@220 ~ ]# docker images

REPOSITORY TAG IMAGE ID CREATED SIZE

nginx_with_jinghao latest 30a140de4242 4 seconds ago 142MB

[root@220 ~ ]# docker run -it nginx_with_jinghao:latest /bin/bash

root@6220e1fc4b7e:/# ls -l

-rw-r–r-- 1 root root 0 Jun 9 06:04 jing_hao.sh 这个是根据环境变量 run指令 创建的

root@6220e1fc4b7e:/# env

HOSTNAME=6220e1fc4b7e

PWD=/

PKG_RELEASE=1~bullseye

HOME=/root

NJS_VERSION=0.7.3

TERM=xterm

SHLVL=1

jinghao=jing_hao 这个就是固化进来的

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

NGINX_VERSION=1.21.6

_=/usr/bin/env

[root@220 ~ ]# cat Dockerfile

FROM centos:7

ENV VER=“9.11.4”

RUN yum install -y bind-$VER

4、CMD/ENTRYPOINT指令:

使用CMD构建的镜像,当容器启动时就执行 CMD配置的命令。CMD是启动容器时执行的操作

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)

注意:如果在dockerfile里出现多个CMD,只有最后一个CMD会生效

CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。

[root@220 ~ ]# cat Dockerfile

FROM centos:7

RUN yum install -y httpd

CMD [“httpd”,“-D”,“FOREGROUND”] 容器启动时执行 httpd -D FOREGROUND 从而让httpd进程启动起来

[root@220 ~ ]# docker build . -t centos_with_httpd:v1.3

[root@220 ~ ]# docker run -itd -p 2256:80 centos_with_httpd:v1.3

[root@220 ~ ]# docker ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

2848add746ea centos_with_httpd:v1.3 “httpd -D FOREGROUND” 4 seconds ago Up 3 seconds 0.0.0.0:2256->80/tcp, :::2256->80/tcp elastic_fermi

5、ENTRYPOINT指令:

如果容器启动时没带 CMD指令,默认会执行/entrypoint.sh

当然也可以手动编辑entrypoint.sh然后add到镜像中

/entrypoint.sh 必须带执行权限

[root@220 ~ ]# cat entrypoint.sh

#!/bin/bash

/sbin/nginx -g “daemon off;”

[root@220 ~ ]# chmod +x entrypoint.sh

[root@220 ~ ]# cat Dockerfile

FROM centos:7

ADD entrypoint.sh /

RUN yum install -y epel-release -q && yum install -y nginx

ENTRYPOINT /entrypoint.sh

[root@220 ~ ]# docker build . -t centos_with_nginx:v3

[root@220 ~ ]# docker run --rm -p 8898:80 centos_with_nginx:v3

进入容器查看:

[root@220 ~ ]# docker exec -it intelligent_benz /bin/bash

[root@eeb9d46f54f0 /]# ls -l

total 16

-rw-r–r-- 1 root root 12114 Nov 13 2020 anaconda-post.log

lrwxrwxrwx 1 root root 7 Nov 13 2020 bin -> usr/bin

drwxr-xr-x 3 root root 18 Jun 15 08:53 boot

drwxr-xr-x 5 root root 340 Jun 15 08:54 dev

-rwxr-xr-x 1 root root 41 Jun 15 08:52 entrypoint.sh 这个就是add进来的文件

当不指定CMD的时候,默认执行entrypoint.sh

CMD指令会被(run)运行时执行的命令替换掉,ENTRYPOINT 指令不会被替换掉

也可以直接跟指定的命令:

例;替换/usr/share/nginx下的index.html

cd /root/dockerfile/test1

cat dockerfile

FROM centos

MAINTAINER xianchao

RUN yum install wget -y

RUN yum install nginx -y

COPY index.html /usr/share/nginx/html/

EXPOSE 80

ENTRYPOINT [“/usr/sbin/nginx”,“-g”,“daemon off;”]

ENTRYPOINT

两种写法

#exec 格式

ENTRYPOINT [“executable”, “param1”, “param2”]

#shell 格式

ENTRYPOINT command param1 param2

重点

ENTRYPOINT 指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有其他传入值作为该命令的参数

ENTRYPOINT 的值可以通过 docker run --entrypoint 来覆盖掉

只有 Dockerfile 中的最后一条 ENTRYPOINT 指令会起作用

ENTRYPOINT 和 CMD 联合使用

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令

换句话说实际执行时,会变成

“”

灵魂拷问

那么有了 CMD 后,为什么还要有 ENTRYPOINT 呢?这种 “” 有什么好处么?

CMD 和 ENTRYPOINT 区别

CMD # 指定这个容器启动的时候要运行的命令,不可以追加命令

ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令

啥意思?这其实也是 ENTRYPOINT 的应用场景之一,下面来看

测试 CMD

编写 dockerfile 文件

FROM centos

CMD [“ls”,“-a”]

构建镜像

docker build -f CMD.dockerfile -t test .

运行容器

docker run test

.

.dockerenv

bin

dev

etc

home

lib

lib64

lost+found

media

运行容器并追加命令

docker run test -l

docker: Error response from daemon: OCI runtime create failed: container_linux.go:380:

starting container process caused: exec: “-l”: executable file not found in $PATH: unknown.

看到可执行文件找不到的报错,executable file not found

跟在镜像名后面的是 command,运行时会替换 CMD 的默认值,因此这里的 -l 替换了原来的 CMD,而不是追加在原来的 ls -a 后面

而 -l 根本不是命令,所以自然找不到

如果想加入 -i 参数,必须重写 ls 命令

docker run test ls -a -l

total 56

drwxr-xr-x 1 root root 4096 Oct 28 09:36 .

drwxr-xr-x 1 root root 4096 Oct 28 09:36 …

-rwxr-xr-x 1 root root 0 Oct 28 09:36 .dockerenv

lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin

drwxr-xr-x 5 root root 340 Oct 28 09:36 dev

drwxr-xr-x 1 root root 4096 Oct 28 09:36 etc

可以了,但这明显不是最优选择,ENTRYPOINT 就可以解决这个问题

测试 ENTRYPOINT

编写 dockerfile 文件

FROM centos

ENTRYPOINT [“ls”,“-a”]

构建镜像

docker build -f ENTRYPOINT.dockerfile -t test .

运行容器并追加命令

docker run test -l

total 56

drwxr-xr-x 1 root root 4096 Oct 28 09:38 .

drwxr-xr-x 1 root root 4096 Oct 28 09:38 …

-rwxr-xr-x 1 root root 0 Oct 28 09:38 .dockerenv

lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin

drwxr-xr-x 5 root root 340 Oct 28 09:38 dev

drwxr-xr-x 1 root root 4096 Oct 28 09:38 etc

drwxr-xr-x 2 root root 4096 Nov 3 2020 home

相关文章
|
4天前
|
存储 安全 持续交付
【Docker 专栏】Docker 镜像的版本控制与管理
【5月更文挑战第9天】本文探讨了Docker镜像版本控制与管理的重要性,包括可重复性、回滚能力、协作开发和持续集成。常用方法有标签、构建参数和版本控制系统。管理策略涉及定期清理、分层管理和镜像仓库。语义化标签、环境变量和配置文件在版本控制中有应用。版本系统与Docker结合能跟踪历史和促进协作。注意点包括优化镜像大小、确保安全性和兼容性。案例分析和未来趋势展示了持续发展的镜像管理技术,为Docker应用的稳定与进步保驾护航。
【Docker 专栏】Docker 镜像的版本控制与管理
|
2天前
|
存储 Linux Docker
CentOS7修改Docker容器和镜像默认存储位置
CentOS7修改Docker容器和镜像默认存储位置
|
4天前
|
存储 安全 开发者
如何删除 Docker 镜像、容器和卷?
【5月更文挑战第11天】
22 2
如何删除 Docker 镜像、容器和卷?
|
4天前
|
运维 安全 Docker
【Docker 专栏】Docker 镜像安全扫描与漏洞修复
【5月更文挑战第9天】Docker技术在软件开发和部署中带来便利,但其镜像安全问题不容忽视。本文探讨了Docker镜像安全扫描与漏洞修复,强调了镜像安全对应用和系统的重要性。文中介绍了静态和动态扫描方法,列举了软件漏洞、配置漏洞和恶意软件等常见安全问题,并提到了Clair和Trivy等扫描工具。修复策略包括更新软件、调整配置和重建镜像。此外,加强安全意识、规范镜像制作流程和定期扫描是管理建议。未来,将持续面对新的安全挑战,需持续研究和完善安全技术。
【Docker 专栏】Docker 镜像安全扫描与漏洞修复
|
4天前
|
Java Linux 数据安全/隐私保护
Docker自定义JDK镜像并拉取至阿里云镜像仓库全攻略
Docker自定义JDK镜像并拉取至阿里云镜像仓库全攻略
|
4天前
|
存储 弹性计算 运维
Docker数据集与自定义镜像:构建高效容器的关键要素
Docker数据集与自定义镜像:构建高效容器的关键要素
|
4天前
|
存储 缓存 运维
【Docker 专栏】Docker 镜像的分层存储与缓存机制
【5月更文挑战第8天】Docker 镜像采用分层存储,减少空间占用并提升构建效率。每个镜像由多个层组成,共享基础层(如 Ubuntu)和应用层。缓存机制加速构建和运行,通过检查已有层来避免重复操作。有效管理缓存,如清理无用缓存和控制大小,可优化性能。分层和缓存带来资源高效利用、快速构建和灵活管理,但也面临缓存失效和层管理挑战。理解这一机制对开发者和运维至关重要。
【Docker 专栏】Docker 镜像的分层存储与缓存机制
|
4天前
|
数据库 Docker 容器
【Docker 专栏】使用 Dockerfile 自动化构建 Docker 镜像
【5月更文挑战第8天】Dockerfile是构建Docker镜像的关键,它包含一系列指令,用于描述应用运行环境及所需软件包。通过自动化构建,能提高效率、保证可重复性并提升灵活性。确定基础镜像、安装依赖、设置环境后,执行Dockerfile生成镜像,用于应用程序部署。虽然需要熟悉Docker技术和应用细节,但其带来的益处使其成为现代软件开发和部署的重要工具。
【Docker 专栏】使用 Dockerfile 自动化构建 Docker 镜像
|
4天前
|
缓存 安全 数据安全/隐私保护
【Docker专栏】深入理解Docker镜像的构建与推送
【5月更文挑战第7天】本文介绍了Docker镜像的核心作用及基础概念,包括镜像作为容器模板的特性。文章详细阐述了Dockerfile的编写,例如设置基础镜像、工作目录、安装依赖及定义启动命令。通过`docker build`命令构建镜像,并提示了优化构建过程的技巧。此外,还讲解了如何将镜像推送到远程仓库,包括选择仓库、认证、标签和推送镜像的步骤,以及镜像安全性的考虑,如扫描漏洞和遵循最小权限原则。本文旨在帮助读者掌握Docker镜像的构建与推送,以高效管理容器化应用。
【Docker专栏】深入理解Docker镜像的构建与推送
|
4天前
|
运维 Linux 数据安全/隐私保护
Docker详解(九)——Docker镜像发布
Docker详解(九)——Docker镜像发布
36 2
http://www.vxiaotou.com