Module 4: Networking Ingress And Networkpolicy

1. Module introduction: the layer that's "not batteries included"

Description

Module 3 left andes-cargo-status-api with externalized configuration, real probes, and a HorizontalPodAutoscaler that already demonstrated, with real load, that it adjusts replicas on its own. All of that is true — and all of it remains invisible from outside the cluster. Today, the only way to talk to andes-cargo-status-api is kubectl port-forward, a temporary tunnel that requires kubectl installed, an open terminal session, and that closes the moment you close it. No Andes Cargo logistics partner is going to install kubectl to check on a shipment. This module resolves exactly that: a real, permanent HTTP door, and a network guardrail that decides who can cross it.

Connection to the module

This is, literally, the module lesson 2 of this guide's own Module 1 already previewed with a full quote. It's worth reading it again now, with the cluster already built in front of you, instead of as an abstract promise.


The quote that opened this guide is now this module

In Module 1, lesson 2, this guide quoted an engineer with real experience, in a Hacker News thread about exactly this topic:

"it's not at all batteries included... you're still going to be installing a bunch of additional controllers (ingress, cert-manager, external dns to start)" — mikeocool, Hacker News (discussion 48548137).

That quote named three pieces a freshly created Kubernetes cluster doesn't come with by default: an Ingress controller, cert-manager, and external-dns. This guide, true to its honesty rule, didn't skip any of them without naming it: cert-manager and external-dns depend on a public domain and a real DNS provider, outside a $0 kind lab's scope — they get named, not installed. But the first piece, Ingress, is exactly what this module builds start to finish, with the same real friction the quote describes: nothing arrives preinstalled, you decide which manifest to apply, you decide which image to run, and you confirm, with kubectl get pods, that it finished starting up.

kind create cluster (Module 1) gave you a functional cluster in under a minute. Installing and configuring Ingress — this module's lesson 4 — takes more steps, more YAML, and a real architecture decision (which host ports to map, which node the controller should run on). That difference in effort is the point mikeocool describes, and is the gap VALIDACION.md (this ecosystem's market audit) found in 0 of 10 previous guides.


What this module builds, in two halves

This module has two halves, each resolving a different networking problem:

First half (lessons 2-5): getting traffic in. Today, status-api-service is a ClusterIP-type Service — reachable only from inside the cluster, or via kubectl port-forward from your machine. Lesson 2 explains the networking model that makes this possible (every Pod, its own IP). Lesson 3 explains what an Ingress is and why "one directly-exposed Service per client" doesn't scale. Lessons 4 and 5 really build it: ingress-nginx installed inside kind, and a real Ingress rule, with curl responding with no tunnel in the middle.

Second half (lessons 6-8): deciding who can talk to whom, inside. Opening a door outward says nothing about what happens inside the cluster — by default, any Pod in any namespace can talk to any other Pod. That's exactly what cloud-security-and-guardrails-guide delegated to this guide, with a literal quote from its own design (see below). Lessons 6, 7, and 8 build the opposite model: nothing passes until an explicit rule allows it.

                    THIS MODULE'S MAP

  Today (end of M3)                        By the end of this module
  ─────────────────                        ──────────────────────
  status-api-service (ClusterIP)           status-api-service (ClusterIP)
        │                                        │
   kubectl port-forward                     ┌─────────────┐
   (temporary tunnel,                       │ Ingress      │◄── real curl,
    requires kubectl)                       │ (ingress-nginx)   no tunnel
        │                                   └──────┬──────┘
        ▼                                          │
   your terminal                            ┌──────▼──────┐
                                             │ NetworkPolicy │  default-deny +
                                             │ (deny-by-      │  explicit rule
                                             │  default)       │  only for
                                             └─────────────────┘  ingress-nginx

The exact delegation this module closes

This isn't a module this guide decided to build on its own initiative — it's, in part, a promise another already-designed sibling guide left in writing, pointing here. cloud-security-and-guardrails-guide declared, in its own DISEÑO.md, the exact boundary of what it does not build:

"EKS and container runtime (in-cluster admission controllers — OPA Gatekeeper, Kyverno —, Docker image scanning, NetworkPolicy) → kubernetes-and-eks-in-production-guide."

NetworkPolicy is named, literally, in that quote. Lesson 6 of this module picks up this exact same complete quote, with the context of why that guide — focused on Terraform infrastructure and supply-chain security — never built the in-cluster network guardrail: it's a different layer of the same discipline, and this guide is where it lives.


Analogy: the building that already has employees, but no front desk or access control

Think of andes-cargo-status-api as a company that already hired its staff (Module 2), gave them operating manuals and a health routine (Module 3) — but still operates in a building with no front desk and no locked door. Any visitor who knows the exact office number can walk straight in, with no front desk to pass; and any other company in the same building can walk right up to that office with nothing stopping them. This module installs two things, in that order: a single front desk (Ingress) that receives every external visitor through the same main door, with no office having to solve its own entry security; and a card-access system (NetworkPolicy) where, by default, no interior door opens for anyone, until someone explicitly authorizes who can pass where. No real office building operates without both — and neither does any production Kubernetes cluster.


This module's map

#LessonWhat it resolves
2Kubernetes' networking model: every Pod, an IPThe contract that makes an Ingress possible: why every Pod can talk to every Pod, with no NAT, by default
3Ingress: an HTTP door for the clusterWhat problem it solves, with the explicit API Gateway parallel (aws-serverless-and-containers-guide M5)
4Hands-on: installing ingress-nginx in kindThe real controller, running inside the cluster
5Hands-on: Ingress for status-api-serviceReal curl, no port-forward, for the first time in this guide
6NetworkPolicy: by default, everyone talks to everyone — and why that doesn't lastcloud-security-and-guardrails-guide's delegation, and the deny-by-default model
7Hands-on: real NetworkPolicy on andes-cargoReal, verified blocking, with a finding that corrects a common assumption about kind
8Project: status-api-service exposed and protectedIngress + NetworkPolicy together, the allowed path and the blocked one, both verified

