( 참고 : [교재] 핵심만 콕! 쿠버네티스 )

쿠버네티스 네트워킹

Service 리소스 = 쿠버네티스의 네트워크 담당

  • Pod와 마찬가지로 YAML 형식으로 정의
  • Pod IP와는 다른 “독자적인 IP” 부여 받아 서비스의 Endpoint 제공
  • 라벨링 시스템으로 Pod로 트래픽 전달


1. Service 소개

Pod 자체에도 IP가 있는데…굳이 왜 Service 통해서?

kubectl run mynginx --image nginx

kubectl get pod -o wide
# ....... 10.42.0.26

# 이렇게 접속해도 되는데...굳이?
kubectl exec mynginx -- curl -s 10.42.0.26


1-1. 불안정한 Pod vs 안정적인 Service

Pod = 불안정한 자원

  • 쉽게 생성 가능 & 사용 끝나면 쉽게 삭제

  • 무슨 이유로든, 종료될 수 있는 리소스로 생각

    ( = 불안정한 Endpoint 제공 )

  • Pod의 IP로 서비스 호출하면…..계속해서 바뀐 IP 추적해야!

\(\rightarrow\) 따라서, 안정적인 서비스 endpoint를 제공하는 “Service”


Service 리소스 덕분에, 사용자는 계속 동일한 IP로 접근 가능!


1-2. 서비스 탐색 (Service Discovery)

안정적인 IP제공 뿐만 아니라, “서비스 탐색 기능”도 제공

\(\rightarrow\) “이름을 기반으로 DNS 참조 가능”

  • ex) myservice라는 Service 리소스 생성하면, 사용자는 myservice라는 도메인 주소로 요청 가능!


1-3. Service 첫 만남

Service 리소스 생성하기 : myservice.yaml

# myservice.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    hello: world
  name: myservice
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 80
  selector:
    run: mynginx
  • kind : Service 리소스 선언
  • metadata
    • labels : 라벨 부여
    • name : 이름 지정 ( 도메인 주소로 활용 )
  • spec
    • ports : service의 포트들 관련 정보
      • port : 포트 지정
      • protocol : 프로토콜 지정 ( ex. TCP, UDP, HTTP )
      • targetPort : 트래픽을 전달할 컨테이너 포트 지정
    • selector : 트래픽을 전달할 컨테이너의 라벨
      • 여기서는 mynginx라벨을 가짐


라벨 셀렉터를 사용하여 Pod 선택하는 이유

이유 : Loosely Coupled (느슨한 연결)

  • Pod IP = STRICT

    • 특정 리소스를 직접 참조
  • Label Selector = LOOSE

    • 특정 리소스를 간접 참조

    • 매번 새로운 Pod 정보를, Service에 등록/삭제할 필요가 없음

      ( just 라벨 달기만하면 끝! )


Service 리소스 생성, 조회, Pod IP 확인

kubectl apply -f myservice.yaml

kubectl get service # svc
# NAME, TYPE, CLUSTER-IP, EXTERNEL-IP, PORT(S), AGE
# myservice의 ip는 10.43.152.73:8080

kubectl get pod -o wide
# mynginx pod의 ip는 10.42.0.226
  • myservice의 Service IP를 확인해보면,

    mynginx Pod IP와 다른 고유의 IP를 가진 것을 알 수 있음


curl 요청할 client pod 생성 후, 3가지 방식으로 트래픽 전달하기

# client Pod 생성
kubectl run client --image nginx

# 방법 1) Pod IP 통해
kubectl exec client -- curl 10.42.0.226

# 방법 2) Service IP 통해
kubectl exec client -- curl 10.43.152.73:8080

# 방법 3) Service 이름 통해
kubectl exec client -- curl myservice:8080


myservice라는 이름을 가진 서비스의 IP주소를 확인하기 위해서는?

( 이미 10.43.152.73인걸 알지만, 모른다고 했을 때 )

