GitOps no Amazon EKS com ArgoCD [Lab Session]
Tags: community, aws, argocd, gitops
Published: 2024-01-30 10:17:04

![intro](https://miro.medium.com/v2/resize:fit:640/format:webp/1*dmsHA5FfJvn9H9mI2VB2dQ.png)

> Why Argo CD?
> Application definitions, configurations, and environments should be declarative and version controlled. Application deployment and lifecycle management should be automated, auditable, and easy to understand.

*Source: [https://argoproj.github.io/cd/](https://argoproj.github.io/cd/)*

![Infraestrutura](https://cdn-images-1.medium.com/max/2000/1*BlkDAjj9DOwsLmJB2pGW1w.png)

[Repositório Terraform (Infraestrutura).](https://github.com/paulofponciano/EKS-Istio-Karpenter-ArgoCD.git)

[Repositório app de exemplo.](https://github.com/paulofponciano/app-cd-with-argo.git)

---

### Deploy Amazon EKS

- Definir variáveis no terraform (*variables.tfvars*):

```tf
## PROJECT BASE
    
cluster_name = "pegasus"
environment  = "staging"
project      = "devops"
aws_region   = "us-east-2"
az1          = "us-east-2a"
az2          = "us-east-2b"

## CLUSTER OPTIONS

k8s_version = "1.27"

endpoint_private_access = true

instance_type = [
  "t3a.medium"
]

desired_size = "1"
min_size     = "1"
max_size     = "1"

enabled_cluster_log_types = [
  "api", "audit", "authenticator", "controllerManager", "scheduler"
]

addon_cni_version       = "v1.13.4-eksbuild.1"
addon_coredns_version   = "v1.10.1-eksbuild.2"
addon_kubeproxy_version = "v1.27.3-eksbuild.2"
addon_csi_version       = "v1.21.0-eksbuild.1"

## INGRESS OPTIONS (ISTIO NLB)

nlb_ingress_internal         = "false"
enable_cross_zone_lb         = "true"
nlb_ingress_type             = "network"
proxy_protocol_v2            = "false"
grafana_virtual_service_host = "grafana.pauloponciano.pro"
kiali_virtual_service_host   = "kiali.pauloponciano.pro"
jaeger_virtual_service_host  = "jaeger.pauloponciano.pro"
argocd_virtual_service_host  = "argocd.pauloponciano.pro"

## KARPENTER OPTIONS

karpenter_instance_class = [
  "m5",
  "c5",
  "t3a"
]
karpenter_instance_size = [
  "large",
  "2xlarge"
]
karpenter_capacity_type = [
  "spot"
]
karpenter_azs = [
  "us-east-2a",
  "us-east-2b"
]

## NETWORKING

vpc_cidr                = "10.0.0.0/16"
public_subnet_az1_cidr  = "10.0.16.0/20"
public_subnet_az2_cidr  = "10.0.32.0/20"
private_subnet_az1_cidr = "10.0.48.0/20"
private_subnet_az2_cidr = "10.0.64.0/20"
```

- Caso utilize um certificado existente do *ACM*, informe o 'arn' em *nlb.tf*:

```tf
resource "aws_lb_listener" "ingress_443" {
  load_balancer_arn = aws_lb.istio_ingress.arn
  port              = "443"
  #protocol          = "TCP"
  protocol        = "TLS"
  certificate_arn = "arn:aws:acm:us-east-2:accountid:certificate/925700c8-03d1-4f4a-890a-249ffe2fa4f0"
  alpn_policy     = "HTTP2Preferred"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.https.arn
  }
}
```

Caso não utilize certificado nenhum, altere esse bloco para:

```tf
resource "aws_lb_listener" "ingress_443" {
  load_balancer_arn = aws_lb.istio_ingress.arn
  port              = "443"
  protocol          = "TCP"
  #protocol        = "TLS"
  #certificate_arn = "arn:aws:acm:us-east-2:accountid:certificate/925700c8-03d1-4f4a-890a-249ffe2fa4f0"
  #alpn_policy     = "HTTP2Preferred"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.https.arn
  }
}
```

- Executar terraform:

`terraform init`
`terraform plan --var-file variables.tfvars`
`terraform apply --var-file variables.tfvars`

![](https://cdn-images-1.medium.com/max/2652/1*HYTNmF1qFh3yo5Re0_qhUA.png)

- Conecte no cluster EKS:

`aws eks --region us-east-2 update-kubeconfig --name pegasus`

![update-kubeconfig](https://cdn-images-1.medium.com/max/2642/1*rNau3539xlchTD0ZfOsxXQ.png)

![kubectl get nodes](https://cdn-images-1.medium.com/max/2150/1*YNkpwI4yEF2zOF46zrhhVw.png)

![kubectl get pods -A](https://cdn-images-1.medium.com/max/2506/1*4v4dCSX1lKqlId4AjPhWYg.png)

- Aqui eu utilizei um domínio público já criado no Route 53 e inseri os registros abaixo apontando para o *CNAME* do *NLB* que foi criado pelo terraform:

![Route 53](https://cdn-images-1.medium.com/max/2462/1*kAe_EaBb1GHFK4RRe314Vg.png)

### ArgoCD

Na execução da *stack* terraform dos passos anteriores, já foi feito *deploy* do Argo e vamos fazer alguns ajustes para acessá-lo e seguir na configuração.

- Ajuste para terminação do *SSL* no *NLB* — Alterando o* ConfigMap* do ArgoCD server:

`kubectl edit cm argocd-cmd-params-cm -n argocd`

Inserir:

```yaml
data:
  server.insecure: 'true'
```

![argocd-cmd-params-cm](https://cdn-images-1.medium.com/max/2650/1*ze7zcpxdmiSPLrCggF-ZAQ.png)

Reciclar:

`kubectl rollout restart deployment argocd-server -n argocd`

![](https://cdn-images-1.medium.com/max/2360/1*eGUD-hMlZhnLJrFg37zkVg.png)

- Acessando o Argo UI:

![](https://cdn-images-1.medium.com/max/3816/1*65n_qh_vgU4b3PnzZR89yA.png)

- Recuperar senha inicial para *login*:

`kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo`

![](https://cdn-images-1.medium.com/max/2656/1*n5AflnUL3NaVjbC4lkLAcQ.png)

- Já conectado no Argo, adicionamos um novo repositório em *'Settings'*. Esse respositório é onde o Argo fica olhando e buscando por diferenças/alterações nos manifestos do kubernetes:

![Connect Repo](https://cdn-images-1.medium.com/max/3814/1*Deq5uXK41g3gO0OdsN7twA.png)

O repositório sendo privado no GitHub, é necessário gerar um *PAT (Personal Access Token)* para informar como *password* no momento de conectar através do Argo.

![Repositories](https://cdn-images-1.medium.com/max/3826/1*3_2lOuX-dQI68xqSctBt3Q.png)

- Ainda em *'Settings', criamos um novo 'Project':

![](https://cdn-images-1.medium.com/max/3386/1*WCgX-TJeWyHYUKEEbXesdA.png)

- Em 'Applications', criamos uma nova especificando o _path_ dos manifestos kubernetes dentro do repositório adicionado anteriormente:

![](https://cdn-images-1.medium.com/max/3072/1*y-sd68st66o0dCPHU2jnRQ.png)

Rapidamente o Argo entrega a aplicação rodando no cluster, pois o _Auto sync_ já está habilitado neste caso:

![Applications](https://cdn-images-1.medium.com/max/3824/1*nyo7Q5UiDvlSPs9kQD04Fw.png)

![Application (webgo)](https://cdn-images-1.medium.com/max/3814/1*2FRQ_ynbqyW6p4f3YnZepA.png)

- O _app_ de exemplo está acessível:

![](https://cdn-images-1.medium.com/max/3066/1*HbtNwfN7_0XbNx3myOtntA.png)

### GitHub / Actions

Agora na prática, vamos alcançar o cenário ilustrado abaixo utilizando GitHub Actions:

![](https://cdn-images-1.medium.com/max/2000/1*u1xM_izp-ZRLOv2VfHzK6Q.png)

No repositório do *app* e manifestos existe essa estrutura:
> .github/workflows/cd.yaml

```yaml
name: CD
on:
  push:
    branches: [main]
    paths-ignore:
      - 'k8s/**'
jobs: 
  build: 
    name: Build
    runs-on: ubuntu-latest
    steps: 
      - name: checkout 
        uses: actions/checkout@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: build image and push to docker hub
        uses: docker/build-push-action@v4
        with: 
          context: .
          push: true
          tags: paulofponciano/webgo:${{ github.sha }}, paulofponciano/webgo:latest

  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: checkout
        uses: actions/checkout@v3

      - name: kustomize
        uses: imranismail/setup-kustomize@v2
        with: 
          kustomize-version: v5.1.1

      - name: update k8s
        run: |
          cd k8s
          kustomize edit set image webgo=paulofponciano/webgo:${{ github.sha }}
          cat kustomization.yaml

      - name: commit
        run: | 
          git config --local user.email "paulofponciano@gmail.com"
          git config --local user.name "Paulo"
          git commit -am "actions change image tag"

      - name: push
        uses: ad-m/github-push-action@master
```
Basicamente sempre que houver um *push* na *branch* 'main' que não seja no diretório de manifestos 'k8s', as *actions* serão executadas. Isso significa que alguém está querendo entregar uma alteração na aplicação e uma nova imagem docker será criada para essa entrega.

- No GitHub precisamos configurar os _Repository secrets_ necessários para _push_ da imagem no Docker Hub e _Workflow permissions_:

![Actions secrets and variables](https://cdn-images-1.medium.com/max/3342/1*6KBPlJjTEEc5XOp6Dii-JQ.png)

![Settings > Actions > General > Workflow permissions](https://cdn-images-1.medium.com/max/2000/1*xCAbsk2EnTSSc_XrVBqNTw.png)*Settings > Actions > General > Workflow permissions*

- Acionando as _actions_ através de uma alteração no 'main.go':

![](https://cdn-images-1.medium.com/max/2390/1*gdIWAZyrD6Q-fhxyMeU9Jg.png)

*Push* para o repositório:

![](https://cdn-images-1.medium.com/max/2052/1*O1CvQJZLTAmXlfxdopLFBw.png)

- *Actions*:

![](https://cdn-images-1.medium.com/max/3350/1*VWNfth2dZcGvlYIbPdfG_A.png)

![](https://cdn-images-1.medium.com/max/2594/1*3XnmXwRWgOuJ27TEQFabBA.png)

O resultdo do _job_ chamado de ‘Build’, é o _push_ de uma nova imagem no docker hub. Os parâmetros para o _build_ da imagem estão no *Dockerfile*.

![Docker hub](https://cdn-images-1.medium.com/max/2544/1*tscqu8EW5oYesv_wS1pyVA.png)

No _job_ chamado de 'Deploy', é atualizado o manifesto _kustomization.yaml_ para inserir a nova _tag_ da imagem docker (usando o _sha_), é assim que o Argo vai saber que houve uma alteração e que é necessário um _Sync_:

```yaml
- name: kustomize
  uses: imranismail/setup-kustomize@v2
  with: 
    kustomize-version: v5.1.1

- name: update k8s
  run: |
    cd k8s
    kustomize edit set image webgo=paulofponciano/webgo:${{ github.sha }}
    cat kustomization.yaml

- name: commit
  run: | 
    git config --local user.email "paulofponciano@gmail.com"
    git config --local user.name "Paulo"
    git commit -am "actions change image tag"

- name: push
  uses: ad-m/github-push-action@master
```

![kustomization.yaml](https://cdn-images-1.medium.com/max/3812/1*zBi7XZvIKB8Axz8FwKRgVg.png)

- *Sync* no Argo:

![](https://cdn-images-1.medium.com/max/3816/1*wC5TfC2Ibst-a7SbG7-KSA.png)

- Nova versão do *app*:

![](https://cdn-images-1.medium.com/max/3038/1*Os9MV28LFYva5RddhFWEuQ.png)

> Lembrando que nossa unica ação manual até aqui, foi realizar o _push_ da alteração que fizemos no main.go \o/

Happy building!