3、ENTRYPOINT
3.1、执行时机
容器创建时执行,而不是镜像构建时执行。
3.2、解释说明
在容器启动的时候执行此命令,且Dockerfile中只有最后一个ENTRYPOINT会被执行,推荐用EXEC格式。
3.3、举例
ENTRYPOINT ["ps","-ef"]
4、代码演示
4.1、执行时机演示
FROM centos RUN ["echo", "image building!!!"] CMD ["echo", "container starting..."]
docker build -t chentongwei.com/test-docker-run .
构建镜像的过程中发现我们RUN的image building!!! 输出了,所以RUN命令是在镜像构建时执行。而并没有container starting…的输出。
docker run chentongwei.com/test-docker-run
结果:
container starting...
,足以发现CMD命令是在容器启动的时候执行。
4.2、CMD和ENTRYPOINT演示
ENTRYPOINT和CMD可以共用,若共用则他会一起合并执行。如下Demo:
FROM centos RUN ["echo", "image building!!!"] ENTRYPOINT ["ps"] CMD ["-ef"]
# 构建镜像 docker build -t chentongwei.com/docker-run . # 启动容器 docker run chentongwei.com/docker-run
输出结果:
UID PID PPID C STIME TTY TIME CMD root 1 0 0 13:02 ? 00:00:00 ps -ef
他给我们合并执行了:ps -ef
,这么做的好处在于如果容器启动的时候添加额外指令,CMD会失效,可以理解成我们可以动态的改变CMD内容而不需要重新构建镜像等操作。
比如
docker run chentongwei.com/docker-run -aux
输出结果:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 2.0 0.0 46340 1692 ? Rs 13:02 0:00 ps -aux
结果直接变成了 ps -aux
,CMD命令不执行了。但是ENTRYPOINT一定执行,这也是CMD和ENTRYPOINT的区别之一。
四、实战
1、部署应用到tomcat
1.1、准备工作
# 在服务器上创建test-dockerfile文件夹 mkdir test-dockerfile # 进入test-dockerfile目录 cd test-dockerfile # 创建需要部署到tomcat的应用 mkdir helloworld # 在helloworld目录下创建index.html写上hello dockerfile cd helloworld/ vim index.html
效果如下图:
1.2、Dockerfile
# 在test-dockerfile目录下创建Dockerfile文件,注意大写D,没有后缀。 touch Dockerfile
在Dockerfile里写上如下内容
FROM tomcat:latest MAINTAINER baidu.com WORKDIR /usr/local/tomcat/webapps ADD helloworld ./helloworld
逐行解释:
第一行:因为我们要部署应用到tomcat上,所以需要从远程仓库里拉取tomcat作为基础镜像。
第二行:描述性东西,还可以LABEL XXX XXX 添加更详细的注释信息。
第三行:cd到/usr/local/tomcat/webapps
,发现没有这个目录,我就自动创建出来,然后在cd进去
为什么是这个目录呢?因为当我们制作完镜像把容器run起来的时候tomcat的位置是在/usr/local/tomcat,加个/webapps是因为我们要将我们的应用程序扔到webapps下才能跑。如果懵,继续往下看就懂了。
第四行:tomcat有了,tomcat的webapps我们也cd进去了,那还等啥?直接把我们的应用程序拷贝到webapps下就欧了。所以ADD命令宿主机上的helloworld文件夹下的内容拷贝到当前目录(webapps,上一步刚cd进来的)的helloworld文件夹下。
1.3、制作镜像
docker build -t baidu.com/test-helloworld:1.0.0 .
. 代表当前目录。这些命令不懂的看上面的【三、Dockerfile命令】,都是上面提到的。没新知识。
命令执行后的结果
[root@izm5e3qug7oee4q1y4opibz test-dockerfile]# docker build -t baidu.com/test-helloworld:1.0.0 . Sending build context to Docker daemon 3.584kB Step 1/4 : FROM tomcat:latest ---> 1b6b1fe7261e Step 2/4 : MAINTAINER baidu.com ---> Running in ac58299b3f38 Removing intermediate container ac58299b3f38 ---> 5d0da6398f7e Step 3/4 : WORKDIR /usr/local/tomcat/webapps ---> Running in 1c21c39fc58e Removing intermediate container 1c21c39fc58e ---> 9bf9672cd60e Step 4/4 : ADD helloworld ./helloworld ---> 6d67c0d48c20 Successfully built 6d67c0d48c20 Successfully tagged baidu.com/test-helloworld:1.0.0
好像分了1/2/3/4步呢?这是啥意思。这是镜像分层的概念,下面说。现在只看到SuccessFully就哦了。
再查看下我们的镜像真实存在了吗?
docker images
完美
1.4、启动容器
docker run -d -p 8100:8080 baidu.com/test-helloworld:1.0.0 # 然后docker ps查看容器是否存在 docker ps
浏览器访问:http://服务器ip:8100/helloworld/index.html,很完美。这个helloworld就是我们Dockerfile里自己的应用程序。
1.5、进入容器
docker exec -it 730f9e144f68 /bin/bash
疑问1:怎么进入容器后直接在webapps目录下,这就是因为我们这个镜像是用Dockerfile制作的,Dockerfile上面我们自己WORKDIR到webapps目录下的呀。
答疑1:我们ls下可以看到Dockerfile里的helloworld应用就在这里
root@730f9e144f68:/usr/local/tomcat/webapps# ls helloworld
答疑2:Dockerfile里WORKDIR /usr/local/tomcat/webapps
了,为啥是这个目录也很清晰了。容器里的tomcat就在这
root@730f9e144f68:/usr/local/tomcat/webapps# pwd usr/local/tomcat/webapps
2、从0制作Redis镜像
一般没人制作Redis镜像,Redis有官方的docker镜像,docker pull一下就行。这里是为了演示上面的命令,从0到1的过程。
2.1、准备工作
1.去官网下载Redis的源码包,因为我们演示的是Redis从无到有的过程。
2.准备Redis的配置文件redis-6379.conf
2.2、Dockerfile
# 将Redis运行在centos上 FROM centos # 安装Redis所需要的基础库 RUN ["yum", "install", "-y", "gcc", "gcc-c++", "net-tools", "make"] # 将Redis目录放到/usr/local WORKDIR /usr/local # 别忘了ADD命令自带解压缩的功能 ADD redis-4.0.14.tag.gz . WORKDIR /usr/local/redis-4.0.14/src # 编译安装Redis RUN make && make install WORKDIR /usr/local/redis-4.0.14 # 将配置文件仍到Redis根目录 ADD redis-6379.conf . # 声明容器中Redis的端口为6379 EXPOSE 6379 # 启动Redis redis-server redis-6379.conf CMD ["redis-server", "redis-6379.conf"]
2.3、制作镜像&&启动容器
# 制作镜像 docker build -t chentongwei.com/docker-redis . # 查看 docker images # 启动容器 docker run -p 6379:6379 chentongwei.com/docker-redis
上面三套小连招执行完后redis就起来了,可以redis-cli去链接了。也可以docker exec进入容器去查看。
3、用docker部署jar包
FROM openjdk:8-jdk-alpine:latest ADD target/helloworld-0.0.1-SNAPSHOT.jar /helloworld.jar ENTRYPOINT ["java","-jar","/helloworld.jar"]
然后build成镜像再run启动容器,很简单粗暴。
五、补充:镜像分层的概念
1、Dockerfile
FROM tomcat:latest MAINTAINER baidu.com WORKDIR /usr/local/tomcat/webapps ADD helloworld ./helloworld
2、镜像分层
就拿上面的Dockerfile来build的话,执行过程是如下的:
Sending build context to Docker daemon 3.584kB Step 1/4 : FROM tomcat:latest ---> 1b6b1fe7261e Step 2/4 : MAINTAINER baidu.com ---> Running in ac58299b3f38 Removing intermediate container ac58299b3f38 ---> 5d0da6398f7e Step 3/4 : WORKDIR /usr/local/tomcat/webapps ---> Running in 1c21c39fc58e Removing intermediate container 1c21c39fc58e ---> 9bf9672cd60e Step 4/4 : ADD helloworld ./helloworld ---> 6d67c0d48c20 Successfully built 6d67c0d48c20 Successfully tagged baidu.com/test-helloworld:1.0.0
会发现我们Dockerfile文件内容一共四行,执行过程也是Step 1/2/3/4四步,这就知道了Dockerfile内容的行数决定了Step的步骤数。
那么每一步都代表啥呢?
其实每一步都会为我们创建一个临时容器,这样做的好处是如果下次再构建这个Dockerfile的时候,直接从cache里读出已有的容器,不重复创建容器,这样大大节省了构建时间,也不会浪费资源重复创建容器。比如如下:
FROM tomcat:latest MAINTAINER baidu.com WORKDIR /usr/local/tomcat/webapps ADD helloworld ./helloworld ADD helloworld ./helloworld2
啥也没动,就是多部署一份helloworld且在容器内部改名为helloworld2,接下来看执行过程
Step 1/5 : FROM tomcat:latest ---> 1b6b1fe7261e Step 2/5 : MAINTAINER baidu.com ---> Using cache ---> 5d0da6398f7e Step 3/5 : WORKDIR /usr/local/tomcat/webapps ---> Using cache ---> 9bf9672cd60e Step 4/5 : ADD helloworld ./helloworld ---> Using cache ---> 6d67c0d48c20 Step 5/5 : ADD helloworld ./helloworld2 ---> 4e5ffc24522f Successfully built 4e5ffc24522f Successfully tagged baidu.com/test-helloworld:1.0.1
首先可以发现如下:
1.Step变成了5步。
2.前四步骤用了缓存Using Cache,并没有重复创建容器。Step 1 没有Using Cache是因为它是从本地仓库直接拉取了tomcat:latest当作基础镜像,run的时候会创建容器。
3.第五步重新创建了临时容器。
END