Docker Workflow(四):服务发现与负载均衡


【编者的话】作者讲述了如何将服务发现(Consul.io与Consul-Template)与负载均衡(Nginx)相结合,实现灵活的配置和自动化重载,降低运维难度。当你还执着于给容器分配固定IP这件事上时,也许服务发现才是正道。

这是关于我们如何在IIIEPE的生产环境中使用Docker的系列文章的最后一部分。如果你还没看过第一译文)、第二译文)和第三译文)部分,请先前往阅读再继续。本文中,我将讨论如何配置服务发现和负载均衡的。

服务发现

市面上有不少服务发现方案,不过我们只测试过etcdConsul.io。Etcd是CoreOS的一部分,虽然你可以脱离CoreOS使用它,但你很快就会发现你需要学习的不仅仅是etcd。Consul.io使用非常简单,通过Docker运行,并且拥有一个不错的生态系统。

在细说之前,如果你不知道什么是服务发现,我得说明一下,它与负载均衡无关,只是一款知晓运行在基础设施上的容器当前状态的软件。也就是说,服务发现跟你如何发送信息给它无关。

与其它服务发现工具一样,Consul.io解决了一个问题,它会存储应用的IP、端口和状态。要将应用注册到Consul.io里(正确的说法是服务),我们使用了Registrator。一旦Consul.io感知到应用,就需要做点什么,在我们的案例中,要重新载入负载均衡配置,因此我们使用了Consul-Template

因为Consul.io和Registrator都运行在Docker容器里,实现起来比我们想象的要简单得多。

对于Consul-Template,最困难的部分是弄明白模板的语法。

负载均衡

我们使用Nginx作为负载均衡器,因为我们已使用多年,知道如何配置它,而像HAProxy这样的方案只会给整个工作流增加复杂度。因为这是所有事情的最后一块,我们选择了简单的方式来完成它。最终,我们想用HAProxy取代Nginx,不过这得再等几个星期。

在一个正常的负载均衡场景中,你会这样配置Nginx:
upstream myapp {
ip_hash;
server 10.10.10.10:14001 fail_timeout=0;
keepalive 64;
}

server {
listen 80;
server_name example.com;
location / {
proxy_pass          http://myapp;
}
}

当你想在upstream块里动态分配一系列的IP和端口时,问题就出现了。使用consul-template,我们创建了一个模板文件/etc/nginx/templates/template,每个使用该负载均衡器处理的应用都会有一个区块:
upstream myapp {
ip_hash;
{{range service "myapp"}}
server {{.Address}}:{{.Port}} fail_timeout=0;
{{end}}
keepalive 64;
}

server {
listen 80;
server_name example.com;
location / {
proxy_pass          http://myapp;
}
}

对于使用SSL的网站,模板会长一点:
# this part handles the list of IPs and ports
upstream myapp {
ip_hash;
{{range service "myapp"}}
server {{.Address}}:{{.Port}} fail_timeout=0;
{{end}}
keepalive 64;
}

# this section handles requests to port 80, which we'll redirect to the port 445
server {
    listen                          80;
    server_name                     example.com;
    return 301 https://$host$request_uri;
}

server {
    listen                          443 ssl spdy;
    server_name                     example.com;
    keepalive_timeout 75 75;

    ssl                             on;
    ssl_certificate                 /etc/nginx/cert/path_to_cert.crt;
    ssl_certificate_key             /etc/nginx/cert/path_to_key.key;
    ssl_session_cache               builtin:1000 shared:SSL:10m;
    ssl_protocols                   TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers                     HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers       on;

    add_header                      Strict-Transport-Security max-age=31536000;

    location / {
            proxy_pass              http://myapp;
            proxy_set_header        Host $host;
            proxy_set_header        X-Forwarded-Proto $scheme;
            proxy_set_header        X-Real-IP $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_connect_timeout   150;
            proxy_send_timeout      100;
            proxy_read_timeout      90;
            proxy_buffers           4 32k;
            client_max_body_size    500m;
            client_body_buffer_size 128k;
            proxy_redirect          http:// https://;
    }
}

