为“多变”的Docker容器构建自动化的Nginx反向代理

nwbg 9年前

【编者的话】Docker容器是无状态的,它们会随机分配IP和端口号。而在Web服务器中,我们经常需要适用Nginx来做负载均衡。众所周知,Nginx的配置需要制定主机IP和端口,那容器的IP、端口是变化的,这个时候我们应该如何配置了?作者给出了自己的解决方案,读者可以参考。需要注意的是,这种方案只适用于所有容器都在一台服务器上。

反向代理服务器通常是位于Web服务器的前端,它会提供一些Web服务器无法提供的附加功能。例如,一个反向代理服务器可以提供SSL、负载均衡、路由请求、缓存、压缩甚至A/B测试。

当在Docker容器中运行Web服务的时候,如果能在容器前运行一个反向代理服务器,那将会极大的简化部署流程。

-------------------------------------------------------------------------------------------------------------------------

为什么Docker要使用反向代理?

Docker容器会随机分配IP和端口号,所以从客户端寻找它们会变得异常复杂(译者注:比如你部署了一个Web服务,但每次重启后容器地址都会发生改变,这个时候你怎么去访问你的服务了?)。默认情况下,IP和端口号是专用于容器的,不能被外部访问,除非它们被绑定到主机上。

绑定容器到主机端口上的思路并不可行,它不便于在同一台主机运行多个容器。例如,在同一个时间内只能有一个容器可以绑定到80端口。同时这种方案也不利于扩展,因为在新容器开始之前,老容器必须停止。

反向代理可以帮助解决这些问题,也可以通过加快零死机时间部署来提高可用性。

-------------------------------------------------------------------------------------------------------------------------

生成反向代理配置

当容器开始和停止时,设置一个反向代理配置是非常复杂的。典型的配置都需要手动升级,但这个方式不仅容易出错,而且还很浪费时间。

幸运的是Docker提供了一个远程应用程序接口(API)来检查容器和它们的IP、端口和其它配置元数据。除此之外,也提供了一个实时事件API,当容器开始和停止的时候,可以发出实时通知。这些API都可以被用于自动生成一个反向代理配置。

docker-gen 是一个小程序,它可以使用这些API将容器元数据传递给模板,随之模板就可以重新生成,并通过一个可选择的通知指令来重启服务。

通过使用docker-gen,我们可以在重启改变的时候自动生成Nginx配置文件并重启。当然这些方法也可以用来管理Docker日志

-------------------------------------------------------------------------------------------------------------------------

用Docker建Nginx反向代理

下面例子中的这个Nginx模板可以为Docker容器生成一个反向代理配置,这个模板是使用golangtext/template package来实现的。它使用一个自定义的groupBy模板函数来对运行的容器进行分组(通过VIRTUAL_HOST环境变量)。这简化了遍历容器来生成一个负载均衡后端以及使得零停机部署变得可行。
{{ range $host, $containers := groupBy $ "Env.VIRTUAL_HOST" }}  upstream {{ $host }} {    {{ range $index, $value := $containers }}  {{ with $address := index $value.Addresses 0 }}  server {{ $address.IP }}:{{ $address.Port }};  {{ end }}  {{ end }}    }    server {  #ssl_certificate /etc/nginx/certs/demo.pem;  #ssl_certificate_key /etc/nginx/certs/demo.key;    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;    server_name {{ $host }};    location / {      proxy_pass http://{{ $host }};      include /etc/nginx/proxy_params;  }  }  {{ end }}

这个模板可以使用docker-gen来运行:

docker-gen -only-exposed -watch -notify "/etc/init.d/nginx reload" templates/nginx.tmpl /etc/nginx/sites-enabled/default
  • -only-exposed- 仅使用已经暴露的端口。
  • -watch- 启动之后,监控docker容器事件和生成模板。
  • -notify "/etc/init.d/nginx reload"- 在生成模板之后重载Nginx配置。
  • templates/nginx.tmpl- Nginx模板。
  • /etc/nginx/sites-enabled/default- 目标文件。

这是一个容器配置了VIRTUAL_HOST=demo1.localhost,另一个配置VIRTUAL_HOST=demo2.localhost的容器的模板。
upstream demo1.localhost {  server 172.17.0.4:5000;  server 172.17.0.3:5000;  }    server {  #ssl_certificate /etc/nginx/certs/demo.pem;  #ssl_certificate_key /etc/nginx/certs/demo.key;    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;    server_name demo1.localhost;    location / {      proxy_pass http://demo.localhost;      include /etc/nginx/proxy_params;  }  }    upstream demo2.localhost {  server 172.17.0.5:5000;  }    server {  #ssl_certificate /etc/nginx/certs/demo.pem;  #ssl_certificate_key /etc/nginx/certs/demo.key;    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;    server_name demo2.localhost;    location / {      proxy_pass http://demo2.localhost;      include /etc/nginx/proxy_params;  }  }

}}}

#### 试一试  我创建了一个[可信任的Docker镜像](https://index.docker.io/u/jwilder/nginx-proxy/)来做试验。 运行Nginx-proxy容器: {{{$ docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock -t jwilder/nginx-proxy

使用VIRTUAL_HOST环境变量来启动你的容器:
docker run -e VIRTUAL_HOST=foo.bar.com -t ...

如果你需要HTTPS,可能会喜欢在一个Nginx分组容器里运行docker-gen,需要Websocket支持或者其它特性,请在GitHub项目网站查看以获取更多信息。

-------------------------------------------------------------------------------------------------------------------------

结论

我们可以通过自动地使用Docker API和一些基本的模板来为Docker容器生成Nginx反向代理配置,这可以简化部署也可以提高可用性。

需要注意的是这种方案仅适用于单台机器,如果有多台机器可能就得考虑服务发现来生成配置了。如果你有兴趣,请查看Docker服务发现寻找解决问题的方法。

另外,这里有一些类似的观点值得你阅读:

原文链接:Automated Nginx Reverse Proxy for Docker (翻译:徐霞 校对: 吴锦晟)

来自:http://dockone.io/article/297