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

paulofponciano

Paulo Ponciano

Posted on January 30, 2024

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

intro

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

  • 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

  • Conectando no cluster provisionado:

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

  • Listando os pods do crossplane e providers:

kubectl get pods -n crossplane-system

Deploy Crossplane composition

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

  • 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
Enter fullscreen mode Exit fullscreen mode

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

  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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

  • 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

  • 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"
}
Enter fullscreen mode Exit fullscreen mode

Dashboard API Gateway

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

Logs:

  • 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

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

  • Deixamos o Argo fazer o deploy:

  • 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!

💖 💪 🙅 🚩
paulofponciano
Paulo Ponciano

Posted on January 30, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related