如何configurationDocker端口映射来使用Nginx作为上游代理?
更新II
现在是2015年7月16日,事情又改变了。 我从Jason Wilder发现了这个自动化的容器:
https://github.com/jwilder/nginx-proxy
:https://github.com/jwilder/nginx-proxy
,只要需要docker run
容器,它就解决了这个问题。 这是现在我用来解决这个问题的解决scheme。
更新
现在是2015年7月,事情发生了巨大的变化,关于networkingDocker容器。 现在有许多不同的解决scheme可以解决这个问题(以各种方式)。
您应该使用这篇文章来获得对
docker --link
的基本了解 – 连接服务发现的方法,它基本上是一样的,工作得很好,实际上比大多数其他解决scheme所需的花式跳舞less。 这是有限的,很难在任何给定的集群单独的主机上的容器networking,容器不能重新启动一旦联网,但提供了一个快速和相对简单的方法,在同一个主机上的networking容器。 弄清楚你可能用来解决这个问题的软件实际上是在做什么的。此外,你可能还想看看Docker的新生
network
,Hashicorp的consul
,Weaveworksweave
,Jeff Lindsay的progrium/consul
&gliderlabs/registrator
以及Google的Kubernetes
。还有使用
etcd
,fleet
和flannel
etcd
产品。如果你真的想参加一个聚会,你可以build立一个集群来运行
Mesosphere
,Deis
或者Flynn
。如果你刚接触networking(比如我),那么你应该拿出你的老花镜,在Wi-Hi-Fi上popup“用星星涂写天空 – 恩雅的精华” ,然后开一杯啤酒 – 这将会是有一段时间你才真正明白你在做什么。 提示:您正试图在您的
Cluster Control Plane
实现Service Discovery Layer
。 这是一个非常好的方式来度过一个星期六晚上。这很有趣,但是我希望自己能花点时间好好教育自己,以便更好地了解networking。我最终从仁慈的数字海洋教程(Digital Ocean Tutorials Gods)网站上find了几篇文章:
Introduction to Networking Terminology
和Understanding ... Networking
Introduction to Networking Terminology
Understanding ... Networking
。 我build议在潜水前先阅读几遍。玩的开心!
原始post
我似乎无法掌握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
configuration文件看起来像这样:
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的答案是正确的,但我想我会扩大细节,因为它实际上花了我大约20个小时终于得到一个工作的解决scheme实施。
如果您想在自己的容器中运行Nginx并将其用作反向代理以在同一服务器实例上对多个应用程序进行负载平衡,那么您需要遵循的步骤如下:
链接你的容器
当docker run
你的容器时,通常通过在User Data
input一个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
容器并没有链接到任何其他的,但是App
容器链接到API
而Nginx
链接到API
和App
。
这样做的结果是改变了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 Vars
在你的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
我截断了许多实际的变数,但以上是将stream量代入您的容器所需的关键值。
要获取一个shell来运行正在运行的容器中的上述命令,请使用以下命令:
sudo docker exec -i -t Nginx bash
您可以看到,现在已经有/etc/hosts
文件条目和env
vars包含任何已链接的容器的本地IP地址。 据我所知,这是当你运行声明链接选项的容器时发生的一切。 但是你现在可以使用这个信息来在你的Nginx
容器中configurationnginx
。
configurationNginx
这是一个棘手的地方,有几个选项。 您可以select将站点configuration为指向docker
创build的/etc/hosts
文件中的条目,也可以使用ENV
variables并在nginx.conf
和其他任何conf文件上运行stringreplace(我使用sed
)可能在你的/etc/nginx/sites-enabled
文件夹中插入IP值。
选项A:使用ENV VarsconfigurationNginx
这是我去的选项,因为我不能让
/etc/hosts
文件选项工作。 我会尽快尝试Option B,并更新这个post的任何发现。
此选项和使用/etc/hosts
文件选项之间的主要区别在于,如何编写Dockerfile
以使用shell脚本作为CMD
参数, CMD
参数又处理stringreplace以将IP值从ENV
复制到您的conf文件S)。
下面是我最终的一组configuration文件:
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; }
注意:
daemon off;
非常重要daemon off;
在您的nginx.conf
文件中,以确保您的容器在启动后不会立即退出。
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
大部分内容的api.myapp.conf
。
神奇的事情发生在Nginx-Startup.sh
,我们使用sed
在APP_IP
占位符上进行stringreplace,我们已经写入了我们的api.myapp.conf
和app.myapp.conf
文件的upstream
模块。
这个ask.ubuntu.com问题很好地解释了它: 使用命令查找和replace文件中的文本
GOTCHA在OSX上,
sed
处理选项的方式有所不同,特别是-i
标志。 在Ubuntu上,-i
标志将处理replace'in place'; 它会打开文件,更改文本,然后“保存”相同的文件。 在OSX上,-i
标志需要文件扩展名,您希望生成的文件具有。 如果您使用的是没有扩展名的文件,则必须input“'作为-i
标志的值。GOTCHA要在正则expression式中使用ENVvariables来查找要replace的string,需要将variables包含在双引号内。 所以,正确的,虽然看起来很诡异,但语法如上。
因此, Nginx-Startup.sh
启动了我们的容器并触发了Nginx-Startup.sh
脚本运行,它使用sed
将值APP_IP
更改为我们在sed
命令中提供的相应的ENV
variables。 我们现在在我们的/etc/nginx/sites-enabled
目录下有conf文件,它们具有启动容器时由docker设置的ENV
variables的IP地址。 在您的api.myapp.conf
文件中,您会看到upstream
块已经更改为:
upstream api_upstream{ server 172.0.0.2:3000; }
你看到的IP地址可能不同,但我注意到它通常是172.0.0.x
你现在应该有适当的路由。
GOTCHA运行初始实例启动后,无法重新启动/重新运行任何容器。 Docker在启动时为每个容器提供一个新的IP,似乎不会重复使用之前使用过的任何IP。 所以
api.myapp.com
第一次得到172.0.0.2,下次得到172.0.0.4。 但是Nginx
已经将第一个IP设置到它的conf文件中,或者在/etc/hosts
文件中,所以它将不能确定api.myapp.com
的新IP。 解决这个问题的办法很可能是使用etcd
及其etcd
服务,在我有限的理解下,etcd
服务对于注册在同一个CoreOS
集群中的所有机器来说就像一个共享的ENV
。 这是我将要搭build的下一个玩具。
选项B:使用/etc/hosts
文件条目
这应该是这样做的更快,更简单的方法,但我不能得到它的工作。 表面上你只需api.myapp.conf
/etc/hosts
项的值input到你的api.myapp.conf
和app.myapp.conf
文件中,但是我不能使用这个方法。
更新:请参阅@Wes Tod的答案有关如何使此方法工作的说明。
以下是我在api.myapp.conf
所做的尝试:
upstream api_upstream{ server API:3000; }
考虑到我的/etc/hosts
文件中有一个条目,如下所示: 172.0.0.2 API
我认为它只是拉入值,但似乎不是。
我的Elastic Load Balancer
从所有AZ中采购都有一些辅助问题,所以当我尝试这个路由时可能会遇到问题。 相反,我不得不学习如何在Linux中处理replacestring,所以这很有趣。 我会稍微试一试,看看它是如何发展的。
使用docker链接 ,可以将上游容器链接到nginx容器。 一个附加的function是,dockerpipe理主机文件,这意味着您将能够使用名称而不是可能随机的IP来引用链接的容器。
AJB的“选项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;"]
我的nginxconfiguration(又名: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。
我试着使用stream行的Jason Wilder反向代理,它代码神奇地适用于每个人,并且知道它不适用于每个人(即:我)。 而且我对NGINX是全新的,并不喜欢我不了解我正在尝试使用的技术。
想要增加我的2美分,因为上面关于linking
容器的讨论现在是过时的,因为它是一个不推荐的function。 所以这里有一个解释如何使用networks
来做到这一点。 这个答案是使用Docker Compose
和nginxconfiguration将nginx设置为静态分页网站的反向代理的完整示例。
TL; DR;
将需要相互通话的服务添加到预定义的networking中。 对于Dockernetworking的逐步讨论,我在这里学到了一些东西: https : //technologyconversations.com/2016/04/25/docker-networking-and-dns-the-good-the-bad-and-在丑/
定义networking
首先,我们需要一个所有后端服务可以与之通话的networking。 我打电话给我的web
但它可以是任何你想要的。
docker network create web
build立应用程序
我们只是做一个简单的网站应用程序。 该网站是一个简单的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应该在你的容器内工作。
build立代理
Nginx反向代理:
Dockerfile
FROM nginx RUN rm /etc/nginx/conf.d/*
我们重置所有的虚拟主机configuration,因为我们要定制它。
泊坞窗,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
假设没有问题,那么你有两个运行的容器可以用他们的名字相互交谈。 我们来testing一下。
docker exec -it nginx-proxy bash ping sample-site ping nginx-proxy
设置虚拟主机
最后的细节是设置虚拟主机文件,以便代理可以根据您想要设置您的匹配来指导stream量:
sample-site.conf用于我们的虚拟主机configuration:
server { listen 80; listen [::]:80; server_name my.domain.com; location / { proxy_pass http://sample-site; } }
根据代理如何设置,您需要将此文件存储在我们通过docker-compose
文件中的volumes
声明装载的本地conf.d
文件夹下。
最后但并非最不重要的,告诉nginx重新加载它的configuration。
docker exec nginx-proxy service nginx reload
这些顺序的步骤是我痛苦的502 Bad Gateway错误,并且第一次学习nginx,因为我的大部分经验都是使用Apache的,所以我要经历几个小时的冲击。
这个答案是演示如何杀死由于容器不能相互通信而导致的502错误网关错误。
我希望这个答案可以节省几个小时的痛苦,因为让容器相互交谈真的很难找出来,尽pipe这是我期望的一个明显的用例。 但是再一次,我愚蠢。 请让我知道如何改善这种方法。
刚刚find一篇来自Anand Mani Sankar的文章 ,展示了一个使用docker composer的nginx上游代理的简单方法。
基本上,必须在docker-compose文件中configuration实例链接和端口,并相应地在nginx.conf中更新上游。