你们好,我是杰哥

在Docker篇(四):怎样将程序与运行环境打包成一个镜像中,我们通过将集成mariadb的SpringBoot项目打包为一个镜像,并成功运行的事例,率领你们了解了怎样一次性将程序以及程序依赖的组件打包到一个镜像中进行交付

然而在实际生产中,大部份情况下,我们会把程序和组件分开来布署,也就是说将程序和各个组件分别置于不同镜像中。启动各个镜像,都会对应多个容器

相当于把各个运行的进程隔离开了,这些独立运行的思想类似于微服务的思想,常常比较好维护,但是某个组件挂了,也不会影响整个应用服务

既然各个组件须要运行在不同容器,我们就须要晓得各个容器间是怎样通信的了,例如说,容器1中的应用程序怎样联接到容器2中的数据库呢?

1Docker的网路模式

Docker支持4种网路模式

刚开始,Docker默认只有host、none以及bridge这三种模式

其中,bridge模式是Docker默认的网路设置,此模式会为每一个容器分配NetworkNamespace、设置IP等,并将一个主机上的Docker容器联接到一个虚拟集线器上

具体来说,当第一个Docker容器启动时,都会在主机上创建一个名为docker0的虚拟集线器,随后,主机上启动的所有Docker容器docker 宿主机ip,还会手动联接到这个虚拟集线器上

这么,主机上的所有容器就通过交换机连在了同一个网路中

之后,Docker会从RFC1918所定义的私有IP网关中,选择一个和宿主机不同的IP地址和子网分配给docker0。接出来每启动一个容器,Docker都会从这个子网中选择一个未占用的IP进行分配

2用事例说明问题

接出来,我们通过一个反例来说明这个理论

1)启动本地的mysql镜像

执行命令

sudo docker run -d -p 3306:3306 --name mysql -v /Users/wangjie/mysqldata:/var/lib/mysql  -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 --lower_case_table_names=1

执行命令dockerps

即,说明mysql的确已成功启动

2)查看bridge网路信息

 "Containers": {
            "a9e99e17a19a271ec78c0c26ce9aa537d15b391509f27a912b16008e1696e416": {
                "Name": "mysql",
                "EndpointID": "a330a38e765c4d8b2345284b054f9e3410efc6788e96707d5bba5a74a9a88073",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }

发觉容器mysql已成功添加至bridge网路中,ip地址分配为172.17.0.2/16,但是已开启一个名称为docker0的桥接模式虚拟网路

3)启动另一个镜像,并查看bridge网路信息

启动另一个镜像,之后执行命令dockernetworkinspectbridge

"Containers": {
            "0fffe6f12edb47ef9e0d5c4e8ad7771ecbab296530a30ba04ced5256dca02d9b": {
                "Name": "fervent_dewdney",
                "EndpointID": "3e29640510dc255394ba671eef0a5ca9c61b84d8155314de3db21404d1ca2b13",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            },
            "a172ffdd67308cf9c4a6a2ae6c863a7f13046880284c5e1e994696d1b7e2f429": {
                "Name": "mysql",
                "EndpointID": "1c669f10cab71b309d58a17724f17bd50ceb538048a874253a88b600745d5145",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },

此时我们发觉,这个新启动的容器,也加入到docker0集线器中,并被分配的ip地址为172.17.0.3/16

这么,你就肯定很快理解了docker中的容器是如何通信的了吧。对啊,主要是保证了所有的容器都在同一个网关中,其实可以正常通讯啦

4DockerNetworking

从Docker1.9以后,Docker容许用户创建自己的网路,容器被分配到指定的同一网路内。解决了现有的docker0不支持跨越不同宿主机来通讯的问题,但是网路配置可以更灵活地定做

据悉,DockerNetworking也和DockerCompose以及Swarm进行了集成,下一篇推送,我们将专门介绍这种。目前,你只须要晓得,DockerNetworking比上述Docker本身支持的网路联接的区别在于,容许用户创建自己的网路即可

接出来,我们就来一起探究一下使用DockerNetworking进行容器间通信的方法实现~

为了实现容器间的互联,我们须要分别执行以下四个步骤

1创建network网路app

2创建mariadb镜像,指定network=app

宿主机ip怎么看_宿主机是什么_docker 宿主机ip

3配置程序的创建druid_demo2镜像,指定network=app

4分别启动两个镜像,测试服务的运行

一网路创建

创建网路app

在建立两个镜像之前,我们须要创建network,我们起名为app

1创建network网路app

执行dockernetworkcreateapp,创建名称为app的网路

2查看当前所有的network网路列表

执行dockernetworkls

宿主机ip怎么看_docker 宿主机ip_宿主机是什么

发觉,此时不仅原先的bridge、host和none三个网路以外,还新增了名称为app的网路,但是直接新增的这个app网路,也为bridge-桥接模式

3查看app网路的信息

执行dockernetworkinspectapp

宿主机是什么_宿主机ip怎么看_docker 宿主机ip

发觉其分配的子网地址为172.18.0.0/16,网段地址为172.18.0.1,模式为桥接模式。并且范围为local(本地),不支持IPv6等,(先不管这种,这种信息完全可以支持我们实现明天的目的-实现容器间通讯)

好了,网路创建好了,我们就开始进行mariadb和SpringBoot两个镜像的分别建立

说明

虽然这两个镜像与Docker篇(四):怎么将程序与运行环境打包成一个镜像中的镜像,一模一样,为了实战的完整性,我还是再列一遍

二MariaDB

MariaDB镜像建立

接出来,我们即将开始MariaDB环境的建立

1编撰Dockerfile文件

FROM centos
VOLUME ["test1", "test2", "/mylog/test3"]
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum install -y mariadb-server
EXPOSE 3306
CMD /bin/bash

说明

1)基于centos镜像

