丢了翅膀的鱼 發表於 2019-5-13 02:07:00

Docker环境下的前后端分离项目部署与运维(六)搭建MySQL集群

<h2>单节点数据库的弊病</h2>
<ul>
<li>大型互联网程序用户群体庞大,所以架构必须要特殊设计</li>
<li>单节点的数据库无法满足性能上的要求</li>
<li>单节点的数据库没有冗余设计,无法满足高可用</li>
</ul>
<h2>单节点MySQL的性能瓶领颈</h2>
<p>2016年春节微信红包巨大业务量,数据库承受巨大负载</p>
<p><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190507225214316-411176921.png" alt="" width="779" height="348"></p>
<h2>常见MySQL集群方案</h2>
<p>  mysql 集群方案介绍,建议使用pxc,因为弱一致性会有问题,比如说a节点数据库显示我购买成功,b 节点数据库显示没有成功,这就麻烦了,pxc 方案是在全部节点都写入成功之后才会告诉你成功,是可读可写双向同步的,但是replication是单向的,不同节点的数据库之间都会开放端口进行通讯,如果从防火墙的这个端口关闭,pxc就不会同步成功,也不会返给你成功了。<br><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190507225950269-1729992416.png" alt="" width="803" height="239"></p>
<p><strong>Replication</strong></p>
<ul>
<li>速度快,但仅能保证弱一致性,适用于保存价值不高的数据,比如日志、帖子、新闻等。</li>
<li>采用master-slave结构,在master写入会同步到slave,能从slave读出;但在slave写入无法同步到master。</li>
<li>采用异步复制,master写入成功就向客户端返回成功,但是同步slave可能失败,会造成无法从slave读出的结果。<br><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190507230450807-1013209702.png" alt="" width="376" height="269">
<p>&nbsp;</p>





















</li>























</ul>
<p><strong>PXC (Percona XtraDB Cluster)</strong></p>
<ul>
<li>速度慢,但能保证强一致性,适用于保存价值较高的数据,比如订单、客户、支付等。</li>
<li>数据同步是双向的,在任一节点写入数据,都会同步到其他所有节点,在任何节点上都能同时读写。</li>
<li>采用同步复制,向任一节点写入数据,只有所有节点都同步成功后,才会向客户端返回成功。事务在所有节点要么同时提交,要么不提交。<br><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190507230413226-1762874626.png" alt="" width="435" height="284"></li>






















</ul>
<p><strong>建议PXC使用PerconaServer (MySQL改进版,性能提升很大)</strong></p>
<p><strong>PXC的数据强一致性</strong></p>
<ul>
<li>同步复制,事务在所有集群节点要么同时提交,要么不提交 </li>
<li>Replication采用异步复制,无法保证数据的一致性</li>





















