Part 2 — Build the platform · wrap-up
Cheat sheet & self-check
Every command from this section on one page.
Inspect the network
aws ec2 describe-subnets --filters Name=vpc-id,Values=<vpc> --query 'Subnets[].[SubnetId,AvailabilityZone,CidrBlock,AvailableIpAddressCount]' --output table | Subnets, AZs and free IPs |
aws ec2 describe-route-tables --filters Name=vpc-id,Values=<vpc> | Routes (IGW for public, NAT for private) |
aws ec2 describe-nat-gateways --filter Name=vpc-id,Values=<vpc> | NAT gateways per AZ |
aws ec2 describe-vpc-endpoints --filters Name=vpc-id,Values=<vpc> | Private endpoints to AWS services |
aws ec2 describe-subnets --filters Name=tag:kubernetes.io/role/internal-elb,Values=1 | Subnets marked for internal load balancers |
Inspect
kubectl -n kube-system get ds aws-node -o jsonpath='{.spec.template.spec.containers[0].env}' | jq | VPC CNI settings (env vars) |
kubectl get nodes -o custom-columns=NAME:.metadata.name,PODS:.status.allocatable.pods | Max pods per node |
aws ec2 describe-subnets --subnet-ids <id> --query 'Subnets[].AvailableIpAddressCount' | Free IPs left in a subnet |
Key settings
ENABLE_PREFIX_DELEGATION=true | Assign /28 prefixes to ENIs (more pods per node) |
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true | Pods use subnets from ENIConfig (e.g. a secondary CIDR) |
WARM_IP_TARGET / WARM_PREFIX_TARGET | How many spare IPs/prefixes each node keeps ready |
Cluster
eksctl create cluster -f cluster.yaml | Create a cluster from a config file (quick labs) |
aws eks update-kubeconfig --name prod --region eu-west-1 | Write a kubeconfig entry for the cluster |
aws eks describe-cluster --name prod --query 'cluster.resourcesVpcConfig' | Endpoint access, subnets, security groups |
aws eks update-cluster-config --name prod --resources-vpc-config endpointPublicAccess=false,endpointPrivateAccess=true | Make the API endpoint private |
Compute
aws eks list-nodegroups --cluster-name prod | Managed node groups |
kubectl get nodes -L eks.amazonaws.com/nodegroup,node.kubernetes.io/instance-type,topology.kubernetes.io/zone | Nodes with group, type and AZ |
aws eks list-fargate-profiles --cluster-name prod | Fargate profiles |
Add-ons
aws eks describe-addon-versions --addon-name vpc-cni --kubernetes-version 1.33 | Compatible add-on versions for a Kubernetes version |
aws eks create-addon --cluster-name prod --addon-name eks-pod-identity-agent | Install a managed add-on |
aws eks describe-addon --cluster-name prod --addon-name coredns --query 'addon.[addonVersion,status]' | Installed version and health |
Cluster access (access entries)
aws eks describe-cluster --name prod --query cluster.accessConfig | Authentication mode |
aws eks list-access-entries --cluster-name prod | Who has access |
aws eks create-access-entry --cluster-name prod --principal-arn <role-arn> | Grant an IAM role access |
aws eks associate-access-policy --cluster-name prod --principal-arn <role-arn> --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy --access-scope type=namespace,namespaces=shop | View access to one namespace |
aws eks list-associated-access-policies --cluster-name prod --principal-arn <role-arn> | What a principal can do |
kubectl auth can-i --list -n shop | Your own effective Kubernetes permissions |
Pod permissions
aws eks create-pod-identity-association --cluster-name prod --namespace shop --service-account orders --role-arn <role-arn> | Give a ServiceAccount an IAM role (Pod Identity) |
aws eks list-pod-identity-associations --cluster-name prod | All Pod Identity associations |
aws eks describe-cluster --name prod --query cluster.identity.oidc.issuer | OIDC issuer (for IRSA) |
kubectl annotate sa orders -n shop eks.amazonaws.com/role-arn=<role-arn> | IRSA: link a ServiceAccount to a role |
kubectl run awscli -n shop --rm -it --image=amazon/aws-cli --overrides='{"spec":{"serviceAccountName":"orders"}}' -- sts get-caller-identity | Which role does a pod really get? |
Guard-rails
kubectl get resourcequota,limitrange -n shop | Quotas and default limits in a namespace |
kubectl label ns shop pod-security.kubernetes.io/enforce=restricted | Enforce the restricted Pod Security Standard |
aws service-quotas get-service-quota --service-code ec2 --quota-code L-1216C47A | Running On-Demand standard instance vCPU quota |
Controller
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=prod | Install (after creating its IAM role; eks = https://aws.github.io/eks-charts) |
kubectl -n kube-system logs deploy/aws-load-balancer-controller | Why an LB wasn't created |
kubectl get ingress,svc -A -o wide | LB DNS names on Ingresses and Services |
Subnet tags (required for discovery)
kubernetes.io/role/elb = 1 | Public subnets for internet-facing LBs |
kubernetes.io/role/internal-elb = 1 | Private subnets for internal LBs |
ECR
aws ecr create-repository --repository-name shop/api --image-tag-mutability IMMUTABLE --image-scanning-configuration scanOnPush=true | Repository with immutable tags and scan on push |
aws ecr get-login-password | docker login --username AWS --password-stdin <acct>.dkr.ecr.<region>.amazonaws.com | Log Docker in to ECR (12-hour token) |
docker push <acct>.dkr.ecr.<region>.amazonaws.com/shop/api:1.4.0 | Push an image |
aws ecr describe-image-scan-findings --repository-name shop/api --image-id imageTag=1.4.0 | Vulnerability findings |
aws ecr put-lifecycle-policy --repository-name shop/api --lifecycle-policy-text file://lifecycle.json | Expire old images automatically |
Roll out
kubectl set image deploy/api api=<acct>.dkr.ecr.<region>.amazonaws.com/shop/api:1.4.1 -n shop | Start a rolling update |
kubectl rollout status deploy/api -n shop | Watch it complete |
kubectl rollout undo deploy/api -n shop | Roll back to the previous ReplicaSet |
kubectl get pods -n shop -o wide -L topology.kubernetes.io/zone | Are replicas spread across AZs? |
Drivers
aws eks create-addon --cluster-name prod --addon-name aws-ebs-csi-driver | EBS CSI driver (give it a role via Pod Identity) |
aws eks create-addon --cluster-name prod --addon-name aws-efs-csi-driver | EFS CSI driver |
kubectl get csidrivers | ebs.csi.aws.com, efs.csi.aws.com, … |
Tiers
gp3 (WaitForFirstConsumer) | Hot: databases, general RWO volumes |
io2 | Hot and demanding: consistent high IOPS |
EFS (access points) | Shared RWX files across AZs |
S3 (+ Mountpoint CSI) | Cold, large, read-heavy data; backups |
Inspect
kubectl get nodepools,ec2nodeclasses | Karpenter configuration |
kubectl get nodeclaims -o wide | Nodes Karpenter launched (type, zone, capacity type) |
kubectl -n kube-system logs deploy/karpenter -f | Why it launched or removed a node (namespace depends on install) |
kubectl get nodes -L karpenter.sh/capacity-type,node.kubernetes.io/instance-type,topology.kubernetes.io/zone | Capacity type, instance type and zone per node |
Protect a workload
karpenter.sh/do-not-disrupt: "true" | Pod annotation: don't voluntarily disrupt this pod's node |
PodDisruptionBudget | Karpenter respects PDBs when draining |