Deploy OrderFlow to Amazon EKS
Publish OrderFlow images to ECR and deploy its persistent stack on Amazon EKS.
Introduction
30 Second Summary
An application that works perfectly on your laptop can fail when another machine cannot access the same files or data. A reliable release makes every required piece available wherever the application runs.
In this project, you will publish OrderFlow's application images to private Amazon ECR repositories. You will run the complete checkout stack on Amazon EKS with durable data stored on encrypted Amazon EBS volumes.
What You'll Build
A temporary localhost gateway will accept your cloud checkout request before its unique reference appears in Inventory's durable results.
By the end of this project, you'll have:
- Private image releases that let remote workers pull three hardened linux/amd64 application images under immutable v1 tags.
- A reusable Kustomize overlay that applies cloud image addresses and encrypted storage settings while preserving the inherited local base.
- A complete cloud checkout proof that sends an order through Caddy before producing a matching durable Inventory result.
- Secret Mission: A unique Catalog v2 release that you roll out on Amazon EKS before restoring the healthy v1 revision.
Are there any prerequisites?
The project starts with the completed persistent-checkout workspace from the preceding OrderFlow projects.
The live orderflow-foundation cluster must still have its one-node baseline.
The retained ~/Desktop/cluster.yaml file must also be available.
Before We Start
Before you publish anything, this checkpoint commits you to proving that a remote Amazon EKS worker can pull and run OrderFlow without using your local Docker image store. It also makes the same-session cleanup requirement explicit because the inherited AWS resources remain billable while they exist.
Verify the EKS Foundation and Add Cloud Storage
Your inherited Amazon EKS cluster can schedule a test Pod. OrderFlow's databases need a purpose-built storage driver before their claims can bind.
The Amazon EBS CSI Driver needs narrowly scoped AWS permissions. EKS Pod Identity links the storage controller to those permissions.
In this step, get ready to:
- Confirm the inherited toolchain.
- Select the intended cluster context.
- Activate the two storage add-ons.
Confirm the inherited environment
The local clients must work before any cloud change is safe. Their output also confirms that this terminal still matches the inherited foundation.
- Verify the installed command-line tools by running these checks:
aws --version
kubectl version --client
eksctl version
docker buildx version
code --version
curl --version
docker buildx inspect --bootstrap
What do these checks prove?
- The version checks prove the AWS CLI, kubectl, eksctl, Visual Studio Code, and curl are reachable.
- The eksctl line confirms the inherited release.
- The Docker Buildx inspection boots the selected builder before listing its supported platforms.
- The linux/amd64 platform matches the inherited worker architecture.
- Confirm eksctl reports version 0.230.0.
- Confirm the Buildx output lists linux/amd64 under its supported platforms.
- Inspect the current workspace file tree.
- Confirm the k8s folder contains 00-foundation.yaml, 10-catalog.yaml, 20-ordering.yaml, 30-app-config.yaml, and 40-gateway.yaml.
- Use your Linux file manager to inspect ~/Desktop.
- Confirm cluster.yaml remains available.
Is a tool check failing?
A missing command means the inherited Linux environment is incomplete. Stop before changing the cluster.
If the Buildx inspection fails, confirm the Docker service is running before repeating the checks.
Help me diagnose the failed environment check.
Your AWS identity controls which account receives every later resource. This next check only reads the configured identity. It creates no resources.
- Read your current AWS identity by running:
aws sts get-caller-identity
Why check the caller identity?
The response identifies the account currently used by the AWS CLI. Confirming it now prevents changes from landing in an unfamiliar account.
- Confirm the response belongs to the intended AWS account.
- Stop here if the account is unfamiliar.
A kubeconfig context tells kubectl which cluster receives each command. The selected context must point to the inherited foundation before you inspect its worker or add-ons.
- Select the inherited EKS cluster context by running these commands:
aws eks update-kubeconfig --region us-east-1 --name orderflow-foundation
kubectl config current-context
kubectl get nodes
eksctl get addons --cluster orderflow-foundation
What does this cluster preflight do?
- The kubeconfig update selects orderflow-foundation in us-east-1.
- The context check shows which cluster receives later kubectl operations.
- The node check reveals whether the inherited worker can schedule workloads.
- The add-on inventory reveals which storage prerequisites already exist from a partial run.
- Confirm the current context identifies orderflow-foundation.
- Confirm the single worker reports Ready.
- Review the add-on list for eks-pod-identity-agent.
- Review the add-on list for aws-ebs-csi-driver.
- Stop here if the selected context identifies another cluster.
- Stop here if the worker is unavailable.
Is the foundation unavailable?
An unexpected context usually means another cluster was selected after the foundation project. Repeat the kubeconfig update before checking the context again.
An unready worker needs investigation before storage add-ons can be trusted.
Help me diagnose the EKS foundation state.
Check the Pod Identity Agent
Pod Identity associations depend on the agent running inside the cluster. The agent lets EKS deliver temporary AWS credentials to an associated Kubernetes service account.
- Find eks-pod-identity-agent in the preflight add-on list.
- Skip creation when its status is ACTIVE.
- Create the missing Pod Identity Agent by running this command only when its name is absent:
eksctl create addon --cluster orderflow-foundation --name eks-pod-identity-agent --region us-east-1
What does this command do?
The command adds the Pod Identity Agent to orderflow-foundation. Eksctl resolves the default add-on version compatible with the live cluster.
When creation finishes, the cluster has the agent required for Pod Identity associations. An existing ACTIVE installation remains unchanged when you skip the command.
Did the agent creation fail?
A duplicate-resource response usually means the add-on appeared after your preflight. Return to the add-on inventory before attempting another creation.
An authentication or permission failure means the selected AWS identity cannot manage this cluster.
Help me troubleshoot the Pod Identity Agent creation.
Prepare EBS permissions
The EBS CSI controller provisions block volumes for Kubernetes claims. Its service account needs an add-on-owned Pod Identity association before those requests can reach Amazon EBS.
- Find aws-ebs-csi-driver in the preflight add-on list.
- Skip creation when its status is ACTIVE.
- Create the missing EBS CSI Driver with its recommended Pod Identity configuration by running this command only when its name is absent:
eksctl create addon --cluster orderflow-foundation --name aws-ebs-csi-driver --region us-east-1 --auto-apply-pod-identity-associations
What does this command configure?
- Eksctl installs the managed EBS CSI Driver add-on.
- The automatic association option applies the recommended Pod Identity configuration.
- The association targets the EBS controller service account named ebs-csi-controller-sa.
The add-on may need a short wait while EKS starts its Pods. A changing status during that period means the installation is still progressing.
Did the EBS add-on creation fail?
Confirm the Pod Identity Agent is present before retrying the EBS add-on. The association cannot work without that prerequisite.
If a later provisioning event contains UnauthorizedOperation, return here to inspect the association.
Help me troubleshoot the EBS CSI Driver add-on.
Before you run the final checks, ask yourself whether both add-ons and the worker are ready.
- Verify the completed storage foundation by running these commands:
eksctl get addons --cluster orderflow-foundation
aws eks list-pod-identity-associations --cluster-name orderflow-foundation --region us-east-1
kubectl get pods -n kube-system
kubectl get nodes
What proves the foundation is ready?
- Both eks-pod-identity-agent and aws-ebs-csi-driver report ACTIVE.
- The Pod Identity association names ebs-csi-controller-sa.
- The add-on Pods are running in kube-system.
- The single worker reports Ready.
That is the storage foundation complete. Your cluster can now give OrderFlow's databases persistent EBS-backed storage with scoped permissions.
Is a final check incomplete?
- Wait briefly if an add-on status is still changing.
- Repeat the final verification block after the add-on settles.
- Return to the Pod Identity Agent check if the EBS association is absent.
- Describe the first unhealthy add-on Pod if its status is not running.
Help me diagnose the incomplete storage foundation.
Your inherited cluster now has the storage permissions OrderFlow needs. Next, you will publish its hardened application images to private Amazon ECR repositories.
Publish the OrderFlow Images
The storage add-ons are active on your Amazon EKS cluster. Your next goal is to make the hardened OrderFlow images available to its remote worker.
A remote worker cannot read images stored only in your local Docker engine. You will publish each service to a private Amazon ECR repository under an immutable v1 tag. Docker Buildx targets linux/amd64 so the images match the cluster's t3.large worker.
In this step, get ready to:
- Create three private repositories with immutable tags.
- Authenticate Docker to your account-specific registry.
- Publish three hardened linux/amd64 images under v1 tags.
Create the private ECR repositories
Each application gets its own repository so its releases have a separate history. Immutable tags protect an existing v1 release from being overwritten during a rerun.
- Define the registry identity by running these commands:
AWS_REGION=us-east-1
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
What do these variables hold?
- The AWS_REGION variable keeps every repository in us-east-1.
- The AWS_ACCOUNT_ID variable identifies the active AWS account.
- The ECR_REGISTRY variable builds the private registry address for that account.
- Inventory the existing repositories by running:
aws ecr describe-repositories --region "$AWS_REGION"
What does this inventory prove?
The response lists the private repositories that already exist in the active account. This check makes the setup safe to resume after a partial run.
A returned OrderFlow repository already exists. Reuse it only when it has immutable tag behavior.
- Create orderflow/order-service only if it was missing by running:
aws ecr create-repository --repository-name orderflow/order-service --image-tag-mutability IMMUTABLE --region "$AWS_REGION"
What does this create?
This creates a private repository for the Order image. The IMMUTABLE setting prevents a later push from replacing an existing tag.
- Create orderflow/inventory-service only if it was missing by running:
aws ecr create-repository --repository-name orderflow/inventory-service --image-tag-mutability IMMUTABLE --region "$AWS_REGION"
What does this create?
This creates a private repository for the Inventory image. Its immutable tags preserve the identity of every published release.
- Create orderflow/catalog-service only if it was missing by running:
aws ecr create-repository --repository-name orderflow/catalog-service --image-tag-mutability IMMUTABLE --region "$AWS_REGION"
What does this create?
This creates a private repository for the Catalog image. Keeping Catalog separate lets its releases move independently from the other services.
- Confirm all three repositories exist by running the inventory again:
aws ecr describe-repositories --region "$AWS_REGION"
What should the inventory contain?
You should find orderflow/order-service, orderflow/inventory-service, and orderflow/catalog-service in the response. Each project repository should use immutable tag behavior.
Repository creation failed?
If AWS reports that a repository already exists, return to the inventory output. Reuse the repository only if it is private and immutable.
If an existing project repository is mutable, stop before publishing v1.
Help me diagnose the ECR repository state.
Good progress. Your three private repositories now provide stable destinations for the OrderFlow releases.
Authenticate Docker and preflight v1
Docker needs permission to push into your account-specific registry. A tag preflight also protects every existing immutable v1 image from an unnecessary rebuild.
- Authenticate Docker to ECR_REGISTRY by running:
aws ecr get-login-password --region "$AWS_REGION" | docker login --username AWS --password-stdin "$ECR_REGISTRY"
What does this command do?
The AWS CLI supplies registry credentials directly to Docker. Docker can then push images to the private registry stored in ECR_REGISTRY.
Docker confirms that the authentication succeeded. Your password is passed through the command pipeline instead of being typed into the terminal.
Docker authentication failed?
Confirm your shell still contains the three registry variables from the previous substep. Repeat the registry identity commands if you opened a new terminal.
Help me troubleshoot Docker authentication to Amazon ECR.
- Confirm the active builder supports the worker architecture by running:
docker buildx inspect --bootstrap
Why inspect the builder?
The builder reports which target platforms it can produce. The cluster's worker needs images built for linux/amd64.
You should see linux/amd64 among the supported platforms. That confirms the builder can produce images for the EKS worker.
The next checks can return either an existing digest or ImageNotFoundException. A missing tag is an expected preflight result because the next substep publishes it.
- Check the Order v1 tag by running:
aws ecr describe-images --repository-name orderflow/order-service --image-ids imageTag=v1 --region "$AWS_REGION"
How do I read this result?
A returned digest means the immutable Order v1 release already exists. Reuse that image and skip its build command.
An ImageNotFoundException means the Order image still needs to be built.
- Check the Inventory v1 tag by running:
aws ecr describe-images --repository-name orderflow/inventory-service --image-ids imageTag=v1 --region "$AWS_REGION"
How do I read this result?
A returned digest means the immutable Inventory v1 release already exists. Reuse that image and skip its build command.
An ImageNotFoundException means the Inventory image still needs to be built.
- Check the Catalog v1 tag by running:
aws ecr describe-images --repository-name orderflow/catalog-service --image-ids imageTag=v1 --region "$AWS_REGION"
How do I read this result?
A returned digest means the immutable Catalog v1 release already exists. Reuse that image and skip its build command.
An ImageNotFoundException means the Catalog image still needs to be built.
Build and verify the missing images
Each missing release must be built from its hardened service directory. The --platform target matches the remote worker architecture. The --push option sends the finished image directly to its private repository.
Each build can take a few minutes while Docker compiles layers and uploads them. Steady build output means the process is still working.
- Build the Order release only if its preflight returned ImageNotFoundException by running:
docker buildx build --platform linux/amd64 --tag "$ECR_REGISTRY/orderflow/order-service:v1" --push ./order-service
What happens during this build?
Buildx reads the hardened Order source from ./order-service. It publishes the finished linux/amd64 image as the immutable Order v1 release.
When the build finishes, you should see the pushed tag and its digest in the output.
- Build the Inventory release only if its preflight returned ImageNotFoundException by running:
docker buildx build --platform linux/amd64 --tag "$ECR_REGISTRY/orderflow/inventory-service:v1" --push ./inventory-service
What happens during this build?
Buildx reads the hardened Inventory source from ./inventory-service. It publishes the finished linux/amd64 image as the immutable Inventory v1 release.
When the build finishes, you should see the pushed tag and its digest in the output.
- Build the Catalog release only if its preflight returned ImageNotFoundException by running:
docker buildx build --platform linux/amd64 --tag "$ECR_REGISTRY/orderflow/catalog-service:v1" --push ./catalog-service
What happens during this build?
Buildx reads the hardened Catalog source from ./catalog-service. It publishes the finished linux/amd64 image as the immutable Catalog v1 release.
When the build finishes, you should see the pushed tag and its digest in the output.
Build or push failed?
If Docker cannot find a service directory, confirm your terminal is inside the existing persistent-checkout workspace.
If the registry rejects the push, repeat the ECR authentication command. Never retry a build against an existing immutable v1 tag.
Help me troubleshoot an OrderFlow Buildx push.
Before you run the final checks, do you expect all three repositories to return an image digest?
- Verify every v1 release by running these checks:
aws ecr describe-images --repository-name orderflow/order-service --image-ids imageTag=v1 --region "$AWS_REGION"
aws ecr describe-images --repository-name orderflow/inventory-service --image-ids imageTag=v1 --region "$AWS_REGION"
aws ecr describe-images --repository-name orderflow/catalog-service --image-ids imageTag=v1 --region "$AWS_REGION"
What proves the release is complete?
Each response should identify the v1 tag and its image digest. A digest gives the published image a content-specific identity that EKS can pull from the private registry.
That's the release complete. All three hardened images now have immutable v1 identities in your private registry.
Your remote worker can now reach every custom OrderFlow image. Next, you will test what the inherited manifests assume about where those images live.
Expose the Local-Image Assumption
Your three hardened OrderFlow images now live in Amazon ECR. The inherited Kubernetes manifests still point to image names designed for a local cluster.
This step packages those manifests as a reusable Kustomize base. It also adds a default StorageClass backed by encrypted Amazon EBS volumes.
You will apply the unchanged base to Amazon EKS. The resulting Pod states expose whether its local-image assumption survives on a remote worker.
In this step, get ready to:
- Package the inherited manifests as a reusable Kustomize base.
- Add an encrypted default StorageClass for cloud volumes.
- Apply the unchanged base to test its local-image assumptions on the remote worker.
Prepare the reusable storage base
A Kustomize base groups the inherited manifests without duplicating them. The EKS overlay directory gives cloud-specific configuration a separate home.
- Create the overlay directory from the terminal in your existing persistent-checkout workspace by running this command:
mkdir -p k8s/overlays/eks
What does this command do?
The command creates the k8s/overlays/eks directory tree. The -p flag keeps the command safe when part of that path already exists.
- Open the existing persistent-checkout workspace in your code editor.
- Confirm the file tree now contains k8s/overlays/eks.
- Create k8s/kustomization.yaml by pasting this configuration into a new file:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- 00-foundation.yaml
- 10-catalog.yaml
- 20-ordering.yaml
- 30-app-config.yaml
- 40-gateway.yaml
What does this base contain?
- The resources list turns the five inherited manifests into one reusable base.
- The relative paths work because kustomization.yaml sits beside the five manifest files inside k8s.
- The base preserves every inherited workload setting for the portability test.
- Save k8s/kustomization.yaml.
- Confirm k8s/kustomization.yaml appears beside the five inherited manifests in your file tree.
Base file missing from the file tree?
Check that the filename is exactly kustomization.yaml. Confirm that you saved it directly inside k8s.
Help me check the location and contents of my Kustomize base.
The databases also need a default class for their existing storage claims. This class requests encrypted gp3 volumes through the installed EBS CSI driver.
- Create k8s/overlays/eks/storage-class.yaml by pasting this configuration into a new file:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.aws.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
encrypted: "true"
How will storage be provisioned?
- The ebs.csi.aws.com provisioner sends volume requests to the installed EBS CSI driver.
- The gp3 type selects the EBS volume type.
- The encrypted: "true" parameter enables encryption for each provisioned volume.
- The WaitForFirstConsumer mode delays provisioning until a Pod needs the claim.
- The Delete reclaim policy removes dynamically provisioned storage after its claim is deleted.
The default annotation lets an inherited PersistentVolumeClaim use this class without naming it directly.
- Save k8s/overlays/eks/storage-class.yaml.
- Confirm storage-class.yaml appears inside k8s/overlays/eks in your file tree.
Storage file in the wrong folder?
Confirm that storage-class.yaml is inside k8s/overlays/eks. A copy directly inside k8s does not match the overlay path.
Help me verify my EKS storage file path.
✔️ Awesome, I've got everything!
Great. Double check that both YAML files are saved before applying the storage class.
ⓧ I'd like to double check the full code
Compare both files with these complete versions. Their paths must match the filenames shown.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- 00-foundation.yaml
- 10-catalog.yaml
- 20-ordering.yaml
- 30-app-config.yaml
- 40-gateway.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.aws.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
encrypted: "true"
- Create the cluster's default storage class by running this command:
kubectl apply -f k8s/overlays/eks/storage-class.yaml
What did kubectl apply?
The command sends the saved StorageClass definition to the selected cluster. Future claims without an explicit class can now request encrypted gp3 storage.
- Verify the available storage classes by running this command:
kubectl get storageclass
What should you see?
You should see ebs-gp3 listed as the default class. Its provisioner should be ebs.csi.aws.com.
Storage class missing or not default?
- Check that the file is saved at k8s/overlays/eks/storage-class.yaml.
- Compare the default annotation with the full-file reference above.
- Confirm that your current context still identifies orderflow-foundation.
Help me diagnose why ebs-gp3 is missing or not marked as default.
Audit the unchanged local image settings
Rendering a base shows the final manifests without changing the cluster. This check confirms that the inherited application settings remain untouched.
- Render the base and filter its image settings by running this command:
kubectl kustomize k8s | grep -E 'image: orderflow-|imagePullPolicy:'
What does the render reveal?
You should see orderflow-order-service:v1. You should also see orderflow-inventory-service:v1 plus orderflow-catalog-service:v1.
The custom workloads still use imagePullPolicy: Never. That policy permits only an image already present on the worker.
Expected image settings missing?
Compare k8s/kustomization.yaml with the full-file reference. Confirm that all five inherited manifest filenames appear under resources.
Help me find why the Kustomize render is missing the inherited image settings.
Test the unchanged base on Amazon EKS
The rendered YAML is structurally valid. Applying it asks the remote worker to satisfy the inherited image policy with its own local image store.
Before you apply the base, do you think the remote worker has everything required to start the custom application Pods?
- Apply the unchanged local base by running this command:
kubectl apply -k k8s
What did the base create?
The command submits the inherited namespace resources to the remote cluster. It preserves the original image names plus their existing pull policies.
Infrastructure resources can begin progressing. The default StorageClass can also serve the inherited storage claims.
Before you inspect the workloads, which custom Pod state do you expect from an image policy that only accepts images already on the worker?
- Inspect the Pod and claim states by running this command:
kubectl -n orderflow get pods,pvc
What should you observe?
You will see infrastructure Pods progress while the custom application Pods remain unready. This is the intended shortfall in the unchanged local base.
The remote worker does not contain the three local OrderFlow images. The Never policy prevents Kubernetes from retrieving them.
Before you inspect the Catalog events, what do you expect the worker to report about an absent image that it cannot pull?
- Inspect the Catalog Pod events by running this command:
kubectl -n orderflow describe pod -l app=catalog-service
What proves the assumption?
The Catalog Pod remains unready. Its events explain that the local image is absent while the configured policy prevents a pull.
That event connects the visible failure to the inherited local-image assumption. The manifest reached EKS exactly as rendered.
Before the final status check, do you expect the custom Pods to become ready without a manifest change?
- Recheck the custom Pod state by running this command:
kubectl -n orderflow get pods
What should the final check show?
The custom application Pods should still be unready. The unchanged base continues to reference local v1 images with imagePullPolicy: Never.
You have now reproduced the portability gap on the main deployment path. The failure is visible plus diagnosable from the Pod events.
Seeing a different failure?
If a public infrastructure image also fails, inspect that Pod's events for a registry or network problem. Keep that issue separate from the expected custom-image shortfall.
Help me distinguish the intended local-image failure from another Pod problem.
That is the key diagnosis complete. Next, you will keep the reusable base intact while an EKS overlay supplies the cloud image references.
Apply the EKS Overlay
The previous step proved that the remote Amazon EKS worker cannot start custom Pods from local-only image names. Those workloads remain unready until their container settings point to a registry the worker can reach.
A Kustomize overlay preserves the inherited manifests while replacing the cloud-specific settings. It maps the custom images to private Amazon ECR addresses. It also changes the pull policy so EKS can download each image.
In this step, get ready to:
- Create the EKS overlay with private image mappings.
- Audit the rendered manifests before changing the cluster.
- Apply the overlay and recover every OrderFlow workload.
Create the cloud overlay
The overlay starts with the existing base. Its image map replaces only the three local application image names while preserving every inherited Service name.
- Return to the Explorer sidebar in VS Code.
- Select the k8s/overlays/eks folder.
- Click the New File icon.
- Name the file kustomization.yaml.
- Paste the overlay resources and image mappings into k8s/overlays/eks/kustomization.yaml using this code:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../..
- storage-class.yaml
images:
- name: orderflow-order-service
newName: 111122223333.dkr.ecr.us-east-1.amazonaws.com/orderflow/order-service
newTag: v1
- name: orderflow-inventory-service
newName: 111122223333.dkr.ecr.us-east-1.amazonaws.com/orderflow/inventory-service
newTag: v1
- name: orderflow-catalog-service
newName: 111122223333.dkr.ecr.us-east-1.amazonaws.com/orderflow/catalog-service
newTag: v1
What do these image mappings do?
- The resources section imports the reusable base plus the existing storage-class.yaml manifest.
- Each newName replaces a local application image with its private ECR repository address.
- Each newTag keeps the release pinned to the immutable v1 image.
- Save k8s/overlays/eks/kustomization.yaml with Ctrl+S.
- Confirm kustomization.yaml appears beside storage-class.yaml in the eks folder.
File missing from the overlay folder?
Check that the file is inside k8s/overlays/eks. A file created directly inside k8s would replace the base file instead of defining the overlay.
Help me check the location of my EKS overlay file.
The image map supplies reachable addresses. The first two patches change the Catalog and Order workloads from a local-only pull policy.
- Place your cursor after the Catalog image mapping.
- Paste the first two pull-policy patches using this code:
patches:
- target:
group: apps
version: v1
kind: Deployment
name: catalog-service
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
- target:
group: apps
version: v1
kind: Deployment
name: order-service
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
What do these patches change?
Each targeted patch replaces imagePullPolicy for one Deployment. The IfNotPresent policy lets the worker pull the image when it is absent from the node.
The patches leave probes plus configuration untouched. This keeps the cloud change focused on image delivery.
- Save k8s/overlays/eks/kustomization.yaml with Ctrl+S.
- Render the partial overlay from the persistent-checkout terminal by running this command:
kubectl kustomize k8s/overlays/eks | grep -E 'image:|imagePullPolicy:|provisioner:'
What does this partial render prove?
The output should contain three registry-shaped image addresses using 111122223333 as the temporary account placeholder. Catalog and Order should now show IfNotPresent.
Inventory still shows Never at this point. The remaining patches complete that change next.
Partial overlay fails to render?
Check the indentation beneath patches. Make sure the resources paths still point to ../.. plus storage-class.yaml.
Help me debug my partial Kustomize overlay.
Two workloads still use the local-only policy. The final patches update Inventory's API Deployment plus its asynchronous worker.
- Place your cursor after the order-service patch.
- Paste the remaining pull-policy patches using this code:
- target:
group: apps
version: v1
kind: Deployment
name: inventory-service
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
- target:
group: apps
version: v1
kind: Deployment
name: inventory-worker
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
Why patch both Inventory workloads?
The Inventory API and Inventory worker run the same private application image as separate Deployments. Both need a pull-enabled policy because either Pod may start on a worker without that image.
- Save k8s/overlays/eks/kustomization.yaml with Ctrl+S.
- Render the completed overlay by running this command:
kubectl kustomize k8s/overlays/eks | grep -E 'image:|imagePullPolicy:|provisioner:'
What does the completed render show?
All four custom workloads should now show IfNotPresent. The output should also show the ebs.csi.aws.com provisioner.
The image addresses still contain the temporary account placeholder. Replacing that value is the last file edit.
Still see a Never policy?
Compare the affected Deployment name with the matching target in the overlay. A misspelled target leaves the inherited policy unchanged.
Help me find why a workload still renders with Never.
✔️ Awesome, I've got everything!
Great. Double check that k8s/overlays/eks/kustomization.yaml is saved before replacing the account placeholder.
ⓧ I'd like to double check the full code
Compare your overlay with this reference. The account number remains a placeholder here because your saved copy receives its account-specific value next.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../..
- storage-class.yaml
images:
- name: orderflow-order-service
newName: 111122223333.dkr.ecr.us-east-1.amazonaws.com/orderflow/order-service
newTag: v1
- name: orderflow-inventory-service
newName: 111122223333.dkr.ecr.us-east-1.amazonaws.com/orderflow/inventory-service
newTag: v1
- name: orderflow-catalog-service
newName: 111122223333.dkr.ecr.us-east-1.amazonaws.com/orderflow/catalog-service
newTag: v1
patches:
- target:
group: apps
version: v1
kind: Deployment
name: catalog-service
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
- target:
group: apps
version: v1
kind: Deployment
name: order-service
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
- target:
group: apps
version: v1
kind: Deployment
name: inventory-service
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
- target:
group: apps
version: v1
kind: Deployment
name: inventory-worker
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
How should you use this reference?
Match the resource paths plus image names exactly. Match all four Deployment patch targets exactly. The next command replaces only the placeholder account number in your saved file.
Your current terminal already holds the active account number in AWS_ACCOUNT_ID. Substituting that value turns each template address into the private ECR address for your AWS account.
- Replace the placeholder in k8s/overlays/eks/kustomization.yaml by running this command:
sed -i "s/111122223333/${AWS_ACCOUNT_ID}/g" k8s/overlays/eks/kustomization.yaml
What does this replacement change?
The command replaces every occurrence of 111122223333 with the account number already stored in AWS_ACCOUNT_ID. It updates all three ECR image addresses in one file edit.
Before you check, do you expect the placeholder to appear anywhere in the saved overlay?
- Check the replacement by running this command:
grep -n '111122223333' k8s/overlays/eks/kustomization.yaml
What should this check return?
You should see no output. An empty result proves the placeholder no longer exists in the overlay.
Placeholder still appears?
Confirm AWS_ACCOUNT_ID still contains the active account number. Rerun the replacement command above after correcting the terminal value.
Help me replace the ECR account placeholder safely.
Render and audit the overlay
A rendered manifest shows the exact resources that kubectl would send to the cluster. Auditing it now catches an incorrect registry address or an unchanged pull policy before either reaches EKS.
Before you render, do you expect any local-only image address or Never policy to remain?
- Render the account-specific overlay by running this command:
kubectl kustomize k8s/overlays/eks | grep -E 'image:|imagePullPolicy:|provisioner:'
What does the final audit prove?
- Each custom image starts with your account-specific private ECR registry address.
- Each custom workload uses IfNotPresent.
- The StorageClass uses ebs.csi.aws.com.
You should see no 111122223333 placeholder. You should see no Never policy for the custom workloads.
Audit shows a local image or placeholder?
Do not apply the overlay yet. Check the three newName values plus the four patch targets in k8s/overlays/eks/kustomization.yaml.
Help me audit my rendered EKS overlay.
Apply the overlay and wait for recovery
Applying the overlay updates the existing workloads in place. It keeps the inherited Service names plus probes plus configuration boundaries intact.
- Apply the audited overlay by running this command:
kubectl apply -k k8s/overlays/eks
What does applying the overlay do?
Kustomize builds the base plus the EKS-specific changes into one resource set. Kubernetes updates the custom Pod templates with private ECR images plus pull-enabled policies.
The updated Pod templates trigger replacement Pods. The existing Services continue to use the same selectors.
You should see resources reported as configured or unchanged. That output confirms the cluster accepted the rendered resource set.
Overlay application rejected?
Return to the render audit if the cluster rejects the overlay. A YAML indentation issue or an invalid patch target can prevent the resource set from applying.
Help me debug why kubectl rejected my EKS overlay.
Recovery can take several minutes while the worker pulls the images. The Amazon EBS CSI driver also provisions storage for the pending claims.
Before you wait, do you expect every Deployment to become available now that the worker can pull the custom images?
- Wait for all ten Deployments by running this command:
kubectl -n orderflow wait --for=condition=available deployment/catalog-db deployment/catalog-cache deployment/catalog-service deployment/order-db deployment/inventory-db deployment/rabbitmq deployment/order-service deployment/inventory-service deployment/inventory-worker deployment/caddy --timeout=600s
What does this wait confirm?
The command watches every Deployment until it reports the available condition. Its 600s timeout gives image pulls plus volume provisioning time to finish.
That's the recovery complete. Every OrderFlow Deployment is now available on the remote cluster.
Before the final check, what counts do you expect for Deployments, Services, and bound claims?
- Inspect the recovered namespace by running these commands:
kubectl -n orderflow get deployments,services,pods,pvc
kubectl -n orderflow describe deployment catalog-service order-service inventory-service inventory-worker
What do these checks verify?
The resource list confirms workload availability plus Pod readiness plus claim binding. The Deployment descriptions expose the active image addresses for all four custom workloads.
Together these views prove that scheduling, private image delivery, and dynamic storage provisioning have recovered.
You should see ten available Deployments plus nine Services. You should also see four 1Gi claims with a Bound status through ebs-gp3.
The four custom Deployment descriptions should show full private ECR addresses ending in :v1. Their Pods should be ready.
A workload is still unready?
Inspect the first unready Pod instead of applying the entire overlay again. Image-related events point to the registry address or pull policy. Storage-related events point to the CSI driver or its Pod Identity association.
Help me diagnose the first unready OrderFlow Pod.
Your OrderFlow stack now runs from private ECR images with persistent cloud storage. Next up, you will send a real checkout through Caddy and prove that the complete remote pipeline works.
Prove the Cloud Checkout Path
Your ten OrderFlow Deployments are available on Amazon EKS. Four PersistentVolumeClaims are Bound through encrypted gp3 storage.
Ready Pods only prove that Kubernetes scheduled the containers. A real checkout proves Service discovery through Caddy.
The same checkout tests Order, RabbitMQ, Inventory, and the persistent databases as one remote pipeline.
In this step, get ready to:
- Expose the remote Caddy gateway through a temporary local port-forward.
- Seed Catalog with a product through the gateway.
- Submit a unique order and find it in Inventory's durable results.
Open the remote gateway
A Kubernetes port-forward creates a temporary local tunnel to a cluster Service. Local port 8080 carries your requests to Caddy on EKS.
- Start the temporary Caddy tunnel in terminal A by running this command:
kubectl -n orderflow port-forward service/caddy 8080:8080
What does this command do?
- The service/caddy target sends traffic to the Caddy Service in the orderflow namespace.
- The 8080:8080 mapping connects local port 8080 to remote port 8080.
Terminal A stays occupied while the tunnel is active.
- Leave terminal A running.
- Open a second tab in your current Linux terminal application.
- Use the new tab as terminal B.
Before you test the tunnel, do you think the request can reach Caddy inside the remote cluster?
- Check the gateway health from terminal B by running this command:
curl --include http://127.0.0.1:8080/gateway-health
What does this check prove?
The curl request sends an HTTP request through the localhost tunnel. A response proves that the request reached Caddy in EKS.
You'll see an HTTP 200 response. Your Linux device can now reach the remote gateway.
Can't reach the gateway?
- Check that terminal A still shows an active port-forward process.
- Check that terminal B uses the exact localhost address from the command.
- Confirm that the Caddy Pod remains ready in the orderflow namespace.
Ask for help with the tunnel using this troubleshooting prompt.
Seed and read Catalog through Caddy
Catalog needs a known product before the checkout can use the gateway as a complete application entry point. The first request checks whether that product already exists.
Before you request the product, do you think a fresh Catalog database already contains K8S-TEE-001?
- Request the Catalog product from terminal B by running this command:
curl --include http://127.0.0.1:8080/products/K8S-TEE-001
What does this request check?
The request asks Catalog for the product identified by K8S-TEE-001. It travels through the same Caddy gateway as the health check.
You'll see an HTTP 404 response. That result confirms the Catalog route works while showing that the product still needs to be created.
- Create the Catalog product after the HTTP 404 response by running this command:
curl --include --header "Content-Type: application/json" --data '{"sku":"K8S-TEE-001","name":"Kubernetes Tee","price_cents":3200}' http://127.0.0.1:8080/products
What does this request create?
- The JSON body identifies the product with the SKU K8S-TEE-001.
- The body stores Kubernetes Tee as the product name.
- The price_cents value stores the price as 3200.
Catalog accepts the product through the remote gateway.
- Request the new product again by running this command:
curl --include http://127.0.0.1:8080/products/K8S-TEE-001
What does the second request prove?
The repeated request reads the product through Caddy. A successful response confirms that Catalog stored the record in its persistent database.
You'll see an HTTP 200 response containing K8S-TEE-001, Kubernetes Tee, and 3200.
Can't read the Catalog product?
- Check that the creation request followed the initial HTTP 404 response.
- Check the SKU spelling in both requests.
- Confirm that terminal A still has the Caddy port-forward running.
Use this Catalog troubleshooting prompt if the product remains unavailable.
Submit a rerun-safe asynchronous order
The order flow completes asynchronously. The order endpoint acknowledges the request first. Inventory records the processed result after RabbitMQ delivers the work.
A timestamped reference gives every run a unique identity. This lets you find your exact order in the durable results.
- Create a unique order reference in terminal B by running these commands:
ORDER_REF="eks-flow-$(date +%s)"
echo "$ORDER_REF"
How does the reference stay unique?
The timestamp becomes part of ORDER_REF. The second command prints the exact value that the order request sends.
You'll see a reference beginning with eks-flow- in terminal B.
- Keep the printed reference visible in terminal B.
Before you submit the order, do you think the endpoint waits for Inventory to finish processing it?
- Submit the unique order through Caddy by running this command:
curl --include --header "Content-Type: application/json" --data "{\"client_reference\":\"${ORDER_REF}\",\"sku\":\"SCALE-001\",\"quantity\":1}" http://127.0.0.1:8080/orders
What happens to this order?
- The request sends your unique reference to the Order service.
- The order uses the SKU SCALE-001 with a quantity of 1.
- The asynchronous pipeline passes the work through RabbitMQ for Inventory to process.
You'll see an HTTP 202 response. Order has accepted the request for asynchronous processing.
Before you check Inventory, do you expect the printed reference to appear in the durable results?
- Check Inventory's processed orders by running this command:
curl --include http://127.0.0.1:8080/processed-orders
What does this final check prove?
The response comes from Inventory's durable processed results. Finding your unique reference proves that Caddy, Order, RabbitMQ, the Inventory worker, and persistent storage completed the remote checkout path.
You'll see the same eks-flow- reference that terminal B printed earlier.
- Run the processed-orders check again after a short wait if the reference is still absent.
Reference still missing?
- Confirm that the order request returned HTTP 202.
- Compare the printed reference with the value in the processed results.
- Check that terminal A still has the port-forward running.
Use this checkout troubleshooting prompt if Inventory does not record the order.
That's the complete cloud checkout proven. Your request entered through Caddy and finished in Inventory's durable results.
Secret mission
Roll Out and Roll Back Catalog v2
Publish a unique immutable Catalog v2 image to Amazon ECR. Roll it across the live Amazon EKS Deployment. Restore v1 through Kubernetes revision history without interrupting the Catalog route.
Clean Up Your Resources
Clean Up Your Resources
Your Amazon Web Services resources remain billable while they run. Decide whether to keep them, pause the worker, or delete them while retaining the no-cost persistent-checkout workspace.
Cost warning
The Amazon EKS control plane costs $0.10 per cluster-hour. The t3.large worker costs $0.0835 per hour.
The public IPv4 address remains billable. Your Amazon EBS volumes also remain billable.
Private Amazon ECR storage costs $0.10 per GB-month. T3 Unlimited CPU credit charges are also possible during sustained CPU use.
- Choose the Pause or Delete tab below if you are finished testing today.
Resources you used:
- The orderflow Kubernetes namespace with ten Deployments, nine Services, ready Pods, and four 1Gi PersistentVolumeClaims.
- Four encrypted gp3 volumes dynamically provisioned through Amazon EBS.
- The default ebs-gp3 StorageClass with a Delete reclaim policy.
- The private orderflow/order-service, orderflow/inventory-service, and orderflow/catalog-service ECR repositories with immutable images.
- The orderflow-foundation cluster with its control plane, managed node group, VPC, worker root volume, and public IPv4 address.
- The EKS Pod Identity Agent plus the Amazon EBS CSI Driver add-ons with their Pod Identity resources.
Keep everything running
No cloud cleanup is needed. Choose this if you plan to continue working with the running OrderFlow deployment soon.
- Stop the Caddy port-forward with Ctrl+C when you no longer need localhost access.
- Keep the orderflow namespace running.
- Keep the four PersistentVolumeClaims available.
- Retain the three private ECR repositories.
- Retain the orderflow-foundation cluster.
- Keep the persistent-checkout workspace with its Kustomize base and EKS overlay.
EKS, EC2, EBS, ECR, public IPv4, and possible data-transfer charges continue while these resources remain available.
Pause - I'll come back to this later
Shut down the running worker to reduce compute charges. Your cluster configuration, volumes, images, and local files remain available.
- Stop the Caddy port-forward with Ctrl+C.
- Use the scaling method from Create an Amazon EKS Foundation for the orderflow-workers node group.
- Set its desired capacity to 0.
- Set its minimum capacity to 0.
- Keep its maximum capacity at 2.
- Restore one worker before resuming OrderFlow.
The worker charge stops after it scales down. The EKS control plane remains billable.
Your EBS volumes remain billable. Your ECR storage also remains billable.
Delete - I don't want to use this again
Deleting these resources is permanent. The cleanup order gives the storage driver time to remove each backing volume before the cluster disappears.
- Stop terminal A by pressing Ctrl+C.
- Delete the orderflow namespace by running this command:
kubectl delete namespace orderflow --wait=true --timeout=300s
What does this delete?
Namespace deletion removes all namespaced workloads. It also deletes the four PersistentVolumeClaims.
The Delete reclaim policy tells the CSI driver to remove each PersistentVolume. The driver also removes its backing EBS volume.
- Check for remaining PersistentVolumes by running:
kubectl get pv
What should you see?
You should see no remaining volume backed by ebs-gp3. This confirms the dynamically provisioned storage has been released.
- Repeat the previous check until no volume backed by ebs-gp3 remains.
- Avoid deleting any PersistentVolume manually.
- Delete the ebs-gp3 StorageClass by running:
kubectl delete storageclass ebs-gp3
Why delete the StorageClass last?
The StorageClass controls dynamic EBS provisioning. Removing it after the volumes disappear preserves the correct cleanup sequence.
- Delete the orderflow/order-service repository by running:
aws ecr delete-repository --repository-name orderflow/order-service --force --region us-east-1
What does this command remove?
The --force option deletes the non-empty repository. Its immutable v1 image is removed with it.
- Press the up-arrow key in your terminal to recall the repository deletion command.
- Replace orderflow/order-service with orderflow/inventory-service.
- Run the edited command.
- Press the up-arrow key again to recall the edited command.
- Replace orderflow/inventory-service with orderflow/catalog-service.
- Run the edited command.
Good work. The three private repositories have now been removed with their immutable v1 images and the unique Catalog v2-<timestamp> image.
Cluster deletion can take several minutes while AWS removes the managed infrastructure.
- Change your terminal location to ~/Desktop.
- Delete the inherited EKS cluster by running:
eksctl delete cluster --config-file=cluster.yaml --disable-nodegroup-eviction --wait
What does this command remove?
This removes the orderflow-foundation cluster and its managed node group. It also removes the associated cluster infrastructure managed by the retained configuration.
The --wait option keeps the command attached to the deletion. Any cleanup errors remain visible in your terminal.
- Verify that the cluster is gone by running:
aws eks list-clusters --region us-east-1
What confirms cleanup?
The response should no longer contain orderflow-foundation. That is the critical cleanup complete because the paid cluster is no longer running.
Nice Work!
Nice Work!
Outstanding work! OrderFlow now has a cloud release path from hardened images to a durable deployment on Amazon EKS.
Here's what you learned:
- Published three hardened images for linux/amd64 under immutable v1 tags in private Amazon ECR repositories.
- Exposed the local-only image boundary through a controlled failure caused by imagePullPolicy: Never. Recovered all ten Deployments with a reusable Kustomize overlay. Backed four persistent claims with encrypted Amazon EBS gp3 storage through EKS Pod Identity.
- Proved the end-to-end cloud checkout through Caddy. Confirmed the rerun-safe order in Inventory's durable processed results.
- Completed the optional Secret Mission by publishing a unique Catalog v2 release tag. Observed its controlled rollout on Kubernetes. Restored v1 through a rollback.
Ready to quiz yourself?