# MySQL 主从复制

# 新建主服务器容器实例 3307

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

# 新建 my.cnf 文件

进入 /mydata/mysql-master/conf 目录下新建 my.cnf

vim my.cnf

添加配置文件内容

[mysqld]
# 配置 server_id,同一局域网中需要唯一
server_id=101
# 配置指定不需要的数据库名称
binlog-ignore-db=mysql
# 开启二进制日志功能
log-bin=mail-mysql-slave1-bin
# 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
# 设置使用的二进制日志格式(mixed,statement,row) 
binlog_format=mixed
# 二进制日志过期清理时间,默认值为 0,表示不自动清理
expire_logs_days=7
# 跳过主从复制中遇到的所有错误或指定类型的错误,避免 slave 端复制中断
slave_skip_errors=1062

# 重启 master 实例

docker restart mysql-master

# 进入 mysql-master 容器

docker exec -it mysql-master /bin/bash
mysql -uroot -proot

# 创建数据同步用户

# 创建用户
create user 'slave'@'%' identified by '123456';
# 授予权限
grant replication slave, replication client on *.* to 'slave'@'%';

# 新建从服务器 3308

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

# 新建 my.cnf 文件

进入 /mydata/mysql-slave/conf 目录下新建 my.cnf

vim my.cnf

添加配置文件内容

[mysqld]
# 配置 server_id,同一局域网中需要唯一
server_id=102
# 配置指定不需要的数据库名称
binlog-ignore-db=mysql
# 开启二进制日志功能
log-bin=mail-mysql-slave1-bin
# 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
# 设置使用的二进制日志格式(mixed,statement,row) 
binlog_format=mixed
# 二进制日志过期清理时间,默认值为 0,表示不自动清理
expire_logs_days=7
# 跳过主从复制中遇到的所有错误或指定类型的错误,避免 slave 端复制中断
slave_skip_errors=1062
# reply_log 配置中继日志
relay_log=mail-mysql-relay-bin
# log_slave_updates 表示将复制事件写进自己的二进制日志
log_slave_updates=1
# slave 设置为只读(具有 super 权限的用户除外)
read_only=1

# 重启 slave 实例

docker restart mysql-slave

# 在主数据库查看主从同步状态

show master status;

image-20230801144331746

# 进入 mysql-slave 容器

docker exec -it mysql-slave /bin/bash
mysql -uroot -p123456

# 在从数据库中配置主从复制

change master to master_host='192.168.116.129', master_user='slave', master_password='123456', master_port=3307, master_log_file='mail_mysql_bin.000001', master_log_pos=617, master_connect_retry=30;

# 在从数据库中查看主从同步状态

show slave status \G;

image-20230801145134479

# 在从数据库中开启主从同步

start slave;

# 查看从数据库状态发现已经同步

show slave status \G;

image-20230801145103964

# 分布式存储

# 哈希取余分区算法

# 介绍

每次进行取余操作,计算出哈希值,用来决定数据映射到哪一个节点上

image-20230801150022409

# 优点

简单粗暴,直接有效,只需要预估好数据规划好节点,就能保证一段时间的数据支撑,使用 Hash 算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡 + 分而治之的作用

# 缺点

某个 redis 机器宕机了,由于台数数量变化,会导致 hash 取余全部数据重新洗牌

# 一致性哈希算法

# 算法构建一致性哈希环

一致性哈希算法必然有个 hash 函数并按照算法产生 hash 值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个 hash 空间,这个是一个线性空间,但在算法中,我们通过适当的逻辑控制将它首位连接,这样就让它逻辑上形成一个环形空间。

# 服务器 IP 节点映射

将集群中的 IP 节点映射到环上的某一个位置

将各个服务器使用 Hash 进行一个哈希,具体可以选择服务器的 IP 或主机名作为关键字进行哈希,这样每台机器就能确定哈希环上的位置,假如 4 个节点 NodeA,B,C,D,经过 IP 地址的哈希函数计算 (hash (ip)),使用 IP 地址哈希后的环空间的位置如下:

image-20230801151053073

# key 落到服务器的落键规则

当我们需要存储一个 kv 键值对时,首先计算 key 的 hash 值,hash (key),将这个 key 使用相同的函数 Hash 计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针行走,第一台遇到的服务器就是其应该定位到的服务器,并将键值对存储在该节点上

假设我们有 ObjectA、ObjectB、ObjectC、ObjectD 四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性 Hash 算法,数据 A 会定为 Node A 上,B 会定为 Node B 上,C 会定为 Node C 上,D 会定为 Node D 上。

