docker学习笔记八、Dockerfile实战教程

一、初识

1、Docker 简介:

镜像的定制实际上就是定制每一层所添加的配置、文件。那么如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是Dockerfile。

Dockerfile描述了组装镜像的步骤,其中每一条命令都是单独执行的,除了FROM指令外,其他每一条指令都在上一条指定所生成的镜像基础上执行,执行完会生成一个新的镜像层,新的镜像层覆盖在原来的镜像层之上,从而形成了新的镜像。Dockerfile所生成的最终镜像就是在基础叠加镜像上一层层的镜像层组成的。

在Dockerfile中,指令不区分大小写,但是为了与参数区分,推荐大写。Docker会顺序执行Dockerfile中的指令,第一条必须是FROM指令,它用于指定构建镜像的基础镜像。在Dockerfile中,以#开头的行是注释。

 

2、FROM指令和RUN指令

FROM指定基础镜像;

格式:FROM <image>或 FROM <image>:<tag>。

FROM指令的功能是为后面的指令提供基础镜像,因此一个有效的Dockerfile必须以FROM指令作为第一条非注解指令。若FROM指令中tag参数为空,则tag默认为latest;若参数image或tag指定镜像不存在,则返回错误。

RUN执行命令;

格式:RUN <command>(shell格式)或RUN [“executable”, “param1“, “param2”](exec格式,非常推荐)。

RUN指令是用来执行命令行命令的。RUN指令会在前一条命令创建出的镜像的基础上创建一个容器,并在容器中运行命令。在命令结束运行后提交新容器为新镜像,新镜像被Dockerfile的下一条指令使用

 

3、Dockerfile构建一个镜像

步骤:

首先创建一个空文件夹:mkdir newdir;

然后进入该文件夹:cd newdir;

在该文件夹下创建一个名为Dockerfile的文件,根据实际需求补全Dockerfile的内容;

使用Dockerfile构建一个镜像:docker build -t testimage .(注意这个小数点)其中-t指定新镜像的镜像名。

构建第二次参考案例2

 

例子1:

#先创建一个新的空文件夹

#mkdir newdir

#进入这个新文件夹中

#cd newdir

#创建一个Dockerfile文件

#touch Dockerfile

#补全Dockerfile的内容(为了方便展示,这里用的是echo向Dockerfile中输入内容)

#echo "FROM ubuntu:latest" > Dockerfile

#echo "RUN mkdir /dir1" >> Dockerfile

#使用该Dockerfile构建一个名为testimage的镜像

#docker build -t testimage . //这个点注意有空格

Dockerfile 的内容为下

FROM ubuntu:latest

RUN mkdir /dir1

 

Docker指令是从上到下一层一层执行的,所以在使用这个Dockerfile构建镜像时,首先执行FROM ubuntu:latest这条指令。

FROM ubuntu:latest指定ubuntu:latest作为基础镜像,也就是将ubuntu:latest镜像的所有镜像层放置在testimage镜像的最下面。

然后执行RUN mkdir dir1指令,前面我们说过,执行RUN指令时,会在之前指令创建出的镜像的基础上创建一个临时容器,在这里的容器Id为c5117d908931,并在容器中运行命令。在命令结束运行后提交新容器为新镜像,并删除临时创建的容器c5117d908931。

在Dockerfile的所有指令执行完后,新镜像就构建完成了!

 

注意事项,谨慎使用RUN

4、修改前的Dokcerfile文件

 

修改Dockerfile 构建第二层镜像

FROM debian:jessie

RUN buildDeps='gcc libc6-dev make' \

&& apt-get update \

&& apt-get install -y $buildDeps \

&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \

&& mkdir -p /usr/src/redis \

&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-component

s=1 \

&& make -C /usr/src/redis \

&& make -C /usr/src/redis install \

