Portinari quick start com Docker

Motivação

Faz tempo que venho estudando esses frameworks Javascript da moda, tipo React, Vue e Angular.

Agora com a liberação do THF como Portinari, resolvi dar um gás no Angular.

Mas, cada vez que vou iniciar uma aplicação é aquela sofrência, npm install, baixa a internet inteira e aguarda.

Bom, como também to estudando muito docker, achei facilmente um monte de imagens prontas, já com Node, Angular, Ionic e até com JDK do Java pra conectar com o device.

Mas, usar o pronto não tem graça, o legal é fazer o meu mesmo. Então, dá-lhe quebrar a cabeça pra montar essas maquininhas:

Testes basicos

Comecei montando uma maquina simples, só instalando o Angular-CLI, e já coloquei o Ionic também:

FROM node:12.8.0-alpine

RUN apk add yarn && \
    npm install --quiet --no-progress -g @angular/cli@8.0.1 &> /dev/null && \
    npm install --quiet --no-progress -g ionic@4.1.2 &> /dev/null


CMD ["/bin/sh"]

O Alpine é a menor distro Linux que encontrei, e eu queria imagens bem pequenas.

Mas o problema é que ele é totalmente seco, e dá uma puta trabalheira se precisar instalar muita coisa nele.

O legal dele é pra distribuir só o App já rodando, mas pra montar uma máquina de desenvolvimento acho que não é a melhor opção.

Bom, dockerfile na mão, experimentei buildar a maquineta e ver se conseguia tocar a vida e deixar as coisas rodando de vez.

Depois de montar a máquina com Angular e conseguir instalar tudo manualmente e fazer rodar, resolvi de fazer com duas frentes:

1 - Uma máquina com o Portinari, todo instalado e com um projeto modelo 2 - Uma máquina com Ionic "Portinarizado", também prontinha, já com tudo o que for necessário pra sair buildando app.

Pra ambas eu pretendo fazer versões com o Tema do THF aplicado, e algumas aplicações inicializadas, tipo login, um "Crudizinho", configuração de i18n. E outras coisinhas que a gente vai se batendo pra configurar num projeto THF do Zero.

Hora de iniciar a máquina do Portinari com base na de Angular Puro

FROM tgmti/angular:0.0.1

LABEL maintainer="Thiago Mota <tgmspawn@gmail.com>"
LABEL version="0.0.1"
LABEL title="Portinari Start Container"
LABEL description="A started Portinari project"
LABEL "https://portinari.io"="Portinari UI"

WORKDIR /app

RUN ng new my-po-project --skipInstall --skip-git

WORKDIR /app/my-po-project

RUN npm install

RUN ng add @portinari/portinari-ui

RUN npm install @portinari/portinari-templates

RUN sed -i 's/ng serve/ng serve --host 0.0.0.0/g' package.json

EXPOSE 4200

CMD ["npm","start"]

Nesse caso já ajustei o script ng serve pra servir 0.0.0.0, senão ele não aceita conexões do Host Windows (aí vai servir pra quê afimal?)

Funcionou que foi uma beleza, aparece a página, aplicação já inicializada, achei massa! Só que...

Se eu faço mapeamento do volume, lá se vai a pasta: docker run --rm -v "${PWD}:/app -p 4200:4200 tgmti/ionic:0.0.1-buster

Claro né animal, se montar uma pasta do Host dentro do Container, o que estava lá vai ser apagado (técnicamente fica no layer da imagem e nem chega a subir pro FS do container, mas na hora de usar é o mesmo que deletado).

Como diabos fazer pra mapear uma pasta do Windows e replicar os dados do Linux pra dentro do Windows?

Puta merda, aí eu sofri heim. Tentei só copiar, ou mover a pasta, mas sempre demorava uma eternidade pra processar a node_modules. E é examente por causa dessa bagaça que eu quero usar container.

Se monto uma pasta do Host dentro do container, no fim das contas replicar aquela pasta aqui é copiar arquivos entre filesystems diferentes.

Tentei compactar a node_modules e descompactar na hora de iniciar o container, consegui várias formas de mapear o container pra fazer a replicação automática.