image-20230801151612682

# 优点

# 一致性哈希算法的容错性

加入 Node C 宕机,可以看到此对象 A、B、C、D 不会受到影响,只有 C 对象被定位到 Node D,一般的,在一致性 Hash 算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走的低的第一台服务器)之间的数据,其它不会受到影响,简单说,就是 C 挂了,受到影响的是 B、C 之间的数据,并且这些数据会转移到 D 进行存储

# 一致性哈希算法的扩张性

数据量增加了,需要增加一台节点 Node X,X 的位置在 A 和 B 之间,那受到影响的也就是 A 到 X 之间的数据,重新把 A 到 X 的数据录入到 X 上即可,不会导致 hash 取余全部数据重新洗牌

# 缺点

# Hash 环数据倾斜问题

一致性 Hash 算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题

# 哈希槽分区算法

# 哈希槽简介

在数据和节点之间加入了一层,这层称为哈希槽(slot),用于管理数据与节点之间的关系,现在就相当于节点上放的是槽,槽里面放的是数据

image-20230801153345757

# 哈希槽计算

Redis 集群内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同节点,当需要 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 取余,这样每个 key 就会对应一个编号在 0 - 16383 之间的哈希槽,也就是映射到某个对应的节点上

image-20230801153907258

# 主 3 从 redis 集群配置

# 创建 6 个 redis 容器

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
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
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
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
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
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

# 进入容器

docker exec -it redis-node-1 /bin/bash

# 构建主从关系

redis-cli --cluster create 192.168.116.129:6381 192.168.116.129:6382 192.168.116.129:6383 192.168.116.129:6384 192.168.116.129:6385 192.168.116.129:6386 --cluster-replicas 1

# 查看集群状态

redis-cli -p 6381
cluster info
cluster nodes

# redis 集群读写

# error 说明

image-20230801155011562

# 解决办法

使用集群方式连接

redis-cli -p 6381 -c

# 查看集群路由状态

redis-cli --cluster check 192.168.116.129:6381

# 主从容错切换迁移

  • 主 redis 节点宕机,对应的从 redis 节点升为 master
  • 之前的主 redis 节点恢复,现在恢复的 redis 节点为从 redis 节点

# 主从扩容

# 新建 6387、6388 两个节点

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
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

# 进入 6387 容器实例内部

docker exec -it redis-node-7 /bin/bash

# 将新增的 6387 系欸但作为 master 节点加入原集群

redis-cli --cluster add-node 192.168.116.129:6387 192.168.116.129:6381

# 重新分配槽号

redis-cli --cluster reshard 192.168.116.129:6381

image-20230801160645824

# 将 6388 作为从节点挂载到 6387 下面

redis-cli --cluster add-node 192.168.116.129:6388 192.168.116.126:6387 --cluster-slave --cluster-master-id e2e097252f20727da3df32b05eabc23bac879ca2

# 主从缩容

# 检查集群情况获取 6388 节点 ID

redis-cli --cluster check 192.168.116.129:6382

# 将从节点 6388 清除

redis-cli --cluster del-node 192.168.116.129:6387 e2e097252f20727da3df32b05eabc23bac879ca2

# 重新分配槽号

# 清空 6387 槽位分配给 6381

redis-cli --cluster reshard 192.168.116.129:6381

image-20230801204017323

# 将 6387 从集群中删除

redis-cli --cluster del-node 192.168.116.129:6387 e2e097252f20727da3df32b05eabc23bac879ca2

# Dockerfile

# 介绍

Dockerfile 是用来构建 Docker 镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本

# 构建三步骤

  1. 编写 Dockerfile 文件
  2. docker build 命令构建镜
  3. docker run 依镜像运行容器实例

# Dockerfile 构建过程解析

# Dockerfile 基础知识

  1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. #表示注解
  4. 每条指令都会创建一个新的镜像层并对镜像进行提交

# Docker 执行 Dockerfile 的大致流程

  1. docker 从基础镜像运行一个容器
  2. 执行一条指令并对容器做出修改
  3. 执行类似 docker commit 的操作提交一个新的镜像层
  4. docker 再基于刚提交的镜像运行一个新的容器
  5. 执行 dockerfile 中的下一条指令直到所有指令都要执行完成

# Dockerfile 常用保留字

# FROM

基础镜像,当前新的镜像是基于哪个镜像,指定一个已经存在的镜像作为模板,第一条必须是 from

# MAINTAINER

镜像维护者的姓名和邮箱

# RUN

容器构建时需要运行的命令,Run 命令是在 docker build 时运行的

