Skip to main content

Provider

Los providers permiten que Crossplane controle un recurso utilizando una API externa. Nuevos providers siempre están surgiendo y podemos consultar en la lista de providers de la documentación oficial.

Los providers de Crossplane permiten:

  • Crear recursos gestionados en las plataformas de nube (como AWS, Azure, GCP) u otros servicios
  • Gestionar el ciclo de vida de esos recursos (creación, actualización, eliminación)
  • Reconciliar el estado deseado con el estado actual de los recursos

Los ejemplos disponibles en la documentación de Crossplane demuestran su utilización para creación de recursos en cloud, pero ya sabemos que Crossplane es mucho más que eso.

¿Quién proporciona los providers?

  • Upbound
  • Comunidad (contrib)
  • Otras empresas

Una lista de los providers más utilizados:

  • provider-family-aws (upbound)
    • provider-aws (contrib) también es un provider de AWS, pero desarrollado por la comunidad.
  • provider-family-gcp (upbound) y provider-family-gcp-beta (upbound)
    • provider-gcp (contrib) también es un provider de GCP, pero desarrollado por la comunidad.
  • provider-family-azure (upbound)
    • provider-azure (contrib) también es un provider de Azure, pero desarrollado por la comunidad.
  • provider-azuread (upbound) en caso de que tu empresa utilice Active Directory
  • provider-kubernetes (upbound)
  • provider-terraform (upbound) y provider-opentofu (upbound)
  • provider-vault (upbound)
  • provider-helm (upbound) y también tenemos el de la comunidad.
  • provider-argocd (contrib)
  • provider-confluent (contrib) para trabajar con kafka
  • provider-github (coopnorge)
  • provider-gitlab (contrib)
  • provider-jenkins (ankasoftco)
  • provider-okta (contrib)
  • provider-keycloack (contrib)
  • provider-harbor (contrib)
  • provider-grafana (grafana)
  • provider-postgresql (tages)
  • provider-http (contrib)

Vamos instalando providers de acuerdo con nuestra necesidad. Por eso considero interesante no instalar nada durante la instalación del helm chart de Crossplane.

Los providers son responsables de todos los aspectos de la conexión con recursos no-Kubernetes. Esto incluye autenticación, haciendo llamadas de API externas y proporcionando la lógica del Kubernetes Controller para cualesquiera recursos externos.

Como un provider se comunica con una API externa es necesario también crear una configuración que contendrá los datos necesarios para la autenticación con esa API.

Mira cuántos providers solo para AWS.

alt text

Aquí el repositorio de Upbound para el provider aws.

Este es el repositorio del proveedor AWS oficial mantenido por el equipo Upbound. Upbound es la empresa que lidera el desarrollo de Crossplane y de algunos proveedores nativos, como este para AWS.

Siendo el proveedor oficial, probablemente recibirá más soporte y actualizaciones frecuentes.

Sigue las directrices y las especificaciones oficiales de Crossplane.

El provider-aws community también hace el mismo trabajo pero es mantenido por la comunidad. Los proveedores mantenidos por la comunidad pueden ser igualmente útiles y confiables, pero pueden tener diferencias en las implementaciones y en el soporte en comparación con el proveedor oficial. ¡Si existe oficial con una empresa detrás úsalo!

La ventaja de los providers mantenidos por la comunidad es que pueden proporcionar recursos o funcionalidades específicas que pueden no estar presentes en el provider oficial, pero también puede suceder lo contrario.

Cada uno de estos providers extienden la API de Kubernetes de una manera diferente, por eso el objeto de un provider no funcionará en el otro, a pesar de poder hacer lo que necesitas con cualquiera de los dos.

# upbound
apiVersion: s3.aws.upbound.io/v1
kind: Bucket
...

# community
apiVersion: s3.aws.crossplane.io/v1
kind: Bucket

Vamos a instalar el provider para AWS S3.