2)创建test1、test2以及/mylog/test3,这三个挂载目录(在这儿无实际意义,只是为了演示VOLUMN关键字的作用)

3)申明环境变量MYPATH,值为/usr/local

4)步入到/usr/local目录下

5)使用yum安装mariadb-server

6)曝露端口3306

7)步入容器目录/bin/bash

2建立镜像

执行命令

宿主机是什么_docker 宿主机ip_宿主机ip怎么看

docker build -f Dockerfile -t mycentos:1.2 . 

进行镜像的建立

其中dockerbuild为建立镜像的Docker指令

-fDockerfile,表示指定当前目录下的Dockerfile文件

-tmycentos:1.2,表示建立的镜像名称和版本分别为mycentos和1.2

最后的一点,表示在当前目录进行重构

下边为具体的建立步骤

1)前五步

宿主机ip怎么看_docker 宿主机ip_宿主机是什么

2)前面两个步骤

docker 宿主机ip_宿主机ip怎么看_宿主机是什么

其实,镜像已成功打造

3查看镜像

执行dockerimages查看本地镜像linux系统教程,如下所示

docker 宿主机ip_宿主机是什么_宿主机ip怎么看

mycentos:1.2这个镜像已成功创建

说明:为了名子具有可识别性,后来,我将其更名为mymariadb重新建立了一次,建立步骤完全一样

因而,如右图所示,最终的镜像为mymariadb:1.2

宿主机是什么_宿主机ip怎么看_docker 宿主机ip

4运行镜像

执行命令

ocker run -it  --net=app --privileged=true -h mariadb --name mariadb  mymariadb:1.2 /sbin/init

启动镜像

其中--net=app,表示将其运行在app网路

--privileged=true,表示以最大权限启动该镜像

-hmariadb表示设置该容器的主机名为mariadb

宿主机ip怎么看_宿主机是什么_docker 宿主机ip

5验证

执行dockerps,发觉该镜像已启动,并存在其对应的容器号,名称为mariadb,映射的tcp端口为3306

6启动mariadb环镜

步入这个容器,执行systemctlstartmariadb,启动mariadb环境

前面就可以进行设置密码,初始化数据库的操作步骤

7验证容器的主机名

docker 宿主机ip_宿主机是什么_宿主机ip怎么看

查看hosts文件,执行cat/etc/hosts,查看其ip地址对应的主机名

docker 宿主机ip_宿主机是什么_宿主机ip怎么看

我们发觉linux培训机构,docker为mariadb容器分配了一个ip地址:172.18.0.2,其对应的主机名为mariadb

接出来,我们步入SpringBoot项目打包为镜像的步骤

三应用

SpringBoot项目建立

1更改其配置文件(联接数据库部份)

更改前:

spring.datasource.url=jdbc:mariadb://127.0.0.1:3306/adp_test?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456

更改后:

spring.datasource.url=jdbc:mariadb://mariadb:3306/adp_test?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456

即,将其数据库联接的本地ip地址,更改为主机名(mariadb)

说明

之所以采用mariadb的主机名,而不是其ip地址,主要是为了防止mariadb容器重新启动,造成ip发生变化的情况,其主机名总是不变的

是不是让你想到了之前的注册中心的作用,对,就是一种DSL合同

接出来,进行镜像的建立

2编撰Dockerfile文件

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=druid_demo-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} druid_demo-0.0.1-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /druid_demo-0.0.1-SNAPSHOT.jar ${0} ${@}"]

说明

1)基于jdk8镜像建立

2)创建/tmp挂载目录

3)创建参数JAR_FILE,默认值为druid_demo-0.0.1-SNAPSHOT.jar

4)将druid_demo-0.0.1-SNAPSHOT.jar包拷贝到镜像根目录

5)曝露8080端口

6)执行java-jardruid_demo-0.0.1-SNAPSHOT.jar,完成项目的启动

3建立镜像

步入项目的target目录(jar包存在的目录),执行命令

docker build  -f ../Dockerfile -t druid_demo2:0.0.1-SNAPSHOT .

