制作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

相关文章
|
1月前
|
应用服务中间件 Linux nginx
Docker镜像-手动制作yum版nginx镜像
这篇文章介绍了如何手动制作一个基于CentOS 7.6的Docker镜像,其中包括下载指定版本的CentOS镜像,创建容器,配置阿里云软件源,安装并配置nginx,自定义nginx日志格式和web页面,最后提交镜像并基于该镜像启动新容器的详细步骤。
108 21
Docker镜像-手动制作yum版nginx镜像
|
8天前
|
jenkins 持续交付 Docker
docker之自定义制作镜像(python程序)
docker之自定义制作镜像(python程序)
|
1月前
|
应用服务中间件 nginx Docker
Docker镜像-基于DockerFile制作编译版nginx镜像
这篇文章介绍了如何基于Dockerfile制作一个编译版的nginx镜像,并提供了详细的步骤和命令。
149 17
Docker镜像-基于DockerFile制作编译版nginx镜像
|
1月前
|
应用服务中间件 Linux nginx
Docker镜像管理篇
关于Docker镜像管理的教程,涵盖了Docker镜像的基本概念、管理命令以及如何制作Docker镜像等内容。
118 7
Docker镜像管理篇
|
1月前
|
应用服务中间件 Linux nginx
Docker镜像-基于DockerFile制作yum版nginx镜像
本文介绍了如何使用Dockerfile制作一个基于CentOS 7.6.1810的yum版nginx镜像,并提供了详细的步骤和命令。
75 20
|
1月前
|
Docker 容器
Docker Hub镜像公共仓库使用
这篇文章介绍了如何使用Docker Hub公共仓库进行镜像的创建、上传、下载和管理。
440 8
|
7天前
|
Docker 容器
7-13|docker build -t image-name:tag path/to/Dockerfile 这个命令具体什么意思
7-13|docker build -t image-name:tag path/to/Dockerfile 这个命令具体什么意思
|
2月前
|
运维 Ubuntu Shell
掌握Docker容器的创建:从镜像到实例
【8月更文挑战第27天】
174 4
|
2月前
|
物联网 Serverless API
函数计算产品使用问题之怎么部署Docker镜像进行lora训练
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
2月前
|
运维 前端开发 API
实战 web 应用 Docker 镜像解耦交付
实战 web 应用 Docker 镜像解耦交付
下一篇
无影云桌面