Skip to main content

네트워크 서비스 다루기

Service 동작원리

  • Pod Network: CNI(Container Network Interface plugin)에서 관리하는 포드 간 통신에 사용되는 클러스터 전체 네트워크
  • Service Network: Service Discovery를 위해 kube-proxy가 관리하는 Cluster-wide 범위의 가상 아이피
  • Kubernetes Network Proxy: 각각의 노드에서 실행되고, Kubernetes Service API에 정의된 서비스를 각 노드에서 반영

kube-proxy의 역할: iptables rule을 설정하고 외부 네트워크와 파드를 연결시킨다.

Service Type

  • ClusterIP(default): Pod 그룹(동일한 서비스를 지원하는 파드 모음)의 단일 진입점(VirtualIP: LB) 생성
  • NodePort: ClusterIP가 생성된 후 모든 워커 노드에 외부에서 접속 가능한 포트가 예약
  • LoadBalancer: 클라우드 인프라스트럭쳐(AWS, Azure, GCP)에 적용, LoadBalancer를 자동으로 프로비저닝하는 기능 지원

ClusterIP 생성하기

# deployment 생성
# vi deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
name: nginx-pod
labels:
app: web
spec:
containers:
- name: nginx-container
image: nginx:1.14

# service 생성
# vi service.yaml
apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
clusterIP: 10.96.100.100
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 80

# 디플로이먼트와 서비스 생성
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

# 생성된 파드 확인 및 아이피 확인
kubectl get pods -o wide | grep -i web
...
web-8b7cdf948-m2ckx 1/1 Running 0 40m 192.168.104.9 node2 <none> <none>
web-8b7cdf948-qchrm 1/1 Running 0 40m 192.168.104.5 node2 <none> <none>

# 생성된 두 파드의 아이피에 대한 단일 진입점으로 10.96.100.100 생성
# 생성된 서비스 확인
kubectl get svc

# 위 두 파드가 node1 / node2 에서 실행되므로
# node1 / node2 접속
ssh node1
ssh node2

# clusterIP (단일 진입점)로 요청
$ curl 10.96.100.100

Service Type: ClusterIP

  • selector의 label이 동일한 Pod들을 그룹으로 묶어 단일 진입점(VirtualIP)을 생성
  • 클러스터 내부에서만 사용 가능
  • Service Type 생략 시 default로 설정된다.
  • 10.96.0.0/12 범위에서 클러스터 아이피가 할당
  • 서비스에 의해 할당되는 IP 주소에는 Cluster IP와 External IP가 있습니다. Cluster IP는 클러스터 안의 Pod끼리 통신하기 위한 Private IP이며 External IP는 클러스터 외부에 공개하는 IP 주소입니다.
  • 클러스터 내부에서만 접근할 수 있으며, 외부에서 접근이 불가능하므로 Port 포워딩이나 Proxy를 통해 접근해야 합니다.
note

동일한 서비스를 제공하는 Pod 그룹에 ClusterIP 생성하기

  • deployment name: web, image: nginx, port: 80, replicas: 2
  • service name: web, type: clusterIP
# 디플로이먼트 생성
kubectl create deployment web --image=nginx --port=80 --replicas=2 --dry-run=client -o yaml
kubectl create deployment web --image=nginx --port=80 --replicas=2 -o yaml

# 파드 생성 확인
kubectl get pods -o wide | grep -i web

# 디플로이먼트에서 생성되는 파드의 아이피를 연결하는 단일 진입점으로 ClusterIP 생성
# 서비스 생성
kubectl expose deployment web --type=ClusterIP --port=80 --target-port=80 --dry-run=client -o yaml
kubectl expose deployment web --type=ClusterIP --port=80 --target-port=80 -o yaml

# svc 확인
kubectl get svc web
...
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.99.47.201 <none> 80/TCP 8m17s

# node1 접속
ssh node1

# curl
curl 10.99.47.201

# 클러스터 IP 대역 확인
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
...
"--service-cluster-ip-range=10.96.0.0/12",
"--cluster-cidr=192.168.0.0/16",


note
  • 작업클러스터: k8s 'devops' namespace에서 운영되고 있는 eshop-order deployment의 서비스를 만드세요.
  • Service name: eshop-order-svc
  • Type: ClusterIP
  • Port: 80
