Ravindra BagaleCourses & study guides मराठी

Chapter 65: Kubernetes Domain Names and HTTPS: Ingress, DNS, ExternalDNS, cert-manager and Gateway API

65.6 End-to-end on AWS: shop.example.com with ALB, ACM, Route 53 and ExternalDNS

Why: the problem

So far each piece ran on its own: Ingress in 65.1, DNS in 65.2, ExternalDNS in 65.3, cert-manager in 65.4, Gateway API in 65.5. In a real company these pieces meet on one cluster, and the most common production shape on AWS, used by teams at Swiggy, Freshworks and Ola among many others, is: the AWS Load Balancer Controller creates an Application Load Balancer from an Ingress, AWS Certificate Manager (ACM) provides a free certificate that AWS renews itself, ExternalDNS creates a Route 53 alias record to the ALB, and pods receive traffic directly through target type ip. Why this shape? The ALB is managed by AWS, scales on its own, integrates with AWS WAF and Shield, and terminates TLS with an ACM certificate whose private key never leaves AWS, so there is no Secret to protect and nothing to renew. The AWS Load Balancer Controller is a maintained project from AWS, so it is not affected by the ingress-nginx retirement. Its IngressGroup feature lets many Ingress objects in different namespaces share one ALB, saving cost, and annotations control the listener ports, the SSL policy and the health check path. The cost is AWS lock-in and a slower feedback loop: an ALB takes a few minutes to create, and rules live in AWS, not inside the cluster. Many teams combine both worlds: the ALB with ACM at the edge for TLS and WAF, then an in-cluster Gateway such as Envoy Gateway for fine routing. Both are valid; pick deliberately. In this lesson we write the whole thing for an EKS cluster named shop-prod in ap-south-1 (Mumbai): request and validate an ACM certificate with DNS, install the AWS Load Balancer Controller with a scoped IAM role, create one Ingress for shop.example.com and api.shop.example.com, let ExternalDNS publish the names, and verify the chain from dig to curl. Every value is a placeholder; no real keys appear anywhere.

What: every term in plain English

  • AWS Load Balancer Controller — Controller that creates ALBs for Ingress and NLBs for Services on AWS.
  • ALB — Application Load Balancer: AWS layer 7 load balancer.
  • ACM — AWS Certificate Manager: free public certificates that AWS renews automatically.
  • DNS validation — ACM proves domain control through a CNAME record you add once.
  • target-type ip — ALB sends traffic straight to pod IPs, skipping NodePorts.
  • IngressGroup — alb.ingress.kubernetes.io/group.name, letting many Ingresses share one ALB.
  • ssl-policy — Annotation choosing the TLS versions and ciphers on the ALB listener.
  • ssl-redirect — Annotation that makes port 80 redirect to 443.
  • Subnet tags — kubernetes.io/role/elb=1 on public subnets so the controller finds them.
  • Health check path — URL the ALB probes on each target before sending traffic.
Request an ACM certificate with DNS validation. Tag public subnets for ALBs. Create the controller IAM role with IRSA. Install the AWS Load Balancer Controller with Helm. Deploy web and api with readiness probes. Create the ALB Ingress with ACM and redirect. Let ExternalDNS publish both names.
shop.example.com: Route 53, ALB, ACM, pods

How: step by step

  1. Request an ACM certificate with DNS validation. Status ISSUED.
  2. Tag public subnets for ALBs. Controller can find them.
  3. Create the controller IAM role with IRSA. Scoped role.
  4. Install the AWS Load Balancer Controller with Helm. Two pods Running.
  5. Deploy web and api with readiness probes. Pods Ready.
  6. Create the ALB Ingress with ACM and redirect. ALB provisioned.
  7. Let ExternalDNS publish both names. Alias records in Route 53.
  8. Verify with dig, curl and openssl. HTTPS 200 with Amazon issuer.

Example 1: ACM certificate and the controller install

Goal. ACM certificate and the controller install.

acm.sh

aws acm request-certificate --region ap-south-1 \
  --domain-name shop.example.com \
  --subject-alternative-names "*.shop.example.com" \
  --validation-method DNS
CERT_ARN=arn:aws:acm:ap-south-1:111122223333:certificate/1a2b3c4d-0000-4000-8000-123456789abc
aws acm describe-certificate --certificate-arn $CERT_ARN \
  --query "Certificate.DomainValidationOptions[0].ResourceRecord"
# Add that CNAME to Route 53 once (console or change-resource-record-sets), then:
aws acm wait certificate-validated --certificate-arn $CERT_ARN
aws acm describe-certificate --certificate-arn $CERT_ARN --query Certificate.Status

lbc.sh

eksctl create iamserviceaccount --cluster shop-prod --namespace kube-system \
  --name aws-load-balancer-controller \
  --attach-policy-arn arn:aws:iam::111122223333:policy/AWSLoadBalancerControllerIAMPolicy \
  --approve
