@ The answer of T0xicCode is correct, but I thought I would talk in detail about the details, as it actually took me about 20 hours to finally get a working solution.
If you want to run Nginx in your own container and use it as a reverse proxy to load multiple applications on the same server instance, then the steps you need to follow are as follows:
Tie your containers
When you docker run your containers, typically by entering a shell script in User Data , you can declare links to other running containers. This means that you need to start your containers in order, and only the last containers can communicate with the previous ones. For example:
So, in this example, the API container is not associated with any other, but the App container is associated with the API , and Nginx is associated with both the API and the App .
This results in changes to the env and /etc/hosts files that are in the API and App containers. The results look like this:
/ etc / hosts
Running cat /etc/hosts in your Nginx container will result in the following:
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
Running env in your Nginx container will result in the following:
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
I cut back many of the actual vars, but the above are the key values ββneeded for proxy traffic in your containers.
To get a shell to run the above commands in a running container, use the following:
sudo docker exec -i -t Nginx bash
You can see that now you have both the /etc/hosts and env vars entries that contain the local IP address for any of the containers associated with it. As far as I can tell, this is all that happens when you start containers with declared link options. But now you can use this information to configure Nginx in the Nginx container.
Configure Nginx
Here it gets a little complicated, and there are several options. You can configure your sites to point to the entry in the /etc/hosts that docker created, or you can use vars env and run a line replacement (I used sed ) on your nginx.conf and any other conf files that might be in your /etc/nginx/sites-enabled folder to insert IP values.
OPTION A: Configuring Nginx Using ENV Vars
This is the option I went with because I could not get the /etc/hosts version of the file to work with. I will try option B soon and update this entry with any results.
The key difference between this option and the /etc/hosts option is how you write your Dockerfile to use a shell script as a CMD argument, which in turn handles a replacement string to copy the IP address of the value from env to your conf file .
Here is the set of configuration files I got into:
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 {
NOTE. It is important that in nginx.conf daemon off; file daemon off; daemon off; turned on daemon off; so that your container does not exit immediately after launch.
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
I will leave everything to do homework about most of the contents of nginx.conf and api.myapp.conf .
The magic happens in Nginx-Startup.sh , where we use sed to replace the string with APP_IP , which we wrote in the upstream block of our api.myapp.conf and app.myapp.conf files.
This ask.ubuntu.com question explains this very nicely: Find and replace text in a file using commands
GOTCHA On OSX sed handles options differently, the -i flag. On Ubuntu, the -i flag will handle in-place replacements; This will open the file, change the text, and then save its file. In OSX, the -i flag requires the file extension to which you want to receive the received file. If you are working with a file that does not have an extension, you must enter '' as the value for the -i flag.
GOTCHA To use the ENV lines in a regular expression that sed uses to find the line you want to replace, you must wrap var in double quotes. Thus, the correct, albeit attractive, syntax is as described above.
So, docker launched our container and launched the Nginx-Startup.sh script, which used sed to change the APP_IP value to the corresponding env variable specified in the sed command. Now we have the conf files in our directory /etc/nginx/sites-enabled , which have IP addresses from env vars that are set when the container loads. In your api.myapp.conf file api.myapp.conf you will see that the upstream block has changed to this:
upstream api_upstream{ server 172.0.0.2:3000; }
The IP address you see may be different, but I noticed that it is usually 172.0.0.x
You should now have all the routing appropriately.
GOTCHA You cannot restart / restart all containers after starting the launch of the original instance. Docker provides each container with a new IP address at startup and does not seem to use it before. So api.myapp.com will get 172.0.0.2 the first time, and then will get 172.0.0.4 the next time. But Nginx already set the first IP address in its conf files or in the /etc/hosts , so it will not be able to determine the new IP for api.myapp.com . The solution to this is most likely to use CoreOS and its etcd service, which in my limited understanding acts as a common env for all machines registered in the same CoreOS cluster. This is the next toy I'm going to play with customization.
OPTION B: Use /etc/hosts File Entries
It should be a quick and easy way to do this, but I couldn't get it to work. Allegedly, you simply enter the value of the /etc/hosts entry into your api.myapp.conf and app.myapp.conf , but I could not get this method to work.
UPDATE: See @Wes Tod answer for instructions on how to make this method work.
Here is the attempt I made in api.myapp.conf :
upstream api_upstream{ server API:3000; }
Given that there is an entry in my /etc/hosts , for example: 172.0.0.2 API I realized that it would just pull the value, but it looks like it is not.
I also had some additional problems with my Elastic Load Balancer source from all AZs, so maybe this was a problem when I tried this route. Instead, I had to learn how to handle string replacement on Linux, so it was fun. I will give it a try after a while and see how this happens.