</ul>
<p>&nbsp;<img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190507231323738-304603362.png" alt="" width="396" height="238"><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190507231454223-75159759.png" alt="" width="431" height="218"></p>
<h2>PXC集群安装介绍</h2>
<p>在Docker中安装PXC集群,使用Docker仓库中的PXC官方镜像:https://hub.docker.com/r/percona/percona-xtradb-cluster</p>
<ol>
<li>从docker官方仓库中拉下PXC镜像:<br>
<div class="cnblogs_code">
<pre>docker pull percona/percona-xtradb-cluster</pre>
</div>
<p>或者本地安装</p>
<div class="cnblogs_code">
<pre>docker load &lt; /home/soft/pxc.tar.gz</pre>
</div>
<p>安装完成:</p>
<div class="cnblogs_code"><img id="code_img_closed_b366a88e-a40e-4962-802f-0d5ee4be77b6" class="code_img_closed" src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_b366a88e-a40e-4962-802f-0d5ee4be77b6" class="code_img_opened" style="display: none" src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="">
<div id="cnblogs_code_open_b366a88e-a40e-4962-802f-0d5ee4be77b6" class="cnblogs_code_hide">
<pre># docker pull percona/percona-xtradb-<span style="color: rgba(0, 0, 0, 1)">cluster
Using </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> tag: latest
Trying to pull repository docker.io</span>/percona/percona-xtradb-<span style="color: rgba(0, 0, 0, 1)">cluster ...
latest: Pulling </span><span style="color: rgba(0, 0, 255, 1)">from</span> docker.io/percona/percona-xtradb-<span style="color: rgba(0, 0, 0, 1)">cluster
ff144d3c0ab1: Pull complete
eafdff1524b5: Pull complete
c281665399a2: Pull complete
c27d896755b2: Pull complete
c43c51f1cccf: Pull complete
6eb96f41c54d: Pull complete
4966940ec632: Pull complete
2bafadcea292: Pull complete
3c2c0e21b695: Pull complete
52a8c2e9228e: Pull complete
f3f28eb1ce04: Pull complete
d301ece75f56: Pull complete
3d24904bec3c: Pull complete
1053c2982c37: Pull complete
Digest: sha256:17c64dacbb9b62bd0904b4ff80dd5973b2d2d931ede2474170cbd642601383bd
Status: Downloaded newer image </span><span style="color: rgba(0, 0, 255, 1)">for</span> docker.io/percona/percona-xtradb-<span style="color: rgba(0, 0, 0, 1)">cluster:latest
# docker images
REPOSITORY                                 TAG               IMAGE ID            CREATED             SIZE
docker.io</span>/percona/percona-xtradb-cluster   latest            70b3670450ef      <span style="color: rgba(128, 0, 128, 1)">2</span> months ago      <span style="color: rgba(128, 0, 128, 1)">408</span> MB</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</li>
<li>重命名镜像:(名称太长,重命名一下)<br>
<div class="cnblogs_code">
<pre>docker tag percona/percona-xtradb-cluster:latest pxc</pre>
</div>
<p>然后原来的镜像就可以删除掉了</p>
<div class="cnblogs_code"><img id="code_img_closed_ce92ef81-2e6d-40d4-9c17-3bdf5d279e30" class="code_img_closed" src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_ce92ef81-2e6d-40d4-9c17-3bdf5d279e30" class="code_img_opened" style="display: none" src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="">
<div id="cnblogs_code_open_ce92ef81-2e6d-40d4-9c17-3bdf5d279e30" class="cnblogs_code_hide">
<pre># docker images
REPOSITORY                                 TAG               IMAGE ID            CREATED             SIZE
docker.io</span>/percona/percona-xtradb-cluster   latest            70b3670450ef      <span style="color: rgba(128, 0, 128, 1)">2</span> months ago      <span style="color: rgba(128, 0, 128, 1)">408</span><span style="color: rgba(0, 0, 0, 1)"> MB
pxc                                        latest            70b3670450ef      </span><span style="color: rgba(128, 0, 128, 1)">2</span> months ago      <span style="color: rgba(128, 0, 128, 1)">408</span><span style="color: rgba(0, 0, 0, 1)"> MB
docker.io</span>/java                           latest            d23bdf5b1b1b      <span style="color: rgba(128, 0, 128, 1)">2</span> years ago         <span style="color: rgba(128, 0, 128, 1)">643</span><span style="color: rgba(0, 0, 0, 1)"> MB
# docker rmi docker.io/percona/percona-xtradb-<span style="color: rgba(0, 0, 0, 1)">cluster
Untagged: docker.io</span>/percona/percona-xtradb-<span style="color: rgba(0, 0, 0, 1)">cluster:latest
Untagged: docker.io</span>/percona/percona-xtradb-<span style="color: rgba(0, 0, 0, 1)">cluster@sha256:17c64dacbb9b62bd0904b4ff80dd5973b2d2d931ede2474170cbd642601383bd
# docker images
REPOSITORY          TAG               IMAGE ID            CREATED             SIZE
pxc               latest            70b3670450ef      </span><span style="color: rgba(128, 0, 128, 1)">2</span> months ago      <span style="color: rgba(128, 0, 128, 1)">408</span><span style="color: rgba(0, 0, 0, 1)"> MB
docker.io</span>/java      latest            d23bdf5b1b1b      <span style="color: rgba(128, 0, 128, 1)">2</span> years ago         <span style="color: rgba(128, 0, 128, 1)">643</span> MB</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</li>
<li><strong><strong>出于安全考虑,给PXC集群创建Docker内部网络<br></strong></strong>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 创建网段
docker network create </span>--subnet=<span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>/<span style="color: rgba(128, 0, 128, 1)">24</span><span style="color: rgba(0, 0, 0, 1)"> net1
# 查看网段
docker network inspect net1
# 删除网段
# docker network rm net1</span></pre>
</div>
</li>
<li>创建Docker卷:<br>使用Docker时,业务数据应保存在宿主机中,采用目录映射,这样可以使数据与容器独立。但是容器中的PXC无法直接使用映射目录,解决办法是采用Docker卷来映射<br>
<div class="cnblogs_code">
<pre># 创建名称为v1的数据卷,--<span style="color: rgba(0, 0, 0, 1)">name可以省略
docker volume create </span>--name v1</pre>
</div>
<p>查看数据卷</p>
<div class="cnblogs_code">
<pre>docker inspect v1</pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre># docker inspect v1
[
    {
      </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Driver</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">local</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
      </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Labels</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {},
      </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mountpoint</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/var/lib/docker/volumes/v1/_data</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,#这里是在宿主机的保存位置
      </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">v1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
      </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Options</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {},
      </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Scope</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">local</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    }
]</span></pre>
</div>
<p>删除数据卷</p>
<div class="cnblogs_code">
<pre>docker volume rm v1</pre>
</div>
<p><strong>创建5个数据卷</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 创建5个数据卷
docker volume create </span>--<span style="color: rgba(0, 0, 0, 1)">name v1
docker volume create </span>--<span style="color: rgba(0, 0, 0, 1)">name v2
docker volume create </span>--<span style="color: rgba(0, 0, 0, 1)">name v3
docker volume create </span>--<span style="color: rgba(0, 0, 0, 1)">name v4
docker volume create </span>--name v5</pre>
</div>
</li>
<li>创建5个PXC容器:<br>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 创建5个PXC容器构成集群
# 第一个节点
docker run </span>-d -p <span style="color: rgba(128, 0, 128, 1)">3306</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span> -e MYSQL_ROOT_PASSWORD=abc123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=abc123456 -v v1:/<span style="color: rgba(0, 0, 255, 1)">var</span>/lib/mysql --name=node1 --network=net1 --ip <span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.2</span><span style="color: rgba(0, 0, 0, 1)"> pxc
# 在第一个节点启动后要等待一段时间,等候mysql启动完成。

# 第二个节点
docker run </span>-d -p <span style="color: rgba(128, 0, 128, 1)">3307</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span> -e MYSQL_ROOT_PASSWORD=abc123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=abc123456 -e CLUSTER_JOIN=node1 -v v2:/<span style="color: rgba(0, 0, 255, 1)">var</span>/lib/mysql --name=node2 --net=net1 --ip <span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.3</span><span style="color: rgba(0, 0, 0, 1)"> pxc
# 第三个节点
docker run </span>-d -p <span style="color: rgba(128, 0, 128, 1)">3308</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span> -e MYSQL_ROOT_PASSWORD=abc123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=abc123456 -e CLUSTER_JOIN=node1 -v v3:/<span style="color: rgba(0, 0, 255, 1)">var</span>/lib/mysql --name=node3 --net=net1 --ip <span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.4</span><span style="color: rgba(0, 0, 0, 1)"> pxc
# 第四个节点
docker run </span>-d -p <span style="color: rgba(128, 0, 128, 1)">3309</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span> -e MYSQL_ROOT_PASSWORD=abc123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=abc123456 -e CLUSTER_JOIN=node1 -v v4:/<span style="color: rgba(0, 0, 255, 1)">var</span>/lib/mysql --name=node4 --net=net1 --ip <span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.5</span><span style="color: rgba(0, 0, 0, 1)"> pxc
# 第五个节点
docker run </span>-d -p <span style="color: rgba(128, 0, 128, 1)">3310</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span> -e MYSQL_ROOT_PASSWORD=abc123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=abc123456 -e CLUSTER_JOIN=node1 -v v5:/<span style="color: rgba(0, 0, 255, 1)">var</span>/lib/mysql --name=node5 --net=net1 --ip <span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.6</span> pxc</pre>
</div>
<p>查看:</p>
<div class="cnblogs_code">
<pre># docker ps
CONTAINER ID      IMAGE               COMMAND             CREATED            STATUS            PORTS                                 NAMES
f4708ce32209      pxc               </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/entrypoint.sh </span><span style="color: rgba(128, 0, 0, 1)">"</span>   About a minute ago   Up About a minute   <span style="color: rgba(128, 0, 128, 1)">4567</span>-<span style="color: rgba(128, 0, 128, 1)">4568</span>/tcp, <span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>:<span style="color: rgba(128, 0, 128, 1)">3309</span>-&gt;<span style="color: rgba(128, 0, 128, 1)">3306</span>/<span style="color: rgba(0, 0, 0, 1)">tcp   node4
bf612f9586bc      pxc               </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/entrypoint.sh </span><span style="color: rgba(128, 0, 0, 1)">"</span>   <span style="color: rgba(128, 0, 128, 1)">17</span> minutes ago       Up <span style="color: rgba(128, 0, 128, 1)">17</span> minutes       <span style="color: rgba(128, 0, 128, 1)">4567</span>-<span style="color: rgba(128, 0, 128, 1)">4568</span>/tcp, <span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>:<span style="color: rgba(128, 0, 128, 1)">3310</span>-&gt;<span style="color: rgba(128, 0, 128, 1)">3306</span>/<span style="color: rgba(0, 0, 0, 1)">tcp   node5
9fdde5e6becd      pxc               </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/entrypoint.sh </span><span style="color: rgba(128, 0, 0, 1)">"</span>   <span style="color: rgba(128, 0, 128, 1)">17</span> minutes ago       Up <span style="color: rgba(128, 0, 128, 1)">17</span> minutes       <span style="color: rgba(128, 0, 128, 1)">4567</span>-<span style="color: rgba(128, 0, 128, 1)">4568</span>/tcp, <span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>:<span style="color: rgba(128, 0, 128, 1)">3308</span>-&gt;<span style="color: rgba(128, 0, 128, 1)">3306</span>/<span style="color: rgba(0, 0, 0, 1)">tcp   node3
edd5794175b6      pxc               </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/entrypoint.sh </span><span style="color: rgba(128, 0, 0, 1)">"</span>   <span style="color: rgba(128, 0, 128, 1)">18</span> minutes ago       Up <span style="color: rgba(128, 0, 128, 1)">18</span> minutes       <span style="color: rgba(128, 0, 128, 1)">4567</span>-<span style="color: rgba(128, 0, 128, 1)">4568</span>/tcp, <span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>:<span style="color: rgba(128, 0, 128, 1)">3307</span>-&gt;<span style="color: rgba(128, 0, 128, 1)">3306</span>/<span style="color: rgba(0, 0, 0, 1)">tcp   node2
33d842de7f42      pxc               </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/entrypoint.sh </span><span style="color: rgba(128, 0, 0, 1)">"</span>   <span style="color: rgba(128, 0, 128, 1)">21</span> minutes ago       Up <span style="color: rgba(128, 0, 128, 1)">21</span> minutes       <span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span>-&gt;<span style="color: rgba(128, 0, 128, 1)">3306</span>/tcp, <span style="color: rgba(128, 0, 128, 1)">4567</span>-<span style="color: rgba(128, 0, 128, 1)">4568</span>/tcp   node1</pre>
</div>
</li>
</ol>
<h2>数据库负载均衡的必要性</h2>
<p>虽然搭建了集群,但是不使用数据库负载均衡,单节点处理所有请求,负载高,性能差</p>
<p><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508001540980-1359624843.png" alt="" width="593" height="262"></p>
<p>将请求均匀地发送给集群中的每一个节点。</p>
<ul>
<li>所有请求发送给单一节点,其负载过高,性能很低,而其他节点却很空闲。</li>
<li>使用Haproxy做负载均衡,可以将请求均匀地发送给每个节点,单节点负载低,性能好</li>
</ul>
<p><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508001629362-2045646.png" alt="" width="588" height="335"></p>
<h2>负载均衡中间件对比</h2>
<p>  负载均衡首先是数据库的集群,加入5个集群,每次请求都是第一个的话,有可能第一个数据库就挂掉了,所以更优的方案是对不同的节点都进行请求,这就需要有中间件进行转发,比较好的中间件有nginx,haproxy等,因nginx 支持插件,但是刚刚支持了tcp/ip 协议,haproxy 是一个老牌的中间转发件。如果要用haproxy的话,可以从官方下载镜像,然后呢对镜像进行配置(自己写好配置文件,因为这个镜像是没有配置文件的,配置好之后再运行镜像的时候进行文件夹的映射,配置文件开放3306(数据库请求,然后根据check心跳检测访问不同的数据库,8888 对数据库集群进行监控))。配置文件里面设置用户(用户在数据库进行心跳检测,判断哪个数据库节点是空闲的,然后对空闲的进行访问),还有各种算法(比如轮训),最大连接数,时间等,还有对集群的监控。配置文件写好以后运行这个镜像,镜像运行成功后进入容器启动配置文件 。其实haprocy返回的也是一个数据库实例(但是并不存储任何的数据,只是转发请求),这个实例用来check其他节点。</p>
<p><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508001851732-1032385691.png" alt="" width="797" height="355"></p>
<h2>安装Haproxy</h2>
<ol>
<li>
<p>从Docker仓库拉取haproxy镜像:https://hub.docker.com/_/haproxy</p>
<div class="cnblogs_code">
<pre>docker pull haproxy</pre>
</div>
<div class="cnblogs_code"><img id="code_img_closed_a9149d10-e4d2-4b20-b056-d62dcec4157c" class="code_img_closed" src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_a9149d10-e4d2-4b20-b056-d62dcec4157c" class="code_img_opened" style="display: none" src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="">
<div id="cnblogs_code_open_a9149d10-e4d2-4b20-b056-d62dcec4157c" class="cnblogs_code_hide">
<pre># docker images
REPOSITORY          TAG               IMAGE ID            CREATED             SIZE
docker.io</span>/haproxy   latest            11fa4d7ff427      <span style="color: rgba(128, 0, 128, 1)">11</span> days ago         <span style="color: rgba(128, 0, 128, 1)">72.2</span> MB</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</li>
<li>
<p>创建Haproxy配置文件。供Haproxy容器使用(docker中未生成配置文件,我们需要在宿主机中自己创建配置文件)<br>配置文件详情参考:https://www.cnblogs.com/wyt007/p/10829184.html</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 启动容器时使用目录映射技术使容器读取该配置文件
touch </span>/home/soft/haproxy/haproxy.cfg</pre>
</div>
<p>haproxy.cfg</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># haproxy.cfg
</span><span style="color: rgba(0, 0, 255, 1)">global</span><span style="color: rgba(0, 0, 0, 1)">
    #工作目录
    chroot </span>/usr/local/etc/<span style="color: rgba(0, 0, 0, 1)">haproxy
    #日志文件,使用rsyslog服务中local5日志设备(</span>/<span style="color: rgba(0, 0, 255, 1)">var</span>/log/<span style="color: rgba(0, 0, 0, 1)">local5),等级info
    log </span><span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span><span style="color: rgba(0, 0, 0, 1)"> local5 info
    #守护进程运行
    daemon

defaults
    log    </span><span style="color: rgba(0, 0, 255, 1)">global</span><span style="color: rgba(0, 0, 0, 1)">
    mode    http
    #日志格式
    option    httplog
    #日志中不记录负载均衡的心跳检测记录
    option    dontlognull
    #连接超时(毫秒)
    timeout connect </span><span style="color: rgba(128, 0, 128, 1)">5000</span><span style="color: rgba(0, 0, 0, 1)">
    #客户端超时(毫秒)
    timeout client</span><span style="color: rgba(128, 0, 128, 1)">50000</span><span style="color: rgba(0, 0, 0, 1)">
    #服务器超时(毫秒)
    timeout server</span><span style="color: rgba(128, 0, 128, 1)">50000</span><span style="color: rgba(0, 0, 0, 1)">

#监控界面   
listenadmin_stats
    #监控界面的访问的IP和端口
    bind</span><span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>:<span style="color: rgba(128, 0, 128, 1)">8888</span><span style="color: rgba(0, 0, 0, 1)">
    #访问协议
    mode      http
    #URI相对地址
    stats uri   </span>/<span style="color: rgba(0, 0, 0, 1)">dbs
    #统计报告格式
    stats realm   Global\ statistics
    #登陆帐户信息
    <strong>stats authadmin:abc123456</strong>
#数据库负载均衡
listenproxy</span>-<span style="color: rgba(0, 0, 0, 1)">mysql
    #访问的IP和端口
    <strong>bind</strong></span><strong><span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span></strong><span style="color: rgba(0, 0, 0, 1)">
    #网络协议
    modetcp
    #负载均衡算法(轮询算法)
    #轮询算法:roundrobin
    #权重算法:</span><span style="color: rgba(0, 0, 255, 1)">static</span>-<span style="color: rgba(0, 0, 0, 1)">rr
    #最少连接算法:leastconn
    #请求源IP算法:source
    balanceroundrobin
    #日志格式
    optiontcplog
    #在MySQL中创建一个没有权限的haproxy用户,密码为空。Haproxy使用这个账户对MySQL数据库心跳检测
    optionmysql</span>-<span style="color: rgba(0, 0, 0, 1)">check user <strong>haproxy</strong>
    serverMySQL_1 </span><strong><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.2</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span></strong> check weight <span style="color: rgba(128, 0, 128, 1)">1</span> maxconn <span style="color: rgba(128, 0, 128, 1)">2000</span><span style="color: rgba(0, 0, 0, 1)">
    serverMySQL_2 </span><strong><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.3</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span></strong> check weight <span style="color: rgba(128, 0, 128, 1)">1</span> maxconn <span style="color: rgba(128, 0, 128, 1)">2000</span><span style="color: rgba(0, 0, 0, 1)">
    serverMySQL_3 </span><strong><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.4</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span></strong> check weight <span style="color: rgba(128, 0, 128, 1)">1</span> maxconn <span style="color: rgba(128, 0, 128, 1)">2000</span><span style="color: rgba(0, 0, 0, 1)">
    serverMySQL_4 </span><strong><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.5</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span></strong> check weight <span style="color: rgba(128, 0, 128, 1)">1</span> maxconn <span style="color: rgba(128, 0, 128, 1)">2000</span><span style="color: rgba(0, 0, 0, 1)">
    serverMySQL_5 </span><strong><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.6</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span></strong> check weight <span style="color: rgba(128, 0, 128, 1)">1</span> maxconn <span style="color: rgba(128, 0, 128, 1)">2000</span><span style="color: rgba(0, 0, 0, 1)">
    #使用keepalive检测死链
    optiontcpka</span></pre>
</div>
<p>&nbsp;</p>
</li>
<li>在数据库集群中创建空密码、无权限用户haproxy,来供Haproxy对MySQL数据库进行心跳检测<br>
<div class="cnblogs_code">
<pre>create user <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">haproxy</span><span style="color: rgba(128, 0, 0, 1)">'</span>@<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">%</span><span style="color: rgba(128, 0, 0, 1)">'</span> identified by <span style="color: rgba(128, 0, 0, 1)">''</span>;</pre>
</div>
</li>
<li>创建Haproxy容器(name=h1的原因是为了高可用)<br>
<div class="cnblogs_code">
<pre># 这里要加 --<span style="color: rgba(0, 0, 0, 1)">privileged
docker run </span>-it -d -p <span style="color: rgba(128, 0, 128, 1)">4001</span>:<span style="color: rgba(128, 0, 128, 1)">8888</span> -p <span style="color: rgba(128, 0, 128, 1)">4002</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span> -v /home/soft/haproxy:/usr/local/etc/haproxy --name h1 --net=net1 --ip <span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.7</span> --privileged haproxy</pre>
</div>
</li>
<li>进入容器<br>
<div class="cnblogs_code">
<pre>docker exec -it h1 bash</pre>
</div>
</li>
<li>在容器bash中启动Haproxy<br>
<div class="cnblogs_code">
<pre>haproxy -f /usr/local/etc/haproxy/haproxy.cfg</pre>
</div>
</li>
<li>接下来便可以在浏览器中打开Haproxy监控界面,端口4001,在配置文件中定义有用户名admin,密码abc123456。<br>我这边访问的是http://192.168.63.144:4001<strong><strong>/dbs,并且要使用用户名密码进行登录(小插曲,使用的是Basic登录,我的Chrome不知为何被屏蔽了,我最后用的火狐)<br><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508021459588-428533558.png" alt="" width="1394" height="541"></strong></strong>
<p>这时候我们手动挂掉一个Docker节点,看一下变化(我们会发现已经显示挂掉了)<br><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508021859242-1882279736.png" alt="" width="1403" height="536"></p>













</li>
<li>Haproxy不存储数据,只转发数据。可以在数据库中建立Haproxy的连接,端口4002,用户名和密码为数据库集群的用户名和密码</li>














</ol>
<h2>为什么要采用双机热备</h2>
<p>  单节点Haproxy不具备高可用,必须要有冗余设计</p>
<p>  双机就是两个请求处理程序,比如两个haproxy,当一个挂掉的时候,另外 一个可以顶上。热备我理解就是keepalive。在haproxy 容器中安装keepalive。</p>
<p><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508232959833-979782030.png" alt="" width="673" height="267"></p>
<h2>虚拟IP地址</h2>
<p>  linux系统可以在一个网卡中定义多个IP地址,把这些地址分配给多个应用程序,这些地址就是虚拟IP,Haproxy的双机热备方案最关键的技术就是虚拟IP。</p>
<p>  关键就是虚拟ip,定义一个虚拟ip,然后比如两个haproxy分别安装keepalive镜像,因为haproxy是ubuntu系统的,所以安装用apt-get,keepalive是作用是抢占虚拟ip,抢到的就是主服务器,没有抢到的就是备用服务器,然后两个keepalive进行心跳检测(就是创建一个用户到对方那里试探,看是否还活着,mysql的集群之间也是心跳检测),如果 挂掉抢占ip。所以在启动keepalive 之前首先要编辑好他的配置文件,怎么抢占,权重是什么,虚拟ip是什么,创建的用户交什么。配置完启动完以后可以ping一下看是否正确,然后将虚拟ip映射到局域网的ip</p>
<p><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508233127753-747053290.png" alt="" width="738" height="282"></p>
<h2>利用Keepalived实现双机热备</h2>
<ul>
<li>定义虚拟IP</li>
<li>在Docker中启动两个Haproxy容器,每个容器中还需要安装Keepalived程序(以下简称KA)</li>
<li>两个KA会争抢虚拟IP,一个抢到后,另一个没抢到就会等待,抢到的作为主服务器,没抢到的作为备用服务器</li>
<li>两个KA之间会进行心跳检测,如果备用服务器没有受到主服务器的心跳响应,说明主服务器发生故障,那么备用服务器就可以争抢虚拟IP,继续工作</li>
<li>我们向虚拟IP发送数据库请求,一个Haproxy挂掉,可以有另一个接替工作</li>






</ul>
<p><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508233341776-732914867.png" alt="" width="438" height="273"></p>
<h2>Нaproxy双机热备方案</h2>
<p><img src="https://img2018.cnblogs.com/blog/991704/201905/991704-20190508233632304-1557751673.png" alt="" width="1136" height="338"></p>
<ul>
<li>Docker中创建两个Haproxy,并通过Keepalived抢占Docker内地虚拟IP</li>
<li>Docker内的虚拟IP不能被外网,所以需要借助宿主机Keepalived映射成外网可以访问地虚拟IP</li>






</ul>
<h2>安装Keepalived</h2>
<ol>
<li>进入Haproxy容器,安装Keepalived:<br>
<div class="cnblogs_code">
<pre>$ docker exec -<span style="color: rgba(0, 0, 0, 1)">it h1 bash
apt</span>-<span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)"> update
apt</span>-<span style="color: rgba(0, 0, 255, 1)">get</span> install keepalived</pre>
</div>
</li>
<li>Keepalived配置文件(Keepalived.conf):<br>Keepalived的配置文件是/etc/keepalived/keepalived.conf<br>
<div class="cnblogs_code">
<pre># vim /etc/keepalived/<span style="color: rgba(0, 0, 0, 1)">keepalived.conf
vrrp_instanceVI_1 {
    stateMASTER# Keepalived的身份(MASTER主服务要抢占IP,BACKUP备服务器不会抢占IP)。
    </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)">eth0    # docker网卡设备,虚拟IP所在
    virtual_router_id</span><span style="color: rgba(128, 0, 128, 1)">51</span># 虚拟路由标识,MASTER和BACKUP的虚拟路由标识必须一致。从0~<span style="color: rgba(128, 0, 128, 1)">255</span><span style="color: rgba(0, 0, 0, 1)">
    priority</span><span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)"># MASTER权重要高于BACKUP数字越大优先级越高
    advert_int</span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"># MASTER和BACKUP节点同步检查的时间间隔,单位为秒,主备之间必须一致
    authentication {# 主从服务器验证方式。主备必须使用相同的密码才能正常通信
      auth_typePASS
      auth_pass</span><span style="color: rgba(128, 0, 128, 1)">123456</span><span style="color: rgba(0, 0, 0, 1)">
    }
    virtual_ipaddress {# 虚拟IP。可以设置多个虚拟IP地址,每行一个
      </span><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.201</span><span style="color: rgba(0, 0, 0, 1)">
    }
}</span></pre>
</div>
</li>
<li>启动Keepalived<br>
<div class="cnblogs_code">
<pre>service keepalived start</pre>
</div>
启动成功后,通过&nbsp;<span class="cnblogs_code">ip a</span>&nbsp;可以查看网卡中虚拟IP是否成功,另外可以在宿主机中ping成功虚拟IP&nbsp;&nbsp;<span class="cnblogs_code"><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.201</span></span>&nbsp;</li>
<li>可以按照以上步骤,再另外创建一个Haproxy容器,注意映射的宿主机端口不能重复,Haproxy配置一样。然后在容器中安装Keepalived,配置也基本一样(可以修改优先权重)。这样便基本实现了Haproxy双机热备方案<br>命令如下:<br>
<p>创建Haproxy容器(name=h2的原因是为了高可用)</p>
<div class="cnblogs_code">
<pre># 这里要加 --<span style="color: rgba(0, 0, 0, 1)">privileged
docker run </span>-it -d -p <strong><span style="color: rgba(128, 0, 128, 1)">4003</span>:<span style="color: rgba(128, 0, 128, 1)">8888</span> -p <span style="color: rgba(128, 0, 128, 1)">4004</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span></strong> -v /home/soft/haproxy:/usr/local/etc/haproxy --name <strong>h2</strong> --net=net1 --ip <strong><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.8</span></strong> --privileged haproxy</pre>
</div>
<p>进入容器</p>
<div class="cnblogs_code">
<pre>docker exec -it h2 bash</pre>
</div>
<p>在容器bash中启动Haproxy</p>
<div class="cnblogs_code">
<pre>haproxy -f /usr/local/etc/haproxy/haproxy.cfg</pre>
</div>
<p>接下来便可以在浏览器中打开Haproxy监控界面,端口4003,在配置文件中定义有用户名admin,密码abc123456。<br>我这边访问的是http://192.168.63.144:4003<strong><strong>/dbs,并且要使用用户名密码进行登录(小插曲,使用的是Basic登录,我的Chrome不知为何被屏蔽了,我最后用的火狐)</strong></strong></p>