# 네임스페이스 devops에서 구동중인 디플로이먼트 확인
kubectl get deployments.apps -n devops

# 네임스페이스 devops에서 구동중인 파드 확인
kubectl get pod -n devops -o wide
...
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
eshop-order-74dbc8f5bf-ddhjm 1/1 Running 0 22h 192.168.166.189 node1 <none> <none>
eshop-order-74dbc8f5bf-pt87f 1/1 Running 2 (42h ago) 2d15h 192.168.166.148 node1 <none> <none>

# 파드 상세 확인
kubectl describe pod -n devops eshop-order-74dbc8f5bf-pt87f

# selector 확인
kubectl get deployments.apps -n devops -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
eshop-order 2/2 2 2 2d15h nginx-container nginx:1.14 name=order

# 서비스 생성, 셀렉터 자동 기입 확인
kubectl expose deployment -n devops eshop-order --type-ClusterIP --port=80 --target-port=80 --dry-run=client --name=eshop-order-svc -o yaml
kubectl expose deployment -n devops eshop-order --type=ClusterIP --port=80 --target-port=80 --name=eshop-order-svc -o yaml

# 서비스 생성 확인
kubectl get svc -n devops

# 서비스 상세 확인
kubectl describe service -n devops eshop-order-svc

# pod: node1
# node1 접속
ssh node1

# 클러스터 IP 확인
curl 10.100.50.187
note
  • 작업클러스터: k8s
  • 미리 배포한 front-end에 기존의 nginx 컨테이너의 포트 80/tcp를 expose하는 http라는 이름을 추가합니다.
  • 컨테이너 포트 http를 expose하는 front-end-svc라는 새 서비스를 만듭니다.
  • 준비된 노드의 NodePort를 통해 개별 파드들을 expose 되도록 서비스를 구성합니다.

NodePort 변경하기

# context 확인
kubectl config current-context
kubectl config use-context k8s

kubectl get deployments.apps | grep -i front
kubectl get deployments.apps front-end -o yaml > front-end.yaml

# vi front-end.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: front-end
spec:
replicas: 2
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx
name: http
ports:
- containerPort: 80
name: http
---

apiVersion: v1
kind: Service
metadata:
name: front-end-svc
spec:
selector:
run: nginx
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http # 이름 기반 포트 설정


# 기존 디플로이먼트 삭제
kubectl delete deployments.apps front-end

# 디플로이먼트, 서비스 생성
kubectl apply -f front-end.yaml

# 파드 생성 확인
kubectl get pods | grep -i front

# 서비스 생성 확인
kubectl get svc front-end-svc

# ClusterIP -> NodePort 수정 (type: NodePort)
kubectl edit services front-end-svc

# 노드 포트로 변경 확인
kubectl get svc front-end-svc
...
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
front-end-svc NodePort 10.102.235.178 <none> 80:32641/TCP 9m40s

# nginx 페이지 확인
curl node1:32641
curl node2:32641

NodePort

  • 모든 노드를 대상으로 외부 접속 가능한 포트를 예약
  • Default NodePort 범위: 30000 - 32767
  • ClusterIP를 생성 후 NodePort를 예약
note
  • 작업클러스터: k8s
  • 'front-end' deployment의 nginx 컨테이너를 expose하는 'front-end-nodesvc'라는 새 서비스를 생성
  • front-end로 동작중인 Pod에는 node의 30200 포트로 접속
  • 구성 테스트 curl node1:30200 연결시 nginx 홈페이지 표시
kubectl config current-context
kubectl config use-context k8s

# 디플로이먼트 동작 확인
kubectl get deployments.apps front-end

# 파드 동작 확인
kubectl get pods | grep -i front-end

# port, label 검색
kubectl describe pod front-end-5f785fb946-gcjh | grep -i -e port -e labels

kubectl describe deployments.apps front-end | grep -i -e port -e labels
...
Labels: <none>
Labels: run=nginx
Port: 80/TCP
Host Port: 0/TCP

