跳转至

09-Docker镜像构建

Dockerfile简单介绍

一张图就能学会Dockerfile你知道吗?

http://mp.weixin.qq.com/s?__biz=MzIxMDAwOTcwMA==&mid=2247483818&idx=1&sn=4a49793b166be681ce62857132dfdfbf&chksm=976a6aa1a01de3b72c3fa45c5d4fa4abb35bab99b7fd9e7ad640c2da83aef46d12ffedda64e6&scene=1&srcid=09103aPaRwojpdRyPZzcrgL2#rd

Dockerfile是非常容易学的,和SHELL相比那简单的太多了。

Dockerfile是为快速构建docker image而设计的,当你使用docker build 命令的时候,docker 会读取当前目录下的命名为Dockerfile(首字母大写)的纯文本文件并执行里面的指令构建出一个docker image。这比SaltStack的配置管理要简单的多,不过还是要掌握一些简单的指令。

Dockerfile 由一行行命令语句组成,并且支持以#开头的注释行。指令是不区分大小写的,但是通常我们都大写。

Dockerfile的四大部分

基础镜像信息
维护者信息
镜像操作指令
容器启动时执行指令

Dockerfile图表理解

类型 解释
FROM 基础镜像来自于
MAINTAINER 作者信息
RUN 你先让他干啥(把命令前面加上RUN)
ADD 往他肚子里放点文件(COPY文件,会自动解压)
WORKDIR 我的cd,(当前工作目录)
VOLUME 给我一个存放行李的地方(目录挂载)
EXPOSE 我要打开的门是啥(端口)
RUN 奔跑吧,兄弟!(进程要一直运行下去)
C 镜像启动时需要执行的

简单构建Dockerfile

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。 基于 Dockerfile 构建镜像可以使用 docker build 命令。docker build 命令中使用-f 可以指定具体的dockerfile 文件

mkdir /root/dockerfile -p
cd /root/dockerfile/
echo "hello world" >index.html

[root@linux-bkce-node21 dockerfile]# vi Dockerfile
FROM centos:7.9.2009
MAINTAINER chris
RUN yum -y install epel-release
RUN yum -y install nginx wget
COPY index.html /usr/share/nginx/html/
EXPOSE 80
ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"]

docker build -t mynginx:v1 ./

dockerfile 构建过程:
1.从基础镜像运行一个容器
2.执行一条指令,对容器做出修改
3.执行类似 docker commit 的操作,提交一个新的镜像层
4.再基于刚提交的镜像运行一个新的容器
5.执行 dockerfile 中的下一条指令,直至所有指令执行完毕

构建完毕之后,我们就可以Run起来。

docker run --name nginx01 -d -p8080:80 mynginx:v1

环境清理

docker rm -f nginx01
docker rmi -f mynginx:v1

Dockerfile指令详解

下面我们来分别介绍下上面使用到的命令:

FROM

格式:FROM或FROM:

解释:FROM是Dockerfile里的第一条指令(必须是),后面跟有效的镜像名(如果该镜像你的本地仓库没有则会从远程仓库Pull取)。然后后面的其它指令FROM的镜像中执行。

例子:

cd /root/dockerfile

cat >Dockerfile<<EOF
FROM nginx
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -d -p8080:80 mydemo:v1

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1

MAINTAINER

格式:MAINTAINER

解释:指定维护者信息。 例子:

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM nginx
MAINTAINER chris
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -d -p8080:80 mydemo:v1

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1

RUN

格式:RUN 或 RUN["executable", "param1", "param2"]。

解释:运行命令,命令较长使可以使用\来换行。推荐使用上面数组的格式

例子:

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
RUN yum -y install wget
RUN ["/bin/bash","-c","echo hello"]
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -it mydemo:v1 /bin/bash 

rpm -qa|grep wget

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1

COPY

COPY.. COPY[“”...“”] 复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

