Serverless com Crossplane composition no EKS + GitOps [Lab Session]

Paulo Ponciano
6 min readOct 7, 2023

--

Build control planes without needing to write code. Crossplane has a highly extensible backend that enables you to orchestrate applications and infrastructure no matter where they run, and a highly configurable frontend that lets you define the declarative API it offers.

https://www.crossplane.io/

Neste lab vamos provisionar a arquitetura serverless abaixo na AWS utilizando uma composition do Crossplane. Tudo é provisionado como código e com o Crossplane, os componentes/serviços AWS se tornam recursos dentro do Kubernetes e são gerenciados como tal.

Além da composition que serve como um template para criação de vários recursos gerenciados de uma só vez (stack na imagem abaixo), criamos também os managed resources, que são os recursos gerenciados de uma forma mais 'individual'. Adicionamos as funcionalidades do ArgoCD para GitOps neste cenário de managed resources.

Serverless architecture managed by crossplane controller in kubernetes

O código node.js das funções lambda que utilizamos bem como o pattern, é fruto do trabalho sensacional realizado pela galera que contribui no https://serverlessland.com/.

Crossplane Introduction

Source: https://docs.crossplane.io/latest/getting-started/introduction/

Repositório.

Deploy Amazon EKS — Cluster de gerenciamento

  1. O cluster EKS e toda infra necessária para ele (vpc, nlb, etc.) nós subimos com terraform. Nesse deploy também subimos o crossplane via helm:
terraform init
terraform plan --var-file variables.tfvars
terraform apply --var-file variables.tfvars

2. Conectando no cluster provisionado:

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

3. Listando os pods do crossplane e providers:

kubectl get pods -n crossplane-system

Deploy Crossplane composition

  1. Primeiro criamos um secret para utilizar no ProviderConfig do crossplane:
kubectl create secret \
generic aws-secret \
-n crossplane-system \
--from-file=creds=./cred.txt

No arquivo cred.txt existente na raiz do repositório, temos as informações de access key e secret key da AWS. O crossplane precisa dessas credenciais para provisionar os recursos.

2. Criando o ProviderConfig:

cat <<EOF | kubectl apply -f -
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: aws-secret
key: creds
EOF

Reference: https://docs.crossplane.io/latest/getting-started/provider-aws/

3. Agora vamos aplicar no cluster a composition e definition da stack serverless:

kubectl apply -f composition.yaml
kubectl apply -f definition.yaml

Neste ponto, temos nosso template pronto no cluster para ser acionado. Fazemos isso através do claim. O claim pode ser a única interação necessária para o usuário/desenvolvedor, sendo um yaml extremamente enxuto. No final das contas, todas as definições e guardrails estão no template (composition.yaml e definition.yaml).

composition.yaml

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: order.pauloponciano.pro
spec:
compositeTypeRef:
apiVersion: api.pauloponciano.pro/v1alpha1
kind: XCustomOrder
patchSets:
- name: common-fields
patches:
- type: FromCompositeFieldPath
fromFieldPath: spec.resourceConfig.providerConfigName
toFieldPath: spec.providerConfigRef.name
- type: FromCompositeFieldPath
fromFieldPath: spec.resourceConfig.region
toFieldPath: spec.forProvider.region
resources:
## API GATEWAY
- name: order-api-gateway
base:
apiVersion: apigateway.aws.upbound.io/v1beta1
kind: RestAPI
# Removed for brevity

definition.yaml

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xcustomorders.api.pauloponciano.pro
spec:
group: api.pauloponciano.pro
names:
kind: XCustomOrder
plural: xcustomorders
claimNames:
kind: CustomOrder
plural: customorders
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
resourceConfig:
description: ResourceConfig defines general properties of this AWS
resource.
properties:
providerConfigName:
type: string
region:
type: string
oneOf:
- pattern: 'us-east-2'
- pattern: 'us-east-1'
# Removed for brevity

4. Aplicando o claim.yaml:

apiVersion: api.pauloponciano.pro/v1alpha1
kind: CustomOrder
metadata:
name: order-composition
namespace: default
spec:
resourceConfig:
providerConfigName: default
region: us-east-1
tags:
ownerName: SRE
projectName: PE
kubectl apply -f claim.yaml

Agora a composite CustomOrder será provisionada. Podemos ver:

kubectl get composite
kubectl describe composite order-composition-qmfjw
kubectl get managed

Caso algum desses recursos seja deletado, via console AWS por exemplo, o crossplane vai identificar e provisionar novamente… reconciliation process!

Testando a stack serverless

  1. Vamos fazer um describe para identificar a URL de invocação da API Gateway:
kubectl describe deployment.apigateway.aws.upbound.io/order-composition-qmfjw-k9p95

2. Fazemos vários POST's na API Gateway no /order, neste caso usando Thunder Client no VS Code:

{
"restaurantName": "calabresoPizza",
"order": "samplePizza",
"customerName": "Paulo",
"amount": "10"
}
Dashboard API Gateway

3. Podemos ver que a lambda de putOrder foi invocada pela API Gateway:

Logs:

4. Na rule do EventBridge, estamos esperando o event pattern abaixo para invocar a outra lambda, definida como target:

Event pattern
Event targets

Podemos ver em 'Monitoring' da rule, que ela foi acionada após nossos POST's na API Gateway e também invocou a outra lambda:

Event monitoring

5. Lambda consumer:

Logs:

Crossplane managed resources — GitOps com ArgoCD

Adicionamos uma application no Argo, baseada no mesmo repositório que usamos até agora, porém no path crossplane_managed/vpc.

São os recursos para construção de uma VPC, mas dessa vez não como uma 'composition' do crossplane.

Para os passos mais detalhados de deploy do ArgoCD no EKS, podem dar uma olhada no post: https://medium.com/@paulofponciano/gitops-no-amazon-eks-com-argocd-lab-session-b4b957b7a84c.

  1. Deixamos o Argo fazer o deploy:

2. Vamos olhar no kubernetes:

kubectl get vpc
kubectl get managed

Além do controller do crossplane, contamos com o Argo para sempre manter o estado desejado dos recursos no cluster.

Happy building!

--

--

Paulo Ponciano

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