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

    Heads up! This alert needs your attention, but it's not super important.