# nslookup 명령 설치
kubectl exec client -- sh -c "apt update && apt install -y dnsutils"

# myservice의 DNS 조회
kubectl exec client -- nslookup myservice
  • service의 이름이 도메인 주소의 역할을 한다
  • DNS의 이름 :
    • 단순히 myservice (X)
    • myservice.default.svc.cluster.local


1-4. Service 도메인 주소 법칙

법칙 : <서비스 이름>.<네임스페이스>.svc.cluster.local


# 1) Service의 "전체" 도메인 주소 조회
kubectl exec client -- nslookup myservice.default.svc.cluster.local

# 2) Service의 "일부" 도메인 주소 조회
kubectl exec client -- nslookup myservice.default
 
# 3) Service츼 이름으로 조회
kubectl exec client -- nslookup myservice


1-5. 클러스터 DNS 서버

위처럼, “도메인 주소” 사용 가능한 이유는?

\(\rightarrow\) 쿠버네티스에서 제공하는 “DNS 서버” 덕분에

( 확인 in /etc/resolv/conf 파일 )


kubectl exec client -- cat /etc/resolv.conf
# nameserver 10.43.0.10
  • 쿠버네티스의 모든 Pod는 이 “10.43.0.10” IP를 통해 DNS를 조회 한다


해당 IP의 주인은? ( kube-system 에서 확인)

kubectl get svc -n kube-system
# NAME TYPE CLUSTER-IP EXTERNEL-IP PORT(S)
# kube-dns ClusterIP 10.43.0.10 <none> ...
  • 주인은 바로 “kube-dns”


어떠한 pod들이 매핑되는지?

kubectl get pod -n kube-system -l k8s-app=kube-cns
# NAME READY STATUS RESTARTS AGE
# coredns-6c6bb68 1/1 Running 0 46h
  • coredns-xxx라는 pod가 조회됨

    \(\rightarrow\) 클러스터 DNS 서버

  • 모든 Pod들은, 내/외부 DNS질의를 바로 이 “coredns”를 통해 수행함!

    ( = 쿠버네티스의 자체적인 DNS 시스템 )


2. Service의 종류

4종류

  • 1) ClusterIP
  • 2) NodePort
  • 3) LoadBalancer
  • 4) ExternalName


2-1. ClusterIP

간단 소개

  • 가장 기본이 되는 타입 ( default 값 )
  • cluster “내부”에서만 접근 가능


(외부에서 접근 못하는) ClusterIP가 필요한 이유?

  • 1) 네트워크 보안 및 관리
  • 2) 더 확장된 쿠버네티스 네트워킹을 위한 기본 빌딩 블록


ClusterIP 타입의 Service 생성하는 파일 ( cluster-ip.yaml )

  • ( + 이에 대응하는 Pod도 생성)
# cluster-ip.yaml
apiVersion: v1
kind: Service
metadata:
  name: cluster-ip
spec:
  # type: ClusterIP # 생략가능 (default)
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 80
  selector:
    run: cluster-ip
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: cluster-ip
  name: cluster-ip
spec:
  containers:
  - image: nginx
    name: nginx
    ports:
    - containerPort: 80


kubectl run cluster-ip --image nginx --expose --port 80 \
	--dry-run=client -o yaml > cluster-ip.yaml


pod & service 생성한 뒤, 조회

# pod & service 둘 다 생성됨
kubectl apply -f cluster-ip.yaml

# 조회
kubectl get svc cluster-ip -o yaml | grep type
# type : ClusterIP

# (clusterIP 타입의) Service를 통해 접속
kubectl exec client -- curl -s cluster-ip


2-2. NodePort

  • ClusterIP : 외부 X
  • NodePort : 외부 O


localhost의 특정 포트를 service의 특정포트와 연결시켜서, “외부 트래픽을 service까지 전달”


# node-port.yaml
apiVersion: v1
kind: Service
metadata:
  name: node-port
