Our Nginx config file uses our CustomConfig convention which means it uses the Liquid template language developed by Shopify and used by many websites. There are many good resources on the web on how to use the Liquid syntax.
Today, I'm going through serving your docker services in a way that one container serves /
and the other one serves /app
This is a simple one to give you some idea as to how you can find your way. Once you start getting the hang of it, you can make more intricate configurations.
I'm going to demonstrate using a sample stack with two services: one is called web and the other one is called app. And I want the web to serve the root i.e. /
and the app one serve /app
.
I have tried to explain this via the nginx configuration file.
All the values like this {{ workers }} are the values that get swapped when you click on preview, so for instance if you have this line in your config:
{% for service_container in service_containers %}
You can try adding a comment like bellow
# {{ service_containers }}
and after clicking on the preview button you will see that has been expanded to its value. This is good for understanding the format.
Now back to my example:
This is my service file:
services:
web:
git_url: YOUR_WEB_GIT_REPOSITORY
git_branch: GIT_BRANCH
ports:
- container: 3000
http: 80
https: 443
command: bundle exec rails server -e _env:RAILS_ENV
build_command: /bin/sh -c "bundle exec rake db:schema:load"
deploy_command: /bin/sh -c "bundle exec rake db:migrate"
build_root: "."
env_vars:
RAILS_ENV: production
app:
git_url: YOUR_APP_GIT_REPOSITORY
git_branch: GIT_BRANCH
ports:
- container: 3000
http: 80
https: 443
command: bundle exec rails server -e _env:RAILS_ENV
build_root: "."
env_vars:
RAILS_ENV: production
databases:
- mysql
This is my Nginx config which includes the explanation, you may need to read it a few times to get familiar with.
Note:
I have taken out the parts that we don't touch to make it easier to follow
user nginx;
worker_processes {{ workers }};
error_log /var/log/nginx.log;
events {
worker_connections 1024;
accept_mutex off;
}
http {
root /etc/cloud66/webroot;
...
{% for service_container in service_containers %}
{% for upstream in service_container.upstreams %}
{% if upstream.port.http or upstream.port.https %}
upstream {{ upstream.name }} {
{% for private_ip in upstream.private_ips %}
server {{private_ip}}:{{upstream.port.container}};
{% endfor # upstream.private_ips %}
}
{% endif # upstream.port.http or upstream.port.https %}
{% endfor # service_container.upstreams %}
{% endfor # service_containers %}
{% if websocket_support == true %}
map $http_upgrade $connection_upgrade {
default Upgrade;
'' close;
}
{% endif %}
{% if cors_enabled == true %}
# Cross domain resource
...
{% endif # cors_enabled == true %}
#First capturing the app services upstream name and assign it to WEB_APP to use it later on in the config
# ==------BEGIN-USER-DEFINED-----==
{% for service_container in service_containers %}
{% if service_container.service_name == "app" %}
{% for upstream in service_container.upstreams %}
{% capture WEB_APP %}{{ upstream.name }}{% endcapture %}
{% endfor # service_container.upstreams %}
{% endfor # service_containers %}
# ------END-USER-DEFINED-----
{% for service_container in service_containers %}
# Prevent creating a server block for app service as it is going to be added as a location block for web instead
# ------BEGIN-USER-DEFINED-----
{% if service_container.service_name == "app" %}
{% continue # to skip the iteration when is for app %}
{% endif %}
# ------END-USER-DEFINED-----
{% for upstream in service_container.upstreams %}
{% if upstream.port.http != blank %}
server {
listen {{ upstream.port.http }};
...
{% if maintenance_mode_active and upstream.port.http == 80 %}
location / {
root /etc/cloud66/pages;
rewrite ^(.*)$ /cloud66_maintenance.html break;
}
{% else %}
location / {
{% if websocket_support == true %}
# Next three lines implement websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
{% endif %}
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://{{ upstream.name }};
break;
}
# Add this block only if the whole block belongs to web service.
# ------BEGIN-USER-DEFINED-----
{% if service_container.service_name == "web" %}
# This is the location added for app service
location /app {
{% if websocket_support == true %}
# Next three lines implement websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
{% endif %}
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
#Here WEB_APP is the app service captured upstream
proxy_pass http://{{ WEB_APP }};
break;
}
{% endif %}
# ------END-USER-DEFINED-----
{% endif %}
}
{% endif # if upstream.port.http != blank %}
{% if allow_ssl == true %}
{% if upstream.port.https != blank %}
server {
listen {{ upstream.port.https }};
ssl on;
...
{% if maintenance_mode_active and upstream.port.https == 443 %}
location / {
root /etc/cloud66/pages;
rewrite ^(.*)$ /cloud66_maintenance.html break;
}
{% else %}
location / {
{% if websocket_support == true %}
# Next three lines implement websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
{% endif %}
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
proxy_pass http://{{ upstream.name }};
break;
}
# Add this block only if the whole block belongs to web service.
# ------BEGIN-USER-DEFINED-----
{% if service_container.service_name == "web" %}
# This is the location added for app service
location / {
{% if websocket_support == true %}
# Next three lines implement websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
{% endif %}
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
#Here WEB_APP is the app service captured upstream
proxy_pass http://{{ WEB_APP }};
break;
}
{% endif %}
# ------END-USER-DEFINED-----
{% endif %}
}
{% endif # if upstream.port.https != blank %}
{% endif # if allow_ssl == true %}
{% endfor # service_container.upstreams %}
{% endfor # service_containers %}
}
...
}
I hope this help you understand what is happening in the config.
This is just a very simple example to showcase Cloud 66's Nginx config template.
Heads up:
If client use http://example.com to use your website in the example above the app container will receive http://example.com/app not http://example.com/
Happy coding with nginx and containers!