Docker定制镜像(Dockerfile)

一、什么是 Dockerfile?

        dockerfile 是用来构建 docker 镜像的命令参数脚本文件。

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

        Dockerfile 是一个文本文件,其内包含了一条条的指令 (Instruction),每一条指令构建一层,因此每一条指令的内容, 就是描述该层应当如何构建。

二、Dockerfile 规则:

1、格式

        # 是注释,指令建议要大写,内容小写。这样更能区分。比如:FROM centos

2、执行顺序

        docker 是按照 Dockerfile 指令顺序依次执行的,也就是说从上到下。

3、其他

        每一个 Dockerfile 的第一行都是非注释性的,也就是说第一行不能是注释,必须是 FROM 指令,来指定基础镜像,后面的指令都以基础镜像为运行环境。如果构建过程中本地没有指定镜像文件,就会去远端仓库拉。

三、Dockerfile 指令:

前提:

        首先根据自己的需要在指定目录下先创建一个 dockerfile 文件,我是在 /usr/local/dockerfile 目录下创建的自己的 xhf_dockerfile 文件。如下图所示。

        接下来我们要说的这些标签,都是这个 xhf_dockerfile 文件里面的内容。

FROM 标签

        这个 FROM 指令是 dockerfile 的第一个指令,然后指定了基础镜像,后面的所有指令都是运行在该基础镜像环境上的。

# tag 是可选的,如果不使用这两个值时,会使用 latest 版本的基础镜像
FROM <image>
FROM <image>:<tag>
# 指定基础镜像为 tomcat:8 
FROM tomcat:8

MAINTAINER 标签

        该指令是描述的维护者信息。

MAINTAINER <name>
# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 作者的名字为 xhf ,邮箱是1982392926@qq.com
MAINTAINER xhf<1982392926@qq.com>

ENV 标签

        该指令是用于定义环境变量的

# 两种写法都可以满足
ENV <key>=<value>
ENV <key> <value>
# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 作者的名字为 xhf ,邮箱是1982392926@qq.com
MAINTAINER xhf<1982392926@qq.com>

# 定义一个变量 MYPATH,路径为 /usr/local
ENV MYPATH /usr/local

WORKDIR 标签

        该指令是切换到 WORKDIR 目录下工作,这个与 linux 里面的 cd 差不多。如果 WORKDIR 不存在,它将被创建。

        需要注意的是通过 WORKDIR 设置工作目录后,Dockerfile 中其后的命令 RUNCMDENTRYPOINTADDCOPY 等命令都会在该目录下执行。在使用 docker run 运行容器时,可以通过 -w 参数覆盖构建时所设置的工作目录。

WORKDIR /usr/workdir

# 这时工作目录为/a
WORKDIR /a 

# 这时工作目录为/a/b
WORKDIR b

# 这时工作目录为/a/b/c
WORKDIR c
# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 作者的名字为 xhf ,邮箱是1982392926@qq.com
MAINTAINER xhf<1982392926@qq.com>

# 定义一个变量 MYPATH,路径为 /usr/local
ENV MYPATH /usr/local

# 切换到 MYPATH 的路径下
WORKDIR $MYPATH

        这个命令啥时候会生效呢?会等我们进入容器的时候生效,下面这个截图是如果我们不加这行命令创建的容器,默认进入时候的路径。 

        下面这个截图是加了这行命令后,进入容器默认的当前路径。

RUN 标签

        该指令用于在容器中执行命令。我们常用来安装基础软件,或者执行一些命令。

        需要注意的是 RUN 指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定 --no-cache 参数,如:docker build --no-cache

# 第一种 shell 执行,eg: RUN mkdir /usr/local/tomcat/webapps/ROOT
RUN <command>

# 第二种 exec 执行,eg:echo 'Hello xhf Docker'>/usr/local/tomcat/webapps/ROOT/index.html
RUN ["executable", "param1", "param2"]
# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 作者的名字为 xhf ,邮箱是1982392926@qq.com
MAINTAINER xhf<1982392926@qq.com>

# 定义一个变量 MYPATH,路径为 /usr/local
ENV MYPATH /usr/local

# 切换到 MYPATH 的路径下
WORKDIR $MYPATH

# 在容器的指定目录下创建一个 ROOT 文件夹
RUN mkdir -p /usr/local/tomcat/webapps/ROOT/

        也可以这么用

# 安装 yum 工具
RUN yum -y install vim

