您的位置:首页 > 运维架构 > Docker

Haproxy+etcd+confd+Docker搭建节点自动发现的高可用负载均衡框架

2019-05-08 09:26 441 查看

操作系统:Centos 7.0

先扔出一张图来解释这四个组件之间的关系

下面细说

1.Haproxy

Haproxy不用多说,负载均衡软件,安装Haproxy

[code]yum -y install haproxy
  •  

版本是haproxy-1.5.4-3.el6.x86_64.rpm

2.etcd

etcd,是一个高可用的 Key/Value 的内存数据库,提供 发布/订阅 模式的操作,每当有新的后端节点加入的时候,我们都会用脚本操作到etcd上去发布新节点信息,而已经订阅该消息的confd客户端就会收到新节点的发布信息,它会修改配置文件且分发去覆盖本机的Haproxy的配置,并且重启Haproxy,以达到给Haproxy新增后端分发节点的目的,安装etcd

[code] 
  1. wget https://github.com/coreos/etcd/releases/download/v3.0.3/etcd-v3.0.3-linux-amd64.tar.gz

  2. tar xvf etcd-v3.0.3-linux-amd64.tar.gz

  3. [root@201 data]# cd etcd-v3.0.3-linux-amd64/

  4. [root@201 etcd-v3.0.3-linux-amd64]# cp etcd* /bin/

  5. [root@201 etcd-v3.0.3-linux-amd64]# etcd -version

  6. etcd Version: 3.0.3

  7. Git SHA: 24a90ba

  8. Go Version: go1.6.2

  9. Go OS/Arch: linux/amd64

启动etcd

[code] 
  1. [root@201 data]# etcd --listen-peer-urls 'http://192.168.1.204:2380' --advertise-client-urls 'http://192.168.1.204:2379' --listen-client-urls 'http://192.168.1.204:2379' -data-dir /data/default.etcd -name etcd1 &

  2.  
  3. 查看其监听的端口

  4. [root@201 data]# netstat -npl|grep etcd

  5. tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 4750/etcd

  6. tcp 0 0 127.0.0.1:2380 0.0.0.0:* LISTEN 4750/etcd

  7.  
  8. 设置测试key value

  9. [root@201 data]# curl -L http://192.168.1.204:2379/v2/keys/key1 -XPUT -d value="Hello world"

  10. {"action":"set","node":{"key":"/key1","value":"Hello world","modifiedIndex":5,"createdIndex":5}}

  11.  
  12. 查询测试key

  13. [root@201 data]# curl -L http://192.168.1.204:2379/v2/keys/key1

  14. {"action":"get","node":{"key":"/key1","value":"Hello world","modifiedIndex":5,"createdIndex":5}}

  15.  
  16. 删除key

  17. curl -XDELETE http://192.168.1.204:2379/v2/keys/app/servers

  18.  
  19. 另外还可以用etcdctl命令来操作,创建一个新的目录和键分别使用 etcdctl mkdir 和 etcdctl mk 命令。

  20. [root@201 data]# etcdctl mkdir /demo

  21. [root@201 data]# etcdctl get /demo

  22. /demo: is a directory

  23. [root@201 data]# etcdctl mk /demo/hello "Hello Etcd" # 实际情况中这里会回显输出“Hello Etcd”,省略

  24. [root@201 data]# etcdctl get /demo/hello

  25. Hello Etcd

3.confd

上面说过了,confd是分发新的配置到Haproxy中,安装confd

[code] 
  1. wget https://github.com/kelseyhightower/confd/releases/download/v0.11.0/confd-0.11.0-linux-amd64

  2. [root@201 data]# mv confd-0.11.0-linux-amd64 /usr/local/bin/confd

  3. [root@201 data]# chmod +x /usr/local/bin/confd

  4. [root@201 data]# confd -version

  5. confd 0.11.0

confd要与haproxy安装在同一台主机上,以便能生成给Haproxy用的配置。创建confd的默认配置存放路径,confd会根据该文件找到要修改的haproxy文件路径

[code] 
  1. [root@201 data]# mkdir -p /etc/confd/templates

  2. vim /etc/confd/conf.d/haproxy.toml

  3. [template]

  4. #模板文件,基于它进行修改

  5. src = "haproxy.cfg.tmpl"

  6. #haproxy的默认配置路径

  7. dest = "/etc/haproxy/haproxy.cfg"

  8. #keys是在etcd上订阅消息的前缀

  9. keys = [

  10. "/app/servers",

  11. ]

  12. #更新配置后重启haproxy

  13. reload_cmd = "/etc/init.d/haproxy reload"

创建haproxy.cfg的模板文件,以便confd能根据模板生成配置,语法格式是基于Go语言的语法

vim /etc/confd/templates/haproxy.cfg.tmpl

