Using Docker in Vagrant for a Ghost blog behind NGINX
Vagrant in Docker can be used more usefully to simulate traditional setups such as an application behind a load balancer or a reverse proxy. We've already set up NGINX, so what about using it as a front reverse proxy with a blog engine such as Ghost behind it? We'll end up by showing how to do something similar with docker-compose.
Getting ready
To step through this recipe, you will need the following:
- A working Vagrant installation (no hypervisor needed)
- A working Docker installation and basic Docker knowledge
- An Internet connection
How to do it…
The previous example allows only one container to be launched simultaneously, which is sad considering the power of Docker. Let's define multiple containers and start by creating a front
container (our previous NGINX):
config.vm.define "front" do |front| front.vm.provider "docker" do |docker| docker.image = "nginx:stable" docker.ports = ['80:80'] docker.volumes = ["#{Dir.pwd}/src:/usr/share/nginx/html"] end end
Now how about creating an application container, maybe a blog engine such as Ghost? Ghost publishes a ready-to-use container on the Docker Hub, so let's use that (version 0.9.0 at the time of writing) and expose on TCP/8080 the application container listening on TCP/2368:
config.vm.define "app" do |app| app.vm.provider "docker" do |docker| docker.image = "ghost:0.9.0" docker.ports = ['8080:2368'] end end
Check if you can access the blog on http://localhost:8080
and NGINX on http://localhost
:
$ curl -IL http://localhost:8080 HTTP/1.1 200 OK X-Powered-By: Express […] $ curl -IL http://localhost HTTP/1.1 200 OK Server: nginx/1.10.1
Now let's use NGINX for what it's for—serving the application. Configuring NGINX as a reverse proxy is beyond the scope of this book, so just use the following simple configuration for the nginx.conf
file at the root of your working folder:
server { listen 80; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_pass http://app:2368; } }
Change the configuration of the front
container in Vagrant to use this configuration, remove the old index.html
as we're not using it anymore, and link this container to the app
container:
config.vm.define "front" do |front| front.vm.provider "docker" do |docker| docker.image = "nginx:stable" docker.ports = ['80:80'] docker.volumes = ["#{Dir.pwd}/nginx.conf:/etc/nginx/conf.d/default.conf"] docker.link("app:app") end end
Linking the app
container makes it available to the front
container, so now there's no need to expose the Ghost blog container directly, let's make it simpler and more secure behind the reverse proxy:
config.vm.define "app" do |app| app.vm.provider "docker" do |docker| docker.name = "app" docker.image = "ghost:0.9.0" end end
We're close! But this setup will eventually fail for a simple reason: our systems are too fast, and Vagrant parallelizes the startup of virtual machines by default, and also does this for containers. Containers start so fast that the app
container may not be ready for NGINX when it's started. To ensure sequential startup, use the VAGRANT_NO_PARALLEL
environment variable at the top of the Vagrantfile:
ENV['VAGRANT_NO_PARALLEL'] = 'true'
Now you can browse to http://localhost/admin
and start using your Ghost blog in a container, behind a NGINX reverse proxy container, with the whole thing managed by Vagrant!
There's more…
You can access the containers logs directly using Vagrant:
$ vagrant docker-logs --follow ==> app: > [email protected] start /usr/src/ghost ==> app: > node index ==> app: Migrations: Creating tables... […] ==> front: 172.17.0.1 - - [21/Aug/2016:10:55:08 +0000] "GET / HTTP/1.1" 200 1547 "-" "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:48.0) Gecko/20100101 Firefox/48.0" "-" ==> app: GET / 200 113.120 ms - - […]
A Docker Compose equivalent
Docker Compose is a tool to orchestrate multiple containers and manage Docker features from a single YAML file. So if you're more familiar with Docker Compose, or if you'd like to do something similar with this tool, here's what the code would look like in the docker-compose.yml
file:
version: '2' services: front: image: nginx:stable volumes: - "./nginx.conf:/etc/nginx/conf.d/default.conf" restart: always ports: - "80:80" depends_on: - app links: - app app: image: ghost:0.9.0 restart: always
Note
Remember that with Vagrant, you can mix virtual machines and Docker containers, while you can't with docker-compose.