spec:
  type: NodePort 
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 80
    nodePort: 30080 
  selector:
    run: node-port
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: node-port
  name: node-port
spec:
  containers:
  - image: nginx
    name: nginx
    ports:
    - containerPort: 80
  • type : Service의 타입을 지정
  • nodePort : 호스트 서버(노드)에서 사용할 포트 번호
    • 범위 : 30000~32767


kubectl apply -f node-port.yaml

kubectl get svc
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# node-port NodePort 10.43.94.27 <none> 8080:30080/TCP 42h
  • Port 매핑
    • service port : 8080
    • node port : 30080


마스터, 워커 node 모두가 “동일한 NodePort”로 서비스에 접근

kubectl get pod node-port -o wide

MASTER_IP = $$(kubectl get node master -o jsonpath="{.status.addresses[0].address}")
WORKER_IP = $$(kubectl get node worker -o jsonpath="{.status.addresses[0].address}")

# 접속 with MASTER
curl $$MASTER_IP:30080

# 접속 with NODE
curl $$WORKER_IP:30080


2-3. LoadBalancer

  • NodePort : 외부 O
  • LoadBalancer : 외부 O


각 node로 트래픽을 분산

public cloud (AWS,GCP,Azure 등) 플랫폼에서 제공하는 로드밸런서를 service 리소스에 연결할 수 있음

( 일반적으로, cloud 플랫폼에서 제공하는 경우에 사용 )


but, NodePort 말고 LoadBalancer를 쓰는 이유는?

  • 1) 보안적인 측면

    • 노드포트 대역 (30000~32767)을 외부에 노출할 필요 X
  • 2) 로드밸런서가 cluster 앞단에 존재하면, 사용자가 각각의 서버 IP를 직접 알 필요 X

    ( 로드밸런서의 IP or 도메인주소 만으로도 요청 가능 )


ClusterIP : “Pod” 레벨에서의 안정적인 서비스 endpoint 제공

Loadbalancer : “Node” 레벨에서의 안정적인 서비스 endpoint 제공


LoadBalancer 생성 & 조회

kubectl apply -f load-bal.yaml

kubectl get svc load-bal
# NAME		TYPE		CLUSTER-IP		EXTERNAL-IP	PORT(S)
# load-bal  LoadBalancer 10.43.230.45 	10.0.1.1 	8080:30088/TCP
  • 기존과는 다르게, ‘EXTERNAL-IP’에 IP가 생성된 것을 확인 가능
  • 각 cloud 플랫폼에서 제공하는 LoadBalancer의 IP & DNS 확인 가능


Loadbalancer의 endpoint에 연결

  • 로드밸런서 IP + 서비스 port
curl 10.0.1.1:8080


2-4. ExternalName

외부 DNS 주소에 (클러스터 내부에서 사용 할)”별칭” 부여

  • ex) ‘google-svc’라는 별칭으로 google.com 연결


external.yaml

# external.yaml
apiVersion: v1
kind: Service
metadata:
  name: google-svc  # 별칭
spec:
  type: ExternalName
  externalName: google.com  # 외부 DNS


ExternalName 리소스 생성 및 조회

kubectl apply -f external.yaml

kubectl run call-google --image curlimages/culr \ -- curl -s -H "Host:google.com" google-svc

kubectl logs call-google


언제사용?

  • cluster에 편입되지 않는 “외부 서비스”에 쿠버네티스 네트워킹 기능을 연결하고 싶은 경우


3. 네트워크 모델

쿠버네티스 : “NAT를 통한 네트워킹 싫어함”

NAT 통신을 이용한 container간 통신?

\(\rightarrow\) 모든 container이 동일한 IP & 포트로만 구분

\(\rightarrow\) 포트 충돌 가능성! 클러스터 관리자의 중재가 필요


이를 극복하기 위해,

container의 네트워크 환경을 node 레벨의 네트워크 환경과 분리하여 고립도를 높임