Sempre que eu testava só com os arquivos da src, era uma beleza, mas colocando a node_modules o bicho sentava, e demorava as vezes mais que o npm install normal (pelo menos na minha percepção).

Comecei a pensar em outras formas, se eu deixar a máquina rodando só dentro do Linu, e abrir o VsCode lá dentro.

VsCode + Docker = Um milhão de opções

A opção mais na cara do gol é instalar o VsCode na própria maquineta, ou em uma outra, e criar um volume compartilhado pras duas.

VsCode no Linux compartilhando o Volume

Pro VsCode eu não quis perder muito tempo, fui direto atrás de quem manja, e achei uma mina Belga que só tem imagem top Maartje Eyskens, dei pull na imagem meyskens/vscode, abri um xServer e apontei o DISPLAY do Linux pra ele. Foi na hora.

Coisa lindja de tudo, funciona bem rápido, dá pra instalar as extensões.

Bem funcional, mas essa não é nada, eu paguei pau quando vi essa outra máquina aqui meyskens/vscode-maartje. Caramba meu, um ambiente completasso!!! Tem até Docker dentro do Docker, e eu fiquei tipo "WTF???". Ainda bem que ela comentou:

    #Install Docker, what??? Why are you looking that way at me?
    RUN curl https://get.docker.com | bash
    RUN apt-get -y install docker-compose

Hahaha, depois de perder um tempão admirando estas configurações, e mais umas outras que foram aparecendo, voltei pra minha saga. Montei um docker-compose.yml pra subir as duas maquinetas juntas

version: "3.7"

services:

portinari:
    image: tgmti/portinari
    ports: 
        - 4200:4200
    volumes: 
        - portinari_app:/app

vscode:
    image: meyskens/vscode
    environments:
        - DISPLAY=10.0.0.11:0.0
    volumes:
        - portinari_app:/app

volumes:
    portinari_app:

docker-compose up -d e "seje feliz"! Sem grilos, sem problemas, sem as minhas extensões e configurações.

Ai ai, eu ia ter que ficar horas trabalhando em cima pra montar uma máquina do meu jeito, igual a da menina Belga. E outra, eu quero usar o VsCode no Windows, não porque é melhor, só porque eu não consegui fazer ainda.

Então, já que desse jeito eu sei que funciona, parti pra ver os outros links que apareceram na pesquisa docker vscode.

VsCode Server dentro do Container

A Microsoft lançou um pacote de extensões sensacional pra trabalhar com Docker. A extensão Docker eu já uso a um tempo, apesar de que eu costumo mesmo é rodar um docker exec, ao invés de clicar com o direito em attach terminal.

Só que o pacote Remote Development é coisa de maluco:

  • Remote SSH
  • Remote Containers
  • Remote WSL

Caramba, fui direto nessa terceira, rodei um docker run dentro do wsl, criando uma pasta na home e mapeando pro volume, mas deu pau nas permissões. Putz, deixei pra lá (depois tenho que tentar outra vez).

Remote SSH eu já sabia como ia funcionar, dei uma fuçada pra ver se tinha algum jeito de fazer sem mexer no dockerfile, humm, serve um monte, mas não pra o que eu queria agora.

Remote Container é show. Dá pra selecionar quais extenções serão instadas dentro do container, e ela já sai fazendo tudo sozinha, configurando as portas, instalando o VSCode Server, Instalando as extenções, monta o docker-compose, cria um Dockerfile.

Faz tudo sozinha. Vai ser bem útil no futuro, mas ainda demora muito, e também, se eu tivesse afim de pegar o negócio pronto, não tinha partido do zero (ah, lembrei agora foi nessa hora que eu mudei do alpine pro Debian Buster Slim na verdade, o VsCode não conseguia instalar as coisas no alpine, o vscode-insiders consegue, mas deu pau, acho até que vou tentar voltar pro alpine depois).

Ainda não tava do jeito que eu queria.

Não que as opções que achei não fossem sensacionais, é que, pra compartilhar isso, tem que ser bem fácil, e de preferência rodar no Windows Home com Dockertools da firma, por isso, voltei pra pesquisa.

