관리형 쿠버네티스에서는 Service 타입을 LoadBalancer 로 두면 클라우드가 로드밸런서와 공인 IP 를 붙여 준다. 온프렘에서 쓰던 NodePort 구성을 그대로 옮길 이유가 없다.
Internet -> Azure Load Balancer(공인 IP) -> ingress-nginx Pod -> Service -> Pod
EKS 에서 NLB 를 쓰던 것과 같은 자리다. 바뀌는 것은 애너테이션뿐이고 Ingress 리소스 작성 방식은 동일하다.
kubectl create namespace ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--set controller.service.type=LoadBalancer \
--set controller.ingressClassResource.enabled=true \
--set controller.ingressClassResource.name=nginx \
--set controller.config.use-forwarded-headers="true" \
--set controller.replicaCount=2
kubectl -n ingress-nginx get svc
EXTERNAL-IP 가 채워지면 성공이다. <pending> 에서 멈추면 클라우드 쪽 권한이나 IP 할당 문제다.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
ingress-nginx-controller LoadBalancer 10.0.149.41 20.249.55.68 80:30080/TCP,443:30443/TCP
IP 가 바뀌면 DNS 를 따라 고쳐야 하므로 운영에서는 미리 만들어 붙인다. 중요한 점은 AKS 가 관리하는 리소스 그룹(MC_<RG>_<클러스터>_<리전>)에 만들어야 로드밸런서에 붙는다는 것이다.
# 관리 리소스 그룹 이름 확인
az aks show -g <RG> -n <클러스터> --query nodeResourceGroup -o tsv
# 공인 IP 생성 (Standard SKU)
az network public-ip create \
--resource-group <위에서 확인한 MC_ 리소스 그룹> \
--name ingress-nginx-pip \
--sku Standard --allocation-method Static
az network public-ip show -g <MC_...> -n ingress-nginx-pip --query ipAddress -o tsv
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--set controller.service.type=LoadBalancer \
--set controller.service.loadBalancerIP=<공인 IP>
내부망 전용으로 열려면 내부 로드밸런서 애너테이션을 준다.
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"="true"
공개용이라면 이 애너테이션을 넣지 않는다.
# 온프렘에서 쓰던 형태 - AKS 에서는 부적절
--set controller.service.type=NodePort \
--set controller.service.nodePorts.http=80 \
--set controller.service.nodePorts.https=443
LoadBalancer 로 두면 NodePort 는 내부적으로 자동 할당되며 신경 쓸 필요가 없다.
kubectl get svc 에 보이는 80:30080, 443:30443 의 뒷번호는 LB 와 노드 사이의 내부 포트다. 사용자는 443 으로 접속한다.
O https://app.example.com
X https://app.example.com:30443
애플리케이션 설정에 넣는 외부 URL 에도 포트를 붙이지 않는다. SAS Viya 처럼 URL 문자열을 비교해 리다이렉트를 만드는 제품은 https://host 와 https://host:443 을 다른 값으로 취급해 로그인 루프나 404 가 생긴다. Host 헤더 역시 기본 포트일 때는 포트가 붙지 않으므로 Ingress 의 host 규칙과도 어긋난다.
정식 도메인을 쓰는 것이 원칙이고, DNS 등록 전 임시 확인은 hosts 로 한다.
20.249.55.68 app.example.com
hosts 에 넣더라도 이름은 나중에 쓸 정식 이름으로 정해 둔다. Ingress 규칙 · 인증서 · 애플리케이션 설정이 모두 그 이름을 기준으로 만들어지므로, 나중에 이름을 바꾸면 재배포가 필요하다. 이름만 맞으면 DNS 로 옮길 때 hosts 한 줄을 지우는 것으로 끝난다.
IP 를 URL 로 쓰는 구성은 피한다. 인증서 검증도 안 되고 이름 기반 라우팅도 못 쓴다.
클러스터 안에서는 되는데 외부 PC 에서만 타임아웃이면 네트워크 보안 그룹(NSG)을 먼저 본다.
# 노드가 속한 NSG 목록
az network nsg list -g <MC_...> -o table
# 인바운드 규칙
az network nsg rule list -g <MC_...> --nsg-name <NSG> -o table
LoadBalancer Service 를 만들면 필요한 규칙이 자동으로 추가되는 것이 보통이지만, 조직 정책으로 NSG 를 직접 관리하는 환경에서는 80 · 443 인바운드를 명시해야 한다.
# 도달 여부 확인
curl -I --connect-timeout 5 https://app.example.com/
nc -vz 20.249.55.68 443
Connection timed out 은 차단, Connection refused 는 도달했으나 듣는 프로세스가 없는 것이다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
namespace: default
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80
kubectl create secret tls app-tls --cert=fullchain.crt --key=server.key -n default
--cert 에는 서버 인증서와 중간 CA 를 이어 붙인 파일을 넣는다. 서버 인증서만 넣으면 일부 클라이언트에서만 신뢰 실패가 나며, 원인은 인증서 체인이 끊길 때 와 같다.