Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update: Change ACK lab to use DynamoDB instead of RDS #712

Merged
merged 22 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2d66b77
Modified cleanup script for the workshop to better reflect what we're…
Oct 10, 2023
1d92536
Updated ACK DynamoDB Controller to install with EKS Blueprints v5 Ter…
Oct 10, 2023
988c88e
created Kubernetes manifests for creating DynamoDB tables, configmaps…
Oct 10, 2023
533a205
modifying addon.tf to use ack TF and changes in workshop content
Oct 12, 2023
bc27196
Updated ACK DynamoDB Controller to install with EKS Blueprints v5 Ter…
Oct 22, 2023
365cfcc
Updated ACK DynamoDB Controller to install with EKS Blueprints v5 Ter…
Oct 22, 2023
62d3a66
Updated ACK DynamoDB Controller to install with EKS Blueprints v5 Ter…
Oct 22, 2023
ba9aaad
Updated ACK DynamoDB Controller to install with EKS Blueprints v5 Ter…
Oct 22, 2023
089cd3a
Updated ACK DynamoDB Controller to install with EKS Blueprints v5 Ter…
Oct 22, 2023
f4cdd6c
Updated ACK DynamoDB Controller to install with EKS Blueprints v5 Ter…
Oct 22, 2023
c008fbd
Updated configmap creation with env, made changes to service account …
Oct 22, 2023
014e210
Updated steps to perform, screenshots and other manifest items
Oct 25, 2023
76e4792
Made changes to language, titles and ENV vars for AWS region
Oct 25, 2023
c6555d8
Merge branch 'aws-samples:main' into dynamodb-ack
thiru85 Oct 27, 2023
b737250
Made changes as per PR comments
Oct 30, 2023
9f4e170
Merge branch 'aws-samples:main' into dynamodb-ack
thiru85 Oct 30, 2023
6946a49
Merge branch 'dynamodb-ack' of github.com:thiru85/eks-workshop-v2 int…
Oct 30, 2023
2cf50f4
Made changes as per PR comments
Oct 30, 2023
4710331
broken website link
Oct 31, 2023
c86f44c
broken website link
Oct 31, 2023
3afc098
fixed bash prompt in md
Oct 31, 2023
61ba415
Ensure ACK dynamo table and IAM role are named appropriately
niallthomson Oct 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/bin/bash

echo "Deleting RDS resources created by ACK..."
echo "Deleting resources created by ACK..."

eksctl delete iamserviceaccount --name carts-ack --namespace carts --cluster $EKS_CLUSTER_NAME -v 0 > /dev/null
kubectl delete table items -n carts --ignore-not-found=true > /dev/null
kubectl delete namespace ack-dynamodb --ignore-not-found=true > /dev/null

kubectl delete namespace catalog > /dev/null
Original file line number Diff line number Diff line change
@@ -1,108 +1,58 @@
locals {
ec2_name = "ack-ec2"
rds_name = "ack-rds"
ddb_name = "ack-ddb"
}