# EXPOSE

当前容器对外暴露出的端口

# WORKDIR

指定创建容器后,中断默认登录进来的工作目录,一个落脚点

# USER

指定该镜像以什么用户去执行,如果不指定,默认是 root

# ENV

用来在构建镜像过程中设置环境变量

# ADD

将宿主机目录下的文件拷贝进镜像且会自动处理 URL 和解压 tar 压缩包

# COPY

拷贝文件和目录到镜像中

# VOLUME

容器数据卷,用于数据保存和持久化工作

# CMD

  • 指定容器启动后要干的事情
  • Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
  • 可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
  • CMD 在 docker run 时运行
  • RUN 在 docker build 时运行

# ENTRYPOINT

  • 指定一个容器启动时要运行的命令
  • 类似于 CMD 命令,但是 ENTRYPOINT 不会被 docker run 后面的命令覆盖,而且这些命令参数会被当前参数传给 ENTRYPOINT 指令指定的程序
  • ENTRYPOINT 和 CMD 一起用,一般变参才会使用 CMD,这里的 CMD 等于是在给 ENTRYPOINT 传参
  • 当指定了 ENTRYPOINT 后,CMD 的含义就发生了变化,不再是运行命令,而是将 CMD 的内容作为参数传递给 ENTRYPOINT 指令
FROM nginx
ENTRYPOINT ["nginx", "-c"]
CMD ["/etc/nginx/nginx.conf"]

# 虚悬镜像

# 介绍

仓库名、标签都是 none 的镜像,俗称 dangline image

# 删除

docker image prune

# 微服务工程 Docker 部署

# 编写 Dockerfile

# Dockerfile 内容

# 基础镜像使用 java8
FROM java:8
# 作者
MAINTAINER baozi
# VOLUME 指定临时文件目录为 /tmp
VOLUME /tmp
# 将 jar 包添加到容器中并更名
ADD docker_boot-0.0.1-SNAPSHOT.jar baozi_docker.jar
# 运行 jar 包
RUN bash -c 'touch /baozi_docker.jar'
ENTRYPOINT ["java","-jar","/baozi_docker.jar"]
# 暴露 6001 端口作为微服务
EXPOSE 6001

# 目录存放

将微服务 jar 包和 Dockerfile 文件上传到统一目录下

# 构建镜像

docker build -t baozi_docker:1.6 .

# 运行容器

docker run -d -p 6001:6001 baozi_docker:1.6

# network

# 介绍

docker 启动之后,会产生一个名为 docker0 的虚拟网桥。就是通过这个网桥与宿主机之间,以及容器与容器之间进行连接

# 常用命令

# ls

查看所有的 docker 网络模式命令

docker network ls

# COMMAND

docker network COMMAND

# create

创建一个 network

docker network create name__network

# rm

删除一个 network

docker network rm name_network

# inspect

查看网络详情

docker network inspect bridge

# 作用

  • 容器间的互联和通信以及端口映射
  • 容器 IP 变动的时候可以通过服务名直接网络通信而不受到影响

# 网络模式分类

网络模式 简介
bridge 为每一个容器分配、设置 IP,并将容器连接到一个 docker,虚拟网桥,默认为该模式
host 容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口
none 容器有独立的 Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP 等
container 新创建的容器不会创建自己的网卡和配置自己的 IP,而是和一个指定的容器共享 IP,端口范围等

# bridge 模式

# 简介

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为 docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络,Docker 默认指定了 docker0 接口的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信

# 查看 bridge 网络详细信息

docker network inspect bridge | grep name

# 底层连接模式

  • 整个宿主机的网桥模式都是 docker0,类似于一个交换机有一堆接口,每个接口叫 veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫 veth pair)
  • 每一个容器实例内部也有一块网卡,每个接口叫 eth0
  • docker0 上面的每个 veth 匹配某个容器实例内部的 eth0,两两配对,一一配对

image-20230802080718470

# host 模式

# 介绍

直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行 NAT 转换

# 底层连接模式

容器将不再获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace,容器将不会虚拟出自己的网卡而是使用宿主机的 IP 和端口

# none 模式

# 介绍

禁用网络功能,只有 lo 标识(就是 127.0.0.1 表示本地回环)

# 底层连接模式

  • 在 none 模式下,并不为 Docker 容器进行任何网络配置
  • 也就是说,这个 Docker 容器没有网卡、IP、路由等信息,只有一个 lo
  • 需要我们自己为 Docker 容器添加网卡、配置 IP 等

# container 模式

# 介绍

