Spring boot, githubActions e argocd
Tags: spring, argocd, java, kubernetes
Published: 2022-07-11 02:20:55

CI/CD não é novidade no mundo do desenvolvimento de software, no entanto a entrega de manifestos kubernetes apartado da aplicação, é uma prática a ser seguida.
Esse artigo tem por objetivo demonstrar tal situação, através do action github e o argocd.
_Pré-requisito: ter noção sobre docker, kubernetes, java e terraform.

## Breve explicação dos recursos utilizados neste exemplo

- **Spring boot:** framework para aplicações java, que utiliza padrões de projeto para controlar a injeção de dependência, alem de prover objetos para solucionar diversos problemas. A aplicação é executa dentro do seu contexto, alem de possuir um webservice embarcado (modelo imperativo é o apache e reativo é o netty).
- **Terraform:** utilizado para criar recursos/infraestrutura na nuvem ou local via código. Nesse artigo criaremos um cluster kubernetes utilizando terraform localmente, em conjunto com kind.
- **Argocd:** ele inspeciona um repositório que possui os manifestos kubernetes, e caso ocorra alguma alteração, o mesmo os aplica. Em resumo ele cuida da entrega dos manifestos kubernetes dentro do cluster.
- **GithubActions:** recurso do github que cuida do CI/CD. No nosso projeto utilizaremos para efetuar o deploy da aplicação, criação da imagem docker e atualização do repositório dos manifestos.
- **Helm:** instalador de pacotes dentro do cluster kubernetes. Ele atualizará a imagem dentro do deployment da aplicação.
- **docker:** container runtime
- **Kubernetes:** um orquestrador de containers.

### Pré requisitos
- instalar a ultima versão do terraform
- instalar o kind, pois através dele criaremos o cluster kubernetes local
- docker instalado
- kubectl (cli kubernetes)
- jdk 17
- helm
- clone dos projetos:
  - https://github.com/fabriciolfj/customer-service
  - https://github.com/fabriciolfj/customer-service-k8s
- criação de um token dentro da sua conta github (existe um print dentro do projeto de exemplo ou seguir a orientação do próprio github https://docs.github.com/pt/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)

### Criação do cluster
Nessa etapa criaremos nosso cluster k8s, instalaremos o argocd e por último criaremos a aplicação dentro do argo, onde ele vai monitorar as mudanças no repositório https://github.com/fabriciolfj/customer-service-k8s.
Com o clone do repositório customer-service efetuado, acesse o diretório cluster, nele existe o arquivo main.tf, onde encontra-se o descritivo do que o terraform criará. Dentro do main.tf, vamos olhar para o trecho abaixo e entender o que está ocorrendo.

```
resource "helm_release" "argocd" {
  name  = "argocd"

  repository       = "https://argoproj.github.io/argo-helm"
  chart            = "argo-cd"
  namespace        = "argocd"
  version          = "4.9.7"
  create_namespace = true

  values = [
    file("argocd/application.yaml")
  ]
}
```
O trecho acima representa o uso de um módulo dentro do terraform. Onde ele executará as seguintes etapas:

- instalar o argo dentro do cluster, com base na configuração do arquivo application.yaml
- dentro deste arquivo encontra-se as configurações adicionais para criar aplicação dentro do argocd
- quando falamos em criar aplicação dentro do argocd, é o repositório git com os manifestos que serão monitorados.
- qualquer modificação nestes manifestos e submetidos ao repositório git, estes serão re-aplicados dentro do cluster kubernetes.

Abaixo o conteúdo do arquivo application.yaml e em seguida o arquivo que cria a app dentro do argocd (estão no repositório de exemplo salientado acima):

```
server:
  additionalApplications:
    - name: cluster-config
      namespace: argocd
      project: default
      source:
        repoURL: https://github.com/fabriciolfj/customer-service
        targetRevision: HEAD
        path: argocd/manifests/cluster #local onde encontra-se o arquivo para criar a app
        directory:
          recurse: true
      destination:
        server: https://kubernetes.default.svc
      syncPolicy:
        automated:
          prune: false
          selfHeal: false
```

```
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: customer
  namespace: argocd
spec:
  syncPolicy:
    syncOptions:
      - CreateNamespace=false
    automated:
      selfHeal: true # sincronizar automaticamente com o repositorio
      prune: true # caso exclua algum arquivo, o argo vai excluir o recurso no kubernetes
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: customer
  source:
    path: helm #por traz usaremos o helm para efetuar o deploy, dentro do cluster
    repoURL: 'https://github.com/fabriciolfj/customer-service-k8s' #repositório da app, que o argo aplicará no cluster quando houver commits.
    targetRevision: HEAD
    helm:
      valueFiles:
        - values.yaml
  project: default
```
Os demais conteúdos do arquivo main.tf, tem por objetivo criar o cluster k8s local, utilizando kind. obs: está fora do escopo desse artigo o detalhamento desse conteúdos.
Certifique-se que o docker esteja rodando na sua máquina e execute o comando abaixo para inicializar o terraform, com as configurações do projeto, no path aonde encontra-se o arquivo main.tf:

```
terraform init
```
Em seguida vamos analisar o que será criado, através do comando:

```
terraform plan
```
Teremos o seguinte resultado: 
 - Plan: 24 to add, 0 to change, 0 to destroy.

Após a criação do cluster, vamos acessar o argocd. Abaixo o script para pegar a senha admin:

```
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo ``
```
Para redirecionar a porta:
```
kubectl port-forward svc/argocd-server -n argocd 8080:80
```
Acesse localhost:8080, usuário admin e a senha que você pegou  conforme o script acima.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y1wtsps1sai03320iazq.png)