module "rds" {
source = "github.com/aws-ia/terraform-aws-eks-blueprints//modules/kubernetes-addons/helm-addon?ref=v4.12.2"
#This module installs the ACK controller for DynamoDB through the AWS EKS Addons for ACK
module "dynamodb_ack_addon" {

helm_config = {
name = local.rds_name
chart = "rds-chart"
repository = "oci://public.ecr.aws/aws-controllers-k8s"
version = "v0.1.1"
namespace = local.rds_name
create_namespace = true
description = "ACK RDS Controller Helm chart deployment configuration"
values = [
# shortens pod name from `ack-rds-rds-chart-xxxxxxxxxxxxx` to `ack-rds-xxxxxxxxxxxxx`
<<-EOT
nameOverride: ack-rds
EOT
]
}

set_values = [
{
name = "serviceAccount.name"
value = local.rds_name
},
{
name = "serviceAccount.create"
value = false
},
{
name = "aws.region"
value = data.aws_region.current.id
}
]

irsa_config = {
create_kubernetes_namespace = true
kubernetes_namespace = local.rds_name

create_kubernetes_service_account = true
kubernetes_service_account = local.rds_name
source = "aws-ia/eks-ack-addons/aws"
version = "2.1.0"

# Cluster Info
cluster_name = local.addon_context.eks_cluster_id
cluster_endpoint = local.addon_context.aws_eks_cluster_endpoint
oidc_provider_arn = local.addon_context.eks_oidc_provider_arn

irsa_iam_policies = [data.aws_iam_policy.rds.arn]
}
# Controllers to enable
enable_dynamodb = true

addon_context = local.addon_context

tags = local.tags
}

data "aws_iam_policy" "rds" {
name = "AmazonRDSFullAccess"
}

module "ec2" {
source = "github.com/aws-ia/terraform-aws-eks-blueprints//modules/kubernetes-addons/helm-addon?ref=v4.12.2"

helm_config = {
name = local.ec2_name
chart = "ec2-chart"
repository = "oci://public.ecr.aws/aws-controllers-k8s"
version = "v0.1.1"
namespace = local.ec2_name
create_namespace = true
description = "ACK EC2 Controller Helm chart deployment configuration"
values = [
# shortens pod name from `ack-ec2-ec2-chart-xxxxxxxxxxxxx` to `ack-ec2-xxxxxxxxxxxxx`
<<-EOT
nameOverride: ack-ec2
EOT
]
}

set_values = [
#module "iam_assumable_role_carts" {
# source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc"
# version = "~> v5.5.5"
# create_role = true
# role_name = "${local.addon_context.eks_cluster_id}-carts-dynamo"
# provider_url = local.addon_context.eks_oidc_issuer_url
# role_policy_arns = [aws_iam_policy.carts_dynamo.arn]
# oidc_subjects_with_wildcards = ["system:serviceaccount:carts:*"]
#
# tags = local.tags
#}

resource "aws_iam_policy" "carts_dynamo" {
name = "${local.addon_context.eks_cluster_id}-carts-dynamo"
path = "/"
description = "DynamoDB policy for AWS Sample Carts Application"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
name = "serviceAccount.name"
value = local.ec2_name
},
{
name = "serviceAccount.create"
value = false
},
{
name = "aws.region"
value = data.aws_region.current.id
"Sid": "AllAPIActionsOnCart",
"Effect": "Allow",
"Action": "dynamodb:*",
"Resource": [
"arn:aws:dynamodb:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*"
]
}
]

irsa_config = {
create_kubernetes_namespace = true
kubernetes_namespace = local.ec2_name

create_kubernetes_service_account = true
kubernetes_service_account = local.ec2_name

irsa_iam_policies = [data.aws_iam_policy.ec2.arn]
}

addon_context = local.addon_context
}

data "aws_iam_policy" "ec2" {
name = "AmazonEC2FullAccess"
EOF
tags = local.tags
}

module "eks_blueprints_addons" {
Expand All @@ -120,31 +70,8 @@ module "eks_blueprints_addons" {
oidc_provider_arn = local.addon_context.eks_oidc_provider_arn
}

data "aws_vpc" "selected" {
tags = {
created-by = "eks-workshop-v2"
env = local.addon_context.eks_cluster_id
}
}

data "aws_subnets" "private" {
tags = {
created-by = "eks-workshop-v2"
env = local.addon_context.eks_cluster_id
}

filter {
name = "tag:Name"
values = ["*Private*"]
}
}

output "environment" {
value = <<EOF
export VPC_ID=${data.aws_vpc.selected.id}
export VPC_CIDR=${data.aws_vpc.selected.cidr_block}
%{for index, id in data.aws_subnets.private.ids}
export VPC_PRIVATE_SUBNET_ID_${index + 1}=${id}
%{endfor}
export DYNAMODB_POLICY_ARN=${aws_iam_policy.carts_dynamo.arn}
EOF
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: carts
namespace: carts
spec:
template:
spec:
containers:
- name: carts
envFrom:
- configMapRef:
name: carts-ack
serviceAccountName: carts-ack
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: carts-ack
namespace: carts
data:
CARTS_DYNAMODB_TABLENAME: Items
CARTS_DYNAMODB_ENDPOINT: https://dynamodb.${AWS_REGION}.amazonaws.com
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: dynamodb.services.k8s.aws/v1alpha1
kind: Table
metadata:
name: items
namespace: carts
spec:
keySchema:
- attributeName: id
keyType: HASH
attributeDefinitions:
- attributeName: id
attributeType: 'S'
- attributeName: customerId
attributeType: 'S'
billingMode: PAY_PER_REQUEST
tableName: "Items"
globalSecondaryIndexes:
- indexName: idx_global_customerId
keySchema:
- attributeName: customerId
keyType: HASH
- attributeName: id
keyType: RANGE
projection:
projectionType: 'ALL'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../../../../base-application/carts
resources:
- dynamodb-ack-configmap.yaml
- dynamodb-create.yaml
patches:
- deployment.yaml
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 32 additions & 35 deletions website/docs/automation/controlplanes/ack/configure-application.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,45 @@
---
title: "Bind Application to AWS Resources"
title: "Updating the application with new resources"
sidebar_position: 10
---

Now that the RDS database has been created successfully, we can reconfigure the catalog component to use it for persistence instead of its existing pod-based MySQL. But how do we configure the catalog component with the RDS endpoint and credentials for the connection?
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.

The ACK `FieldExport` custom resource 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.
Now, there are two ways to achieve this.

The `DBInstance` resource contains the information for connecting to the RDS database instance. The host information can be found in `status.endpoint.address` and the master username in `spec.masterUsername`. Lets create some `FieldExport` objects to extract these values in to a Kubernetes secret named `catalog-db-ack`.
- 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.

```file
manifests/modules/automation/controlplanes/ack/rds/fieldexports/rds-fieldexports.yaml
```
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.

Apply this configuration:
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.

```bash
$ export CATALOG_PASSWORD=$(kubectl get secrets -n default catalog-rds-pw -n catalog -o go-template='{{.data.password|base64decode}}')
$ kubectl apply -k ~/environment/eks-workshop/modules/automation/controlplanes/ack/rds/fieldexports
secret/catalog-db configured
fieldexport.services.k8s.aws/catalog-db-endpoint created
fieldexport.services.k8s.aws/catalog-db-user created
```file
manifests/modules/automation/controlplanes/ack/dynamodb/deployment.yaml
```

And now we can see that the `catalog-db-ack` secret has been populated:
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.

```bash
$ kubectl -n catalog get secret -o yaml catalog-db-ack | yq '.data'
endpoint: ZWtzLXdvcmtzaG9wLWNhdGFsb2ctYWNrLmNqa2F0cWQxY25yei51cy13ZXN0LTIucmRzLmFtYXpvbmF3cy5jb20=
password: TVdZM09UUTNNVGc1TUdVM1lXTTNabVV3TjJWbU5qQmo=
username: YWRtaW4=
```file
manifests/modules/automation/controlplanes/ack/dynamodb/dynamodb-ack-configmap.yaml
```

Finally, we can update the application to use the RDS endpoint and credentials sourced from the `catalog-db-ack` secret:
And when we apply these manifests using Kustomize as we've done in the earlier section, the **Carts** component gets updated as well.

```bash
$ kubectl apply -k ~/environment/eks-workshop/modules/automation/controlplanes/ack/rds/application
namespace/catalog unchanged
serviceaccount/catalog unchanged
configmap/catalog unchanged
secret/catalog-db unchanged
service/catalog unchanged
service/catalog-mysql unchanged
service/ui-nlb created
deployment.apps/catalog configured
statefulset.apps/catalog-mysql unchanged
$ kubectl rollout status -n catalog deployment/catalog --timeout=120s
```
----

An NLB has been created to expose the sample application for testing:
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:

Expand All @@ -66,3 +52,14 @@ Once the load balancer is provisioned you can access it by pasting the URL in yo
<browser url="http://k8s-ui-uinlb-a9797f0f61.elb.us-west-2.amazonaws.com">
<img src={require('@site/static/img/sample-app-screens/home.png').default}/>
</browser>

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.png)

And navigating to the Items table in the DynamoDB console to *Explore table items* shows
thiru85 marked this conversation as resolved.
Show resolved Hide resolved

![DynamoDB screenshot](./assets/dynamodb-items-table.png)

Congratulations! You've successfully created AWS Resources without leaving the confines of the Kubernetes API!

18 changes: 13 additions & 5 deletions website/docs/automation/controlplanes/ack/how-it-works.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
---
title: "How it works"
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.
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).

The controllers for both Amazon RDS and Amazon EC2 have been pre-installed in the cluster, each running as a deployment in their respective namespaces. For example, let's take a look at the running RDS controller:
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.

```bash
$ kubectl describe deployment -n ack-rds ack-rds
$ kubectl describe deployment ack-dynamodb -n ack-dynamodb -oyaml
thiru85 marked this conversation as resolved.
Show resolved Hide resolved
```

This controller will watch for Kubernetes custom resources for RDS such as `rds.services.k8s.aws.DBInstance` and will make API calls to RDS based on the configuration in those resources created. As resources are created, the controller will feed back status updates to the custom resources in the `Status` fields.
The ```-oyaml``` flag simply extracts the full YAML manifest of the deployment definition instead of the formatted output. Feel free to run the ```describe``` command without the flag as well.
thiru85 marked this conversation as resolved.
Show resolved Hide resolved

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/).

If you'd like to dive deeper into the mechanics of what objects and API calls the controller listens for, run:

```bash
$ kubectl get crd
```
Loading