Vous êtes sur la page 1sur 23

Getting Started with

Docker and Ruby on


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.

Docker is particularly great when your app has a lot of dependencies, or


requires specific versions of libraries and tools to be configured.

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:

$ rails new --database=postgresql --skip-bundle --template=https://gist.githubusercontent.com/


cblunt/1d3b0c1829875e3889d50c27eb233ebe/raw/01456b8ad4e0da20389b0b91dfec8b272a14a
635/rails-docker-pg-template.rb my-app
$ cd my-app

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.

Edit your config/database.yml configuration


Getting Started with Docker and Ruby on Rails

(Note: You dont need to do this if youve used the application template
above)

Update your apps config/database.yml to use environment variables:

# 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.

You use it to install dependencies, configure default environment variables,


copy code into the container, and so on.

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.

Write Your Dockerfile

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

# Set local timezone


RUN apk add --update tzdata && \
cp /usr/share/zoneinfo/Europe/London /etc/localtime && \
echo "Europe/London" > /etc/timezone

# Install your app's runtime dependencies in the container


RUN apk add --update --virtual runtime-deps postgresql-client nodejs libffi-dev readline sqlite

# Bundle into the temp directory


WORKDIR /tmp
ADD Gemfile* ./

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

# Copy the app's code into the container


ENV APP_HOME /app
COPY . $APP_HOME
WORKDIR $APP_HOME

# Configure production environment variables


ENV RAILS_ENV=production \
RACK_ENV=production

# Expose port 3000 from the container


EXPOSE 3000

# Run puma server by default


CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

What if I dont use PostgreSQL?

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:

Build an image for your app

$ docker build . -t my-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:

$ docker run --rm -it --env RAILS_ENV=development --env POSTGRES_USER=postgres --env PO


STGRES_PASSWORD=superSecret123 --publish 3000:3000 --volume ${PWD}:/app my-app

Were passing a few arguments to the Docker run command:


Rails on Docker: Getting Started with Docker and Ruby on Rails

-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

Run a Database Container


Unfortunately, although the container runs successfully, if you try to access
the app in your browser, it will crash with a database error.

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:

ProTip Remember that in Docker, a container should be designed to do one


thing (and one thing only).

In our case, well use two containers: one for our app, and one for our
database (PostgreSQL).

Start a new container running 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.

Using SingleTask Containers


Docker containers are disposable, and their single-purpose nature means
that once they have finished, they are stopped (and, optionally, removed).

This makes them perfect for running one-off tasks, such as rails commands
(e.g. bin/rails db:setup ).

Well do that now to setup your apps database on mydbcontainer :

Run the rails db:migrate task using a container:

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 :

$ docker run --rm --env RAILS_ENV=development --env POSTGRES_USER=postgres --env POST


GRES_PASSWORD=superSecret123 --link mydbcontainer:db --volume ${PWD}:/app my-app bin/
rails db:create db:migrate

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!

Run your app!

Lets spin up a new container using our apps image. Notice that there are a
couple of additional arguments when running the command:

$ 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

=> Puma starting in single mode...


=> * Version 3.8.2 (ruby 2.4.1-p111), codename: Sassy Salamander
=> * Min threads: 5, max threads: 5
=> * Environment: development
=> * Listening on tcp://0.0.0.0:3000
=> Use Ctrl-C to stop

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.

In this free 7-lesson course, you'll learn to use proven techniques,


tools and systems to keep your Ruby on Rails web apps healthy and
delivering a great experience to your users.

You'll learn how to optimise your app's content, automatically monitor


and audit your codebase, keep gems and dependencies up-to-date,
and more.

This free 7-lesson course is delivered to your inbox. Get started


right now by clicking the button below.

Get the First Lesson Now


Using Docker Compose
As you work on your app, it often becomes difficult (and tiresome) to
remember all the different configurations and arguments you need to create
and link containers. The complexity increases as your apps dependencies
grow (such as when you need to using Sidekiq and to handle background
jobs).

Thankfully, Docker comes with a great solution to this problem in the form of
Docker Compose.

Compose allows you to declare your apps requirements and configuration a


simple yaml file. From this file, Docker will create, configure and start the
containers. You can also use Compose to specify data volumes for your
containers, and connect containers across an isolated virtual network.
In this tutorial, well take the simple Rails app from last time and use
Compose to replicate the container setup. This will make creating an
instance of our Rails app much faster, and allow configurationt to be easily
shared between developers.

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:

$ rails new --database=postgresql --skip-bundle --template=https://gist.githubusercontent.com/


cblunt/1d3b0c1829875e3889d50c27eb233ebe/raw/01456b8ad4e0da20389b0b91dfec8b272a14a
635/rails-docker-pg-template.rb my-app

Creating a Compose File


The simple rails app we built makes use of two containers: one for the app
itself and one for the PostgreSQL database. To run them, we created and
linked two containers using the following commands:

$ 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

Well recreate this setup in a new docker-compose.yml file:


version: '3'

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!

ProTip: What are Services?


Rather than specify each individual container, in Compose we specify
services. Each service ( db , web in the file above) can be thought of as
representing one or more containers that running a particular Docker image.
Services allow us to create any number of identical containers running the
same application. This is important when we come to scale services (i.e. run 2
or more copies of our Rails application and load-balance between them).

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:

$ docker-compose logs -f web


web_1 | Puma starting in single mode...
...
web_1 | * Listening on tcp://0.0.0.0:3000
web_1 | Use Ctrl-C to stop

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).

As in the previous tutorial, well need to run a database migration task to


create our apps database inside the db container. Just like normal Docker,
we can use Compose to run create, use and remove a single-task container.
Compose will take care of creating, configuring and running any dependent
containers (e.g. the database container) as specified in the docker-
compose.yml file before running the task.

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 run --rm web bin/rails db:create db:migrate


Starting myapp_db_1 ... done
Created database 'my-app_development'
Created database 'my-app_test'
== 20170909110604 CreatePosts: migrating ======================================
-- create_table(:posts)
-> 0.0221s
== 20170909110604 CreatePosts: migrated (0.0223s) =============================
Specifying the --rm flag tells Compose to remove the container once the task
has finished. Notice that although we used the web specification from our
docker-compose.yml file, Compose created, used and removed a completely
separate container to run this task. The previous instance of our web
container is still running:

$ 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.

Adding a docker-compose.yml file to your repository is a great way to make


getting started with your app as easy as possible. Often, your README file
for people to get started using your app can be as simple as:

$ git clone https://github.com/your-app.git


$ cd your-app
$ docker-compose run --rm web bin/rails db:setup
$ docker-compose up -d
$ open http://localhost:3000/

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.

In this free 7-lesson course, you'll learn to use proven techniques,


tools and systems to keep your Ruby on Rails web apps healthy and
delivering a great experience to your users.

You'll learn how to optimise your app's content, automatically monitor


and audit your codebase, keep gems and dependencies up-to-date,
and more.

This free 7-lesson course is delivered to your inbox. Get started


right now by clicking the button below.

Get the First Lesson Now

Vous aimerez peut-être aussi