Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
执行DockerFile文件,就相当于多次使用docker commit命令构建镜像。DockerFile文件中的每条指令都需要执行一次docker commit命令。

1:每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2:指令按照从上到下,顺序执行
3:#表示注释
4:每条指令都会创建一个新的镜像层并对镜像进行提交
(1)docker从基础镜像运行一个容器
(2)执行一条指令并对容器作出修改
(3)执行类似docker commit的操作提交一个新的镜像层
(4)docker再基于刚提交的镜像运行一个新容器
(5)执行dockerfile中的下一条指令直到所有指令都执行完成
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,

1、Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
2、Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时会真正开始提供服务;
3、Docker容器,容器是直接提供服务的。
参考tomcat8的dockerfile入门:
https://github.com/docker-library/tomcat
FROM
基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是FROM
MAINTAINER
镜像维护者的姓名和邮箱地址
RUN
容器build构建时需要运行的命令
RUN是在 docker build时运行
两种格式:
shell格式:
比如:RUN yum install vim -y
exec格式

EXPOSE
当前容器对外暴露出的端口
WORKDIR
指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点

USER
指定该镜像以什么样的用户去执行,如果都不指定,默认是root
ENV
用来在构建镜像过程中设置环境变量
相当于先定义了环境变量,供后续引用该变量
ENV MY_PATH /usr/mytest
这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样;
也可以在其它指令中直接使用这些环境变量,
比如:WORKDIR $MY_PATH
VOLUME
容器数据卷,用于数据保存和持久化工作
ADD
作用相当于COPY命令+解压
将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
COPY
类似ADD,拷贝宿主机上的文件和目录到镜像中。
将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
和docker cp命令类似。
写法:COPY src dest 等同于COPY [“src”, “dest”]
CMD
CMD命令是在docker run时运行
指定容器启动后的要干的事情

相当于,指定ENTRYPOINT命令之后,就使用CMD给ENTRYPOINT传递参数。
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
ENTRYPOINT
也是用来指定一个容器启动时要运行的命令
类似于 CMD 指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序
命令格式:
ENTRYPOINT可以和CMD一起用,一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。
当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合会变成
自定义镜像mycentosjava8
要求:
Centos7镜像具备vim+ifconfig+jdk8
JDK的下载镜像地址:https://www.oracle.com/java/technologies/downloads/#java8
或者:https://mirrors.yangxingzhen.com/jdk/

cat Dockerfile
FROM centos
MAINTAINER zzyy<zzyybs@126.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安装vim编辑器
RUN yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装java8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
#ADD 是相对路径jar,把jdk-8u171-linux-x64.tar.gz添加到容器中,
#安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
构建镜像:
docker build -t centosjava8:1.5 .
从dockerhub或者阿里云镜像加速器上拉取的centos只有231MB,但是自己构建的就有756MB.
运行镜像:
docker run -it centosjava8:1.5 /bin/bash
终端进入容器,工作目录在/usr/local
vim ifconfig java-version命令都能正常使用
从镜像的构建过程能体会到docker镜像的分层结构,由一层一层的含有特定功能镜像堆叠,形成最终含有完整功能的镜像。
镜像构建或者镜像删除的时候出现一些错误,导致仓库名、标签都是.虚悬镜像对系统可能会存在一些风险,建议直接删除。
vim Dockerfile
from ubuntu
CMD echo 'action is success'
构建镜像:
docker build .

虚悬镜像已经失去存在价值,可以删除。
查看系统中的虚悬镜像:
docker image ls -f dangling=true
删除虚悬镜像:
docker image prune

virbr0:
在CentOS7的安装过程中,如果有选择相关虚拟化的的服务,安装系统后启动网卡,会发现有一个以网桥连接的私网地址的virbr0网卡(virbr0网卡:有一个固定的默认IP地址192.168.122.1),是做虚拟机网桥的使用的,其作用是为连接其上的虚机网卡提供 NAT访问外网的功能。
Linux安装,勾选安装系统的时候附带了libvirt服务才会生成的一个东西,如果不需要可以直接将libvirtd服务卸载,
yum remove libvirt-libs.x86_64

