一、dockerfile解析
官方文档: Dockerfile reference | Docker Docs
1.1、dockfile是什么?
dockerfile是用来构建docker镜像的文本文件,由一条条构建镜像所需的指令和参数构成的脚本。
之前我们介绍过通过具体容器反射构建镜像(docker commit)的方法;这里就是第二种直接构建镜像的方法。
构建三部曲:
(1)编写Dockerfile文件
(2)docker build命令构建镜像
(3)docker run镜像运行容器实例
1.2、dockerfile构建过程
1.2.1、 dockerfile基础知识
(1)每条保留字指令都必须为大写字母且后面要跟随至少一个参数;
(2)指令按照从上到下,顺序执行;
(3)#表示注释;
(4)每条指令都会创建一个新的镜像层并对镜像进行提交;
注:官网上的诸如ADD/CMD/COPY/FROM等关键字,也称保留字。
1.2.2、docker执行dockerfile的大致流程
(1)docker从基础镜像运行一个容器;
(2)执行一条指令并对容器作出修改;
(3)执行类似docker commit的操作提交一个新的镜像层;
(4)docker在基于刚提交的镜像运行一个新容器;
(5)执行dockerfile中的下一条指令直到所有指令都执行完成;
1.2.3、小总结
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,
- Dockerfile是软件的原材料
- Docker镜像是软件的交付品
- Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
- Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
- Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时会真正开始提供服务;
- Docker容器,容器是直接提供服务的。
1.3、dockerfile常用保留字指令
1.3.1、dockerfile直观认识
hub.docker.com 搜tomcat(随便搜啥都行)。都会有个"Dockerfile links",我们打开就可以看到其对应的dockerfile。
1.3.2、dockerfile常用指令
FROM:
基础镜像。即当前新镜像是基于哪个镜像,指定一个已经存在的镜像作为模板,一般第一条必须是from。
MAINTAINER:
镜像维护者的姓名和邮箱地址。
RUN:
RUN是在容器构建(docker build)时需要运行的命令。有两种格式:
(1)shell
RUN <命令行命令>
# <命令行命令>等同于,在终端操作的shell命令。
#举个例子:我需要在build的时候安装一个vim,就可以按如下写:
RUN yum -y install vim
(2)exec
RUN ["可执行文件", "参数1", "参数2"]
#例如:
RUN ["./test.php", "dev", "offline"]等价于 ./test.php dev offline
注:感觉比价鸡肋,用shell的方式其实就好了。
EXPOSE
指定当前容器对外暴露的端口。
WORDIR
指定在创建容器后,终端默认登陆进来的工作目录(就是登陆容器的默认落脚路径)。
USER
指定该镜像以什么样的用户去执行,如果都不指定默认就是root用户。
ENV
用来在构建镜像过程中设置环境变量。就理解为定义了一个变量名(key)。
#举个例子:就是定义了一个环境变量"MY_HOME"并使用。
ENV MY_HOME /usr/local/tomcat
WORKDIR $MY_HOME
ADD
将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包。相当于 COPY+解压
COPY
将宿主机目录下的文件拷贝进镜像(不解压)。显然,用ADD更好。
VOLUME
容器数据卷,用于数据保存和持久化。其实就是和 -v 选项平齐。
CMD
作用:指定容器启动后要干的事情。
CMD指令的格式和 RUN 相似,也是两种格式:
(1)shell格式: CMD <命令>
(2)exec格式: CMD["可执行文件", "参数1", "参数2" ……]
注意: Dockerfile中可以有多个CMD指令,但只有最后一个生效; 另外,CMD会被docker run之后的参数替换。
啥意思呢?解释一下。对于 tomcat 的dockerfile的最后两句如下:
EXPOSE 8080 #暴露8080端口
CMD ["catalina.sh", "run"] #执行tomcat的启动脚本catalina.sh
如果docker run不加参数的启动。 docker run -it -p 8080:8080 镜像id 就是按照dockerfile来执行。
但是如果变成了 "docker run -it -p 8080:8080 镜像id /bin/bash",那么上述的 CMD ["catalina.sh", "run"] 就会失效。
实际执行的效果变成了 CMD ["/bin/bash", "run"]; 此时容器确实是启动了,但是tomcat本身确没有正常启动,显然也不通。
和前面的RUN命令的区别:
CMD是在docker run时运行;RUN是在docker build时运行。
ENTRYPOINT
作用: 也是用来指定一个容器启动时要运行的命令。
类似于CMD指令,但是 ENTRYPOINT 不会被docker run后面的命令覆盖;而且这些命令行参数会被当做参数送给ENTRYPOINT指令指定的程序。
命令格式: ENTRYPOINT ["executeable", "param1", "param2",……]
ENTRYPOINT可以和CMD一起用,一般是变参才会使用CMD,这里的CMD等于是在给ENTRYPOINT传参。
当指定ENTRYPOINT后CMD的含义就发生了变化,不再是直接运行其明星而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合会变成 <ENTRYPOINT> "<CMD>"
看个具体的案例:假设已经通过Dockerfile构建了 nginx:test 镜像:
FROM nginx
ENTRYPOINT ["nginx", "-c"] #定参
CMD ["/etc/nginx/nginx.conf"] #变参
如果执行:
docker run nginx test
,则容器启动后,会执行nginx -c /etc/nginx/nginx.conf
docker run nginx:test /app/nginx/new.conf
,则容器启动后,会执行nginx -c /app/nginx/new.conf
1.3、实际案例
1.3.1、自定义镜像mycentosjava8
目标:docker原始的centos不具备vim、ifconfig、java等功能;现在要做的事情就是在原有centos镜像的基础上构建出具有这些功能的镜像。
(0)前期准备
#建立一个工作目录
mkdir myfile
下载jdk8的tar包放在此目录
jdk-8u171-linux-x64.tar.gz
(1)编写Dockerfile文件
注:文件名必须是 "Dockerfile"。
创建 Dockerfile 文件,并粘贴如下内容。
FROM centos
MAINTAINER zs<zs@126.com>ENV MYPATH /usr/local
WORKDIR $MYPATH#必须要添加这一坨,否则会报错
#参照:https://blog.csdn.net/weixin_51689532/article/details/127533832
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
RUN yum update -y#安装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:$PATHEXPOSE 80CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
(2)构建镜像
#格式如下
docker build -t 新镜像名字:TAG .#具体执行指令如下
docker build -t centosjava8:1.5 .注:完整的执行过程有点久,主要是"yum update -y"花了很多时间。
执行完毕后我们就可以通过 docker images查看到生成的镜像了。
(3)运行镜像
docker run -it centosjava8:1.5
(4)验证确实含有java/vim/ifconfig命令
java -version
1.3.2、虚悬镜像
虚悬镜像:仓库名、标签名都是<none>的镜像,称为dangling images(虚悬镜像)。
一般都是在镜像构建或删除出错导致出现虚悬镜像,这种镜像占用资源但是是没有意义的要予以删除。
(1)构建一个虚悬镜像。
创建包含如下内容的 Dockerfile,并构建。
from ubuntu
CMD echo 'action is success'
#构建
docker build .
(2)列出docker中的虚悬镜像
docker image ls -f dangling=true
(3)删除虚悬镜像:
docker image prune
1.3.3、自定义镜像myubuntu
二、docker微服务实战
2.1、java微服务的玩法
(0)编写java源码并构建jar包
得到 docker_boot-1.0-SNAPSHOT.jar ; java的玩法不做介绍。
(1)编写Dockerfile
FROM openjdk:8-oracle
MAINTAINER lee# 在主机 /var/lib/docker目录下创建一个临时文件,并链接到容器的 /tmp
VOLUME /tmp# 将jar包添加到容器中,并命名为 springboot_docker.jar
ADD docker_boot-1.0-SNAPSHOT.jar /springboot_docker.jar
# 运行jar包
RUN bash -c 'touch /springboot_docker.jar'
ENTRYPOINT ["java", "-jar", "/springboot_docker.jar"]# SpringBoot项目配置的端口号为6001,需要将6001暴露出去
EXPOSE 6001
(2)构建镜像
docker build -t springboot_docker:1.0 .
(3)启动容器
docker run -d -p 6001:6001 --name springboot springboot_docker:1.0
2.2、部署我的mongo_proxy
看xxx
2.3、qd的玩法
看看怎么搞搞!!!
三、docker网络
3.1、docker network简介
先简单比对下没启动docker和启动docker后的主机的网络情况。
#没有docker执行ifconfig:ens33/eth0: 这个标识宿主机的地址lo: 回环地址 127.0.0.1vibri0:#启动docker后执行ifconfig:docker0eth0lovirbro
可以看到多了个docker0。这是因为docker启动后会产生一个名为docker0的虚拟网桥。
docker就是通过docker0这个网桥进行容器↔宿主机、容器→容器间的网络通信。
Docker 服务默认会创建一个docker0
网桥(其上有一个docker0
内部接口),该桥接网络的名称为 docker0
,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。
Docker默认指定了docker0
接口的IP地址和子网掩码,让主机和容器之间可以通过网桥互相通信。
查看bridge
网络的详细信息,并通过grep
获取名称:
docker network inspect bridge | grep name
可以看到其名称为docker0
。
3.2、docker network常用命令
(1)列出当前网络
docker network ls
注:主要用的是brige、其次是host,第三个none几乎不用。(2)创建一个网络
docker network create my_network(3)删除一个网络
docker network rm my_network(4)查看网络元数据
docker network inspect bridge
3.3、docker network能干啥
(1)容器间的互联和通讯以及端口映射;
(2)容器ip变动的时候可以通过服务名直接网络通信而不受到影响(而不是写死ip);
其实就是名字服务的意思。docker1去访问docker2的mysql你绝对不能直接写死ip地址,而应该写的是访问mysql。因为重启后mysql所在的容器的ip地址是会变的!!!!!。
3.4、验证示例内默认网络ip生成规则
默认启动ubuntu系统得到u1和u2。(记得通过win+p+q退出,否则容器也随之退出了)
docker run -it --name u1 ubuntu bash
docker run -it --name u2 ubuntu bash
#查看网络情况
docker inspect 6302d5f329b3 | tail -n 25
docker inspect ffd05fb83ff1 | tail -n 25
其中bridge即表示网络模式;IPAddress即为容器的ip地址。
可以看到u1的ip是172.17.0.2,u2的ip是172.17.0.3;显然两个容器的IPAddress彼此独立。
我们现在将u2删除,然后启动u3,并查看u3的ip。
docker rm -f u2
docker run -it --name u3 ubuntu bash
docker inspect u3 | tail -n 25
发现u3的ip是172.17.0.3了(此ip之前被u2占有),显然ip是不能表征某个具体的容器的。
结论:docker容器内部的ip是可能发生改变的,ip不能用于表征具体容器。在访问的时候也就不能根据ip进行访问。
3.5、docker network网络模式有哪几种
总共有如下五种模式。主流就是bridge、host、none三种;其中bridge最常用、host次之。其他平时不怎么用。
ps: 分别使用 --network bridge / --network host / --network none 指定。
网络模式 | 简介 | 使用方式 |
bridge | 为每一个容器分配、设置IP等,并将容器连接到一个 虚拟网桥,默认为该模式 |
|
host | 容器将不会虚拟出自己的网卡、配置自己的IP等,而是使用宿主机的IP和端口 |
|
none | 容器有独立的 Network namespace,但并没有对齐进行任何网络设置,如分配 和 网桥连接、IP等 |
|
container | 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP、端口范围等 |
|
自定义 | 就是bridge模式,理解为自定义的bridge(可以实现访问名字) | / |
3.5.1、bridge模式(默认)
Docker使用Linux桥接,在宿主机虚拟一个Docker
容器网桥(docker0
),Docker启动一个容器时会根据Docker
网桥的网段分配给容器一个IP地址,称为Container-IP
,同时Docker网桥是每个容器的默认网关。因为在同一个宿主机内的容器接入同一个网桥,这样容器之间就能够通过容器的Container-IP
直接通信。
docker run
的时候,没有指定--network
的话,默认使用的网桥模式就是bridge
,使用的就是docker0
。在宿主机ifconfig
就苦役看到docker0
和自己create
的network
。
网桥docker0
创建一对对等虚拟设备接口,一个叫veth
,另一个叫eth0
,成对匹配:
整个宿主机的网桥模式都是docker0
,类似一个交换机有一堆接口。其中docker0就交换器;veth就是交换机上的插槽;运行的容器就视为一个精简后的linux,系统的eth0网卡就插在veth上。<veth,eth0>就称为veth pair。显然通过这种方式就可以实现容器和宿主机、容器与容器间的网络通信。
我们分别在宿主机和容器内执行"ip addr"可以看到匹配情况。
3.5.2、host模式
直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行 NAT 转换。
容器不会获得独立的Network Namespace,而是和宿主机公用一个Network Namespace。容器也不会虚拟出自己的网卡(eth0)而是使用宿主机的ip和端口。
如果在 docker run 命令中同时使用了 --network host 和 -p端口映射,例如:
docker run -d -p 8083:8080 --network host --name tomcat83 tomcat#不指定网址启动tomcat82
docker run -d -p 8082:8080 --name tomcat82 tomcat
执行如下指令会出现警告:
WARNING: Published ports are discarded when using host network mode
因为此时已经使用了host模式,本身就是直接使用的宿主机的IP和端口,此时的-p端口映射就没有了意义,也不会生效,端口号还是会以主机端口号为主。
正确做法是:不再进行-p端口映射,或者改用bridge模式
显然tomcat82和83的区别如下:
查看tomcat83的内部信息,因为和宿主机公用IP、端口,所以就没有自己独立的了。
docker inspect tomcat83 | tail -n 25
同理进入容器后执行“ip addr”的返回信息也和宿主机执行差不多。
此时访问tomcat的ip就是宿主机的ip,port就是默认的8080;其效果就类似于在宿主机上装了一个tomcat。
3.5.3、none模式
禁用网络功能。
在none
模式下,并不为docker容器进行任何网络配置。进入容器内,使用 ip addr
查看网卡信息,只能看到 lo
(本地回环网络127.0.0.1
网卡)。
3.5.4、container模式
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡、不会配置自己的ip,而是和一个指定的容器共享ip、端口范围等。当然,两个容器除了网络方面,其他的如文件系统、进程列表等都还是隔离的。
实例
docker run -it --name alpine1 alpine /bin/sh
# 指定和 alpine1 容器共享网络
docker run -it --netrowk container:alpine1 --name alpine2 alpine /bin/sh
3.5.5、自定义网络
自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)!!
注:这里使用 billygoo/tomcat8-jdk8 是因为这个镜像有"ip addr"/"ping"等指令(官方认证的tomcat没有这些指令)。
不用自定义网络,按照IP地址去ping是ok的,即容器1去ping容器2的ip能通,反之亦然。但是按照服务名去ping就是不通的。
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
#通过一下指令直到81的ip是 172.17.0.2, 82的ip是 172.17.0.3
docker inspect tomcat81 | tail -n 25
docker inspect tomcat82 | tail -n 25
#进入两个容器ping对方的ip发现都是通的,但是尝试ping对方名字(tomcat81、tomcat82)发现都是不通的。
docker exec -it tomcat81 /bin/bash
ping 172.17.0.3
ping tomcat82docker exec -it tomcat82 /bin/bash
ping 172.17.0.2
ping tomcat81
前面我们知道随着容器的销毁创建ip是会变的(即ip代表不了容器),但是直接访问名字又不通,那要怎么办呢?—— 自定义网络就可以解决这个问题。
自定义桥接网络,自定义网络默认使用的就是桥接网络bridge。
自定义网络效果演示:
新建自定义网络:
docker network create zszs_network
docker network ls#新建的容器加入上一步新建的自定义网络。
docker run -d -p 8081:8080 --network zszs_network --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --network zszs_network --name tomcat82 billygoo/tomcat8-jdk8#进入容器ping对方的名字,发现可以ping通
docker exec -it tomcat81 /bin/bash
#然后在进入tomcat81,tomcat82看看名字能不能ping通。果然可以ping通了。
注意:docker多容器的集群规划一定要访问名字不能直接粗暴访问ip。
四、docker-compose容器编排
官网: https://docs.docker.com/compose/
4.1、docker-compose是什么?能干什么?
一句话:容器太多(集群),而且容器之间涉及启动顺序、网络调用、一键部署、一键重启等诉求所以需要一个大总管管理起来。
compose是docker公司推出的一个工具软件,用于管理多个Docker容器组成的一个大的应用。通过定义一个yaml格式的配置文件docker-compose.yml写好容器之间的调用关系。然后,只要一个命令就能同时启动/关闭这个大规模容器集群。
docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用的资源极少。这种细粒度的划分显然就会让整体流程更加繁琐。
举个例子:我的一个web微服务可能依赖redis、mongodb、kafka、es,还可能需要注册中心、负载均衡等等;显然执行N遍docker run就太傻了。
compose运行用户通过一个单独的docker-compose.yml模板文件来定义一组相关联的应用容器为一个项目。然后就可以很容易的用一个配置文件定义一个多容器的应用。一条指令就可以完成这个项目的所有依赖并完成构建。它解决了容器与容器之间的管理编排问题。
4.2、下载与安装
安装简介参照这里: https://docs.docker.com/compose/install/
我们这里选择 "Scenario two: Install the Compose plugin" 的 “Downloading and installing manually”。
步骤如下。
1、To download and install the Compose CLI plugin, run:
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
注: wget太慢的话就手动下载然后在上传到 $DOCKER_CONFIG/cli-plugins 路径,并重命名为"docker-compose"。
2、Apply executable permissions to the binary:
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
3、测试安装
docker compose version
总结下来就是把docker-compose下载下来,然后放到$DOCKER_CONFIG/cli-plugins路径下就可以了。
卸载方式参见: https://docs.docker.com/compose/install/uninstall/
4.3、compose使用步骤
(1)一个文件: docker-compose.yml
(2)两个要素:
1)服务(service):一个个应用容器实例,比如业务微服务、mysql容器、nginx容器或redis容器。
2)工程(project):由一组关联的应用容器组成的一个完成业务单元,在docker-compose.yml文件中定义。
(3)compose使用的三个步骤
首先,编写Dockerfile定义各个微服务应用并构建出对应的镜像文件;
使用 docker-compose.yml定义一个完整业务单元,安排好整体应用中的各个容器服务。
最后,执行docker-compose.yml命令来启动并运行整个工程,完成一键部署上线。
4.4、compose常用命令
docker-compose -h #查看帮助
docker-compose up #启动所有docker-compose服务
docker-compose up -d #启动所欲docker-compose服务并后台运行
docker-compose down #停止并删除容器、网络、卷、镜像
docker-compose exec yml里面的服务id #进入容器实例内部
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 #停止服务
4.5、compose编排微服务
五、docker轻量级可视化工具Portainer
5.1、Portainer简介
(0)编
六、docker容器监控CAdvisor+InflusDB+Granfana
6.1、方案简介
(0)编