参考文章:【学习笔记】尚硅谷周阳老师的Docker教程学习笔记。
一 DockerFile
1. DockerFile
(1) 是什么
- Dockerfile是用来构建docker镜像的构建文件,是由一系列命令和参数构成的脚本。
- Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等。
(2) docker运行DockerFile的大致流程
- 1)docker从基础镜像运行一个容器。
- 2)执行一条指令并对容器做出修改。
- 3)执行类似docker commit的操作提交一个新的镜像层。
- 4)docker再基于刚提交的镜像运行一个新容器。
- 5)执行dockerFile中的下一条指令知道所有指令都执行完成。
(3) DockerFile内命令的基本语法
- 1)每条保留字指令都必须为大写字母且后面要跟随至少一个参数。
- 2)指令从上到下顺序执行。
- 3)#表示注释。
- 4)每条指令都会创建一个新的镜像层,并对镜像进行提交。
(4) DockerFile构建三步骤
- 1)编写DockerFile文件。
- 2)docker build。
- 3)docker run。
2. 保留字指令
- 1)FROM:表示当前镜像是基于哪个镜像的。
- 2)MAINTAINER:镜像维护者的姓名和邮箱地址。
- 3)RUN:容器构建时需要运行的命令。一般指额外的linux命令。
- 4)EXPOSE:当前容器对外暴露出的端口。
- 5)WORKDIR:指定在创建容器并进入容器后,终端默认登录进来的工作目录。不写默认是根目录。
- 6)ENV:用来在构建镜像过程中设置环境变量。
- 7)ADD:将宿主机目录下的文件拷贝进镜像,且ADD命令会自动处理URL和解压tar压缩包。
- 8)COPY:类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中<源文件>的文件/目录复制到镜像内的<目标路径>位置。但与ADD的唯一区别是COPY不会自动解压。
- 9)VOLUME:容器数据卷,用于数据保存和持久化工作。
- 10)CMD:指定一个容器启动时要运行的命令,DockerFile中可以有多个CMD命令,但只有最后一个生效,CMD会被docker run之后的参数替换。但是注意,如果DockerFile中可以有多个CMD命令,在docker build时同样会执行,但是在docker run时只会run最后一个CMD参数。具体看下面的案例。
- 11)ENTRYPOINT:指定一个容器启动时要运行的命令,ENTRYPOINT的目的和CMD一样,都是指定容器启动程序及参数。
- 12)ONBUILD:当构建一个继承了某个镜像(父镜像)的镜像(子镜像)时,父镜像在被子镜像继承后,若父镜像的dockerfile中有onbuild关键字,那么父镜像的onbuild会被触发。 具体看下面的案例。
3. 案例
(1) 自定义镜像myubuntu
- 1)查看默认ubuntu镜像的情况。看到是不支持vim、ifconfig等命令。
- 2)编写DokerFile文件,用于新建镜像,并且使该镜像支持vim、ifconfig,以及改变WORKDIR的默认目录。
# 1. 首先回到宿主机,在某个目录下编辑DokerFile文件。注意最好使用root权限。
mkdir /mydockerFile
cd /mydockerFile
vim Dockerfile
# 添加下面内容
# 注意,若不update,会报E: Unable to locate package vim的错误。
FROM ubuntu
MAINTAINER tyy<tyy@126.com>
ENV MYPATH /tmp
WORKDIR $MYPATH
RUN apt-get update
RUN apt-get install -y vim
RUN apt-get install -y net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "success ok"
CMD /bin/bash
- 3)构建。
# 因为名字是Dockerfile默认名字,所以不需要添加-f选项。
docker build -t myubuntu:1.3 .
- 4)运行。可以看到进入容器后,默认WORKDIR变成了/tmp,并且可以使用vim、ifconfig命令。
docker run -it myubuntu:1.3
- 5)此时我们可以查看一下镜像变更历史。
docker history myubuntu:1.3
(2) CMD/ENTRYPOINT镜像案例
DockerFile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换。在tomcat容器dockerFile的最后一行是CMD [“catalina.sh”,“run”],但如果docker run -it tomcat ls -l时,则命令行参数会把最后一行CMD命令给替换,导致tomcat不能正常启动。
注意,build时多个CMD是可以都被执行,但是只有在run的时候会被run后面的参数替换。
演示CMD。
为了演示dockfile中带有CMD的命令,这里使用tomcat的旧版本去测试。
拉取tomcat指定版本,可以先到https://hub.docker.com/_/tomcat,然后点击Tags,那么全是tomcat的版本。这里我选择旧版本的tomcat:8.5.76-jdk17-temurin(在第3页左右)。
点击进去,看到dockerfile末尾确实有CMD命令。
- 1)拉取指定版本的tomcat镜像。
docker pull tomcat:8.5.76-jdk17-temurin
- 2)编写dockerfile,该dockerfile的意思是,通过该dockerfile生成的容器,可以查询到IP信息。
cd /mydockerFile/
vim dockerfile4
#添加以下内容:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y curl
# curl命令可以用来执行下载、发送各种HTTP请求,指定HTTP头部等操作
# 执行后,https://ip.cn的html就会显示在屏幕上了
CMD ["curl","-s","https://ip.cn"]
- 3)构建。
# 注意,如果不加--no-cache选项,在执行RUN apt-get update时,会报许多错误。
docker build --no-cache -f dockerfile4 -t myip .
- 4)运行容器。
docker run myip
若加上-i,则会报错,这是因为如果在run的时候加上参数,并且dockfile创建的镜像本身就有CMD命令,那么这个-i就会被当成最后的一个CMD,所以会覆盖掉dockerfile中的CMD [“curl”,"-s",“https://ip.cn”]命令。
docker run myip -i
演示ENTRYPOINT
使用ENTRYPOINT,docker run之后的参数会被当作参数传递给ENTRYPOINT,之后形成新的命令组合,而不会覆盖。
和上面过程差不多,步骤就直接写在一起了。
# 1. 编写dockerfile文件。
cd /mydockerFile/
vim dockerfile5
#添加以下内容:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y curl
# curl命令可以用来执行下载、发送各种HTTP请求,指定HTTP头部等操作
# 执行后,https://ip.cn的html就会显示在屏幕上了
ENTRYPOINT ["curl","-s","https://ip.cn"]
# 2. 构建。
docker build --no-cache -f dockerfile5 -t myip2 .
docker images # 可以看到多了一个myip2镜像。
# 3. 运行容器。
docker run myip2
# 4. 添加CMD命令-i选项。-i在curl代表获取http头部相关信息。
docker run myip2 -i
可以看到,此时-i添加后,使用 ENTRYPOINT 的dockerfile成功获取到信息,而不像CMD会被覆盖。
(3) ONBUILD案例
以上面ENTRYPOINT的例子为基础,指在dockerfile末尾添加ONBUILD关键字。
# 1. 编写dockerfile文件。
cd /mydockerFile/
vim dockerfile6
#添加以下内容:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y curl
# curl命令可以用来执行下载、发送各种HTTP请求,指定HTTP头部等操作
# 执行后,https://ip.cn的html就会显示在屏幕上了
ENTRYPOINT ["curl","-s","https://ip.cn"]
ONBUILD RUN echo "father onbuild--------886"
# 2. 构建。
docker build --no-cache -f dockerfile6 -t myip_father .
docker images # 此时会多了一个myip_father镜像。
# 3. 创建新的dockerfile,但是该基础镜像为myip_father,代表继承于该镜像,myip_father 就是父镜像。
vim dockerfile7
#添加以下内容:
FROM myip_father
RUN apt-get update
RUN apt-get install -y curl
# 这里填CMD或者ENTRYPOINT都行,因为这里不是验证它们的作用。
CMD ["curl","-s","https://ip.cn"]
# 4. 构建父镜像为myip_father的子镜像。
# 此时在构建过程,会见到触发的信息。
docker build --no-cache -f dockerfile7 -t myip_son .
可以看到,在build子镜像时,确实触发了父镜像中的ONBUILD命令。
(4) 自定义镜像Tomcat9
# 1. 创建相关目录和文件。
mkdir -p /mydockerfile/tomcat9
cd /mydockerfile/tomcat9
touch c.txt
# 2. 下载指定版本的jdk包以及tomcat包。
# 1)先下载jdk包。我这里下载jdk-8u171-linux-x64.tar.gz。
https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html # 这个网址可以直接下载指定的jdk包,非常方便。
# 或者直接CSDN使用积分下载。我就是因为官网需要注册,所以没注册,用的积分下载,如果没积分的还是注册一下吧。
https://download.csdn.net/download/qq_39611209/10956769?spm=1001.2014.3001.5503
# 2)下载tomcat。这里下载apache-tomcat-9.0.8.tar.gz版本。
https://tomcat.apache.org/download-90.cgi # 点击Archives,选择对应版本,点击bin目录。然后选择对应压缩包,这里选择tar.gz后缀。
wget -c https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.8/bin/apache-tomcat-9.0.8.tar.gz
# 3. 编写Dockerfile文件。
vim Dockerfile
# 添加内容:
FROM ubuntu
MAINTAINER tyy<tyy@126.com>
# 把宿主机当前上下文的c.txt拷贝到容器/usr/local/路径下,并重命名为cincontainer.txt。
COPY c.txt /usr/local/cincontainer.txt
# 把java和tomcat添加到容器内,ADD会自动将它们解压。jdk解压可能比较久。
ADD jdk-8u171-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.8.tar.gz /usr/local
# 安装vim编辑器。
RUN apt-get update
RUN apt-get install -y vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 配置java和tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_171
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.8
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.8
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
# 容器运行时的监听的端口
EXPOSE 8080
# ENTRYPOINT ["/usr/local/apache-tomcat-9.0.8/bin/startup.sh"]
# CMD ["/usr/local/apache-tomcat-9.0.8/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-9.0.8/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.8/bin/logs/catalina.out
# 4. 构建。
docker build --no-cache -t tyytomcat9 .
# 5,运行。
docker run -d -p 9080:8080 --name myt9 -v /mydockerfile/tomcat9/test:/usr/local/apache-tomcat-9.0.8/webapps/test -v /mydockerfile/tomcat9/tomcat9logs/:/usr/local/apache-tomcat-9.0.8/logs --privileged=true tyytomcat9
下面是上面命令的部分重要截图:
构建前的截图。
注意,如果上面update一直报错write (28: No space left on device),可能是磁盘空间不足。使用df -h查看,发现我的/目录满了,删除部分内容后,成功update。
删除后成功构建该镜像。
验证:在浏览器访问虚拟机ip:9080。
此时可以动态查看日志。
(5) 综合前面的tomcat容器卷将测试的web服务test发布
# 在mydockerfile/tomcat9/test下
mkdir WEB-INF
cd WEB-INF
vim web.xml
cd ..
vim a.jsp
# 修改完后,重启一下该容器。
docker restart myt9
web.xml内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>test</display-name>
</web-app>
a.jsp内容:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0.1 Transitional//EN" "http://www.23.org/TR/html/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
-------------------welcome------------------
<%="I am in docker tomcat self"%>
<br>
<br>
<% System.out.println("=============docker tomcat self");%>
</body>
</html>
测试:在浏览器访问虚拟机ip:9080/test/a.jsp:
并且,如果此时直接在服务器中修改的a.jsp内容,服务器不需要再重启,就可以让客户端看到修改的内容。
当然,需要客户端即浏览器刷新一下。
例如:修改a.jsp中的一点内容并保存:
然后浏览器直接刷新,即可看到我服务器修改的内容:
html中的br代表换行。