# 安装网络监测工具
RUN yum -y install net-tools

ADD 标签

        该指令是用来将宿主机某个文件或目录放到(复制)容器某个目录下面。

        如果复制的文件是 tar 类型的,那么该文件会自动解压 ( 网络压缩资源不会被解压 ) ,可以访问网络资源,类似 wget

ADD <src>... <dest>

ADD ["<src>",... "<dest>"] 

         我们自己创建一个 index.html 文件用于测试,内容很简单。

# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 作者的名字为 xhf ,邮箱是1982392926@qq.com
MAINTAINER xhf<1982392926@qq.com>

# 定义一个变量 MYPATH,路径为 /usr/local
ENV MYPATH /usr/local

# 切换到 MYPATH 的路径下
WORKDIR $MYPATH

# 在容器的指定目录下创建一个 ROOT 文件夹
RUN mkdir -p /usr/local/tomcat/webapps/ROOT/

# 将 index.html 放到指定的目录下
ADD index.html /usr/local/tomcat/webapps/ROOT/index.html

EXPOSE 标签

        该指令用于暴露容器里的端口,没有什么用,加了和不加没有什么区别,仅仅是为了告诉使用这个镜像的人,我的这个容器用到了 xxxx 端口号。

EXPOSE 端口
# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 作者的名字为 xhf ,邮箱是1982392926@qq.com
MAINTAINER xhf<1982392926@qq.com>

# 定义一个变量 MYPATH,路径为 /usr/local
ENV MYPATH /usr/local

# 切换到 MYPATH 的路径下
WORKDIR $MYPATH

# 在容器的指定目录下创建一个 ROOT 文件夹
RUN mkdir -p /usr/local/tomcat/webapps/ROOT/

# 将 index.html 放到指定的目录下
ADD index.html /usr/local/tomcat/webapps/ROOT/index.html

# 对外暴露 8080 端口
EXPOSE 8080

build 标签

        该标签用于构建镜像,我们先执行一下我上面的这些命令,验证一下,将目录切换到 /usr/local/dockerfile 下,因为我的 xhf_dockerfile 文件在这个目录下面,执行以下的命令:

docker build -f xhf_dockerfile -t mytomcat:0.1 .

# -f xhf_dockerfile 表示构建的文件路径

# -t 镜像名称:版本号

# . 表示当前路径 


# 如果你当前的目录下只有一个 dockerfile 文件,那么可以省略 -f 参数,如下所示:
docker build -t mytomcat:0.1 .


        执行完命令之后,我们可以看下镜像是否成功创建了 

        然后用我们自己的镜像创建容器,输入命令如下所示:

docker run --rm -d --name tomcat-8082 -p 8082:8080 mytomcat:0.1

        接下来我们需要验证两个东西,第一个东西是当我们进入到容器里面,默认的目录是不是 /usr/local ,第二个需要验证的是访问 tomcat 看是否可以出现登录页,因为登录页是我们自己复制过去的,如下所示,这两个需要验证的东西都没有问题。

COPY 标签

        该标签功能类似于 ADD 标签,但是是不会自动解压文件,也不能访问网络资源。

COPY <源路径> <目标路径>
# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 在容器的指定目录下创建一个 ROOT 文件夹
RUN mkdir -p /usr/local/tomcat/webapps/ROOT/

# 将 index.html 复制到指定的目录下
COPY index.html /usr/local/tomcat/webapps/ROOT/index.html

VOLUME 标签

        该标签用于在 image 中创建一个挂载目录,以挂载宿主机上的目录。

# path 代表容器中的目录,与 docker run 不同,Dockerfile 中不能指定宿主机目录,默认使用 docker 管理的挂载点
VOLUME <path>
VOLUME ["path"]
# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 在容器的指定目录下创建一个 ROOT 文件夹
RUN mkdir -p /usr/local/tomcat/webapps/ROOT/

# 将 index.html 复制到指定的目录下
COPY index.html /usr/local/tomcat/webapps/ROOT/index.html

# 创建两个挂载点
VOLUME ["/data1","/data2"]

        1、我们先使用 run -v 指定容器和宿主机的目录进行挂载,执行命令

docker run --rm -d --name tomcat-8081 -p 8081:8080 -v /usr/local/docker/ROOT:/usr/local/tomcat/webapps/ROOT tomcat:8

        查看当前容器的挂载信息,如下所示,挂载的路径是我们上面指定的宿主和容器的路径,没有问题。

        2、再次使用 docker run -v 参数,但不指定宿主机的目录,执行命令