[code] 
  1. global

  2. log 127.0.0.1 local3

  3. maxconn 5000

  4. uid 99

  5. gid 99

  6. daemon

  7.  
  8. defaults

  9. log 127.0.0.1 local3

  10. mode http

  11. option dontlognull

  12. retries 3

  13. option redispatch

  14. maxconn 2000

  15. timeout connect 5000

  16. timeout client 50000

  17. timeout server 50000

  18.  
  19. frontend myhttp

  20. mode http

  21. bind 192.168.1.204:80

  22. use_backend myserver

  23. backend myserver

  24. mode http

  25. balance roundrobin

  26. #confd的语法会替换下面的变量

  27. {{range gets "/app/servers/*"}}

  28. server {{base .Key}} {{.Value}} weight 10

  29. {{end}}

启动con 3ff7 fd

[code]confd -interval 10 -node '192.168.1.204:2379' -confdir /etc/confd > /var/log/confd.log &
  •  

这样当我们手动修改haproxy.cfg.tmpl文件以后,每隔不到10s左右的时间,confd都会发现模板文件已经更新,会重新生成配置发布,看到更新时候的日志如下

[code] 
  1. 2016-07-23T21:45:39+08:00 204.localdomain confd[3317]: INFO /etc/haproxy/haproxy.cfg has md5sum 4b3b5d42ffd945117fe74d5d234ae49b should be e81aa47326241d0d1530c9d4b13e1e39

  2. 2016-07-23T21:45:39+08:00 204.localdomain confd[3317]: INFO Target config /etc/haproxy/haproxy.cfg out of sync

  3. 2016-07-23T21:45:39+08:00 204.localdomain confd[3317]: INFO Target config /etc/haproxy/haproxy.cfg has been updated

4.docker启动脚本

我们启动docker要写一个脚本,先启动docker,得到容器id。然后更新信息到etcd上,进而出发confd发布新的配置给Haproxy,这样在Haproxy配置文件中将我们新启动的容器ip地址添加到后端列表,以达到注册新节点的目的。记住启动和停止docker都要用这个脚本,不要直接使用docker命令,因为脚本还要处理etcd的信息。

docker.sh

[code] 
  1. --dns 172.17.42.1

  2.  
  3. #!/bin/bash

  4.  
  5. if [ -z $1 ]; then

  6. echo "Usage: c start <image name>:<version>"

  7. echo " c stop <container name>"

  8. exit 1

  9. fi

  10.  
  11. # etcd地址我先写死了,最好改成用脚本获取

  12. if [ -z $ETCD_HOST ]; then

  13. ETCD_HOST="192.168.1.204:2379"

  14. fi

  15.  
  16. if [ -z $ETCD_PREFIX ]; then

  17. ETCD_PREFIX="app/servers"

  18. fi

  19.  
  20. if [ -z $CPORT ]; then

  21. CPORT="80"

  22. fi

  23.  
  24. # 网卡是eth?要写清楚

  25. if [ -z $FORREST_IP ]; then

  26. FORREST_IP=`ifconfig eth1| grep "inet addr" | head -1 | cut -d : -f2 | awk '{print $1}'`

  27. fi

  28.  
  29. # 启动docker

  30. function launch_container {

  31. echo "Launching $1 on $FORREST_IP and mapped port $CPORT to ..."

  32.  
  33. CONTAINER_ID=`docker run -d -P $1 python app.py`

  34. #CONTAINER_ID=`docker run -d -P -v /data:/data -v /etc/httpd/conf:/etc/httpd/conf -v /etc/httpd/conf.d:/etc/httpd/conf.d -v /etc/localtime:/etc/localtime:ro $1 /bin/sh -c "/usr/bin/supervisord -c /etc/supervisord.conf"`

  35. PORT=`docker inspect $CONTAINER_ID|grep "\"Ports\"" -A 50|grep "\"$CPORT/tcp\"" -A 3| grep HostPort|cut -d '"' -f4|head -1`

  36. NAME=`docker inspect $CONTAINER_ID | grep Name | cut -d '"' -f4 | sed "s/\///g"|sed -n 1p`

  37.  
  38. echo "Announcing to $ETCD_HOST..."

  39. # 发布到etcd上去,触发confd刷新haproxy的配置,以注册新节点

  40. args="http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$NAME -d value=$FORREST_IP:$PORT"

  41. #echo "curl -XPUT "$args

  42. curl -XPUT $args

  43.  
  44. echo "$1 running on Port $PORT with name $NAME"

  45. }

  46.  
  47. # 停止docker

  48. function stop_container {

  49. echo "Stopping $1..."

  50. CONTAINER_ID=`docker ps -a| grep $1 | awk '{print $1}'`

  51. echo "Found container $CONTAINER_ID"

  52. docker stop $CONTAINER_ID

  53. echo http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$1

  54. curl -XDELETE http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$1 &> /dev/null

  55. echo "Stopped."

  56. }

  57.  
  58. if [ $1 = "start" ]; then

  59. launch_container $2

  60. else

  61. stop_container $2

  62. fi

