zoukankan      html  css  js  c++  java
  • 基于docker+etcd+confd + haproxy构建高可用、自发现的web服务

     

    基于docker+etcd+confd + haproxy构建高可用、自发现的web服务

    各个工具介绍

    (1)docker:Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux机器上,也可以实现虚拟化,docker是集群世界中的“进程”。通过docker我们可以非常方便的管理服务以及服务之间的依赖。

    (2)etcd:是一个高可用的集中配置管理和协作平台,功能和zookeeper很相似,两者的优劣势比较可以参考网上相关文档。分布式系统相关的配置信息可以集中的存储在etcd中统一管理。

    (3)confd:一个轻量级的配置管理工具,后端支持etcd、consul、vault、environment variables、redis、zookeeper等,当后端的数据发生变化时,confd会根据模板重新生成服务的配置文件,并reload。

    (4)haproxy:提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机。

    原理

    通过docker启动nginx服务,并且把docker实例的相关信息注册到etcd中,confd监控到etcd的数据发生变化,重新生成haproxy的配置文件,并且reload haproxy。

    当前端服务的负载升高时,可以发起新的docker实例,confd会把该实例添加到haproxy中。当负载降低时,可以kill 一些docker实例,confd会从haproxy中移除该实例。由于confd需要修改haproxy的配置文件,因此一般confd和haproxy运行在同一台服务器上。

    本文的所有服务都运行在一台ubuntu主机上。

     

    实施步骤:

    (1)安装docker

    curl -fsSL https://get.docker.com/gpg | sudo apt-key add -

    curl -fsSL https://get.docker.com/ | sh

    可以运行一个helloworld实例来测试docker是否安装成功

    docker run hello-world #docker首先会下载hello-world镜像到本地,然后运行。

    我们运行一个nginx实例,并测试nginx是否工作

    docker run -d -P nginx #-P让docker将nginx的80、443端口映射到主机的一个随机端口上,-d以detatch模式运行

    以上命令会返回创建的docker实例的id

    根据返回的docker id,可以查询该实例的详细信息,包括具体映射的端口。

    docker inspect cf04729a1b16232f72cab0bb89396b0acf20f36a9a38a2fa1d36f55c4baffb00|grep "Ports" -A 20

    可以看到如下类似的输出:

    "Ports": {

                "443/tcp": [

                    {

                        "HostIp": "0.0.0.0",

                        "HostPort": "32768"

                    }

                ],

                "80/tcp": [

                    {

                        "HostIp": "0.0.0.0",

                        "HostPort": "32769"

                    }

                ]

            }

    启用docker远程访问,

    编辑/etc/default/docker, 设置DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:5555"

    (2)安装etcd

    curl -L  https://github.com/coreos/etcd/releases/download/v3.0.0-beta.0/etcd-v3.0.0-beta.0-linux-amd64.tar.gz -o etcd-v3.0.0-beta.0-linux-amd64.tar.gz

    tar xzvf etcd-v3.0.0-beta.0-linux-amd64.tar.gz

    cd etcd-v3.0.0-beta.0-linux-amd64

    运行etcd

    ./etcd #etcd支持分布式集群的搭建,这里简单的采用单机版运行。关于集群的搭建,可以参考官网相关文档或者网上文章。

    etcd提供了一个工具“etcdctl”来同etcd交互,

    例如:

    ./etcdctl set mykey "this is awesome" #设置mykey的内容为“this is awesome”

    ./etcdctl get mykey  #获取mykey对应的值

    更多操作参考 ./etcdctl -h 

    (3)安装confd

    confd基于Go语言开发,因此首先安装go语言。

    confd的源码托管在github上,地址为:https://github.com/kelseyhightower/confd

    安装参见github上的文档

    由于haproxy的配置文件由confd生成,我们配置一下confd。

    创建相关目录

    mkdir -p /etc/confd/{conf.d,templates} 

    编辑资源文件 /etc/confd/conf.d/ haproxy.toml,设置内容为:

    [template]  

    src = "haproxy.cfg.tmpl"  

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

    keys = [  

      "/app/servers",  

    ]  

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

    配置模板文件/etc/confd/templates/haproxy.cfg.tmpl

    global

    defaults

            timeout client          30s

            timeout server          30s

            timeout connect         30s

    frontend MyFrontend

            mode http

            bind    *:80

            default_backend         TransparentBack_http

            stats enable

            # CHANGE: Your stats credentials

            # stats auth admin:admin

            stats uri /haproxy_stats

    backend TransparentBack_http

            mode                    http

            {{range gets "/app/servers/*"}} #获取所有以/app/servers/开头的key,

            server {{base .Key}} {{.Value}} check inter 5000 fall 1 rise 2 #base获取路径的最后一段

            {{end}}

            stats enable

            stats uri /admin-status

            stats auth admin:123456

            stats admin if TRUE

    运行confd

    confd -backend etcd -interval 10 -node http://127.0.0.1:4001

    查看/etc/haproxy/haproxy.conf,发现confd已经根据模板和etcd中的数据生成了一份haproxy的配置文件。

    (4)安装haproxy

    apt-get install haproxy

    haproxy默认通过init.d脚本无法启动,编辑/etc/default/haproxy,将ENABLED设置为1

    启动haproxy

    /etc/init.d/haproxy start

    重新加载配置

    /etc/init.d/haproxy reload

    (5)发起docker实例的脚本

    安装docker的python api

    pip install --no-use-wheel docker-py

    安装etcd的python lib

    pip install --no-use-wheel python-etcd

    [python] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. #!/usr/local/Python/bin/python    
    2. # coding:utf8  
    3. import docker    
    4. import etcd    
    5. import sys    
    6.     
    7.   
    8.   
    9. Server_ip="192.168.252.138"    
    10. Image="nginx"    
    11. idict={}    
    12. rinfo={}    
    13. Etcd_ip="127.0.0.1"    
    14. Port=""    
    15. Name=""    
    16. try:    
    17.     c = docker.Client(base_url='tcp://'+Server_ip+':5555', version='1.18')    
    18. except Exception,e:    
    19.     print "Connection docker server error:"+str(e)    
    20.     sys.exit()    
    21.   
    22. try:    
    23.     rinfo=c.create_container(image=Image,volumes=['/usr/share/nginx/html'],ports=[80])    
    24.     containerId=rinfo['Id']    
    25. except Exception,e:    
    26.     print "Create docker container error:"+str(e)    
    27.     sys.exit()    
    28.   
    29. try:    
    30.     #/usr/share/nginx/html为nginx的文档根目录,这里将其映射到host的/root/htmldoc目录  
    31.     c.start(container=containerId, binds={'/root/htmldoc':{'bind': '/usr/share/nginx/html'}}, port_bindings={80:None}, publish_all_ports=True)    
    32. except Exception,e:    
    33.     print "Start docker container error:"+str(e)    
    34.     sys.exit()    
    35.   
    36.   
    37. try:    
    38.     idict=c.inspect_container(containerId)    
    39.     Name=idict["Name"][1:]    
    40.     skey='80/tcp'    
    41.     for _key in idict["NetworkSettings"]["Ports"].keys():    
    42.         if _key==skey:    
    43.             Port=idict["NetworkSettings"]["Ports"][skey][0]["HostPort"]    
    44. except Exception,e:    
    45.     print "Get docker container inspect error:"+str(e)    
    46.     sys.exit()    
    47.   
    48. #更新docker实例的相关信息到etcd中  
    49. if Name != "" and Port != "":    
    50.     try:    
    51.         client = etcd.Client(host=Etcd_ip, port=4001)    
    52.         client.write('/app/servers/'+Name, Server_ip+":"+str(Port))    
    53.         print Name+" container run success!"    
    54.     except Exception,e:    
    55.         print "set etcd key error:"+str(e)    
    56. else:    
    57.     print "Get container name or port error."    

    (6)杀死docker实例的脚本

    [python] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. #!/usr/local/Python/bin/python    
    2. #coding:utf8  
    3. import docker    
    4. import etcd    
    5. import sys    
    6.     
    7. Etcd_ip="127.0.0.1"    
    8. Server_ip="127.0.0.1"    
    9.   
    10.   
    11. if len(sys.argv) != 2:  
    12.     print "usage: delete_docker_instance.py containername"  
    13.     exit(0)  
    14.     
    15. try:    
    16.     c = docker.Client(base_url='tcp://'+Server_ip+':5555',version='1.18',timeout=10)    
    17.     c.stop(sys.argv[1])    
    18. except Exception,e:    
    19.     print str(e)    
    20.     sys.exit()    
    21.     
    22. try:    
    23.     client = etcd.Client(host=Etcd_ip, port=4001)    
    24.     client.delete('/app/servers/'+sys.argv[1])    
    25.     print sys.argv[1]+" container stop success!"    
    26. except Exception,e:    
    27.     print str(e)    

  • 相关阅读:
    PHPMailer邮件发送
    php 统计在线人数,今日访问量,总访问量
    php 首页定时生成静态页面
    jquery自动生成二维码
    php图片等比例缩放
    判断checkbox选中
    Pojo和JavaBean的区别
    Highcharts简单入门
    [转载]了解Activity工作流
    集群和分布式初探
  • 原文地址:https://www.cnblogs.com/fengjian2016/p/5824485.html
Copyright © 2011-2022 走看看