O Dockerfile que o Rails te deu

Prévia grátis 5 min de leitura

O rails new escreve um Dockerfile. A maioria dos desenvolvedores nunca abre esse arquivo, faz deploy com Kamal, e tudo funciona. É um bom desfecho e também uma oportunidade perdida: o arquivo é uma aula compacta sobre containers em produção, e já está no seu repositório.

Este app tem um. As primeiras linhas dizem para que ele serve:

# syntax=docker/dockerfile:1
# check=error=true

# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
# docker build -t newbie_on_rails .
# docker run -d -p 80:80 -e RAILS_MASTER_KEY=<value from config/master.key> --name newbie_on_rails newbie_on_rails

Antes de tudo vêm duas diretivas. # syntax=docker/dockerfile:1 escolhe o frontend atual de Dockerfile em vez do que vier com o daemon, e é isso que libera os recursos modernos da aula 4. # check=error=true transforma avisos de lint da build em falhas: um erro interrompe a build em vez de passar despercebido no meio do log.

O vocabulário, num parágrafo

Uma imagem é um sistema de arquivos mais os metadados que dizem como iniciar um processo. É um artefato de build: imutável e identificado por um digest. Um container é uma imagem em execução. Uma camada é o diff de sistema de arquivos que uma instrução de build produz. Imagens são pilhas de camadas, que podem ser compartilhadas entre imagens e reaproveitadas em cache entre builds.

As aulas 2 e 4 cabem nessas duas últimas frases. Quase toda otimização de Dockerfile é, no fundo, uma decisão sobre camadas.

Lendo a build deste app

O arquivo tem três linhas FROM, o que significa três estágios:

ARG RUBY_VERSION=4.0.1
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
...
FROM base AS build
...
FROM base

O estágio base instala o que o app precisa para rodar (curl, libjemalloc2, libvips, postgresql-client) e define as variáveis de ambiente de produção.

O estágio build acrescenta o que é preciso para compilar: build-essential, git, libpq-dev, libyaml-dev, pkg-config. Ele instala as gems, copia o app e precompila os assets.

O estágio final parte de base de novo, e não de build, e copia apenas os artefatos prontos:

COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build /rails /rails

Um compilador C, o cache de gems e o ferramental de build nunca chegam à imagem entregue. Isso é uma build multi-estágio, e a aula 3 mostra por que ela importa tanto.

Escolhas que merecem atenção

Quatro linhas desse arquivo guardam decisões que, sem elas, você só aprenderia apanhando.

ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development"

BUNDLE_DEPLOYMENT="1" faz o bundler se recusar a atualizar o Gemfile.lock. A imagem sai do lockfile que você commitou, ou a build falha. O bundler nunca resolve outra coisa em silêncio.

RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile

A precompilação de assets inicializa o app, e a inicialização normalmente exige um secret. SECRET_KEY_BASE_DUMMY=1 fornece um secret descartável, então a master key real nunca é necessária durante a build. Ela nunca aparece num argumento de build, numa camada ou num log de CI.

RUN groupadd --system --gid 1000 rails && \
    useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER 1000:1000

O container roda como usuário não root, e só os quatro diretórios que precisam ser graváveis são entregues a ele. Todo o resto fica somente leitura para o processo do app.

EXPOSE 80
CMD ["./bin/thrust", "./bin/rails", "server"]

O comando padrão sobe o servidor atrás do Thruster, que está no Gemfile deste app. O Thruster fica na frente do Puma e cuida de cache HTTP, compressão e aceleração X-Sendfile.

Para que serve este curso

O arquivo gerado é bom. Mesmo assim, um dia você vai precisar mudá-lo: um pacote de sistema que uma gem exige, um passo de build para algo que o Rails não conhece, outra imagem base, uma build de CI mais rápida. Quem mexe num Dockerfile sem entendê-lo transforma uma edição de cinco minutos numa imagem de dois gigabytes com a master key gravada na camada quatro.

  1. O Dockerfile que o Rails te deu (você está aqui).
  2. Camadas e o cache de build.
  3. Builds multi-estágio, e o que é descartado.
  4. Deixando as builds rápidas.
  5. Deixando as imagens pequenas e seguras.
  6. O que acontece em tempo de execução.
  7. Juntando tudo: um Dockerfile customizado.

Abra o Dockerfile deste repositório enquanto lê. Todo exemplo deste curso é aquele arquivo.

Pontos-chave

  • O rails new gera um Dockerfile de produção; ele é uma aula compacta sobre builds de container, no seu repositório.
  • Uma imagem é uma pilha de camadas, um container é uma instância em execução, e uma camada é o diff de uma instrução. Quase toda otimização é sobre camadas.
  • O arquivo deste app tem três estágios: base para pacotes de runtime, build para compilação, e um final que copia só os artefatos prontos sobre o base.
  • BUNDLE_DEPLOYMENT=1 constrói estritamente a partir do lockfile commitado, e SECRET_KEY_BASE_DUMMY=1 mantém a master key real inteiramente fora da build.
  • A imagem roda como usuário não root, com apenas db, log, storage e tmp graváveis.
Aula 1 de 7 · grátis

Continue com a aula 2

Camadas e o cache de build

Como cada instrução vira uma camada em cache, o que a invalida e por que a ordem importa.

Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.

Digite para buscar...