Deploy de Aplicações
Conceitos Gerais
Nos projetos da PDPJ, os ambientes de deployment estão instalados em clusters Kubernetes, gerenciados por meio da ferramenta open source de orquestração de containeres chamada Rancher.
De toda sorte, o desenvolvedor não necessita maiores conhecimentos a respeito desses ambientes, bastando que sua aplicação possua os arquivos yamls sugeridos para que o deploy ocorra de forma automática por meio de pipelines de CI/CD (Continuous Delivery e Continuous Integration) executados pelo GitLab do CNJ (https://git.cnj.jus.br/).
Além disso, como a aplicação será executada em ambiente "contineirizado", ela deve possuir um arquivo Dockerfile, especificando o modo pelo qual ela deve ser empacotada.
Por fim, o projeto deve possuir um arquivo .gitlab-ci.yml, o qual especifica a pipeline propriamente dita.
Em suma, para que ocorra o deploy da aplicação de forma automática, é necessário:
- Presença de Dockerfile definindo o modo de empacotamento da aplicação
- Presença do arquivo .gitlab-ci.yml definindo a pipeline de deploy automático
- Presença da pasta kubernetes, com os arquivos yamls necessários para os diversos ambientes de destino
Quando ocorrerá o deploy automático? Depende da configuração feita no arquivo .gitlab-ci.yaml, mas o exemplo trazido mais abaixo gera um deploy automático para o ambiente de homologação quando houver um push para a branch master do repositório git, e um deploy automático para o ambiente de produção quando uma tag é lançada no projeto.
Acesso aos Logs da Aplicação
Caso seguida as recomendações da seção anterior, a equipe de desenvolvimento terá acesso aos logs gerados pelos pods da aplicação diretamente no GitLab.
Para tanto, basta clicar no menu Operações, submenu Logs, na página do projeto:
Logs diretamente pelo GitLab

Estrutura do Projeto
A estrutura do projeto, independentemente da linguagem de programação em que for escrita, deve possuir, portanto, a seguinte estrutura mínima:
projeto
├── Dockerfile [1]
├── .gitlab-ci.yml [2]
└── kubernetes/ [3]
   ├── base/
   │   ├── base.yml
   │   └── kustomization.yml
   └── overlays/
       ├── eks-hml-01/ [4]
       │   ├── configmaps.yml  [5]
       │   ├── ingress.yml  [6]
       │   └── kustomization.yml  [7]
       └── eks-prd-01/ [8]
           ├── configmaps.yml
           ├── ingress.yml
           └── kustomization.yml
- Dockerfile que define o empacotamento/build da aplicação (varia para cada linguagem de programação/caso concreto)
- Arquivo yaml que define os passos da pipeline de build e deploy automáticos
- Pasta que define as configurações necessárias para o ambiente kubernetes
- Pasta com configurações para o ambiente de homologação da PDPJ
- Arquivo de configuração/propriedades da aplicação para o ambiente em questão
- Arquivo de configuração do ingress da aplicação. Necessário apenas para aquelas aplicações que necessitem de URL própria exposta na Internet. Se a aplicação for um serviço que será acessado exclusivamente via Gateway API, pode ser omitido
- Arquivo da ferramenta Kustomize, que realiza a personalização das configurações conforme o ambiente
- Pasta com configurações para o ambiente de produção da PDPJ
Se o projeto for uma aplicação Java, sugere-se a existência também de um arquivo .m2/settings.xml, que é o arquivo de configuração do maven apontando para o repositório de artefatos do CNJ, utilizado no exemplo abaixo para o build de aplicações Java.
Exemplos de Arquivos
Dockerfile para uma aplicação em Java
# Dockerfile
FROM openjdk:11-jre-slim
COPY target/myapp.jar /opt/myapp/myapp.jar
EXPOSE 8080
ENTRYPOINT [ "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/opt/myapp/myapp.jar" ]
.gitlab-ci.yml
(independe da lingaguem de programação, exceto a fase de build, que varia conforme a linguagem adotada. No exemplo abaixo, presume Java com maven)
stages:
  - package
  - build
  - release
  - deploy
# build de aplicações java
maven-package:
  stage: package
  interruptible: true
  image:
    name: maven:3-jdk-11
    entrypoint: [ "" ]
  variables:
    DOCKER_HOST: "tcp://docker:2375"
    DOCKER_TLS_CERTDIR: ""
    DOCKER_DRIVER: overlay2
    MAVEN_CLI_OPTS: "--settings .m2/settings.xml --batch-mode "
    MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
  services:
      - docker:dind
  cache:
    key: "${CI_PROJECT_ID}-${CI_JOB_NAME}"
    paths: [ ".m2/repository" ]
  artifacts:
    paths: [ "target/myapp.jar" ]
  script:
    - mvn $MAVEN_CLI_OPTS package
# Ambiente de homologação
.env-eks-hml-01:
  environment:
    name: eks-hml-01
    deployment_tier: staging
    kubernetes:
      namespace: negociais
# Ambiente de produção
.env-eks-prd-01:
  environment:
    name: eks-prd-01
    deployment_tier: production
    kubernetes:
      namespace: negociais
.prepare-job-template:
  stage: package
  image:
    name: registry.cnj.jus.br/segsa/k8s-utils:latest
    entrypoint: [ "" ]
  dependencies: []
  artifacts:
    paths: [ "kubernetes/" ]
  environment:
    action: prepare
  script:
    - cd "$CI_PROJECT_DIR/kubernetes/overlays/$CI_ENVIRONMENT_NAME"
    - kustomize edit set image "registry.cnj.jus.br/pdpj/myapp:$CI_COMMIT_REF_NAME"
    - kustomize edit add annotation --force "app.gitlab.com/app:${CI_PROJECT_PATH_SLUG}"
        "app.gitlab.com/env:${CI_ENVIRONMENT_SLUG}" "app.gitlab.com/commit-short-sha:${CI_COMMIT_SHORT_SHA}"
    - kubectl apply --dry-run=client -k .
prepare-eks-hml-01:
  extends:
    - .prepare-job-template
    - .env-eks-hml-01
prepare-eks-prd-01:
  extends:
    - .prepare-job-template
    - .env-eks-prd-01
# Criação da imagem Docker da aplicação e seu envio para o servidor do CNJ
docker-image:
  stage: build
  image: docker:20.10
  services:
    - docker:20.10-dind
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  before_script:
    - docker login -u "$NEXUS_USERNAME" -p "$NEXUS_PASSWORD" registry.cnj.jus.br
  script:
    - docker build --pull -t "registry.cnj.jus.br/pdpj/myapp:$CI_COMMIT_REF_NAME"
        -t "registry.cnj.jus.br/pdpj/myapp:latest" -f Dockerfile "$CI_PROJECT_DIR"
    - docker push "registry.cnj.jus.br/pdpj/myapp:$CI_COMMIT_REF_NAME"
    - docker push "registry.cnj.jus.br/pdpj/myapp:latest"
release-job:
  stage: release
  image:
    name: registry.gitlab.com/gitlab-org/release-cli:latest
    entrypoint: [ "" ]
  dependencies: []
  rules:
    - if: $CI_COMMIT_TAG
  variables:
    GIT_STRATEGY: none
  script:
    - release-cli create --tag-name "${CI_COMMIT_TAG}" --description "${CI_COMMIT_MESSAGE}"
.deploy-job-template:
  stage: deploy
  image:
    name: registry.cnj.jus.br/segsa/k8s-utils:latest
    entrypoint: [ "" ]
  variables:
    GIT_STRATEGY: none
  retry:
    max: 2
    when: stuck_or_timeout_failure
  script:
    - cd "$CI_PROJECT_DIR/kubernetes/overlays/$CI_ENVIRONMENT_NAME"
    - kubectl apply -k . || (kubectl delete --ignore-not-found --wait -k . && kubectl apply -k .)
deploy-eks-hml-01:
  rules:
    - if: '$CI_COMMIT_BRANCH == "master"'
  extends:
    - .deploy-job-template
    - .env-eks-hml-01
  dependencies: [ "prepare-eks-hml-01" ]
deploy-eks-prd-01:
  rules:
    - if: $CI_COMMIT_TAG
  extends:
    - .deploy-job-template
    - .env-eks-prd-01
  dependencies: [ "prepare-eks-prd-01" ]
Importante destacar que a imagem docker deve ser sempre salva no repositório de artefatos do CNJ, uma vez que o Rancher busca de lá as imagens para o deploy da aplicação. O usuário não precisa se preocupar com as credenciais do repositório, uma vez que eles são fornecidas de forma transparente pelo ambiente do GitLab.
Os arquivos dentro do diretório kubernetes/base contêm a aplicação e tudo que é comum a todos os ambientes:
kubernetes/base/base.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: negociais # namespace para módulos negociais da PDPJ
  labels:
    app: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app: myapp
              topologyKey: kubernetes.io/hostname
      containers:
      - name: myapp
        image: registry.cnj.jus.br/pdpj/myapp:latest
        imagePullPolicy: Always
        env:
        - name: TZ
          value: America/Sao_Paulo
        - name: EUREKA_INSTANCE_PREFERIPADDRESS
          value: "true"
        envFrom:
        - secretRef:
            name: db-secret # secret no Kubernetes (senhas e outras informações confidenciais devem ser armazenadas em secrets)
        ports:
        - containerPort: 8080
          name: http
        livenessProbe:
          httpGet:
            path: /
            port: http
          failureThreshold: 6
          initialDelaySeconds: 10
          periodSeconds: 30
          timeoutSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: http
          failureThreshold: 3
          initialDelaySeconds: 10
          periodSeconds: 30
          timeoutSeconds: 10
        startupProbe:
          httpGet:
            path: /
            port: http
          failureThreshold: 60
          periodSeconds: 30
          timeoutSeconds: 10
        resources:
          requests:
            memory: "512Mi"
            cpu: "50m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        volumeMounts:
        - name: myapp-config
          mountPath: /opt/myapp/myapp.properties
          subPath: myapp.properties
          readOnly: true
      volumes:
      - name: myapp-config
        configMap:
          name: myapp-config
...
---
apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: negociais # namespace para módulos negociais da PDPJ
  labels:
    app: myapp
spec:
  type: NodePort
  ports:
  - name: http
    port: 8080
  selector:
    app: myapp
...
kubernetes/base/kustomization.yml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- base.yml
...
Os arquivos dentro do diretório kubernetes/overlays/eks-hml-01 contêm somente as configurações que devem ser aplicadas ao ambiente de homologação da aplicação.
kubernetes/overlays/eks-hml-01/configmaps.yml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
  namespace: myapp-namespace
  labels:
    app: myapp
data:
  myapp.properties: |
    ambiente=homologação
    propriedade.X=valorX
...
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-env
  namespace: myapp-namespace
  labels:
    app: myapp
data:
  SPRING_PROFILES_ACTIVE: "staging"
...
Um configmap nada mais é do que uma ferramenta de substituição de arquivos e seus conteúdos fornecido pelo ambiente Kubernetes, a fim de facilitar a configuração da aplicação conforme o ambiente em que ela esteja a executar (homologação, sustentação, produção, treinamento, etc).
kubernetes/overlays/eks-hml-01/ingress.yml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp
  namespace: myapp-namespace
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
  labels:
    app: myapp
spec:
  rules:
  - host: myapp.stg.pdpj.jus.br
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp
            port:
              name: http
...
Um ingress, por sua vez, é a configuração do modo pelo qual a aplicação será acessada externamente pela Internet. Essa configuração só faz sentido se a aplicação possuir uma URL/frontend própria. Caso ela vá servir apenas como fachada REST acessível via o Gateway API, a configuração de ingress é desnecessária.
kubernetes/overlays/eks-hml-01/kustomization.yml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- configmaps.yml
- ingress.yml
...
O arquivo kustomization.yaml possui a configuração da ferramenta Kustomize, utilizada pelo CNJ para viabilizar o deploy automático a depender do ambiente de destino.
Por sua vez, os arquivos dentro do diretório kubernetes/overlays/eks-prd-01 contêm somente as configurações que devem ser aplicadas ao ambiente de produção da aplicação:
kubernetes/overlays/eks-prd-01/configmaps.yml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
  namespace: myapp-namespace
  labels:
    app: myapp
data:
  myapp.properties: |
    ambiente=produção
    propriedade.X=valorY
...
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-env
  namespace: myapp-namespace
  labels:
    app: myapp
data:
  SPRING_PROFILES_ACTIVE: "production"
...
kubernetes/overlays/eks-prd-01/ingress.yml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp
  namespace: myapp-namespace
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
  labels:
    app: myapp
spec:
  rules:
  - host: myapp.pdpj.jus.br
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp
            port:
              name: http
...
kubernetes/overlays/eks-prd-01/kustomization.yml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- configmaps.yml
- ingress.yml
# Na produção utilizamos 2 ou mais réplicas, para alta disponibilidade da aplicação
replicas:
- name: myapp
  count: 2
...
Por fim, se aplicação for escrita na linguagem Java e fizer uso da ferramenta maven, o seguinte arquivo deve existir no projeto, na pasta .m2 (obs: as variáveis de ambiente NEXUS_USERNAME e NEXUS_PASSWORD são preenchidas em tempo de execução pelo próprio GitLab, ou seja, pode-se deixar o conteúdo arquivo abaixo ipsis litteris no projeto):
m2/settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <servers>
    <server>
      <id>nexus</id>
      <username>${env.NEXUS_USERNAME}</username>
      <password>${env.NEXUS_PASSWORD}</password>
    </server>
  </servers>
  <mirrors>
    <mirror>
      <id>nexus</id>
      <name>nexus.cnj.jus.br</name>
      <url>https://nexus.cnj.jus.br/repository/maven-public</url>
      <mirrorOf>external:*</mirrorOf>
    </mirror>
  </mirrors>
</settings>
Dúvidas e necessidades oriundas do desenvolvimento ou da infraestrutura devem ser dirigidas ao Mentor Técnico do projeto, indicado pelo CNJ e apresentado na reunião de Kick off.