# 커맨드라인에서는 노드포트 번호 할당 불가
# 서비스 생성
kubectl expose deployment front-end --type=NodePort --name=front-node-svc --port=80 --target-port=80 --dry-run=client -o yaml
kubectl expose deployment front-end --type=NodePort --name=front-node-svc --port=80 --target-port=80 --dry-run=client -o yaml > front-end-nodesvc.yaml

# vi front-end-nodesvc.yaml
apiVersion: v1
kind: Service
metadata:
name: front-node-svc
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30200
selector:
run: nginx
type: NodePort


kubectl apply -f front-end-nodesvc.yaml

kubectl get services
...
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
front-end-svc NodePort 10.102.235.178 <none> 80:32641/TCP 37m
front-node-svc NodePort 10.100.1.215 <none> 80:30200/TCP 4s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d17h
web ClusterIP 10.100.217.36 <none> 80/TCP 75m

# 외부 접속 허용
curl node1:30200

# vi front-end-nodesvc.yaml
apiVersion: v1
kind: Service
metadata:
name: front-node-svc
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30200
- port: 443
protocol: TCP
targetPort: 443
nodePort: 30300
selector:
run: nginx
type: NodePort

Network Policy

  • Kubernetes가 지원하는 Pod 통신 접근제한.
  • 일종의 방화벽으로 Pod로 트래픽이 들어오고(Inbound), 나가는(Outbound)것을 설정하는 정책
  • Ingress 트래픽: Inbound 정책, 들어오는 트래픽을 허용할 것인지를 정의
  • Egress 트래픽: Outbound 정책, 트래픽이 나갈 수 있는 허용 범위 정의

트래픽 컨트롤 정의

  • ipBlock: CIDR IP 대역으로 특정 IP 대역에서만 트래픽이 들어오도록 지정할 수 있다.
  • podSelector: label을 이용하여, 특정 label를 가지고 있는 Pod들에서 들어오는 트래픽만 받을 수 있다. 예를 들어 DB Pod의 경우에는 API 서버로부터 들어오는 트래픽만 받는 것과 같은 정책 정의가 가능하다.
  • namespaceSelector: 특정 namespace로부터 들어오는 트래픽만을 받는 기능이다. 운영 로깅 서버의 경우에는 운영 환경 namespace에서만 들어오는 트래픽을 받거나, 특정 서비스 컴포넌트의 namespace에서의 트래픽만 들어오게 컨트롤이 가능하다. 내부적으로 새로운 서비스 컴포넌트를 오픈했을 때, 베타 서비스를 위해서 특정 서비스나 팀에게만 서비스를 오픈하고자 할 때 유용하게 사용할 수 있다.
  • Protocol & Port: 특정 Protocol 또는 Port로 설정된 트래픽만 허용되는 포트를 정의할 수 있다.

app: web 레이블을 가진 파드에 특정 namespace의 파드들만 접근 허용

# 컨텍스트 스위칭
kubectl config current-context
kubectl config use-context hk8s

kubectl get nodes
kubectl get pod -A

# 파드 실행
kubectl run webpod --image=nginx --port=80 --labels=app=web --dry-run=client -o yaml
kubectl run webpod --image=nginx --port=80 --labels=app=web

# webpod 파드 확인
kubectl get pod webpod -o wide

# 네임스페이스 생성
kubectl create namespace dev
kubectl create namespace prod

# 네임스페이스 라벨 설정
kubectl label namespaces prod purpose=production
kubectl label namespaces dev purpose=development

# 네임스페이스 확인
kubectl get namespace -L purpose
kubectl get namespace --show-labels

# vi web-allow-prod.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-allow-prod
namespace: default
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
purpose: production
ports:
- protocol: TCP
port: 80


# 네트워크 정책 생성
kubectl apply -f web-allow-prod.yaml

# 생성된 파드 확인
kubectl get pods -o wide
...
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
campus-01 1/1 Running 2 (45h ago) 2d18h 192.168.166.150 node1 <none> <none>
custom-app 1/1 Running 2 (45h ago) 2d18h 192.168.166.152 node1 <none> <none>
webpod 1/1 Running 0 12m 192.168.104.7 node2 <none> <none>

kubectl get networkpolicies
kubectl describe networkpolicies web-allow-prod

# dev 네임스페이스 파드 생성과 동시 접속
kubectl run testpod -it --rm --image=centos:7 -n dev -- /bin/bash

