Construindo uma imagem Docker para aplicações Rails

Continuando a série sobre Docker, hoje veremos como construir uma imagem Docker preparada para executar aplicações Rails. Se você ainda não viu o post anterior sobre os primeiros passos com Docker, acesse e veja:http://infoslack.com/linux/docker-primeiros-passos/.

Bem antes de continuar, tenha em mente que o Dockerfile que será criado, vai gerar a imagem base para a construção de containers direto no servidor a aplicação Rails poderá ser hospedada, pois quero que minha imagem e as informações de configuração relacionadas a app sejam privadas, apesar de podermos criar um registro privado no https://registry.hub.docker.com/, farei da maneira mais simples neste post ;)

Dockerfile

Para começar, no host teremos um diretório com o Dockerfile e alguns arquivos de configuração do Nginx que serão utilizados na construção da imagem. No arquivo Dockerfile teremos as instruções base para instalar o Nginx e o Ruby:

FROM ubuntu:trusty

# Update the repository
RUN apt-get update

# Install necessary tools
RUN apt-get install -y wget net-tools build-essential git

# Setup Install Nginx
RUN wget -q -O - http://nginx.org/keys/nginx_signing.key | apt-key add -
RUN echo 'deb http://nginx.org/packages/ubuntu/ trusty nginx' | tee /etc/apt/sources.list.d/nginx.list

# Setup Install Ruby
RUN wget -q -O - http://apt.hellobits.com/hellobits.key | apt-key add -
RUN echo 'deb [arch=amd64] http://apt.hellobits.com/ trusty main' | tee /etc/apt/sources.list.d/hellobits.list

# Install Nginx and Ruby
RUN apt-get update
RUN apt-get install -y nginx ruby-2.1

# Install Bundler
RUN gem install bundler

# Copy Nginx files
ADD nginx.conf /etc/nginx/nginx.conf
ADD myapp.conf /etc/nginx/sites/myapp.conf

A novidade no Dockerfile é o ADD que copia arquivos locais no host para o PATH na imagem que será preparada. Com esse modelo já podemos criar nossa imagem e testar:

$ docker build -t docker_on_rails .

O nome dado a imagem foi docker_on_rails, se verificarmos as imagens disponíveis veremos a base que é o Ubuntu 14.04 (trusty) e a nossa imagem:

$ docker images

REPOSITORY        TAG      IMAGE ID       CREATED        VIRTUAL SIZE
docker_on_rails   latest   7f947af605e8   3 minutes ago  385.1 MB
ubuntu            trusty   6b4e8a7373fe   6 days ago     194.8 MB

Podemos conferir nossa instalação criando um container apartir da nossa imagem, para isso:

$ docker run --rm -i -t docker_on_rails /bin/bash
root@3cefe7708ace:/# ruby -v
ruby 2.1.3p242 (2014-09-19 revision 47630) [x86_64-linux]
root@3cefe7708ace:/# nginx -v
nginx version: nginx/1.6.2
root@3cefe7708ace:/#

Tudo foi instalado corretamente.

Executando o container

Com base na imagem criada, poderiamos inicializar um container em background:

$ docker run -d -p 80:80 docker_on_rails

Porém teriamos que passar instruções ainda para inicializar o nginx, em vez disso vamos melhorar o Dockerfile para que ele faça o mapeamento da porta 80 e possa inicializar o Nginx sempre que um container for criado:

...
# Ports
EXPOSE 80

# Start nginx
CMD ["/usr/sbin/nginx","-c","/etc/nginx/nginx.conf","-g","daemon off;"]

Para que essa alteração possa valer, precisamos atualizar nossa imagem:

$ docker build -t docker_on_rails .

E para o container conseguir ler os arquivos da aplicação rails que vai ficar sempre disponível no host, podemos utilizar a diretiva WORKDIR no Dockerfile:

...
# set workdir
WORKDIR /home/ubuntu/my_app

# Ports
EXPOSE 80
...

Podemos aproveitar e informar no Dockerfile onde ele deverá replicar os arquivos lidos em WORKDIR, como as regras do Nginx estão apontando para /var/www, a configuração poderia ser assim:

...
# set workdir
WORKDIR /home/ubuntu/my_app
ADD . /var/www/my_app/
...

Depois de gerar a imagem novamente e acessar o container é possível verificar que os arquivos relacionados a aplicação rails estão sendo exibidos em /var/www/my_app:

$ docker build -t docker_on_rails .
$ docker run --rm -i -t docker_on_rails /bin/bash
$ ll /var/www/my_app/
total 84
drwxr-xr-x 12 root     root     4096 Oct  8 04:45 ./
drwxrwxr-x  3 www-data www-data 4096 Oct  8 04:45 ../
-rw-r--r--  1 root     root      466 Oct  8 04:22 .gitignore
-rw-r--r--  1 root     root      951 Oct  8 04:45 Dockerfile
-rw-r--r--  1 root     root     1339 Oct  8 04:22 Gemfile
-rw-r--r--  1 root     root     2871 Oct  8 04:22 Gemfile.lock
-rw-r--r--  1 root     root      478 Oct  8 04:22 README.rdoc
-rw-r--r--  1 root     root      249 Oct  8 04:22 Rakefile
drwxr-xr-x  8 root     root     4096 Oct  8 04:22 app/
drwxr-xr-x  2 root     root     4096 Oct  8 04:22 bin/
drwxr-xr-x  5 root     root     4096 Oct  8 04:22 config/
-rw-r--r--  1 root     root      154 Oct  8 04:22 config.ru
drwxr-xr-x  2 root     root     4096 Oct  8 04:22 db/
drwxr-xr-x  4 root     root     4096 Oct  8 04:22 lib/
drwxr-xr-x  2 root     root     4096 Oct  8 04:22 log/
-rw-r--r--  1 root     root     1006 Oct  8 04:29 myapp.conf
-rw-r--r--  1 root     root     1331 Oct  8 04:29 nginx.conf
drwxr-xr-x  2 root     root     4096 Oct  8 04:22 public/
drwxr-xr-x  8 root     root     4096 Oct  8 04:22 test/
drwxr-xr-x  6 root     root     4096 Oct  8 04:22 tmp/
drwxr-xr-x  3 root     root     4096 Oct  8 04:22 vendor/

Para testar a nova imagem e a aplicação funcionando, basta criar um novo container:

$ docker run -p 80:80 -i -t docker_on_rails /bin/bash
$ cd /var/www/my_app/
$ bundle install
$ bundle exec unicorn -c config/unicorn.rb

Rails app run Docker

Conclusão

Todo o comportamento dos containers pode ser preestabelecido na imagem que será utilizada, ou seja, tarefas como bundle install, scripts de upstart para o Unicorn ou para a própria aplicação podem ser adicionados ao Dockerfile.

Nos próximos posts sobre Docker tentarei abordar o uso da diretiva VOLUME, a instalação de um banco de dados e uma demonstração real de deploy.

Os arquivos utilizados neste post estão aqui: https://gist.github.com/infoslack/93b9e89ac97c9775880f

Happy Hacking ;)

Referências

Comentários