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

操作系统:Centos 6.5

记得先要安装docker,具体这里不赘述了,百度或者看我的文章http://www.jianshu.com/p/3ffa4e5cbb86

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

Haproxy+etcd+confd+Docker

下面细说

1.Haproxy

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

yum -y install haproxy

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

2.etcd

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

wget https://github.com/coreos/etcd/releases/download/v3.0.3/etcd-v3.0.3-linux-amd64.tar.gz
tar xvf etcd-v3.0.3-linux-amd64.tar.gz 
[root@201 data]# cd etcd-v3.0.3-linux-amd64/
[root@201 etcd-v3.0.3-linux-amd64]# cp etcd* /bin/
[root@201 etcd-v3.0.3-linux-amd64]# etcd -version
etcd Version: 3.0.3
Git SHA: 24a90ba
Go Version: go1.6.2
Go OS/Arch: linux/amd64

启动etcd

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

查看其监听的端口
[root@201 data]# netstat -npl|grep etcd
tcp        0      0 127.0.0.1:2379              0.0.0.0:*                   LISTEN      4750/etcd           
tcp        0      0 127.0.0.1:2380              0.0.0.0:*                   LISTEN      4750/etcd 

设置测试key value
[root@201 data]# curl -L http://192.168.1.204:2379/v2/keys/key1 -XPUT -d value="Hello world"             
{"action":"set","node":{"key":"/key1","value":"Hello world","modifiedIndex":5,"createdIndex":5}}

查询测试key
[root@201 data]# curl -L http://192.168.1.204:2379/v2/keys/key1
{"action":"get","node":{"key":"/key1","value":"Hello world","modifiedIndex":5,"createdIndex":5}}

删除key
curl -XDELETE http://192.168.1.204:2379/v2/keys/app/servers

另外还可以用etcdctl命令来操作,创建一个新的目录和键分别使用 etcdctl mkdir 和 etcdctl mk 命令。
[root@201 data]# etcdctl mkdir /demo
[root@201 data]# etcdctl get /demo
/demo: is a directory
[root@201 data]# etcdctl mk /demo/hello "Hello Etcd"  # 实际情况中这里会回显输出“Hello Etcd”,省略
[root@201 data]# etcdctl get /demo/hello
Hello Etcd

3.confd

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

wget https://github.com/kelseyhightower/confd/releases/download/v0.11.0/confd-0.11.0-linux-amd64
[root@201 data]# mv confd-0.11.0-linux-amd64 /usr/local/bin/confd
[root@201 data]# chmod +x /usr/local/bin/confd 
[root@201 data]# confd -version
confd 0.11.0

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

[root@201 data]# mkdir -p /etc/confd/templates
vim /etc/confd/conf.d/haproxy.toml
[template]
#模板文件,基于它进行修改
src = "haproxy.cfg.tmpl"
#haproxy的默认配置路径
dest = "/etc/haproxy/haproxy.cfg"
#keys是在etcd上订阅消息的前缀
keys = [
  "/app/servers",
]
#更新配置后重启haproxy
reload_cmd = "/etc/init.d/haproxy reload"

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

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

global
        log 127.0.0.1 local3
        maxconn 5000
        uid 99
        gid 99
        daemon
  
defaults
        log 127.0.0.1 local3
        mode http
        option dontlognull
        retries 3
        option redispatch
        maxconn 2000
        timeout connect 5000
        timeout client  50000
        timeout server  50000

frontend myhttp
        mode http
        bind 192.168.1.204:80
        use_backend myserver
backend myserver
        mode http
        balance roundrobin
        #confd的语法会替换下面的变量
        {{range gets "/app/servers/*"}}
        server {{base .Key}} {{.Value}} weight 10
        {{end}}

启动confd

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

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

2016-07-23T21:45:39+08:00 204.localdomain confd[3317]: INFO /etc/haproxy/haproxy.cfg has md5sum 4b3b5d42ffd945117fe74d5d234ae49b should be e81aa47326241d0d1530c9d4b13e1e39
2016-07-23T21:45:39+08:00 204.localdomain confd[3317]: INFO Target config /etc/haproxy/haproxy.cfg out of sync
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

 --dns 172.17.42.1

#!/bin/bash