# 접근 불가
curl 192.168.104.7

# prod 네임스페이스 파드 생성과 동시 접속
kubectl run testpod -it --rm --image=centos:7 -n prod -- /bin/bash

# 접근 가능
curl 192.168.104.7

# 리소스 정리
kubectl delete pod webpod
kubectl delete namespaces {dev,prod}
kubectl delete networkpolicies web-allow-prod
note
  • 작업 클러스터: hk8s
  • default namespace에 다음과 같은 파드를 생성하세요.
  • name: poc
  • image: nginx
  • port: 80
  • label: app=poc

"partition=customera"를 사용하는 namespace에서만 poc의 80포트로 연결할 수 있도록 default namespace에 "allow-web-from-customera"라는 network Policy를 설정하세요. 보안 정책상 다른 네임스페이스의 접근은 제한합니다.

# 컨텍스트 스위칭
kubectl config current-context
kubectl config use-context hk8s

# 파드 실행
kubectl run poc --image=nginx --port=80 --labels=app=poc --dry-run=client
kubectl run poc --image=nginx --port=80 --labels=app=poc

kubectl get pods -o wide
...
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
campus-01 1/1 Running 2 (45h ago) 2d18h 192.168.166.150 node1 <none> <none>
custom-app 1/1 Running 2 (45h ago) 2d18h 192.168.166.152 node1 <none> <none>
poc 1/1 Running 0 14s 192.168.104.8 node2 <none> <none>

# 네임스페이스에 라벨링
kubectl label namespaces customera partion=customera
kubectl label namespaces customerb partion=customerb

# 라벨 확인
kubectl get namespace -L partion

# vi allow-web-from-customera.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-from-customera
namespace: default
spec:
podSelector:
matchLabels:
app: poc
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
partition: customera
ports:
- protocol: TCP
port: 80

# 네트워크 정책 생성
kubectl apply -f allow-web-from-customera.yaml

# namespace customera에서 접속
kubectl run testpod -n customera --image=centos:7 -it --rm -- /bin/bash

# 접속 확인
curl 192.168.104.8

# namespace customerb에서 접속
kubectl run testpod -n customerb --image=centos:7 -it --rm -- /bin/bash

# 접속 불가
curl 192.168.104.8

Ingress

Ingress는 외부로부터 들어오는 요청에 대한 로드밸런싱, TLS/SSL 인증서 처리, 도메인 기반 가상 호스팅 제공, 특정 HTTP 경로의 라우팅 등의 규칙 등을 정의해 둔 자원입니다. 이런 규칙들을 실제로 동작하게 해주는 것은 Ingress-Controller입니다.

하지만 클라우드 서비스를 사용하게 되면 별다른 설정없이 각 클라우드 서비스에서 자사의 로드밸런서 서비스들과 연동해서 Ingress를 사용할 수 있게 해주지만, 클라우드를 사용하지 않고 클러스터를 구축해서 사용하는 경우는 Ingress Controller를 직접 Ingress와 연동시켜야 합니다.

인그레스 컨트롤러는 쿠키 기반이 세션 어피니티를 가지고 있으며 Nginx HTTP 서버와 리버스 프록시 기능을 통해 제공되며, 인그레스 컨트롤러는 노드의 개수만큼 각 노드에 실행합니다.

  • L7 스위치 역할을 논리적으로 수행

  • 클러스터로 접근하는 URL별로 다른 서비스에 트래픽을 분산

  • Kubernetes Ingress의 기능

    • service에 외부 url을 제공
    • 트래픽을 로드밸런싱
    • SSL 인증서 처리
    • 가상 호스팅을 지정

Ingress 컨트롤러 설치

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.3/deploy/static/provider/cloud/deploy.yaml
note
  • 작업 클러스터: k8s
  • ingress-nginx namespace에 nginx 이미지를 app=nginx 레이블을 가지고 실행하는 nginx pod를 구성하세요.
  • 현재 appjs-service와 nginx 서비스는 이미 동작중입니다. 별도 구성이 필요 없습니다.
  • app-ingress.yaml 파일을 생성하고, 다음 조건의 ingress를 구성하세요.
    • name: app-ingress
    • NodePort: 30080
    • / 접속시 nginx 서비스로 연결
    • /app 접속시 appjs-service 서비스로 연결
    • Ingress 구성에 다음 annotations을 포함
      • annotations:
        • kubernetes.io/ingress.class: nginx