docker网路命令,查询帮助:
docker network --help
查看网络: docker network ls
查看网络源数据:docker network inspect XXX网络名字
删除网络:docker network rm XXX网络名字

bridge模式:使用–network bridge指定,默认使用docker0
host模式:使用–network host指定
none模式:使用–network none指定
container模式:使用–network container:NAME或者容器ID指定(说明和哪个容器共享IP,指定该容器IP或id)
容器的IP地址是会变化的。
比如,先创建容器:
docker run -it --name u1 ubuntu /bin/bash
docker run -it --name u2 ubuntu /bin/bash
查看两个容器的IP地址:
docker inspect u1
docker inspect u2
得知,u1的IP地址为172.17.0.2,u2的IP地址为172.17.0.3。
将容器u2删除,docker rm -f u2
再次新建容器u3,
docker run -it --name u3 ubuntu /bin/bash
发现容器u3的IP地址仍为,172.17.0.3。和已经删除的容器u2分配得到的IP地址相同。这是由于docker容器实例的变化,导致了容器IP的变化。因此,如果网络调用时,把网络IP固定写死,那么网络调用会出错。
这说明,底层的网络会变动。docker容器内部的ip是有可能会发生改变的。 这就导致一种情况,原本意图通过IP访问u2提供的服务,但是最终却访问的是容器u3的服务。因此,需要网络设计和规划。
通过网络规划设计,最终希望实现的目标:
第一:即使容器IP地址发生变动,也不能影响调用通信。
第二:希望能将需要调用的一些服务放置在同一网段内。
有些时候对网络通信、安全加固的一些特别设置,需要将某些网络就设置在指定的范围以内。例如创建网络docker network create xxxxx.
Docker 服务默认会创建一个 docker0 网桥 (其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。 Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
Docker 服务默认会创建一个 docker0 网桥,新建的容器会自动桥接到这个docker0接口。docker0相当于一根网线,一头连接到docker容器内,一头连接到宿主机,从而宿主机和容器可以相互通信。

每个容器都自带了各自的网络设备eth0,都会自动连接到docker0接口,从而和宿主机之间通过docker0相互通信。而容器之间是相互独立的,容器之间的相互通信也需要通过docker0.(容器和宿主机、容器之间都是通过docker0通信)
实例说明:
宿主机运行两个容器:
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
两两匹配验证

直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。
容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。

实例说明:
运行tomcat容器实例,使用host网络模式,并指定端口映射
docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
删除容器:
docker rm -f tomcat83
启动容器实例,会出现警告。在使用host网络模式的时候,-p 参数指示的和宿主机的端口映射就无效了。因为host模式已经指定了容器使用宿主机的IP地址和端口与外界通信。再使用-p 参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。
解决:
解决的办法就是使用docker的其他网络模式,例如–network=bridge,这样就可以解决问题,或者直接无视。。。。
正确的运行实例写法:
docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8
此时,就不会有警告
查看容器tomcat83的网络:
因为和主机共用IP和端口,就不会显示网关和IP地址。

进入容器tomcat83内部,查看:
docker exec -it tomcat83 /bin/bash
ip addr

能看到和宿主机ip addr查看到的几乎一样。
没有设置-p的端口映射了,如何访问启动的tomcat83??
http://宿主机IP:8080/
相当于是在宿主机上安装了tomcat。
在CentOS里面用默认的火狐浏览器访问容器内的tomcat83看到访问成功,因为此时容器的IP借用主机的,所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。
禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环地址)
在none模式下,并不为Docker容器进行任何网络配置。
也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo
需要我们自己为Docker容器添加网卡、配置IP等。
启动网络模式为none的容器实例:
docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8
进入容器tomcat84,内部查看:
只有本地回环地址
在容器外部查看:

