GitOps no Amazon EKS com ArgoCD [Lab Session]

Paulo Ponciano
6 min readSep 2, 2023

--

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/

Infraestrutura

Repositório Terraform (Infraestrutura).

Repositório app de exemplo.

Deploy Amazon EKS

  1. Definir variáveis no terraform (variables.tfvars):
## 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"

2. Caso utilize um certificado existente do ACM, informe o 'arn' em nlb.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:

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
}
}

3. Executar terraform:

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

4. Conecte no cluster EKS:

aws eks --region us-east-2 update-kubeconfig --name pegasus
update-kubeconfig
kubectl get nodes
kubectl get pods -A

5. 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

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.

  1. Ajuste para terminação do SSL no NLB — Alterando o ConfigMap do ArgoCD server:
kubectl edit cm argocd-cmd-params-cm -n argocd

Inserir:

data:
server.insecure: 'true'
argocd-cmd-params-cm

Reciclar:

kubectl rollout restart deployment argocd-server -n argocd

2. Acessando o Argo UI:

3. Recuperar senha inicial para login:

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

4. 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

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

5. Ainda em 'Settings', criamos um novo 'Project':

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

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

Applications
Application (webgo)

7. O app de exemplo está acessível:

GitHub / Actions

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

No repositório do app e manifestos existe essa estrutura:

.github/workflows/cd.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.

  1. No GitHub precisamos configurar os Repository secrets necessários para push da imagem no Docker Hub e Workflow permissions:
Actions secrets and variables
Settings > Actions > General > Workflow permissions

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

Push para o repositório:

3. Actions:

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

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:

- 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

4. Sync no Argo:

5. Nova versão do app:

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

Happy building!

--

--

Paulo Ponciano

Solutions Architect | 7x AWS Certified | AWS Black Belt | AWS Community Builder