Pensei um pouco mais no que aprendi nessas brincadeiras.

  • Quando eu compartilho um volume com o Windows (-v c:\temp:/app) dá pra usar localmente, mas fica lento pra copiar a node_modules
  • Se eu mapear a pasta do app inteira, tenho que usar recursos bem mais complicados pra acessar a pasta através de outro container, ou com as extenções do VsCode.
  • Mas se eu criar o volume só na estrutura dos containers, fica super rápido.

E finalmente

E se eu mapear o /app, mas não o /app/node_modules?

Google, diz aí: docker map volume exclude subfolder

E o stackoverflow nosso de cada dia não deixou na mão: Add a volume to Docker, but exclude a sub-folder

Using docker-compose I'm able to use node_modules locally, but ignore it in the docker container using the following syntax in the docker-compose.yml
    volumes:
    - './angularApp:/opt/app'
    - /opt/app/node_modules/
So everything in ./angularApp is mapped to /opt/app and then I create another mount volume /opt/app/node_modules/ which is now empty directory - even if in my local machine ./angularApp/node_modules is not empty.

Caramba, porque que sempre tem um jeito 300x mais simples de fazer as coisas quando a gente tentou um monte de coisa mais difícil.

Funcionou que é uma maravilha. Navalha de Occam, a explicação mais fácil, a menor quantidade de passos. Que coisa de loko!!!


Compartilhando:

Ainda vou mexer nesse repo, mas a versão de agora está taggeada como 0.0.1.

https://github.com/tgmti/docker-dev-utils

Ficou um pequeno problema ainda:

Quando mexo em um arquivo pelo Windows, o servidor não recompila o código automaticamente.

Pra resolver eu dou um "toque" nele pelo shell do container: touch src/app/app.component.html

Anotei essa Issue pra resolver outra hora.


Conclusão

Passei trabalho?

  • Nãaa, que isso?

Valeu a pena?

  • Bagarai véi. Tentando fazer a bagaça funcionar do meu jeito, sem pegar muita coisa pronta, aprendi uma porrada de coisa. Sobre Node, Angular e Ionic Cli, VsCode Remote, Linux e principalmente Docker

TL;DR

Pra montar tudo numa máquina só, basta seguir a receitinha:

Dockerfile

FROM node:12.8.1-buster-slim

LABEL maintainer="Thiago Mota <tgmspawn@gmail.com>"
LABEL version="0.0.1"
LABEL title="Portinari Start Container"
LABEL description="A started Portinari project"
LABEL "https://portinari.io"="Portinari UI"

# Determina se vai iniciar o Server ao criar/iniciar o container
ENV INIT_NG_SERVER=true

EXPOSE 4200

RUN apt update && \
    apt install -y yarn

RUN npm install -g @angular/cli
RUN npm install -g npm-add-script

WORKDIR /

RUN ng new app --skipInstall --skip-git

WORKDIR /app
RUN npm install
RUN ng add @portinari/portinari-ui
RUN npm install @portinari/portinari-templates

RUN sed -i 's/ng serve/ng serve --host 0.0.0.0/g' package.json

COPY ./start.sh /
RUN chmod 777 /start.sh

# Guardo os arquivos gerados na pasta /template, exceto os node_modules
RUN mkdir /template/
RUN cp -rf `ls -A | grep -v "node_modules"` /template/

COPY ./start.sh /start.sh
CMD ["/start.sh"]

start.sh

#!/bin/sh

if find angular.json 2>/dev/null; then
    echo "App exists"
else
    echo "Copy template to app directory..."
    cp -rf /template/* /app/
fi

if $INIT_NG_SERVER ; then
    echo "Starting ionic Server..."
    npm start
else
    bash
fi

docker-compose.yml

version: "3.7"

services:

  portinari:
    image: tgmti/portinari:0.0.1-buster
    ports:
      - 4200:4200
    volumes:
      - .:/app
      - node_modules:/app/node_modules
    tty: true
    environment:
      - INIT_IONIC_SERVER=false

volumes:
  node_modules:

Executou: docker-compose up

E já aparece:

TGM Portinari Docker page