# Instala SOLO el provider de S3
cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-s3
spec:
package: xpkg.upbound.io/upbound/provider-aws-s3:v1
EOF

❯ k get provider
NAME INSTALLED HEALTHY PACKAGE AGE
provider-aws-s3 True True xpkg.upbound.io/upbound/provider-aws-s3:v1 19s
upbound-provider-family-aws True True xpkg.upbound.io/upbound/provider-family-aws:v1.20.1 14s

❯ k get pods -n crossplane-system
NAME READY STATUS RESTARTS AGE
crossplane-654d5644f4-mbr7j 1/1 Running 0 14h
crossplane-rbac-manager-59d8fcb968-kxrql 1/1 Running 0 14h
provider-aws-s3-e91dae831b35-5885cbd9b9-5fdhn 1/1 Running 0 19s
upbound-provider-family-aws-49b8fc96b73c-6b6b9d8bb5-t9c2q 1/1 Running 0 25s

# Con la instalación de este provider ya tenemos los resources que podemos usar para declarar recursos

❯ k get customresourcedefinitions.apiextensions.k8s.io -n crossplane-system -o custom-columns=NAME:.metadata.name | grep s3.aws.upbound.io
bucketaccelerateconfigurations.s3.aws.upbound.io
bucketacls.s3.aws.upbound.io
bucketanalyticsconfigurations.s3.aws.upbound.io
bucketcorsconfigurations.s3.aws.upbound.io
bucketintelligenttieringconfigurations.s3.aws.upbound.io
bucketinventories.s3.aws.upbound.io
bucketlifecycleconfigurations.s3.aws.upbound.io
bucketloggings.s3.aws.upbound.io
bucketmetrics.s3.aws.upbound.io
bucketnotifications.s3.aws.upbound.io
bucketobjectlockconfigurations.s3.aws.upbound.io
bucketobjects.s3.aws.upbound.io
bucketownershipcontrols.s3.aws.upbound.io
bucketpolicies.s3.aws.upbound.io
bucketpublicaccessblocks.s3.aws.upbound.io
bucketreplicationconfigurations.s3.aws.upbound.io
bucketrequestpaymentconfigurations.s3.aws.upbound.io
buckets.s3.aws.upbound.io
bucketserversideencryptionconfigurations.s3.aws.upbound.io
bucketversionings.s3.aws.upbound.io
bucketwebsiteconfigurations.s3.aws.upbound.io
directorybuckets.s3.aws.upbound.io
objectcopies.s3.aws.upbound.io
objects.s3.aws.upbound.io

La instalación automática del provider-family-aws ocurre debido a la dependencia obligatoria definida en los providers de servicio individuales de Upbound (como el S3). Esto es un comportamiento intencional de la arquitectura de provider families.

El provider provider-aws-s3 declara en su manifiesto una dependencia hard-coded para el provider-family-aws. El Crossplane Package Manager resuelve esta dependencia automáticamente.

Propósito del Family Provider es:

  • Gestionar configuraciones compartidas (como ProviderConfig, autenticación AWS)
  • Proporcionar recursos comunes a todos los servicios de la familia (ej: IAM Roles, políticas de seguridad)
  • Actuar como punto único de actualización para múltiples servicios.

Si instalamos el provider-aws-ec2 varias dependencias son obligatorias entre los customs resources entonces también instalará todo lo que será necesario.

cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-ec2
spec:
package: xpkg.upbound.io/upbound/provider-aws-ec2:v1
EOF
provider.pkg.crossplane.io/provider-aws-ec2 created

# No creó otro upbound-provider-family...
❯ k get provider
NAME INSTALLED HEALTHY PACKAGE AGE
provider-aws-ec2 True True xpkg.upbound.io/upbound/provider-aws-ec2:v1 107s
provider-aws-s3 True True xpkg.upbound.io/upbound/provider-aws-s3:v1 7m59s
upbound-provider-family-aws True True xpkg.upbound.io/upbound/provider-family-aws:v1.20.1 7m54s

