A imagem alpine é derivada do projeto Alpine Linux e nos ajudará a manter nossa imagem com tamanho reduzido. Para obter mais informações sobre se a imagem alpine é a escolha certa para seu projeto, consulte a discussão completa na seção de Variantes de imagem da página de imagens do Docker Hub Ruby.
Manter a imagem com tamanho reduzido irá diminuir os tempos de carregamento da página e de recursos, especialmente se você também mantiver os volumes a um mínimo. Isso tornará a experiência do seu usuário - durante o desenvolvimento - rápida e mais próxima da experiência de se trabalhar localmente, em um ambiente não conteinerizado.
A paridade entre as imagens de desenvolvimento e de produção facilita implantações bem-sucedidas. Como as equipes optam frequentemente por usar imagens do Alpine na produção - pelos benefícios em termos de velocidade, o desenvolvimento com uma base de Alpine ajuda a compensar os problemas na transição para a produção.
Esse é um dos passos que vamos adotar para evitar conflitos de versão entre a versão bundler padrão, disponível no nosso ambiente e nosso código de aplicativo, que exige o Bundler 2.0.2.
Lembre-se de que a imagem alpine tem tamanho diminuto: a lista apresentada aqui não esgota os pacotes que você talvez queira ou precise no desenvolvimento, quando estiver conteinerizando o seu próprio aplicativo.
Esse passo irá garantir a paridade entre o nosso ambiente conteinerizado e as especificações no arquivo Gemfile.lock desse projeto.
Copiar esses arquivos como um passo independente, seguido de bundle install, significa que as gems do projeto não precisam ser recompiladas toda vez que você fizer alterações no código do seu aplicativo. Isso funcionará em conjunto com o volume da gem que vamos incluir no nosso arquivo Compose, o qual irá montar as gems para o seu contêiner de aplicativo, nos casos em que o serviço for recriado.As gems do projeto, porém, permanecerão as mesmas.
Esse passo compila a nokigiri com as versões de biblioteca libxml2 e libxslt que adicionamos ao contêiner do aplicativo no passo RUN apk add... acima.
Em seguida, vamos repetir o mesmo procedimento que usamos com as gems em relação aos nossos pacotes e dependências do JavaScript. Primeiro, vamos copiar os metadados do pacote; em seguida, vamos instalar as dependências e, por fim, vamos copiar o código do aplicativo na imagem do contêiner.
Para começar com a seção Javascript do nosso Dockerfile, copie os arquivos package.json e yarn.lock do diretório de seu projeto atual, no host, para o contêiner:
Essa instrução inclui um sinalizador --check-files com o comando yarn, um recurso que garante que nenhum arquivo instalado anteriormente tenha sido removido. Como no caso das nossas gems, vamos gerenciar a permanência dos pacotes no diretório node_modules com um volume, quando escrevermos nosso arquivo Compose.
Esse diretório incluirá nosso script de ponto de entrada principal e um script para o nosso serviço Sidekiq.
A primeira linha importante é set -e, que diz ao shell /bin/sh - que executa o script - para falhar rapidamente se houver problemas mais tarde no script. Em seguida, o script verifica se o tmp/pids/server.pid não está presente, a fim de garantir que não haverá conflitos de servidor quando iniciarmos o aplicativo. Por fim, o script inicia o servidor Rails com o comando bundle exec rails s. Usamos a opção -b com esse comando para vincular o servidor a todos os endereços IP e não para o localhost padrão. Essa invocação faz com que o servidor Rails encaminhe os pedidos de entrada para o IP do contêiner e não para o localhost padrão.
Em seguida, vamos criar um script para iniciar nosso serviço sidekiq, que irá processar nossas tarefas do Sidekiq. Para obter mais informações sobre como esse aplicativo usa o Sidekiq, consulte o artigo sobre Como adicionar o Sidekiq e o Redis a um aplicativo Ruby on Rails.
Com seus scripts de ponto de entrada e o Dockerfile instalados, você está pronto para definir seus serviços no seu arquivo Compose.
Usando o Docker Compose, conseguiremos executar vários contêineres necessários para nossa configuração. Vamos definir nossos serviços do Compose no nosso arquivo principal docker-compose.yml. Um serviço no Compose é um contêiner em execução e as definições de serviço — que você incluirá no seu arquivo docker-compose.yml — contém informações sobre como cada imagem de contêiner será executada. A ferramenta Compose permite que você defina vários serviços para construir aplicativos multi-contêiner.
Também vamos incluir uma montagem bind como parte da nossa preparação, de modo que qualquer alteração de código que fizermos durante o desenvolvimento será imediatamente sincronizada com os contêineres que precisam de acesso a esse código.
Note que não estamos definindo um serviço de test, uma vez que testar está fora do âmbito deste tutorial e desta série. Porém, você pode fazer esse teste, seguindo o anterior que estamos usando aqui para o serviço do sidekiq.
build: define as opções de configuração, incluindo o context e dockerfile, que serão aplicadas quando o Compose construir a imagem do aplicativo. Se quisesse usar uma imagem existente de um registro como o Docker Hub, você poderia usar como alternativa a instrução image, com informações sobre seu nome de usuário, repositório e tag da imagem.