视频链接:https://www.bilibili.com/video/BV1og4y1q7M4
Docker镜像讲解
镜像是什么
Docker镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含某个软件所需的所有内容,包括代码、库、环境变量、配置文件、运行时环境等。
所有的应用,直接打包成Docker镜像,然后通过镜像创建出容器,然后就可以直接跑起来。
如何得到镜像:
从远程仓库下载,比如docker hub、阿里云的镜像仓库等。
朋友拷贝给你。
自己制作一个镜像DockerFile。
通过对原有的镜像创建的容器进行一些修改(也可以不修改),然后通过Commit命令提交一个新的镜像。
UnionFS(联合文件系统)
联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来只能看到一个文件系统。联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
Docker镜像加载原理
docker镜像实际上是由一层层的文件系统组成,这种层级文件系统就是联合文件系统。
bootfs(boot file system)主要包含BootLoader和kernel,BootLoader主要负责引导加载kernel,Linux刚启动时会加载bootfs文件系统来引导内核的加载,Docker镜像的最底层就是bootfs。这一层与我们典型的unix系统是一样的,包含boot引导器和内核,当boot加载完成后整个内核就在内存中了,此时内存的使用权已经由bootfs转交给内核,此时系统会卸载bootfs。
rootfs(root file system),包含典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同Unix操作系统发行版(Ubuntu,Centos等)。因为底层直接用Host的kernel,rootfs只包含最基本的命令,工具和程序就可以了。
我们平时安装的虚拟机centos镜像好几个G,Docker安装的才200多M,因为对于一个精简的OS,rootfs可以很小,只需包含最基本的命令,工具和程序库就行了,因为底层直接使用宿主机的内核,自己只需提供rootfs(相当于操作内核的客户端)就可以,由此可见不同发行版的bootfs基本是一致的,roorfs有差别,因此不同的发行版可以公有bootfs。虚拟机是分钟级别,容器是秒级。
第一个图仅仅是bootfs+rootfs,然后如果要制作一个emacs环境的镜像,就在这个基础上新加一层emacs镜像,如图二。如果要在添加一个Apache环境,那就再图二基础上加一个apache镜像。如图三。图中的每一层镜像都能进行复用。
分层理解
分层的镜像
比如:上面的redis镜像。使用docker inspect redis镜像的ID 命令查看镜像的元信息,找到layer信息。
由上图可以看到下载的redis镜像是由6个镜像一层层组成的。
这些镜像都是一个个独立可复用的镜像,如果下载其他镜像是,某一层镜像是已经存在本地的了,就不用在下载,直接复用该镜像,节省空间。比如上面下载redis镜像时,提示某个镜像已经存在。
注意 :
Docker镜像都是只读的,用镜像创建容器启动时,实际上是在原本的镜像上新建了一层可写层到原本镜像的顶部,这一层我们叫作容器层,容器层之下的叫作镜像层。
如上图,使用Tomcat镜像创建容器后,会在Tomcat镜像的基础上新建一个可写层,容器的写入是在可写层进行记录,然后使用commit命令把该容器创建一个新的镜像,实际上新的镜像是tomcat镜像+可写层镜像,以tomcat镜像为基础。通过下面介绍使用容器构建镜像,可以更好地理解。
commit镜像
命令:
1 2 docker commit 提交容器成为一个新的副本 docker commit -m="描述信息,类似git提交的信息" -a="作者" 容器id 目标镜像名:[TAG]$ 编辑容器后提交容器成为一个新镜像a
实战测试
1 2 3 4 5 6 7 8 9 docker run -it -p 8080:8080 tomcat cp -r webapps.dist/* webapps docker commit =a="lixingze" -m="add webapps app" 容器ID tomcat02:1.0
容器数据卷
什么是容器数据卷
为了实现数据持久化,使容器之间可以共享数据。可以将容器内的目录,挂载到宿主机上或其他容器内,实现同步和共享的操作。即使将容器删除,挂载到本地的数据卷也不会丢失。
docker的理念回顾
将应用和环境打包成一个镜像
数据如果都在容器中,那么我们容器一旦删除,数据就会丢失 需求:数据可以持久化
MySQL,容器删了,删库跑路 需求:MySQL数据可以存储在本地
容器之间可以有一个数据共享的技术,Docker容器中产生的数据同步到本地
这就是卷技术,目录的挂载将我们容器内的目录挂载到Linux上面
容器的持久化和同步操作,容器间也是可以做数据共享的
使用数据卷
方式一:直接使用命令来挂载 -v
1 2 3 4 5 6 7 docker run -it -v 主机目录:容器内目录 $ docker run -it -v /home/ceshi:/home centos /bin/bash $ docker inspect 990046eb50f5
测试文件的同步 (在主机上改动,观察容器变化)
再来测试 (测试通过)
停止容器
主机上修改文件
启动容器
容器内的数据依旧是同步的!
好处:我们以后只要在本地进行修改即可,不需要每次都进入容器内部都能实现自动同步
实战:安装MySQL
思考:MySQL的数据持久化的问题!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ docker pull mysql -d 后台运行 -p 端口映射 -e 环境配置 --name 容器名字 $ docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=[password] --name mysql01 mysql
假设删除了docker镜像
发现,挂载到本地的数据卷依然没有丢失,这实现了容器数据持久化功能
具名和匿名挂载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 -v 容器内挂载 docker run -d -P --name nginx01 -v /etc/nginx nginx $ -P 随机指定端口 $ docker volume ls DRIVER VOLUME NAME local 8026aa409bf4851f9056cc3092e27493db1ee654b461b1ea05c3bedfafed3e32$ docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx 72bf479b190b74d53256311e626bab459630af2028b9c9e1a83064c2ff745d9e $ docker volume ls $ docker volume ls DRIVER VOLUME NAME local 8026aa409bf4851f9056cc3092e27493db1ee654b461b1ea05c3bedfafed3e32local 04862392ad0fcff6ecabd65989ae57e109a9963de0d517f4e8b305b44700795alocal d7111fd00a6d3704ec4bd5419eee07ba6c4bac699abe227b908f6d385188c472local jumping-nginx $ docker volume inspect juming-nginx [ { "CreatedAt" : "2022-04-19T14:52:24+08:00" , "Driver" : "local" , "Labels" : null, "Mountpoint" : "/var/lib/docker/volumes/juming-nginx/_data" , "Name" : "jumping-nginx" , "Options" : null, "Scope" : "local" } ]
所有docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/xxxxx/_data
我们通过具名挂载可以方便找到我们的一个卷,大多数情况都使用具名挂载
1 2 3 4 -v 容器内路径 -v 卷名:容器内路径 -v /宿主机路径::容器内路径
拓展
1 2 3 4 5 6 7 8 9 ro read only rw read write $ docker run -d -P --name nginx02 -v jumping-nginx:/etc/nginx:ro nginx $ docker run -d -P --name nginx02 -v jumping-nginx:/etc/nginx:rw nginx
初识DockerFile
Dockerfile就是用来构建docker镜像的构建文件!也就是命令脚本
通过这个脚本可以生成一个镜像,镜像是一层一层的,脚本一个个的命令对应层数
1 2 3 4 5 6 7 8 9 10 FROM centos VOLUME ["volume01" ,"volume02" ] CMD echo "----end----" CMD /bin/bash
部署容器
启动自己的容器
这个卷和外部一定有一个同步的目录!
在目录下新建一个测试用的文件
查看卷挂载的路径
测试一下刚才的文件是否同步到主机上了
成功。
这种方式我们未来使用的十分多, 因为我们通常会构建自己的镜像!
假设构建镜像时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径!
数据卷容器
多个mysql同步数据!
CTRL+p+q 退出当前的docker01,通过docker01挂载docker02
同理创建一个docker03,在数据卷上新建的文件也会在其它容器上同步
多个mysql实现数据共享
1 2 3 4 5 $ docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=[password] --name mysql01 mysql $ docker run -d -p 3311:3306 -e MYSQL_ROOT_PASSWORD=[password] --name mysql02 --volumes-from mysql01 mysql
结论
容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用为止。
但是一旦持久化到了本地,这个时候,本地的数据是不会被删除的。
DockerFile
DockerFile介绍
Dockerfile是用来构建docker镜像的文件,命令参数脚本
构建步骤:
构建一个dockerfile文件
docker build 构建成为一个镜像
docker run 运行镜像
docker push 发布镜像(DockerHub、阿里云镜像仓库)
查看一下官方是怎么做的
很多官方镜像都是基础包,我们通常会自己搭建自己的镜像。
官方既然可以制作镜像,那我们也可以自己构建。
DockerFile构建过程
基础知识:
每个保留关键字(指令)都必须是大写字母
执行顺序从上到下
#表示注释
每一个指令都会创建提交一个新的镜像层,并提交!
Docker镜像原理
Dockerfile概念及作用
Dockerfile 关键字
关键字
作用
备注
FROM
指定父镜像
指定dockerfile基于那个image构建
MAINTAINER
作者信息
用来标明这个dockerfile谁写的
LABEL
标签
用来标明dockerfile的标签 可以使用Label代替Maintainer 最终都是在docker image基本信息中可以查看
RUN
执行命令
执行一段命令 默认是/bin/sh 格式: RUN command 或者 RUN [“command” , “param1”,“param2”]
CMD
容器启动命令
提供启动容器时候的默认命令 和ENTRYPOINT配合使用.格式 CMD command param1 param2 或者 CMD [“command” , “param1”,“param2”]
ENTRYPOINT
入口
一般在制作一些执行就关闭的容器中会使用
COPY
复制文件
build的时候复制文件到image中
ADD
添加文件
build的时候添加文件到image中 不仅仅局限于当前build上下文 可以来源于远程服务
ENV
环境变量
指定build时候的环境变量 可以在启动的容器的时候 通过-e覆盖 格式ENV name=value
ARG
构建参数
构建参数 只在构建的时候使用的参数 如果有ENV 那么ENV的相同名字的值始终覆盖arg的参数
VOLUME
定义外部可以挂载的数据卷
指定build的image那些目录可以启动的时候挂载到文件系统中 启动容器的时候使用 -v 绑定 格式 VOLUME [“目录”]
EXPOSE
暴露端口
定义容器运行的时候监听的端口 启动容器的使用-p来绑定暴露端口 格式: EXPOSE 8080 或者 EXPOSE 8080/udp
WORKDIR
工作目录
指定容器内部的工作目录 如果没有创建则自动创建 如果指定/ 使用的是绝对地址 如果不是/开头那么是在上一条workdir的路径的相对路径
USER
指定执行用户
指定build或者启动的时候 用户 在RUN CMD ENTRYPONT执行的时候的用户
HEALTHCHECK
健康检查
指定监测当前容器的健康监测的命令 基本上没用 因为很多时候 应用本身有健康监测机制
ONBUILD
触发器
当存在ONBUILD关键字的镜像作为基础镜像的时候 当执行FROM完成之后 会执行 ONBUILD的命令 但是不影响当前镜像 用处也不怎么大
STOPSIGNAL
发送信号量到宿主机
该STOPSIGNAL指令设置将发送到容器的系统调用信号以退出。
SHELL
指定执行脚本的shell
指定RUN CMD ENTRYPOINT 执行命令的时候 使用的shell
Dockerfile 案例
dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单。
步骤:开发、部署、上线运维
Dockerfile:构建文件,定义了一切的步骤,源代码
Dockerimage:通过Dockerfile构建生成的镜像,最终发布和运行的产品
Docker容器:镜像运行起来提供服务
DockerFile指令
以前的时候我们是使用别人的,现在我们尝试用dockerfile构建一个自己的镜像
1 2 3 4 5 6 7 8 9 10 11 12 FROM MAINTAINER RUN ADD WORKDIR VOLUME EXPOSE CMD ENTRYPOINT ONBUILD COPY ENV
实战测试
DockerHub中99%的镜像都是从基础镜像过来的 FROM scratch,然后配置需要的软件和配置来进行构建
创建一个自己的centos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 $ cat mydockerfile-centos FROM centos:7 MAINTAINER lixingze<lixingzee@gmail.com> ENV MYPATH /user/local WORKDIR $MYPATH RUN yum -y install vim RUN yum -y install net-tools EXPOSE 80 CMD echo $MYPATH CMD echo "-----end-----" CMD /bin/bash $ docker build -f mydockerfile-centos -t mycentos:0.1 . Successfully built c8310c185c21 Successfully tagged mycentos:0.1
对比之前原生的centos:增加之后的镜像默认进入了工作目录,且可以使用vim
我们可以列出本地进行的变更历史
我们平时拿到一个镜像,可以研究一下它是如何构建的了。
CMD 和 ENTRYPOINT 区别
测试CMD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 $ cat dockerfile-cmd-test FROM centos:7 CMD ["ls" ,"-a" ] $ docker build -f dockerfile-cmd-test -t cmdtest . $ docker run 608fe2c633ee . .. .dockerenv bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var $ docker run 608fe2c633ee -l docker: Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: exec : "-l" : executable file not found in $PATH : unknown.
测试ENTRYPOINT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 $ vim dockerfile-entrypoint-test FROM centos ENTRYPOINT ["ls" , "-a" ] $ docker build -f dockerfile-entrypoint-test -t entrypoint-test . $ docker run entrypoint-test . .. .dockerenv bin dev etc home lib $ docker run entrypoint-test -l total 56 drwxr-xr-x 1 root root 4096 Aug 13 07:52 . drwxr-xr-x 1 root root 4096 Aug 13 07:52 .. -rwxr-xr-x 1 root root 0 Aug 13 07:52 .dockerenv lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin drwxr-xr-x 5 root root 340 Aug 13 07:52 dev drwxr-xr-x 1 root root 4096 Aug 13 07:52 etc drwxr-xr-x 2 root root 4096 May 11 2019 home lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64 drwx------ 2 root root 4096 Aug 9 21:40 lost+found
DockerFile中很多命令都十分相似,我们需要了解他们的区别,最好的学习方式就是测试并对比效果
实战:Tomcat镜像
准备镜像文件 tomcat 压缩包
编写dockerfile文件,官方命名Dockerfile
,build会自动寻找这个文件,就不需要 -f 进行指定了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ cat Dockerfile FROM centos:7 MAINTAINER lixingze<lixingzee@gmail.com> COPY README.txt /user/local /README.txt ADD jdk-8u202-linux-x64.tar.gz /usr/local ADD apache-tomcat-10.0.20.tar.gz /usr/local RUN yum -y install vim ENV MYPATH /usr/local WORKDIR $MYPATH ENV JAVA_HOME /usr/local /jdk1.8.0_202 ENV CLASSPATH $JAVA_HOME /lib/dt.jar:$JAVA_HOME /lib/tools.jar ENV CATALINA_HOME /usr/local /apache-tomcat-10.0.20 ENV CATALINA_BASH /usr/local /apache-tomcat-10.0.20 ENV PATH $PATH :$JAVA_HOME /bin:$CATALINA_HOME /lib:$CATALINA_HOME /bin EXPOSE 8080 CMD /usr/local /apache-tomcat-10.0.20/bin/startup.sh && tail -F /usr/local /apache-tomcat-10.0.20/bin/logs/catalina.out
构建镜像
启动镜像
1 2 $ docker run -d -p 9090:8080 --name lxztomcat -v /home/lxz/build/tomcat/test :/usr/local /apache-tomcat-10.0.20/webapps/test -v /home/lxz/build/tomcat/tomcatlogs/:/usr/local /apache-tomcat-10.0.20/logs diytomcat f8fcb1d5565c7a00a46ff95ee7effb3b19a58931bb47b385cf6b11fe06cc4676
访问测试
访问网站
发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了)
在tomcat的test文件夹下建立文件夹WEB-INF ,在其中创建web.xml
1 2 3 4 5 6 7 8 9 10 11 12 <?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_3_0.xsd" id="WebApp_ID" version="3.0" > <display-name>FirstWebFontEnd</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
在test文件夹下创建index.jsp文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > <title>首页</title> <style> *{ padding:0; margin:0; font-family:"微软雅黑" ; } .header{ height:72px; background: } .header .logo{ color: line-height:70px; font-size:30px; margin-left:20px; display:inline-block; text-align:center; } a { color: text-decoration: none ; } .header .login{ float :right; color: line-height:72px; margin-right:2px; display:inline-block; } .banner{ height:380px; overflow:hidden; background: } </style> </head> <body> <div class="header" > <div class="logo" >web实践</div> <div class ="login" > <a href ="javascript:void(0)" >登录</a> <span>|</span> <a href ="javascript:void(0)" >故事</a> </div> </div> Hello World!<br/> My blog is lxz9.com!<br/> </body> </html>
发现:项目部署成功,可以正常访问页面http://IP地址:9090/test/
以后开发的步骤:需要掌握Dockerfile的编写,我们之后的一切都是使用docker镜像来进行
发布自己的镜像
DockerHub
地址https://hub.docker.com/ 注册自己的账号
确定这个账号可以登录
在我们的服务器上提交自己的镜像
1 2 3 4 5 6 7 8 9 10 11 $ docker login --help Usage: docker login [OPTIONS] [SERVER] Log in to a Docker registry. If no server is specified, the default is defined by the daemon. Options: -p, --password string Password --password-stdin Take the password from stdin -u, --username string Username
登录完毕后就可以提交镜像了 docker push
1 2 3 4 5 6 7 $ docker login -u xingzeli Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/ Login Succeeded
5.提交镜像
1 2 3 4 5 6 7 8 9 10 11 $ docker tag diytomcat xingzeli/tomcat:1.0 $ docker push xingzeli/tomcat:1.0 The push refers to repository [docker.io/xingzeli/tomcat] 85d10e85e1ff: Pushed d3da19ae481a: Pushed 533ab4087ebb: Pushed e7d29c8e7b5a: Pushed 174f56854903: Mounted from library/centos 1.0: digest: sha256:dff3c979a270967f2bc1b1e18170bb515c0e67938ee38f0971c1ecc7d58f06ac size: 1373
提交的时候也是按照镜像层级来进行提交的
阿里云镜像服务
登录阿里云
找到容器镜像服务
创建命名空间
创建容器镜像
浏览阿里云
小结:
Docker 网络
理解Docker0
清空所有环境
1 2 3 4 5 6 7 8 9 10 11 docker ps -aq docker stop $(docker ps -aq) docker rm $(docker ps -aq) docker rmi $(docker images -q)
三个网络
Docker在官网下载Tomcat镜像里面没有ip addr等命令解决
1 2 3 $ docker exec -it tomcat01 ip addr OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec : "ip" : executable file not found in $PATH : unknown
解决方案:
进入上面你利用Tomcat镜像运行的容器。
1 2 root 16:50:34 ~ $ docker exec -it tomcat01 /bin/bash
执行下面两个命令
1 2 3 4 5 6 root@7cc13e6f0963:/usr/local /tomca$ apt update root@7cc13e6f0963:/usr/local /tomca$ apt install -y iproute2
成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $ docker run -d -P --name tomcat01 tomcat $ docker exec -it tomcat01 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 70: eth0@if71: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever $ ping 172.17.0.2 PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.028 ms 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.013 ms 64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.014 ms
原理:
我们每启动一个docker容器,docker就会给docker容器分配一个id,我们只要安装了docker,就会有一个网卡docker0
桥接模式,使用的技术是veth-pair技术
再次测试 ip addr
再启动一个容器测试,发现又多了一对网卡
我们来测试下tomcat01和tomcat02是否可以ping通
1 2 3 4 5 6 7 $ docker exec -it tomcat02 ping 172.17.0.2 PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.078 ms 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.032 ms 64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.021 ms
绘制一个网络模型图
结论:tomcat01 和tomcat02 是共用的一个路由器,docker0。
所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用IP
小结
docker使用的是Linux的桥接,宿主机中是一个docker容器的网桥 docker0
docker中所有网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件)
只要容器删除,对应网桥一对就没了!
–link
思考一个场景,我们编写一个微服务,database url=ip: ,项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以用名字来访问容器?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 $ docker exec -it tomcat02 ping tomcat01 ping: tomcat01: Name or service not known $ docker run -d -P --name tomcat03 --link tomcat02 tomcat 24fe246a54f3e169b18db73e71f17694ffbe95900b885a4653a232023ad0635b $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 24fe246a54f3 tomcat "catalina.sh run" About a minute ago Up About a minute 0.0.0.0:49160->8080/tcp, :::49160->8080/tcp tomcat03 56140c84bab0 tomcat "catalina.sh run" 33 minutes ago Up 33 minutes 0.0.0.0:49159->8080/tcp, :::49159->8080/tcp tomcat02 7cc13e6f0963 tomcat "catalina.sh run" 3 hours ago Up 3 hours 0.0.0.0:49158->8080/tcp, :::49158->8080/tcp tomcat01 $ docker exec -it tomcat03 ping tomcat02 PING tomcat02 (172.17.0.3) 56(84) bytes of data. 64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.129 ms 64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.100 ms 64 bytes from tomcat02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.110 ms 64 bytes from tomcat02 (172.17.0.3): icmp_seq=4 ttl=64 time=0.107 ms $ docker exec -it tomcat02 ping tomcat03 ping: tomcat03: Name or service not known
1 2 3 4 5 6 7 8 $ docker network ls NETWORK ID NAME DRIVER SCOPE be0679d23977 bridge bridge local d334b9fee722 host host local e56141257cf7 none null local $ docker network inspect be0679d23977
其实这个tomcat03就是在本地配置了tomcat02的配置
1 2 3 4 5 6 7 8 9 $ docker exec -it tomcat03 cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.3 tomcat02 56140c84bab0 172.17.0.4 24fe246a54f3
本质探究 :–link 就是我们在hosts配置中增加了一个172.17.0.3 tomcat02 56140c84bab0
我们现在玩Docker已经不建议使用–link了!
自定义网络!不使用Docker0!
Docker0的问题:它不支持容器名链接访问!
自定义网络
查看所有的docker网络
1 2 3 4 5 $ docker network ls NETWORK ID NAME DRIVER SCOPE be0679d23977 bridge bridge local d334b9fee722 host host local e56141257cf7 none null local
网络模式
bridge: 桥接模式,桥接 docker 默认,自己创建的也是用brdge模式
none: 不配置网络
host: 和宿主机共享网络
container:容器网络连通!(用的少, 局限很大)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 docker run -d -P --name tomcat01 tomcat docker run -d -P --name tomcat01 --net bridge tomcat $ docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet 340bd988f751b88da07e0213fc3941068eb5eb19dcd563b9fc9f4b952b1bc8db $ docker network ls NETWORK ID NAME DRIVER SCOPE be0679d23977 bridge bridge local d334b9fee722 host host local 340bd988f751 mynet bridge local e56141257cf7 none null local
我们自己的网络就创建好了
在自己创建的网络里面启动两个容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 $ docker run -d -P --name tomcat-net-01 --net mynet tomcat a3aa944c9cba8b72a540fa58d914f098282be89dd81e7f29f9cef91249898749 $ docker run -d -P --name tomcat-net-02 --net mynet tomcat 93210c62a4f513800aa3cf00310eab710427f2f5d0e95a9e177a5e13fe06fe7a $ docker network inspect mynet [ { "Name" : "mynet" , "Id" : "340bd988f751b88da07e0213fc3941068eb5eb19dcd563b9fc9f4b952b1bc8db" , "Created" : "2022-04-20T20:37:39.632971496+08:00" , "Scope" : "local" , "Driver" : "bridge" , "EnableIPv6" : false , "IPAM" : { "Driver" : "default" , "Options" : {}, "Config" : [ { "Subnet" : "192.168.0.0/16" , "Gateway" : "192.168.0.1" } ] }, "Internal" : false , "Attachable" : false , "Ingress" : false , "ConfigFrom" : { "Network" : "" }, "ConfigOnly" : false , "Containers" : { "93210c62a4f513800aa3cf00310eab710427f2f5d0e95a9e177a5e13fe06fe7a" : { "Name" : "tomcat-net-02" , "EndpointID" : "b1aaabbb9c8c27797a3577fdb1563ac6a0a9a3c57c9533fdd49965a7a75315a9" , "MacAddress" : "02:42:c0:a8:00:03" , "IPv4Address" : "192.168.0.3/16" , "IPv6Address" : "" }, "a3aa944c9cba8b72a540fa58d914f098282be89dd81e7f29f9cef91249898749" : { "Name" : "tomcat-net-01" , "EndpointID" : "52395f6049507e7128621beffe1a2b96a7de9086af5540868fff62cfaa73e266" , "MacAddress" : "02:42:c0:a8:00:02" , "IPv4Address" : "192.168.0.2/16" , "IPv6Address" : "" } }, "Options" : {}, "Labels" : {} } ] $ docker exec -it tomcat-net-01 ping 192.168.0.3 PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data. 64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.113 ms 64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.093 ms $ docker exec -it tomcat-net-01 ping tomcat-net-02 PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data. 64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.068 ms 64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.096 ms 64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.094 ms
我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络
好处:
redis - 不同的集群使用不同的网络,保证集群时安全和健康的
mysql - 不同的集群使用不同的网络,保证集群时安全和健康的
网络连通
测试打通tomcat01 和 mynet
1 2 3 4 5 $ docker network connect mynet tomcat01
1 2 3 4 5 6 7 8 9 10 $ docker exec -it tomcat01 ping tomcat-net-01 PING tomcat-net-01 (192.168.0.2) 56(84) bytes of data. 64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.100 ms 64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.085 ms $ docker exec -it tomcat02 ping tomcat-net-01 ping: tomcat-net-01: Name or service not known
实战 部署Redis集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 docker network create redis --subnet 172.38.0.0/16 for port in $(seq 1 6); \do \mkdir -p /mydata/redis/node-${port} /conf touch /mydata/redis/node-${port} /conf/redis.conf cat << EOF >/mydata/redis/node-${port}/conf/redis.conf port 6379 bind 0.0.0.0 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 cluster-announce-ip 172.38.0.1${port} cluster-announce-port 6379 cluster-announce-bus-port 16379 appendonly yes EOF done docker run -p 637${port} :6379 -p 1637${port} :16379 --name redis-${port} \ -v /mydata/redis/node-${port} /data:/data \ -v /mydata/redis/node-${port} /conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf docker run -p 6371:6379 -p 16371:16379 --name redis-1 \ -v /mydata/redis/node-1/data:/data \ -v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf docker run -p 6372:6379 -p 16372:16379 --name redis-2 \ -v /mydata/redis/node-2/data:/data \ -v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf docker run -p 6373:6379 -p 16373:16379 --name redis-3 \ -v /mydata/redis/node-3/data:/data \ -v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf docker run -p 6374:6379 -p 16374:16379 --name redis-4 \ -v /mydata/redis/node-4/data:/data \ -v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf docker run -p 6375:6379 -p 16375:16379 --name redis-5 \ -v /mydata/redis/node-5/data:/data \ -v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf docker run -p 6376:6379 -p 16376:16379 --name redis-6 \ -v /mydata/redis/node-6/data:/data \ -v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf $ docker exec -it redis-1 /bin/sh /data appendonly.aof nodes.conf /data >>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 172.38.0.15:6379 to 172.38.0.11:6379 Adding replica 172.38.0.16:6379 to 172.38.0.12:6379 Adding replica 172.38.0.14:6379 to 172.38.0.13:6379 M: 541b7d237b641ac2ffc94d17c6ab96b18b26a638 172.38.0.11:6379 slots:[0-5460] (5461 slots) master M: a89c1f1245b264e4a402a3cf99766bcb6138dbca 172.38.0.12:6379 slots:[5461-10922] (5462 slots) master M: 259e804d6df74e67a72e4206d7db691a300c775e 172.38.0.13:6379 slots:[10923-16383] (5461 slots) master S: 9b19170eea3ea1b92c58ad18c0b5522633a9e271 172.38.0.14:6379 replicates 259e804d6df74e67a72e4206d7db691a300c775e S: 061a9d38f22910aaf0ba1dbd21bf1d8f57bcb7d5 172.38.0.15:6379 replicates 541b7d237b641ac2ffc94d17c6ab96b18b26a638 S: 7a16b9bbb0615ec95fc978fa62fc054df60536f0 172.38.0.16:6379 replicates a89c1f1245b264e4a402a3cf99766bcb6138dbca Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join ... >>> Performing Cluster Check (using node 172.38.0.11:6379) M: 541b7d237b641ac2ffc94d17c6ab96b18b26a638 172.38.0.11:6379 slots:[0-5460] (5461 slots) master 1 additional replica(s) M: a89c1f1245b264e4a402a3cf99766bcb6138dbca 172.38.0.12:6379 slots:[5461-10922] (5462 slots) master 1 additional replica(s) S: 7a16b9bbb0615ec95fc978fa62fc054df60536f0 172.38.0.16:6379 slots: (0 slots) slave replicates a89c1f1245b264e4a402a3cf99766bcb6138dbca S: 061a9d38f22910aaf0ba1dbd21bf1d8f57bcb7d5 172.38.0.15:6379 slots: (0 slots) slave replicates 541b7d237b641ac2ffc94d17c6ab96b18b26a638 M: 259e804d6df74e67a72e4206d7db691a300c775e 172.38.0.13:6379 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 9b19170eea3ea1b92c58ad18c0b5522633a9e271 172.38.0.14:6379 slots: (0 slots) slave replicates 259e804d6df74e67a72e4206d7db691a300c775e [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
docker搭建redis集群完成!
SpringBoot微服务打包Docker镜像
构建springboot项目
打包应用
1 2 3 4 5 6 7 8 9 FROM java:8 COPY *.jar /app.jar CMD ["--server.port=8080" ] EXPOSE 8080 ENTRYPOINT ["java" , "-jar" , "/app.jar" ]
编写Dockerfile
构建镜像
发布运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 $ ll total 16140 -rw-r--r-- 1 root root 16519871 Aug 14 17:38 demo-0.0.1-SNAPSHOT.jar -rw-r--r-- 1 root root 122 Aug 14 17:38 Dockerfile $ docker build -t lixingze . Sending build context to Docker daemon 16.52MB Step 1/5 : FROM java:8 8: Pulling from library/java 5040bd298390: Pull complete fce5728aad85: Pull complete 76610ec20bf5: Pull complete 60170fec2151: Pull complete e98f73de8f0d: Pull complete 11f7af24ed9c: Pull complete 49e2d6393f32: Pull complete bb9cdec9c7f3: Pull complete Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d Status: Downloaded newer image for java:8 ---> d23bdf5b1b1b Step 2/5 : COPY *.jar /app.jar ---> d4de8837ebf9 Step 3/5 : CMD ["--server.port=8080" ] ---> Running in e3abc66303f0 Removing intermediate container e3abc66303f0 ---> 131bb3917fea Step 4/5 : EXPOSE 8080 ---> Running in fa2f25977db7 Removing intermediate container fa2f25977db7 ---> d98147377951 Step 5/5 : ENTRYPOINT ["java" , "-jar" , "/app.jar" ] ---> Running in e1885e23773b Removing intermediate container e1885e23773b ---> afb6b5f28a32 Successfully built afb6b5f28a32 Successfully tagged lixingze:latest $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE lixingze latest afb6b5f28a32 14 seconds ago 660MB tomcat latest 2ae23eb477aa 8 days ago 647MB redis 5.0.9-alpine3.11 3661c84ee9d0 3 months ago 29.8MB java 8 d23bdf5b1b1b 3 years ago 643MB $ docker run -d -P --name xiaofan-springboot-web lixingze fd9a353a80bfd61f6930c16cd92204532bfd734e003f3f9983b5128a27b0375e $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fd9a353a80bf lixingze "java -jar /app.jar …" 9 seconds ago Up 8 seconds 0.0.0.0:32779->8080/tcp xiaofan-springboot-web $ curl localhost:32779 {"timestamp" :"2020-08-14T09:42:57.371+00:00" ,"status" :404,"error" :"Not Found" ,"message" :"" ,"path" :"/" } $ $ curl localhost:32779/hello hello, xiaofan