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

相关文章
|
11天前
|
缓存 Linux 网络安全
docker的镜像无法下载如何解决?
【10月更文挑战第31天】docker的镜像无法下载如何解决?
329 28
|
24天前
|
缓存 监控 持续交付
|
7天前
|
存储 关系型数据库 Linux
【赵渝强老师】什么是Docker的镜像
Docker镜像是一个只读模板,包含应用程序及其运行所需的依赖环境。镜像采用分层文件系统,每次修改都会以读写层形式添加到原只读模板上。内核bootfs用于加载Linux内核,根镜像相当于操作系统,上方为应用层。镜像在物理存储上是一系列文件的集合,默认存储路径为“/var/lib/docker”。
|
11天前
|
应用服务中间件 PHP nginx
Docker-compose 编排lnmp(dockerfile) 完成Wordpress
通过使用Docker Compose,我们可以轻松编排LNMP环境并部署WordPress。本文详细介绍了各组件的Dockerfile和配置文件编写,并通过docker-compose.yml文件实现了整个环境的自动化部署。这种方法不仅简化了部署过程,还提高了环境的可移植性和一致性。希望本文能帮助你更好地理解和使用Docker Compose来管理和部署复杂的应用程序。
37 3
|
13天前
|
存储 监控 Linux
docker构建镜像详解!!!
本文回顾了Docker的基本命令和管理技巧,包括容器和镜像的增删改查操作,容器的生命周期管理,以及如何通过端口映射和数据卷实现容器与宿主机之间的网络通信和数据持久化。文章还详细介绍了如何使用Docker部署一个简单的Web应用,并通过数据卷映射实现配置文件和日志的管理。最后,文章总结了如何制作自定义镜像,包括Nginx、Python3和CentOS镜像,以及如何制作私有云盘镜像。
81 2
|
14天前
|
关系型数据库 MySQL Docker
docker环境下mysql镜像启动后权限更改问题的解决
在Docker环境下运行MySQL容器时,权限问题是一个常见的困扰。通过正确设置目录和文件的权限,可以确保MySQL容器顺利启动并正常运行。本文提供了多种解决方案,包括在主机上设置正确的权限、使用Dockerfile和Docker Compose进行配置、在容器启动后手动更改权限以及使用 `init`脚本自动更改权限。根据实际情况选择合适的方法,可以有效解决MySQL容器启动后的权限问题。希望本文对您在Docker环境下运行MySQL容器有所帮助。
28 1
|
16天前
|
存储 Java 开发者
成功优化!Java 基础 Docker 镜像从 674MB 缩减到 58MB 的经验分享
本文分享了如何通过 jlink 和 jdeps 工具将 Java 基础 Docker 镜像从 674MB 优化至 58MB 的经验。首先介绍了选择合适的基础镜像的重要性,然后详细讲解了使用 jlink 构建自定义 JRE 镜像的方法,并通过 jdeps 自动化模块依赖分析,最终实现了镜像的大幅缩减。此外,文章还提供了实用的 .dockerignore 文件技巧和选择安全、兼容的基础镜像的建议,帮助开发者提升镜像优化的效果。
|
20天前
|
存储 缓存 Java
Java应用瘦身记:Docker镜像从674MB优化至58MB的实践指南
【10月更文挑战第22天】 在容器化时代,Docker镜像的大小直接影响到应用的部署速度和运行效率。一个轻量级的Docker镜像可以减少存储成本、加快启动时间,并提高资源利用率。本文将分享如何将一个Java基础Docker镜像从674MB缩减到58MB的实践经验。
32 1
|
27天前
|
Docker 容器
docker中使用Dockerfile自动创建数据卷
【10月更文挑战第12天】
18 5
|
27天前
|
Docker 容器
docker:记录如何在x86架构上构造和使用arm架构的镜像
为了实现国产化适配,需将原x86平台上的Docker镜像转换为适用于ARM平台的镜像。本文介绍了如何配置Docker buildx环境,包括检查Docker版本、安装buildx插件、启用实验性功能及构建多平台镜像的具体步骤。通过这些操作,可以在x86平台上成功构建并运行ARM64镜像,实现跨平台的应用部署。
489 2