运行如下:

docker 宿主机ip_宿主机ip怎么看_宿主机是什么

4启动镜像

执行命令

docker run -it  -p 8080:8080 
--net=app --privileged=true 
druid_demo2:0.0.1-SNAPSHOT /sbin/init

docker 宿主机ip_宿主机ip怎么看_宿主机是什么

4.1解决问题

小插曲

原本,直接启动是报错的。。。。。。

对,执行了上述启动命令以后,就报下边的错:拒绝联接

宿主机是什么_docker 宿主机ip_宿主机ip怎么看

本人检测了配置的地址、主机名等等都没有问题。而且通过以下步骤,验证三者都是在同一个网路下的:

执行命令dockernetworkinspectapp查看app网路中的容器列表

"Containers": {
"55cd102344045110d9b0c210ae81264788f97b5bbc7a6b3d634edc314e0b168e": {
"Name": "mariadb",
"EndpointID": "3cfcb879b607e480ecda01c3b2c490844063726aae108372b141e60b49891339",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
            },
"d883f6032d9e6b03c6639069a8e78331d324021aa6ad95be255e677868f45e89": {
"Name": "druid_demo2",
"EndpointID": "5dd52ff987e9b6b7fadf47a17e531da0645bec65da8e1a7bd274f673fd9337d1",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
            }
        },
"Options": {},

我们发觉,druid_demo2也已被成功添加到网路中,即druid_demo2和mariadb是在同一网路中的,但是ip地址分别为172.18.0.2和172.18.0.3

于是就想到了数据库的字符集(由于url中带有字符集的指定?characterEncoding=utf8)

于是,确定

问题缘由:数据库mariadb的字符集与地址手指定的字符集不一致

解决方法:解决方法就很简单,设置字符集为utf-8咯~具体操作如下所示:

步入mariadb容器,

1)更改/etc/f.d/f文件

在内容[client]下加一行配置default-character-set=utf8

最终内容

2)更改

/etc/f.d/f文件

在[mysql]下加一行配置default-character-set=utf8

最终内容如下

宿主机ip怎么看_docker 宿主机ip_宿主机是什么

3)更改

/etc/f.d/f文件

在[mysqld]下添加配置

collation-server = utf8_general_ci
init-connect='SET NAMES utf8'
character-set-server = utf8
sql-mode = TRADITIONAL

最终内容如下

4)重启mariadb

执行systemctlstartmariadb,重新启动mariadb

4.2重新启动应用

执行命令

docker run -it  -p 8080:8080 
-h druid_demo2 
--name druid_demo2 
--net=app 
--privileged=true druid_demo2:0.0.1-SNAPSHOT /sbin/init

宿主机ip怎么看_宿主机是什么_docker 宿主机ip

即,指定其网路为app,并分别指定主机名和容器名为druid_demo2,以最大权限启动

宿主机是什么_宿主机ip怎么看_docker 宿主机ip

其实,启动成功

4.3访问插口

插口正常返回,说明我们早已实现了druid_demo应用程序容器与mariadb容器之间的通信

接出来,我们再来验证一下,重启mariadb以后,服务是否可正常恢复

四测试

mariadb重启对服务的影响

我们来瞧瞧,mariadb重启对服务的影响

1停止mariadb镜像

执行dockerstop1010aa170170170aa22eeee49,停止mariadb容器

2程序运行如下

docker 宿主机ip_宿主机ip怎么看_宿主机是什么

我们发觉,此时,应用程序联接数据库失败,并不断重连数据库

3重新启动mariadb容器

宿主机ip怎么看_docker 宿主机ip_宿主机是什么

4查看程序运行日志

也就是说,当mariadb挂了,在一段时间之内,容许运维人员解决问题,可以在一定程度上保证服务的可用性

四总结

总而言之

好了,Dockerfile实战-容器间通信篇,完满结束~

本篇内容,我们为你们介绍了Docker的四种网路模式,以及默认网路模式-bridg模式的网路通信原理,并分别通过以下4个步骤:

1创建network网路app

2创建mariadb镜像docker 宿主机ip,指定network=app

3配置程序的创建druid_demo2镜像,指定network=app

4分别启动两个镜像,测试服务的运行

即采用DockerNetworking的形式(支持用户创建自己的网路),为你们演示了Docker将SpringBoot项目和mariadb分为两个容器启动的实现步骤。

如果大家的项目中还使用到了redis、注册中心、消息中心等组件,你还可以通通将这种组件分别作为一个容器来运行,有问题或则经验分享,也欢迎随时在线讨论或则步入群聊讨论哦~

这么,我们就面临管理多个容器的情形,该怎么管理呢?下一节,我们再一上去瞧瞧吧~

嗯,就这样。每晚学习一点,时间接见证你的强悍~

上期预告:

Docker篇(六):多个容器该怎么管理?

欢迎你们关注们的公众号【青梅主码】,一起持续性学习吧~

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复