Blog Post

...

Docker compose: combine two applications under a single nginx gateway

Docker compose is a really handy tool in the world of DevOps, it allows to combine multiple Docker containers within a single monolithic app. And moreover it’s possible to have a bunch of such apps hosted within a server. It sounds like a cool feature, but what if we would like to share services between some of these apps or even more: have a single gateway that relays all incoming internet communications to the appropriate app? In this post we’ll see how to handle such situation.

Let’s assume that we have two docker-compose apps:

  1. one of them mailu – an smtp server, consisting of postfix, dovecot, antispam etc having a nginx server on top of them in order to relay incoming request from various mail-clients. Nginx gateway called front there:
  2. services:
      # Core services
      front:
        image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-1.9}
        restart: always
        env_file: mailu.env
        logging:
          driver: json-file
        ports:
          - "11.220.171.213:587:587"
          - "11.220.171.213:143:143"
        volumes:
          - "/mailu/certs:/certs"
          - "/mailu/overrides/nginx:/overrides:ro"
      smtp:
        image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}postfix:${MAILU_VERSION:-1.9}
        depends_on:
          - front  
  3. another app is a Tomcat application container. It includes mysql service (db), tomcat itself (tomcat) and nginx (nginx) gateway – we do need our app to be available online through SSL:
  4. services:
      db:
        image: mariadb
        volumes:
          - /opt/test:/var/lib/mysql
        ports:
          - 3306:3306
      tomcat:
        depends_on:
          - db
        image: tomcat:7.0.70
        volumes:
          - ./tomcat/lib/mail.jar:/usr/local/tomcat/lib/mail.jar
          - ./tomcat/webapps:/usr/local/tomcat/webapps
          - ./tomcat/logs:/usr/local/tomcat/logs
          - ./tomcat/mediafiles:/opt/mediafiles
        ports:
          - '8080:8080'
      nginx:
        image: nginx
        depends_on:
          - tomcat
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - ./nginx:/etc/nginx
          - /mailu/certs:/etc/nginx/certs  

As we see from our yaml compose configs – our apps use different port numbers exposed through SSL. It’s okay to have one nginx server that routes requests to mail ports (e.g., 143, 587) and also another one that routes requests to the web app exposed on port 443 (SSL) and 80. But what if our mailu server has web interface enabled? For example it’s possible to turn on roundcube or admin interface there which should be accessible on 443 and 80 as well, in this case we’ll get a collision. The best way to overcome this issue is to assume that tomcat’s app is the primary net that will pass mailu 80/443 requests to the “mailu” subnet. As a first step we should add tomcat nginx <-> maily nginx mapping to mailu yaml:

ports:
  - "127.0.0.1:9080:80"
  - "127.0.0.1:9443:443"

Here we expose inner docker port 80 and 443 to the outside port 9080 and 9443, it allows us to avoid interference with the nginx 80 and 443 port of the main app.

Then we need to pass mailu front service to the nginx of the main net. To make this work, we see that network name of the mailu within the yaml is “default”, persist right at the bottom:

networks:
  default:
    driver: bridge

And let’s connect containers from the main network, called “facet”, to the mailu one. Since the mailu app is the name of the folder it’s located in, i.e. mailu, to access its network we should use %APP_NAME%_%NETWORK_NAME% in Tomcat’s yaml:

networks:
  mailu_default:
    external: true
  # current app network name
  facet:
    driver: bridge

Also need to pass front service to the nginx of the main app, it can be done though the network section:

nginx:
  image: nginx
  depends_on:
    - tomcat
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - ./nginx:/etc/nginx
    - /mailu/certs:/etc/nginx/certs      
  links:
    - tomcat:tomcat
  # pass "front" to the main nginx
  networks:
    - mailu_default

Now we can refer to the mailu service in Tomcat’s app, really great, nginx.conf:

server {
  listen 443 ssl;
  server_name soft29.info;
  ssl_certificate_key /etc/nginx/certs/key.pem;	  
  ssl_certificate /etc/nginx/certs/cert.pem;
  # note mailu pages: admin, webmail etc
  location ~* ^/(admin|sso|static|webdav|webmail|(apple\.)?mobileconfig|(\.well\-known/autoconfig/)?mail/|Autodiscover/Autodiscover) {
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
        # "front" refers to mailu service
	proxy_pass http://front;
  }
}

Now all SSL requests to mailu subpages will be redirected to mailu nginx.

Since the network section was added to the compose config, current app’s services are not available to the combined network, they should be added to it. For example, db service which is required to tomcat app should be exposed to network, plus the tomcat service itself should be available:

db:
  image: mariadb
  volumes:
    - /opt/test:/var/lib/mysql
  ports:
    - 3306:3306
  # expose db to the network
  networks:
    - facet   
tomcat:
  depends_on:
    - db
  image: tomcat:7.0.70
  # expose tomcat to the network	
  networks:
    - facet 

MySql url is defined within the tomcat’s webapp in this way (db is the name of the service there):

database.jdbc.connectionURL=jdbc:mariadb://db/jroller?autoReconnect=true&useUnicode=true&characterEncoding=utf-8

And the Tomcat is passed to the main nginx server through this nginx.conf section:

  location / {
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
        # "tomcat" is the name of the service
	proxy_pass http://tomcat:8080;
  }

In this was all request to the SSL port of the root “/” will be passed to and handled by the Tomcat.

To summarize: in this article we’ve seen how to implement a very common use case when a couple of docker compose apps should be exposed within a single server and also saw how to handle port routings. I hope you enjoyed it, thanks!

Comments (0)

Tags: docker


0 comments

Leave a Comment