开发者社区> 轩墨> 正文

在Nginx反向代理后面运行Docker

简介: 本文讲的是在Nginx反向代理后面运行Docker,【编者的话】想让Docker拥有公共网关,能支持多租户,以及我们期望的安全认证等功能?请看作者通过重新编译Nginx让其像支持Websockets一样支持Docker自带协议来实现该需求。
+关注继续查看
本文讲的是在Nginx反向代理后面运行Docker【编者的话】想让Docker拥有公共网关,能支持多租户,以及我们期望的安全认证等功能?请看作者通过重新编译Nginx让其像支持Websockets一样支持Docker自带协议来实现该需求。

几周前,我想试试看Docker是如何运行在云/共享宿主机类似的环境中。当时,Docker正好发布了1.4版本,1.4版本带来了额外的安全和身份认证特性,另外Docker machine也可以自动化创建并运行一个远程Docker实例。

共享主机环境通常基于某个公共网关建立,网关可用于管理大量流入和流出的流量,包括FTP和SSH。环境中最大的部分(与冰山没有什么不同)会“隐藏”在这些网关后面的私有网络中。

因此,我的问题是有没有一种方式可以使得Docker拥有类似行为的网关,包含多租户支持和你所期望的所有功能?

事实证明,有。

Docker二进制文件扮演着3种角色:
  • Docker 命令行 -> 使得Docker易用且非常简单
  • Docker Daemon -> 在幕后默默做着苦差事
  • Docker init -> 做幕后的早期容器设置

命令行和Docker Daemon主要基于HTTP协议来通信。我说“主要”是因为有几个API会“拦截”连接,尤其是container/attach命令,它又称为“forward my container’s console”。

网上常见的文章都推荐设置一个Nginx反向代理,并添加基本的身份认证,以保证安全。

不幸的是,这种方法有两个缺点:
  • 现有的Docker客户端无法与HTTP基础身份认证通信
  • 当Docker拦截连接的时候,现存的Nginx会完全丢失连接

关于认证的问题,我推荐使用Docker的TLS认证,因为它们支持开箱即用。同时,使用Lua magic(译者注:Lua里的Magic字符),我们可以使用它们作为“公钥”来达到适当的平衡。

那我们应该怎么处理第二个问题(Nginx丢失连接)呢?

如果确认拦截方式,那事情就变得简单多了:HTTP可以看成是“半双工”的协议,也就是说,在同一时刻流量只能单向流动:客户端向服务器发送请求(单向),然后服务器响应请求(单向)。当执行docker attach时,Docker可以使用原始的“全双工”模式的TCP连接,任何一端都可以在任何时候发起请求。这就是为什么反向代理会丢失:因为HTTP协议需要请求之后得到确认回复(这段作者讲的有些模糊,有理解的读者可以在评论中和我们交流)。

有趣的是,这里有另外一个主流的协议可以做到这个。事实证明,这个标准协议是如此流行以至于几年前已经被Nginx所接受,我称它为 WebSocket

因此,本质上,这个想法是告诉Nginx 怎样像处理Websocket一样处理Docker的自带协议。这是补丁:
--- a/src/http/ngx_http_upstream.c  Tue Nov 04 19:56:23 2014 +0900
+++ b/src/http/ngx_http_upstream.c  Sat Nov 15 16:21:58 2014 +0100
@@ -89,6 +89,8 @@
 ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r,
 ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_content_type(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_last_modified(ngx_http_request_t *r,
 ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,
@@ -175,7 +177,7 @@
              ngx_http_upstream_copy_header_line, 0, 0 },

 { ngx_string("Content-Type"),
-                 ngx_http_upstream_process_header_line,
+                 ngx_http_upstream_process_content_type,
              offsetof(ngx_http_upstream_headers_in_t, content_type),
              ngx_http_upstream_copy_content_type, 0, 1 },

@@ -2716,6 +2718,7 @@
 u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
 r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
 r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
+    u->headers_in.chunked = 0;

 if (clcf->tcp_nodelay) {
     tcp_nodelay = 1;
@@ -3849,6 +3852,25 @@

static ngx_int_t
+ngx_http_upstream_process_content_type(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    ngx_int_t ret = ngx_http_upstream_process_header_line(r, h, offset);
+    if (ret != NGX_OK) {
+        return ret;
+    }
+
+    // is docker header ?
+    if (ngx_strstrn(h->value.data,
+                    "application/vnd.docker.raw-stream", 34 - 1) != NULL) {
+        r->upstream->upgrade = 1;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
ngx_http_upstream_process_last_modified(ngx_http_request_t *r,
 ngx_table_elt_t *h, ngx_uint_t offset)
{


唯一剩下的步骤就是配置反向代理了。顺便说一句,这是我的 nginx 测试配置文件 nginx.conf
worker_processes  1;

events {
worker_connections  1024;
}

http {
include       mime.types;
default_type  application/octet-stream;

sendfile        on;

keepalive_timeout  65;

server {
    listen 9000;

    location / {
        proxy_buffering off;
        proxy_pass http://localhost:8080;
    }
}
}


你仅仅需要使用以下命令在8080端口运行Docker或者是把你的参数添加进/etc/default/docker
docker -d -H tcp://localhost:8080

我们就完成任务了!
最后的问题
虽然hacking了这个,但是我注意到所有的Nginx都需要为Websocket协议切换到合适的HTTP头:

Request

Connection: Upgrade Upgrade: websocket

Response

HTTP/1.1 101 Upgraded Connection: Upgrade Upgrade: websocket
因此我想另外一个方法是在Docker协议中注入合适的头。

原文链接: How to run Docker behind an Nginx reverse proxy(翻译:叶可强 审校:郭蕾)

原文发布时间为:2015-01-05 
本文作者:叶可强
本文来自云栖社区合作伙伴DockerOne,了解相关信息可以关注DockerOne。
原文标题:在Nginx反向代理后面运行Docker

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
10016 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
18998 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
17208 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
25240 0
阿里云服务器ECS远程登录用户名密码查询方法
阿里云服务器ECS远程连接登录输入用户名和密码,阿里云没有默认密码,如果购买时没设置需要先重置实例密码,Windows用户名是administrator,Linux账号是root,阿小云来详细说下阿里云服务器远程登录连接用户名和密码查询方法
21687 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
19220 0
+关注
2351
文章
701
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载