## Github Actions
Para utilizar o CI/CD do githubactions, precisa constar um arquivo yaml no seguinte path do seu projeto: .github/workflows/commit-stage.yml
Abaixo a explicação de cada etapa que consta no nosso githubactions.

- pipeline disparará a cada commit
```

name: Commit Stage
on: push
```
- variáveis de ambiente utilizados dentro desse contexto. obs: a tag version será alimentada pelo hash do commit.
```
env:
  REGISTRY: docker.io
  IMAGE_NAME: fabricio211/customer-service
  VERSION: ${{ github.sha }}
```
- job/build que primeiro fará o checkout do nosso código, usando como base uma imagem ubuntu
```
jobs:
  build:
    name: Build and Test
    runs-on: ubuntu-20.04
    permissions:
      contents: read
      security-events: write
    steps:
      - name: Checkout source code
        uses: actions/checkout@v2
```
- job/package executará os testes unitários, gerar a imagem docker com base nas variáveis de ambiente e por mim enviará ao registry do docker hub (obs: podemos utilizar o registry do próprio github).

```
  package:
    name: Package and Publish
    needs: [ build ]
    runs-on: ubuntu-20.04
    permissions:
      contents: read
      packages: write
      security-events: write
    steps:
      - name: Checkout source code
        uses: actions/checkout@v2
      - name: Set up JDK
        uses: actions/setup-java@v2
        with:
          distribution: temurin
          java-version: 17
          cache: maven
      - name: Build container image
        run: |
          chmod +x ./mvnw
          ./mvnw clean install spring-boot:build-image -Dspring-boot.build-image.imageName=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}
      - name: Log into container registry
        uses: docker/login-action@v1
        with:
          registry: ${{ env.REGISTRY }}
          username: seu usuario 
          password: sua senha
      - name: Publish container image
        run: docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}
      - name: Publish container image (latest)
        run: |
          docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} \
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
          docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}
```
- por fim atualizaremos a imagem dentro do nosso manifesto, no outro repositório. obs: nesse ponto precisamos do secret do github para executar essa operação (salientado acima como proceder).
```
      - name: Update values.yaml
        uses: fjogeleit/yaml-update-action@v0.10.0
        with:
          valueFile: 'helm/values.yaml'
          propertyPath: 'image.tag'
          value: ${{ env.VERSION }}
          commitChange: true
          updateFile: true
          token: ${{ secrets.SPRING_FAST }}
          targetBranch: main
          branch: main
          repository: fabriciolfj/customer-service-k8s
```
Ao realizar algum commit, você verá as seguintes situações.
- entrega da app:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wkrznvj3s3v1wjqf2871.png)

- commit no outro repositório

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axaebqhc0nlfjc9hb2hd.png)

## Aplicação
- execute o script baixo que encontra-se no path raiz do projeto, dentro do namespace customer
```
kubectl apply -f postgresql.yaml
```