# EKS_CHARTS_REPO: the eks-charts Helm repo URL from the official AWS Load Balancer Controller install docs
helm repo add eks "$EKS_CHARTS_REPO" && helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system \
  --set clusterName=shop-prod \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller \
  --version 1.14.0
kubectl get deploy -n kube-system aws-load-balancer-controller

What each line does

  • Apex plus wildcard — One certificate covers shop.example.com and every single-label sub-name.
  • DNS validation — One CNAME, kept forever, lets ACM renew automatically every year.
  • acm wait — Blocks until the status is ISSUED.
  • IAM policy — AWSLoadBalancerControllerIAMPolicy is created from the JSON file published in the controller's install docs.
  • Pinned chart version — Same install every time; upgrade on purpose.

Green output

$ bash acm.sh
{
    "CertificateArn": "arn:aws:acm:ap-south-1:111122223333:certificate/1a2b3c4d-0000-4000-8000-123456789abc"
}
{
    "Name": "_3f1c9a2b7d.shop.example.com.",
    "Type": "CNAME",
    "Value": "_8e4d2c1b0a.xlfgrmvvlj.acm-validations.aws."
}
"ISSUED"
$ bash lbc.sh
created serviceaccount "kube-system/aws-load-balancer-controller"
NAME: aws-load-balancer-controller
STATUS: deployed
NAME                           READY   UP-TO-DATE   AVAILABLE
aws-load-balancer-controller   2/2     2            2

Expected result. The certificate is ISSUED and will renew without anyone touching it, and the controller runs with a role that only it can use. Nothing secret was written to the cluster.

Example 2: One ALB Ingress for two host names

Goal. One ALB Ingress for two host names.

alb-ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop
  namespace: shop
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/group.name: shop-public
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-south-1:111122223333:certificate/1a2b3c4d-0000-4000-8000-123456789abc
    alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
    alb.ingress.kubernetes.io/healthcheck-path: /healthz
    external-dns.alpha.kubernetes.io/ttl: "60"
spec:
  ingressClassName: alb
  rules:
    - host: shop.example.com
      http:
        paths:
          - {path: /, pathType: Prefix, backend: {service: {name: web, port: {number: 80}}}}
    - host: api.shop.example.com
      http:
        paths:
          - {path: /, pathType: Prefix, backend: {service: {name: api, port: {number: 80}}}}

What each line does

  • ingressClassName alb — Handled by the AWS Load Balancer Controller, not by an in-cluster proxy.
  • target-type ip — Pod IPs are registered as ALB targets, so readiness gates and fast scaling work well.
  • group.name — Other teams' Ingresses with the same group join this ALB instead of creating new ones.
  • ssl-redirect 443 — Port 80 answers with a redirect to HTTPS.
  • ssl-policy TLS13-1-2 — Allows only TLS 1.2 and 1.3.

Green output

$ kubectl apply -f alb-ingress.yaml
ingress.networking.k8s.io/shop created
$ kubectl get ingress shop -n shop
NAME   CLASS   HOSTS                                   ADDRESS                                                          PORTS
shop   alb     shop.example.com,api.shop.example.com   k8s-shoppublic-6f2d1c9a8b-1234567890.ap-south-1.elb.amazonaws.com   80
$ kubectl describe ingress shop -n shop | tail -2
  Normal  SuccessfullyReconciled  2m   ingress  Successfully reconciled
$ aws elbv2 describe-target-health --target-group-arn arn:aws:elasticloadbalancing:ap-south-1:111122223333:targetgroup/k8s-shop-web-0a1b2c3d4e/0123456789abcdef --query "TargetHealthDescriptions[].TargetHealth.State"
[
    "healthy",
    "healthy"
]

Expected result. The controller created one ALB with HTTP and HTTPS listeners, attached the ACM certificate, and registered the web pods by IP. Both targets passed the /healthz check before receiving traffic.

Example 3: Publish the names and verify the whole chain

Goal. Publish the names and verify the whole chain.

verify.sh

kubectl logs -n kube-system deploy/external-dns --tail=3
dig +short shop.example.com
dig +short api.shop.example.com
curl -sI http://shop.example.com/ | head -2
curl -s -o /dev/null -w "%{http_code} %{ssl_verify_result}\n" https://shop.example.com/
curl -s https://api.shop.example.com/healthz
echo | openssl s_client -connect shop.example.com:443 -servername shop.example.com 2>/dev/null | openssl x509 -noout -issuer -ext subjectAltName

checklist.txt

1 DNS     dig returns ALB IPs for both names
2 Redirect  http answers 301 to https
3 TLS     verify result 0 and Amazon issuer
4 SAN     certificate lists apex and wildcard
5 App     /healthz answers ok through the ALB
6 Cleanup  delete Ingress, then ExternalDNS (sync) removes records