# 컨텍스트 스위칭
kubectl config current-context
kubectl config use-context k8s

# 파드 생성
kubectl run nginx --image=nginx --labels=app=nginx -n ingress-nginx --dry-run=client -o yaml
kubectl run nginx --image=nginx --labels=app=nginx -n ingress-nginx

# 파드 생성 확인
kubectl get pods -o wide -n ingress-nginx -L app
...
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES APP
nginx 1/1 Running 0 27s 192.168.104.24 node2 <none> <none> nginx

# 네임스페이스 ingress-nginx에 서비스 nginx 생성
kubectl expose pod nginx -n ingress-nginx --type=ClusterIP --port=80 --target-port=80 --name=nginx --dry-run=client
kubectl expose pod nginx -n ingress-nginx --type=ClusterIP --port=80 --target-port=80 --name=nginx

# 네임스페이스 ingress-nginx에 서비스 appjs-service 생성
kubectl expose pod nginx -n ingress-nginx --type=ClusterIP --port=80 --target-port=80 --name=appjs-service --dry-run=client
kubectl expose pod nginx -n ingress-nginx --type=ClusterIP --port=80 --target-port=80 --name=appjs-service

# 서비스 생성 확인
kubectl get svc -n ingress-nginx
...
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
appjs-service ClusterIP 10.110.181.7 <none> 80/TCP 15s
nginx ClusterIP 10.97.26.66 <none> 80/TCP 54s

# vi app-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: ingress-nginx
name: app-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80

- path: /app
pathType: Prefix
backend:
service:
name: appjs-service
port:
number: 80

# vi node-port-svc.yaml
apiVersion: v1
kind: Service
metadata:
namespace: ingress-nginx
name: nginx-service
labels:
app: nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30080
selector:
app: nginx
type: NodePort

# Ingress 생성
kubectl apply -f app-ingress.yaml
kubectl apply -f node-port-svc.yaml

# Ingress 생성 확인
kubectl get ingress -n ingress-nginx
kubectl describe ingress -n ingress-nginx app-ingress
...
Name: app-ingress
Labels: <none>
Namespace: ingress-nginx
Address:
Ingress Class: <none>
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
*
/ nginx:80 (192.168.104.24:80)
/app appjs-service:80 (192.168.104.24:80)
Annotations: kubernetes.io/ingress.class: nginx
Events: <none>

curl node1:30080/
curl node1:30080/app
TroubleShooting
# 하단 에러 발생시
Error from server (InternalError): error when creating "test.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": context deadline exceeded

# ingress-nginx-admission 삭제
kubectl delete validatingwebhookconfiguration ingress-nginx-admission

Kube-dns

  • 쿠버네티스 클러스터에서 사용하는 DNS

  • 클러스터 내의 모든 서비스는 DNS 네임이 할당된다.

  • svc-name.namespace.svc.cluster.local

  • 클러스터에서 동작되는 모든 Pod의 /etc/resolv.conf에는 kube-dns가 namespace로 정의되어 있다.

    • cat /etc/resolv.conf
    • nameserver 10.32.0.10
    • search <namespace>.svc.cluster.local svc.cluster.local cluster.local
  • 특정 Pod에서 서비스명이나 파드명으로 Access 가능

    • svc-name.namespace.svc.cluster.local
    • pod-ip.namespace.pod.cluster.local

DNS를 통한 서비스 검색

Service 이름을 도메인 주소로도 사용이 가능하며 이는 바로 쿠버네티스에서 제공하는 DNS 서버가 있기 때문이다.

  1. kube-system 네임스페이스 내에 kube-dns라는 파드가 존재한다.
  2. kube-dns 파드는 DNS 서버를 실행하며, 클러스터에서 실행 중인 다른 모든 파드는 자동으로 이를 사용하도록 구성한다 (쿠버네티스는 각 컨테이너의 /etc/resolv.conf 파일을 수정하여 이를 수행한다.)
  3. 파드에서 실행 중인 프로세스에서 수행된 모든 DNS 쿼리는 시스템에서 실행 중인 모든 서비스를 알고 있는 쿠버네티스의 자체 DNS 서버로 처리된다.
