docker高级部分-Dockerfile,数据卷再认识
Docker高级部分笔记
初识Dockerfile
用来构建docker镜像的构建文件。构建docker镜像之前介绍了一种方式docker commit,另一种方式就是利用Dockerfile脚本文件生成。
解析Dockerfile文件
# 建议使用Dockerfile作为文件名
FROM centos
VOLUME ["volume1", "volume2"]
CMD "----END----"
CMD /bin/bash
# 这里的每个命令就是镜像的一层
执行Dockerfile文件
$ docker build -f Dockerfile -t nickel/my_centos .
# -f 指定dockerfile的路径
# -t target 指定目标,镜像的名称(不能以/开头)
# 最后的.不能忽略,表示输出的目录
启动刚才创建的容器
$ docker run -it --name my_iamges nickel/my_centos /bin/bash

这个卷一定在宿主机有相应的目录对应,具体可以通过命令查看
$ docker inspect my_iamges
其中有这样的信息

这就是挂载的数据卷的信息。
假设构建镜像时没有挂载镜像,可以使用-v手动挂载数据卷。
数据卷容器
$ docker run -it --name docker1 nickel/my_centos # 创建并启动容器docker1
$ docker run -it --name docker2 --volumes-from docker1 nickel/my_centos # 创建并启动容器docker2,并且卷继承docker1
# 这样就能完成两个容器的数据卷的同步
同一个镜像产生的容器,数据卷共享就是这么简单,使用--volumes-from 就可以了,即使父容器停止了,依然不影响数据共享。这种共享实际是一种备份机制,而不仅仅是共享,docker1,docker2,docker3三个容器中的volume1里的数据是复制的,其中某一容器停止,不影响其他容器里的数据共享,就和容器和宿主机数据卷挂载一样。
现在有个问题,要是两个容器是从两个不同的镜像创建的会发生什么,或者能不能挂载?
多个mysql实现数据共享
# 只要多个mysql容器挂载相同的目录就可以了
$ docker run -d -p 3301:3306 --name mysql57_01 -v /Users/nickel/mysql/conf:/etc/mysql/conf.d -v /Users/nickel/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
$ docker run -d -p 3301:3306 --name mysql57_02 --volumes-from mysql57_01 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
# 就可以实现mysql57_01和mysql57_02使用相同的数据卷
结论:
容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用为止,可以理解为数据卷是独立容器存在的,容器可以不用运行,数据卷也可以独立存在,当没有任何容器使用数据卷(挂载),则该数据卷才会被销毁。但是一旦数据卷持久化宿主机,则宿主机上的数据不会随容器的销毁而销毁。
再识Dockerfile
构建步骤:
- 编写一个Dockefile文件
- docker build构建为一个镜像
- docker run 运行镜像
- docker push 发布镜像(docker hub等镜像仓库)
Dockerfile的指令
基础知识:
- 每个保留,关键字都是大写字母
- 指令是从上到下顺序执行
- #表示注释
- 每一个指令都会创建提交一个镜像层

Dockerfile是面向开发的,我们需要经常自己写Dockerfile文件。
开发三步骤:
开发:Dockerfile:构建文件,类似源代码
运维:Docker image:通过Dockerfile构建的镜像,是要交付的东西
部署:Docker 容器:就是镜像运行起来提供服务的
常用的指令