# Y ahora tenemos estos CRDs para el ec2.

k get customresourcedefinitions.apiextensions.k8s.io -n crossplane-system -o custom-columns=NAME:.metadata.name | grep ec2.aws.upbound.io
amicopies.ec2.aws.upbound.io
amilaunchpermissions.ec2.aws.upbound.io
amis.ec2.aws.upbound.io
availabilityzonegroups.ec2.aws.upbound.io
capacityreservations.ec2.aws.upbound.io
carriergateways.ec2.aws.upbound.io
customergateways.ec2.aws.upbound.io
defaultnetworkacls.ec2.aws.upbound.io
defaultroutetables.ec2.aws.upbound.io
defaultsecuritygroups.ec2.aws.upbound.io
defaultsubnets.ec2.aws.upbound.io
defaultvpcdhcpoptions.ec2.aws.upbound.io
defaultvpcs.ec2.aws.upbound.io
ebsdefaultkmskeys.ec2.aws.upbound.io
ebsencryptionbydefaults.ec2.aws.upbound.io
ebssnapshotcopies.ec2.aws.upbound.io
ebssnapshotimports.ec2.aws.upbound.io
ebssnapshots.ec2.aws.upbound.io
ebsvolumes.ec2.aws.upbound.io
egressonlyinternetgateways.ec2.aws.upbound.io
eipassociations.ec2.aws.upbound.io
eips.ec2.aws.upbound.io
fleets.ec2.aws.upbound.io
flowlogs.ec2.aws.upbound.io
hosts.ec2.aws.upbound.io
instances.ec2.aws.upbound.io
instancestates.ec2.aws.upbound.io
internetgateways.ec2.aws.upbound.io
keypairs.ec2.aws.upbound.io
launchtemplates.ec2.aws.upbound.io
mainroutetableassociations.ec2.aws.upbound.io
managedprefixlistentries.ec2.aws.upbound.io
managedprefixlists.ec2.aws.upbound.io
natgateways.ec2.aws.upbound.io
networkaclrules.ec2.aws.upbound.io
networkacls.ec2.aws.upbound.io
networkinsightsanalyses.ec2.aws.upbound.io
networkinsightspaths.ec2.aws.upbound.io
networkinterfaceattachments.ec2.aws.upbound.io
networkinterfaces.ec2.aws.upbound.io
networkinterfacesgattachments.ec2.aws.upbound.io
placementgroups.ec2.aws.upbound.io
routes.ec2.aws.upbound.io
routetableassociations.ec2.aws.upbound.io
routetables.ec2.aws.upbound.io
securitygroupegressrules.ec2.aws.upbound.io
securitygroupingressrules.ec2.aws.upbound.io
securitygrouprules.ec2.aws.upbound.io
securitygroups.ec2.aws.upbound.io
serialconsoleaccesses.ec2.aws.upbound.io
snapshotcreatevolumepermissions.ec2.aws.upbound.io
spotdatafeedsubscriptions.ec2.aws.upbound.io
spotfleetrequests.ec2.aws.upbound.io
spotinstancerequests.ec2.aws.upbound.io
subnetcidrreservations.ec2.aws.upbound.io
subnets.ec2.aws.upbound.io
tags.ec2.aws.upbound.io
trafficmirrorfilterrules.ec2.aws.upbound.io
trafficmirrorfilters.ec2.aws.upbound.io
transitgatewayconnectpeers.ec2.aws.upbound.io
transitgatewayconnects.ec2.aws.upbound.io
transitgatewaymulticastdomainassociations.ec2.aws.upbound.io
transitgatewaymulticastdomains.ec2.aws.upbound.io
transitgatewaymulticastgroupmembers.ec2.aws.upbound.io
transitgatewaymulticastgroupsources.ec2.aws.upbound.io
transitgatewaypeeringattachmentaccepters.ec2.aws.upbound.io
transitgatewaypeeringattachments.ec2.aws.upbound.io
transitgatewaypolicytables.ec2.aws.upbound.io
transitgatewayprefixlistreferences.ec2.aws.upbound.io
transitgatewayroutes.ec2.aws.upbound.io
transitgatewayroutetableassociations.ec2.aws.upbound.io
transitgatewayroutetablepropagations.ec2.aws.upbound.io
transitgatewayroutetables.ec2.aws.upbound.io
transitgateways.ec2.aws.upbound.io
transitgatewayvpcattachmentaccepters.ec2.aws.upbound.io
transitgatewayvpcattachments.ec2.aws.upbound.io
volumeattachments.ec2.aws.upbound.io
vpcdhcpoptions.ec2.aws.upbound.io
vpcdhcpoptionsassociations.ec2.aws.upbound.io
vpcendpointconnectionnotifications.ec2.aws.upbound.io
vpcendpointroutetableassociations.ec2.aws.upbound.io
vpcendpoints.ec2.aws.upbound.io
vpcendpointsecuritygroupassociations.ec2.aws.upbound.io
vpcendpointserviceallowedprincipals.ec2.aws.upbound.io
vpcendpointservices.ec2.aws.upbound.io
vpcendpointsubnetassociations.ec2.aws.upbound.io
vpcipampoolcidrallocations.ec2.aws.upbound.io
vpcipampoolcidrs.ec2.aws.upbound.io
vpcipampools.ec2.aws.upbound.io
vpcipams.ec2.aws.upbound.io
vpcipamscopes.ec2.aws.upbound.io
vpcipv4cidrblockassociations.ec2.aws.upbound.io
vpcpeeringconnectionaccepters.ec2.aws.upbound.io
vpcpeeringconnectionoptions.ec2.aws.upbound.io
vpcpeeringconnections.ec2.aws.upbound.io
vpcs.ec2.aws.upbound.io
vpnconnectionroutes.ec2.aws.upbound.io
vpnconnections.ec2.aws.upbound.io
vpngatewayattachments.ec2.aws.upbound.io
vpngatewayroutepropagations.ec2.aws.upbound.io
vpngateways.ec2.aws.upbound.io

