2017 07 07 Rails On Docker Getting Started Docker Ruby Rails
2017 07 07 Rails On Docker Getting Started Docker Ruby Rails
Chris Blunt
Rails on Docker: Getting Started with Docker and Ruby on Rails
Version 2.0 (2017-09-09)
Table Of Contents
Table Of Contents 1
Introduction 2
PreRequisites 2
Database Configuration 2
Create a Dockerfile 4
Run a Database Container 8
Using SingleTask Containers 9
Using Docker Compose 12
Creating a Compose File 13
Starting Services 15
Running Single-Task Containers with Compose 17
Stopping and Tidying Up Containers 19
Use Docker Compose in your Apps 21
Next Steps 21
Introduction
Docker is a fantastic tool for isolating your app and its environment, and
allows easy distribution and state-replication across multiple environments
(dev, test, beta, prod, etc.). Using Docker can get rid of the it works on my
machine problem, and help you to easily scale your app as it grows.
In this tutorial, youll learn how to take a basic Rails app and prepare it for
use in a Docker container ( dockerise it).
PreRequisites
For this tutorial, Im using a simple Rails 5 application configured to use a
PostgreSQL database. If you use a different database, youll need to tweak a
few of the files below.
You can use the following template to create a basic Rails application that is
configured with a Dockerfile and config/database.yml as below:
Database Configuration
We can make use of environment variables to configure the details for our
apps database. Youll use this later so that your apps docker container can
connect to a PostgreSQL container.
(Note: You dont need to do this if youve used the application template
above)
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: db
username: <%= ENV.fetch('POSTGRES_USER') %>
password: <%= ENV.fetch('POSTGRES_PASSWORD') %>
development:
<<: *default
database: my-app_development
test:
<<: *default
database: my-app_test
production:
<<: *default
database: my-app_production
Create a Dockerfile
With your app prepared, its time to start using Docker. Lets start by creating
a Dockerfile . This is a plain text file that instructs Docker how to build an
image for your application.
To keep your image size small, I prefer to use the alpine-linux Ruby base
image. Alpine linux is a tiny linux distribution thats perfect for containers,
and Docker provides a default ruby:alpine base image that we can use.
Lets start by creating a basic Dockerfile for your rails app. In your apps
folder, create the following Dockerfile .
(Note: You dont need to do this if youve used the application template
above)
Rails on Docker: Getting Started with Docker and Ruby on Rails
# /path/to/app/Dockerfile
FROM ruby:2.3-alpine
RUN apk add --virtual build-deps build-base openssl-dev postgresql-dev libc-dev linux-headers l
ibxml2-dev libxslt-dev readline-dev && \
bundle install --jobs=2 && \
apk del build-deps
If youre using a different database server (e.g. MySQL), youll need to tweak
the Dockerfile to install the appropriate packages.
You can search for the correct package(s) using the following Docker
command:
Rails on Docker: Getting Started with Docker and Ruby on Rails
$ docker run --rm -it ruby:2.3-alpine apk search --update mysql | sort
...
mariadb-client-libs-10.1.22-r0
mariadb-dev-10.1.22-r0
mariadb-libs-10.1.22-r0
mysql-10.1.22-r0
mysql-bench-10.1.22-r0
...
With your Dockerfile written, you can now instruct Docker to build an image
for your app:
Once the image is built, were ready to get started! You can spin up a new
container based on your apps image using the following command:
-it is actually two arguments that allow you to interact with your
container via the shell (e.g. to issue Ctrl+C commands).
--env allows you to pass environment variables into the container.
Here, youre using it to set the database connection values.
--rm will instruct Docker to remove the container once it nishes (i.e.
when you hit Ctrl+C).
--publish forwards port 3000 from the container to port 3000 on your
host. This allows you to access the container as if it was running on
your host (i.e. http://localhost:3000 ).
Finally --volume instructs Docker to mount the current folder (from
your host machine) into the container. This means as you edit code on
your workstation, it will be available to the container. Without this,
youd need to recreate the container with every code change you
make.
Once its up and running, you can access your container by opening
localhost:3000 in your browser.
Rails on Docker: Getting Started with Docker and Ruby on Rails
could not translate host name db to address: Name does not resolve
At the moment, there isnt a PostgreSQL server available for the app too
connect. Well fix that now by spinning up a separate Docker container in
which PostgreSQL will run:
In our case, well use two containers: one for our app, and one for our
database (PostgreSQL).
Hit Ctrl+C to stop (and remove) your running app container, then spin up a
new container for PostgreSQL:
$ docker run -d -it --env POSTGRES_PASSWORD=superSecret123 --env DB_NAME=my-app_dev
elopment --name mydbcontainer postgres:9.6
The -d flag will detach the container from our terminal, allowing it to run in
the background. We also give the container a name ( mydbcontainer ) which
well use below.
This makes them perfect for running one-off tasks, such as rails commands
(e.g. bin/rails db:setup ).
Use the following command to spin up a copy of your apps container, run
the bin/rails db:setup task, and then shut down.
Note that youll need to configure environment variables for the database
connection (these are injected into the config/database.yml file you edited
earlier).
Youll also use the --link option which allows the container to connect to
PostgreSQL container that is running ( mydbcontainer ), using the hostname
db :
The --rm flag will remove (delete) the container once it has finished running.
Once that command has run, your apps database will be setup on the
mydbcontainer container. Finally, we can run our app!
Lets spin up a new container using our apps image. Notice that there are a
couple of additional arguments when running the command:
Open your browser to localhost:3000, and you should see your app running
entirely on Docker!
Keep Your Rails Apps
Healthy
Now your Rails apps are running in Docker, learn how to keep them
healthy: fast, secure, optimised and monitored.
Thankfully, Docker comes with a great solution to this problem in the form of
Docker Compose.
Need the code? If you dont have the Rails application code from last time,
you can create it as new Rails app using the following template:
$ cd my-app
$ docker build . -t my-app
$ docker run -d -it --env POSTGRES_PASSWORD=superSecret123 --env DB_NAME=my-app_dev
elopment --name mydbcontainer postgres:9.6
$ docker run --rm -it --env RAILS_ENV=development --env POSTGRES_USER=postgres --env PO
STGRES_PASSWORD=superSecret123 --publish 3000:3000 --volume ${PWD}:/app --link mydbco
ntainer:db my-app
services:
db:
image: postgres:9.6
environment:
- POSTGRES_PASSWORD=superSecret123
- DB_NAME=my-app_development
web:
build: .
environment:
- RAILS_ENV=development
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=superSecret123
ports:
- '3000:3000'
volumes:
- .:/app
links:
- db
Notice that while the db container uses the postgres:9.6 image, the web
container (which is our app) will build a new docker image from the existing
Dockerfile in your apps folder.
You can see that the YAML file reflects the command-line arguments we
previously supplied to Docker. However, it is much easier to read and
manage!
For this tutorial, well only be using one container per service so you can think
of each service as a single container.
Starting Services
Next well use Docker Compose to create and start the containers (services)
specified in the docker-compose.yml file. Compose will automatically
download or build any images it needs to create each container:
$ docker-compose up -d
Creating network "myapp_default" with the default driver
Creating myapp_db_1 ... done
Creating myapp_web_1 ... done
Once your image is built, Compose will create and start the containers
necessary for your app to run. The -d flag detaches your console allowing
the containers to run in the background. You can tail the log messages from
the containers just like you would with docker, but using the docker-
compose command instead.
Compose will tail the logs for all of the containers specified in the docker-
compose.yml file:
$ docker-compose logs -f
Attaching to myapp_web_1, myapp_db_1
web_1 | Puma starting in single mode...
...
web_1 | * Listening on tcp://0.0.0.0:3000
web_1 | Use Ctrl-C to stop
...
db_1 | LOG: database system was shut down at 2017-09-09 11:20:25 UTC
db_1 | LOG: MultiXact member wraparound protections are now enabled
db_1 | LOG: database system is ready to accept connections
db_1 | LOG: autovacuum launcher started
Hit [Ctrl-C] to stop tailing the logs. You can also show or tail logs from just one
of the containers by using the name given to it in docker-compose.yml . For
example, to tail only our apps logs:
Hit [Ctrl-C] to exit the logs (note if you omit the -f flag, the logs command will
automatically exit)
You can also see the app is running by opening your browser and visiting
localhost:3000.
Running Single-Task Containers
with Compose
Just as before, your app will throw a missing database error. This is because
the container that was created by Compose is a completely separate instance
from the previous container (remember that containers are ephemeral. As
soon as they are removed, any data within them is lost).
To create and migrate our database, use the docker-compose run command
(again, this is nearly identical to the standard docker run command:
$ docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------
myapp_db_1 docker-entrypoint.sh postgres Up 5432/tcp
myapp_web_1 bundle exec puma -C config ... Up 0.0.0.0:3000->3000/tcp
Now if you revisit localhost:3000, youll see the familiar Posts screen.
Stopping and Tidying Up
Containers
Compose takes care of tidying up containers for us just as easily as creating
them. You can stop and start containers in the normal way. This will stop but
not remove them, allowing them to be restarted with data intact:
$ docker-compose stop
Stopping myapp_web_1 ... done
Stopping myapp_db_1 ... done
# Visiting localhost:3000 will now show a connection refused error as the containers are not runni
ng.
$ docker-compose start
Starting db ... done
Starting web ... done
To stop and completely remove the containers (including all their data), use
the docker-compose down command:
$ docker-compose down
Stopping myapp_web_1 ... done
Stopping myapp_db_1 ... done
Removing myapp_web_1 ... done
Removing myapp_db_1 ... done
Removing network myapp_default
(Note that you can also stop and remove individual service containers using,
for example, docker-compose stop web and `docker-compose rm web}).
Usin g docker-compose down will also remove any other unnecessary
resources that were created, such as the virtual network (in this case
myapp_default ). Data volumes (which weve not used here) are not removed
by default, allowing you to persist data between runs.
Use Docker Compose in your
Apps
Compose is a great tool, and forms the basis of more advanced Docker tools
(such as configuring stacks for Docker Swarm orchestration).
You can see how using Docker Compose greatly simplifies the configuration
of your containers. By specifying the various services necessary for your app
in a single place, it is easy to create disposable instances of your app for
development, and share configuration with other developers.
Next Steps
Thanks for reading this book. I hope youve found it useful and you can begin
using Docker and Docker Compose in your own projects. Let me know how
you get on by getting in touch.
Keep Your Rails Apps
Healthy
Now your Rails apps are running in Docker, learn how to keep them
healthy: fast, secure, optimised and monitored.