서비스 IP에 핑을 할 수 없는 이유

서비스 아이피에 ping을 날리면 아무런 응답이 없는데 이는 서비스의 클러스터 아이피가 가상 아이피이기에 서비스 포트와 결합된 경우에만 의미가 있기 때문입니다.


# 디플로이먼트 생성
kubectl create deployment web --image=nginx --port=80 --replicas=2

# default cluster IP
kubectl expose deployment web --port=80

# 서비스, 디플로이먼트 생성 확인
kubectl get deployments.apps,svc
...
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 2/2 2 2 45s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d20h
service/web ClusterIP 10.96.15.75 <none> 80/TCP 21s


kubectl run dns-test --image=busybox -it --rm -- /bin/sh

cat resolv.conf
...
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local openstacklocal
options ndots:5

# kube dns 서버 확인
kubectl get svc -n kube-system
...
kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 2d20h


nslookup 10.96.0.10
note

image nginx를 사용하는 resolver pod를 생성하고 resolver-service라는 service를 구성합니다. 클러스터 내에서 service와 pod 이름을 조회할 수 있는지 테스트합니다.

  • dns 조회에 사용하는 pod 이미지는 busybox이고, service와 pod 이름 조회는 nslookup을 사용합니다.
  • service 조회 결과는 /var/CKA2022/nginx.svc에 pod name 조회 결과는 /var/CKA2022/nginx.pod 파일에 기록합니다.

# 파드 생성
kubectl run resolver --image=nginx --dry-run=client
kubectl run resolver --image=nginx

# 서비스 구성
kubectl expose pod resolver --port=80 --name=resolver-service

# 파드 생성 확인 / IP 확인
kubectl get pod resolver
kubectl get pod resolver -o wide

# 서비스 생성 확인 / IP 기록
kubectl get svc resolver-service

# test 파드 생성 및 접속
kubectl run test --image=busybox -it --rm -- /bin/sh

# sudo vi /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

nslookup [clusterip or 서비스명] > /var/CKA2022/nginx.svc
nslookup [pod-ip(10.x.x.x)].default.pod.cluster.local > /var/CKA2022/nginx.pod

# 서비스가 서로 다른 네임스페이스인 경우
<service-name>.<service-namespace>
<service-name>.<service-namespace>.svc
<service-name>.<service-namespace>.svc.cluster.local

서비스 및 파드용 DNS

쿠버네티스는 파드와 서비스를 위한 DNS 레코드를 생성한다. 사용자는 IP 주소 대신에 일관된 DNS 네임을 통해서 서비스에 접속할 수 있다.

소개

쿠버네티스 DNS는 클러스터의 서비스와 DNS 파드를 관리하며, 개별 컨테이너들이 DNS 네임을 해석할 때 DNS 서비스의 IP를 사용하도록 kubelets를 구성한다.

클러스터 내의 모든 서비스(DNS 서버 자신도 포함하여)에는 DNS 네임이 할당된다. 기본적으로 클라이언트 파드의 DNS 검색 리스트는 파드 자체의 네임스페이스와 클러스터의 기본 도메인을 포함한다.

서비스의 네임스페이스

DNS 쿼리는 그것을 생성하는 파드의 네임스페이스에 따라 다른 결과를 반환할 수 있다. 네임스페이스를 지정하지 않은 DNS 쿼리는 파드의 네임스페이스에 국한된다. DNS 쿼리에 네임스페이스를 명시하여 다른 네임스페이스에 있는 서비스에 접속한다.

예를 들어, test 네임스페이스에 있는 파드를 생각해보자. data 서비스는 prod 네임스페이스에 있다.

이 경우, data에 대한 쿼리는 파드의 test 네임스페이스를 사용하기 때문에 결과를 반환하지 않을 것이다.

data.prod로 쿼리하면 의도한 결과를 반환할 것이다. 왜냐하면 네임스페이스가 명시되어 있기 때문이다.