What each line does

  • ExternalDNS logs — Shows it created alias records for both hosts pointing at the ALB.
  • dig — Returns several ALB IPs; they change over time, which is why we use alias, not fixed A records.
  • ssl_verify_result 0 — curl trusted the chain with the system store.
  • Amazon issuer — ACM certificates are issued by Amazon RSA or ECDSA intermediate CAs.
  • Checklist — Order matters: DNS, then redirect, then TLS, then app.

Green output

$ bash verify.sh
level=info msg="Desired change: CREATE shop.example.com A [Id: /hostedzone/Z0123456789EXAMPLE]"
level=info msg="Desired change: CREATE api.shop.example.com A [Id: /hostedzone/Z0123456789EXAMPLE]"
level=info msg="4 record(s) in zone shop.example.com. were successfully updated"
13.235.10.21
3.110.45.87
13.235.10.21
3.110.45.87
HTTP/1.1 301 Moved Permanently
Location: https://shop.example.com:443/
200 0
ok
issuer=C=US, O=Amazon, CN=Amazon RSA 2048 M03
X509v3 Subject Alternative Name:
    DNS:shop.example.com, DNS:*.shop.example.com

Expected result. Every link of the chain works: Route 53 alias records from ExternalDNS, an ALB redirect, an ACM certificate trusted by the system, and healthy pods behind it. No certificate file, private key or DNS ticket was handled by a human.

Every command, step by step (kind lab with kubectl)

  1. ACM

    aws acm request-certificate --domain-name shop.example.com --subject-alternative-names "*.shop.example.com" --validation-method DNS
    

    What it does: Request.

    "CertificateArn": "arn:aws:acm:ap-south-1:111122223333:certificate/1a2b..."
    
  2. Validate

    aws acm wait certificate-validated --certificate-arn $CERT_ARN
    

    What it does: ISSUED.

    "ISSUED"
    
  3. Subnets

    aws ec2 create-tags --resources subnet-0abc1234 subnet-0def5678 --tags Key=kubernetes.io/role/elb,Value=1
    

    What it does: Public subnets.

    (no output)
    
  4. IRSA

    eksctl create iamserviceaccount --cluster shop-prod --name aws-load-balancer-controller ...
    

    What it does: Role.

    created serviceaccount
    
  5. Helm

    helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --version 1.14.0 ...
    

    What it does: Controller.

    STATUS: deployed
    
  6. Ingress

    kubectl apply -f alb-ingress.yaml
    

    What it does: ALB.

    ingress.networking.k8s.io/shop created
    
  7. Address

    kubectl get ingress shop -n shop
    

    What it does: ALB host name.

    k8s-shoppublic-6f2d1c9a8b-1234567890.ap-south-1.elb.amazonaws.com
    
  8. Targets

    aws elbv2 describe-target-health --target-group-arn ...
    

    What it does: Healthy.

    "healthy"
    
  9. DNS

    dig +short shop.example.com
    

    What it does: ALB IPs.

    13.235.10.21
    
  10. HTTPS

    curl -s -o /dev/null -w "%{http_code}" https://shop.example.com/
    

    What it does: 200.

    200
    

When it is red 1

Failed deploy model due to couldn't auto-discover subnets

Cause. Public subnets missing the kubernetes.io/role/elb tag.

Fix, step by step

  1. Tag the subnets.
  2. Or set alb.ingress.kubernetes.io/subnets.

When it is red 2

CertificateNotFound: Certificate ... not found

Cause. ACM certificate in another region than the ALB.

Fix, step by step

  1. Request the certificate in ap-south-1, the cluster's region.

When it is red 3

502 Bad Gateway from the ALB

Cause. Targets unhealthy: wrong health check path or pod not listening.

Fix, step by step

  1. Set healthcheck-path.
  2. aws elbv2 describe-target-health.

When it is red 4

ACM status stays PENDING_VALIDATION

Cause. Validation CNAME not created or created in the wrong zone.

Fix, step by step

  1. Add the exact Name and Value from describe-certificate.

When it is red 5

Each team's Ingress creates a new ALB

Cause. No shared IngressGroup.

Fix, step by step

  1. Use the same alb.ingress.kubernetes.io/group.name.

Practice tasks

Easy Task 1: ACM

Why: Request.

What to do: Request and validate a certificate.

How:
  1. DNS method.
Solution
aws acm describe-certificate --certificate-arn $CERT_ARN --query Certificate.Status

Easy Task 2: Tags

Why: Subnets.

What to do: Tag public subnets.

How:
  1. role/elb.
Solution
aws ec2 describe-subnets --filters Name=tag:kubernetes.io/role/elb,Values=1

