- Utilizar o
COPY
em vez doADD
O
ADD
tem outras funções como baixar coisas da Internet, untar, deszipar, etc
-
Não é necessário instalar o
npm
ouyarn
é recomendado utilizar as versões dos mesmos que vêm empactadas junto com a imagem do Node que estiver utilizando, em casos extremos caso seja necessário uma versão específica, deve-se instalar através do build do Dockerfile. -
Utilizar
node
noCMD
do Dockerfile, por algumas razões, ao utilizar o clássiconpm start
, isso irá inicializar o node como um subprocesso do npm, adicionando complexidade e camadas desnessárias ao programa. Dockerfiles devem representar exatamente o que irá acontecer, sem necessiamente dependender do conhecimento do script do npm para conhecer que tipo de processo será inicializado. Há cenários em que o processo do npm não repassa corretamente os signals de um kill por exemplo para o subprocesso do Node. -
Sempre priorizar o uso do
WORKDIR
(criar [se não existir] e acessar) em vez doRUN mkdir XXX
, exceto quando for necessário determinar permissões através dochown
-
Priorizar versões pares da imagem do Node.js, visto que representam major releases / LTS (Long Term Support), versões ímpares tendem à serem designadas à versões de release/experimental Node.js Release Schedule
-
Não utilizar a tag
:latest
-
Priorizar início de uso com imagens
:debian
visto que todas as imagens por padrão usam a do debian como imagem base e aos poucos migrando para:alpine
, sendo que em grandes apps o Alpine pode não possuir alguma dependência necessária para o projeto. Evitar utilizar imagens:slim
e:onbuild
:alpine
images são pequenas, com o mínimo de "coisas" instaladas, sendo assim focadas em segurança, visto que diminui as portas para vulnerabilidade.
-
Não rodar containers com o usuário
root
que é padrão das imagens, a fim de reduzir sempre os riscos.- Por padrão a imagem do Node já possui o usuário
node
, porém desabilitado. - Sempre usar os utilitarios que requerem o
root
(apt
/apk
,npm i -g
) antes de trocar de usuário. - Instalar dependencias do
npm i
depois de trocar usuário. - Algumas permissões de escrita podem ser necessárias, fazer através de
chown node:node
- Por padrão a imagem do Node já possui o usuário
-
Para alterar usuário no Dockerfile
USER node
, assim os comandos delimitados porRUN
,CMD
eENTRYPOINT
passarão a respeitar o usuário, os outros ainda executarão comoroot
- Uma alternativa para o
WORKDIR
(sempre executa com usuárioroot
) para criar um diretório utilizando permissões do usuárionode
é criar manualmente:RUN mkdir app && chown -R node:node .
- Após definir o usuário node, ao executar
docker compose exec
por padrão você irá ser o usuárionode
no container, para alterar basta executar o exec assim:docker-compose exec -u root
- Uma alternativa para o
-
Ao descrever imagens no intuito de obter eficiência (tempo de build e tamanho), deve ser considerado os tópicos abaixo:
- Escolher uma imagem de
FROM
adequada, fixar ao mínimo uma versão major, em uma imagen de produção definir até o patch, se necessário. Experimentar utilizar imagensalpine
pelas vantagens vistas acima. - A ordem dos comandos do Dockerfile são bastante importantes, prestar atenção a fim de otimizar ao máximo as camadas em cache.
- Evitar copiar todo o fonte antes de executar o
npm install
, desta maneira, toda alteração de fonte irá causar uma instalação de dependências novamente. O correto é copiar somente opackage.json
epackage-lock.json
, em seguida instalar as dependências e depois copiar os arquivos fonte, em uma camada abaixo.- Dica para copiar um arquivo opcionalmente (se não existir não irá falhar) quando deseja-se ser bastante literal no Dockerfile (o que é bastante recomendado), para isso coloca-se um asterisco ao final do arquivo, exemplo
COPY package.json package-lock.json* ./
- Dica para copiar um arquivo opcionalmente (se não existir não irá falhar) quando deseja-se ser bastante literal no Dockerfile (o que é bastante recomendado), para isso coloca-se um asterisco ao final do arquivo, exemplo
- Gerenciadores de pacotes a nivel de host, executar somente um
apt-get && apt-get install
por Dockerfile e colocá-lo no topo do arquivo.
- Escolher uma imagem de
- O Docker deve ser a camada intermediária e não será preciso
nodemon
,forever
oupm2
no servidor.- O
nodemon
entretanto pode ser utilizado em desenvolvimento por causa dowatch file
- O
- O Docker irá gerenciar corretamente o processo (iniciar, parar, reiniciar, controle de healthcheck)
- O Docker irá gerenciar multiplos containers da mesma imagem ("replicas/tasks", "multi-thread")
- Por padrão tanto
npm
quantonode
não escutam por sinais de shutdown por padrão, o que é essencial em um ambiente Docker.
- PID 1 (identificador de processo) é o primeiro processo em um sistema (ou container), e este têm basicamente dois trabalhos: remover "zombie" processes e passar signals para sub-processes
- Zombie processes não é um grande problema com Node
-
Signals são utilizado pelo Docker para se comunicar com o processo Ex: "Opa, quero parar a execução deste container"
-
O Docker utiliza os signals do Linux para parar um app (
SIGINT
/SIGTERM
/SIGKILL
)SIGINT
eSIGTERM
permitem graceful stop, oSIGKILL
o processo não tem nem a chance de responder.- O npm não responde a esses comandos (por isso deve ser evitado em imagens Docker para executar processos).
- O Node.js por padrão não responde, mas pode ser implementado via código.
-
Ao utilizar o Tini para ser o init process do container, ele irá remover o container logo quando receber um sigint ou sigterm (ctrl+c em terminais linux/mac ou docker stop windows)
-
Quando não utiliza-se o Tini ao enviar os signals para remover, o Docker aguardará 10 segundos e irá efetuar o kill do processo (caso a aplicação não esteja preparada para responder o sigterm/sigint)
-
Docker Docs: Inject --init into docker run for process management
-
Funcionalidade adicionada na versão 17.06 (mid-2017)
-
Permite a criação de multiplas imagens de um único arquivo Dockerfile, perfeito para diferentes ambientes.
-
Permite além da facilidade de ter um único arquivo, segurança e melhor aproveitamento de espaço entre imagens.
-
É chamado também como "artifact only images"
-
Como o build pode ser feito de um Dockerfile com multiplos stages?
-
Por padrão irá ser feito top-down e o ultimo que irá ser levado em consideração no comando
docker build -t myapp .
-
Para um stage específico deve ser utilizado o comando
docker build -t myapp:prod --target prod .
-
The Twelve-Factor App possui 12 princípios design de aplicações distribuídas, escrito pelo time do Heroku.
-
Utilizar variáveis de ambiente para configuração
-
Logar para stdout/stderr
-
Fixar todas as versões da aplicação, até o
npm
-
Graceful exit utilizando SIGTERM/INIT
-
Criar o
.dockerignore
Config Twelve-Factor App - Config
-
Armazenar configurações de ambiente como Environment Variables
-
Docker e Docker Compose atuam muito bem como multiplas opções de variáveis de ambiente
-
Lecay Apps: Usar CMD ou ENTRYPOINT script com envsubst para passar Environment Variables para config files
-
Ex:
hostname
,remote api dns
-
Apps não devem rotear ou transportar logs para nenhum outro local que não seja
stdout
oustrerr
-
console.log()
funciona pois irá por padrão para o standard output -
Winston, Bunyan e Morgan são as opções mais utilizadas para controlar os leveis de verbosidade de logs no Node.js
-
Prevenir carregar imagens com arquivos desnecessários como:
.git/
,node_modules/
,npm-debug
,docker-compose*.yml
-
Não é necessário mas é importante que esteja na imagem:
Dockerfile
eREADME.md
- "Traditional Apps" = Pré-Docker App
- Assignment MTA
-
Utilizar o docker-compose para desenvolvimento local
-
Versão 2 é ideal para desenvolvimento local
- Versão 3 do .yaml é mais voltada para swarm ou kubernetes apps (não possui o depends_on e outros controles de hardwares específicos que existe no v2)
-
Production stages:
npm i --only=production
-
Development stages:
npm i --only=development
-
CI stages:
npm ci
(instala diretamente do package-lock) é mais rápido. -
Garantir que
NODE_ENV
esteja definido
-
Imprimir informações de configuração do npm
npm config list
-
RUN npm test
em um stage específico, também é sugere-se utilizar para linting -
Rodar somente testes unitários em build time, não rodar automações, etc
-
RUN npm audit
- How to deal with docker-compose CI exit codes
- Hadolint, a Dockerfile linter
- dockerfilelint, An opinionated Dockerfile linter
- Docker Hub automated repo testing with docker-compose
- Docker tag "latest" confusion
- Utilizar múltiplas réplicas ao invés de PM2 ou forever
- Começar com 1-2 replicas por CPU
- Para testes unitários utilizar uma única replica
- Para testes de integração utilizar multiplas replicas