新创建的container容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP、端口范围等。
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

创建容器实例演示:
docker run -d -p 8085:8080 --name tomcat85 billygoo/tomcat8-jdk8
docker run -d -p 8086:8080 --network container:tomcat85 --name tomcat86 billygoo/tomcat8-jdk8
运行出现错误:

相当于tomcat86和tomcat85公用同一个ip同一个端口,导致端口冲突
本案例用tomcat演示不合适。需要换一个镜像:
docker rm -f tomcat85
使用Alpine镜像。Alpine操作系统是一个面向安全的轻型 Linux发行版。
Alpine Linux 是一款独立的、非商业的通用 Linux 发行版,专为追求安全性、简单性和资源效率的用户而设计。经常用 Docker 的朋友可能都用过,因为他小,简单,安全而著称,所以作为基础镜像是非常好的一个选择,可谓是麻雀虽小但五脏俱全,镜像非常小巧,不到 6M的大小,所以特别适合容器打包。
运行容器实例:
docker run -it --name alpine1 alpine /bin/sh
docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh
两个容器实例的网络配置是相同的。
此时,关闭容器alpine1,查看alpine2的网络,发现只有本地回环地址了。
当容器alpine2需要共享alpine1的IP和端口时,如果alpine1容器运行,那么alpine2能共享alpine1的网络,如果alpine1容器停止,那么alpine2就只有回环地址了。
为什么有时候需要自己定义一个网络呢?
希望将所有的容器分门别类、这些容器能在各自的网络中和谐共存,并能完成网络的通信。
【如果不使用自定义网络】
创建两个tomcat容器实例:
(容器默认使用bridge网络模式)
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
容器启动成功之后,进入容器内部:
docker exec -it tomcat81 /bin/bash
docker exec -it tomcat82 /bin/bash
发现两个容器都能ping对方的IP地址:

但是不能ping通服务名:


但是在容器的网络规划和设计中,不能将服务的IP地址写成固定值。因为docker容器内部的ip是有可能会发生改变的。
没有使用自定义网络之前,两个容器不能相互ping通服务名。
【使用自定义网络】
自定义桥接网络,自定义网络默认使用的是桥接网络bridge
新建自定义网络

新建容器加入上一步新建的自定义网络
docker run -d -p 8081:8080 --network zzyy_network --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --network zzyy_network --name tomcat82 billygoo/tomcat8-jdk8
ping各自的服务名,测试


在集群上,如果构建了多台容器,那么最好自定义网络,并将容器加入到自定义网络中。因为自定义网络本身就维护好了主机名和ip的对应关系(保证了ip和域名都能通)
解决的是容器开启的太多了,该如何管理的问题。
比如容器的之间的启动顺序、涉及到的网络通信,等需要管理。
Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。 你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器
docker通过docker compose管理多个容器实例。
Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。
docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?
当需要同时部署好多个服务时,如果要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样费时费力。所以docker官方给我们提供了docker-compose多服务部署的工具。
例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。。。。。。
Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
根据compose,可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
docker compose
官网:https://docs.docker.com/compose/compose-file/compose-file-v3/
下载:https://docs.docker.com/compose/install/
docker compose安装:
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
docker compose卸载:

执行一次docker-compose up命令,相当于执行了多个docker run 运行容器的命令。
docker-compose -h # 查看帮助
docker-compose up # 启动所有docker-compose服务
docker-compose up -d # 启动所有docker-compose服务并后台运行
docker-compose down # 停止并删除容器、网络、卷、镜像。
docker-compose exec yml里面的服务id # 进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash
docker-compose ps # 展示当前docker-compose编排过的运行的所有容器
docker-compose top # 展示当前docker-compose编排过的容器进程
docker-compose logs yml里面的服务id # 查看容器输出日志
docker-compose config # 检查配置
docker-compose config -q # 检查配置,有问题才有输出
docker-compose restart # 重启服务
docker-compose start # 启动服务
docker-compose stop # 停止服务