现在是2015年7月16日,事情再次发生了变化。我有 从Jason Wilder发现了这个自动化容器:
https://github.com/jwilder/nginx-proxy
它解决了 这个问题大约需要docker run
容器。这是我用来解决这个问题的解决方案。
现在是2015年7月,事情发生了巨大变化 联网Docker容器。现在有很多不同 解决这个问题的产品(以各种方式)。
您应该使用这篇文章来基本了解
docker --link
服务发现的方法,这种方法基本上是基本的,效果非常好,并且实际上需要比其他大多数方式更少的花哨跳舞解决方案。它的局限性在于,在任何给定集群中将容器联网在单独的主机上非常困难,并且一旦联网就无法重新启动容器,但它确实提供了一种快速且相对简单的方法来在同一主机上联网容器。这是一个很好的方式来了解你可能用来解决这个问题的软件实际上是在做什么。此外,您可能还想查看Docker的新生
network
,Hashicorp的consul
,Weaveworksweave
,Jeff Lindsay的progrium/consul
&gliderlabs/registrator
和Google的Kubernetes
}。还有CoreOS个产品使用
etcd
,fleet
和flannel
。如果您真的想举办派对,可以启动群集来运行
Mesosphere
,Deis
或Flynn
。如果你是网络新手(比如我)那么你应该拿出你的老花镜,在Wi-Hi-Fi上弹出"Paint The Sky With Stars — The Best of Enya",然后点一杯啤酒 - 这将是你的一段时间之前真的很清楚你正在尝试做什么。提示:您正试图在
Service Discovery Layer
中实施Cluster Control Plane
。这是一个非常好的方式来度过一个周六晚上。这很有趣,但我希望在潜入之前我花时间更好地了解网络。我最终发现了一些来自仁慈的数字海洋教程神的帖子:
Introduction to Networking Terminology
和Understanding ... Networking
。我建议在潜水之前先阅读几次。玩得开心!
我似乎无法掌握Docker
容器的端口映射。具体来说,如何将请求从Nginx传递到另一个容器,在同一服务器上侦听另一个端口。
我有一个Nginx容器的Dockerfile,如下所示:
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
这一切都很好,但请求没有传递到其他容器/端口。当我进入Nginx容器并检查日志时,我发现没有错误。
任何帮助?
答案 0 :(得分:55)
@T0xicCode's answer是正确的,但我想我会扩展细节,因为它实际上花了我大约20个小时才能最终实现一个有效的解决方案。
如果您希望在自己的容器中运行Nginx并将其用作反向代理来在同一服务器实例上对多个应用程序进行负载均衡,那么您需要遵循的步骤如下:
关联您的容器
当您docker run
容器时,通常通过将shell脚本输入User Data
,您可以声明指向任何其他运行容器的链接。这意味着您需要按顺序启动容器,只有后面的容器可以链接到前面的容器。像这样:
#!/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
容器没有链接到任何其他容器,但是
App
容器与API
相关联,Nginx
与API
和App
相关联。
这样做的结果是env
变量和/etc/hosts
和API
容器中的App
文件的更改。结果如下:
<强>的/ etc /主机强>
在cat /etc/hosts
容器中运行Nginx
将产生以下内容:
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 Vars
在env
容器中运行Nginx
将产生以下内容:
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
我已截断了许多实际变量,但以上是将流量代理到容器所需的关键值。
要获取shell以在正在运行的容器中运行上述命令,请使用以下命令:
sudo docker exec -i -t Nginx bash
您可以看到,您现在拥有/etc/hosts
个文件条目和env
个vars,其中包含已链接的任何容器的本地IP地址。据我所知,当您运行声明了链接选项的容器时,就会发生这种情况。但您现在可以使用此信息在nginx
容器中配置Nginx
。
配置Nginx
这是一个有点棘手的地方,还有几个选择。您可以选择将站点配置为指向/etc/hosts
创建的docker
文件中的条目,或者您可以使用ENV
变量并运行字符串替换(我使用{{1您sed
以及nginx.conf
文件夹中可能插入IP值的任何其他配置文件。
选项A:使用ENV Vars配置Nginx
这是我选择的选项,因为我无法获得 工作
/etc/nginx/sites-enabled
个文件选项。我很快就会尝试选项B. 并根据任何调查结果更新此帖子。
此选项与使用/etc/hosts
文件选项之间的主要区别在于您如何编写/etc/hosts
以使用shell脚本作为Dockerfile
参数,后者又处理字符串替换将IP值从CMD
复制到您的配置文件。
这是我最终得到的配置文件集:
<强> Dockerfile 强>
ENV
<强> nginx.conf 强>
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"]
注意:在
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; }
文件中加入daemon off;
非常重要,以确保您的容器在启动后不会立即退出。
<强> api.myapp.conf 强>
nginx.conf
<强> Nginx-Startup.sh 强>
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;
}
}
我会让您完成关于#!/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
块的upstream
占位符上进行字符串替换{1}}和api.myapp.conf
个文件。
这个ask.ubuntu.com问题解释得非常好: Find and replace text within a file using commands
<强> GOTCHA 强> 在OSX上,
app.myapp.conf
以不同方式处理选项,具体是sed
标志。 在Ubuntu上,-i
标志将处理替换&#39;到位&#39 ;;它 将打开文件,更改文本,然后“保存”。相同 文件。 在OSX上,-i
标志需要您希望生成的文件的文件扩展名。如果您正在处理没有扩展名的文件,则必须输入“&#39;&#39;作为-i
标志的值。<强> GOTCHA 强> 要在
-i
用于查找要替换的字符串的正则表达式中使用ENV变量,需要将var包装在双引号内。因此,正确的,尽管看起来很难看的语法如上所述。
所以docker启动了我们的容器,并触发了sed
脚本运行,该脚本使用Nginx-Startup.sh
将值sed
更改为我们提供的相应APP_IP
变量ENV
命令。我们现在在sed
目录中有conf文件,其中包含docker在启动容器时设置的/etc/nginx/sites-enabled
vars的IP地址。在ENV
文件中,您会看到api.myapp.conf
块已更改为:
upstream
您看到的IP地址可能有所不同,但我注意到它通常是upstream api_upstream{
server 172.0.0.2:3000;
}
。
现在你应该适当地路由一切。
<强> GOTCHA 强> 一旦您运行初始实例启动,就无法重新启动/重新运行任何容器。 Docker在启动时为每个容器提供一个新的IP,并且似乎不再重复使用它之前使用的任何IP。所以
172.0.0.x
第一次得到172.0.0.2,但下次得到172.0.0.4。但api.myapp.com
已将第一个IP设置到其conf文件或其Nginx
文件中,因此无法确定/etc/hosts
的新IP。对此的解决方案可能会使用api.myapp.com
及其CoreOS
服务,在我有限的理解中,对于注册到同一etcd
集群的所有计算机,它就像共享ENV
一样。这是我将要设置的下一个玩具。
选项B:使用CoreOS
文件条目
这个应该是更快,更简单的方法,但我无法让它工作。表面上,您只需将/etc/hosts
条目的值输入到/etc/hosts
和api.myapp.conf
文件中,但我无法使用此方法。
更新: 有关如何使此方法有效的说明,请参阅@Wes Tod's answer。
这是我在app.myapp.conf
中所做的尝试:
api.myapp.conf
考虑到upstream api_upstream{
server API:3000;
}
文件中的条目是这样的:/etc/hosts
我认为它只会提取值,但它似乎不是。< / p>
我在所有AZ的172.0.0.2 API
采购时也遇到了一些辅助问题,所以当我尝试这条路线时可能会出现这个问题。相反,我必须学习如何在Linux中处理替换字符串,所以这很有趣。我会在一段时间内尝试一下,看看它是怎么回事。
答案 1 :(得分:9)
使用docker links,您可以将上游容器链接到nginx容器。添加的功能是docker管理主机文件,这意味着您可以使用名称而不是潜在的随机ip来引用链接容器。
答案 2 :(得分:9)
我尝试使用流行的Jason Wilder反向代理,它代码神奇地适用于每个人,并且了解到它并不适用于所有人(即:我)。我是NGINX的新手,并不喜欢我不了解我试图使用的技术。
想要加上我的2美分,因为上面关于linking
个容器的讨论现在已经过时了,因为它是一个不推荐使用的功能。以下是使用networks
解释如何执行此操作的说明。这个答案是使用Docker Compose
和nginx配置将nginx设置为静态分页网站的反向代理的完整示例。
将需要相互通信的服务添加到预定义的网络中。有关Docker网络的分步讨论,我在这里学到了一些东西: https://technologyconversations.com/2016/04/25/docker-networking-and-dns-the-good-the-bad-and-the-ugly/
首先,我们需要一个网络,您的所有后端服务都可以在其上进行通信。我打电话给我web
,但它可以是你想要的任何东西。
docker network create web
我们只是做一个简单的网站应用程序。该网站是一个简单的index.html页面,由nginx容器提供服务。内容是文件夹content
DockerFile:
FROM nginx
COPY default.conf /etc/nginx/conf.d/default.conf
default.conf
server {
listen 80;
server_name localhost;
location / {
root /var/www/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
搬运工-compose.yml
version: "2"
networks:
mynetwork:
external:
name: web
services:
nginx:
container_name: sample-site
build: .
expose:
- "80"
volumes:
- "./content/:/var/www/html/"
networks:
default: {}
mynetwork:
aliases:
- sample-site
请注意,我们不再需要端口映射。我们简单地公开端口80.这对于避免端口冲突很方便。
使用
启动此网站docker-compose up -d
关于容器的dns映射的一些有趣检查:
docker exec -it sample-site bash
ping sample-site
这个ping应该在你的容器内工作。
Nginx反向代理:
Dockerfile
FROM nginx
RUN rm /etc/nginx/conf.d/*
我们重置了所有虚拟主机配置,因为我们要自定义它。
搬运工-compose.yml
version: "2"
networks:
mynetwork:
external:
name: web
services:
nginx:
container_name: nginx-proxy
build: .
ports:
- "80:80"
- "443:443"
volumes:
- ./conf.d/:/etc/nginx/conf.d/:ro
- ./sites/:/var/www/
networks:
default: {}
mynetwork:
aliases:
- nginx-proxy
使用我们值得信赖的
启动代理docker-compose up -d
假设没有问题,那么你有两个运行的容器可以使用它们的名字相互通信。我们来试试吧。
docker exec -it nginx-proxy bash
ping sample-site
ping nginx-proxy
最后一个细节是设置虚拟主机文件,以便代理可以根据您想要设置匹配来引导流量:
我们的虚拟主机配置的sample-site.conf:
server {
listen 80;
listen [::]:80;
server_name my.domain.com;
location / {
proxy_pass http://sample-site;
}
}
根据代理的设置方式,您需要将此文件存储在我们通过conf.d
文件中的volumes
声明安装的本地docker-compose
文件夹下。
最后但同样重要的是,告诉nginx重新加载它的配置。
docker exec nginx-proxy service nginx reload
这些步骤是因为我遇到了痛苦的502 Bad Gateway错误,并且第一次学习nginx,因为我的大部分经验都是使用Apache,所以这些步骤是头疼的几个小时的高潮。
这个答案是为了演示如何消除由于容器无法相互通信而导致的502 Bad Gateway错误。
我希望这个答案可以让那些人在那里度过几个小时的痛苦,因为由于某种原因,让容器互相交谈真的很难理解,尽管它是我期望的一个明显的用例。但话又说回来,我傻了。请告诉我如何改进这种方法。
答案 3 :(得分:7)
AJB's "Option B"可以通过使用基础Ubuntu映像并自行设置nginx来实现。 (当我使用Docker Hub的Nginx图像时,它没有用。)
这是我使用的Docker文件:
FROM ubuntu
RUN apt-get update && apt-get install -y nginx
RUN ln -sf /dev/stdout /var/log/nginx/access.log
RUN ln -sf /dev/stderr /var/log/nginx/error.log
RUN rm -rf /etc/nginx/sites-enabled/default
EXPOSE 80 443
COPY conf/mysite.com /etc/nginx/sites-enabled/mysite.com
CMD ["nginx", "-g", "daemon off;"]
我的nginx配置(又名:conf / mysite.com):
server {
listen 80 default;
server_name mysite.com;
location / {
proxy_pass http://website;
}
}
upstream website {
server website:3000;
}
最后,我如何开始我的容器:
$ docker run -dP --name website website
$ docker run -dP --name nginx --link website:website nginx
这让我起步并运行所以我的nginx将上游指向第二个Docker容器,它暴露了端口3000.
答案 4 :(得分:6)
@gdbj的答案是一个很好的解释和最新的答案。然而,这是一种更简单的方法。
因此,如果您想将来自nginx收听resizeMode: stretch
的所有流量重定向到另一个展示80
的容器,则最低配置可以是:
nginx.conf:
8080
搬运工-compose.yml
server {
listen 80;
location / {
proxy_pass http://client:8080; # this one here
proxy_redirect off;
}
}
答案 5 :(得分:2)
刚从Anand Mani Sankar找到一个article,它展示了一种使用nginx上游代理与docker composer的简单方法。
基本上,必须在docker-compose文件中配置实例链接和端口,并相应地在nginx.conf上游更新。