安装Keepalived:<br>
<div class="cnblogs_code">
<pre>apt-<span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)"> update
apt</span>-<span style="color: rgba(0, 0, 255, 1)">get</span> install keepalived</pre>
</div>
<p>Keepalived配置文件(Keepalived.conf):<br>Keepalived的配置文件是/etc/keepalived/keepalived.conf</p>
<div class="cnblogs_code">
<pre># vim /etc/keepalived/<span style="color: rgba(0, 0, 0, 1)">keepalived.conf
vrrp_instanceVI_1 {
    stateMASTER# Keepalived的身份(MASTER主服务要抢占IP,BACKUP备服务器不会抢占IP)。
    </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)">eth0    # docker网卡设备,虚拟IP所在
    virtual_router_id</span><span style="color: rgba(128, 0, 128, 1)">51</span># 虚拟路由标识,MASTER和BACKUP的虚拟路由标识必须一致。从0~<span style="color: rgba(128, 0, 128, 1)">255</span><span style="color: rgba(0, 0, 0, 1)">
    priority</span><span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)"># MASTER权重要高于BACKUP数字越大优先级越高
    advert_int</span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"># MASTER和BACKUP节点同步检查的时间间隔,单位为秒,主备之间必须一致
    authentication {# 主从服务器验证方式。主备必须使用相同的密码才能正常通信
      auth_typePASS
      auth_pass</span><span style="color: rgba(128, 0, 128, 1)">123456</span><span style="color: rgba(0, 0, 0, 1)">
    }
    virtual_ipaddress {# 虚拟IP。可以设置多个虚拟IP地址,每行一个
      </span><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.201</span><span style="color: rgba(0, 0, 0, 1)">
    }
}</span></pre>
</div>
<p>启动Keepalived</p>
<div class="cnblogs_code">
<pre>service keepalived start</pre>
</div>
<p>启动成功后,通过&nbsp;<span class="cnblogs_code">ip a</span>&nbsp;可以查看网卡中虚拟IP是否成功,另外可以在宿主机中ping成功虚拟IP&nbsp;<span class="cnblogs_code"><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.201</span></span>&nbsp;</p>
</li>
</ol>
<h2>实现外网访问虚拟IP</h2>
<p>查看当前局域网IP分配情况:</p>
<div class="cnblogs_code">
<pre>yum install nmap -<span style="color: rgba(0, 0, 0, 1)">y
nmap </span>-sP <span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">1.0</span>/<span style="color: rgba(128, 0, 128, 1)">24</span></pre>
</div>
<ol>
<li>在宿主机中安装Keepalived<br>
<div class="cnblogs_code">
<pre>yum install keepalived</pre>
</div>
</li>
<li>宿主机Keepalived配置如下(/etc/keepalived/keepalived.conf):<br>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">vrrp_instance VI_1 {
    state MASTER
