Amazon EKS · A worked example
Cartwright on EKS: one enterprise app, end to end
A single realistic B2C retail platform — its services, its multi-account environments, its repos, and its pipelines — drawn as one connected picture so you can see how a real application lands on Amazon EKS.
What you’re looking at
Cartwright is an invented but realistic online-retail platform: ten microservices (storefront, BFF [backend-for-frontend], catalog, cart, orders, payments, inventory, users/auth, search, notifications) backed by RDS, DynamoDB, ElastiCache, S3, and MSK. This piece walks it in four layers — the application (who calls whom), the environments (a multi-account, cluster-per-env layout), the repos (where code, config, and infrastructure live), and the pipelines (CI per service, CD by GitOps) — and then draws the whole thing as one end-to-end diagram. The through-line: every change lands as a GitOps-driven deployment onto an EKS platform, and the platform, not any single cluster, is the product.
Read this for
- A concrete mental model of a real app on EKS, not a feature tour.
- How repos, environments, and pipelines actually connect.
- Where the fleet-platform services (Karpenter, Pod Identity, ESO, Kyverno, ALB, AMP/AMG) plug in.
This is not
- A step-by-step install guide or a Terraform tutorial.
- A claim that Cartwright’s exact shape fits every org.
- A reference for version/pricing specifics — those carry a re-verify date.
The application — who calls whom
Start with the app itself, because everything downstream exists to ship it. A browser hits an ALB, which fronts the server-rendered storefront and a BFF/API gateway. The BFF is the fan-out point: it calls catalog, search, cart, orders, and users/auth. Checkout is where the graph deepens — orders calls inventory to reserve stock and payments to charge a card through an external processor.
State is deliberately split by access pattern rather than dumped in one database. Relational, transactional data (orders, users) sits in RDS; high-throughput key-value data (cart, catalog) in DynamoDB; sessions in ElastiCache; static assets in S3. The event backbone is MSK: orders, payments, and inventory publish domain events, and notifications consumes them to send email and push. Read the map once and the rest of the article is just “how does this get built and shipped, safely, many times a day.”
The environments — one account and one cluster per stage
Cartwright runs in four AWS accounts under one Organization: dev, staging, prod, and a shared platform account that holds ECR and the Argo CD hub. Each environment gets its own account and its own EKS cluster — hard isolation at the blast-radius boundary that matters, which is exactly what AWS’s own guidance recommends: separate clusters for dev, test, and prod rather than leaning on namespaces for environment isolation.1
Inside a cluster, the split is by namespace: one per service (storefront, cart, orders…), plus short-lived pr-preview-* namespaces spun up per pull request. So the tenancy model is account-and-cluster per environment, namespace per service — strong isolation between stages, cheap isolation between services within a stage.
The repos — code, config, and infrastructure, kept apart
The repo layout is where teams either stay sane or drift into chaos, so it’s worth being explicit. Four kinds of repository, each with one job:
- Per-service app repos (one each for storefront, cart, orders, …) hold the source, the Dockerfile, the service’s Helm chart, and its CI workflow — everything needed to turn that service into an image.
- A shared Helm/library repo holds the base chart and common CI templates every app repo imports, so a change to the golden path is made once.
- The GitOps config repo is the fleet’s desired state: Argo CD ApplicationSets and per-environment values, plus the image tags CI writes on every merge.
- The IaC repo (Terraform) provisions the accounts, VPCs, EKS clusters, data stores, and ECR — and hands its outputs (IAM role ARNs and the like) to Argo CD through the GitOps bridge, so charts consume real values instead of hard-coded ones.2
The rule that keeps it clean: app code, desired state, and infrastructure never share a repo. CI writes to the config repo but never to app source; humans change infrastructure in the IaC repo, never by clicking in a console.
The pipelines — CI per service, CD by GitOps
The pipeline has two halves that meet at the config repo. CI is per service and runs on every pull request: build and unit test, integration test, build the container, scan it with Trivy in ECR, sign it with cosign, push to ECR, and — the step that connects everything — commit the new image tag into the GitOps config repo. Three gates can stop the line: failing tests, a HIGH/CRITICAL vulnerability, or an unsigned image. Nothing reaches a cluster that didn’t pass all three.
CD is that someone else: Argo CD. It watches the config repo and reconciles the merged tag onto the dev cluster automatically. Promotion then walks the environments — dev to staging behind automated and canary tests, staging to prod behind a manual approval — with Argo Rollouts driving progressive delivery and auto-rollback on an SLO breach at each step. Every pull request additionally gets an ephemeral preview namespace, created on open and torn down on merge. No human ever runs kubectl apply against a cluster; they change Git, and Argo converges the fleet.
The platform it rides on
None of the above works without the fleet-platform services underneath, and Cartwright consumes them rather than reinventing them:
- Karpenter provisions right-sized nodes on demand. AWS states there is no SLA for Karpenter, so you own its operation.3
- EKS Pod Identity is how pods get AWS permissions. It is the recommended default over IRSA for EKS-in-cloud — no per-cluster OIDC provider, and roles reusable across clusters.4
- External Secrets Operator syncs secrets from AWS Secrets Manager into the cluster.
- Kyverno enforces the policy baseline (no privileged pods, no host namespaces).
- AWS Load Balancer Controller turns an Ingress into the ALB out front.
- AMP + AMG (Managed Prometheus and Grafana) give one pane of glass over the fleet.
- Cluster access is granted through EKS access entries. These replace the deprecated
aws-authConfigMap.5
The complete picture
Now put the four layers on one canvas. A developer pushes a change; the app repo’s CI builds, tests, scans, and signs an image into ECR, then bumps the image tag in the GitOps config repo. The Argo CD hub — sitting in the shared platform account — reconciles that desired state into the dev, staging, and prod clusters in their separate accounts, gated by previews, canary tests, and one manual approval. And in every one of those clusters, Cartwright runs on the same platform: Karpenter, Pod Identity, External Secrets, Kyverno, the ALB Controller, and AMP/AMG. Read left to right, this is the entire lifecycle of a change.
That’s Cartwright, whole. The value of seeing it on one page is that the boundaries become obvious: app code is separate from desired state is separate from infrastructure; isolation is strongest between environments and cheapest between services; and no change reaches production except by passing through Git and the gates on the way. Swap in your own service names and account layout and the shape holds — which is rather the point of a reference worth copying.
aws-auth, no SLA for Karpenter) are current as of 2026-07-01; version- and pricing-sensitive EKS facts roll forward roughly every four months, so re-verify against the primary sources before acting.Sources
EKS mechanism, version, and pricing facts are current as of 2026-07-01 and should be re-verified against the primary source before acting.
- AWS — EKS Best Practices: use separate clusters for dev/test/prod rather than namespaces for hard environment isolation. docs.aws.amazon.com/eks/latest/best-practices
- AWS — Terraform EKS Blueprints and the GitOps bridge: IaC-generated metadata (IAM role ARNs) flows into the Argo CD cluster secret so charts consume real values. aws-ia.github.io/terraform-aws-eks-blueprints
- Karpenter: groupless, workload-aware autoscaling; AWS states there is no SLA for Karpenter. karpenter.sh
- AWS — EKS Pod Identities: the recommended default over IRSA for EKS-in-cloud. docs.aws.amazon.com/.../pod-identities.html
- AWS — EKS access entries: replace the deprecated aws-auth ConfigMap. docs.aws.amazon.com/.../access-entries.html
- Argo CD documentation: ApplicationSets and app-of-apps for GitOps-driven, multi-cluster delivery. argo-cd.readthedocs.io
- cosign (Sigstore): container image signing and verification in the supply chain. docs.sigstore.dev