如何配置Docker端口映射以使用Nginx作为上游代理?

更新二

It’s now July 16th, 2015 and things have changed again. I’ve
discovered this automagical container from 07000:
07001 and it solves
this problem in about as long as it takes to docker run the
container. This is now the solution I’m using to solve this problem.

更新

It’s now July of 2015 and things have change drastically with regards
to networking Docker containers. There are now many different
offerings that solve this problem (in a variety of ways).

You should use this post to gain a basic understanding of the 07002 approach to service discovery, which is about as basic as it gets, works very well, and actually requires less fancy-dancing than most of the other solutions. It is limited in that it’s quite difficult to network containers on separate hosts in any given cluster, and containers cannot be restarted once networked, but does offer a quick and relatively easy way to network containers on the same host. It’s a good way to get an idea of what the software you’ll likely be using to solve this problem is actually doing under the hood.

Additionally, you’ll probably want to also check out Docker’s nascent 07003, Hashicorp’s 07004, Weaveworks 07005, Jeff Lindsay’s 07006, and Google’s 07007.

There’s also the 07008 offerings that utilize 07009, 070010, and 070011.

And if you really want to have a party you can spin up a cluster to run 070012, or 070013, or 070014.

If you’re new to networking (like me) then you should get out your reading glasses, pop 070015 on the Wi-Hi-Fi, and crack a beer — it’s going to be a while before you really understand exactly what it is you’re trying to do. Hint: You’re trying to implement a Service Discovery Layer in your Cluster Control Plane. It’s a very nice way to spend a Saturday night.

It’s a lot of fun, but I wish I’d taken the time to educate myself better about networking in general before diving right in. I eventually found a couple posts from the benevolent Digital Ocean Tutorial gods: 070016 and 070017. I suggest reading those a few times first before diving in.

Have fun!

原帖

我似乎不能抓住端口映射的Docker容器。具体如何将请求从Nginx传递到另一个容器,监听另一个端口,在同一个服务器上。

我有一个Dockerfile的Nginx容器,像这样:

FROM ubuntu:14.04
MAINTAINER Me <me@myapp.com>

RUN apt-get update && apt-get install -y htop git nginx

ADD sites-enabled/api.myapp.com /etc/nginx/sites-enabled/api.myapp.com
ADD sites-enabled/app.myapp.com /etc/nginx/sites-enabled/app.myapp.com
ADD nginx.conf /etc/nginx/nginx.conf

RUN echo "daemon off;" >> /etc/nginx/nginx.conf

EXPOSE 80 443

CMD ["service", "nginx", "start"]

然后api.myapp.com配置文件看起来像这样:

upstream api_upstream{

    server 0.0.0.0:3333;

}


server {

    listen 80;
    server_name api.myapp.com;
    return 301 https://api.myapp.com/$request_uri;

}


server {

    listen 443;
    server_name api.mypp.com;

    location / {

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_pass http://api_upstream;

    }

}

然后另一个为app.myapp.com以及。

然后我运行:

sudo docker run -p 80:80 -p 443:443 -d --name Nginx myusername/nginx

并且它都站得很好,但是请求没有被传递到其他容器/端口。当我ssh进入Nginx容器和检查日志,我看到没有错误。

任何帮助?

@T0xicCode’s answer是正确的,但我想我会扩大细节,因为它实际上需要我大约20个小时,终于得到一个工作的解决方案实施。

如果你想在自己的容器中运行Nginx,并将其用作反向代理来负载平衡同一服务器实例上的多个应用程序,那么你需要遵循的步骤如下:

链接您的容器

当Docker运行容器时,通常通过向用户数据中输入shell脚本,可以声明到任何其他正在运行的容器的链接。这意味着您需要按顺序启动容器,只有后面的容器可以链接到前者。像这样:

#!/bin/bash
sudo docker run -p 3000:3000 --name API mydockerhub/api
sudo docker run -p 3001:3001 --link API:API --name App mydockerhub/app
sudo docker run -p 80:80 -p 443:443 --link API:API --link App:App --name Nginx mydockerhub/nginx

因此在这个例子中,API容器没有链接到任何其他,但是
应用程序容器链接到API,Nginx链接到API和应用程序。

这样做的结果是更改为env vars和驻留在API和App容器中的/ etc / hosts文件。结果如下:

/ etc / hosts

在你的Nginx容器中运行cat / etc / hosts会产生以下结果:

172.17.0.5  0fd9a40ab5ec
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3  App
172.17.0.2  API

ENV变量

在你的Nginx容器中运行env将产生以下结果:

API_PORT=tcp://172.17.0.2:3000
API_PORT_3000_TCP_PROTO=tcp
API_PORT_3000_TCP_PORT=3000
API_PORT_3000_TCP_ADDR=172.17.0.2

APP_PORT=tcp://172.17.0.3:3001
APP_PORT_3001_TCP_PROTO=tcp
APP_PORT_3001_TCP_PORT=3001
APP_PORT_3001_TCP_ADDR=172.17.0.3

我截断了许多实际的vars,但上面是你需要代理流量到你的容器的关键值。

要获取shell以在正在运行的容器中运行以上命令,请使用以下命令:

sudo docker exec -i -t Nginx bash

您可以看到现在具有/ etc / hosts文件条目和env vars,其中包含已链接的任何容器的本地IP地址。就我所知,这是所有发生当你运行容器链接选项声明。但是你现在可以使用这些信息在你的Nginx容器中配置nginx。

配置Nginx

这是它有点棘手,有几个选项。您可以选择将站点配置为指向docker创建的/ etc / hosts文件中的条目,或者可以使用ENV vars并对nginx.conf和任何其他conf文件运行字符串替换(我使用sed)这可能在您的/ etc / nginx / sites-enabled文件夹中以插入IP值。