Easy Task 3: Controller

Why: Install.

What to do: IRSA plus Helm.

How:
  1. Pinned chart.
Solution
kubectl get deploy -n kube-system aws-load-balancer-controller

Medium Task 4: Ingress

Why: ALB.

What to do: Apply alb-ingress.yaml.

How:
  1. Annotations.
Solution
kubectl describe ingress shop -n shop

Medium Task 5: Health

Why: Targets.

What to do: Check target health.

How:
  1. /healthz.
Solution
aws elbv2 describe-target-health --target-group-arn ...

Medium Task 6: DNS

Why: Publish.

What to do: Read ExternalDNS logs and dig.

How:
  1. Alias A.
Solution
dig +short shop.example.com

Hard Task 7: Verify

Why: Chain.

What to do: Run verify.sh in order.

How:
  1. openssl SAN.
Solution
bash verify.sh

Hard Task 8: Ola

Why: Main task.

What to do: Full drivers.example.com setup.

How:
  1. IngressGroup.
Solution
kubectl get ingress -A

Homework: try at home

For Ola's driver app API, write the full AWS setup for drivers.example.com and api.drivers.example.com: ACM certificate with DNS validation, AWS Load Balancer Controller with IRSA, one IngressGroup ALB with TLS 1.2+ policy and HTTP redirect, ExternalDNS with TTL 60, and a verify.sh that checks DNS, redirect, TLS issuer and app health in order.

Recap

  • On AWS the common pattern is ALB from the AWS Load Balancer Controller plus an ACM certificate.
  • ACM certificates are free, auto-renewing and never exist as a Secret in the cluster.
  • ExternalDNS publishes alias records for the ALB host name.
  • IngressGroup shares one ALB across teams; target-type ip sends traffic straight to pods.
  • Verify in order: DNS, redirect, TLS, app.

FAQ

Q. Can I use cert-manager instead of ACM with an ALB?

No, an ALB can only use ACM or IAM-uploaded certificates. Use cert-manager when TLS ends inside the cluster.

Q. Does the AWS Load Balancer Controller support Gateway API?

Yes, newer releases support Gateway API for ALB and NLB, and AWS also has a separate Gateway API controller for VPC Lattice.

Q. How much does an ALB cost?

An hourly charge plus load balancer capacity units; sharing one ALB with IngressGroup across many apps keeps cost down.

Q. Where does WAF fit?

Attach an AWS WAF web ACL with the alb.ingress.kubernetes.io/wafv2-acl-arn annotation.

Interview questions

  1. Walk me through how a user reaches a pod on EKS over HTTPS.

    Model answer: Route 53 alias to the ALB, TLS with ACM at the ALB, listener rule by host and path, target group with pod IPs, pod.

  2. ALB Ingress or NGINX-style in-cluster controller?

    Model answer: ALB for managed scaling, ACM and WAF; in-cluster proxy or Gateway API for fine routing and portability. Many companies use both.

  3. How do you avoid one ALB per app?

    Model answer: IngressGroup with a shared group.name.

  4. How do you renew certificates on this setup?

    Model answer: ACM renews automatically through the DNS validation record; nothing to do.

Quick quiz

Q1. Who renews an ACM certificate?

AWS, as long as the validation CNAME stays.

Q2. What does target-type ip do?

Registers pod IPs directly.

Q3. How do Ingresses share an ALB?

Same group.name.

Q4. Where must the ACM certificate live?

Same region as the ALB.

Q5. Which tag lets the controller find public subnets?

kubernetes.io/role/elb=1.

Samajla ka?

Lab

  1. Create a fresh kind cluster named ch65 with port 80 and 443 mapped from the control plane node.
  2. Keep YAML files in a folder named k8s-ch65.
  3. Use example.com names in the lab; on AWS use a domain you own in Route 53.
  4. Never paste real AWS keys into YAML; use IAM roles for service accounts or Pod Identity.
  5. Delete the cluster at the end with kind delete cluster --name ch65.

Practice task

Publish a shop on shop.example.com and api.shop.example.com: one Ingress or Gateway, path routing to three Services, DNS records created automatically by ExternalDNS, and a Let's Encrypt certificate issued and renewed by cert-manager, then prove it with curl -v.

Ravindra Bagale's Tip

Interview madhe 'Ingress ani LoadBalancer Service madhla farak', 'HTTPS certificate kasa yeto' aani 'Gateway API ka' he prashna nakki yetat. Lab karun uttar dya.

In short

  • Ingress is a set of HTTP routing rules; an Ingress controller makes them real.
  • DNS maps a name to the load balancer; ExternalDNS keeps those records in sync.
  • cert-manager asks Let's Encrypt for certificates and renews them before expiry.
  • Gateway API splits the platform team's Gateway from the app team's HTTPRoute.