#这里是宿主机的网卡,可以通过ip a查看当前自己电脑上用的网卡名是哪个
    </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"><strong> ens33</strong>
    virtual_router_id </span><span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">
    priority </span><span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">
    advert_int </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
    authentication {
      auth_type PASS
      auth_pass </span><span style="color: rgba(128, 0, 128, 1)">1111</span><span style="color: rgba(0, 0, 0, 1)">
    }
    virtual_ipaddress {
#这里是指定的一个宿主机上的虚拟ip,一定要和宿主机网卡在同一个网段,
#我的宿主机网卡ip是192.</span><span style="color: rgba(128, 0, 128, 1)">168.63</span>.<span style="color: rgba(128, 0, 128, 1)">144</span><span style="color: rgba(0, 0, 0, 1)">,所以指定虚拟ip是160
         </span><strong><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">63.160</span></strong><span style="color: rgba(0, 0, 0, 1)">
    }
}

#接受监听数据来源的端口,网页入口使用
virtual_server </span><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">63.160</span> <span style="color: rgba(128, 0, 128, 1)">8888</span><span style="color: rgba(0, 0, 0, 1)"> {
    delay_loop </span><span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">
    lb_algo rr
    lb_kind NAT
    persistence_timeout </span><span style="color: rgba(128, 0, 128, 1)">50</span><span style="color: rgba(0, 0, 0, 1)">
    protocol TCP