格式: COPY [--chown=:] <源路径 1>... <目标路径> COPY [--chown=:] ["<源路径 1>",... "<目标路径>"] [--chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。 <源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如: COPY hom* /mydir/ COPY hom?.txt /mydir/ <目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

例子:

cd /root/dockerfile/
echo "This is dockerfile COPY demo" >index.html

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
COPY index.html /usr/share/nginx/html/
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -it mydemo:v1 /bin/bash 

[root@f2d42048dc97 /]# cat /usr/share/nginx/html/index.html 
This is dockerfile COPY demo

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1

ADD

格式:

ADD ...

ADD ["",... ""]

解释:将指定的复制到容器文件系统中的

所有拷贝到container中的文件和文件夹权限为0755,uid和gid为0

如果文件是可识别的压缩格式,则docker会帮忙解压缩

ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下, 会自动复制并解压到 <目标路径>。 ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像 构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

例子:

cd /root/dockerfile/
echo "This is dockerfile ADD demo" >index.html
tar zcf index.html.tar.gz index.html 

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
ADD index.html.tar.gz /usr/share/nginx/html/
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -it mydemo:v1 /bin/bash 

[root@c52f02bac63f /]# ls -l /usr/share/nginx/html/
total 4
-rw-r--r-- 1 root root 28 Apr 12 02:12 index.html
[root@c52f02bac63f /]# cat /usr/share/nginx/html/index.html 
This is dockerfile ADD demo

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1
rm -f /root/dockerfile/index.html.tar.gz 

CMD

格式:

CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;

CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;

CMD ["param1","param2"] 提供给ENTRYPOINT的默认参数;

解释:

CMD指定容器启动时执行的命令,每个Dockerfile只能有一条CMD命令, 如果指定了多条,只有最后一条会被执行。 如果你在启动容器的时候也指定的命令,那么会覆盖Dockerfile构建的镜像里面的CMD命令。

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

1、CMD 在 docker run 时运行。 2、RUN 是在 docker build 构建镜像时运行的

例子:

cd /root/dockerfile/
echo "This is dockerfile CMD demo" >index.html

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
RUN yum -y install epel-release
RUN yum -y install nginx wget
COPY index.html /usr/share/nginx/html/
CMD echo "demo" >/tmp/demo.txt
CMD ["/usr/sbin/nginx","-g","daemon off;"]
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -d -p8080:80 mydemo:v1
[root@linux-bkce-node21 ~]# sh /root/in01.sh demo01
[root@8e012b8b5d9c ~]# ps -ef|grep nginx|grep sbin
root      56769  56747  0 10:25 ?        00:00:00 nginx: master process /usr/sbin/nginx -g daemon off;


[root@linux-bkce-node21 ~]# sh /root/in02.sh demo01
[root@8e012b8b5d9c /]# ls /tmp/
ks-script-DrRL8A  yum.log
[root@8e012b8b5d9c /]# ls -l /tmp/
total 4
-rwx------ 1 root root 836 Nov 13  2020 ks-script-DrRL8A
-rw------- 1 root root   0 Nov 13  2020 yum.log

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1

EXPOSE

格式:EXPOSE [...]

解释:设置Docker容器内部暴露的端口号,如果需要外部访问,还需要启动容器时增加-p或者-P参数进行分配。

EXPOSE 的用途:

1、在Dockerfile中声明了那些端口是将要开放的。

2、在构建容器时通过 -P (大写的P)可以随机映射端口。(如果EXPOSE没有指定端口,那么使用 -P 参数无效)

准确来说 Dockerfile 中的 EXPOSE用处不大!原因:

1、真正的暴露端口是在创建容器 run 的时候指定的 -p 或者 -P 参数,先来说说 -p 参数后面跟的是【主机端口:容器端口】,那么问题就来了既然在运行的时候还需要指定端口那么 EXPOSE还要什么用呢!

2、当我们创建容器 run 的时候指定参数是 -P,那么在运行之后 会把 EXPOSE 的端口随机映射到主机的不同端口,这时问题又来了既然映射到不同的端口那么容器的端口就是是随机的不确定的,那就要在运行之后才能知道端口,这样使用起来是极其不便的。

本着存在即合理的想法来解释一下EXPOSE的真正用途:

EXPOSE可以不用但是不能没有,因为 Dockerfile 不一定是一个人维护的,或者说当下一个运维人员接手项目之后能够通过 Dockerfile 里面的参数掌握整体的逻辑,一切还是为了规范。

例子:

cd /root/dockerfile/
echo "This is dockerfile EXPOSE demo" >index.html

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
RUN yum -y install epel-release
RUN yum -y install nginx wget
COPY index.html /usr/share/nginx/html/
EXPOSE 80
CMD ["/usr/sbin/nginx","-g","daemon off;"]
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -d -p 8080:80 mydemo:v1 

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1

ENTRYPOINT

格式:

ENTRYPOINT ["executable", "param1","param2"]

ENTRYPOINT command param1 param2(shell中执行)。

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。 但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 entrypoint 指令指定的程序。 优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。 注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

ENTRYPOINT ["/usr/bin/rethinkdb"]
CMD ["--help"]

列子:

现在我们开始做一个nginx镜像

cd /root/dockerfile/
echo "This is dockerfile ENTRYPOINT demo" >index.html

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
RUN yum -y install epel-release
RUN yum -y install nginx wget
COPY index.html /usr/share/nginx/html/
ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"]
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -d -p 8080:80 mydemo:v1 

[root@linux-bkce-node21 dockerfile]# curl 192.168.1.21:8080
This is dockerfile ENTRYPOINT demo

环境清理

  docker rm -f demo01
  docker rmi -f mydemo:v1

USER

格式:USER daemon

解释:指定运行容器时的用户名和UID,后续的RUN指令也会使用这里指定的用户。

用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

列子:

现在我们开始做一个centos镜像

cd /root/dockerfile/
echo "This is dockerfile USER demo" >index.html

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
RUN ls -l /root
RUN whoami
RUN useradd docker01 ; echo "123456"|passwd --stdin docker01
USER docker01
RUN whoami
EOF

docker build -t mydemo:v1 ./

ENV

格式:ENV

ENV = ...

解释:设置环境变量,可以在RUN之前使用,然后RUN命令时调用,容器启动时这些环境变量都会被指定

列子:

现在我们开始做一个centos镜像

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
ENV VERSION=1.0.0
ENV VERSION01=1.0.1
ENV VERSION02=1.0.2
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -it mydemo:v1 /bin/bash 

[root@cd8eee5f0f80 /]# env|grep VERSION
VERSION=1.0.1
[root@3ad7642b58da /]# echo $VERSION02
1.0.2

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1 

ARG

https://www.cnblogs.com/poloyy/p/15476476.html

格式:ARG[=]

解释:ARG指定了一个变量在docker build的时候使用,可以使用--build-arg =来指定参数的值,不过如果构建的时候不指定就会报错。

详解 在执行 docker build 时,可以通过 --build-arg <参数名>=<值> 来为声明的变量赋值 当镜像编译成功后,ARG 指定的变量将不再存在(ENV指定的变量将在镜像中保留) Docker内置了一些镜像创建变量,用户可以直接使用而无须声明,包括(不区分大小写)HTTP_PROXY、HTTPS_PROXY、FTP_PROXY、NO_PROXY

ARG 和 ENV 的区别 ARG 定义的变量只会存在于镜像构建过程,启动容器后并不保留这些变量 ENV 定义的变量在启动容器后仍然保留

注意 不要通过 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的

构建参数,与 ENV 作用一至。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

列子:

如果在 FROM 指令之前指定,那么只能用于 FROM 指令中

cd /root/dockerfile
cat >Dockerfile<<\EOF
FROM centos:7.9.2009
ARG DOCKER_USERNAME=ubuntu
RUN set -x ; echo ${DOCKER_USERNAME}
RUN cat /etc/redhat-release 
EOF
docker build -t mydemo:v1 --build-arg  DOCKER_USERNAME=test .
docker history  mydemo:v1

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1 

VOLUME

格式:VOLUME ["/data"]

解释:可以将本地文件夹或者其他container的文件夹挂载到container中。

定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。 作用: 1、避免重要的数据,因容器重启而丢失,这是非常致命的。 2、避免容器不断变大。

格式: VOLUME ["<路径 1>", "<路径 2>"...] VOLUME <路径> 在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

相信大部分人对docker run -v这个参数都比较熟悉,无非就是把宿主机目录和容器目录做映射,以便于容器中的某些文件可以直接保存在宿主机上,实现容器被删除之后数据还在,比如我们把mysql装在容器中,肯定不能说容器被删mysql所有的数据也都不在了。第二个作用是也可以用来实现多容器共享同一份文件。

volume和run -v的区别,什么时候需要使用volume

容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。

那么Dockerfile中的VOLUME指令实际使用中是不是就是跟docker run中的-v参数一样是将宿主机的一个目录绑定到容器中的目录以达到共享目录的作用呢? 并不然,其实VOLUME指令只是起到了声明了容器中的目录作为匿名卷,但是并没有将匿名卷绑定到宿主机指定目录的功能。 当我们生成镜像的Dockerfile中以Volume声明了匿名卷,并且我们以这个镜像run了一个容器的时候,docker会在安装目录下的指定目录下面生成一个目录来绑定容器的匿名卷(这个指定目录不同版本的docker会有所不同),我当前的目录为:/var/lib/docker/volumes/{容器ID}。

总结: volume只是指定了一个目录,用以在用户忘记启动时指定-v参数也可以保证容器的正常运行。比如mysql,你不能说用户启动时没有指定-v,然后删了容器,就把mysql的数据文件都删了,那样生产上是会出大事故的,所以mysql的dockerfile里面就需要配置volume,这样即使用户没有指定-v,容器被删后也不会导致数据文件都不在了。还是可以恢复的。

volume指定的位置在容器被删除以后数据文件会被删除吗

volume与-v指令一样,容器被删除以后映射在主机上的文件不会被删除。

如果-v和volume指定了不同的位置,会发生什么事呢?

会以-v设定的目录为准,其实volume指令的设定的目的就是为了避免用户忘记指定-v的时候导致的数据丢失,那么如果用户指定了-v,自然而然就不需要volume指定的位置了。

总结:

其实一般的dockfile如果不是数据库类的这种需要持久化数据到磁盘上的应用,都是无需指定volume的。指定volume只是为了避免用户忘记指定-v时导致的数据全部在容器中,这样的话容器一旦被删除所有的数据都丢失了。 那么为什么dockerfile中不提供一个能够映射为主机目录:容器目录这样的指令呢?其实这样的设计是有道理的,如果在dockerfile中指定了主机目录,这样dockerfile就不具备了可移植性了,毕竟每个人所需要映射的目录可能是不同的,那么最好的办法就是把这个权利交给每个运行这个dockerfile的人,所以才会有 run -v 主机目录:容器目录 这样的指令。

例子:

创建测试目录:

mkdir -p /data
echo "This is VOLUME demo" >/data/volume.txt

现在我们开始做一个centos镜像

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
VOLUME ["/data"]
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -d -it mydemo:v1 /bin/bash 

[root@linux-bkce-node21 dockerfile]# docker ps |grep demo01
5f0ae025f02a        mydemo:v1           "/bin/bash"         33 seconds ago      Up 31 seconds                           demo01
[root@linux-bkce-node21 dockerfile]# docker inspect 5f0ae025f02a|grep -A 5 Mounts
        "Mounts": [
            {
                "Type": "volume",
                "Name": "bb37ada0a46572707eeacf161295cf14d9c1cbc075ea574e107202dc1d561436",
                "Source": "/var/lib/docker/volumes/bb37ada0a46572707eeacf161295cf14d9c1cbc075ea574e107202dc1d561436/_data",
                "Destination": "/data",

docker run --name demo02  -it -v /data:/opt mydemo:v1 /bin/bash 
[root@45bf28b24396 /]# ls -l /opt/
total 8
-rw-r--r-- 1 root root 28 Apr  6 15:14 myvolume.txt
-rw-r--r-- 1 root root 20 Apr 12 04:37 volume.txt
[root@45bf28b24396 /]# cat /opt/volume.txt 
This is VOLUME demo

环境清理

docker rm -f demo01
docker rm -f demo02
docker rmi -f mydemo:v1 

WORKDIR

格式:WORKDIR/path/to/workdir

解释:切换目录,为后续的RUN、CMD、ENTRYPOINT 指令配置工作目录。

可以多次切换(相当于cd命令),

也可以使用多个WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。

现在我们开始做一个centos镜像

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
RUN mkdir /a/b/c -p
WORKDIR /a
RUN pwd
WORKDIR b
RUN pwd
WORKDIR c
RUN pwd
EOF

docker build -t mydemo:v1 ./

环境清理

docker rmi -f mydemo:v1 

LABEL

LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下: LABEL = = = ... 比如我们可以添加镜像的作者: LABEL org.opencontainers.image.authors="Chris"

现在我们开始做一个centos镜像

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
EOF

docker build -t mydemo:v1 ./

检查镜像属性

docker image inspect --format='' mydemo:v1

HEALTHCHECK

Dockerfile-HEALTHCHECK指令 https://www.cnblogs.com/weiyiming007/p/10186087.html

用于指定某个程序或者指令来监控 docker 容器服务的运行状态。 格式: HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令 HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令 HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。

下面我们主要介绍第一种形式的应用: options的可设定参数:

interval:间隔(s秒、m分钟、h小时),从容器运行起来开始计时interval秒(或者分钟小时)进行第一次健康检查,随后每间隔interval秒进行一次健康检查;还有一种特例请看timeout解析。

--start-period=DURATION 启动时间, 默认 0s, 如果指定这个参数, 则必须大于 0s ;--start-period 为需要启动的容器提供了初始化的时间段, 在这个时间段内如果检查失败, 则不会记录失败次数。 如果在启动时间内成功执行了健康检查, 则容器将被视为已经启动, 如果在启动时间内再次出现检查失败, 则会记录失败次数。

timeout:执行command需要时间,比如curl 一个地址,如果超过timeout秒则认为超时是错误的状态,此时每次健康检查的时间是timeout+interval秒。   retries:连续检查retries次,如果结果都是失败状态,则认为这个容器是unhealth的

CMD关键字后面可以跟执行shell脚本的命令或者exec数组。CMD后面的命令执行完的返回值代表容器的运行状况,可能的值:

0 health状态,1 unhealth状态,2 reserved状态,这个没细研究,用的也很少。 注意:在Dockerfile中只能有一个HEALTHCHECK指令。如果您列出多个,则只有最后一个HEALTHCHECK将生效。

现在我们开始做一个nginx镜像

cat >test.sh<<\EOF
nginx_count=$(ps -ef|grep nginx|wc -l)
if [ $nginx_count != 1 ];then
  echo 0
  exit 0
else
  echo 1
  exit 1            
fi
EOF

cd /root/dockerfile/
echo "This is dockerfile HEALTHCHECK demo" >index.html


cd /root/dockerfile
cat >Dockerfile<<EOF
FROM centos:7.9.2009
MAINTAINER chris
RUN yum -y install epel-release
RUN yum -y install nginx wget
COPY index.html /usr/share/nginx/html/
EXPOSE 80
CMD ["/usr/sbin/nginx","-g","daemon off;"]
ADD test.sh /opt/
HEALTHCHECK --interval=10s --timeout=5s --retries=3 CMD sh /opt/test.sh
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -it -d -p 8080:80 mydemo:v1 /bin/bash 
sh /root/in01.sh demo01

环境清理

docker rm -f demo01
docker rmi -f mydemo:v1 

ONBUILD

用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜 像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

ONBUILD 指定的命令在构建镜像时并不执行,而是在它的子镜像中执行

为镜像添加触发器 当一个镜像被其他镜像作为基础镜像时需要写上 OBNBUILD 会在构建时插入触发器指令

例子01:

现在我们开始做一个nginx01镜像

cd /root/dockerfile/
echo "This is dockerfile ONBUILD demo" >index.html

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM nginx
MAINTAINER chris
ONBUILD COPY index.html /usr/share/nginx/html/
EXPOSE 80
EOF

docker build -t mydemo:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -d -p 8081:80 mydemo:v1 
curl 192.168.1.21:8081

例子02:

现在我们开始做一个nginx02镜像

cd /root/dockerfile/
echo "This is dockerfile ONBUILD demo" >index.html

cd /root/dockerfile
cat >Dockerfile<<EOF
FROM mydemo:v1
MAINTAINER chris
EXPOSE 80
EOF

docker build -t mydemo:v2 ./

构建完毕之后,我们就可以Run起来。

docker run --name demo02 -d -p 8082:80 mydemo:v2
sleep 3
curl 192.168.1.21:8082

环境清理

docker rm -f demo01
docker rm -f demo02
docker rmi -f mydemo:v1
docker rmi -f mydemo:v2

生产环境如何构建镜像

系统层==》运行环境层==〉应用服务层

生产环境实战准备

(1)创建好目录

[root@linux-node1 ~]# mkdir /opt/docker
[root@linux-node1 ~]# cd /opt/docker
[root@linux-node1 docker]# mkdir app/{xxx-admin,xxx-api} -p
[root@linux-node1 docker]# mkdir runtime/{java,php,python} -p
[root@linux-node1 docker]# mkdir system/{centos,centos-ssh,ubuntu} -p

下面为创建的结构图

[root@linux-node1 docker]# cd /opt/docker
[root@linux-node1 docker]# yum -y install unzip
[root@linux-node1 docker]# tree -L 2
.
|-- app
|   |-- shop-api
|   |-- xxx-admin
|   `-- xxx-api
|-- runtime
|   |-- java
|   |-- php
|   |-- python
|   `-- python-ssh
`-- system
    |-- centos
    |-- centos-ssh
    `-- ubuntu

提前下载好centos7.6的基础镜像

docker pull centos:7.6.1810

构建基础centos镜像容器

创建基础centos镜像容器专属目录

 mkdir /root/dockerfile/centos -p

配置sshd自动启动

cd /root/dockerfile/centos
cat >>supervisord.conf<<\EOF
[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D
EOF

下载yum文件

curl -o /root/dockerfile/centos/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
curl -o /root/dockerfile/centos/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

编写centos的基本镜像的文件

cd /root/dockerfile/centos
cat >Dockerfile<<\EOF
# Docker for CentOS
#Base image
FROM centos:7.6.1810

#Who
MAINTAINER Jason.admin admin@gmail.com

#EPEL
ADD epel.repo /etc/yum.repos.d/
ADD CentOS-Base.repo /etc/yum.repos.d/

#Base pkg
RUN echo "nameserver 114.114.114.114" > /etc/resolv.conf ; cat /etc/resolv.conf ; ping -c 4 114.114.114.114 ; ping -c 4 www.baidu.com
RUN yum clean all && yum repolist
RUN yum -y install openssh-clients openssl-devel openssh-server wget mysql-devel supervisor git redis tree net-tools sudo psmisc ansible initscripts net-tools vim tmux supervisor ; yum clean all 
RUN yum group -y install "Minimal Install" ; yum clean all
RUN yum -y install wget redis git tree net-tools ansible tmux vim supervisor

# Port
EXPOSE 22

## For SSHD
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key
RUN echo "root:123456" | chpasswd

#set supervisor
RUN mkdir -p /var/log/supervisor
ADD supervisord.conf /etc/supervisord.conf

#run supervisor
CMD /usr/bin/supervisord -u root -c /etc/supervisord.conf 
EOF

(2)下面是生成基本系统镜像的文件,

[root@harbor01 centos]# ls -l /root/dockerfile/centos/
total 16
-rw-r--r-- 1 root root 2523 Apr 12 15:13 CentOS-Base.repo
-rw-r--r-- 1 root root 1027 Apr 12 15:09 Dockerfile
-rw-r--r-- 1 root root  664 Apr 12 15:13 epel.repo
-rw-r--r-- 1 root root   71 Apr 12 15:12 supervisord.conf

(3)现在开始构建镜像

cd /root/dockerfile/centos
docker build -t centos:base .

(4)查看构建的镜像

[root@linux-node1 centos]# docker images|grep centos|grep base
oldboy/centos       base     edbc1b966f2e     37 seconds ago       283.3 MB

运行一个容器

docker run --name demo01 -d -it centos:base /bin/bash 

进入容器

docker exec -it demo01 /bin/bash

rpm -qa|wc -l
cat /etc/redhat-release
ip addr
lsblk

清理容器

docker rm -f demo01

基于基础容器构建ssh容器

例子:

配置sshd nginx自动启动

mkdir -p /root/dockerfile/centos-ssh
cd /root/dockerfile/centos-ssh
cat >>supervisord.conf<<\EOF
[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D
EOF

配置dockerfile文件

mkdir -p /root/dockerfile/centos-ssh/

cd /root/dockerfile/centos-ssh
cat >Dockerfile<<\EOF
FROM centos:base
MAINTAINER chris
ADD supervisord.conf /etc/supervisord.conf
EXPOSE 22
ENTRYPOINT /usr/bin/supervisord -u root -c /etc/supervisord.conf 
EOF

docker build -t centos:ssh ./

构建完毕之后,我们就可以Run起来。

docker run --name demo01 -d -it -p 2222:22 centos:ssh /bin/bash
ssh 192.168.1.21 -p2222

环境清理

docker rm -f demo01
docker rmi -f centos:ssh

基于基础容器构建nginx容器

例子:

配置sshd 自动启动

mkdir -p /root/dockerfile/nginx
cd /root/dockerfile/nginx
cat >>supervisord.conf<<\EOF
[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

[program:nginx]
command=/usr/sbin/nginx -c /etc/nginx/nginx.conf
EOF

配置dockerfile

mkdir -p /root/dockerfile/nginx
cd /root/dockerfile/nginx
echo "This is centos base nginx demo" >index.html

cd /root/dockerfile/nginx
cat >Dockerfile<<\EOF
FROM centos:base
MAINTAINER chris
RUN yum -y install nginx wget
COPY index.html /usr/share/nginx/html/
ADD supervisord.conf /etc/supervisord.conf
EXPOSE 80
ENTRYPOINT /usr/bin/supervisord -u root -c /etc/supervisord.conf 
EOF

docker build -t mynginx:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name nginx01 -d -it -p 8080:80 -p 2222:22 mynginx:v1 /bin/bash
curl 192.168.1.21:8080
ssh 192.168.1.21 -p2222

环境清理

docker rm -f nginx01
docker rmi -f mynginx:v1

基于基础容器构建tomcat容器

例子:

mkdir -p /root/dockerfile/tomcat
cd /root/dockerfile/tomcat

cat >Dockerfile<<\EOF
FROM centos:base
MAINTAINER chris
#RUN yum install wget -y
ADD jdk-8u45-linux-x64.rpm /usr/local/
ADD apache-tomcat-8.0.26.tar.gz /usr/local/
RUN cd /usr/local && rpm -ivh jdk-8u45-linux-x64.rpm
RUN mv /usr/local/apache-tomcat-8.0.26 /usr/local/tomcat8
ENTRYPOINT /usr/local/tomcat8/bin/startup.sh && tail -F /usr/local/tomcat8/logs/catalina.out
EXPOSE 8080
EOF

docker build -t mytomcat:v1 ./

构建完毕之后,我们就可以Run起来。

docker run --name tomcat01 -d -p 8080:8080 mytomcat:v1 

打开浏览器访问:http://192.168.1.21:8080/

环境清理

docker rm -f tomcat01
docker rmi -f mytomcat:v1