Coding 技术小馆是 Coding 主办的技术沙龙, 2018.1.20 将在深圳与大家探讨高效 Workflow 实践 , 开放报名中, 点击了解更多
适合具有 Docker 和 Bash 相关基础的开发运维等同学本文没有太过深入的介绍, 也并没有用到一些高级特性, 仅适合用来作为一个基础科普文来阅读
背景
最近在搭新的 Coding 内部测试环境, 老大说把以前使用 nginx 插件来做的 LB(Load balancing) 全部换成 HAProxy 来做经过几天的不断查阅资料, 简单实现了该服务的动态构建
本文循序渐进按照以下几个层次来讲:
HAProxy 与 Keepalived 的简单介绍
HAProxy 介绍
Keepalived 介绍
搭建 haproxy + keepalived 服务过程
搭建业务服务
使用 HAProxy 做业务服务的高可用和负载均衡
使用 Keepalived 做 HAProxy 服务的高可用
环境准备:
五台 Linux 主机
主机名 | 操作系统 | 环境 | IP |
---|---|---|---|
haproxy-master | Ubuntu 16.04.3 64bit (2G RAM) | Docker: 17.09.0-ce | 192.168.0.144 |
haproxy-backup | Ubuntu 16.04.3 64bit (2G RAM) | Docker: 17.09.0-ce | 192.168.0.145 |
host-1 | Ubuntu 16.04.3 64bit (2G RAM) | Docker: 17.09.0-ce | 192.168.0.189 |
host-2 | Ubuntu 16.04.3 64bit (2G RAM) | Docker: 17.09.0-ce | 192.168.0.190 |
host-3 | Ubuntu 16.04.3 64bit (2G RAM) | Docker: 17.09.0-ce | 192.168.0.191 |
一个虚拟的 IP
192.168.0.146(一个内网没人用的 IP)
HAProxy 与 Keepalived 简单介绍
HAProxy 是一个提供高可用负载均衡和基于 HTTP/TCP 应用代理的解决方案
Keepalived 是用 C 编写的路由软件, 主要目标是为 Linux 系统及基于 Linux 的设施提供强大的高可用性和负载均衡
在本文中, HAProxy 我们用来做 HTTP/TCP 应用的 HA + LB 方案, Keepalived 被用来做 HAProxy 的 HA 方案下边是对两者的简单介绍和简单使用
HAProxy
关于 HAProxy 的一些概念限于篇幅的原因不再详细说了, 可以参考这篇文章 An Introduction to HAProxy and Load Balancing Concepts
下边仅会介绍我们会使用到的一些概念
HAProxy 的 Proxy 配置可以分为如下几个部分(HAProxy Configuration#4):
- defaults [<name>]
- frontend<name>
- backend <name>
- listen <name>
其中 defaults 包含一些全局的默认环境变量, frontend 定义了如何把数据 forward 到 backend,backend 描述了一组接收 forward 来的数据的后端服务 listen 则把 frontend 和 backend 组合到了一起, 定义出了一个完整的 proxy
下面是一段简单的 HAProxy 的配置:
- listen app1-cluster
- bind *:4000
- mode http
- maxconn 300
- balance roundrobin
- server server1 192.168.0.189:4004 maxconn 300 check
- server server2 192.168.0.190:4004 maxconn 300 check
- server server3 192.168.0.191:4004 maxconn 300 check
- listen app2-cluster
- bind *:5000
- mode http
- maxconn 300
- balance roundrobin
- server server1 192.168.0.189:5555 maxconn 300 check
- server server2 192.168.0.190:5555 maxconn 300 check
- server server3 192.168.0.191:5555 maxconn 300 check
我们在 HAProxy 的配置文件中定义了两个 listen 模块, 分别监听在 40005000 端口监听在 4000 端口的模块, 使用 roundrobin (轮询)负载均衡算法, 把请求分发到了三个后端服务
Keepalived
关于 Keepalived 的详细介绍可以参考: Keepalived Introduction
Keepalived 是一个用于负载均衡和高可用的路由软件
其负载均衡 (Load balancing) 的特性依赖于 Linux 虚拟服务器 (LVS) 的 IPVS 内核模块, 提供了 Layer 4 负载均衡器(TCP 层级, Layer 7 是 HTTP 层级, 即计算机网络中的 OSI 七层网络模型与 TCP/IP 四层网络模型)
Keepalived 实现了虚拟冗余路由协议 (VRRP, Virtual Redundancy Routing Protoco),VRRP 是路由故障切换(failover) 的基础
简单来说, Keepalived 主要提供两种功能:
依赖 IPVS 实现服务器的健康检查;
实现 VRRPv2 协议来处理路由的故障切换
我们接下来会用个简单的配置来描述后者的工作原理:
在 haproxy-master 机器上:
- vrrp_script chk_haproxy {
- script "killall -0 haproxy" # verify haproxy's pid existance
- interval 2 # check every 2 seconds
- weight -2 # if check failed, priority will minus 2
- }
- vrrp_instance VI_1 {
- state MASTER # Start-up default state
- interface ens18 # Binding interface
- virtual_router_id 51 # VRRP VRID(0-255), for distinguish vrrp's multicast
- priority 105 # VRRP PRIO
- virtual_ipaddress { # VIP, virtual ip
- 192.168.0.146
- }
- track_script { # Scripts state we monitor
- chk_haproxy
- }
- }
在 haproxy-backup 机器上:
- vrrp_script chk_haproxy {
- script "killall -0 haproxy"
- interval 2
- weight -2
- }
- vrrp_instance VI_1 {
- state BACKUP
- interface ens18
- virtual_router_id 51
- priority 100
- virtual_ipaddress {
- 192.168.0.146
- }
- track_script {
- chk_haproxy
- }
- }
我们为两台机器 (masterbackup) 安装了 Keepalived 服务并设定了上述配置
可以发现, 我们绑定了一个虚拟 IP (VIP, virtual ip): 192.168.0.146, 在 haproxy-master + haproxy-backup 上用 Keepalived 组成了一个集群在集群初始化的时候, haproxy-master 机器的 <state> 被初始化为 MASTER
间隔 2 seconds(<interval>) 会定时执行 <script > 脚本 , 每个 < vrrp_instance> 会记录脚本的 exit code
关于 < weight> 参数的使用:
检测失败, 并且 weight 为正值: 无操作
检测失败, 并且 weight 为负值: priority = priority - abs(weight)
检测成功, 并且 weight 为正值: priority = priority + weight
检测成功, 并且 weight 为负值: 无操作
weight 默认值为 0, 对此如果感到迷惑可以参考: HAProxy github code
故障切换工作流程:
当前的 MASTER 节点 < script> 脚本检测失败后, 如果当前 MASTER 节点的 priority < 当前 BACKUP 节点的 priority 时, 会发生路由故障切换
当前的 MASTER 节点脚本检测成功, 无论 priority 是大是小, 不会做故障切换
其中有几处地方需要注意:
一个 Keepalived 服务中可以有个 0 个或者多个 vrrp_instance
可以有多个绑定同一个 VIP 的 Keepalived 服务(一主多备), 本小节中只是写了两个
注意 <virtual_router_id>, 同一组 VIP 绑定的多个 Keepalived 服务的 <virtual_router_id> 必须相同; 多组 VIP 各自绑定的 Keepalived 服务一定与另外组不相同否则前者会出现丢失节点, 后者在初始化的时候会出错
关于 Keepalived 各个参数代表含义的问题, 可以同时参考下文与 Github 代码文档来看:
keepalived 工作原理和配置说明
Github keepalived.conf.SYNOPSIS
搭建 haproxy + keepalived 服务过程
搭建业务服务
我们在 host-1host-2host-3 三台机器上, 每台机器的 40045555 端口分别启起来一个服务, 服务打印一段字符串, 用来模拟业务集群中的三个实例
如下所示:
192.168.0.189/host-1
4004 端口:
5555 端口:
192.168.0.190/host-2
4004 端口:
5555 端口:
192.168.0.191/host-3
4004 端口:
5555 端口:
如上所示, 我们在三台机器上搭建了两个集群, 结构如下图所示:
使用 HAProxy 做业务服务的高可用和负载均衡
我们现在有两台机器: haproxy-masterhaproxy-backup, 本小节的目标是在这两台机器上分别搭一套相同的 HAProxy 服务(为了方便, 下边直接用 Docker 做了)
我们直接使用了 haproxy:1.7.9 版本的 Docker 镜像, 下边是具体的步骤:
- coding@haproxy-master:~/haproxy$ tree .
- .
- Dockerfile
- haproxy.cfg
- 0 directories, 2 files
- coding@haproxy-master:~/haproxy$ cat Dockerfile
- FROM haproxy:1.7.9
- COPY haproxy.cfg /usr/local/etc/haproxy/
- coding@haproxy-master:~/haproxy$ cat haproxy.cfg
- global
- daemon
- maxconn 30000
- log 127.0.0.1 local0 info
- log 127.0.0.1 local1 warning
- defaults
- mode http
- option http-keep-alive
- option httplog
- timeout connect 5000ms
- timeout client 10000ms
- timeout server 50000ms
- timeout http-request 20000ms
- # custom your own frontends && backends && listen conf
- #CUSTOM
- listen app1-cluster
- bind *:4000
- mode http
- maxconn 300
- balance roundrobin
- server server1 192.168.0.189:4004 maxconn 300 check
- server server2 192.168.0.190:4004 maxconn 300 check
- server server3 192.168.0.191:4004 maxconn 300 check
- listen app2-cluster
- bind *:5000
- mode http
- maxconn 300
- balance roundrobin
- server server1 192.168.0.189:5555 maxconn 300 check
- server server2 192.168.0.190:5555 maxconn 300 check
- server server3 192.168.0.191:5555 maxconn 300 check
- listen stats
- bind *:1080
- stats refresh 30s
- stats uri /stats
在完成上述代码后, 我们可以构建我们自己的 Docker 镜像, 并运行它:
- coding@haproxy-master:~/haproxy$ docker build -t custom-haproxy:1.7.9 .
- Sending build context to Docker daemon 3.584kB
- Step 1/2 : FROM haproxy:1.7.9
- 1.7.9: Pulling from library/haproxy
- 85b1f47fba49: Pull complete
- 3dee1a596b5f: Pull complete
- 259dba5307a2: Pull complete
- 9d51568f5880: Pull complete
- d2c6077a1eb7: Pull complete
- Digest: sha256:07579aed81dc9592a3b7697d0ea116dea7e3dec18e29f1630bc2c399f46ada8e
- Status: Downloaded newer image for haproxy:1.7.9
- ---> 4bb854517f75
- Step 2/2 : COPY haproxy.cfg /usr/local/etc/haproxy/
- ---> 4e846d42d719
- Successfully built 4e846d42d719
- Successfully tagged custom-haproxy:1.7.9
- coding@haproxy-master:~/haproxy$ docker run -it --net=host --privileged --name haproxy-1 -d custom-haproxy:1.7.9
- fb41ab81d2140af062708e5b84668b7127014eb9ae274e4c2761d06e2f6d7950
通过命令 docker ps -a 我们可以看到, 容器已经正常运行了, 接下来我们在 web 端打开相应的地址可以看到所有服务的状态:
我们刚才在 haproxy-master 机器上搭建了一个 haproxy 服务, 在其 40005000 端口绑定了两个业务集群我们在浏览器访问: http://192.168.0.144:4000/http://192.168.0.144:5000/ , 并且重复刷新即可看到会打印不同的 JSON 出来可以证明我们请求确实是分发到了业务集群的几个实例中
接下来, 还要在 haproxy-backup 上搭建一个一模一样的 HAProxy 服务, 步骤同上
使用 Keepalived 做 HAProxy 服务的高可用
在上一小节中, 我们在 haproxy-masterhaproxy-backup 两台机器上搭了两套相同的 HAProxy 服务我们希望在一个连续的时间段, 只由一个节点为我们提供服务, 并且在这个节点挂掉后另外一个节点能顶上
本小节的目标是在 haproxy-masterhaproxy-backup 上分别搭 Keepalived 服务, 并区分主备节点, 以及关停掉一台机器后保证 HAProxy 服务仍然正常运行
使用如下命令安装 Keepalived:
- coding@haproxy-master:~$ sudo apt-get install keepalived
- coding@haproxy-master:~/haproxy$ keepalived -v
- Keepalived v1.2.19 (03/13,2017)
我们在 haproxy-master 机器上, 增加如下配置:
- coding@haproxy-master:~$ cat /etc/keepalived/keepalived.conf
- vrrp_script chk_haproxy {
- script "killall -0 haproxy"
- interval 2
- }
- vrrp_instance VI_1 {
- state MASTER
- interface ens18
- virtual_router_id 51
- priority 105
- virtual_ipaddress {
- 192.168.0.146
- }
- track_script {
- chk_haproxy
- }
- }
在 haproxy-backup 机器上增加如下配置:
- coding@haproxy-backup:~$ cat /etc/keepalived/keepalived.conf
- vrrp_script chk_haproxy {
- script "killall -0 haproxy"
- interval 2
- }
- vrrp_instance VI_1 {
- state BACKUP
- interface ens18
- virtual_router_id 51
- priority 100
- virtual_ipaddress {
- 192.168.0.146
- }
- track_script {
- chk_haproxy
- }
- }
在配置结束之后, 我们分别重启两台机器上的 Keepalived 服务:
- oot@haproxy-master:~# systemctl restart keepalived
- (注: 不同的发行版命令不同, 本文基于 Ubuntu 16.04)
此时我们的 Keepalived 服务就搭建完成了, VIP 是 192.168.0.146 此时我们通过浏览器访问: http://192.168.0.146:1080/stats, 即可看到 HAProxy 服务的状态
我们也可以通过 ssh user@192.168.0.146 看目前 MASTER 节点为哪台机器 ssh 后发现目前的 MASTER 节点为 haproxy-master 机器, 我现在把这台机器上的 haproxy 服务停掉:
- root@haproxy-master:~# docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- fb41ab81d214 custom-haproxy:1.7.9 "/docker-entrypoin..." About an hour ago Up 1 second haproxy-1
- root@haproxy-master:~# docker stop fb41ab81d214
- fb41ab81d214
然后我们访问: http://192.168.0.146:1080/stats, 会在稍微停顿下后仍然能显示出来 haproxy 服务的状态我们再 ssh user@192.168.0.146 后, 发现我们进入了 haproxy-backup 机器上此时的 MASTER 节点就是 haproxy-backup 机器
以此来看, 我们已经搭建出来了一个简单的 HAProxy + Keepalived 服务了整体结构图如下所示:
结尾
有一点需要注意, 容易混淆:
区分主节点备节点是 Keepalived 服务来做的, HAProxy 在所有节点上的配置应该是相同的
限于篇幅原因会在第二篇文章中介绍剩下的:
使用 Docker 组合动态构建 haproxy-keepalived 服务
如何使用我们构建出来的镜像
如何扩展
查看 使用 Haproxy + Keepalived 构建基于 Docker 的高可用负载均衡服务(二)
来源: https://blog.coding.net/blog/Haproxy&Keepalived