#把接受到的数据转发给docker服务的网段及端口,由于是发给docker服务,所以和docker服务数据要一致
    real_server </span><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.201</span> <span style="color: rgba(128, 0, 128, 1)">8888</span><span style="color: rgba(0, 0, 0, 1)"> {
      weight </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
    }
}

#接受数据库数据端口,宿主机数据库端口是3306,所以这里也要和宿主机数据接受端口一致
virtual_server </span><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">63.160</span> <span style="color: rgba(128, 0, 128, 1)">3306</span><span style="color: rgba(0, 0, 0, 1)"> {
    delay_loop </span><span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">
    lb_algo rr
    lb_kind NAT
    persistence_timeout </span><span style="color: rgba(128, 0, 128, 1)">50</span><span style="color: rgba(0, 0, 0, 1)">
    protocol TCP
#同理转发数据库给服务的端口和ip要求和docker服务中的数据一致
    real_server </span><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.201</span> <span style="color: rgba(128, 0, 128, 1)">3306</span><span style="color: rgba(0, 0, 0, 1)"> {
      weight </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
    }
}</span></pre>
</div>
</li>
<li>启动Keepalived服务<br>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">service keepalived start
#service keepalived status
#service keepalived stop</span></pre>
</div>
之后其他电脑便可以通过虚拟IP&nbsp;<span class="cnblogs_code"><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">63.160</span></span>&nbsp;的8888和3306端口来访问宿主机Docker中的&nbsp;<span class="cnblogs_code"><span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.201</span></span>&nbsp;的相应端口。</li>
</ol>
<h2>暂停PXC集群的办法</h2>
<div class="cnblogs_code">
<pre>vi /etc/<span style="color: rgba(0, 0, 0, 1)">sysctl.conf
#文件中添加net.ipv4.ip_forward</span>=<span style="color: rgba(0, 0, 0, 1)">1这个配置
systemctl restart network</span></pre>
</div>
<p>然后把虚拟机挂起</p>
<h2>热备份数据</h2>
<h3>冷备份</h3>
<ul>
<li>冷备份是关闭数据库时候的备份方式,通常做法是拷贝数据文件</li>
<li>是简单安全的一种备份方式,不能在数据库运行时备份。</li>
<li>大型网站无法做到关闭业务备份数据,所以冷备份不是最佳选择</li>
</ul>
<h3>热备份</h3>
<p>热备份是在系统运行状态下备份数据<br>MySQL常见的热备份有LVM和XtraBackup两种方案</p>
<ul>
<li>LVM:linux的分区备份命令,可以备份任何数据库;但是会对数据库加锁,只能读取;而且命令复杂</li>
<li>XtraBackup:不需要锁表,而且免费</li>