选项A:使用ENV Vars配置Nginx

This is the option that I went with because I couldn’t get the
/etc/hosts file option to work. I’ll be trying Option B soon enough
and update this post with any findings.

此选项和使用/ etc / hosts文件选项之间的关键区别是如何编写Dockerfile以使用shell脚本作为CMD参数,而后者将处理字符串替换以将IP值从ENV复制到您的conf文件s)。

这里是我最后配置文件的集合:

Dockerfile

FROM ubuntu:14.04
MAINTAINER Your Name <you@myapp.com>

RUN apt-get update && apt-get install -y nano htop git nginx

ADD nginx.conf /etc/nginx/nginx.conf
ADD api.myapp.conf /etc/nginx/sites-enabled/api.myapp.conf
ADD app.myapp.conf /etc/nginx/sites-enabled/app.myapp.conf
ADD Nginx-Startup.sh /etc/nginx/Nginx-Startup.sh

EXPOSE 80 443

CMD ["/bin/bash","/etc/nginx/Nginx-Startup.sh"]

nginx.conf

daemon off;
user www-data;
pid /var/run/nginx.pid;
worker_processes 1;


events {
    worker_connections 1024;
}


http {

    # Basic Settings

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 33;
    types_hash_max_size 2048;

    server_tokens off;
    server_names_hash_bucket_size 64;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;


    # Logging Settings
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;


    # Gzip Settings

gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 3;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/xml text/css application/x-javascript application/json;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";

    # Virtual Host Configs  
    include /etc/nginx/sites-enabled/*;

    # Error Page Config
    #error_page 403 404 500 502 /srv/Splash;


}

NOTE: It’s important to include daemon off; in your nginx.conf file to ensure that your container doesn’t exit immediately after launching.

api.myapp.conf

upstream api_upstream{
    server APP_IP:3000;
}

server {
    listen 80;
    server_name api.myapp.com;
    return 301 https://api.myapp.com/$request_uri;
}

server {
    listen 443;
    server_name api.myapp.com;

    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_pass http://api_upstream;
    }

}

Nginx-Startup.sh

#!/bin/bash
sed -i 's/APP_IP/'"$API_PORT_3000_TCP_ADDR"'/g' /etc/nginx/sites-enabled/api.myapp.com
sed -i 's/APP_IP/'"$APP_PORT_3001_TCP_ADDR"'/g' /etc/nginx/sites-enabled/app.myapp.com

service nginx start

我将把它留给你做你的家庭作业关于nginx.conf和api.myapp.conf的大部分内容。

魔术发生在Nginx-Startup.sh中,我们使用sed对APP_IP占位符执行字符串替换,我们写入了api.myapp.conf和app.myapp.conf文件的上游块。

这问ask.ubuntu.com问题解释很好:
Find and replace text within a file using commands

GOTCHA
On OSX, sed handles options differently, the -i flag specifically.
On Ubuntu, the -i flag will handle the replacement ‘in place’; it
will open the file, change the text, and then ‘save over’ the same
file.
On OSX, the -i flag requires the file extension you’d like the resulting file to have. If you’re working with a file that has no extension you must input ” as the value for the -i flag.

GOTCHA
To use ENV vars within the regex that sed uses to find the string you want to replace you need to wrap the var within double-quotes. So the correct, albeit wonky-looking, syntax is as above.

所以docker启动了我们的容器并触发了Nginx-Startup.sh脚本运行,它使用sed将值APP_IP更改为我们在sed命令中提供的相应的ENV变量。我们现在在/ etc / nginx / sites-enabled目录中有conf文件,它们在启动容器时具有来自ENV变量的IP地址。在您的api.myapp.conf文件中,您将看到上游块已更改为:

upstream api_upstream{
    server 172.0.0.2:3000;
}

您看到的IP地址可能不同,但我注意到,它通常是172.0.0.x.

你现在应该有一切适当的路由。

GOTCHA
You cannot restart/rerun any containers once you’ve run the initial instance launch. Docker provides each container with a new IP upon launch and does not seem to re-use any that its used before. So api.myapp.com will get 172.0.0.2 the first time, but then get 172.0.0.4 the next time. But Nginx will have already set the first IP into its conf files, or in its /etc/hosts file, so it won’t be able to determine the new IP for api.myapp.com. The solution to this is likely to use CoreOS and its etcd service which, in my limited understanding, acts like a shared ENV for all machines registered into the same CoreOS cluster. This is the next toy I’m going to play with setting up.

选项B:使用/ etc / hosts文件条目

这应该是更快,更容易的方式这样做,但我不能让它工作。表面上,你只需将/ etc / hosts条目的值输入到api.myapp.conf和app.myapp.conf文件中,但是我无法使此方法起作用。

UPDATE:
See 07002 for instructions on how to make this method work.

这里是我在api.myapp.conf中的尝试:

upstream api_upstream{
    server API:3000;
}

考虑到我的/ etc / hosts文件中有一个条目,如:172.0.0.2 API我认为它只是拉入的值,但似乎不是。

我也有几个辅助问题,我的弹性负载平衡器从所有AZ的采购,所以这可能是当我尝试这个路由的问题。相反,我不得不学习如何处理在Linux中替换字符串,所以很有趣。我会尝试一下,看看它是怎么回事。

翻译自:https://stackoverflow.com/questions/27912917/how-to-configure-docker-port-mapping-to-use-nginx-as-an-upstream-proxy

转载注明原文:如何配置Docker端口映射以使用Nginx作为上游代理?