FROM # 基础镜像,一切从这里开始构建
MAINTAINER # 镜像是谁写的,提供姓名和邮箱
RUN # 镜像构建的时候,需要运行的命令
ADD # tomcat镜像,添加tocmat的压缩包,添加内容
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露的端口配置
CMD # 指定容器启动的时候要运行的命令,主要是替代
ENTRYPOINT # 和CMD一样的,但CMD只有最后一个会生效,主要是追加命令
ONBUILD # 当构建一个被继承的Dockerfile,这个时候就会运行ONBUILD指令
COPY # 类似ADD命令,将文件复制到镜像中
EVN # 构建的时候设置环境变量
练习自己写一个Dockerfile
docker中99%都是从scratch开始的。
创建自己的centos
# 1. 编写Dockefile文件
FROM centos
MAINTAINER nickel<cumtsgw@163.com>
ENV MYPATH /usr/local # 定义变量MYPATH
WORKDIR $MYPATH # 指定工作目录,创建容器,默认进入的目录 /usr/local
RUN yum install -y vim # 安装vim
RUN yum install -y net-tools # 安装网络工具,可以使用ifconfig命令
EXPOSE 80 # 暴露端口80
CMD echo $MYPATH
CMD echo "----end----"
CMD /bin/bash
# 2. 运行Dockerfile,构建镜像
$ docker build -f Dockerfile -t mycentos:0.1 .
# 3. 测试运行
$ docker run -it --name mycentos01 mycentos:0.1
# 4. 查看镜像的构建历史
$ docker history mycentos:0.1
查看镜像的构建历史命令;docker history 镜像ID
CMD和ENTRYPOINT的区别
# CMD 命令的Dockerfile
FROM centos
CMD ["ls", "-a"]
# 构建Dockerfile
$ docker build -f Dockerfile-cmd -t centos-cmd .
# 启动容器,结果是在容器根目录执行 ls -a
$ docker run centos-cmd
# 追加命令启动,结果会报错
$ docker run centos-cmd -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# 报错原因:-l 会替换CMD中定义的命令(ls -a),所以会报错
$ docker run centos-cmd ls -al # 这样才不会报错,会用ls -al命令替换CMD中定义的["ls", "-a"]
# ENTRYPOINT 命令的Dockerfile
FROM centos
ENTRYPOINT ["ls", "-a"]
# 构建启动容器
$ docker build -f Dockerfile-entry -t centos-entry .
$ docker run centos-entry # 结果会执行ls -a
$ docker run centos-entry -l # 不会报错,结果会执行 ls -a -l
这就是CMD和ENTRYPOINT的区别,要仔细区分。
实战:tomcat镜像
-
准备镜像文件 tomat压缩包,jdk压缩包
-
编写Dockerfile文件
FROM centos MAINTAINER nickel<cumtsgw@163.com> COPY readme.txt /usr/local/readme.txt ADD jdk-8u161-linux-x64.tar.gz /usr/local # ADD命令会自动解压 ADD apache-tomcat-9.0.31.tar.gz /usr/local RUN yum install -y vim # 安装vim ENV MYPATH /usr/local WORKDIR $MYPATH # 设置工作目录,也就是启动容器时默认进入的目录 ENV JAVA_HOME /usr/local/jdk1.8.0_161 # 设置jdk相关的环境变量 ENV CLASS_PATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.31 ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.31 ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin EXPOSE 8080 # 暴露端口 CMD $CATALINA_HOME/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.31/logs/catalina.out # 执行命令,使用&&连接要执行的多条命令,如果不是后台启动,会显示tail命令的内容 -
构建镜像
$ docker build -t diy-tomcat . -
运行容器
$ docker run -d -p 9090:8080 --name nickel-tomcat -v /home/docker/diy-tomcat/test:/usr/local/apache-tomcat-9.0.31/webapps/test -v /home/docker/diy-tomcat/logs:/usr/local/apache-tomcat-9.0.31/logs diy-tomcat # 挂载了几个目录
配置的tomcat修改conf/tomcat-users.xml以后还是不能管理tomcat,不知道什么原因,后面会继续寻找原因。
宿主机不能通过localhost:8080访问tomcat manager程序的原因
远程不能访问tomat manager的解决办法找到,不知道什么原因(具体原因可能和docker的网络有关,的确,容器启动的tomcat和宿主机是两台机器,不能用宿主机访问也能说得过去,但宿主机是通过localhost:8080访问的,tomcat是如何知道我是宿主机还是容器本身访问的?),使用容器启动的tomat,如果想要宿主机访问,需要开启远程访问,即使在宿主机上使用是localhost:8080的访问地址。
开启远程访问有两种方式:
tomcat开启远程管理Manager
- 方式一:
打开/conf/Catalina/localhost/manager.xml,若没有文件,新建,添加以下内容(这种方法没测试过)
<Context privileged="true" antiResourceLocking="false"
docBase="${catalina.home}/webapps/manager">
<Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="^.*$" />
</Context>
- 方式二:
修改/webapps/manager/META-INF/context.xml,注释掉value里内容(测试过,可以实现)
<Context antiResourceLocking="false" privileged="true" >
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
</Context>
改为
<Context antiResourceLocking="false" privileged="true" >
<!--
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
-->
</Context>
/conf/tomcat-users.xml中添加角色就不说了。
<role name="manager-gui"/>
<role name="admin-gui"/>
<user username="admin" password="admin" roles="manager-gui, admin-gui"/>
发布自己的镜像到docker hub
需要有docker hub的账号密码。
$ docker login -u cumtsgw
Login Succeeded.
$ docker tag 5168a3ed0675 cumtsgw/diy-tomcat:1.0 # 添加一个标签,把个人账号信息放上去cumtsgw
$ docker push cumtsgw/diy-tomcat:1.0 # 完成提交
不打标签,很可能会报错,因为直接提交diy-tomcat:1.0很可能是hub上有镜像了,会命名重复,前面加上个人账号。
但直接 docker push cumtsgw/diy-tomcat:1.0 会报错,提示没有这样的镜像,所以,可以先打一个标签,类似给镜像起一个别名,用这个别名提交,这样就保证能找到镜像,同时又保证镜像不冲突(账号是唯一的,所以镜像名冲突的可能性基本为零)。
发布镜像到阿里云仓库,参考官方文档
小结



浙公网安备 33010602011771号