一开始绑定eth0,发现一直失败,原来网卡绑定错了,后来改成eth1了。

5.测试

启动两个后端镜像试试

[code] 
  1. 启动第一个服务

  2. [root@204 data]# ./docker.sh run tutum/lamp

  3. Launching tutum/lamp on 192.168.1.204 and mapped port 80 to ...

  4. Announcing to 192.168.1.204:2379...

  5. {"action":"set","node":{"key":"/app/servers/boring_goldstine","value":"192.168.1.204:32769","modifiedIndex":4,"createdIndex":4}}

  6. tutum/lamp running on Port 32769 with name boring_goldstine

  7.  
  8. 启动第二个服务

  9. [root@204 data]# ./docker.sh run tutum/lamp

  10. Launching tutum/lamp on 192.168.1.204 and mapped port 80 to ...

  11. Announcing to 192.168.1.204:2379...

  12. {"action":"set","node":{"key":"/app/servers/fervent_curie","value":"192.168.1.204:32771","modifiedIndex":5,"createdIndex":5}}

  13. tutum/lamp running on Port 32771 with name fervent_curie

执行上述命令的同时,由于etcd发生了变化,confd订阅的消息会被触发更新Haproxy的配置,以下日志表示更新配置

[code] 
  1. 2016-07-25T16:37:59+08:00 204.localdomain confd[1300]: INFO /etc/haproxy/haproxy.cfg has md5sum b363dce20b5df8e4f82a327532623cd8 should be 2ca9875a74f76e9aa3701c3675d768ed

  2. 2016-07-25T16:37:59+08:00 204.localdomain confd[1300]: INFO Target config /etc/haproxy/haproxy.cfg out of sync

  3. 2016-07-25T16:37:59+08:00 204.localdomain confd[1300]: INFO Target config /etc/haproxy/haproxy.cfg has been updated

查询下etcd中的配置

[code] 
  1. [root@204 data]# etcdctl --endpoints "http://192.168.1.204:2379,http://192.168.1.204:2380" ls /app/servers

  2. /app/servers/boring_goldstine

  3. /app/servers/fervent_curie

可见现在有boring_goldstine和fervent_curie两个服务,那么docker中应该也对应这两个NAMES

[code] 
  1. [root@204 data]# docker ps

  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

  3. 965fceb923a0 tutum/lamp "/run.sh" 52 seconds ago Up 50 seconds 0.0.0.0:32771->80/tcp, 0.0.0.0:32770->3306/tcp fervent_curie

  4. 7bedfc46554b tutum/lamp "/run.sh" 54 seconds ago Up 53 seconds 0.0.0.0:32769->80/tcp, 0.0.0.0:32768->3306/tcp boring_goldstine

好,现在我们整套框架都搭建起来了,可以开始测试。

单独在浏览器去访问两个启动的server,都有响应(如果半天卡死先把防火墙关闭了,但是启动docker的时候则需要打开)http://192.168.1.204:32769/ 

同样另一台机器得到的结果也相同http://192.168.1.204:32771/ 

刚才是直接分别访问两个后端服务,那么我们从haproxy的入口处,80端口去访问下http://192.168.1.204 

测试haproxy转发到后端机器成功了。为了更直观的看到haproxy到底转发到哪台后端服务器了,我们访问http://192.168.1.204/phpinfo.php,可以在输出的信息中看到机器名称 Linux 7bedfc46554b 2.6.32-431 
 
或者Linux 965fceb923a0 2.6.32-431 
 
,机器名称时不时的变化,说明后端访问分发到不同的服务器上去了。

6.遇到问题

  1. etcd cluster is unavailable or misconfigured

操作如下命令的时候出错

[root@204 data]# etcdctl mkdir /app

Error: client: etcd cluster is unavailable or misconfigured

error #0: dial tcp 127.0.0.1:2379: getsockopt: connection refused

error #1: dial tcp 127.0.0.1:4001: getsockopt: connection refused

这是因为没有指定ip信息,加上ip就可以了,比如ls命令,这样写

etcdctl –endpoints “http://192.168.1.204:2379,http://192.168.1.204:2380” ls

  1. 停止一个容器,不要直接去操作docker,要用脚本,否则只停了容器而etcd中的配置信息不会删除掉,如下
[code] 
  1. [root@204 data]# ./docker.sh stop thirsty_lovelace

  2. Stopping thirsty_lovelace...

  3. Found container 08e68416006a

  4. 08e68416006a

  5. http://192.168.1.204:2379/v2/keys/app/servers/thirsty_lovelace

  6. Stopped.

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: