本篇主要是mysql主从,redis主从搭建,Dockerfile解析及docker-compose编排、轻量化可视化工具Portainer、重量级工具CAdvisor+InfluxDB+Granfana的使用。
docker的安装、配置、卸载,自定义镜像和搭建公有和私有镜像仓库,以及常规软件安装示例见另一篇:docker基础篇-docker的安装、配置、卸载,自定义镜像和搭建公有和私有镜像仓库
一、docker集群安装
1. mysql主从服务器搭建(一主一从)
1.1. 新建主服务器容器实例 3307
docker run -d -p 3307:3306 --privileged=true -v /home/mydata/mysql-master/log:/var/log/mysql -v /home/mydata/mysql-master/data:/var/lib/mysql -v /home/mydata/mysql-master/conf:/etc/mysql --name=mysql-master -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
1.2. 进入/home/mydata/mysql-master/conf目录下新建my.cnf
/home/mydata/mysql-master/conf
为自己配置文件映射到宿主机的相应目录下
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
1.3. 修改玩配置后重启master实例
docker restart mysql-master
1.4. 进入mysql-master容器
docker exec -it mysql-master /bin/bash
mysql -u root -p
1.5. master容器实例内创建数据同步用户
create user 'slave'@'%' identified by '123456';
grant replication slave,replication client on *.* to 'slave'@'%';
1.6. 新建从服务器容器实例3308
docker run -d -p 3308:3306 --privileged=true -v /home/mydata/mysql-slave/log:/var/log/mysql -v /home/mydata/mysql-slave/data:/var/lib/mysql -v /home/mydata/mysql-slave/conf:/etc/mysql --name=mysql-slave -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
1.7. 进入/home/mydata/mysql-slave/conf目录下新建my.cnf
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
1.8. 修改完配置后重启slave实例
docker restart mysql-slave
1.9. 在主数据库中查看主从同步状态
show master status;
1.10. 进入mysql-slave容器
docker exec -it mysql-slave /bin/bash
mysql -u root -p
1.11. 在从数据库中配置主从复制
change master to master_host='宿主机ip',master_user='slave',master_password='123456',master_port=3307,master_log_file='mall-mysql-bin.000005',master_log_pos=617,master_connect_retry=30;
参数说明:
- master_host:主数据库的ip
- master_user:在主数据库中创建的用于同步数据的用户账号
- master_password:在主数据库中创建的用于同步数据的用户密码
- master_port:主数据库的运行端口
- master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据库的状态(见上方1.9),获取File参数
- master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据库状态(见上方1.9),获取Position参数
- master_connect_retry:连接失败重试的时间间隔,单位秒
1.12. 在从数据库中查看主从复制状态
# \G可不加,不加就是默认以列表的形式展示,如(上方1.9章节)查询主数据库状态。
#加上\G表示以K-V键值对的形式展示。
show slave status \G;
1.13. 在从数据库中开启主从同步
start slave;
1.14. 查看从数据库状态发现已同步
show slave status \G;
1.15. 主从复制测试
-
主机新建库-使用库-新建表-插入数据
-
从机使用库-查看数据
2. redis集群搭建
2.1 亿级数据需要缓存?
答:肯定是需要进行分布式集群部署的。3种方案,从易到难
- 哈希取余分区
- 概述
假设有3台redis节点,在存数据时使用公式hash(key)/3,计算出余数决定放入哪一个节点,在取数据时,也是用hash(key)/3计算出余数决定到哪一个节点去取出数据。
- 优点
简单粗暴,直接有效,只需要预估好数据、规划好节点,例如3台,6台,10台等,就能保证一段时间的数据支撑,使用hash算法让固定的一部分请求落在同一个服务器上,这样每台服务器固定的处理一部分请求,起到了负载均衡+分而治之的作用。
- 缺点
原来规划好的节点,进行扩容和缩容比较麻烦,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算。在服务器个数不变的情况下没问题,如果需要弹性扩缩或故障停机的情况下,原来的取模公式就会发生变化:hash(key)/?。此时地址经过取余运算的结果将发生很大的变化,根据公式获取的服务器也会变得不可控。
- 一致性哈希算法分区
- 概述
为了解决分布式缓存数据变动和映射问题
,当服务器个数发生变化时,尽量减少影响客户端到服务器的映射关系。
- 实现方式
① 算法构建一致性哈希环:有一个hash函数算法产生一个0~232-1 的hash圆环。
② 服务器IP节点映射:将集群中的节点根据hash函数算法映射到环上的某一个位置。
③ key落到服务器的落键规则:计算出key的hash值,确定key在环上的位置,然后按顺时针的方向找到第一个节点,然后将该键值对存储在这个节点上。
- 优点
具有容错性和扩展性。容错性:当出现宕机时,只会影响出错位置到逆时针上一个节点的一段数据,将其存入顺时针的下一个节点上存储;扩展性:计算出新加节点的hash落点,影响落点到逆时针的上一个节点的数据,不会导致全部数据重新洗牌。
- 缺点
数据倾斜问题:当节点数量太少时,容易因为节点的分布不均造成数据倾斜(缓存的大部分数据集中在同一台服务器上)。
- 哈希槽分区
为了解决数据倾斜问题,hash槽实质上就是一个数组0~214-1 形成hash slot空间。解决了均匀分配的问题,就是在数据和节点之间加了一层,把这层叫做哈希槽(slot),用于管理数据和节点的关系,相当于节点上放的是槽,槽里面放的数据。
槽解决的是粒度的问题,相当于放大了粒度,便于数据移动。
哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
一个集群中只能有16384
个槽,编号0~16383(2^14^-1)
。将这些槽分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪些主节点。集群会记录节点和槽的对应关系。然后计算键值对的key落在那个槽中,slot=CRC16(key)%16384
。以槽为单位移动数据,因为槽是固定的,处理起来很容易,这样数据的移动问题就解决了。
redis集群中内置了16384个哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点
。
2.2 3主3从redis集群搭建
- 新建六个docker容器实例
docker run -d --name=redis-node-1 --net host --privileged=true -v /home/redis/data/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name=redis-node-2 --net host --privileged=true -v /home/redis/data/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name=redis-node-3 --net host --privileged=true -v /home/redis/data/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name=redis-node-4 --net host --privileged=true -v /home/redis/data/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name=redis-node-5 --net host --privileged=true -v /home/redis/data/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name=redis-node-6 --net host --privileged=true -v /home/redis/data/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
参数说明
- --net host :使用宿主机的ip和端口,默认
- --privileged=true :获取宿主机root用户权限
- --cluster-enabled yes :开启redis集群
- --appendonly yes :开启持久化
- --port 6386 :redis端口号
2. 进入redis-node-1并为6台机器构建集群关系
#可以进入任意节点,只是以redis-node-1为例
docker exec -it redis-node-1 /bin/bash
#然后执行下面的命令,注意更换ip地址
redis-cli --cluster create 192.168.64.128:6381 192.168.64.128:6382 192.168.64.128:6383 192.168.64.128:6384 192.168.64.128:6385 192.168.64.128:6386 --cluster-replicas 1
参数说明
- --cluster-replicas 1 :表示为每一个master创建一个slave节点
主从对应关系随机,由系统分配
3. 连接进入6381位切入点,查看集群状态
#进入6381端口的redis
redis-cli -p 6381
cluster info
cluster nodes
主从对应关系随机,由系统分配
2.3 主从容错切换迁移案例
- 数据的读写存储
注:防止路由失效,在进入redis指令后加参数 -c
,不加-c时,只有当新增的key满足当前节点哈希槽才能成功,否则会失败,不会自动路由切换。
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381 -c
查看集群信息:
redis-cli --cluster check 192.168.64.128:6381
- 容错切换迁移
模拟主从切换:停止6381主机,发现6386主机成为master,再次启动6381主机后,6381成为了6386的从机slave。
2.4 主从扩容案例
- 新建6387、6388两个节点,并启动两个节点
docker run -d --name=redis-node-7 --net host --privileged=true -v /home/redis/data/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name=redis-node-8 --net host --privileged=true -v /home/redis/data/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
- 进入6387容器实例,将6387节点(空槽号)作为master节点加入集群
docker exec -it redis-node-7 /bin/bash
# 6387就是要作为master的新增节点
# 6381就是原来集群节点中的领路人,相当于6387拜拜6381的码头找到组织加入集群
redis-cli --cluster add-node 192.168.64.128:6387 192.168.64.128:6381
- 检查集群情况1
redis-cli --cluster check 192.168.64.128:6381
- 重新分配槽号:
redis-cli --cluster reshard 192.168.64.128:6382
- 检查集群情况2
redis-cli --cluster check 192.168.64.128:6381
- 为主节点6387分配从节点6388
redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新master节点的ID
- 检查集群情况3
redis-cli --cluster check 192.168.64.128:6382
2.5 主从缩容案例
- 先删除从节点6388
redis-cli --cluster check 192.168.64.128:6382
redis-cli --cluster del-node ip:从节点端口 从节点ID
2. 检查集群情况:发现已删除6388节点
redis-cli --cluster check 192.168.64.128:6382
- 将6387主节点的槽号重新分配:
redis-cli --cluster reshard 192.168.64.128:6383
4. 检查集群情况:发现6387节点的槽位全部分配给了6386
redis-cli --cluster check 192.168.64.128:6382
- 删除6387主节点
redis-cli --cluster del-node ip:主节点端口 主节点ID
- 检查集群情况:恢复之前的3主3从
redis-cli --cluster check 192.168.64.128:6382
二、dockerFile解析
1. 常用关键字指令
- FROM:基础镜像。指定一个已经存在的镜像作为模板,第一条必须是FROM。
- MAINTAINER:镜像维护者的姓名和邮箱。
- RUN:容器构建时执行的命令,在
docker build
时运行。有两种格式:shell命令格式和exec格式。
- EXPOSE:当前容器对外暴露的端口。
- WORKDIR:指定在创建容器后,终端默认登录进来的工作目录,一个落脚点。
- USER:指定该镜像以什么样的用户去执行,如果不指定,默认是root。
- EVN:用于在构建镜像过程中设置环境变量。如:
ENV MY_PATH /use/local
,可以其他地方使用 $MY_PATH
。
- ADD:将宿主机目录下的文件拷贝进镜像且会自动清理URL和解压tar压缩包。
- COPY:类似于ADD,拷贝文件和目录到镜像中。
COPY src dest
。
- VOLUME:容器数据卷,用于数据保存和持久化工作。
- CMD:指定容器
启动后
要干的事情,但只有最后一个生效,CMD会被docker run
之后的参数替换。有两种格式:shell命令格式和exec格式。
- ENTRYPOINT:指定容器
启动时
要运行的命令,不会被覆盖,而是将docker run
后面的命令行作参数,当做参数送给ENTRYPOINT指令。
2. 自定义centosjdk8镜像
给centos7添加vim+ifconfig+jdk8
2-2. 编写DockerFile文件
- 新建 myfile文件夹,上传
jdk-8u371-linux-x64.tar.gz
2.新建Dockerfile文件
Dockerfile文件
FROM centos:7
MAINTAINER gongl<********@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安装vim编辑器
RUN yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装jdk8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
#ADD是相对路径jar,把jdk-8u371-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一个位置
ADD jdk-8u371-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_371
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 'sucess-------------------ok'
CMD /bin/bash
2-3. 构建
#注意TAG后面有个空格和点.
docker build -t 新镜像的名字:TAG .
2-4. 运行
docker run -it centos7jdk8:1.1 /bin/bash
3. 虚悬镜像
- 编写一个Dockerfile文件
from ubuntu
CMD echo'action is sucess'
- docker build .
- 只查看虚悬镜像指令
docker image ls -f dangling=true
4. 删除虚悬镜像
docker image prune
三、docker微服务实战
- 将java项目打成jar包上传到服务器:如 test.jar
- 编写Dockerfile文件:将Dockerfile文件和jar包放在同一目录下
#基础镜像使用java8
FROM java:8
#作者信息
MAINTAINER gongl<********@qq.com>
# VOLUME 指定临时文件目录/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
#将jar包添加到容器中并命名为 aaaaa_test.jar
ADD test.jar aaaaa_test.jar
#运行jar
ENTRYPOINT ["java","-jar","aaaaa_test.jar"]
#暴露端口9561作为微服务的端口
EXPOSE 9561
-
docker build -t aaaaa_test:1.4 .
生成镜像
- 运行容器并测试
四、docker网络
- 作用
- 容器间的互联和通信以及端口映射
- 容器IP变动时候可以通过服务名直接网络通信而不受IP地址变更影响
- docker 运行时,会产生一个名为docker0的网桥
- 常用指令 :
docker network [OPTIONS]
- connect:连接
- create:创建
- disconnect:中断
- inspect:查看
- ls:列表
- prune:删除所有无效网络
- rm:删除
4. 网络模式
- bridge:虚拟网桥,默认模式。为每个容器分配IP。
--network bridge
- host:直接使用宿主机的IP和端口,所以
-p
参数会无效 ,如上图给出的警告。--network host
- none:用自己的network namespace,但是没有任何的网络配置。
--network none
- container:新建的容器不创建自己的网卡和IP,而是和一个指定的容器共享IP、端口等,
当指定容器停止后,新建的容器也无法使用网络
。 --network container:指定的容器名
- 自定义桥接网络:使用指令
docker network create 自定义网络名
创建。将服务设置同一个自定义网络,可以通过容器的服务名
直接通信。自定义网络本身就维护好了主机名和ip的对应关系。 --network 自定义网络名
五、docker-compose容器编排
实现对docker集群的快速编排
下载地址:https://docs.docker.com/compose/install/
安装文档:https://docs.docker.com/compose/install/other/
compose版本对应文档地址:https://docs.docker.com/compose/compose-file/compose-file-v3/
#下载
curl -SL https://github.com/docker/compose/releases/download/v2.17.3/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
#添加权限
chmod +x /usr/local/bin/docker-compose
#测试安装
docker-compose --version
#卸载
rm /usr/local/lib/docker/cli-plugins/docker-compose
- 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 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 #停止服务
- 举例编排上线微服务
2.1 编写docker-compose.yml文件
version: "3" #版本
services: #所有的服务
microService: #服务名,自定义,不重复即可
image: aaaaa_test:1.4 #镜像名称:TAG
container_name: ms01 #容器名称,自定义。不加该参数默认生成带前缀和后缀的容器名
ports: #端口映射
- "9561:9561"
volumes: #数据卷
- /app/microService:/data
networks: #自定义网络,为了直接使用 服务名 进行容器间通信
- test_net
depends_on: #当前服务依赖于哪些服务
- redis1
- mysql
reids1: #服务名,自定义,不重复即可
image: redis:6.0.8 #镜像名称:TAG
ports: #端口映射
- "6379:6379"
volumes: #数据卷
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- test_net
command: redis-server /etc/redis/redis.conf
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2023'
MYSQL_USER: 'root'
MYSQL_PASSWORD: '123456'
ports:
- "3306:3306"
volumes:
- /app/mysql/db:/var/lib/mysql
- /app/mysql/conf/my.cnf:/etc/my.cnf
- /app/mysql/init:/docker-entrypoint-initdb.d
networks:
- test_net
command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
networks:
test_net:
2.2 将java项目中的ip地址修改为对应的服务名(如:redis的ip修改为上面配置文件的redis1)。打包并生成镜像:具体实现查看上面 三、docker微服务实战
2.3 docker-compose config -q
检查docker-compose.yml文件
2.4 docker-compose up
或者docker-compose up -d
启动服务
2.5 进入mysql容器完成数据库初始化等操作
2.6 docker-compose stop
可停止所有的服务
六、docker轻量化可视化工具Portainer
Portainer是一款轻量级的应用,提供了图形化界面,用于方便的管理Docker环境,包括单机和集群环境。
- 安装
官网地址:https://www.portainer.io/
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
--restart=always
表示docker启动时同时启动该容器
2. 第一次登录需要创建admin,访问地址:ip:9000
- 设置admin用户名和密码后首次登录:设置密码8位即可
- 选择local选项卡后查看本地docker详细信息
七、docker容器监控之CAdvisor+InfluxDB+Granfana(重量级工具)
- CAdvisor:收集服务,2分钟的数据,访问地址:http://ip:8080。首次访问很慢
- InfluxDB:时序数据库,存储服务,访问地址:http://ip:8083
- Granfana:展现服务,默认账号密码 admin/admin,访问地址:http://ip:3000
- 编写
docker-compose.yml
文件
version: '3.1'
volumes:
grafana_data: {} #实现了grafana数据的挂载
services: #表示我们要启动的服务,即要docker run的内容,多个实例服务
influxdb:
image: tutum/influxdb:0.9
restart: always
environment:
- PRE_CREATE_DB=cadvisor #预先创建一个数据库
ports:
- "8083:8083" #对外是8083
- "8086:8086" #内部即8086
volumes:
- ./data/influxdb:/data
cadvisor:
image: google/cadvisor
links:
- influxdb:influxsrv
command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086 #这就是相当于mysql选择的那个驱动
restart: always
ports:
- "18080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
grafana:
image: grafana/grafana
user: "104"
restart: always #随着docker启动,就启动
links:
- influxdb:influxsrv
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- HTTP_USER=admin
- HTTP_PASS=admin
- INFLUXDB_HOST=influxsrv
- INFLUXDB_PORT=8086
- INFLUXDB_NAME=cadvisor
- INFLUXDB_USER=root
- INFLUXDB_PASS=root
-
docker-compose -q
检查并执行 docker-compose up
运行
- 登录3个软件的访问地址查看
- grafana图形界面的配置(略)