From 8fe421a72d0da3989cece79766d591982c1658aa Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Mon, 28 Oct 2024 10:31:25 -0700 Subject: [PATCH] update: Updated ACK/Crossplane app re-configuration (#1158) --- .../controlplanes/ack/.workshop/cleanup.sh | 1 - .../ack/.workshop/terraform/main.tf | 17 +- .../ack/.workshop/terraform/outputs.tf | 2 +- .../ack/app/carts-serviceAccount.yaml | 7 + .../controlplanes/ack/app/config.properties | 1 + .../controlplanes/ack/app/kustomization.yaml | 13 ++ .../ack/dynamodb/deployment.yaml | 14 -- .../ack/dynamodb/dynamodb-ack-configmap.yaml | 8 - .../ack/dynamodb/kustomization.yaml | 4 - .../mq/fieldexports/env-var-transformer.yaml | 5 - .../ack/mq/fieldexports/kustomization.yaml | 19 --- .../ack/mq/fieldexports/mq-ack-options.env | 1 - .../mq/fieldexports/mq-configmap-orders.yaml | 8 - .../fieldexports/mq-fieldexports-orders.yaml | 33 ---- .../ack/mq/k8s/env-var-transformer.yaml | 13 -- .../ack/mq/k8s/kustomization.yaml | 26 --- .../ack/mq/k8s/mq-ack-options.env | 2 - .../controlplanes/ack/mq/k8s/mq-broker.yaml | 25 --- .../ack/mq/k8s/mq-security-group.yaml | 15 -- .../ack/mq/roles/env-var-transformer.yaml | 5 - .../ack/mq/roles/kustomization.yaml | 25 --- .../ack/mq/roles/mq-ack-options.env | 2 - .../ack/mq/roles/mq-iam-role.yaml | 29 ---- .../ack/rds/application/deployment.yaml | 31 ---- .../ack/rds/application/kustomization.yaml | 7 - .../ack/rds/application/nlb.yaml | 19 --- .../rds/fieldexports/env-var-transformer.yaml | 7 - .../ack/rds/fieldexports/kustomization.yaml | 26 --- .../ack/rds/fieldexports/rds-ack-options.env | 2 - .../rds/fieldexports/rds-fieldexports.yaml | 35 ---- .../ack/rds/fieldexports/rds-secret.yaml | 7 - .../ack/rds/k8s/env-var-transformer.yaml | 44 ----- .../ack/rds/k8s/kustomization.yaml | 55 ------- .../ack/rds/k8s/rds-ack-options.env | 6 - .../ack/rds/k8s/rds-dbgroup.yaml | 12 -- .../ack/rds/k8s/rds-instance.yaml | 23 --- .../ack/rds/k8s/rds-security-group.yaml | 15 -- .../crossplane/.workshop/terraform/main.tf | 12 ++ .../crossplane/.workshop/terraform/outputs.tf | 2 +- .../crossplane/app/carts-serviceAccount.yaml | 7 + .../crossplane/app/config.properties | 1 + .../crossplane/app/kustomization.yaml | 13 ++ .../crossplane/application/deployment.yaml | 14 -- .../dynamodb-crossplane-configmap.yaml | 7 - .../crossplane/application/kustomization.yaml | 7 - .../ack/configure-application.md | 63 ++++--- .../controlplanes/ack/how-it-works.md | 12 +- .../automation/controlplanes/ack/index.md | 12 +- .../controlplanes/ack/provision-resources.md | 71 ++------ .../ack/tests/hook-create-secret.sh | 9 - .../controlplanes/crossplane/compositions.md | 154 ------------------ .../crossplane/compositions/claims.md | 71 ++++++++ .../compositions/creating-a-composition.md | 30 ++++ .../crossplane/compositions/index.md | 22 +++ .../crossplane/compositions/testing.md | 45 +++++ .../crossplane/configure-application.md | 80 +++++++++ .../controlplanes/crossplane/how-it-works.md | 14 +- .../controlplanes/crossplane/index.md | 14 +- .../controlplanes/crossplane/resources.md | 103 +----------- 59 files changed, 407 insertions(+), 920 deletions(-) create mode 100644 manifests/modules/automation/controlplanes/ack/app/carts-serviceAccount.yaml create mode 100644 manifests/modules/automation/controlplanes/ack/app/config.properties create mode 100644 manifests/modules/automation/controlplanes/ack/app/kustomization.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/dynamodb/deployment.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/dynamodb/dynamodb-ack-configmap.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/fieldexports/env-var-transformer.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/fieldexports/kustomization.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/fieldexports/mq-ack-options.env delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/fieldexports/mq-configmap-orders.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/fieldexports/mq-fieldexports-orders.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/k8s/env-var-transformer.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/k8s/kustomization.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/k8s/mq-ack-options.env delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/k8s/mq-broker.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/k8s/mq-security-group.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/roles/env-var-transformer.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/roles/kustomization.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/roles/mq-ack-options.env delete mode 100644 manifests/modules/automation/controlplanes/ack/mq/roles/mq-iam-role.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/application/deployment.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/application/kustomization.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/application/nlb.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/fieldexports/env-var-transformer.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/fieldexports/kustomization.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-ack-options.env delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-fieldexports.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-secret.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/k8s/env-var-transformer.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/k8s/kustomization.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/k8s/rds-ack-options.env delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/k8s/rds-dbgroup.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/k8s/rds-instance.yaml delete mode 100644 manifests/modules/automation/controlplanes/ack/rds/k8s/rds-security-group.yaml create mode 100644 manifests/modules/automation/controlplanes/crossplane/app/carts-serviceAccount.yaml create mode 100644 manifests/modules/automation/controlplanes/crossplane/app/config.properties create mode 100644 manifests/modules/automation/controlplanes/crossplane/app/kustomization.yaml delete mode 100644 manifests/modules/automation/controlplanes/crossplane/application/deployment.yaml delete mode 100644 manifests/modules/automation/controlplanes/crossplane/application/dynamodb-crossplane-configmap.yaml delete mode 100644 manifests/modules/automation/controlplanes/crossplane/application/kustomization.yaml delete mode 100644 website/docs/automation/controlplanes/ack/tests/hook-create-secret.sh delete mode 100644 website/docs/automation/controlplanes/crossplane/compositions.md create mode 100644 website/docs/automation/controlplanes/crossplane/compositions/claims.md create mode 100644 website/docs/automation/controlplanes/crossplane/compositions/creating-a-composition.md create mode 100644 website/docs/automation/controlplanes/crossplane/compositions/index.md create mode 100644 website/docs/automation/controlplanes/crossplane/compositions/testing.md create mode 100644 website/docs/automation/controlplanes/crossplane/configure-application.md diff --git a/manifests/modules/automation/controlplanes/ack/.workshop/cleanup.sh b/manifests/modules/automation/controlplanes/ack/.workshop/cleanup.sh index 3fa4b6bce..3fbeea43e 100755 --- a/manifests/modules/automation/controlplanes/ack/.workshop/cleanup.sh +++ b/manifests/modules/automation/controlplanes/ack/.workshop/cleanup.sh @@ -2,5 +2,4 @@ logmessage "Deleting resources created by ACK..." -eksctl delete iamserviceaccount --name carts-ack --namespace carts --cluster $EKS_CLUSTER_NAME -v 0 delete-all-if-crd-exists tables.dynamodb.services.k8s.aws \ No newline at end of file diff --git a/manifests/modules/automation/controlplanes/ack/.workshop/terraform/main.tf b/manifests/modules/automation/controlplanes/ack/.workshop/terraform/main.tf index d54a4ff57..695fa4c15 100644 --- a/manifests/modules/automation/controlplanes/ack/.workshop/terraform/main.tf +++ b/manifests/modules/automation/controlplanes/ack/.workshop/terraform/main.tf @@ -43,10 +43,22 @@ module "dynamodb_ack_addon" { tags = var.tags } +module "iam_assumable_role_carts" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" + version = "5.44.0" + create_role = true + role_name = "${var.addon_context.eks_cluster_id}-carts-ack" + provider_url = var.addon_context.eks_oidc_issuer_url + role_policy_arns = [aws_iam_policy.carts_dynamo.arn] + oidc_fully_qualified_subjects = ["system:serviceaccount:carts:carts"] + + tags = var.tags +} + resource "aws_iam_policy" "carts_dynamo" { name = "${var.addon_context.eks_cluster_id}-carts-dynamo" path = "/" - description = "DynamoDB policy for AWS Sample Carts Application" + description = "Dynamo policy for carts application" policy = < - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID):oidc-provider/$(OIDC_PROVIDER}" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringEquals": { - "${OIDC_PROVIDER}:sub": "system:serviceaccount:ack-system:ack-mq-controller" - } - } - } - ] - } diff --git a/manifests/modules/automation/controlplanes/ack/rds/application/deployment.yaml b/manifests/modules/automation/controlplanes/ack/rds/application/deployment.yaml deleted file mode 100644 index fa2858dd0..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/application/deployment.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: catalog - namespace: catalog -spec: - template: - spec: - containers: - - name: catalog - env: - - name: DB_USER - valueFrom: - secretKeyRef: - name: catalog-db-ack - key: username - - name: DB_PASSWORD - valueFrom: - secretKeyRef: - name: catalog-db-ack - key: password - - name: DB_READ_ENDPOINT - valueFrom: - secretKeyRef: - name: catalog-db-ack - key: endpoint - - name: DB_ENDPOINT - valueFrom: - secretKeyRef: - name: catalog-db-ack - key: endpoint diff --git a/manifests/modules/automation/controlplanes/ack/rds/application/kustomization.yaml b/manifests/modules/automation/controlplanes/ack/rds/application/kustomization.yaml deleted file mode 100644 index c9b9006a2..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/application/kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: - - ../../../../../../base-application/catalog - - nlb.yaml -patches: - - path: deployment.yaml diff --git a/manifests/modules/automation/controlplanes/ack/rds/application/nlb.yaml b/manifests/modules/automation/controlplanes/ack/rds/application/nlb.yaml deleted file mode 100644 index 5e457ff07..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/application/nlb.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: ui-nlb - annotations: - service.beta.kubernetes.io/aws-load-balancer-type: external - service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing - service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: instance - namespace: ui -spec: - type: LoadBalancer - ports: - - port: 80 - targetPort: 8080 - name: http - selector: - app.kubernetes.io/name: ui - app.kubernetes.io/instance: ui - app.kubernetes.io/component: service diff --git a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/env-var-transformer.yaml b/manifests/modules/automation/controlplanes/ack/rds/fieldexports/env-var-transformer.yaml deleted file mode 100644 index 03d350488..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/env-var-transformer.yaml +++ /dev/null @@ -1,7 +0,0 @@ -varReference: - - apiVersion: v1 - kind: Secret - path: stringData/password - - apiVersion: services.k8s.aws/v1alpha1 - kind: FieldExport - path: spec/from/resource/name diff --git a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/kustomization.yaml b/manifests/modules/automation/controlplanes/ack/rds/fieldexports/kustomization.yaml deleted file mode 100644 index e81416acc..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/kustomization.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -configMapGenerator: - - name: rds-ack-options - envs: - - rds-ack-options.env -vars: - - name: EKS_CLUSTER_NAME - objref: - kind: ConfigMap - name: rds-ack-options - apiVersion: v1 - fieldref: - fieldpath: data.EKS_CLUSTER_NAME - - name: CATALOG_PASSWORD - objref: - kind: ConfigMap - name: rds-ack-options - apiVersion: v1 - fieldref: - fieldpath: data.CATALOG_PASSWORD -configurations: - - env-var-transformer.yaml -resources: - - rds-secret.yaml - - rds-fieldexports.yaml diff --git a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-ack-options.env b/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-ack-options.env deleted file mode 100644 index 07f7fec5d..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-ack-options.env +++ /dev/null @@ -1,2 +0,0 @@ -EKS_CLUSTER_NAME -CATALOG_PASSWORD diff --git a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-fieldexports.yaml b/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-fieldexports.yaml deleted file mode 100644 index ca9f57848..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-fieldexports.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: services.k8s.aws/v1alpha1 -kind: FieldExport -metadata: - name: catalog-db-endpoint - namespace: catalog -spec: - to: - name: catalog-db-ack - kind: secret - namespace: catalog - key: endpoint - from: - path: ".status.endpoint.address" - resource: - group: rds.services.k8s.aws - kind: DBInstance - name: ${EKS_CLUSTER_NAME}-catalog-ack ---- -apiVersion: services.k8s.aws/v1alpha1 -kind: FieldExport -metadata: - name: catalog-db-user - namespace: catalog -spec: - to: - name: catalog-db-ack - kind: secret - namespace: catalog - key: username - from: - path: ".spec.masterUsername" - resource: - group: rds.services.k8s.aws - kind: DBInstance - name: ${EKS_CLUSTER_NAME}-catalog-ack diff --git a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-secret.yaml b/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-secret.yaml deleted file mode 100644 index ead30e685..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-secret.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: catalog-db-ack - namespace: catalog -stringData: - password: ${CATALOG_PASSWORD} diff --git a/manifests/modules/automation/controlplanes/ack/rds/k8s/env-var-transformer.yaml b/manifests/modules/automation/controlplanes/ack/rds/k8s/env-var-transformer.yaml deleted file mode 100644 index 47c53ae3e..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/k8s/env-var-transformer.yaml +++ /dev/null @@ -1,44 +0,0 @@ -varReference: - - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: SecurityGroup - path: spec/vpcID - value: rds-ack-options - - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: SecurityGroup - path: metadata/name - value: rds-ack-options - - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: SecurityGroup - path: spec/name - value: rds-ack-options - - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: SecurityGroup - path: spec/ingressRules[]/ipRanges[]/cidrIP - - apiVersion: rds.services.k8s.aws/v1alpha1 - kind: DBSubnetGroup - path: spec/subnetIDs - value: rds-ack-options - - apiVersion: rds.services.k8s.aws/v1alpha1 - kind: DBSubnetGroup - path: metadata/name - value: rds-ack-options - - apiVersion: rds.services.k8s.aws/v1alpha1 - kind: DBSubnetGroup - path: spec/name - value: rds-ack-options - - apiVersion: rds.services.k8s.aws/v1alpha1 - kind: DBInstance - path: spec/dbSubnetGroupRef/from/name - value: rds-ack-options - - apiVersion: rds.services.k8s.aws/v1alpha1 - kind: DBInstance - path: metadata/name - value: rds-ack-options - - apiVersion: rds.services.k8s.aws/v1alpha1 - kind: DBInstance - path: spec/dbInstanceIdentifier - value: rds-ack-options - - apiVersion: rds.services.k8s.aws/v1alpha1 - kind: DBInstance - path: spec/vpcSecurityGroupRefs[]/from/name - value: rds-ack-options diff --git a/manifests/modules/automation/controlplanes/ack/rds/k8s/kustomization.yaml b/manifests/modules/automation/controlplanes/ack/rds/k8s/kustomization.yaml deleted file mode 100644 index 860168862..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/k8s/kustomization.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -configMapGenerator: - - name: rds-ack-options - envs: - - rds-ack-options.env -vars: - - name: EKS_CLUSTER_NAME - objref: - kind: ConfigMap - name: rds-ack-options - apiVersion: v1 - fieldref: - fieldpath: data.EKS_CLUSTER_NAME - - name: VPC_ID - objref: - kind: ConfigMap - name: rds-ack-options - apiVersion: v1 - fieldref: - fieldpath: data.VPC_ID - - name: VPC_CIDR - objref: - kind: ConfigMap - name: rds-ack-options - apiVersion: v1 - fieldref: - fieldpath: data.VPC_CIDR - - name: VPC_PRIVATE_SUBNET_ID_3 - objref: - kind: ConfigMap - name: rds-ack-options - apiVersion: v1 - fieldref: - fieldpath: data.VPC_PRIVATE_SUBNET_ID_3 - - name: VPC_PRIVATE_SUBNET_ID_1 - objref: - kind: ConfigMap - name: rds-ack-options - apiVersion: v1 - fieldref: - fieldpath: data.VPC_PRIVATE_SUBNET_ID_1 - - name: VPC_PRIVATE_SUBNET_ID_2 - objref: - kind: ConfigMap - name: rds-ack-options - apiVersion: v1 - fieldref: - fieldpath: data.VPC_PRIVATE_SUBNET_ID_2 -configurations: - - env-var-transformer.yaml -resources: - - rds-security-group.yaml - - rds-dbgroup.yaml - - rds-instance.yaml diff --git a/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-ack-options.env b/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-ack-options.env deleted file mode 100644 index e1659844c..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-ack-options.env +++ /dev/null @@ -1,6 +0,0 @@ -VPC_ID -VPC_PRIVATE_SUBNET_ID_3 -VPC_PRIVATE_SUBNET_ID_1 -VPC_PRIVATE_SUBNET_ID_2 -EKS_CLUSTER_NAME -VPC_CIDR diff --git a/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-dbgroup.yaml b/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-dbgroup.yaml deleted file mode 100644 index d6451d319..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-dbgroup.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rds.services.k8s.aws/v1alpha1 -kind: DBSubnetGroup -metadata: - name: ${EKS_CLUSTER_NAME}-catalog-ack - namespace: catalog -spec: - description: DBSubnet group - name: ${EKS_CLUSTER_NAME}-catalog-ack - subnetIDs: - - ${VPC_PRIVATE_SUBNET_ID_1} - - ${VPC_PRIVATE_SUBNET_ID_2} - - ${VPC_PRIVATE_SUBNET_ID_3} diff --git a/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-instance.yaml b/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-instance.yaml deleted file mode 100644 index 12539ac96..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-instance.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: rds.services.k8s.aws/v1alpha1 -kind: DBInstance -metadata: - name: ${EKS_CLUSTER_NAME}-catalog-ack - namespace: catalog -spec: - allocatedStorage: 20 - dbInstanceClass: db.t4g.micro - dbInstanceIdentifier: ${EKS_CLUSTER_NAME}-catalog-ack - engine: mysql - engineVersion: "8.0" - masterUsername: "admin" - dbSubnetGroupRef: - from: - name: ${EKS_CLUSTER_NAME}-catalog-ack - vpcSecurityGroupRefs: - - from: - name: ${EKS_CLUSTER_NAME}-catalog-ack - masterUserPassword: - namespace: catalog - name: catalog-rds-pw - key: password - dbName: catalog diff --git a/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-security-group.yaml b/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-security-group.yaml deleted file mode 100644 index e5d94901a..000000000 --- a/manifests/modules/automation/controlplanes/ack/rds/k8s/rds-security-group.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: ec2.services.k8s.aws/v1alpha1 -kind: SecurityGroup -metadata: - name: ${EKS_CLUSTER_NAME}-catalog-ack - namespace: catalog -spec: - description: SecurityGroup - name: ${EKS_CLUSTER_NAME}-catalog-ack - vpcID: ${VPC_ID} - ingressRules: - - ipProtocol: tcp - ipRanges: - - cidrIP: "${VPC_CIDR}" - fromPort: 3306 - toPort: 3306 diff --git a/manifests/modules/automation/controlplanes/crossplane/.workshop/terraform/main.tf b/manifests/modules/automation/controlplanes/crossplane/.workshop/terraform/main.tf index 494a02980..445f0bd06 100644 --- a/manifests/modules/automation/controlplanes/crossplane/.workshop/terraform/main.tf +++ b/manifests/modules/automation/controlplanes/crossplane/.workshop/terraform/main.tf @@ -100,6 +100,18 @@ resource "kubectl_manifest" "upbound_aws_provider_config" { depends_on = [kubectl_manifest.upbound_aws_provider] } +module "iam_assumable_role_carts" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" + version = "5.44.0" + create_role = true + role_name = "${var.addon_context.eks_cluster_id}-carts-crossplane" + provider_url = var.addon_context.eks_oidc_issuer_url + role_policy_arns = [aws_iam_policy.carts_dynamo.arn] + oidc_fully_qualified_subjects = ["system:serviceaccount:carts:carts"] + + tags = var.tags +} + resource "aws_iam_policy" "carts_dynamo" { name = "${var.addon_context.eks_cluster_id}-carts-dynamo" path = "/" diff --git a/manifests/modules/automation/controlplanes/crossplane/.workshop/terraform/outputs.tf b/manifests/modules/automation/controlplanes/crossplane/.workshop/terraform/outputs.tf index f9c7baa3e..30b26b09d 100644 --- a/manifests/modules/automation/controlplanes/crossplane/.workshop/terraform/outputs.tf +++ b/manifests/modules/automation/controlplanes/crossplane/.workshop/terraform/outputs.tf @@ -1,6 +1,6 @@ output "environment_variables" { description = "Environment variables to be added to the IDE shell" value = { - DYNAMODB_POLICY_ARN = aws_iam_policy.carts_dynamo.arn + CARTS_IAM_ROLE = module.iam_assumable_role_carts.iam_role_arn } } \ No newline at end of file diff --git a/manifests/modules/automation/controlplanes/crossplane/app/carts-serviceAccount.yaml b/manifests/modules/automation/controlplanes/crossplane/app/carts-serviceAccount.yaml new file mode 100644 index 000000000..a3b29d7bb --- /dev/null +++ b/manifests/modules/automation/controlplanes/crossplane/app/carts-serviceAccount.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: carts + namespace: carts + annotations: + eks.amazonaws.com/role-arn: ${CARTS_IAM_ROLE} diff --git a/manifests/modules/automation/controlplanes/crossplane/app/config.properties b/manifests/modules/automation/controlplanes/crossplane/app/config.properties new file mode 100644 index 000000000..5613f07a4 --- /dev/null +++ b/manifests/modules/automation/controlplanes/crossplane/app/config.properties @@ -0,0 +1 @@ +CARTS_DYNAMODB_TABLENAME=${EKS_CLUSTER_NAME}-carts-crossplane \ No newline at end of file diff --git a/manifests/modules/automation/controlplanes/crossplane/app/kustomization.yaml b/manifests/modules/automation/controlplanes/crossplane/app/kustomization.yaml new file mode 100644 index 000000000..1677bba67 --- /dev/null +++ b/manifests/modules/automation/controlplanes/crossplane/app/kustomization.yaml @@ -0,0 +1,13 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ../../../../../base-application/carts +patches: + - path: carts-serviceAccount.yaml +configMapGenerator: + - name: carts + namespace: carts + env: config.properties + behavior: replace + options: + disableNameSuffixHash: true diff --git a/manifests/modules/automation/controlplanes/crossplane/application/deployment.yaml b/manifests/modules/automation/controlplanes/crossplane/application/deployment.yaml deleted file mode 100644 index 1120bb1e9..000000000 --- a/manifests/modules/automation/controlplanes/crossplane/application/deployment.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: carts - namespace: carts -spec: - template: - spec: - containers: - - name: carts - envFrom: - - configMapRef: - name: carts-crossplane - serviceAccountName: carts-crossplane diff --git a/manifests/modules/automation/controlplanes/crossplane/application/dynamodb-crossplane-configmap.yaml b/manifests/modules/automation/controlplanes/crossplane/application/dynamodb-crossplane-configmap.yaml deleted file mode 100644 index e7627b2d3..000000000 --- a/manifests/modules/automation/controlplanes/crossplane/application/dynamodb-crossplane-configmap.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: carts-crossplane - namespace: carts -data: - CARTS_DYNAMODB_TABLENAME: "${EKS_CLUSTER_NAME}-carts-crossplane" diff --git a/manifests/modules/automation/controlplanes/crossplane/application/kustomization.yaml b/manifests/modules/automation/controlplanes/crossplane/application/kustomization.yaml deleted file mode 100644 index ff1b2a2ce..000000000 --- a/manifests/modules/automation/controlplanes/crossplane/application/kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: - - ../../../../../base-application/carts - - dynamodb-crossplane-configmap.yaml -patches: - - path: deployment.yaml diff --git a/website/docs/automation/controlplanes/ack/configure-application.md b/website/docs/automation/controlplanes/ack/configure-application.md index b2486a6e5..f79588cf4 100644 --- a/website/docs/automation/controlplanes/ack/configure-application.md +++ b/website/docs/automation/controlplanes/ack/configure-application.md @@ -1,66 +1,83 @@ --- -title: "Updating the application with new resources" +title: "Updating the application" sidebar_position: 10 --- -When new resources are created or updated, application configurations also need to be updated to use these new resources. Environment variables are a popular choice for application developers to store configuration, and in Kubernetes we can pass environment variables to containers through the `env` field of the `container` [spec](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) when creating deployments. +When new resources are created or updated, application configurations often need to be adjusted to utilize these new resources. In Kubernetes, environment variables are a popular choice for storing configuration, and can be passed to containers through the `env` field of the `container` [spec](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) when creating deployments. -Now, there are two ways to achieve this. +There are two primary methods to achieve this: -- First, Configmaps. Configmaps are a core resource in Kubernetes that allow us to pass configuration elements such as Environment variables, text fields and other items in a key-value format to be used in pod specs. -- Then, we have secrets (which are not encrypted by design - this is important to remember) to push things like passwords/secrets. +1. **Configmaps**: These are core Kubernetes resources that allow us to pass configuration elements such as environment variables, text fields, and other items in a key-value format to be used in pod specs. -The ACK `FieldExport` [custom resource](https://aws-controllers-k8s.github.io/community/docs/user-docs/field-export/) was designed to bridge the gap between managing the control plane of your ACK resources and using the _properties_ of those resources in your application. This configures an ACK controller to export any `spec` or `status` field from an ACK resource into a Kubernetes ConfigMap or Secret. These fields are automatically updated when any field value changes. You are then able to mount the ConfigMap or Secret onto your Kubernetes Pods as environment variables that can ingest those values. +2. **Secrets**: These are similar to Configmaps but are intended for sensitive information. It's important to note that Secrets are not encrypted by default in Kubernetes. -However, in the case of DynamoDB in this section of the lab, we will use a direct mapping of the API endpoint by using ConfigMaps, and simply updating the DynamoDB endpoint as an environment variable. +The ACK `FieldExport` [custom resource](https://aws-controllers-k8s.github.io/community/docs/user-docs/field-export/) was designed to bridge the gap between managing the control plane of your ACK resources and using the _properties_ of those resources in your application. It configures an ACK controller to export any `spec` or `status` field from an ACK resource into a Kubernetes ConfigMap or Secret. These fields are automatically updated when any field value changes, allowing you to mount the ConfigMap or Secret onto your Kubernetes Pods as environment variables. -```file -manifests/modules/automation/controlplanes/ack/dynamodb/deployment.yaml +For this lab, we'll directly update the ConfigMap for the carts component. We'll remove the configuration that points it to the local DynamoDB and use the name of the DynamoDB table created by ACK: + +```kustomization +modules/automation/controlplanes/ack/app/kustomization.yaml +ConfigMap/carts ``` -In the new Deployment manifest (which we've already applied), we're updating the `envFrom` attribute `configMapRef` to `carts-ack`. This tells Kubernetes to pick up the environment variable from the new ConfigMap we've created. +We also need to provide the carts Pods with the appropriate IAM permissions to access the DynamoDB service. An IAM role has already been created, and we'll apply this to the carts Pods using IAM Roles for Service Accounts (IRSA): -```file -manifests/modules/automation/controlplanes/ack/dynamodb/dynamodb-ack-configmap.yaml +```kustomization +modules/automation/controlplanes/ack/app/carts-serviceAccount.yaml +ServiceAccount/carts ``` -And when we apply these manifests using Kustomize as we've done in the earlier section, the **Carts** component gets updated as well. +To learn more about how IRSA works, see [here](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). ---- +Let's apply this new configuration: + +```bash +$ kubectl kustomize ~/environment/eks-workshop/modules/automation/controlplanes/ack/app \ + | envsubst | kubectl apply -f- +``` + +Now we need to restart the carts Pods to pick up our new ConfigMap contents: -Now, how do we know that the application is working with the new DynamoDB table? +```bash +$ kubectl rollout restart -n carts deployment/carts +deployment.apps/carts restarted +$ kubectl rollout status -n carts deployment/carts --timeout=40s +Waiting for deployment "carts" rollout to finish: 1 old replicas are pending termination... +deployment "carts" successfully rolled out +``` -An NLB has been created to expose the sample application for testing, allowing us to directly interact with the application through the browser: +To verify that the application is working with the new DynamoDB table, we can interact with it through a browser. An NLB has been created to expose the sample application for testing: ```bash -$ kubectl get service -n ui ui-nlb -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}" -k8s-ui-uinlb-a9797f0f61.elb.us-west-2.amazonaws.com +$ LB_HOSTNAME=$(kubectl -n ui get service ui-nlb -o jsonpath='{.status.loadBalancer.ingress[*].hostname}{"\n"}') +$ echo "http://$LB_HOSTNAME" +http://k8s-ui-uinlb-647e781087-6717c5049aa96bd9.elb.us-west-2.amazonaws.com ``` :::info Please note that the actual endpoint will be different when you run this command as a new Network Load Balancer endpoint will be provisioned. ::: -To wait until the load balancer has finished provisioning you can run this command: +To wait until the load balancer has finished provisioning, you can run this command: ```bash timeout=610 $ wait-for-lb $(kubectl get service -n ui ui-nlb -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}") ``` -Once the load balancer is provisioned you can access it by pasting the URL in your web browser. You will see the UI from the web store displayed and will be able to navigate around the site as a user. +Once the load balancer is provisioned, you can access it by pasting the URL in your web browser. You'll see the UI from the web store displayed and will be able to navigate around the site as a user. -To verify that the **Carts** module is in fact using the DynamoDB table we just provisioned, try adding a few items to the cart. +To verify that the **Carts** module is indeed using the DynamoDB table we just provisioned, try adding a few items to the cart. ![Cart screenshot](./assets/cart-items-present.webp) -And to check if items are in the cart as well, run +To confirm that these items are also in the DynamoDB table, run: ```bash $ aws dynamodb scan --table-name "${EKS_CLUSTER_NAME}-carts-ack" ``` -Congratulations! You've successfully created AWS Resources without leaving the confines of the Kubernetes API! +Congratulations! You've successfully created AWS Resources without leaving the Kubernetes API! diff --git a/website/docs/automation/controlplanes/ack/how-it-works.md b/website/docs/automation/controlplanes/ack/how-it-works.md index d7c3d0e66..f7eb58640 100644 --- a/website/docs/automation/controlplanes/ack/how-it-works.md +++ b/website/docs/automation/controlplanes/ack/how-it-works.md @@ -3,22 +3,24 @@ title: "How does ACK work?" sidebar_position: 5 --- -Each ACK service controller is packaged into a separate container image that is published in a public repository corresponding to an individual ACK service controller. For each AWS service that we wish to provision, resources for the corresponding controller must be installed in the Amazon EKS cluster. We've already done this in the `prepare-environment` step. Helm charts and official container images for ACK are available [here](https://gallery.ecr.aws/aws-controllers-k8s). +Each AWS Controller for Kubernetes (ACK) is packaged as a separate container image, published in a public repository corresponding to an individual ACK service controller. To provision resources for a specific AWS service, the corresponding controller must be installed in the Amazon EKS cluster. We've already completed this step in the `prepare-environment` phase. Official container images and Helm charts for ACK are available [here](https://gallery.ecr.aws/aws-controllers-k8s). -In this section of the workshop, as we will be working with Amazon DynamoDB, the ACK controllers for DynamoDB has been pre-installed in the cluster, running as a deployment in its own Kubernetes namespace. To see what's under the hood, lets run the below. +In this workshop section, we'll be working with Amazon DynamoDB. The ACK controller for DynamoDB has been pre-installed in the cluster, running as a deployment in its own Kubernetes namespace. To examine the deployment details, run the following command: ```bash $ kubectl describe deployment ack-dynamodb -n ack-dynamodb ``` :::info -kubectl also contains useful `-oyaml` and `-ojson` flags which extract either the full YAML or JSON manifests of the deployment definition instead of the formatted output. +kubectl also provides useful `-oyaml` and `-ojson` flags which extract the full YAML or JSON manifests of the deployment definition, respectively, instead of the formatted output. ::: -This controller will watch for Kubernetes custom resources for DynamoDB such as `dynamodb.services.k8s.aws.Table` and will make API calls to the DynamoDB endpoint based on the configuration in these resources created. As resources are created, the controller will feed back status updates to the custom resources in the `Status` fields. For more information about the spec of the manifest, click [here](https://aws-controllers-k8s.github.io/community/reference/). +This controller watches for Kubernetes custom resources specific to DynamoDB, such as `dynamodb.services.k8s.aws.Table`. Based on the configuration in these resources, it makes API calls to the DynamoDB endpoint. As resources are created or modified, the controller updates the status of the custom resources by populating the `Status` fields. For more information about the manifest specifications, refer to the [ACK reference documentation](https://aws-controllers-k8s.github.io/community/reference/). -If you'd like to dive deeper into the mechanics of what objects and API calls the controller listens for, run: +To gain deeper insight into the objects and API calls the controller listens for, you can run: ```bash $ kubectl get crd ``` + +This command will display all the Custom Resource Definitions (CRDs) in your cluster, including those related to ACK and DynamoDB. diff --git a/website/docs/automation/controlplanes/ack/index.md b/website/docs/automation/controlplanes/ack/index.md index df523d999..625c89e05 100644 --- a/website/docs/automation/controlplanes/ack/index.md +++ b/website/docs/automation/controlplanes/ack/index.md @@ -2,7 +2,7 @@ title: "AWS Controllers for Kubernetes (ACK)" sidebar_position: 1 sidebar_custom_props: { "module": true } -description: "Directly managed AWS services from Amazon Elastic Kubernetes Service with Amazon Controllers for Kubernetes." +description: "Directly manage AWS services from Amazon Elastic Kubernetes Service with AWS Controllers for Kubernetes." --- ::required-time @@ -23,14 +23,14 @@ You can view the Terraform that applies these changes [here](https://github.com/ ::: -The [AWS Controllers for Kubernetes (ACK)](https://aws-controllers-k8s.github.io/community/) project lets you define and use AWS service resources directly from Kubernetes using familiar YAML constructs. +The [AWS Controllers for Kubernetes (ACK)](https://aws-controllers-k8s.github.io/community/) project enables you to define and use AWS service resources directly from Kubernetes using familiar YAML constructs. -With ACK, you can take advantage of using AWS services such as databases ([RDS](https://aws-controllers-k8s.github.io/community/docs/tutorials/rds-example/) or others) and/or queues ([SQS](https://aws-controllers-k8s.github.io/community/docs/tutorials/sqs-example/) etc) for your Kubernetes applications without having to define resources manually outside of the cluster. This reduces the overall complexity for managing the dependencies of your application. +With ACK, you can leverage AWS services such as databases ([RDS](https://aws-controllers-k8s.github.io/community/docs/tutorials/rds-example/) or others) and queues ([SQS](https://aws-controllers-k8s.github.io/community/docs/tutorials/sqs-example/) etc.) for your Kubernetes applications without manually defining resources outside of the cluster. This reduces the overall complexity of managing your application's dependencies. -The sample application could be run completely within your cluster, including stateful workloads like database and message queues. This is a good approach when you're developing the application. However, when the team wants to make the application available in other stages like testing and production, they will use AWS managed services such as Amazon DynamoDB databases and Amazon MQ brokers. This allows the team to focus on its customers and business projects and not have to administer and manage databases or message brokers. +While the sample application can run entirely within your cluster, including stateful workloads like databases and message queues (which is suitable for development), using AWS managed services such as Amazon DynamoDB and Amazon MQ in testing and production environments allows your team to focus on customers and business projects rather than administering databases or message brokers. -In this lab, we'll leverage ACK to provision these services and create secrets and configmaps containing the binding information connecting the application to these AWS managed services. +In this lab, we'll use ACK to provision these services and create secrets and configmaps containing the binding information to connect the application to these AWS managed services. -An important point to note here is that during the provisioning process above, we're using the new ACK Terraform module which allows you to rapidly deploy AWS Service Controllers to your cluster. See [here](https://registry.terraform.io/modules/aws-ia/eks-ack-addons/aws/latest#module_dynamodb) for more information. +It's worth noting that during the provisioning process, we're using the new ACK Terraform module, which allows for rapid deployment of AWS Service Controllers to your cluster. For more information, see the [ACK Terraform module documentation](https://registry.terraform.io/modules/aws-ia/eks-ack-addons/aws/latest#module_dynamodb). ![EKS with DynamoDB](./assets/eks-workshop-ddb.webp) diff --git a/website/docs/automation/controlplanes/ack/provision-resources.md b/website/docs/automation/controlplanes/ack/provision-resources.md index ea86cbc57..69332c6c2 100644 --- a/website/docs/automation/controlplanes/ack/provision-resources.md +++ b/website/docs/automation/controlplanes/ack/provision-resources.md @@ -1,84 +1,31 @@ --- -title: "Provisioning ACK Resources" +title: "Provisioning ACK resources" sidebar_position: 5 --- -By default the **Carts** component in the sample application uses a DynamoDB local instance running as a pod in the EKS cluster called `carts-dynamodb`. In this section of the lab, we'll provision an Amazon DynamoDB cloud based table for our application using Kubernetes custom resources and point the **Carts** deployment to use the newly provisioned DynamoDB table instead of the local copy. +By default, the **Carts** component in the sample application uses a DynamoDB local instance running as a pod in the EKS cluster called `carts-dynamodb`. In this section of the lab, we'll provision an Amazon DynamoDB cloud-based table for our application using Kubernetes custom resources and configure the **Carts** deployment to use this newly provisioned DynamoDB table instead of the local copy. ![ACK reconciler concept](./assets/ack-desired-current-ddb.webp) -The AWS Java SDK in the **Carts** component is able to use IAM Roles to interact with AWS services which means that we do not need to pass credentials, thus reducing the attack surface. In the EKS context, IRSA allows us to define per pod IAM Roles for applications to consume. To leverage IRSA, we first need to: - -- Create a Kubernetes Service Account in the Carts namespace -- Create an IAM Policy with necessary DynamoDB permissions -- Create an IAM Role in AWS with the above permissions -- Map the Service Account to use the IAM role using Annotations in the Service Account definition. - -Fortunately, we have a handy one-liner to help with this process. Run the below: - -```bash -$ eksctl create iamserviceaccount --name carts-ack \ - --namespace carts --cluster $EKS_CLUSTER_NAME \ - --role-name ${EKS_CLUSTER_NAME}-carts-ack \ - --attach-policy-arn $DYNAMODB_POLICY_ARN --approve -2023-10-31 16:20:46 [i] 1 iamserviceaccount (carts/carts-ack) was included (based on the include/exclude rules) -2023-10-31 16:20:46 [i] 1 task: { - 2 sequential sub-tasks: { - create IAM role for serviceaccount "carts/carts-ack", - create serviceaccount "carts/carts-ack", - } }2023-10-31 16:20:46 [ℹ] building iamserviceaccount stack "eksctl-eks-workshop-addon-iamserviceaccount-carts-carts-ack" -2023-10-31 16:20:46 [i] deploying stack "eksctl-eks-workshop-addon-iamserviceaccount-carts-carts-ack" -2023-10-31 16:20:47 [i] waiting for CloudFormation stack "eksctl-eks-workshop-addon-iamserviceaccount-carts-carts-ack" -2023-10-31 16:21:17 [i] waiting for CloudFormation stack "eksctl-eks-workshop-addon-iamserviceaccount-carts-carts-ack" -2023-10-31 16:21:17 [i] created serviceaccount "carts/carts-ack" -``` - -`eksctl` provisions a CloudFormation stack to help manage these resources which can be seen in the output above. - -To learn more about how IRSA works, go [here](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - ---- - -Now, let's explore how we'll create the DynamoDB Table via a Kubernetes manifest +Let's examine how we can create the DynamoDB Table using a Kubernetes manifest: ```file manifests/modules/automation/controlplanes/ack/dynamodb/dynamodb-create.yaml ``` :::info - -Astute readers will notice the YAML Spec to be similar to the API endpoints and calls for DynamoDB such as `tableName` and `attributeDefinitions`. - +Keen observers will notice that the YAML Spec closely resembles the API endpoints and calls for DynamoDB, including familiar fields such as `tableName` and `attributeDefinitions`. ::: -Next, we will need to update the regional endpoint for DynamoDB within the Configmap used by the Kustomization to update the **Carts** deployment. - -```file -manifests/modules/automation/controlplanes/ack/dynamodb/dynamodb-ack-configmap.yaml -``` - -Using the `envsubst` utility, we will rewrite the environment variable AWS_REGION into the manifest and apply all the updates to the cluster. Run the below +Now, let's apply these updates to the cluster: ```bash wait=10 $ kubectl kustomize ~/environment/eks-workshop/modules/automation/controlplanes/ack/dynamodb \ | envsubst | kubectl apply -f- -namespace/carts unchanged -serviceaccount/carts unchanged -configmap/carts unchanged -configmap/carts-ack created -service/carts unchanged -service/carts-dynamodb unchanged -deployment.apps/carts configured -deployment.apps/carts-dynamodb unchanged table.dynamodb.services.k8s.aws/items created -$ kubectl rollout status -n carts deployment/carts --timeout=120s ``` -:::info -This command 'builds' the manifests using the kubectl kustomize command, pipes it to `envsubst` and then to kubectl apply. This makes it easy to template manifests and populate them at run-time. -::: - -The ACK controllers in the cluster will react to these new resources and provision the AWS infrastructure we have expressed with the manifests earlier. Lets check if ACK created the table by running +The ACK controllers in the cluster will respond to these new resources and provision the AWS infrastructure we've defined in the manifests. To verify that ACK has created the table, run the following command: ```bash timeout=300 $ kubectl wait table.dynamodb.services.k8s.aws items -n carts --for=condition=ACK.ResourceSynced --timeout=15m @@ -87,7 +34,7 @@ $ kubectl get table.dynamodb.services.k8s.aws items -n carts -ojson | yq '.statu ACTIVE ``` -And now to check if the Table has been created using the AWS CLI, run +Finally, let's confirm that the table has been created using the AWS CLI: ```bash $ aws dynamodb list-tables @@ -99,4 +46,6 @@ $ aws dynamodb list-tables } ``` -This output tells us that the new table has been created! +This output confirms that our new table has been successfully created! + +By leveraging ACK, we've seamlessly provisioned a cloud-based DynamoDB table directly from our Kubernetes cluster, demonstrating the power and flexibility of this approach to managing AWS resources. diff --git a/website/docs/automation/controlplanes/ack/tests/hook-create-secret.sh b/website/docs/automation/controlplanes/ack/tests/hook-create-secret.sh deleted file mode 100644 index 46fc08c4b..000000000 --- a/website/docs/automation/controlplanes/ack/tests/hook-create-secret.sh +++ /dev/null @@ -1,9 +0,0 @@ -before() { - kubectl -n catalog delete secret catalog-rds-pw || true -} - -after() { - echo "noop" -} - -"$@" diff --git a/website/docs/automation/controlplanes/crossplane/compositions.md b/website/docs/automation/controlplanes/crossplane/compositions.md deleted file mode 100644 index 269cb9fde..000000000 --- a/website/docs/automation/controlplanes/crossplane/compositions.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -title: "Compositions" -sidebar_position: 30 ---- - -In addition to provisioning individual cloud resources, Crossplane offers a higher abstraction layer called Compositions. Compositions allow users to build opinionated templates for deploying cloud resources. For example, organizations may require certain tags to be present to all AWS resources or add specific encryption keys for all Amazon Simple Storage (S3) buckets. Platform teams can define these self-service API abstractions within Compositions and ensure that all the resources created through these Compositions meet the organization’s requirements. - -A `CompositeResourceDefinition` (or XRD) defines the type and schema of your Composite Resource (XR). It lets Crossplane know that you want a particular kind of XR to exist, and what fields that XR should have. An XRD is a little like a CustomResourceDefinition (CRD), but slightly more opinionated. Writing an XRD is mostly a matter of specifying an OpenAPI ["structural schema"](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/). - -First, lets provide a definition that can be used to create a DynamoDB table by members of the application team in their corresponding namespace. In this example the user only needs to specify **name**, **key attributes** and **index name** fields. - -```file -manifests/modules/automation/controlplanes/crossplane/compositions/composition/definition.yaml -``` - -A Composition lets Crossplane know what to do when someone creates a Composite Resource. Each Composition creates a link between an XR and a set of one or more Managed Resources - when the XR is created, updated, or deleted the set of Managed Resources are created, updated or deleted accordingly. - -The following Composition provisions the managed resources `Table` - -```file -manifests/modules/automation/controlplanes/crossplane/compositions/composition/table.yaml -``` - -Apply this to our EKS cluster: - -```bash -$ kubectl apply -k ~/environment/eks-workshop/modules/automation/controlplanes/crossplane/compositions/composition -compositeresourcedefinition.apiextensions.crossplane.io/xdynamodbtables.awsblueprints.io created -composition.apiextensions.crossplane.io/table.dynamodb.awsblueprints.io created -``` - -Once we’ve configured Crossplane with the details of the new XR we can either create one directly or use a Claim. Typically only the team responsible for configuring Crossplane (often a platform or SRE team) have permission to create XRs directly. Everyone else manages XRs via a lightweight proxy resource called a Composite Resource Claim (or claim for short). - -With this claim the developer only needs to specify a default **DynamoDB table name, hash keys, global index name** to create the table. This allows the platform or SRE team to standardize on aspects such as billing mode, default read/write capacity, projection type, cost and infrastructure related tags. - -```file -manifests/modules/automation/controlplanes/crossplane/compositions/claim/claim.yaml -``` - -Cleanup the Dynamodb table created from the previous Managed Resource section. - -```bash -$ kubectl delete tables.dynamodb.aws.upbound.io --all --ignore-not-found=true -$ kubectl wait --for=delete tables.dynamodb.aws.upbound.io --all --timeout=5m -``` - -Create the table by creating a `Claim`: - -```bash timeout=400 -$ cat ~/environment/eks-workshop/modules/automation/controlplanes/crossplane/compositions/claim/claim.yaml \ - | envsubst | kubectl -n carts apply -f - -dynamodbtable.awsblueprints.io/eks-workshop-carts-crossplane created -$ kubectl wait dynamodbtables.awsblueprints.io ${EKS_CLUSTER_NAME}-carts-crossplane -n carts \ - --for=condition=Ready --timeout=5m -``` - -It takes some time to provision the AWS managed services, in the case of DynamoDB up to 2 minutes. Crossplane will report the status of the reconciliation in the `SYNCED` field of the Kubernetes Composite and Managed resource. - -```bash -$ kubectl get table -NAME READY SYNCED EXTERNAL-NAME AGE -eks-workshop-carts-crossplane-bt28w-lnb4r True True eks-workshop-carts-crossplane 6s -``` - ---- - -Now, lets try to understand how the DynamoDB table is deployed using this claim: - -![Crossplane reconciler concept](./assets/ddb-claim-architecture.webp) - -On querying the claim `DynamoDBTable` deployed in the carts namespace, we can observe that it points to and creates a Composite Resource (XR) `XDynamoDBTable` - -```bash -$ kubectl get DynamoDBTable -n carts -o yaml | grep "resourceRef:" -A 3 - - resourceRef: - apiVersion: awsblueprints.io/v1alpha1 - kind: XDynamoDBTable - name: eks-workshop-carts-crossplane-bt28w -``` - -The Composition `table.dynamodb.awsblueprints.io` shows Composite Resource Kind (XR-KIND) as `XDynamoDBTable`. This Composition lets Crossplane know what to do when we created the `XDynamoDBTable` XR. Each Composition creates a link between an XR and a set of one or more Managed Resources. - -```bash -$ kubectl get composition -NAME XR-KIND XR-APIVERSION AGE -table.dynamodb.awsblueprints.io XDynamoDBTable awsblueprints.io/v1alpha1 143m -``` - -On querying the `XDynamoDBTable` XR which is not confined to any namespace, we can observe that it creates DynamoDB Managed Resource `Table`. - -```bash -$ kubectl get XDynamoDBTable -o yaml | grep "resourceRefs:" -A 3 - - resourceRefs: - - apiVersion: dynamodb.aws.upbound.io/v1beta1 - kind: Table - name: eks-workshop-carts-crossplane-bt28w-lnb4r -``` - ---- - -When new resources are created or updated, application configurations also need to be updated to use these new resources. We've already configured the workload to use the correct table name in the previous section so lets just restart the pods: - -```bash -$ kubectl rollout restart -n carts deployment/carts -$ kubectl rollout status -n carts deployment/carts --timeout=2m -deployment "carts" successfully rolled out -``` - ---- - -Now, how do we know that the application is working with the new DynamoDB table? - -An NLB has been created to expose the sample application for testing, allowing us to directly interact with the application through the browser: - -```bash -$ kubectl get service -n ui ui-nlb -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}" -k8s-ui-uinlb-a9797f0f61.elb.us-west-2.amazonaws.com -``` - -:::info -Please note that the actual endpoint will be different when you run this command as a new Network Load Balancer endpoint will be provisioned. -::: - -To wait until the load balancer has finished provisioning you can run this command: - -```bash timeout=610 -$ wait-for-lb $(kubectl get service -n ui ui-nlb -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}") -``` - -Once the load balancer is provisioned you can access it by pasting the URL in your web browser. You will see the UI from the web store displayed and will be able to navigate around the site as a user. - - - - - -To verify that the **Carts** module is in fact using the DynamoDB table we just provisioned, try adding a few items to the cart. - -![Cart screenshot](./assets/cart-items-present.webp) - -And to check if items are in the DynamoDB table as well, run - -```bash -$ aws dynamodb scan --table-name "${EKS_CLUSTER_NAME}-carts-crossplane" --query 'Items[].{itemId:itemId,Price:unitPrice}' --output text -PRICE 795 -ITEMID 510a0d7e-8e83-4193-b483-e27e09ddc34d -PRICE 385 -ITEMID 6d62d909-f957-430e-8689-b5129c0bb75e -PRICE 50 -ITEMID a0a4f044-b040-410d-8ead-4de0446aec7e -``` - -Congratulations! You've successfully created AWS Resources without leaving the confines of the Kubernetes API! diff --git a/website/docs/automation/controlplanes/crossplane/compositions/claims.md b/website/docs/automation/controlplanes/crossplane/compositions/claims.md new file mode 100644 index 000000000..70842cbf7 --- /dev/null +++ b/website/docs/automation/controlplanes/crossplane/compositions/claims.md @@ -0,0 +1,71 @@ +--- +title: "Claims" +sidebar_position: 20 +--- + +Once we've configured Crossplane with the details of the new XR, we can either create one directly or use a Claim. Typically, only the team responsible for configuring Crossplane (often a platform or SRE team) has permission to create XRs directly. Everyone else manages XRs via a lightweight proxy resource called a Composite Resource Claim (or claim for short). + +With this claim, the developer only needs to specify a default **DynamoDB table name, hash keys, and global index name** to create the table. This allows the platform or SRE team to standardize aspects such as billing mode, default read/write capacity, projection type, and cost and infrastructure-related tags. + +```file +manifests/modules/automation/controlplanes/crossplane/compositions/claim/claim.yaml +``` + +Let's start by cleaning up the DynamoDB table created in the previous Managed Resource section: + +```bash +$ kubectl delete tables.dynamodb.aws.upbound.io --all --ignore-not-found=true +$ kubectl wait --for=delete tables.dynamodb.aws.upbound.io --all --timeout=5m +``` + +Now, we can re-create the table by creating a `Claim`: + +```bash timeout=400 +$ cat ~/environment/eks-workshop/modules/automation/controlplanes/crossplane/compositions/claim/claim.yaml \ + | envsubst | kubectl -n carts apply -f - +dynamodbtable.awsblueprints.io/eks-workshop-carts-crossplane created +$ kubectl wait dynamodbtables.awsblueprints.io ${EKS_CLUSTER_NAME}-carts-crossplane -n carts \ + --for=condition=Ready --timeout=5m +``` + +It takes some time to provision AWS managed services, in the case of DynamoDB up to 2 minutes. Crossplane will report the status of the reconciliation in the `SYNCED` field of the Kubernetes Composite and Managed resource. + +```bash +$ kubectl get table +NAME READY SYNCED EXTERNAL-NAME AGE +eks-workshop-carts-crossplane-bt28w-lnb4r True True eks-workshop-carts-crossplane 6s +``` + +Now, let's understand how the DynamoDB table is deployed using this claim: + +![Crossplane reconciler concept](../assets/ddb-claim-architecture.webp) + +When querying the claim `DynamoDBTable` deployed in the carts namespace, we can observe that it points to and creates a Composite Resource (XR) `XDynamoDBTable`: + +```bash +$ kubectl get DynamoDBTable -n carts -o yaml | grep "resourceRef:" -A 3 + + resourceRef: + apiVersion: awsblueprints.io/v1alpha1 + kind: XDynamoDBTable + name: eks-workshop-carts-crossplane-bt28w +``` + +The Composition `table.dynamodb.awsblueprints.io` shows Composite Resource Kind (XR-KIND) as `XDynamoDBTable`. This Composition informs Crossplane what to do when we create the `XDynamoDBTable` XR. Each Composition creates a link between an XR and a set of one or more Managed Resources. + +```bash +$ kubectl get composition +NAME XR-KIND XR-APIVERSION AGE +table.dynamodb.awsblueprints.io XDynamoDBTable awsblueprints.io/v1alpha1 143m +``` + +When querying the `XDynamoDBTable` XR, which is not confined to any namespace, we can observe that it creates a DynamoDB managed resource `Table`: + +```bash +$ kubectl get XDynamoDBTable -o yaml | grep "resourceRefs:" -A 3 + + resourceRefs: + - apiVersion: dynamodb.aws.upbound.io/v1beta1 + kind: Table + name: eks-workshop-carts-crossplane-bt28w-lnb4r +``` diff --git a/website/docs/automation/controlplanes/crossplane/compositions/creating-a-composition.md b/website/docs/automation/controlplanes/crossplane/compositions/creating-a-composition.md new file mode 100644 index 000000000..f06e182f3 --- /dev/null +++ b/website/docs/automation/controlplanes/crossplane/compositions/creating-a-composition.md @@ -0,0 +1,30 @@ +--- +title: "Creating a Composition" +sidebar_position: 10 +--- + +A `CompositeResourceDefinition` (XRD) defines the type and schema of your Composite Resource (XR). It informs Crossplane about the desired XR and its fields. An XRD is similar to a CustomResourceDefinition (CRD) but with a more opinionated structure. Creating an XRD primarily involves specifying an OpenAPI ["structural schema"](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/). + +Let's start by providing a definition that allows application team members to create a DynamoDB table in their respective namespaces. In this example, users only need to specify the **name**, **key attributes**, and **index name** fields. + +```file +manifests/modules/automation/controlplanes/crossplane/compositions/composition/definition.yaml +``` + +A Composition informs Crossplane about the actions to take when a Composite Resource is created. Each Composition establishes a link between an XR and a set of one or more Managed Resources. When the XR is created, updated, or deleted, the associated Managed Resources are correspondingly created, updated, or deleted. + +The following Composition provisions the managed resource `Table`: + +```file +manifests/modules/automation/controlplanes/crossplane/compositions/composition/table.yaml +``` + +Let's apply this configuration to our EKS cluster: + +```bash +$ kubectl apply -k ~/environment/eks-workshop/modules/automation/controlplanes/crossplane/compositions/composition +compositeresourcedefinition.apiextensions.crossplane.io/xdynamodbtables.awsblueprints.io created +composition.apiextensions.crossplane.io/table.dynamodb.awsblueprints.io created +``` + +With these resources in place, we've successfully set up a Crossplane Composition for creating DynamoDB tables. This abstraction allows application developers to provision standardized DynamoDB tables without needing to understand the underlying AWS-specific details. diff --git a/website/docs/automation/controlplanes/crossplane/compositions/index.md b/website/docs/automation/controlplanes/crossplane/compositions/index.md new file mode 100644 index 000000000..32a4cc031 --- /dev/null +++ b/website/docs/automation/controlplanes/crossplane/compositions/index.md @@ -0,0 +1,22 @@ +--- +title: "Compositions" +sidebar_position: 30 +--- + +In addition to provisioning individual cloud resources, Crossplane offers a higher level of abstraction called Compositions. Compositions enable users to create opinionated templates for deploying cloud resources. This feature is particularly useful for organizations that need to enforce specific requirements across their infrastructure, such as: + +- Ensuring all AWS resources have certain tags +- Applying specific encryption keys to all Amazon Simple Storage Service (S3) buckets +- Standardizing resource configurations across the organization + +With Compositions, platform teams can define self-service API abstractions that guarantee all resources created through these templates meet the organization's requirements. This approach simplifies resource management and ensures consistency across deployments. + +In this section of the lab, we'll explore how to package our Amazon DynamoDB table as a Crossplane Composition. This will demonstrate how to create a more easily consumable resource for development teams, while maintaining control over the underlying configuration. + +By leveraging Compositions, we'll see how to: + +1. Define a standardized template for DynamoDB tables +2. Simplify the resource creation process for developers +3. Ensure compliance with organizational policies and best practices + +Through this exercise, you'll gain hands-on experience with Crossplane Compositions and understand their benefits in managing cloud resources within a Kubernetes environment. diff --git a/website/docs/automation/controlplanes/crossplane/compositions/testing.md b/website/docs/automation/controlplanes/crossplane/compositions/testing.md new file mode 100644 index 000000000..6ebb1c7f5 --- /dev/null +++ b/website/docs/automation/controlplanes/crossplane/compositions/testing.md @@ -0,0 +1,45 @@ +--- +title: "Testing the application" +sidebar_position: 40 +--- + +Now that we've provisioned our DynamoDB table using Crossplane Compositions, let's test the application to ensure it's working correctly with the new table. + +First, we need to restart the pods to ensure they're using the updated configuration: + +```bash +$ kubectl rollout restart -n carts deployment/carts +$ kubectl rollout status -n carts deployment/carts --timeout=2m +deployment "carts" successfully rolled out +``` + +To access the application, we'll use the same load balancer as in the previous section. Let's retrieve its hostname: + +```bash +$ LB_HOSTNAME=$(kubectl -n ui get service ui-nlb -o jsonpath='{.status.loadBalancer.ingress[*].hostname}{"\n"}') +$ echo "http://$LB_HOSTNAME" +http://k8s-ui-uinlb-647e781087-6717c5049aa96bd9.elb.us-west-2.amazonaws.com +``` + +You can now access the application by copying this URL into your web browser. You'll see the web store's user interface, allowing you to navigate the site as a user would. + + + + + +To verify that the **Carts** module is indeed using the newly provisioned DynamoDB table, follow these steps: + +1. Add a few items to your cart in the web interface. +2. Observe that the items appear in your cart, as shown in the screenshot below: + +![Cart screenshot showing added items](../assets/cart-items-present.webp) + +To confirm that these items are being stored in the DynamoDB table, run the following command: + +```bash +$ aws dynamodb scan --table-name "${EKS_CLUSTER_NAME}-carts-crossplane" +``` + +This command will display the contents of the DynamoDB table, which should include the items you've added to your cart. + +Congratulations! You've successfully created AWS resources using Crossplane Compositions and verified that your application is working correctly with these resources. This demonstrates the power of using Crossplane to manage cloud resources directly from your Kubernetes cluster. diff --git a/website/docs/automation/controlplanes/crossplane/configure-application.md b/website/docs/automation/controlplanes/crossplane/configure-application.md new file mode 100644 index 000000000..d8088b852 --- /dev/null +++ b/website/docs/automation/controlplanes/crossplane/configure-application.md @@ -0,0 +1,80 @@ +--- +title: "Updating the application" +sidebar_position: 25 +--- + +When new resources are created or updated, application configurations often need to be adjusted to utilize these new resources. Environment variables are a popular choice for application developers to store configuration, and in Kubernetes, we can pass environment variables to containers through the `env` field of the `container` [spec](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) when creating deployments. + +There are two primary ways to achieve this in Kubernetes: + +1. **ConfigMaps**: These are core resources in Kubernetes that allow us to pass configuration elements such as environment variables, text fields, and other items in a key-value format to be used in pod specs. +2. **Secrets**: While not encrypted by default (which is important to remember), secrets are used to store sensitive information like passwords. + +For this lab, we'll focus on updating the ConfigMap for the carts component. We'll remove the configuration that points it to the local DynamoDB and instead use the name of the DynamoDB table created by Crossplane: + +```kustomization +modules/automation/controlplanes/crossplane/app/kustomization.yaml +ConfigMap/carts +``` + +Additionally, we need to provide the carts Pods with the appropriate IAM permissions to access the DynamoDB service. An IAM role has already been created, and we'll apply this to the carts Pods using IAM Roles for Service Accounts (IRSA): + +```kustomization +modules/automation/controlplanes/crossplane/app/carts-serviceAccount.yaml +ServiceAccount/carts +``` + +To learn more about how IRSA works, refer to the [official documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). + +Let's apply this new configuration: + +```bash +$ kubectl kustomize ~/environment/eks-workshop/modules/automation/controlplanes/crossplane/app \ + | envsubst | kubectl apply -f- +``` + +Now we need to recycle all the carts Pods to pick up our new ConfigMap contents: + +```bash +$ kubectl rollout restart -n carts deployment/carts +deployment.apps/carts restarted +$ kubectl rollout status -n carts deployment/carts --timeout=40s +Waiting for deployment "carts" rollout to finish: 1 old replicas are pending termination... +deployment "carts" successfully rolled out +``` + +To verify that the application is working with the new DynamoDB table, we can use the Network Load Balancer (NLB) that has been created to expose the sample application for testing. This allows us to directly interact with the application through a web browser: + +```bash +$ LB_HOSTNAME=$(kubectl -n ui get service ui-nlb -o jsonpath='{.status.loadBalancer.ingress[*].hostname}{"\n"}') +$ echo "http://$LB_HOSTNAME" +http://k8s-ui-uinlb-647e781087-6717c5049aa96bd9.elb.us-west-2.amazonaws.com +``` + +:::info +Please note that the actual endpoint will be different when you run this command, as a new Network Load Balancer endpoint will be provisioned. +::: + +To wait until the load balancer has finished provisioning, you can run this command: + +```bash timeout=610 +$ wait-for-lb $(kubectl get service -n ui ui-nlb -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}") +``` + +Once the load balancer is provisioned, you can access it by pasting the URL in your web browser. You will see the UI from the web store displayed and will be able to navigate around the site as a user. + + + + + +To verify that the **Carts** module is indeed using the DynamoDB table we just provisioned, try adding a few items to the cart. + +![Cart screenshot](./assets/cart-items-present.webp) + +To check if items are present in the DynamoDB table as well, run: + +```bash +$ aws dynamodb scan --table-name "${EKS_CLUSTER_NAME}-carts-crossplane" +``` + +Congratulations! You've successfully created and utilized AWS resources without leaving the confines of the Kubernetes API! diff --git a/website/docs/automation/controlplanes/crossplane/how-it-works.md b/website/docs/automation/controlplanes/crossplane/how-it-works.md index 135db3b4f..98fe34d46 100644 --- a/website/docs/automation/controlplanes/crossplane/how-it-works.md +++ b/website/docs/automation/controlplanes/crossplane/how-it-works.md @@ -3,12 +3,12 @@ title: "How it works" sidebar_position: 5 --- -Running Crossplane in a cluster consists of two main parts: +Crossplane operates within a cluster using two primary components: -1. The Crossplane controller which provides the core components -2. One or more Crossplane providers which each provide a controller and Custom Resource Definitions to integrate with a particular provider, such as AWS +1. The Crossplane controller, which provides the core functionality +2. One or more Crossplane providers, each offering a controller and Custom Resource Definitions to integrate with a specific provider, such as AWS -The Crossplane controller, Upbound AWS provider and have been pre-installed in our EKS cluster, each running as a deployment in the `crossplane-system` namespace along with the `crossplane-rbac-manager`: +In our EKS cluster, we've pre-installed the Crossplane controller, the Upbound AWS provider, and the necessary components. These run as deployments in the `crossplane-system` namespace, alongside the `crossplane-rbac-manager`: ```bash $ kubectl get deployment -n crossplane-system @@ -19,8 +19,10 @@ upbound-aws-provider-dynamodb-23a48a51e223 1/1 1 1 3h upbound-provider-family-aws-1ac09674120f 1/1 1 1 21h ``` -Here, `upbound-provider-family-aws` represents Crossplane provider for Amazon Web Services (AWS) developed and supported by Upbound. `upbound-aws-provider-dynamodb` is a subset of the prior dedicated to deploy DynamoDB via Crossplane. +Here, `upbound-provider-family-aws` represents the Crossplane provider for Amazon Web Services (AWS), developed and supported by Upbound. The `upbound-aws-provider-dynamodb` is a subset dedicated to deploying DynamoDB via Crossplane. -Crossplane provides a simplified interface for developers to request infrastructure resources via Kubernetes manifests called claims. As shown in this diagram, claims are the only namespace-scoped Crossplane resources, serving as the developer interface and abstracting away implementation details. When a claim is deployed to the cluster, it creates a Composite Resource (XR), a Kubernetes custom resource representing one or more cloud resources defined through templates called Compositions. The Composite Resource creates one or more Managed Resources which interact with the AWS API to request the creation of the desired infrastructure resources. +Crossplane simplifies the process for developers to request infrastructure resources using Kubernetes manifests called claims. As illustrated in the diagram below, claims are the only namespace-scoped Crossplane resources, serving as the developer interface and abstracting implementation details. When a claim is deployed to the cluster, it creates a Composite Resource (XR), a Kubernetes custom resource representing one or more cloud resources defined through templates called Compositions. The Composite Resource then creates one or more Managed Resources, which interact with the AWS API to request the creation of the desired infrastructure resources. ![Crossplane claim](./assets/claim-architecture-drawing.webp) + +This architecture allows for a clear separation of concerns between developers, who work with high-level abstractions (claims), and platform teams, who define the underlying infrastructure implementations (Compositions and Managed Resources). diff --git a/website/docs/automation/controlplanes/crossplane/index.md b/website/docs/automation/controlplanes/crossplane/index.md index b66982566..1a6f3be32 100644 --- a/website/docs/automation/controlplanes/crossplane/index.md +++ b/website/docs/automation/controlplanes/crossplane/index.md @@ -22,7 +22,17 @@ You can view the Terraform that applies these changes [here](https://github.com/ ::: -[Crossplane](https://crossplane.io/) is an open source project in the CNCF that transforms your Kubernetes cluster into a universal control plane. Crossplane enables platform teams to assemble infrastructure from multiple vendors, and expose higher level self-service APIs for application teams to consume, without having to write any code. -Crossplane extends your Kubernetes cluster to support orchestrating any infrastructure or managed service. Compose Crossplane’s granular resources into higher level abstractions that can be versioned, managed, deployed and consumed using your favorite tools and existing processes. +[Crossplane](https://crossplane.io/) is an open-source project in the Cloud Native Computing Foundation (CNCF) that transforms your Kubernetes cluster into a universal control plane. It enables platform teams to assemble infrastructure from multiple vendors and expose higher-level self-service APIs for application teams to consume, without writing any code. + +Crossplane extends your Kubernetes cluster to support orchestrating any infrastructure or managed service. It allows you to compose Crossplane's granular resources into higher-level abstractions that can be versioned, managed, deployed, and consumed using your favorite tools and existing processes. ![EKS with Dynamodb](./assets/eks-workshop-crossplane.webp) + +With Crossplane, you can: + +1. Provision and manage cloud infrastructure directly from your Kubernetes cluster +2. Define custom resources that represent complex infrastructure setups +3. Create abstraction layers that simplify infrastructure management for application developers +4. Implement consistent policies and governance across multiple cloud providers + +In this module, we'll explore how to use Crossplane to manage AWS resources, specifically focusing on provisioning and configuring a DynamoDB table for our sample application. diff --git a/website/docs/automation/controlplanes/crossplane/resources.md b/website/docs/automation/controlplanes/crossplane/resources.md index ed11555f0..adae4b0d8 100644 --- a/website/docs/automation/controlplanes/crossplane/resources.md +++ b/website/docs/automation/controlplanes/crossplane/resources.md @@ -3,49 +3,17 @@ title: "Managed Resources" sidebar_position: 20 --- -By default the **Carts** component in the sample application uses a DynamoDB local instance running as a pod in the EKS cluster called `carts-dynamodb`. In this section of the lab, we'll provision an Amazon DynamoDB cloud based table for our application using Crossplane managed resources and point the **Carts** deployment to use the newly provisioned DynamoDB table instead of the local copy. +By default, the **Carts** component in the sample application uses a DynamoDB local instance running as a pod in the EKS cluster called `carts-dynamodb`. In this section of the lab, we'll provision an Amazon DynamoDB cloud-based table for our application using Crossplane managed resources and configure the **Carts** deployment to use the newly provisioned DynamoDB table instead of the local copy. ![Crossplane reconciler concept](./assets/Crossplane-desired-current-ddb.webp) -The AWS Java SDK in the **Carts** component is able to use IAM Roles to interact with AWS services which means that we do not need to pass credentials, thus reducing the attack surface. In the EKS context, IRSA allows us to define per pod IAM Roles for applications to consume. To leverage IRSA, we first need to: - -- Create a Kubernetes Service Account in the Carts namespace -- Create an IAM Policy with necessary DynamoDB permissions -- Create an IAM Role in AWS with the above permissions -- Map the Service Account to use the IAM role using Annotations in the Service Account definition. - -Fortunately, we have a handy one-liner to help with this process. Run the below: - -```bash -$ eksctl create iamserviceaccount --name carts-crossplane \ - --namespace carts --cluster $EKS_CLUSTER_NAME \ - --role-name ${EKS_CLUSTER_NAME}-carts-crossplane \ - --attach-policy-arn $DYNAMODB_POLICY_ARN --approve - -2023-10-30 12:45:17 [i] 1 iamserviceaccount (carts/carts-crossplane) was included (based on the include/exclude rules) -2023-10-30 12:45:17 [!] serviceaccounts that exist in Kubernetes will be excluded, use --override-existing-serviceaccounts to override -2023-10-30 12:45:17 [i] 1 task: { - 2 sequential sub-tasks: { - create IAM role for serviceaccount "carts/carts-crossplan", - create serviceaccount "carts/carts-crossplane", - } }2023-10-30 12:45:17 [i] building iamserviceaccount stack "eksctl-eks-workshop-addon-iamserviceaccount-carts-carts-crossplane" -2023-10-30 12:45:18 [i] deploying stack "eksctl-eks-workshop-addon-iamserviceaccount-carts-carts-crossplane" -2023-10-30 12:45:18 [i] waiting for CloudFormation stack "eksctl-eks-workshop-addon-iamserviceaccount-carts-carts-crossplane" -``` - -`eksctl` provisions a CloudFormation stack to help manage these resources which can be seen in the output above. - -To learn more about how IRSA works, go [here](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - ---- - -Now, let's explore how we'll create the DynamoDB table via a Crossplane managed resource manifest +Let's explore how we'll create the DynamoDB table via a Crossplane managed resource manifest: ```file manifests/modules/automation/controlplanes/crossplane/managed/table.yaml ``` -Finally, we can create the configuration for the DynamoDB itself with a `dynamodb.aws.upbound.io` resource. +Now, we can create the configuration for the DynamoDB table using a `dynamodb.aws.upbound.io` resource. ```bash wait=10 timeout=400 hook=table $ kubectl kustomize ~/environment/eks-workshop/modules/automation/controlplanes/crossplane/managed \ @@ -55,7 +23,7 @@ $ kubectl wait tables.dynamodb.aws.upbound.io ${EKS_CLUSTER_NAME}-carts-crosspla --for=condition=Ready --timeout=5m ``` -It takes some time to provision the AWS managed services, in the case of DynamoDB up to 2 minutes. Crossplane will report the status of the reconciliation in the `status` field of the Kubernetes custom resources. +It takes some time to provision AWS managed services, in the case of DynamoDB up to 2 minutes. Crossplane will report the status of the reconciliation in the `status` field of the Kubernetes custom resources. ```bash $ kubectl get tables.dynamodb.aws.upbound.io @@ -63,65 +31,4 @@ NAME READY SYNCED EXTERNAL-NAME eks-workshop-carts-crossplane True True eks-workshop-carts-crossplane 6s ``` -When new resources are created or updated, application configurations also need to be updated to use these new resources. Update the application to use the DynamoDB endpoint: - -```bash timeout=180 -$ kubectl kustomize ~/environment/eks-workshop/modules/automation/controlplanes/crossplane/application \ - | envsubst | kubectl apply -f- -namespace/carts unchanged -serviceaccount/carts unchanged -configmap/carts unchanged -configmap/carts-crossplane created -service/carts unchanged -service/carts-dynamodb unchanged -deployment.apps/carts configured -deployment.apps/carts-dynamodb unchanged -$ kubectl rollout status -n carts deployment/carts --timeout=2m -deployment "carts" successfully rolled out -``` - ---- - -Now, how do we know that the application is working with the new DynamoDB table? - -An NLB has been created to expose the sample application for testing, allowing us to directly interact with the application through the browser: - -```bash -$ kubectl get service -n ui ui-nlb -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}" -k8s-ui-uinlb-a9797f0f61.elb.us-west-2.amazonaws.com -``` - -:::info -Please note that the actual endpoint will be different when you run this command as a new Network Load Balancer endpoint will be provisioned. -::: - -To wait until the load balancer has finished provisioning you can run this command: - -```bash timeout=610 -$ wait-for-lb $(kubectl get service -n ui ui-nlb -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}") -``` - -Once the load balancer is provisioned you can access it by pasting the URL in your web browser. You will see the UI from the web store displayed and will be able to navigate around the site as a user. - - - - - -To verify that the **Carts** module is in fact using the DynamoDB table we just provisioned, try adding a few items to the cart. - -![Cart screenshot](./assets/cart-items-present.webp) - -And to check if items are in the DynamoDB table as well, run - -```bash -$ aws dynamodb scan --table-name "${EKS_CLUSTER_NAME}-carts-crossplane" \ - --query 'Items[].{itemId:itemId,Price:unitPrice}' --output text -PRICE 795 -ITEMID 510a0d7e-8e83-4193-b483-e27e09ddc34d -PRICE 385 -ITEMID 6d62d909-f957-430e-8689-b5129c0bb75e -PRICE 50 -ITEMID a0a4f044-b040-410d-8ead-4de0446aec7e -``` - -Congratulations! You've successfully created AWS Resources without leaving the confines of the Kubernetes API! +With this configuration applied, Crossplane will create a DynamoDB table in AWS, which can then be used by our application. In the next section, we'll update the application to use this newly created table.