新建的容器和已经存在的一个容器共享一个网络 ip 配置而不是和宿主机共享,新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等,同样,这两个容器除了网络方面,其它的如文件系统、进程列表等还是隔离的

image-20230802081640074

# 自定义网络

# 介绍

自定义网络本身就维护好了主机名和 IP 的对应关系(ip 和域名都能通)

# Compose

# 介绍

  • Docker-Compose 是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排
  • Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用,你需要定义一个 YAML 文件格式的配置文件 docker-compose.yml,写好多个容器之间的调用关系,然后,只要一个命令,就能同时启动 / 关闭这些容器

# 作用

  • Compose 允许用户通过一个单独的 docker-compose.yml 模板文件来定义一组相关联的应用容器为一个项目(project)
  • 可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装应用的所有依赖,完成构建,Docker-Compose 解决了容器与容器之间如何管理编排的问题

# 核心概念

# 一个文件

docker-compose.yml 文件

# 两个要素

# 服务(service)

一个应用容器实例,比如订单微服务、库存微服务、mysql 容器

# 工程(project)

由一组关联的应用容器组成一个完整的业务单元,在 docker-compose.yml 文件中定义

# Compose 使用的三个步骤

  • 编写 Dockerfile 定义各个微服务应用并构建出对应的镜像文件
  • 使用 docker-compose.yml 定义一个完整的业务单元,安排好整体应用中的各个容器服务
  • 最后,执行 docker-compose up 命令,来启动并运行整个应用程序,完成一键部署

# 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 停止服务

# Portainer

# 启动 Portainer 实例

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:latest

# GIC 容器重量级监控系统

# 介绍

CAdvisor 监控收集 + InfluxDB 存储数据 + Granfana 展示图表

image-20230802084936454

# CAdvisor

# 介绍

CAdvisor 是一个容器资源监控工具,包括容器的内存 CPU,网络 IO,磁盘 IO 监控,同时提供一个 WEB 页面用于查看容器的实时运行状态,CAdvisor 默认存储是 2 分钟的数据,而且只是针对单物理机,不过,CAvisor 提供了很多数据集成接口,支持 InfluxDB,Redis,Kafka,Elasticsearch 等集成,可以加上对应配置将监控数据发往这些数据库存储起来

# 功能

  • 展示 host 和容器两个层次的监控数据
  • 展示历史变化的数据

# InfluxDB

# 介绍

InfluxDB 是用 Go 语言编写的开源分布式时序、事件和指标数据库,无需外部依赖

CAdvisor 默认只是在本机保存最近两分钟的数据,为了持久化存储数据和统一收集展示监控数据,需要将数据存储到 InfluxDB 中,InfluxDB 是一个时序数据库,专门用于存储时序相关数据,很适合存储 CAdvisor 的数据,而且,CAdvisor 本身已经提供了 InfluxDB 的集成方法,丰启动容器时指定配置即可

# 功能

  • 基于时间排序,支持与时间有关的相关函数
  • 可度量性,你可以实时对大量数据进行计算
  • 基于事件,它支持任意的事件数据

# Granfana

# 介绍

Granfana 是一个开源的数据监控分析可视化平台,支持多种数据源配置和丰富的插件及模板功能,支持图表权限控制与报警

# 功能

  • 灵活丰富的图形化选项
  • 可以混合多种风格
  • 支持白天和黑夜模式
  • 多个数据源

# 使用 compose 部署 GIC

# 配置 docker-compose.yml 文件

version: '3.1'
volumes:
  grafana_data: {}
services:
  influxdb:
    image: tutum/influxdb:0.9
    restart: always
    environment:
      - PRE_CREATE_DB=cadvisor
    ports:
      - "8083:8083"
      - "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
    restart: always
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
  grafana:
    user: "104"
    image: grafana/grafana
    user: "104"
    restart: always
    links:
      - influxdb:influxsrv
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - HTTP_USER=admin
      - HTTP_PASS=admin
      - INFLUXDB_HOST=influxsrv
      - INFLUXDB_PORT=8066
      - INFLUXDB_NAME=cadvisor
      - INFLUXDB_USER=root
      - INFLUXDB_PASS=root

# 检查配置文件

docker-compose config -q

# 启动 docker-compose 文件

docker-compose up

# 验证容器启动情况

http://ip:8080/ 浏览 cAdvisor

http://ip:8083/ 浏览 influxdb 存储服务

http://ip:3000/ 浏览 grafana 展现服务

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Baozi 微信支付

微信支付

Baozi 支付宝

支付宝

Baozi 微信

微信