Durante la creación de un recurso en aws, por ejemplo un s3, el manifiesto pedirá cuál provider usará y la configuración de este, es decir, la autenticación que utilizará. Si fuéramos hacer un paralelo con terraform, es necesario que las variables de ambiente estén cargadas o apuntadas. En este caso apuntaremos al recurso llamado ProviderConfig.

Primeramente vamos a crear un secret en kubernetes igual al contenido abajo, pero con valores válidos. Crea un IAM en AWS que será usado por el provider, genera el secret, agrega el permiso de AdministratorAccess (solamente para el estudio, no hagas esto en producción).

Crea un archivo aws-creds.txt con el contenido abajo y vamos a aplicar en el namespace de crossplane

echo "[default]
aws_access_key_id = XXXXXXXXXXXXXXX
aws_secret_access_key = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" > aws-creds.txt

kubectl create secret \
generic aws-secret \
-n crossplane-system \
--from-file=creds=./aws-creds.txt

Ahora que tenemos el secret de nombre aws-secret vamos a hacer el 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

Varios providers diferentes podrán utilizar esta configuración, siempre que sea apuntada. Piensa en la configuración como un método de autenticación. Es posible autenticar utilizando otros métodos también, como Roles, WebIdentity, OIDC etc, mira en https://docs.upbound.io/providers/provider-aws/authentication/.

Ya tenemos un provider-aws y tenemos un config, ahora vamos a crear algún recurso para ver si está funcionando.

Vale recordar que este es un ejemplo en AWS, pero otros providers de otras clouds pueden ser usados como Azure, GCP y hasta el propio Kubernetes. La tendencia es que los providers crezcan para todo lo que existe, así como Terraform y Ansible. Lo que vale en el estudio es entender la herramienta y los conceptos.

