diff --git a/patterns/vpc-lattice/README.md b/patterns/vpc-lattice/README.md new file mode 100644 index 0000000000..e2ce19544d --- /dev/null +++ b/patterns/vpc-lattice/README.md @@ -0,0 +1,38 @@ +# Amazon VPC Lattice + +This pattern demonstrates how to expose an EKS cluster hosted application to an internal consumer through Amazon VPC Lattice. + +- [Documentation](https://docs.aws.amazon.com/vpc-lattice/latest/ug/what-is-vpc-lattice.html) +- [AWS Gateway API Controller](https://www.gateway-api-controller.eks.aws.dev/) + +## Scenario + +With this soluton we showcase how to configure Amazon VPC Lattice using the AWS Gateway API Controller in order to manage Amazon VPC Lattice resources through native K8S Gateway API objects. This pattern deploys two distinct VPCs with a client application running in one of them and a server application in the other. The server application is deployed inside an EKS cluster and made exposed to the client application through Amazon VPC Lattice which establishes connectivity between the two applications. Further we demonstrate how to configure a custom domain name for the exposed service using Amazon Route53. + +![diagram](assets/diagram.png) + + +## Deploy + +See [here](https://aws-ia.github.io/terraform-aws-eks-blueprints/getting-started/#prerequisites) for the prerequisites and steps to deploy this pattern. + +## Validate + +In order to test the connectivty between the client and server, please follow the steps outlined below: + +1. Login to the management console of your AWS account and navigate to the EC2 service +2. Select your the EC2 Instance with the name **client**, click **Connect**, choose **Session Manager** and click **Connect** +3. Within the console test the connecvity to the server application by entering the following command: + + ```sh + curl -I http://server.example.com + ``` + + If everythign works as expected yous should receive the following response: + ![output](assets/server-response.jpeg) + +## Destroy + +{% + include-markdown "../../docs/_partials/destroy.md" +%} diff --git a/patterns/vpc-lattice/assets/diagram.png b/patterns/vpc-lattice/assets/diagram.png new file mode 100644 index 0000000000..1c74adbfe5 Binary files /dev/null and b/patterns/vpc-lattice/assets/diagram.png differ diff --git a/patterns/vpc-lattice/assets/server-response.jpeg b/patterns/vpc-lattice/assets/server-response.jpeg new file mode 100644 index 0000000000..32058e26ec Binary files /dev/null and b/patterns/vpc-lattice/assets/server-response.jpeg differ diff --git a/patterns/vpc-lattice/charts/demo-application/.helmignore b/patterns/vpc-lattice/charts/demo-application/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/patterns/vpc-lattice/charts/demo-application/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/patterns/vpc-lattice/charts/demo-application/Chart.yaml b/patterns/vpc-lattice/charts/demo-application/Chart.yaml new file mode 100644 index 0000000000..7e6d29f9b3 --- /dev/null +++ b/patterns/vpc-lattice/charts/demo-application/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: demo-application +description: A Helm chart to deploy the demo-application +type: application +version: 1.0.0 diff --git a/patterns/vpc-lattice/charts/demo-application/templates/deployment.yaml b/patterns/vpc-lattice/charts/demo-application/templates/deployment.yaml new file mode 100644 index 0000000000..a2dc1de5b6 --- /dev/null +++ b/patterns/vpc-lattice/charts/demo-application/templates/deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: server + labels: + app: server +spec: + replicas: 2 + selector: + matchLabels: + app: server + template: + metadata: + labels: + app: server + spec: + containers: + - name: server + image: public.ecr.aws/x2j8p8w7/http-server:latest + env: + - name: PodName + value: "server pod" \ No newline at end of file diff --git a/patterns/vpc-lattice/charts/demo-application/templates/gateway-class.yaml b/patterns/vpc-lattice/charts/demo-application/templates/gateway-class.yaml new file mode 100644 index 0000000000..23f16a9ef0 --- /dev/null +++ b/patterns/vpc-lattice/charts/demo-application/templates/gateway-class.yaml @@ -0,0 +1,6 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: amazon-vpc-lattice +spec: + controllerName: application-networking.k8s.aws/gateway-api-controller \ No newline at end of file diff --git a/patterns/vpc-lattice/charts/demo-application/templates/gateway.yaml b/patterns/vpc-lattice/charts/demo-application/templates/gateway.yaml new file mode 100644 index 0000000000..ca0e22f54c --- /dev/null +++ b/patterns/vpc-lattice/charts/demo-application/templates/gateway.yaml @@ -0,0 +1,11 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: my-services + namespace: apps +spec: + gatewayClassName: amazon-vpc-lattice + listeners: + - name: http + protocol: HTTP + port: 80 \ No newline at end of file diff --git a/patterns/vpc-lattice/charts/demo-application/templates/httproute.yaml b/patterns/vpc-lattice/charts/demo-application/templates/httproute.yaml new file mode 100644 index 0000000000..862a2eeef1 --- /dev/null +++ b/patterns/vpc-lattice/charts/demo-application/templates/httproute.yaml @@ -0,0 +1,20 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: server + namespace: apps +spec: + hostnames: + - server.example.com + parentRefs: + - name: my-services + sectionName: http + rules: + - backendRefs: + - name: server + kind: Service + port: 8090 + matches: + - path: + type: PathPrefix + value: / \ No newline at end of file diff --git a/patterns/vpc-lattice/charts/demo-application/templates/service.yaml b/patterns/vpc-lattice/charts/demo-application/templates/service.yaml new file mode 100644 index 0000000000..39680474aa --- /dev/null +++ b/patterns/vpc-lattice/charts/demo-application/templates/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: server +spec: + selector: + app: server + ports: + - protocol: TCP + port: 8090 + targetPort: 8090 \ No newline at end of file diff --git a/patterns/vpc-lattice/main.tf b/patterns/vpc-lattice/main.tf new file mode 100644 index 0000000000..7f215bcd4c --- /dev/null +++ b/patterns/vpc-lattice/main.tf @@ -0,0 +1,377 @@ +provider "aws" { + region = local.region +} + +data "aws_caller_identity" "current" {} + +data "aws_availability_zones" "available" {} + +provider "kubernetes" { + host = module.eks.cluster_endpoint + cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) + + exec { + api_version = "client.authentication.k8s.io/v1beta1" + command = "aws" + # This requires the awscli to be installed locally where Terraform is executed + args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] + } +} + +provider "helm" { + kubernetes { + host = module.eks.cluster_endpoint + cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) + + exec { + api_version = "client.authentication.k8s.io/v1beta1" + command = "aws" + # This requires the awscli to be installed locally where Terraform is executed + args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] + } + } +} + +locals { + name = basename(path.cwd) + region = "us-west-2" + + cluster_vpc_cidr = "10.0.0.0/16" + client_vpc_cidr = "10.1.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + + tags = { + Blueprint = local.name + GithubRepo = "github.com/aws-ia/terraform-aws-eks-blueprints" + } +} + +################################################################################ +# Cluster +################################################################################ + +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 19.21" + + cluster_name = local.name + cluster_version = "1.28" + cluster_endpoint_public_access = true + enable_irsa = true + + vpc_id = module.cluster_vpc.vpc_id + subnet_ids = module.cluster_vpc.private_subnets + + eks_managed_node_groups = { + initial = { + instance_types = ["m5.large"] + + min_size = 3 + max_size = 10 + desired_size = 3 + } + } + + tags = local.tags +} + +################################################################################ +# Cluster VPC +################################################################################ + +module "cluster_vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.4" + + name = local.name + cidr = local.cluster_vpc_cidr + + azs = local.azs + private_subnets = [for k, v in local.azs : cidrsubnet(local.cluster_vpc_cidr, 4, k)] + public_subnets = [for k, v in local.azs : cidrsubnet(local.cluster_vpc_cidr, 8, k + 48)] + + enable_nat_gateway = true + single_nat_gateway = true + + public_subnet_tags = { + "kubernetes.io/role/elb" = 1 + } + + private_subnet_tags = { + "kubernetes.io/role/internal-elb" = 1 + } + + tags = local.tags +} + +################################################################################ +# Client VPC +################################################################################ + +module "client_vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.4" + + name = local.name + cidr = local.client_vpc_cidr + + azs = local.azs + private_subnets = [for k, v in local.azs : cidrsubnet(local.client_vpc_cidr, 4, k)] + + tags = local.tags +} + +################################################################################ +# EKS Addons (AWS Gateway API Controller) +################################################################################ + +module "addons" { + source = "aws-ia/eks-blueprints-addons/aws" + version = "~> 1.12" + + cluster_name = module.eks.cluster_name + cluster_endpoint = module.eks.cluster_endpoint + cluster_version = module.eks.cluster_version + oidc_provider_arn = module.eks.oidc_provider_arn + + enable_aws_gateway_api_controller = true + aws_gateway_api_controller = { + chart_version = "v1.0.1" + create_namespace = true + namespace = "aws-application-networking-system" + source_policy_documents = [data.aws_iam_policy_document.gateway_api_controller.json] + set = [ + { + name = "clusterName" + value = module.eks.cluster_name + }, + { + name = "log.level" + value = "debug" + }, + { + name = "clusterVpcId" + value = module.cluster_vpc.vpc_id + }, ] + + } + + tags = local.tags +} + +resource "null_resource" "wait_for_crd_registration" { + triggers = { + build_number = "$(timestamp())" + } + + provisioner "local-exec" { + command = < + { + service = service + subnet_ids = module.client_vpc.private_subnets + private_dns_enabled = true + tags = { Name = "${local.name}-${service}" } + } + } + + security_group_ids = [module.endpoint_sg.security_group_id] + + tags = local.tags +} + +module "client_sg" { + source = "terraform-aws-modules/security-group/aws" + version = "~> 5.0" + + name = "client" + description = "Security Group for EC2 Instance Egress" + + vpc_id = module.client_vpc.vpc_id + + egress_with_cidr_blocks = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = "0.0.0.0/0" + + }, + ] + + tags = local.tags +} + +module "endpoint_sg" { + source = "terraform-aws-modules/security-group/aws" + version = "~> 5.0" + + name = "ssm-endpoint" + description = "Security Group for EC2 Instance Egress" + + vpc_id = module.client_vpc.vpc_id + + ingress_with_cidr_blocks = [for subnet in module.client_vpc.private_subnets_cidr_blocks : + { + from_port = 443 + to_port = 443 + protocol = "TCP" + cidr_blocks = subnet + } + ] + + tags = local.tags +} \ No newline at end of file diff --git a/patterns/vpc-lattice/outputs.tf b/patterns/vpc-lattice/outputs.tf new file mode 100644 index 0000000000..b1a50fa2de --- /dev/null +++ b/patterns/vpc-lattice/outputs.tf @@ -0,0 +1,4 @@ +output "configure_kubectl" { + description = "Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig" + value = "aws eks update-kubeconfig --name ${module.eks.cluster_name} --alias ${module.eks.cluster_name} --region ${local.region}" +} \ No newline at end of file diff --git a/patterns/vpc-lattice/variables.tf b/patterns/vpc-lattice/variables.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/patterns/vpc-lattice/versions.tf b/patterns/vpc-lattice/versions.tf new file mode 100644 index 0000000000..b26b395e43 --- /dev/null +++ b/patterns/vpc-lattice/versions.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.30" + } + helm = { + source = "hashicorp/helm" + version = ">= 2.9" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.24" + } + } +}