DNS 쿼리는 파드의 /etc/resolv.conf를 사용하여 확장될 수 있다. Kubelet은 각 파드에 대해서 파일을 설정한다. 예를 들어 data 만을 위한 쿼리는 data.test.svc.cluster.local로 확장된다. search 옵션의 값은 쿼리를 확장하기 위해서 사용된다.

nameserver 10.96.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

요약하면, test 네임스페이스에 있는 파드는 data.prot 또는 data.prod.svc.cluster.local 중 하나를 통해서 성공적으로 해석될 수 있다.

DNS 레코드

어떤 오브젝트가 DNS 레코드를 가지는가 ?

  1. 서비스
  2. 파드

서비스

A/AAAA 레코드

"노멀"(헤드리스가 아닌) 서비스는 서비스 IP 계열에 따라 my-svc.my-namesapce.svc.cluster-domain.example 형식의 이름을 가진 DNS A 또는 AAAA 레코드가 할당된다. 이는 서비스의 클러스터 IP로 해석된다.

"헤드리스"(클러스터 IP가 없는) 서비스 또한 서비스 IP 계열에 따라 my-svc.my-namespace.svc.cluster-domain.example 형식의 이름을 가진 DNS A 또는 AAAA 레코드가 할당된다. 노멀 서비스와는 다르게 이는 서비스에 의해 선택된 파드들의 IP 집합으로 해석된다. 클라이언트는 해석된 IP 집합에서 IP를 직접 선택하거나 표준 라운드로빈을 통해 선택할 수 있다.

파드

A/AAAA 레코드

일반적으로 파드에는 다음과 같은 DNS 주소를 갖는다. pod-ip-address.my-namepsace.pod.cluster-domain.example 예를 들어, default 네임스페이스의 파드에 IP 주소 172.17.0.3이 있고, 클러스터의 도메인 이름이 cluster.local이면, 파드는 다음과 같은 DNS 주소를 갖는다.

172-17-0-3.default.pod.cluster.local 서비스에 의해 노출된 모든 파드는 다음과 같은 DNS 주소를 갖는다. pod-ip-address.service-name.my-namespace.svc.cluster-domain.example

파드의 hostname 및 subdomain 필드

파드가 생성되면 hostname은 해당 파드의 metadata.name 값이 된다.

파드 스펙(Pod spec)에는 선택적 필드인 hostname이 있다. 이 필드는 파드의 호스트 네임을 지정할 수 있다. hostname 필드가 지정되면, 파드의 이름보다 파드의 호스트네임이 우선시된다. 예를 들어 hostname 필드가 my-host로 설정된 파드는 호스트네임이 my-host로 설정된다.

또한 파드 스펙에는 선택적 필드인 subdomain이 있다. 이 필드는 서브도메인을 지정할 수 있다. 예를 들어 my-namespace 네임스페이스에서 hostname 필드가 foo로 설정되고 subdomain 필드가 bar로 설정된 파드는 전체 주소 도메인 네임(FQDN)을 가지게 된다. foo.bar-my-namespace.svc.cluster-domain.example

apiVersion: v1
kind: Service
metadata:
name: default-subdomain
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: foo
port: 1234
targetPort: 1234

---
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox
spec:
hostname: busybox-1
subdomain: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox

---
apiVersion: v1
kind: Pod
metadata:
name: busybox2
labels:
name: buxybox
spec:
hostname: busybox-2
subdomain: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox

파드와 동일한 네임스페이스 내에 같은 서브도메인 이름을 가진 헤드리스 서비스가 있다면, 클러스터의 DNS 서버는 파드의 전체 주소 호스트네임인 A 또는 AAAA 레코드를 반환한다. 예를 들어 호스트네임이 "busybox-1"이고 서브도메인이 "default-subdomain"이고, 같은 네임스페이스 내 헤드리스 서비스의 이름이 "default-subdomain"이면, 파드는 다음과 같이 자기 자신의 FQDN을 얻게 된다. "busybox-1.default-subdomain.my-namespace.svc.cluster-domain.example" DNS는 위 FQDN에 대해 파드의 IP를 가리키는 A 또는 AAAA 레코드를 제공한다. "busybox1"와 "busybox2" 파드 모두 각 파드를 구분 가능한 A 또는 AAAA레코드를 가지고 있다.