Certified Kubernetes Administrator (CKA) #19 Networking 2: Ingress, IngressClass, TLS

#18 Networking 1에서 Service로 Pod 묶음에 안정적인 진입점을 붙였습니다. 그런데 외부에 노출할 서비스가 수십 개라면 어떻게 될까요. NodePort는 포트 번호로만 갈리고, LoadBalancer는 서비스마다 클라우드 로드밸런서를 하나씩 띄워 비용이 부풀어 오릅니다. 도메인이나 경로별로 트래픽을 가르는 요구도 Service 한 층으로는 풀리지 않습니다.

이번 글의 주제인 Ingress가 그 부담을 한곳에 모읍니다. L7(HTTP/HTTPS) 레벨에서 host와 path를 보고 트래픽을 적절한 Service로 갈라 주는 객체입니다. 다만 Ingress는 규칙을 적은 매니페스트일 뿐이고, 그 규칙을 실제 트래픽으로 푸는 것은 Ingress Controller입니다. 이 두 층 모델이 시험과 운영 모두에서 가장 자주 발이 걸리는 지점이므로 거기에 무게를 두겠습니다.

두 층 모델: Ingress와 Ingress Controller #

가장 먼저 잡아야 할 것은 Ingress 객체와 Ingress Controller가 서로 다른 것이라는 점입니다. 이 둘을 혼동하면 시험장에서 “Ingress를 만들었는데 왜 응답이 없지"라는 함정에 빠집니다.

구분정체역할
Ingress(객체)API 리소스(매니페스트)“이 host의 이 path는 저 Service로” 같은 규칙 선언
Ingress Controller실제로 도는 Pod(보통 Deployment + Service)그 규칙을 읽어 트래픽을 실제로 라우팅하는 리버스 프록시

Ingress 매니페스트는 그 자체로는 아무 일도 하지 않습니다. 클러스터에 Ingress Controller가 떠 있어야 컨트롤러가 Ingress 객체를 감시하다가, 규칙대로 프록시 설정을 만들어 외부 트래픽을 받습니다. 컨트롤러는 종류가 여럿입니다.

  • ingress-nginx(쿠버네티스 프로젝트가 관리하는 nginx 기반. 시험과 학습에서 표준)
  • Traefik
  • HAProxy
  • 클라우드 매니지드(GKE Ingress, AWS Load Balancer Controller 등)

CKA 시험 클러스터에는 보통 ingress-nginx가 미리 설치되어 있거나, 설치 매니페스트를 제공하는 경우가 많습니다. 운영 클러스터를 직접 구성한다면 Helm이나 공식 매니페스트로 컨트롤러를 먼저 깔아야 합니다. 컨트롤러 없이 Ingress만 만들면 ADDRESS가 비어 있고 트래픽은 어디에도 닿지 않습니다.

Ingress 객체의 구조 #

Ingress의 핵심 필드는 spec.rules입니다. 규칙 하나는 host(선택)와 path 목록으로 이루어지고, 각 path는 어떤 Service의 어떤 port로 보낼지를 가리킵니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

위 매니페스트는 shop.example.com으로 들어온 요청 가운데 /api로 시작하는 것은 api-service:8080으로, 그 외 /web-service:80으로 보냅니다. 한 도메인 아래에서 경로로 백엔드를 가르는 path 기반 라우팅입니다.

host 기반 라우팅 #

host를 여러 규칙으로 나누면 도메인별로 다른 Service에 보낼 수 있습니다. 하나의 진입점에서 여러 사이트를 호스팅하는 구조입니다.

spec:
  ingressClassName: nginx
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-service
            port:
              number: 80
  - host: blog.example.com   # 도메인별로 규칙을 추가
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: blog-service
            port:
              number: 80

host를 생략하면 모든 호스트명에 규칙이 적용됩니다.

pathType: Prefix와 Exact #

pathType은 v1 Ingress에서 필수 필드입니다. 빠뜨리면 매니페스트가 거부되므로 시험에서 자주 감점되는 지점입니다.