if [ -z $1 ]; then
        echo "Usage: c start <image name>:<version>"
        echo "       c stop <container name>"
        exit 1
fi

# etcd地址我先写死了,最好改成用脚本获取
if [ -z $ETCD_HOST ]; then
  ETCD_HOST="192.168.1.204:2379"
fi

if [ -z $ETCD_PREFIX ]; then
  ETCD_PREFIX="app/servers"
fi

if [ -z $CPORT ]; then
  CPORT="80"
fi

# 网卡是eth?要写清楚
if [ -z $FORREST_IP ]; then
  FORREST_IP=`ifconfig eth1| grep "inet addr" | head -1 | cut -d : -f2 | awk '{print $1}'`
fi

# 启动docker
function launch_container {
        echo "Launching $1 on $FORREST_IP and mapped port $CPORT to ..."

        CONTAINER_ID=`docker run -d -P $1 python app.py`
        #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"`
        PORT=`docker inspect $CONTAINER_ID|grep "\"Ports\"" -A 50|grep "\"$CPORT/tcp\"" -A 3| grep HostPort|cut -d '"' -f4|head -1`
        NAME=`docker inspect $CONTAINER_ID | grep Name | cut -d '"' -f4 | sed "s/\///g"|sed -n 1p`
  
        echo "Announcing to $ETCD_HOST..."
        # 发布到etcd上去,触发confd刷新haproxy的配置,以注册新节点
        args="http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$NAME -d value=$FORREST_IP:$PORT"
        #echo "curl -XPUT "$args
        curl -XPUT $args
  
        echo "$1 running on Port $PORT with name $NAME"
}

# 停止docker
function stop_container {
        echo "Stopping $1..."
        CONTAINER_ID=`docker ps -a| grep $1 | awk '{print $1}'`
        echo "Found container $CONTAINER_ID"
        docker stop $CONTAINER_ID
  echo http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$1
        curl -XDELETE http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$1 &> /dev/null
        echo "Stopped."
}

if [ $1 = "start" ]; then
  launch_container $2
else
  stop_container $2
fi

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

5.测试

启动两个后端镜像试试

启动第一个服务
[root@204 data]# ./docker.sh run tutum/lamp
Launching tutum/lamp on 192.168.1.204 and mapped port 80 to ...
Announcing to 192.168.1.204:2379...
{"action":"set","node":{"key":"/app/servers/boring_goldstine","value":"192.168.1.204:32769","modifiedIndex":4,"createdIndex":4}}
tutum/lamp running on Port 32769 with name boring_goldstine

启动第二个服务
[root@204 data]# ./docker.sh run tutum/lamp
Launching tutum/lamp on 192.168.1.204 and mapped port 80 to ...
Announcing to 192.168.1.204:2379...
{"action":"set","node":{"key":"/app/servers/fervent_curie","value":"192.168.1.204:32771","modifiedIndex":5,"createdIndex":5}}
tutum/lamp running on Port 32771 with name fervent_curie

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

2016-07-25T16:37:59+08:00 204.localdomain confd[1300]: INFO /etc/haproxy/haproxy.cfg has md5sum b363dce20b5df8e4f82a327532623cd8 should be 2ca9875a74f76e9aa3701c3675d768ed
2016-07-25T16:37:59+08:00 204.localdomain confd[1300]: INFO Target config /etc/haproxy/haproxy.cfg out of sync
2016-07-25T16:37:59+08:00 204.localdomain confd[1300]: INFO Target config /etc/haproxy/haproxy.cfg has been updated

查询下etcd中的配置

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

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

[root@204 data]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                            NAMES
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       
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

7bedfc46554b

或者Linux 965fceb923a0 2.6.32-431
965fceb923a0

,机器名称时不时的变化,说明后端访问分发到不同的服务器上去了。

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中的配置信息不会删除掉,如下
[root@204 data]# ./docker.sh stop thirsty_lovelace
Stopping thirsty_lovelace...
Found container 08e68416006a
08e68416006a
http://192.168.1.204:2379/v2/keys/app/servers/thirsty_lovelace
Stopped.

创建于 2016-07-25 杭州,更新于 2016-07-25 杭州

该文章在以下平台同步

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容