docker run --rm -d --name tomcat-8082 -p 8082:8080 -v /usr/local/tomcat/webapps/ROOT tomcat:8

        从下面可以看到,Source 的路径是自动分配的一个目录

        3、使用 dockerfile 中挂载点进行测试。通过 docker run 命令的 -v 标识创建的挂载点只能对创建的容器有效。而通过 dockerfile VOLUME 指令可以在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点。但在 dockerfile 中无法指定主机上对应的目录,是自动生成的。相当于不指定宿主机的目录创建挂载点。执行下面的语句进行测试查看。

# 指定基础镜像为 tomcat:8 
FROM tomcat:8

# 在容器的指定目录下创建一个 ROOT 文件夹
RUN mkdir -p /usr/local/tomcat/webapps/ROOT/

# 将 index.html 复制到指定的目录下
COPY index.html /usr/local/tomcat/webapps/ROOT/index.html

# 创建两个挂载点
VOLUME ["/data1","/data2"]
# 构建镜像
docker build -f xhf_dockerfile -t mytomcat:0.3 .

# 构建容器
docker run --rm -d --name tomcat-8083 -p 8083:8080 mytomcat:0.3

        查看挂载点,会发现一共有两个自动挂载的目录,如下图所示:

CMD 标签

        该标签是构建容器后调用的,也就是在容器启动时才进行调用。

        一个 Dockerfile 只有一个 CMD 指令,若有多个,只有最后一个 CMD 指令生效。

        CMD 主要目的:为容器提供默认执行的命令,这个默认值可以包含可执行文件,也可以不包含可执行文件,意味着必须指定 ENTRYPOINT 指令(第二种写法)。

        注意和 RUN 指令的区别,RUN 是构建镜像时执行的命令,执行的时期不同。

# shell格式
CMD <命令>

# exec格式 
CMD ["可执行文件", "参数1", "参数2", …]

        修改我们的 xhf_dockerfile 文件,改写成下面的内容:

# 指定基础镜像为 centos:7
FROM centos:7

CMD echo "This is a test."

        执行下面的命令,并且启动容器,如下所示,我们可以看到可以正常执行 CMD 标签的内容。

# 构建镜像
docker build -f xhf_dockerfile -t mycentos:0.1 .

# 启动容器
docker run mycentos:0.1

ENTRYPOINT 标签

        该标签是指定容器启动的要运行的命令,可以追加命令。

        ENTRYPOINT CMD 非常类似,不同的是通过 docker run 执行的命令不会覆盖ENTRYPOINT,而 docker run 命令中指定的任何参数,都会被当做参数再次传递给 ENTRYPOINT

        Dockerfile 中只允许有一个 ENTRYPOINT 命令,多指定时会覆盖前面的设置,而只执行最后的 ENTRYPOINT 指令。

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

ENTRYPOINT command param1 param2 (shell内部命令)

        修改我们的 xhf_dockerfile 文件,改写成下面的内容:

# 指定基础镜像为 centos:7
FROM centos:7
ENTRYPOINT ["echo", "dockerfile-entrypoint test"]
ENTRYPOINT ["ls", "-a"]

        执行下面的命令,并且启动容器,如下所示,我们可以看到可以 ENTRYPOINT 标签的只执行了最后一行。

# 构建镜像
docker build -f xhf_dockerfile -t mycentos:0.2 .

# 启动容器
docker run mycentos:0.2

        接下来我们在 docker run 命令行种追加命令参数,新添加的参数会追加到原 dockerfile 的 ENTRYPOINT 命令里。

        相当于在 Dockerfile ENTRYPOINT 里追加了 -l 的参数,即【ls -a -l】。启动容器,如下所示,我们发现是可以正常执行的。

         我们再追加一些错误的参数,看看容器启动的时候会不会报错,如下所示,是会报错的。

CMD 和 ENTRYPOINT 配合使用

        一般情况下,ENTRYPOINT CMD 标签都是互相配合使用的,即:ENTRYPOINT 填写固定的命令,CMD 填写该固定命令对应的参数,CMD 将这个参数传递给 ENTRYPOINT命令。可以理解为 CMD 参数为 ENTRYPOINT 的默认值,如果项目中使用的不是 CMD 的默认值,就可以在启动 docker 容器时添加上真实的参数值,用来覆盖 CMD 的默认值。

        举个例子,比如要在镜像中通过 java -jar 的方式启动一个 java 工程,就可以采用下面的方式,默认启动的时候 commcon.jar 这个工程。