pathType매칭 방식예시
Prefix경로 요소(/ 단위)의 접두사 일치/api/api, /api/v1에 매칭
Exact경로가 정확히 동일/api/api에만 매칭, /api/v1은 불일치
ImplementationSpecific컨트롤러 구현에 위임컨트롤러마다 해석이 다름

대부분의 경우 Prefix를 쓰며, 정확히 한 경로만 받아야 할 때만 Exact를 씁니다. ImplementationSpecific은 컨트롤러 동작에 의존하므로 이식성이 필요하면 피하는 편이 안전합니다.

defaultBackend #

어떤 규칙에도 매칭되지 않는 요청을 받을 기본 백엔드를 둘 수 있습니다. 설정하지 않으면 매칭되지 않는 요청은 컨트롤러의 기본 404로 떨어집니다. spec.rules와 같은 레벨에 둡니다.

spec:
  ingressClassName: nginx
  defaultBackend:        # rules에 매칭 안 되는 요청의 행선지
    service:
      name: fallback-service
      port:
        number: 80
  rules:
  - host: shop.example.com
    # ... (위와 동일한 규칙)

IngressClass: 여러 컨트롤러를 가른다 #

한 클러스터에 Ingress Controller가 여러 개 떠 있을 수 있습니다. 예를 들어 내부용 nginx와 외부용 클라우드 컨트롤러를 함께 운영하는 경우입니다. 이때 각 Ingress 객체가 어느 컨트롤러의 관할인지를 정해 줘야 하는데, 그 역할을 IngressClass가 맡습니다.

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

Ingress 객체는 spec.ingressClassName으로 자신이 따를 IngressClass를 지정합니다. 위 매니페스트들의 ingressClassName: nginx가 바로 이 IngressClass를 가리킵니다. 둘이 이름으로 연결되어야 ingress-nginx 컨트롤러가 그 Ingress를 자기 것으로 인식하고 처리합니다.

is-default-class: "true" 주석이 붙은 IngressClass가 하나 있으면, ingressClassName을 생략한 Ingress는 자동으로 그 기본 클래스에 묶입니다. 컨트롤러가 여럿이거나 기본 클래스가 없는 환경에서는 반드시 ingressClassName을 명시해야 의도한 컨트롤러로 갑니다.

과거에는 kubernetes.io/ingress.class 주석으로 클래스를 지정했습니다. 이 방식은 deprecated이며, 현재는 spec.ingressClassName 필드를 쓰는 것이 표준입니다. 시험에서도 필드 방식으로 작성하겠습니다.

TLS 종단 #

Ingress는 HTTPS 트래픽을 받아 TLS를 종단(terminate)할 수 있습니다. 인증서와 키는 클러스터에 Secret으로 저장하고, Ingress의 spec.tls 섹션에서 그 Secret을 참조합니다.

먼저 kubernetes.io/tls 타입의 Secret을 만듭니다.

# 인증서(tls.crt)와 키(tls.key)로 TLS Secret 생성
kubectl create secret tls shop-tls \
  --cert=tls.crt \
  --key=tls.key

그다음 Ingress에 tls 섹션을 추가하고 해당 host를 묶습니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
  namespace: default
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - shop.example.com
    secretName: shop-tls
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

tls[].hosts에 적은 호스트로 들어오는 HTTPS 요청은 secretName이 가리키는 Secret의 인증서로 종단됩니다. 여기서 흔한 실수가 두 가지입니다. 첫째, tls[].hosts의 도메인과 rules[].host의 도메인이 일치하지 않으면 인증서가 제대로 적용되지 않습니다. 둘째, Secret을 Ingress와 다른 네임스페이스에 만들면 참조되지 않습니다. Secret은 Ingress와 같은 네임스페이스에 있어야 합니다.

검증과 트러블슈팅 #

만든 뒤에는 곧바로 상태를 확인합니다. Ingress의 ADDRESS가 채워졌는지가 1차 신호입니다.