Flujo

  1. Cuando un usuario aplica un manifiesto relacionado a Crossplane (como un recurso gestionado o una composition), esa petición primero va al kube-api-server que valida el recurso contra el CRD apropiado y lo almacena en el etcd.
  2. Después, el kube-api-server notifica a los controladores registrados (incluyendo los controladores de Crossplane) sobre los cambios relevantes.
  3. Los controladores de Crossplane (tanto el core como los providers) continuamente consultan el kube-api-server (usando LIST/WATCH) para obtener informaciones sobre recursos que están gestionando. Cuando Crossplane necesita actualizar el estado de un recurso o crear recursos dependientes, hace llamadas al kube-api-server. Crossplane también se comunica con el kube-api-server para registrar CRDs, crear eventos y actualizar el estado de los recursos.

Este modelo sigue el patrón estándar de controladores de Kubernetes, donde los controladores son clientes del kube-api-server e interactúan con él de forma bidireccional, obteniendo informaciones y enviando actualizaciones conforme necesario para reconciliar el estado deseado.

alt text

Buena Práctica

El provider es el puente entre Kubernetes y un recurso externo. No es necesario instalar dos veces el mismo provider, apenas pasamos para un provider config diferente es lo necesario. Como un provider-config-dev y un provider-config-prod.

Instalar el mismo provider Upbound S3 con la misma tag (versión) dos veces, aunque con nombres diferentes, no trae ninguna ventaja real y puede causar problemas.

# No Hagas Esto
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-ec2-a
spec:
package: xpkg.upbound.io/upbound/provider-aws-ec2:v1
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-ec2-b
spec:
package: xpkg.upbound.io/upbound/provider-aws-ec2:v1

Cuando instalas el mismo provider con la misma versión, intentará registrar los mismos Custom Resource Definitions (CRDs), lo que resultará en:

  • Conflicto de recursos: Ambas instancias intentarán registrar los mismos CRDs, resultando en conflictos en el API Server
  • Controladores duplicados: Dos controladores intentando reconciliar los mismos recursos, llevando a comportamientos impredecibles
  • Desperdicio de recursos: Consumo innecesario de CPU y memoria para ejecutar controladores redundantes
  • Posible "race condition": Los controladores pueden competir entre sí durante la reconciliación de los recursos

La mayoría de estos problemas surgen porque los CRDs son recursos globales en Kubernetes, independientemente del namespace o del nombre de la instancia del provider. El patrón correcto para gestionar múltiples configuraciones es:

  • Instalar el provider apenas una vez
  • Crear múltiples ProviderConfigs que referencian diferentes credenciales o configuraciones
  • Especificar cuál ProviderConfig debe ser usado con cada recurso gestionado a través de la referencia providerConfigRef

Esto permite gestionar recursos en múltiples ambientes o cuentas sin la sobrecarga y los problemas causados por instancias duplicadas del mismo provider.

Importante Entender

Los providers crean y gestionan Managed Resources que representan recursos en las plataformas de destino.

Una Composition es un conjunto o template de recursos gestionados que son agrupados para formar una abstracción de nivel más alto. Si no entendiste, más adelante lo entenderás. Si fuéramos comparar sería como un módulo en terraform, pero no es exactamente así.

El Crossplane Core gestiona las Compositions y Composite Resources (XRs), no los providers. Los providers apenas "ven" e interactúan con los recursos gestionados individuales, no teniendo conocimiento directo de las abstracciones de nivel superior como Compositions.

Cuando defines una Composition, Crossplane Core es responsable de descomponerla en los recursos gestionados correspondientes, y entonces los providers cuidan de esos recursos individuales. Esta separación de responsabilidades es un aspecto fundamental de la arquitectura de Crossplane.

Continúa en Managed Resources para entender mejor sobre esto.