ENTRYPOINT ["java", "-jar"]

CMD ["common.jar"]

        如果我们不想启动这个 common.jar 的工程了,我们在启动容器的时候更换下命令就可以了,如下所示:

docker run 容器名称 xxxx.jar

实战生成 jdk 镜像:

        接下来我们测试下 CMD 和 ENTRYPOINT 标签,在实际应用中到底是不是按照我们想象的这种方式执行。

        第一步、新建两个 springboot 工程,很简单的就可以,命名为 DockerTestOne  和 DockerTestTwo,主要的内容我简单的贴下,如下所示,这两个工程唯一的区别就是名字和输出不一样,其他的都一样。

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.8.RELEASE</version>
	</parent>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<fork>true</fork>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
@SpringBootApplication
public class App 
{
    public static void main( String[] args )
    {
    	//将springboot应用驱动起来
        SpringApplication.run(App.class, args);
    }
}
@RestController
public class HelloWorld {
 
	@RequestMapping("/hello")
	public String Hello(String name) {
		
		return "Hello"+name+" from DockerTestOne";
	}
}
@RestController
public class HelloWorld {
 
	@RequestMapping("/hello")
	public String Hello(String name) {
		
		return "Hello"+name+" from DockerTestOne";
	}
}

        构建出两个可执行的 jar 包,分别重命名为 docker_testone.jar  docker_testtwo.jar 。然后将这两个 jar 包复制到 xhf_dockerfile 同等级的目录下备用。

        第二步、下载一个 linux 环境的 jdk,下载地址是这个,然后也把这个 jdk 复制到上面说的目录下,如图所示:

        第三步、修改 xhf_dockerfile 文件,内容如下所示:

FROM centos:7
MAINTAINER xhf<1982392926@qq.com>
ADD jdk-8u371-linux-x64.tar.gz /usr/local/
ADD docker_testone.jar /usr/local/
ADD docker_testtwo.jar /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_371
ENV CLASSPATH $JAVA_HOME/lib/dt.jar;$JAVA_HOME/lib/tools.jar
ENV PATH $JAVA_HOME/bin
ENTRYPOINT ["java", "-jar"]
CMD ["docker_testone.jar"]

        第四步、创建镜像并启动容器,如下所示:

docker build -f xhf_dockerfile -t mycentos:0.4 .

docker run -d -p 8080:8080 --name mycentos  mycentos:0.4


        第五步、在浏览器输入 http://localhost:8080/hello?name=World,结果如下所示,我们发现我们的服务可以正常启动和访问。

        第六步、关掉服务,删除容器,重新启动容器,命令如下,我们发现我们的服务被命令行的 jar 包替换掉了,启动之后还是正常的。

docker stop mycentos

docker rm -f mycentos

docker run -d -p 8080:8080 --name mycentos  mycentos:0.4 docker_testtwo.jar

实战生成 tomcat 镜像

        第一步、编写 xhf_dockerfile 文件,tomcat 下载地址在这,如下所示:

FROM centos:7
MAINTAINER xhf<1982392926@qq.com>
ADD jdk-8u371-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.33.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_371
ENV CLASSPATH $JAVA_HOME/lib/dt.jar;$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.33
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.33
ENV PATH $PATH;$JAVA_HOME/bin;$CATALINA_HOME/lib;$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.33/bin/startup.sh && tailf /usr/local/apache-tomcat-9.0.33/logs/catalina.out

        第二步、构建和启动,命令如下:

docker build -f xhf_dockerfile -t mytomcat:0.1 .

docker run -d -p 8080:8080 --name mytomcat -v /usr/local/tomcat/test:/usr/local/apache-tomcat-9.0.33/webapps/test -v /usr/local/tomcat/logs:/usr/local/apache-tomcat-9.0.33/logs mytomcat:0.1

        第三步、新建 web.xml index.jsp,内容如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
</web-app>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>youngj</title>
</head>
<body>
Hello World!<br/>
<%
System.out.println("你的 IP 地址 " + request.getRemoteAddr());
%>
</body>
</html>

 第四步、测试,在浏览器输入 http://localhost:8080/test/index.jsp,内容如下所示:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐的小三菊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值