# Ingress 목록과 ADDRESS 확인
kubectl get ingress

# 규칙,TLS,이벤트 자세히 보기
kubectl describe ingress shop-ingress

운영과 시험에서 막히는 전형적인 원인은 다음과 같습니다.

  • ADDRESS가 비어 있음: Ingress Controller가 설치되지 않았거나 떠 있지 않습니다. kubectl get pods -n ingress-nginx로 컨트롤러 Pod 상태를 먼저 봅니다.
  • 404로 떨어짐: pathType이나 path가 어긋났거나, backend가 가리키는 Service,port가 틀렸습니다. kubectl get svc로 대상 Service와 port를 대조합니다.
  • TLS가 적용되지 않음: Secret 이름,타입(kubernetes.io/tls),네임스페이스, 그리고 tls.hostsrules.host의 일치를 확인합니다.
  • 엉뚱한 컨트롤러가 처리함: ingressClassName이 비어 있거나 잘못된 클래스를 가리킵니다.

시험 포인트 #

CKA의 Services and Networking도메인(20%)에서 Ingress는 단골 출제 주제입니다. 다음을 손에 익혀 두면 시간을 아낍니다.

  • Ingress는 **선언(객체)**이고 라우팅은 컨트롤러가 한다는 두 층 모델. 컨트롤러가 없으면 동작하지 않는다는 점을 항상 먼저 떠올리겠습니다.
  • v1 Ingress에서 pathType필수입니다. 빠뜨려 감점되지 않도록 합니다.
  • 컨트롤러 지정은 주석이 아니라 spec.ingressClassName 필드로 합니다.
  • TLS는 kubernetes.io/tls Secret을 만들고 spec.tls에서 참조하며, host가 rules와 일치하고 네임스페이스가 같아야 합니다.
  • apiVersionnetworking.k8s.io/v1입니다. 구버전 표기를 쓰지 않도록 주의합니다.

kubectl create ingress 명령으로 골격을 빠르게 만든 뒤 손편집하는 방법도 유효합니다.

# 명령으로 Ingress 골격 생성 후 YAML 출력
kubectl create ingress shop-ingress \
  --class=nginx \
  --rule="shop.example.com/api*=api-service:8080" \
  --rule="shop.example.com/*=web-service:80" \
  --dry-run=client -o yaml

Ingress의 개념을 더 넓은 맥락에서 복습하려면 K8s 중급 #3 Ingress와 Ingress Controller를 함께 보면 좋습니다.

정리 #

이번 글에서 잡은 것:

  • Ingress는 host,path 기반 L7 라우팅 규칙을 선언하는 객체이고, 실제 트래픽 처리는 Ingress Controller가 합니다. 컨트롤러가 없으면 동작하지 않습니다.
  • 규칙은 spec.rules의 host와 path로 구성하고, 각 path는 pathType(Prefix/Exact)과 backend(Service + port)를 가집니다. defaultBackend로 미매칭 요청의 기본 행선지를 둡니다.
  • IngressClass가 여러 컨트롤러를 가르며, Ingress는 spec.ingressClassName으로 자신의 클래스를 지정합니다.
  • TLSkubernetes.io/tls Secret을 만들어 spec.tls에서 참조하고, host 일치와 동일 네임스페이스가 조건입니다.
  • 검증은 kubectl get ingress의 ADDRESS와 describe로 시작합니다.

다음: Networking 3 #

Ingress로 외부 진입을 정리했습니다. 다음은 클러스터 내부의 이름 해석과 트래픽 통제입니다.

#20 Networking 3: CoreDNS, NetworkPolicy에서는 Pod와 Service가 서로를 이름으로 찾는 CoreDNS의 동작과 설정, 그리고 어떤 Pod가 어떤 Pod와 통신할 수 있는지를 제어하는 NetworkPolicy의 ingress/egress 규칙을 운영 관점에서 정리하겠습니다.

X