</ul>
<h3>XtraBackup</h3>
<p>XtraBackup是一款基于InnoDB的在线热备工具,具有开源免费,支持在线热备,占用磁盘空间小,能够非常快速地备份与恢复mysql数据库</p>
<ul>
<li>备份过程中不锁表,快速可靠</li>
<li>备份过程中不会打断正在执行地事务</li>
<li>备份数据经过压缩,占用磁盘空间小</li>


</ul>
<h3>全量备份和增量备份</h3>
<ul>
<li>全量备份:备份全部数据。备份过程时间长,占用空间大。第一次备份要使用全量备份</li>
<li>增量备份: 只备份变化的那部分数据。备份的时间短,占用空间小。第二次以后使用增量备份</li>

</ul>
<h2>PXC全量备份</h2>
<ol>
<li>备份要在某个PXC节点的容器内进行,但应该把备份数据保存到宿主机内。所以采用目录映射技术。先新建Docker卷:<br>
<div class="cnblogs_code">
<pre>docker volume create backup</pre>
</div>
</li>
<li>
<p>挑选一个PXC节点node1,将其容器停止并删除,然后重新创建一个增加了backup目录映射的node1容器</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">docker stop node1
docker rm node1    # 数据库数据保存在Docker卷v1中,不会丢失
# 参数改变:
# </span><span style="color: rgba(128, 0, 128, 1)">1</span>. -e CLUSTER_JOIN=<span style="color: rgba(0, 0, 0, 1)">node2;原来其他节点是通过node1加入集群的,现在node1重新创建,需要选择一个其他节点加入集群
# </span><span style="color: rgba(128, 0, 128, 1)">2</span>. -v backup:/data;将Docker卷backup映射到容器的/<span style="color: rgba(0, 0, 0, 1)">data目录
docker run </span>-d -u root -p <span style="color: rgba(128, 0, 128, 1)">3306</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span> -e MYSQL_ROOT_PASSWORD=abc123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=abc123456 <strong><span style="color: rgba(255, 0, 0, 1)">-e CLUSTER_JOIN=node2</span></strong> -v v1:/<span style="color: rgba(0, 0, 255, 1)">var</span>/lib/mysql <span style="color: rgba(255, 0, 0, 1)"><strong>-v backup:/data</strong></span> --network=net1 --ip <span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.2</span> --name=node1 pxc</pre>
</div>
</li>
<li>
<p>在node1容器中安装&nbsp;<code>percona-xtrabackup-24<br></code></p>
<div class="cnblogs_code">
<pre>docker exec -<span style="color: rgba(0, 0, 0, 1)">it node1 bash
apt</span>-<span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)"> update
apt</span>-<span style="color: rgba(0, 0, 255, 1)">get</span> install percona-xtrabackup-<span style="color: rgba(128, 0, 128, 1)">24</span></pre>
</div>
<p>之后便可以执行如下命令进行全量备份,备份后的数据会保存在&nbsp;<code>/data/backup/full</code>&nbsp;目录下:</p>
<div class="cnblogs_code">
<pre>mkdir /data/<span style="color: rgba(0, 0, 0, 1)">backup
mkdir </span>/data/backup/<span style="color: rgba(0, 0, 0, 1)">full
#不建议,已过时 innobackupex </span>--backup -u root -p abc123456 --target-dir=/data/backup/<span style="color: rgba(0, 0, 0, 1)">full
xtrabackup </span>--backup -uroot -pabc123456 --target-dir=/data/backup/full</pre>
</div>
<p>官方文档已经不推荐使用&nbsp;<code>innobackupex</code>,而推荐使用&nbsp;<code>xtrabackup</code>&nbsp;命令</p>
</li>
</ol>
<h3>PXC全量还原</h3>
<p>数据库可以热备份,但是不能热还原,否则会造成业务数据和还原数据的冲突。</p>
<p>对于PXC集群为了避免还原过程中各节点数据同步冲突的问题,我们要先解散原来的集群,删除节点。然后新建节点空白数据库,执行还原,最后再建立起其他集群节点。</p>
<p>还原前还要将热备份保存的未提交的事务回滚,还原之后重启MySQL</p>
<ol>
<li>停止并删除PXC集群所有节点<br>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">docker stop node1 node2 node3 node4 node5
docker rm node1 node2 node3 node4 node5
docker volume rm v1 v2 v3 v4 v5</span></pre>
</div>
</li>
<li>
<p>按照之前的步骤重新创建node1容器,并进入容器,执行冷还原</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 创建卷
docker volume create v1
# 创建容器
docker run </span>-d -p <span style="color: rgba(128, 0, 128, 1)">3306</span>:<span style="color: rgba(128, 0, 128, 1)">3306</span> -e MYSQL_ROOT_PASSWORD=abc123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=abc123456 -v v1:/<span style="color: rgba(0, 0, 255, 1)">var</span>/lib/mysql -v backup:/data --name=node1 --network=net1 --ip <span style="color: rgba(128, 0, 128, 1)">172.18</span>.<span style="color: rgba(128, 0, 128, 1)">0.2</span><span style="color: rgba(0, 0, 0, 1)"> pxc
# 以root身份进入容器
docker exec </span>-it -<span style="color: rgba(0, 0, 0, 1)">uroot node1 bash
# 删除数据
rm </span>-rf /<span style="color: rgba(0, 0, 255, 1)">var</span>/lib/mysql<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
# 准备阶段
xtrabackup --prepare --target-dir=/data/backup/full/
# 执行冷还原
xtrabackup --copy-back --target-dir=/data/backup/full/
# 更改还原后的数据库文件属主
chown -R mysql:mysql /var/lib/mysql
# 退出容器后,重启容器
docker stop node1
docker start node1</span></pre>
</div>
<p>&nbsp;</p>
</li>
</ol>

</div>
<div id="MySignature" role="contentinfo">
    <div>作者:一个大西瓜</div>
<div>出处:https://www.cnblogs.com/wyt007/
</div>
<p>-------------------------------------------</p>
<p>个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!</p>
<p>如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个<span>“推荐”</span>哦,博主在此感谢!</p>
<p></p>
<p>万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!</p>
<div align="left">
        <img src="https://www.cnblogs.com/images/cnblogs_com/wyt007/1375409/t_alipay.bmp">
        <img src="https://www.cnblogs.com/images/cnblogs_com/wyt007/1375409/t_wxpay.bmp">
</div><br><br>
来源:https://www.cnblogs.com/wyt007/p/10828879.html
頁: [1]
查看完整版本: Docker环境下的前后端分离项目部署与运维(六)搭建MySQL集群