By the end of this module, andes-cargo-status-api is going to be reachable from outside the cluster through a real HTTP door, and it's going to be protected by a network policy that only lets through exactly the traffic you decided to allow. Module 5 is going to take this exact state and hand it over to ArgoCD as its GitOps source of truth.


Common mistakes

Assuming "installing ingress-nginx" is this module's finish line (expectation). What happens: someone completes lessons 4 and 5, sees curl responding, and considers the module done. Why it happens: a working HTTP door feels like the complete goal — "I can already talk to the service from outside." How to spot it: if you finish lesson 5 without having read anything about NetworkPolicy yet. How to fix it: remember this lesson's analogy — a front desk with no interior access control lets any other company in the building walk into Andes Cargo's office with no restriction. Lessons 6-8 aren't an optional appendix, they're the other half of the same networking problem.

Confusing "the cluster has an Ingress" with "traffic no longer needs a Service" (conceptual, about the relationship between the two objects). What happens: someone, seeing lesson 5, assumes Ingress replaces status-api-service (Module 2), instead of relying on it. Why it happens: both objects solve "how traffic arrives," and it's easy to think one replaces the other. How to spot it: if you wonder whether you should delete service.yaml when you reach this module. How to fix it: lesson 3 explains it in depth — an Ingress never talks directly to a Pod; it always forwards through an existing Service. You're going to keep needing status-api-service, with no change, for the rest of this guide.

Expecting Module 4 to resolve /shipments/<id> (continuity, the same pattern you already saw in Module 3). What happens: someone, seeing curl work for the first time with no port-forward in lesson 5, expects /shipments/4471 to also respond with real data. How to spot it: if you're surprised to see the same 500 Modules 2 and 3 left. How to fix it: this module resolves how traffic arrives and who can send it — not where the data comes from. That second piece depends on a data layer (DynamoDB, via LocalStack) outside this guide's $0 scope: no module installs it. /health remains the signal this module validates end to end; /shipments/<id> stays representative through the capstone.


Exercises

Exercise 1 — Quote this module's source without rereading it. Without going back to the corresponding section, write from memory: (a) mikeocool's literal quote about "batteries included"; (b) the three pieces it names; (c) which of the three this module builds, and why not the other two.

See solution

(a) "it's not at all batteries included... you're still going to be installing a bunch of additional controllers (ingress, cert-manager, external dns to start)". (b) Ingress, cert-manager, external-dns. (c) This module builds Ingress, really executed; cert-manager and external-dns depend on a real public domain and a DNS provider, outside a $0 kind lab's scope — named for honesty, not installed.

Exercise 2 — Explain the building analogy to a colleague who only knows Docker. In two or three sentences, without using the word "Kubernetes," explain why a building with healthy employees (Modules 2-3) might still not be ready to receive visitors.

See solution

A reasonable explanation: "Having healthy employees working inside isn't the same as being ready to receive the public — a single front desk is missing, where every external visitor comes through, instead of every visitor having to know the exact office number; and a card-access system is missing, which, by default, lets no one pass between interior offices until someone explicitly authorizes it."

Exercise 3 — Predict the relationship between Ingress and Service. Before reading lesson 3, with what you already know from Module 2 about status-api-service, predict: does an Ingress talk directly to Pods, or does it go through the existing Service first? Justify your answer with what you already know about why a Service exists in the first place.

See solution

It goes through the Service first. Module 2 (lesson 6) already established why a Service exists: Pod IPs are ephemeral, and the Service gives a stable address that survives Pods changing. An Ingress that talked directly to Pod IPs would have to solve that same stability problem on its own — instead, it reuses the Service that already solves it, and only adds the HTTP routing layer on top.


Summary and next step

This lesson connected this module with the quote that opened this whole guide: mikeocool plainly described that a freshly created Kubernetes cluster doesn't come with Ingress out of the box — it has to be installed, configured, and verified, exactly the real work lessons 4 and 5 do. It also connected this module with a literal promise from cloud-security-and-guardrails-guide, which delegated NetworkPolicy here by name. By the end of this module, andes-cargo-status-api is going to have a real HTTP door and a deny-by-default network guardrail — the two halves of "ready for production traffic" that "3 balanced replicas" (Module 2) and "configuration/health/autoscaling" (Module 3) still didn't cover.

Before moving on you should be able to: quote the market source that opens this module; explain why Ingress doesn't replace Service; and name this module's two halves (incoming traffic, and who can generate it).

Next lesson: Kubernetes' networking model. That's where the technical foundation that makes everything else possible begins — the contract that guarantees every Pod has its own IP, and that any Pod can talk to any other, with no NAT, by default.

Resources

  1. Hacker News — discussion 48548137 — the original thread where mikeocool describes the real "not batteries included" experience, also quoted in Module 1, lesson 2.
  2. src/paths/aws-cloud-ecosystem/VALIDACION.md (NIEVA, market audit, July 19, 2026) — the complete finding: 0 of 10 guides covered ingress, cert-manager, external-dns, CNI, autoscaling, or GitOps.
  3. Kubernetes — Services, Load Balancing, and Networking — the official gateway to everything this module builds.
  4. cloud-security-and-guardrails-guide (NIEVA), DISEÑO.md — the literal NetworkPolicy delegation this module's lesson 6 picks back up in full.
  5. kubernetes-and-eks-in-production-guide (NIEVA), Module 3, lesson 8 — the exact state this module builds on: andes-cargo-status-api with real configuration, probes, and HPA.