搭建步骤:
docker run -p 3307:3306 --name mysql-master -v /mydata/mysql-master/log:/var/log/mysql -v /mydata/mysql-master/data:/var/lib/mysql -v /mydata/mysql-master/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
进入宿主机/mydata/mysql-master/conf目录下新建my.cnfvim my.cnf
,内容:
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=101
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
修改完配置后重启master实例,docker restart mysql-master
进入mysql-master容器,docker exec -it mysql-master /bin/bash
登录,mysql -uroot -proot
master容器实例内创建数据同步用户并授权,CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
,GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
docker run -p 3308:3306 --name mysql-slave -v /mydata/mysql-slave/log:/var/log/mysql -v /mydata/mysql-slave/data:/var/lib/mysql -v /mydata/mysql-slave/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
进入宿主机/mydata/mysql-slave/conf目录下新建my.cnf
vim my.cnf,内容:
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=102
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-slave1-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## slave设置为只读(具有super权限的用户除外)
read_only=1
修改完配置后重启slave实例,docker restart mysql-slave
在主数据库容器内 查看主从同步状态,show master status;
进入mysql-slave容器,docker exec -it mysql-slave /bin/bash
登录,mysql -uroot -proot
在从数据库中配置主从复制,命令:
change master to master_host='宿主机ip', master_user='主数据库授权用户名', master_password='主数据库授权用户密码', master_port=主数据库在宿主机对应端口, master_log_file='mall-mysql-bin.000001', master_log_pos=617, master_connect_retry=30;
主从复制配置命令参数说明:
master_host:主数据库的IP地址;
master_port:主数据库的运行端口;
master_user:在主数据库创建的用于同步数据的用户账号;
master_password:在主数据库创建的用于同步数据的用户密码;
master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
master_connect_retry:连接失败重试的时间间隔,单位为秒。
从数据库查看主从同步状态,show slave statues \G
(Slave_IO_Running:No Slave_SQL_Running:No说明没开始)
在从数据库中开启主从同步,start slave;
查看从数据库状态发现已经同步,show slave statues \G
(Slave_IO_Running:Yes Slave_SQL_Running:Yes 说明已经开始)
主从复制测试,主机新建库-使用库-新建表-插入数据,从机使用库-查看记录
使用场景:亿数量级的数据需要缓存,请问如何设计这个存储案例。应该使用分布式,redis怎么处理数据落地?
一般有三个解决方案:哈希取余分区、一致性哈希算法分区、哈希槽分区
概念:
2亿条记录就是2亿个k,v,我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:
hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
优点:
简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点:
1、原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key) /?。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。
2、某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。
总结:
小厂用,大中厂经常扩缩容,用不了。
服务器个数发生变动时, 尽量减少影响客户端到服务器的映射关系
1、算法构建一致性哈希环:
一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,232-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0 = 232),这样让它逻辑上形成了一个环形空间。
它也是按照使用取模的方法,前面笔记介绍的节点取模法是对节点(服务器)的数量进行取模。而一致性Hash算法是对232取模,简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-232-1(即哈希值是一个32位无符号整形),整个哈希环如下图:整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、……直到232-1,也就是说0点左侧的第一个点代表232-1, 0和232-1在零点中方向重合,我们把这个由232个点组成的圆环称为Hash环。
2、服务器IP节点映射:
将集群中各个IP节点映射到环上的某一个位置。将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:
3、key落到服务器的落键规则
当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),将这个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性Hash算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
容错性
假设Node C宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。简单说,就是C挂了,受到影响的只是B、C之间的数据,并且这些数据会转移到D进行存储。
扩展性
数据量增加了,需要增加一台节点NodeX,X的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,不会导致hash取余全部数据重新洗牌。
Hash环的数据倾斜问题
一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中只有两台服务器:
为了在节点数目发生改变时尽可能少的迁移数据,将所有的存储节点排列在收尾相接的Hash环上,每个key在计算Hash后会顺时针找到临近的存储节点存放。而当有节点加入或退出时仅影响该节点在Hash环上顺时针相邻的后续节点。
优点
加入和删除节点只影响哈希环中顺时针方向的相邻的节点,对其他节点无影响。
缺点
数据的分布和节点的位置有关,因为这些节点不是均匀的分布在哈希环上的,所以数据在进行存储时达不到均匀分布的效果。
解决一致性哈希算法的数据倾斜问题。在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
有多少hash槽?
一个集群只能有16384个槽,编号0-16383(0-214-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对应的槽里。slot = CRC16(key) % 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。如下代码,key之A 、B在Node2, key之C落在Node3上
命令:systemctl start docker
命令1:docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
命令2:docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
命令3:docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
命令4:docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
命令5:docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
命令6:docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
其中:
--cluster-enabled yes
表示开启集群
--appendonly yes
表示开启数据持久化
--net host
表示使用宿主机的IP和端口,默认
启动之后 docker ps
查看
进入1号机:docker exec -it redis-node-1 /bin/bash
构建主从关系:redis-cli --cluster create 宿主机IP:6381 宿主机IP:6382 宿主机IP:6383 宿主机IP:6384 宿主机IP:6385 宿主机IP:6386 --cluster-replicas 1
注意进入docker容器后才能执行一下命令,且注意自己的真实IP地址
其中--cluster-replicas 1
表示为每个master创建一个slave节点
执行命令后会自动分配哈希槽,没问题就会让输入yes
进入6381对应的1号机:docker exec -it redis-node-1 /bin/bash
进入redis客户端:redis-cli -p 6381
查看集群状态:cluster info
查看集群节点信息:cluster nodes
注意主机下挂哪一个是从机是随机的,可以通过本条命令一一对应
1、启动6台机构成的集群并exec进入:
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381
2、对6381新增两个key
如果执行set k1 v1
命令,可能遇到(error)MOVED 哈希值 巴拉巴拉
错误,这是因为设置了哈希槽,如果算出来的结果不落在节点对应区间则无法放入。例如端口6381的节点对应哈希槽0~5460,而算出来的哈希值不在这个区间,则放入失败。
3、防止路由失效加参数-c并新增两个key
正确连接reids:redis-cli -p 6381 -c
会自动跳转到正确的redis
4、查看集群信息
命令:redis-cli --cluster check 宿主机IP:端口
主机宕机从机上位。上述示例中,主机1宕机之后,他的从机会自动上位。
主机1恢复之后,变为从机。
需求:3主3从变为4主4从
问题:哈希槽位如何分配
命令1:docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
命令2:docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
查看是否启动成功:docker ps
命令:docker exec -it redis-node-7 /bin/bash
将新增的6387节点(空槽号)作为master节点加入原集群:redis-cli --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381
(其中:6387 就是将要作为master新增节点,6381 就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群)
检查集群情况:redis-cli --cluster check 真实ip地址:6381
,发现6387没有分配哈希槽位
重新分派槽号:redis-cli --cluster reshard IP地址:6381
,会出现问分配多少,写上平均数就行;会出现问分配给谁,写redis的节点ID;会出现问分配类型,写all
检查集群情况:redis-cli --cluster check 真实ip地址:6381
注意:为什么6387是3个新的区间,以前的还是连续?
重新分配成本太高,所以前3家各自匀出来一部分,从6381/6382/6383三个旧节点分别匀出1364个坑位给新节点6387
命令:redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID
例如:redis-cli --cluster add-node 192.168.111.147:6388 192.168.111.147:6387 --cluster-slave --cluster-master-id e4781f644d4a4e4d4b4d107157b9ba8144631451
检查集群情况:redis-cli --cluster check 真实ip地址:6381
需求:4主4从变回3主3从
问题:哈希槽位重新分配
查询各节点ID:redis-cli --cluster check 真实ip地址:6382
(这端口号使用6381、6382、6383、6384这些主机都行)
集群中将4号从节点6388删除:redis-cli --cluster del-node 真实ip:从机端口 从机6388节点ID
检查集群情况:redis-cli --cluster check 真实ip地址:6381
命令:redis-cli --cluster reshard 192.168.111.147:6381
(是以6381为落脚点操作整个集群)会出现问操作多少槽位slots,写要删除节点所占用的个数;会出现问找哪一个节点来接受,输用来接受slots的节点ID就好,例如节点1ID;会出现问谁来出这些节点,输要删除的节点ID,完了输入 done
检查集群情况:redis-cli --cluster check 真实ip地址:6381
(此时6381有8192个槽位,相当于都给了)
命令:redis-cli --cluster del-node ip:端口 6387节点ID
检查集群情况:redis-cli --cluster check 真实ip地址:6381
Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
官方文档
编写Dockerfile文件:列出所需模块
docker build命令构建镜像
docker run依镜像运行容器实例
Dockerfile内容基础知识:
1:每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2:指令按照从上到下,顺序执行
3:#表示注释
4:每条指令都会创建一个新的镜像层并对镜像进行提交
Docker执行Dockerfile的大致流程:
1、docker从基础镜像运行一个容器
2、执行一条指令并对容器作出修改
3、执行类似docker commit的操作提交一个新的镜像层
4、docker再基于刚提交的镜像运行一个新容器
5、执行Dockerfile中的下一条指令直到所有指令都执行完成
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,
基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from
镜像维护者的姓名和邮箱地址
容器构建时需要运行的命令,RUN是在 docker build时运行,有两种格式:
shell格式:
RUN <命令行命令>
# <命令行命令>等同于在终端执行的shell命令
# 例如 RUN yum -y install vim
exec格式:
RUN ["可执行文件","参数1","参数2"...]
#例如 RUN ["./test.php","dev","online"] 等价于RUN ./test.php dev online
当前容器对外暴露出的端口
指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点。就是进入容器之后的pwd
指定该镜像以什么样的用户去执行,如果都不指定,默认是root
用来在构建镜像过程中设置环境变量
# 例如
ENV MY_PATH /usr/mytest
# 这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样;
# 也可以在其它指令中直接使用这些环境变量,例如:
WORKDIR $MY_PATH
将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包。AND相当于COPY之后还能解压。
类似ADD,拷贝文件和目录到镜像中。 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置COPY ["src","dest"]
容器数据卷,用于数据保存和持久化工作。相当于 -v
指定容器启动后的要干的事情
CMD的命令参考RUN,例如tomcat的是CMD ["catalina.sh","run"]
注意:
在指定了ENTRYPOINT
指令后,CMD就成了指定具体参数的作用。
注意:
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
Tomecat的Dockerfile,官网最后一行:CMD ["catalina.sh","run"]
,我们演示自己的覆盖操作docker run -it -p 8080:8080 容器ID /bin/bash
,这会导致原本的CMD ["catalina.sh","run"]
失效,从而导致tomcat启动失败
CMD和RUN的区别:
CMD是在docker run(容器实例化)时运行;RUN 是在docker build时(构建镜像)运行
也是用来指定一个容器启动时要运行的命令,类似于 CMD 指令,但是ENTRYPOINT不会被docker run后面的命令覆盖, 而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序
命令格式:ENTRYPOINT ["可执行文件","参数1","参数2"...]
ENTRYPOINT可以和CMD一起用,一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合会变成
案例:
假设已通过 Dockerfile 构建了 nginx:test 镜像:
FROM nginx
ENTRYPOINT ["nginx","-c"] #定参
CMD ["/etc/nginx/nginx.conf"] #变参
是否传参 | 按照Dockerfile编写执行 | 传参运行 |
---|---|---|
Docker命令 | docker run nginx:test | docker run nginx:test -c /etc/nignx/new.conf |
实际指令 | nginx -c /etc/nginx/nginx.conf | nginx -c /etc/nginx/new.conf |
优点
在执行docker run的时候可以指定 ENTRYPOINT 运行所需的参数。
注意
如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
自定义镜像mycentosjava8
要求
Centos7镜像具备vim+ifconfig+jdk8
JDK的下载镜像地址:https://www.oracle.com/java/technologies/downloads/#java8
编写Dockerfile文件:
宿主机新建一个文件夹叫myfile,放入jdk
同目录下新建Dockerfile,输入以下内容:
FROM centos
MAINTAINER zzyy<[email protected]>
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新镜像名字:TAG .
注意TAG后有空格和小点
docker build -t centosjava8:1.5 .
Dockerfile制作虚悬镜像:
在编写Dockerfile时,写入如下内容:
from ubuntu
CMD echo'action is success'
再执行命令docker build
命令:docker image prune
通过Dockerfile发布微服务部署到docker容器。
1、先完成代码开发后,通过maven到package打包成jar文件。
2、将jar上传到docker 的宿主机
3、(最好同目录下)新建Dockerfile:
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER zzyy
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar
# 运行jar包
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]
#暴露6001端口作为微服务
EXPOSE 6001
4、构建新的镜像:docker build -t zzyy_docker.jar:1.6 .
注意最后的空格+点
5、运行容器:docker run -d -p 6001:6001 镜像ID
注意:
可能遇到宿主机防火墙 导致启动失败:stop firewalld
,然后重启docker:restart docker
为了容器编排
docker不启动,默认网络情况:
宿主机命令:ifconfig
ens33里面的inet 后的ip地址就是Linux宿主机的地址
lo里面 inet 后的ip地址是本地回环链路的地址
virbr0(虚拟网桥)是在CentOS7的安装过程中如果有选择相关虚拟化的的服务安装系统后,启动网卡时会发现有一个以网桥连接的私网地址的virbr0网卡(virbr0网卡:它还有一个固定的默认IP地址192.168.122.1),是做虚拟机网桥的使用的,其作用是为连接其上的虚机网卡提供 NAT访问外网的功能(虚拟机之间可以连接)。我们之前学习Linux安装,勾选安装系统的时候附带了libvirt服务才会生成的一个东西,如果不需要可以直接将libvirtd服务卸载,yum remove libvirt-libs.x86_64
虚拟机内docker启动后,网络情况:
会产生一个名为docker0的虚拟网桥,inet后的ip地址是 用来容器和宿主机,容器和容器之间通信的。
查看docker网络:docker network ls
,默认创建三个网络模式:bridge、host、none。主要是前两个
命令:docker network 后面加以下指令
connect Connect a container to a network,默认创建的是bridge模式的
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks,格式docker network inspect xxx网络名,查看完了源数据
ls List networks
prune Remove all unused networks
rm Remove one or more networks
1、容器之间的互联和通信以及端口映射(包括不同docker上的容器之间)
2、容器IP变动时候可以通过服务名直接网络通信而不受影响。
网络模式 | 介绍 |
---|---|
bridge | 为每一个容器分配、设置IP等,并将容器连接到一个docker0 虚拟网桥 |
host | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口 |
none | 容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair和网桥连接,IP等 |
container | 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP,端口范围等 |
使用命令:--network xxx
指定,不指定是docker0
1、启动两个ubuntu实例docker run -it --name u1 ubuntu bash
、docker run -it --name u2 ubuntu bash
2、docker inspect 容器ID or 容器名字。查看Networks的bridge的IPAddress
3、关闭u2实例,新建u3,查看ip变化
结论:docker容器内部的ip是有可能会发生改变的,可能原本u2的ip后来分配给u3。因此服务之间的调用 IP不能写死。
Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
查看 bridge 网络的详细信息,并通过 grep 获取名称项
docker network inspect bridge | grep name
1 、Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
2 、docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network(后面讲)eth0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址
3 、网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
3.1 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
3.2 每个容器实例内部也有一块网卡,每个接口叫eth0;
3.3 docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。
通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
代码:启动两个tomcat,
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
宿主机执行ap addr:
注意veth开头的这些,和eth0两两匹配
进入容器之后docker exec -it tomcat81 bash
,命令ip addr
查看(下面两张图)
直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
代码:
docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
会遇到警告,并且docker ps
查看时PORTS会为空
问题:
docke启动时总是遇见标题中的警告
原因:
docker启动时指定–network=host或-net=host,如果还指定了-p映射端口,那这个时候就会有此警告,
并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。
解决:
解决的办法就是使用docker的其他网络模式,例如–network=bridge,这样就可以解决问题,或者直接无视。。。。O(∩_∩)O哈哈~
正确代码:
docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8
无之前的配对显示了,看容器实例内部
查看容器内部
**注意:**没有设置-p的端口映射了,如何访问启动的tomcat83??
访问:http://宿主机IP:8080/
在CentOS里面用默认的火狐浏览器访问容器内的tomcat83看到访问成功,因为此时容器的IP借用主机的,
所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。
在none模式下,并不为Docker容器进行任何网络配置。
也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo
需要我们自己为Docker容器添加网卡、配置IP等。
禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)
代码:
docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8
新建的容器和已经存在的一个容器共享一个网络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同一个端口,导致端口冲突
案例2 使用Alpine Linux
命令:
docker run -it --name alpine1 alpine /bin/sh
docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh
结果:
假如此时关闭alpine1,再看看alpine2
15: eth0@if16: 消失了。。。。。。关闭alpine1,再看看alpine2,就只有本地回环了
使用自定义网络前:可以通过ip访问,但不可以通过服务名访问。
使用自定义网络后:可以使用服务名访问
自定义桥接网络,自定义网络默认使用的是桥接网络bridge
创建自定义网络模式docker network create zzyy_network
新建容器加入上一步新建的自定义网络:
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和域名都能通)
自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
整体说明
从其架构和运行流程来看,Docker 是一个 C/S 模式的架构,后端是一个松耦合架构,众多模块各司其职。
Docker 运行的基本流程为:
1 用户是使用 Docker Client 与 Docker Daemon 建立通信,并发送请求给后者。
2 Docker Daemon 作为 Docker 架构中的主体部分,首先提供 Docker Server 的功能使其可以接受 Docker Client 的请求。
3 Docker Engine 执行 Docker 内部的一系列工作,每一项工作都是以一个 Job 的形式的存在。
4 Job 的运行过程中,当需要容器镜像时,则从 Docker Registry 中下载镜像,并通过镜像管理驱动 Graph driver将下载镜像以Graph的形式存储。
5 当需要为 Docker 创建网络环境时,通过网络管理驱动 Network driver 创建并配置 Docker 容器网络环境。
6 当需要限制 Docker 容器运行资源或执行用户指令等操作时,则通过 Execdriver 来完成。
7 Libcontainer是一项独立的容器管理包,Network driver以及Exec driver都是通过Libcontainer来实现具体对容器进行的操作。
Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器
Docker-Compose是Docker官方的开源项目, 负责实现对Docker容器集群的快速编排。
docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?
如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具
例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。。。。。。
Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
官网
https://docs.docker.com/compose/compose-file/compose-file-v3/
官网下载
https://docs.docker.com/compose/install/
安装步骤
curl -L “https://github.com/docker/compose/releases/download/1.29.2/docker-compose- ( u n a m e − s ) − (uname -s)- (uname−s)−(uname -m)” -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
检查是否成功:docker compose version
如果是curl安装的,卸载shell命令:sudo rm /usr/local/bin/docker-compose
一个文件:docker-compose.yml
两个要素:服务+工程
服务:
一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器
工程:
由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。一个工程由多个服务组成。
先后顺序要求固定,先启动例如mysql、redis才能启动微服务。
多个run 命令
容器之间的启停、宕机,有可能导致IP地址对应的容器实例变化,映射出错。只能通过要么IP写死,要么通过服务调用解决
1、编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
2、使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务。
3、最后,执行docker-compose up命令 来启动并运行整个应用程序,完成一键部署上线。
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 # 停止服务
docker-compose.yml
version: "3"
services:
myService:
image: xxx
container_name: xxx
ports:
- "6001:6001"
volumes:
- /app/xxx/xxx
networks:
- xxx
depend_on:
- redis
- mysql
redis:
省略
mysql:
省略
networks:
xxx
注意spring的配置文件也要改写,之前的spring.redis.host是IP地址,要改成服务名,mysql的url的host部分也改成服务名。
没有指定container_name的服务会被加上前缀名
解决诉求:监控、统计
https://www.bilibili.com/video/BV1gr4y1U7CY?p=87&spm_id_from=pageDriver&vd_source=ef244fa9c2d4d08e1d5d46996300b149
https://www.aliyundrive.com/s/6iaQt9zLDVm/folder/61e7d913e922bf5b8a56420d9426a4b675c271b4