每次Consul-Template运行时,它会向Consul.io查询“myapp”的服务,并将发现的每个容器的IP和端口添加到upstream块中。

因为Consul.io和Consul-Template需要在服务器重启时启动,我们创建了一个Upstart作业来处理:
description    "Consul Template"
author        "Luis Elizondo"

start on filesystem or runlevel [2345]
stop on shutdown

script
echo $$ > /var/run/consul-template.pid
exec consul-template \
    -consul IP_OF_CONSUL:PORT_OF_CONSUL \
    -template "/etc/nginx/templates/template:/etc/nginx/sites-enabled/default:service nginx restart"
end script

pre-start script
echo "[`date`] Consul Template Starting" >> /var/log/consul-template.log
end script

pre-stop script
rm /var/run/consul-template.pid
echo "[`date`] Consul Template Stoping" >> /var/log/consul-template.log
end script

Consul.io也一样:
description    "Consul"
author        "Luis Elizondo"

start on filesystem or runlevel [2345]
stop on shutdown

script
echo $$ > /var/run/consul.pid
exec docker start consul
end script

pre-start script
echo "[`date`] Consul Starting" >> /var/log/consul.log
end script

pre-stop script
rm /var/run/consul.pid
exec docker stop consul
echo "[`date`] Consul Stoping" >> /var/log/consul.log
end script

Registrator也需要,不过Registrator不是运行在LB上的,而是在每个web节点上:
description    "Registrator"
author        "Luis Elizondo"

start on filesystem or runlevel [2345]
stop on shutdown

script
echo $$ > /var/run/registrator.pid
exec docker start registrator
end script

pre-start script
echo "[`date`] Registrator Starting" >> /var/log/registrator.log
end script

pre-stop script
rm /var/run/registrator.pid
exec docker stop registrator
echo "[`date`] Registrator Stoping" >> /var/log/registrator.log

在2014年11月我们开始这趟旅程之前,我们找不到可以采纳或适应我们条件的完整工作流的足够信息,这也是我在此与大家分享的主要动机。作为完成这件事的二人小组成员之一,我意识到我们的工作流还远远不够完善,随着时间的推移,可能会出现我们未预料到的情况。现在我们有很多想改进的事情,迁移到HAProxy(不针对Nginx)是其中这一。不过,我们的工作流和基础设施是以帮助我们完成工作的方式来配置的(我的主要职责是作为开发人员,不是系统管理员),而不是把我们的存在复杂化。

测试、安装和实现新的工作流,然后迁移所有应用总共花费了我们2个半月时间,但这是值得的。在迁移之前,我们使用Digital Ocean来预演大多数的问题和情况,这对我们而言是个很棒的方案,因为它非常便宜(我们花了大概5美元),这么做让我们可以将每一步都记录下来。

如果你有任何问题,请留言或在Twitter上提及我@lelizondo。你可以在GitHub上关注我、克隆并贡献代码到我们组织的项目之一或使用我们的镜像。谢谢!

原文链接:A production ready Docker workflow. Part 4: Service Discovery and the load balancer(翻译:梁晓勇 校对:郭蕾)

2 个评论

你好 提个问题,当前使用consul-template动态修改nginx.conf文件,然后进行reload 命令如下
consul-template -consul 10.*.*.*:8500 -template "/usr/local/nginx/conf/conf-upstream.d/nginx.ctmpl:upstream.conf:/usr/local/nginx/sbin/nginx -s reload
但是当某个项目的容器全部宕机后,这个后台运行的consul-template将会推出,请问这个问题应该如何解决;
背景:我们有几十上百的项目,组合一个upstream文件,生成upstream配置,其中一个项目下线就会导致 upstream为空,reload报错,consul-template进程推出
consul-template进程推出,多半是你template文件没写正确。这是博主的原话“对于Consul-Template,最困难的部分是弄明白模板的语法。”

要回复文章请先登录注册