Consul + fabio enables automatic service discovery, load balancing

Summary

  • Consul introduction
  • Fabio introduction
  • The characteristics of service discovery
  • working principle
  • Demo
  • Combined with Kubernetes expansion

Consul

Hashicorp team development is the famous vagrant team.

  • Consul key to question
  • How does consul start in the background?
  • Research on High Availability Rails Cluster Based on Docker
  • Constructing Docker Service Architecture Based on Nginx and Consul for High Availability and Autodiscover
  • Consul can not find containers that are started by marathon
  • Service discovery: Zookeeper vs etcd vs Consul
  • Consul is a service software that provides service discovery, health detection, and K / V storage support for distributed, highly available multi-data centers.

    More like ZooKeeper but more than it is some of the features. Specifically you can refer to the difference between Consul and ZooKeeper .

    Fabio

    Fabio is an ebay team developed with golang a fast, simple zero configuration that allows consul deployment applications to quickly support http (s) load balancing routers.

    Because consul supports service registration with health checks so fabio can zero configure to provide load, upgrade deployments have never been easier.

    According to the introduction of the project fabio can provide 15,000 requests per second.

    With these two components very easy to do service discovery with automatic load balancing, "artifact in hand, the world i have!" ^ _ ^

    The characteristics of service discovery

    Service and service calls between the need to fill in the configuration file host and port, not easy to maintain and distributed environment is not easy to deploy and expansion.

    Then you need to consider the service at this time to start their own host and port and some other information registered to the registry, so that other services can be found from it.

    Even more simple after the registration through the DNS way to "address". Such as Zookeepr can do a good job of this work, but which also has a drawback is the service of health check service registered to the registry after how to ensure that this service must be available? At this point you need to write their own logic when the service is unavailable from the registration center off the assembly line automatically. Then Consul can easily solve this problem.

    working principle

    Consul provides a set of health detection mechanisms that simply say for http-type services (consul also supports other types such as tcp) at the time of registration can be registered under the health check information, provide a health detection address (url) and a frequency Timeout time such a statement would periodically come to the request when the status code is 200 when the secondary service is set to a healthy state otherwise it is faulty state.

    Since the registration to consul's service to maintain their own health at this time fabio work is very simple! Is directly from the consul registry inside out of a healthy service according to the service when the tag configuration automatically create their own routing table, and then when a http request comes automatically when the load balancing

    The simple flow chart is as follows:

      ====== Service Registration ========= ========= 
    A service <------> consul cluster -> healthy A / unhealthy A cluster
    ====== health check ========= =========
    ^
    Join / remove routing table
    |
    ========
    Fabio cluster
    ========
    |
    A service is successful if it is found otherwise returns an error
    V
    Http request

    Demo

    Here we start to write a demo service to experience a wave of consul + fabio by the way docker + k8s to arrange expansion.

    Because consul + fabio support docker way to run here are docker way as an example.

    Docker pull magiconair / fabio

    Docker pull consul

    Consul can be used to develop the environment can also use the dev mode can refer to here .

    Here you can refer to my finishing stand-alone deployment consul cluster docker compose configuration:

      Version: '2' 

    Services:
    Consul_server_1:
    Image: "consul: latest"
    Container_name: "consul_server_1"
    Environment:
    CONSUL_LOCAL_CONFIG: '{"leave_on_terminate": true}'
    Networks:
    App_net:
    Ipv4_address: 172.17.0.3
    Command: "agent -server -bind = 172.17.0.3 -client = 172.17.0.3 -retry-join = 172.17.0.2"

    Consul_server_2:
    Image: "consul: latest"
    Container_name: "consul_server_2"
    Ports:
    - "8600: 8600"
    - "8500: 8500"
    Networks:
    App_net:
    Ipv4_address: 172.17.0.4
    Command: "agent -server -bind = 172.17.0.4 -client = 172.17.0.4 -retry-join = 172.17.0.3 -ui"

    Consul_server_3:
    Image: "consul: latest"
    Container_name: "consul_server_3"
    Environment:
    CONSUL_LOCAL_CONFIG: '{"leave_on_terminate": true}'
    Networks:
    App_net:
    Ipv4_address: 172.17.0.5
    Command: "agent -server -bind = 172.17.0.5 -client = 172.17.0.5 -retry-join = 172.17.0.4 -bootstrap-expect = 3"

    Networks:
    App_net:
    Driver: bridge
    Ipam:
    Config:
    - subnet: 172.17.0.0/24

    You can also follow the docker hub on the deployment of their own documents, after the success of the visit under the consul ui.
    consul-ui.png
    Then deploy faibo

    Docker-compose.yml

      Fabio 
    Image: "magiconair / fabio"
    Ports:
    - "9998: 9998"
    - "9999: 9999"
    Volumes:
    - ./fabio.properties:/etc/fabio/fabio.properties

    Fabio Although it is zero configuration, but in some cases still need to personalize some things, then you can write a simple configuration fabio.properties

    Specify the consul address port and some of its own statistical information and so on

      Registry.consul.register.addr = 172.16.0.21:9998 

    Registry.consul.addr = 172.16.0.21:8500

    Metrics.target = stdout

    fabio.png
    Fabio can not only be combined with consul to manually write some routing rules syntax as follows:

      Route add <svc> <src> <dst> weight <w> tags "<t1>, <t2>, ..." 
    - Add route for service svc from src to dst and assign weight and tags

    Route add <svc> <src> <dst> weight <w>
    - Add route for service svc from src to dst and assign weight

    Please move by details .

    At this point after the installation and deployment to run a demo try.

    The official provides a simple realization:

     Package main 

    Import (
    "Flag"
    "Fmt"
    "Log"
    "Net"
    "Net / http"
    "Os"
    "Os / signal"
    "Path / filepath"
    "Strconv"
    "Strings"

    "Github.com/magiconair/fabio-example/_third_party/github.com/hashicorp/consul/api"
    )

    Func main () {
    Var addr, name, prefix string
    Flag.StringVar (& addr, "addr", "127.0.0.1:5000", "host: port of the service")
    Flag.StringVar (& name, "name", filepath.Base (os.Args [0]), "name of the service")
    Flag.StringVar (& prefix, "prefix", "", "comma-sep list of host / path prefixes to register")
    Flag.Parse ()

    If prefix == "" {
    Flag.Usage ()
    Os.Exit (1)
    }

    // register prefixes
    Prefixes: = strings.Split (prefix, ",")
    For _, p: = range prefixes {
    Http.HandleFunc (p, func (w http.ResponseWriter, r * http.Request) {
    Fmt.Fprintf (w, "Serving% s from% s on% s \ n", r.RequestURI, name, addr)
    })
    }

    // start http server
    Go func () {
    Log.Printf ("Listening on% s serving% s", addr, prefix)
    If err: = http.ListenAndServe (addr, nil); err! = Nil {
    Log.Fatal (err)
    }
    } ()

    // register consul health check endpoint
    Http.HandleFunc ("/ health", func (w http.ResponseWriter, r * http.Request) {
    Fmt.Fprintln (w, "OK")
    })

    // build urlprefix-host / path tag list
    // eg urlprefix- / foo, urlprefix- / bar, ...
    Var tags [] string
    For _, p: = range prefixes {
    Tags = append (tags, "urlprefix -" + p)
    }

    // get host and port as string / int
    Host, portstr, err: = net.SplitHostPort (addr)
    If err! = Nil {
    Log.Fatal (err)
    }
    Port, err: = strconv.Atoi (portstr)
    If err! = Nil {
    Log.Fatal (err)
    }

    // register service with health check
    ServiceID: = name + "-" + addr
    Service: = & api.AgentServiceRegistration {
    ID: serviceID,
    Name: name,
    Port: port,
    Address: host,
    Tags: tags,
    Check: & api.AgentServiceCheck {
    HTTP: "http: //" + addr + "/ health",
    Interval: "1s",
    Timeout: "1s",
    },
    }

    Client, err: = api.NewClient (api.DefaultConfig ())
    If err! = Nil {
    Log.Fatal (err)
    }

    If err: = client.Agent (). ServiceRegister (service); err! = Nil {
    Log.Fatal (err)
    }
    Log.Printf ("Registered service% q in consul with tags% q", name, strings.Join (tags, ","))

    // run until we get a signal
    Quit: = make (chan os.Signal, 1)
    Signal.Notify (quit, os.Interrupt, os.Kill)
    <-quit

    // deregister service
    If err: = client.Agent (). ServiceDeregister (serviceID); err! = Nil {
    Log.Fatal (err)
    }
    Log.Printf ("Deregistered service% q in consul", name)
    }

    The program at the time of the start to register and register the health check address / health, in the exit time to cancel the registration, to consul registration service when there is a tag to allow this service to pass a label fabio according to this parameter to automatically associate routing Mapping.

    Try to run first

      CONSUL_HTTP_ADDR = 172.16.0.21: 8500 ./fabio-example -addr = 172.16.0.17: 9876 -prefix = a.com / 

    At this point consul has received registration fabio routing has been added.

      # Service Host Path Dest Weight 
    1 fabio-example a.com / http://172.16.0.17:9876/ 100%

      ➜ ~ curl -iv -H 'Host: a.com' 172.16.0.21:9999/ 
    * Trying 172.16.0.21 ...
    * Connected to 172.16.0.21 (172.16.0.21) port 9999 (# 0)
    > GET / HTTP / 1.1
    > Host: a.com
    > User-Agent: curl / 7.43.0
    > Accept: * / *
    >
    <HTTP / 1.1 200 OK
    HTTP / 1.1 200 OK
    <Content-Length: 49
    Content-Length: 49
    <Content-Type: text / plain; charset = utf-8
    Content-Type: text / plain; charset = utf-8
    <Date: Fri, 22 Jul 2016 01:01:28 GMT
    Date: Fri, 22 Jul 2016 01:01:28 GMT

    <
    Serving / from fabio-example on 172.16.0.17:9876
    * Connection # 0 to host 172.16.0.21 left intact

    Combined with kubernetes expansion

    If the need to do some of the arrangements based on k8s need to make some changes For example: ip port to dynamically get from the container inside the service when the need to start the tag prefix configuration, you can refer to fabio documents .

    Here I will use java to build a fast, Spring framework provides a service to find through-train service for the consul is spring-cloud-consul as long as a line of code can be configured quickly to use a few lines 🙂
    Add the @EnableDiscoveryClient annotation on the Application class.

    Application.yml

      Spring: 
    Cloud:
    Consul:
    Discovery
    HealthCheckPath: $ {management.contextPath} / health # The path to health detection
    HealthCheckInterval: 15s # Frequency of health check
    Tags: urlprefix-api.xxxx.com/ #fabio routing rules

    Ps: spring health testing can be achieved with spring-boot-starter-actuator.

    And then k8s inside the need for expansion can be self-expansion, because if you do not use the service discovery method k8s expansion may http request will be forwarded to the start of the container but the service is not available (for example, although the java process started but the initialization may take a few minutes …).

    Feel the expansion of 10

     $ Kubectl scale --replicas = 10 rc api 

    $ Kubectl get pods

    [Root @ 172-16-0-17 fabio-example] # kubectl get pods
    NAME READY STATUS RESTARTS AGE
    Api-6xytx 1/1 Running 0 11s
    Api-9e5838075aae036e2dc971984855e379-ac30s 1/1 Running 0 14h
    Api-dfmtv 1/1 Running 0 11s
    Api-eo01h 1/1 Running 0 11s
    Api-hn1kv 1/1 Running 0 11s
    Api-iyqmg 1/1 Running 0 11s
    Api-k32ud 1/1 Running 0 11s
    Api-q10a7 1/1 Running 0 11s
    Api-re7e1 1/1 Running 0 11s
    Api-tm2pk 1/1 Running 0 11s

    10 seconds in the moment to 10 expansion and then look at the consul and fabio
    docker-consul.png
    Fabio

      # Service Host Path Dest Weight 
    1 api api.com / http://172.31.9.3:8080/ 10%
    2 api api.com / http://172.31.9.2:8080/ 10%
    3 api api.com / http://172.31.82.6:8080/ 10%
    4 api api.com / http://172.31.82.4:8080/ 10%
    5 api api.com / http://172.31.28.3:8080/ 10%
    6 api api.com / http://172.31.28.2:8080/ 10%
    7 api api.com / http://172.31.23.7:8080/ 10%
    8 api api.com / http://172.31.23.2:8080/ 10%
    9 api api.com / http://172.31.12.6:8080/ 10%
    10 api api.com / http://172.31.12.4:8080/ 10%

    At this time in the shrink to a time when fabio can immediately update the routing table.

    ===
    Through the combination of fabio + consul can be easily deployed in a distributed environment and expansion Another consul also provides all the other set up can also complete similar functions such as consul-template .

    Reference link

    • Https://github.com/eBay/fabio/wiki/Configuration
    • Https://www.consul.io/docs/agent/basics.html
    • Http://cloud.spring.io/spring- … .html

    • Consul registered address
    • Consul service problem
    • Docker container interconnection method - Part II
    • Docker combines Consul to achieve service discovery (2)
    • Service discovery: Zookeeper vs etcd vs Consul
    • Constructing Docker Service Architecture Based on Nginx and Consul for High Availability and Autodiscover
    • Heads up! This alert needs your attention, but it's not super important.