&& rm -rf /var/lib/apt/lists/* \

&& rm redis.tar.gz \

&& rm -r /usr/src/redis \

&& apt-get purge -y --auto-remove $buildDeps

 

在Dockerfile的编写过程中一定要牢记一点:镜像的每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。

 

例子2

#创建一个空文件夹,并进入其中

mkdir newdir1

cd newdir1

#创建一个Dockerfile文件

touch Dockerfile

#假设我的Dockerfile文件为

#FROM ubuntu

#RUN mkdir dir1

#可以这么写:

# echo 'FROM ubuntu' > Dockerfile

# echo 'RUN mkdir dir1'>> Dockerfile

#输入Dockerfile文件内容

#********** Begin *********#

#以busybox为基础镜像

echo 'FROM busybox'> Dockerfile

#在基础镜像的基础上,创建一个hello.txt文件

echo 'RUN touch hello.txt'>> Dockerfile

#********** End **********#

#使用Dockerfile创建一个新镜像,镜像名为busybox:v1

docker build -t busybox:v1 . //注意这个点 有空格

二、docker build、COPY和ADD

1、docker buid 命令详解

Dockerfile创建完成后,可以使用docker build命令根据Dockerfile构建一个镜像。在上一关中,我们在Dockerfile所在的文件夹下执行docker build -t myimage .这条命令,然后镜像就被构建了

 

#docker build [OPTIONS] 上下文路径|URL

docker build: 用Dockerfile构建镜像的命令关键词;

[OPTIONS]: 命令选项,常用的指令包括-t指定镜像的名字,-f显示指定Dockerfile,如果不使用-f,则默认将上下文路径下的名为Dockerfile的文件认为是构建镜像的“Dockerfile”;

上下文路径|URL: 指定构建镜像的上下文的路径,构建镜像的过程中,可以且只可以引用上下文中的任何文件

 

https://docs.docker.com/engine/reference/commandline/build/#tarball-contexts

 

2、COPY指令和ADD指令

 

1)COPY复制文件;

格式:COPY <源路径> <目标路径>;

COPY 指令将从构建上下文目录中 <源路径> 的文件或目录复制到新的一层的镜像内的 <目标路径> 位置。<源路径>所指定的源必须在上下文中,即必须是上下文根目录的相对路径!<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR指令来指定,后面介绍)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建目录。

2)ADD更高级的文件复制;

格式:ADD <源路径> <目标路径>;

ADD与COPY指令在功能上十分相似,但是在COPY的基础上增加了一些功能。比如,源路径可以是一个指向一个网络文件的URL,这种情况下,Docker引擎会试图下载这个URL指向的文件到<目标路径>去。

此外,当<源路径>为一个tar压缩文件时,该压缩文件在被复制到容器中时会被解压提取。但是使用COPY指令只会将tar压缩文件拷贝到<目标路径>中

 

例子:

#创建一个空文件夹,并进入其中

mkdir newdir2

cd newdir2

#创建一个文件夹dir1,将其压缩,然后删除dir1

mkdir dir1 && tar -cvf dir1.tar dir1 && rmdir dir1

#创建一个Dockerfile文件

touch Dockerfile

#假设我的Dockerfile文件为

#FROM ubuntu

#RUN mkdir dir1

#可以这么写:

# echo 'FROM ubuntu' > Dockerfile

# echo 'RUN mkdir dir1'>> Dockerfile

#输入Dockerfile文件内容

#********** Begin *********#

#以busybox为基础镜像

echo 'FROM busybox'> Dockerfile

#并将上下文目录下的dir1.tar“解压提取后”,拷贝到busybox:v3的/

echo 'ADD dir1.tar /'>> Dockerfile

#********** End **********#



#文件内容完毕,在当前文件夹中执行

#********** Begin *********#

#以该Dockerfile构建一个名为busybox:v3的镜像

docker build -t busybox:v3 .

#********** End **********#

 

三、CMD和ENTRYPOINT指令

1、CMD指令

CMD 指定默认的容器主进程启动命令

格式:CMD <command>(shell格式)或 CMD [“executable”,”param1”,”param2”](exec格式,推荐格式)或 CMD[”param1”,”param2”]。(为ENTRYPOINT指令提供参数)

 

CMD指令提供容器启动时运行的默认命令,例如ubuntu镜像默认的CMD是/bin/bash,因此我们可以直接使用 docker run -it ubuntu进入bash。

同时也可以使用docker run -it ubuntu cat /etc/os-release,执行该命令后会输出系统版本信息。因为当在执行docker run命令时,如果显示地指定了容器的启动命令,那么会将Dockerfile中CMD设置的默认启动命令覆盖,也就是说:cat /etc/os-release命令会替代成为容器的启动命令,所以输出了系统版本信息。

在指令格式上,一般推荐使用exec格式,因为使用 shell格式时,实际的命令会被包装为 sh -c的参数的形式进行执行。比如:CMD echo $HOME,在实际执行中,会将其变更为:CMD [ "sh", "-c", "echo $HOME" ]。

 

2、ENTRYPOINT指令

ENTRYPOINT 指定默认的容器主进程启动命令

格式:ENTRYPOINT <command>(shell格式)或ENTRYPOINT [“executable”,”param1”,”param2”]。(exec格式,推荐格式)

ENTRYPOINT和CMD一样,都可以指定容器默认的启动命令,但是它又和CMD有所不同。上面我们说过,用户在执行docker run命令创建并启动容器时,如果指定了启动命令,那么“该启动命令”会覆盖CMD指令设置的默认启动命令,但是ENTRYPOINT设置的启动命令该不能被覆盖。

细心的同学可能发现了CMD命令可以为ENTRYPOINT指令提供参数。实际上,如果使用Dockerfile构建镜像时,既使用了ENTRYPOINT指令,又指定了CMD指令,那么CMD指令的含义就发生了改变, CMD 的内容将作为参数传给 ENTRYPOINT指令,换句话说实际执行时,变成了<ENTRYPOINT> <CMD>。同时,如果执行docker run基于该镜像创建并启动容器,并设置了启动命令时,docker run设置的“启动命令”依然会覆盖CMD的内容,但也仅仅是作为ENTRYPOINT指令的参数。

 

例子


 

#创建一个空文件夹,并进入其中

mkdir newdir3

cd newdir3

#创建一个Dockerfile文件

touch Dockerfile

#假设我的Dockerfile文件为

#FROM ubuntu

#RUN mkdir dir1

#可以这么写:

# echo 'FROM ubuntu' > Dockerfile

# echo 'RUN mkdir dir1'>> Dockerfile

#输入Dockerfile文件内容

#********** Begin *********#

#以busybox为基础镜像

echo 'FROM busybox'>Dockerfile

#默认情况下,将启动命令设置为df -Th。要求df命令不能被覆盖,但-Th能够被覆盖。

echo 'ENTRYPOINT ["df"]'>>Dockerfile

echo 'CMD ["-Th"]'>>Dockerfile

#********** End **********#



#文件内容完毕,在当前文件夹中执行

#********** Begin *********#

#以该Dockerfile构建一个名为mydisk:latest的镜像

docker build -t mydisk .



#********** End **********#

四、ENV、EXPOSE、WORKDIR、ARG

 

1、WORKDIR指令

WORKDIR为其他指令设置工作目录;

格式:WORKDIR <工作目录路径>;

WORKDIR指令为Dockerfile中的任何RUN,CMD,ENTRYPOINT,COPY和ADD指令设置工作目录(或称当前目录)。(也就是说以后各层的当前目录就被改为WORKDIR指定的目录)如果WORKDIR对应的目录不存在,将会自动被创建。

 

2、ENV指令

ENV设置环境变量;

格式:ENV <key> <value>或ENV <key>=<value>;

这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,还是运行时的应用,都可以直接使用这里定义的环境变量。如下所示,下面是为tomcat设置环境变量并让tomcat自启动的一个Dockerfile片段。ENV设置的环境变量CATALINA_HOME能够被后续定义指令使用。

ENV CATALINA_HOME /var/tmp/apache-tomcat-8.0.45

ENV PATH $PATH: $CATALINA_HOME/bin

ENTRYPOINT $CATALINA_HOME /bin/startup.sh && /bin/bash

 

3、ARG指令

ARG构建参数;

格式:ARG <参数名>[=<默认值>];

ARG与ENV有些类似,它们都可以被后面的其它指令直接使用,但是它并不是环境变量,这意味着将来容器运行时是不会存在ARG变量的。

Dockerfile中的ARG指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令docker build 中用 --build-arg <参数名>=<值> 来覆盖。

什么时候用ARG,什么时候用ENV?

如果想保存为环境变量,就用ENV;如果只想在Dockerfile中临时使用,就用ARG。

 

4、EXPOSE指令

EXPOSE暴露端口;

格式:EXPOSE <端口1> [<端口2>...]

EXPOSE指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。如果想要公开容器的端口,必须在docker run是指定-p参数去公开端口或者指定-P参数公开所有被EXPOSE的端口。具体可以参照https://docs.docker.com/engine/reference/run/#expose-incoming-ports 。

 

例子1

使用ENV配置java与tomcat的环境变量,由于tomcat服务会默认监听8080端口,所以使用EXPOSE暴露端口号。最后使用ENTRYPOINT设置启动命令,使tomcat服务随容器启动而启动

例子1:

FROM ubuntu

WORKDIR /var/tmp



RUN apt-get update && \

apt-get install -y wget && \

wget --no-check-certificate --no-cookies --header "Cookie: o\fraclelicense=accept-securebackup-cookie" http://download.o\fracle.com/otn-pub/java/jdk/8u144-b01/090f390dda5b47b9b721c7dfaa008135/jre-8u144-linux-x64.tar.gz && \

tar -xzf jre-8u144-linux-x64.tar.gz && \

rm jre-8u144-linux-x64.tar.gz



RUN wget "http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.0.45/bin/apache-tomcat-8.0.45.tar.gz" && \

tar -xzf apache-tomcat-8.0.45.tar.gz && \

rm apache-tomcat-8.0.45.tar.gz



ENV JAVA_HOME /var/tmp/jre1.8.0_144

ENV CATALINA_HOME /var/tmp/apache-tomcat-8.0.45

ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin



EXPOSE 8080



ENTRYPOINT /var/tmp/apache-tomcat-8.0.45/bin/startup.sh && /bin/bash

 

例子2

#创建一个空文件夹,并进入其中

mkdir newdir4

cd newdir4

#创建一个Dockerfile文件

touch Dockerfile

#假设我的Dockerfile文件为

#FROM ubuntu

#RUN mkdir dir1

#可以这么写:

# echo 'FROM ubuntu' > Dockerfile

# echo 'RUN mkdir dir1'>> Dockerfile

#输入Dockerfile文件内容

#********** Begin *********#

#以busybox为基础镜像

echo 'FROM busybox' > Dockerfile

#声明暴露3000端口

echo 'EXPOSE 3000' >> Dockerfile

#将变量var1=test设置为环境变量

echo 'ENV var1=test' >> Dockerfile

#设置工作目录为/tmp

echo 'WORKDIR /tmp' >> Dockerfile

#在工作目录下创建一个1.txt文件

echo 'RUN touch 1.txt' >> Dockerfile

#********** End **********#

#文件内容完毕,在当前文件夹中执行

#********** Begin *********#

#以该Dockerfile构建一个名为testimage:v1的镜像

docker build -t testimage:v1 .

#********** End **********#

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值