๐Ÿš€ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ์„œ๋น„์Šค ํƒ€์ž…๋ณ„ ์‹ค์Šต ๊ฐ€์ด๋“œ

๐Ÿ“‹ ์„œ๋น„์Šค ํƒ€์ž… ๊ฐœ๋… ์ •๋ฆฌ

๐Ÿ”— ExternalName ์„œ๋น„์Šค

์™ธ๋ถ€ ์„œ๋น„์Šค์— ๋Œ€ํ•œ **DNS ๋ณ„์นญ(CNAME)**์„ ์ œ๊ณตํ•˜๋Š” ์„œ๋น„์Šค

  • IP ์ฃผ์†Œ๊ฐ€ ์•„๋‹Œ DNS ์ด๋ฆ„์œผ๋กœ ์™ธ๋ถ€ ์„œ๋น„์Šค์— ์—ฐ๊ฒฐ
  • kube-proxy์— ์˜ํ•œ ํ”„๋ก์‹œ ์—†์Œ
  • ์ฃผ๋กœ ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, API ์„œ๋น„์Šค ์—ฐ๊ฒฐ์— ์‚ฌ์šฉ

๐Ÿ  ClusterIP ์„œ๋น„์Šค (๊ธฐ๋ณธ๊ฐ’)

ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๊ฐ€์ƒ IP๋ฅผ ์ œ๊ณต

  • ๋‚ด๋ถ€ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ
  • ํŒŒ๋“œ ๊ฐ„ ํ†ต์‹ ์˜ ์•ˆ์ •์ ์ธ ์—”๋“œํฌ์ธํŠธ ์ œ๊ณต
  • ์™ธ๋ถ€์—์„œ๋Š” ์ ‘๊ทผ ๋ถˆ๊ฐ€

๐Ÿ‘ป Headless ์„œ๋น„์Šค

ClusterIP๊ฐ€ None์ธ ์„œ๋น„์Šค (IP ํ• ๋‹น ์—†์Œ)

  • DNS๋ฅผ ํ†ตํ•ด ๊ฐœ๋ณ„ ํŒŒ๋“œ IP ์ง์ ‘ ๋ฐ˜ํ™˜
  • StatefulSet๊ณผ ํ•จ๊ป˜ ์ฃผ๋กœ ์‚ฌ์šฉ
  • ํŒŒ๋“œ ๊ฐ„ ์ง์ ‘ ํ†ต์‹ ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ํ™œ์šฉ

๐ŸŒ 1. ExternalName ์„œ๋น„์Šค ์‹ค์Šต

๐Ÿ“ ์‹ค์Šต ์‹œ๋‚˜๋ฆฌ์˜ค

์™ธ๋ถ€ MySQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(db.example.com)์— ๋Œ€ํ•œ ๋‚ด๋ถ€ ๋ณ„์นญ(mysql-external) ์ƒ์„ฑ

๐Ÿ› ๏ธ Step 1: ExternalName ์„œ๋น„์Šค ์ƒ์„ฑ

# external-mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql-external
  namespace: default
spec:
  type: ExternalName
  externalName: db.example.com
  ports:
  - port: 3306
    targetPort: 3306
    protocol: TCP
# ์„œ๋น„์Šค ์ƒ์„ฑ
kubectl apply -f external-mysql-service.yaml
# service/mysql-external created
 
# ์„œ๋น„์Šค ํ™•์ธ
kubectl get svc mysql-external
# NAME             TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)    AGE
# mysql-external   ExternalName   <none>       db.example.com  3306/TCP   5s
 
kubectl describe svc mysql-external
# Name:              mysql-external
# Namespace:         default
# Labels:            <none>
# Annotations:       <none>
# Selector:          <none>
# Type:              ExternalName
# IP Family Policy:  SingleStack
# IP Families:       IPv4
# External Name:     db.example.com
# Port:              <unset>  3306/TCP
# TargetPort:        3306/TCP
# Endpoints:         <none>
# Session Affinity:  None
# Events:            <none>

๐Ÿงช Step 2: DNS ํ•ด์„ ํ…Œ์ŠคํŠธ

# ํ…Œ์ŠคํŠธ์šฉ ํŒŒ๋“œ ์ƒ์„ฑ
kubectl run test-pod --image=busybox --restart=Never -- sleep 3600
# pod/test-pod created
 
# ํŒŒ๋“œ ๋‚ด๋ถ€์—์„œ DNS ์กฐํšŒ ํ…Œ์ŠคํŠธ
kubectl exec -it test-pod -- nslookup mysql-external
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# mysql-external.default.svc.cluster.local	canonical name = db.example.com
# Name:	db.example.com
# Address: 93.184.216.34
 
kubectl exec -it test-pod -- nslookup mysql-external.default.svc.cluster.local
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# mysql-external.default.svc.cluster.local	canonical name = db.example.com
# Name:	db.example.com
# Address: 93.184.216.34
 
# ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ (ํ…”๋„ท)
kubectl exec -it test-pod -- telnet mysql-external 3306
# telnet: can't connect to remote host (93.184.216.34): Connection refused
# (์‹ค์ œ ์™ธ๋ถ€ ์„œ๋น„์Šค๊ฐ€ ์—†์–ด์„œ ์—ฐ๊ฒฐ ์‹คํŒจ - ์ •์ƒ)

๐Ÿ“‹ Step 3: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉ

# app-with-external-db.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: nginx
        env:
        - name: DB_HOST
          value: "mysql-external"  # ExternalName ์„œ๋น„์Šค ์‚ฌ์šฉ
        - name: DB_PORT
          value: "3306"
        ports:
        - containerPort: 80

๐Ÿ  2. ClusterIP ์„œ๋น„์Šค ์‹ค์Šต

๐Ÿ“ ์‹ค์Šต ์‹œ๋‚˜๋ฆฌ์˜ค

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ๋‚ด๋ถ€ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ๊ตฌ์„ฑ

๐Ÿ› ๏ธ Step 1: ๋ฐฑ์—”๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ

# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-app
  labels:
    app: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: nginx:alpine
        ports:
        - containerPort: 80
        env:
        - name: SERVER_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
# ๋ฐฐํฌ ์‹คํ–‰
kubectl apply -f backend-deployment.yaml
# deployment.apps/backend-app created
 
# ํŒŒ๋“œ ์ƒํƒœ ํ™•์ธ
kubectl get pods -l app=backend
# NAME                           READY   STATUS    RESTARTS   AGE
# backend-app-7d4b8c5f47-2j9kl   1/1     Running   0          30s
# backend-app-7d4b8c5f47-8r5m2   1/1     Running   0          30s
# backend-app-7d4b8c5f47-xp3qw   1/1     Running   0          30s
 
kubectl get pods -o wide -l app=backend
# NAME                           READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
# backend-app-7d4b8c5f47-2j9kl   1/1     Running   0          45s   10.244.1.5   worker-1   <none>           <none>
# backend-app-7d4b8c5f47-8r5m2   1/1     Running   0          45s   10.244.2.3   worker-2   <none>           <none>
# backend-app-7d4b8c5f47-xp3qw   1/1     Running   0          45s   10.244.1.6   worker-1   <none>           <none>

๐Ÿ”— Step 2: ClusterIP ์„œ๋น„์Šค ์ƒ์„ฑ

# backend-clusterip-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  type: ClusterIP  # ๊ธฐ๋ณธ๊ฐ’์ด๋ฏ€๋กœ ์ƒ๋žต ๊ฐ€๋Šฅ
  selector:
    app: backend
  ports:
  - name: http
    port: 8080        # ์„œ๋น„์Šค ํฌํŠธ
    targetPort: 80    # ์ปจํ…Œ์ด๋„ˆ ํฌํŠธ
    protocol: TCP
# ์„œ๋น„์Šค ์ƒ์„ฑ
kubectl apply -f backend-clusterip-service.yaml
# service/backend-service created
 
# ์„œ๋น„์Šค ํ™•์ธ
kubectl get svc backend-service
# NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
# backend-service   ClusterIP   10.96.185.123   <none>        8080/TCP   15s
 
kubectl describe svc backend-service
# Name:              backend-service
# Namespace:         default
# Labels:            <none>
# Annotations:       <none>
# Selector:          app=backend
# Type:              ClusterIP
# IP Family Policy:  SingleStack
# IP Families:       IPv4
# IP:                10.96.185.123
# IPs:               10.96.185.123
# Port:              http  8080/TCP
# TargetPort:        80/TCP
# Endpoints:         10.244.1.5:80,10.244.1.6:80,10.244.2.3:80
# Session Affinity:  None
# Events:            <none>
 
# ์—”๋“œํฌ์ธํŠธ ํ™•์ธ
kubectl get endpoints backend-service
# NAME              ENDPOINTS                                   AGE
# backend-service   10.244.1.5:80,10.244.1.6:80,10.244.2.3:80   45s

๐Ÿงช Step 3: ์„œ๋น„์Šค ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ

# ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€์—์„œ ํ…Œ์ŠคํŠธ
kubectl run test-client --image=curlimages/curl --restart=Never -it --rm -- sh
# If you don't see a command prompt, try pressing enter.
/ $ 
 
# ํŒŒ๋“œ ๋‚ด๋ถ€์—์„œ ์„œ๋น„์Šค ํ˜ธ์ถœ
curl backend-service:8080
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# ...
 
curl backend-service.default.svc.cluster.local:8080
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# ...
 
# ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜์—ฌ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ ํ™•์ธ
for i in {1..5}; do curl -s backend-service:8080 | grep -i server; done
# <address>nginx/1.21.5 (backend-app-7d4b8c5f47-2j9kl)</address>
# <address>nginx/1.21.5 (backend-app-7d4b8c5f47-xp3qw)</address>
# <address>nginx/1.21.5 (backend-app-7d4b8c5f47-8r5m2)</address>
# <address>nginx/1.21.5 (backend-app-7d4b8c5f47-2j9kl)</address>
# <address>nginx/1.21.5 (backend-app-7d4b8c5f47-xp3qw)</address>
# (๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์œผ๋กœ ์š”์ฒญ์ด ๋‹ค๋ฅธ ํŒŒ๋“œ๋กœ ๋ถ„์‚ฐ๋˜๋Š” ๊ฒƒ ํ™•์ธ)

๐Ÿ“Š Step 4: ํฌํŠธ ํฌ์›Œ๋”ฉ์œผ๋กœ ์™ธ๋ถ€ ์ ‘๊ทผ

# ๋กœ์ปฌ์—์„œ ์„œ๋น„์Šค์— ์ ‘๊ทผ
kubectl port-forward svc/backend-service 8080:8080
# Forwarding from 127.0.0.1:8080 -> 80
# Forwarding from [::1]:8080 -> 80
# Handling connection for 8080
 
# ๋‹ค๋ฅธ ํ„ฐ๋ฏธ๋„์—์„œ ํ…Œ์ŠคํŠธ
curl localhost:8080
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>
# html { color-scheme: light dark; }
# body { width: 35em; margin: 0 auto;
# font-family: Tahoma, Verdana, Arial, sans-serif; }
# </style>
# </head>
# <body>
# <h1>Welcome to nginx!</h1>
# <p>If you can see this page, the nginx web server is successfully installed and working...</p>
# </body>
# </html>

๐Ÿ‘ป 3. Headless ์„œ๋น„์Šค ์‹ค์Šต

๐Ÿ“ ์‹ค์Šต ์‹œ๋‚˜๋ฆฌ์˜ค

StatefulSet์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํด๋Ÿฌ์Šคํ„ฐ ๊ตฌ์„ฑ

๐Ÿ› ๏ธ Step 1: Headless ์„œ๋น„์Šค ์ƒ์„ฑ

# mongodb-headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mongodb-headless
  labels:
    app: mongodb
spec:
  clusterIP: None  # Headless ์„œ๋น„์Šค์˜ ํ•ต์‹ฌ
  selector:
    app: mongodb
  ports:
  - name: mongodb
    port: 27017
    targetPort: 27017
# Headless ์„œ๋น„์Šค ์ƒ์„ฑ
kubectl apply -f mongodb-headless-service.yaml
# service/mongodb-headless created
 
# ์„œ๋น„์Šค ํ™•์ธ (CLUSTER-IP๊ฐ€ None์ธ์ง€ ํ™•์ธ)
kubectl get svc mongodb-headless
# NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)     AGE
# mongodb-headless   ClusterIP   None         <none>        27017/TCP   10s
 
kubectl describe svc mongodb-headless
# Name:              mongodb-headless
# Namespace:         default
# Labels:            app=mongodb
# Annotations:       <none>
# Selector:          app=mongodb
# Type:              ClusterIP
# IP Family Policy:  SingleStack
# IP Families:       IPv4
# IP:                None
# IPs:               None
# Port:              mongodb  27017/TCP
# TargetPort:        27017/TCP
# Endpoints:         <none>
# Session Affinity:  None
# Events:            <none>

๐Ÿ—„๏ธ Step 2: StatefulSet ๋ฐฐํฌ

# mongodb-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongodb
spec:
  serviceName: mongodb-headless  # Headless ์„œ๋น„์Šค์™€ ์—ฐ๊ฒฐ
  replicas: 3
  selector:
    matchLabels:
      app: mongodb
  template:
    metadata:
      labels:
        app: mongodb
    spec:
      containers:
      - name: mongodb
        image: mongo:5.0
        ports:
        - containerPort: 27017
        env:
        - name: MONGO_INITDB_ROOT_USERNAME
          value: "admin"
        - name: MONGO_INITDB_ROOT_PASSWORD
          value: "password"
        volumeMounts:
        - name: mongodb-storage
          mountPath: /data/db
  volumeClaimTemplates:
  - metadata:
      name: mongodb-storage
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi
# StatefulSet ๋ฐฐํฌ
kubectl apply -f mongodb-statefulset.yaml
# statefulset.apps/mongodb created
 
# ํŒŒ๋“œ ์ƒํƒœ ํ™•์ธ
kubectl get pods -l app=mongodb
# NAME        READY   STATUS    RESTARTS   AGE
# mongodb-0   1/1     Running   0          2m15s
# mongodb-1   1/1     Running   0          1m45s
# mongodb-2   1/1     Running   0          1m15s
 
kubectl get statefulset mongodb
# NAME      READY   AGE
# mongodb   3/3     2m30s
 
# StatefulSet ์ƒ์„ธ ์ •๋ณด
kubectl get pods -l app=mongodb -o wide
# NAME        READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
# mongodb-0   1/1     Running   0          3m      10.244.1.7   worker-1   <none>           <none>
# mongodb-1   1/1     Running   0          2m30s   10.244.2.4   worker-2   <none>           <none>
# mongodb-2   1/1     Running   0          2m      10.244.1.8   worker-1   <none>           <none>

๐Ÿ” Step 3: DNS ํ•ด์„ ํ…Œ์ŠคํŠธ

# ๊ฐ ํŒŒ๋“œ์˜ FQDN ํ™•์ธ
kubectl get pods -l app=mongodb -o wide
# NAME        READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
# mongodb-0   1/1     Running   0          5m      10.244.1.7   worker-1   <none>           <none>
# mongodb-1   1/1     Running   0          4m30s   10.244.2.4   worker-2   <none>           <none>
# mongodb-2   1/1     Running   0          4m      10.244.1.8   worker-1   <none>           <none>
 
# DNS ํ…Œ์ŠคํŠธ์šฉ ํŒŒ๋“œ ์ƒ์„ฑ
kubectl run dns-test --image=busybox --restart=Never -- sleep 3600
# pod/dns-test created
 
# Headless ์„œ๋น„์Šค DNS ์กฐํšŒ (๋ชจ๋“  ํŒŒ๋“œ IP ๋ฐ˜ํ™˜)
kubectl exec -it dns-test -- nslookup mongodb-headless
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.7
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.2.4
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.8
 
# ๊ฐœ๋ณ„ ํŒŒ๋“œ DNS ์กฐํšŒ
kubectl exec -it dns-test -- nslookup mongodb-0.mongodb-headless
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# Name:	mongodb-0.mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.7
 
kubectl exec -it dns-test -- nslookup mongodb-1.mongodb-headless
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# Name:	mongodb-1.mongodb-headless.default.svc.cluster.local
# Address: 10.244.2.4
 
kubectl exec -it dns-test -- nslookup mongodb-2.mongodb-headless
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# Name:	mongodb-2.mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.8
 
# FQDN์œผ๋กœ ๋ชจ๋“  ํŒŒ๋“œ IP ํ™•์ธ
kubectl exec -it dns-test -- nslookup mongodb-headless.default.svc.cluster.local
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.7
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.2.4
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.8

๐Ÿ“Š Step 4: ๊ฐœ๋ณ„ ํŒŒ๋“œ ์ง์ ‘ ์ ‘๊ทผ ํ…Œ์ŠคํŠธ

# MongoDB ํด๋ผ์ด์–ธํŠธ๋กœ ๊ฐœ๋ณ„ ๋…ธ๋“œ ์ ‘๊ทผ
kubectl run mongo-client --image=mongo:5.0 --restart=Never -it --rm -- bash
# If you don't see a command prompt, try pressing enter.
# root@mongo-client:/# 
 
# ๊ฐ MongoDB ์ธ์Šคํ„ด์Šค์— ๊ฐœ๋ณ„ ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ
mongo mongodb://admin:password@mongodb-0.mongodb-headless:27017
# MongoDB shell version v5.0.15
# connecting to: mongodb://admin:password@mongodb-0.mongodb-headless:27017/
# Implicit session: session { "id" : UUID("...") }
# MongoDB server version: 5.0.15
# > 
 
# ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ (ping)
kubectl exec -it dns-test -- ping mongodb-0.mongodb-headless
# PING mongodb-0.mongodb-headless.default.svc.cluster.local (10.244.1.7): 56 data bytes
# 64 bytes from 10.244.1.7: seq=0 ttl=62 time=0.123 ms
# 64 bytes from 10.244.1.7: seq=1 ttl=62 time=0.089 ms
 
kubectl exec -it dns-test -- ping mongodb-1.mongodb-headless  
# PING mongodb-1.mongodb-headless.default.svc.cluster.local (10.244.2.4): 56 data bytes
# 64 bytes from 10.244.2.4: seq=0 ttl=62 time=0.156 ms
# 64 bytes from 10.244.2.4: seq=1 ttl=62 time=0.102 ms
 
# MongoDB ํฌํŠธ ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ
kubectl exec -it dns-test -- telnet mongodb-0.mongodb-headless 27017
# Connected to mongodb-0.mongodb-headless.default.svc.cluster.local
# (์—ฐ๊ฒฐ ์„ฑ๊ณต ํ™•์ธ)

๐Ÿ”ง Step 5: ์ผ๋ฐ˜ ClusterIP์™€ ๋น„๊ต ์‹ค์Šต

# mongodb-clusterip-service.yaml (๋น„๊ต์šฉ)
apiVersion: v1
kind: Service
metadata:
  name: mongodb-clusterip
spec:
  type: ClusterIP
  selector:
    app: mongodb
  ports:
  - name: mongodb
    port: 27017
    targetPort: 27017
# ClusterIP ์„œ๋น„์Šค ์ƒ์„ฑ
kubectl apply -f mongodb-clusterip-service.yaml
# service/mongodb-clusterip created
 
# ClusterIP ์„œ๋น„์Šค ํ™•์ธ
kubectl get svc mongodb-clusterip
# NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
# mongodb-clusterip   ClusterIP   10.96.243.158   <none>        27017/TCP   15s
 
# DNS ๋น„๊ต ํ…Œ์ŠคํŠธ
kubectl exec -it dns-test -- nslookup mongodb-clusterip
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# Name:	mongodb-clusterip.default.svc.cluster.local
# Address: 10.96.243.158
# (๋‹จ์ผ ๊ฐ€์ƒ IP ๋ฐ˜ํ™˜)
 
kubectl exec -it dns-test -- nslookup mongodb-headless
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.7
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.2.4
# Name:	mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.8
# (๋ชจ๋“  ํŒŒ๋“œ IP ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜)
 
# ๐ŸŽฏ ํ•ต์‹ฌ ์ฐจ์ด์  ํ™•์ธ:
# โœ… ClusterIP: ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ์—ญํ• ์˜ ๋‹จ์ผ ๊ฐ€์ƒ IP (10.96.243.158)
# โœ… Headless: DNS๋ฅผ ํ†ตํ•œ ๊ฐœ๋ณ„ ํŒŒ๋“œ IP ์ง์ ‘ ๋ฐ˜ํ™˜ (10.244.1.7, 10.244.2.4, 10.244.1.8)

๐Ÿ”ง 4. ํ†ตํ•ฉ ์‹ค์Šต ์‹œ๋‚˜๋ฆฌ์˜ค ๋ฐ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

๐ŸŽฏ ์ข…ํ•ฉ ์‹ค์Šต: 3-Tier ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์„ฑ

# complete-3tier-app.yaml
---
# Frontend (LoadBalancer/NodePort)
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: LoadBalancer
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nginx:alpine
        env:
        - name: BACKEND_URL
          value: "http://backend-service:8080"
---
# Backend (ClusterIP)
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
  - port: 8080
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: httpd:alpine
        env:
        - name: DB_HOST
          value: "postgres-headless"
        ports:
        - containerPort: 8080
---
# Database (Headless + StatefulSet)
apiVersion: v1
kind: Service
metadata:
  name: postgres-headless
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
  - port: 5432
    targetPort: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres-headless
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:13
        env:
        - name: POSTGRES_PASSWORD
          value: "password"
        - name: POSTGRES_DB
          value: "myapp"
        ports:
        - containerPort: 5432
---
# External Service (ExternalName)
apiVersion: v1
kind: Service
metadata:
  name: external-api
spec:
  type: ExternalName
  externalName: api.github.com
  ports:
  - port: 443
    targetPort: 443

๐Ÿ› ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ฐ€์ด๋“œ

๋ฌธ์ œ 1: ExternalName DNS ํ•ด์„ ์‹คํŒจ

# ์ฆ์ƒ ํ™•์ธ
kubectl exec -it test-pod -- nslookup external-service
# nslookup: can't resolve 'external-service'
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# ** server can't find external-service: NXDOMAIN
 
# ํ•ด๊ฒฐ๋ฐฉ๋ฒ•
# 1. CoreDNS ์ƒํƒœ ํ™•์ธ
kubectl get pods -n kube-system -l k8s-app=kube-dns
# NAME                       READY   STATUS    RESTARTS   AGE
# coredns-558bd4d5db-j8x9k   1/1     Running   0          5d
# coredns-558bd4d5db-q2m7l   1/1     Running   0          5d
 
# 2. ์„œ๋น„์Šค ์ •์˜ ํ™•์ธ
kubectl describe svc external-service
# Name:              external-service
# Namespace:         default
# Labels:            <none>
# Annotations:       <none>
# Selector:          <none>
# Type:              ExternalName
# IP Family Policy:  SingleStack
# IP Families:       IPv4
# External Name:     invalid.example.com  # โ† ์ž˜๋ชป๋œ FQDN
# Port:              <unset>  443/TCP
# TargetPort:        443/TCP
# Endpoints:         <none>
# Session Affinity:  None
 
# 3. ExternalName์„ ์˜ฌ๋ฐ”๋ฅธ FQDN์œผ๋กœ ์ˆ˜์ •
kubectl edit svc external-service
# externalName์„ ์œ ํšจํ•œ ๋„๋ฉ”์ธ(์˜ˆ: api.github.com)์œผ๋กœ ๋ณ€๊ฒฝ

๋ฌธ์ œ 2: ClusterIP ์„œ๋น„์Šค ์—ฐ๊ฒฐ ์‹คํŒจ

# ์ฆ์ƒ ํ™•์ธ
kubectl exec -it client-pod -- curl backend-service:8080
# curl: (7) Failed to connect to backend-service port 8080: Connection refused
 
# ์ง„๋‹จ ๋‹จ๊ณ„
# 1. ์„œ๋น„์Šค ์กด์žฌ ํ™•์ธ
kubectl get svc backend-service
# NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
# backend-service   ClusterIP   10.96.185.123   <none>        8080/TCP   5m
 
# 2. ์—”๋“œํฌ์ธํŠธ ํ™•์ธ (๋ฌธ์ œ ๋ฐœ๊ฒฌ!)
kubectl get endpoints backend-service
# NAME              ENDPOINTS   AGE
# backend-service   <none>      5m
# โ†‘ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋น„์–ด์žˆ์Œ = ํŒŒ๋“œ๊ฐ€ ์„œ๋น„์Šค์™€ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Œ
 
# 3. ํŒŒ๋“œ ์ƒํƒœ ํ™•์ธ
kubectl get pods -l app=backend
# No resources found in default namespace.
# โ†‘ ํŒŒ๋“œ๊ฐ€ ์—†์Œ! ๋˜๋Š” ๋ผ๋ฒจ ๋ถˆ์ผ์น˜
 
# 4. ๋ผ๋ฒจ ์…€๋ ‰ํ„ฐ ํ™•์ธ
kubectl describe svc backend-service
# Selector:          app=backend  # ์„œ๋น„์Šค๋Š” app=backend ๋ผ๋ฒจ์„ ์ฐพ์Œ
 
kubectl get pods --show-labels
# NAME                           READY   STATUS    RESTARTS   AGE     LABELS
# backend-app-7d4b8c5f47-2j9kl   1/1     Running   0          30m     app=web,pod-template-hash=7d4b8c5f47
# โ†‘ ํŒŒ๋“œ ๋ผ๋ฒจ์ด app=web๋กœ ์ž˜๋ชป ์„ค์ •๋จ (app=backend๊ฐ€ ์•„๋‹˜)
 
# 5. ํ•ด๊ฒฐ: ํŒŒ๋“œ ๋ผ๋ฒจ ์ˆ˜์ • ๋˜๋Š” ์„œ๋น„์Šค ์…€๋ ‰ํ„ฐ ์ˆ˜์ •
kubectl patch deployment backend-app -p '{"spec":{"template":{"metadata":{"labels":{"app":"backend"}}}}}'
# deployment.apps/backend-app patched
 
# 6. ์ˆ˜์ • ํ›„ ํ™•์ธ
kubectl get endpoints backend-service
# NAME              ENDPOINTS                                   AGE
# backend-service   10.244.1.5:80,10.244.1.6:80,10.244.2.3:80   7m
# โ†‘ ์ด์ œ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋“ฑ๋ก๋จ

๋ฌธ์ œ 3: Headless ์„œ๋น„์Šค ๊ฐœ๋ณ„ ํŒŒ๋“œ ์ ‘๊ทผ ์‹คํŒจ

# ์ฆ์ƒ ํ™•์ธ
kubectl exec -it client-pod -- nslookup mongodb-0.mongodb-headless
# nslookup: can't resolve 'mongodb-0.mongodb-headless'
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# ** server can't find mongodb-0.mongodb-headless: NXDOMAIN
 
# ํ•ด๊ฒฐ๋ฐฉ๋ฒ•
# 1. StatefulSet ์ƒํƒœ ํ™•์ธ
kubectl get statefulset
# NAME      READY   AGE
# mongodb   2/3     5m    # โ† ์ผ๋ถ€ ํŒŒ๋“œ๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์Œ
 
kubectl describe statefulset mongodb
# Name:               mongodb
# Namespace:          default
# CreationTimestamp:  Mon, 13 Nov 2025 14:30:00 +0900
# Selector:           app=mongodb
# Labels:             <none>
# Annotations:        <none>
# Replicas:           3 desired | 2 ready
# ...
# Conditions:
#   Type             Status  Reason
#   ----             ------  ------
#   Progressing      True    ReplicaSetUpdated
# Events:
#   Type    Reason            Age   From                    Message
#   ----    ------            ----  ----                    -------
#   Normal  SuccessfulCreate  5m    statefulset-controller  create Claim mongodb-storage-mongodb-0 Pod mongodb-0 in StatefulSet mongodb success
#   Warning FailedCreate      2m    statefulset-controller  create Pod mongodb-2 in StatefulSet mongodb failed: persistentvolumeclaim "mongodb-storage-mongodb-2" not found
 
# 2. ํŒŒ๋“œ ์ƒํƒœ ์ƒ์„ธ ํ™•์ธ
kubectl get pods -l app=mongodb
# NAME        READY   STATUS    RESTARTS   AGE
# mongodb-0   1/1     Running   0          5m
# mongodb-1   1/1     Running   0          4m
# mongodb-2   0/1     Pending   0          2m    # โ† ํŒŒ๋“œ๊ฐ€ Pending ์ƒํƒœ
 
kubectl describe pod mongodb-2
# Events:
#   Type     Reason            Age   From               Message
#   ----     ------            ----  ----               -------
#   Warning  FailedScheduling  2m    default-scheduler  persistentvolumeclaim "mongodb-storage-mongodb-2" not bound: no persistent volumes available
 
# 3. PVC ์ƒํƒœ ํ™•์ธ (๋ฌธ์ œ ๋ฐœ๊ฒฌ!)
kubectl get pvc
# NAME                        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
# mongodb-storage-mongodb-0   Bound    pvc-abc123...                              1Gi        RWO            standard       5m
# mongodb-storage-mongodb-1   Bound    pvc-def456...                              1Gi        RWO            standard       4m
# mongodb-storage-mongodb-2   Pending                                                                      standard       2m
 
# 4. ํ•ด๊ฒฐ: ์Šคํ† ๋ฆฌ์ง€ ํ”„๋กœ๋น„์ €๋„ˆ ํ™•์ธ ๋ฐ PV ์ƒ์„ฑ
kubectl get storageclass
# NAME                 PROVISIONER            RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
# standard (default)   kubernetes.io/gce-pd   Delete          Immediate           true                   10d
 
# ์ผ์‹œ์  ํ•ด๊ฒฐ: ํ•ด๋‹น PVC ์‚ญ์ œ ํ›„ StatefulSet ์žฌ์‹œ์ž‘
kubectl delete pvc mongodb-storage-mongodb-2
kubectl delete pod mongodb-2
 
# 5. ์ˆ˜์ • ํ›„ DNS ํ…Œ์ŠคํŠธ
kubectl exec -it client-pod -- nslookup mongodb-0.mongodb-headless
# Server:		10.96.0.10
# Address:	10.96.0.10:53
# 
# Name:	mongodb-0.mongodb-headless.default.svc.cluster.local
# Address: 10.244.1.7
# โœ… ์ด์ œ ์ •์ƒ์ ์œผ๋กœ ํ•ด์„๋จ
 
# 6. Headless ์„œ๋น„์Šค serviceName ํ™•์ธ
kubectl get statefulset mongodb -o yaml | grep serviceName
#  serviceName: mongodb-headless  # โœ… ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •๋จ

๐Ÿ“‹ ์‹ค์Šต ๊ฒ€์ฆ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

โœ… ExternalName ์„œ๋น„์Šค ๊ฒ€์ฆ

  • ์™ธ๋ถ€ DNS ์ด๋ฆ„์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด์„๋˜๋Š”๊ฐ€?
  • ํฌํŠธ๊ฐ€ ์ •ํ™•ํžˆ ๋งคํ•‘๋˜์—ˆ๋Š”๊ฐ€?
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ณ„์นญ์„ ํ†ตํ•ด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ๊ฐ€?

โœ… ClusterIP ์„œ๋น„์Šค ๊ฒ€์ฆ

  • ์„œ๋น„์Šค์— ClusterIP๊ฐ€ ํ• ๋‹น๋˜์—ˆ๋Š”๊ฐ€?
  • ์—”๋“œํฌ์ธํŠธ์— ํŒŒ๋“œ IP๋“ค์ด ๋“ฑ๋ก๋˜์—ˆ๋Š”๊ฐ€?
  • ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์ด ์ •์ƒ ์ž‘๋™ํ•˜๋Š”๊ฐ€?
  • ๋‚ด๋ถ€์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ณ  ์™ธ๋ถ€์—์„œ๋Š” ์ ‘๊ทผ ๋ถˆ๊ฐ€ํ•œ๊ฐ€?

โœ… Headless ์„œ๋น„์Šค ๊ฒ€์ฆ

  • ClusterIP๊ฐ€ None์œผ๋กœ ์„ค์ •๋˜์—ˆ๋Š”๊ฐ€?
  • DNS ์กฐํšŒ์‹œ ๊ฐœ๋ณ„ ํŒŒ๋“œ IP๋“ค์ด ๋ฐ˜ํ™˜๋˜๋Š”๊ฐ€?
  • StatefulSet ํŒŒ๋“œ๋“ค์ด ์•ˆ์ •์ ์ธ ๋„คํŠธ์›Œํฌ ID๋ฅผ ๊ฐ€์ง€๋Š”๊ฐ€?
  • pod-name.service-name ํ˜•ํƒœ๋กœ ๊ฐœ๋ณ„ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•œ๊ฐ€?

๐Ÿงน ์‹ค์Šต ํ™˜๊ฒฝ ์ •๋ฆฌ

# ๋ชจ๋“  ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
kubectl delete -f complete-3tier-app.yaml
kubectl delete -f external-mysql-service.yaml
kubectl delete -f backend-deployment.yaml
kubectl delete -f backend-clusterip-service.yaml
kubectl delete -f mongodb-statefulset.yaml
kubectl delete -f mongodb-headless-service.yaml
 
# ํ…Œ์ŠคํŠธ ํŒŒ๋“œ ์ •๋ฆฌ
kubectl delete pod test-pod dns-test mongo-client --ignore-not-found=true
 
# PVC ์ •๋ฆฌ (StatefulSet ๋ณผ๋ฅจ)
kubectl delete pvc -l app=mongodb

๐ŸŽ“ ํ•ต์‹ฌ ์ •๋ฆฌ ๋ฐ ์‹ค๋ฌด ํŒ

๐Ÿ“Š ์„œ๋น„์Šค ํƒ€์ž…๋ณ„ ์‚ฌ์šฉ ์ผ€์ด์Šค

์„œ๋น„์Šค ํƒ€์ž…์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€DNS ํ•ด์„ ๊ฒฐ๊ณผ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ
ExternalName์™ธ๋ถ€ DB, API ์—ฐ๋™
๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ ํ†ตํ•ฉ
์™ธ๋ถ€ FQDN CNAMEโŒ
ClusterIP๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹ 
๋‚ด๋ถ€ API ๊ฒŒ์ดํŠธ์›จ์ด
๊ฐ€์ƒ IP 1๊ฐœโœ…
Headless๋ถ„์‚ฐ DB ํด๋Ÿฌ์Šคํ„ฐ
P2P ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
๋ชจ๋“  ํŒŒ๋“œ IPโŒ

๐Ÿ’ก ์‹ค๋ฌด ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

1. ExternalName ์‚ฌ์šฉ์‹œ

  • ์™ธ๋ถ€ ์˜์กด์„ฑ ์ถ”์ƒํ™”๋กœ ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ๋ถ„๋ฆฌ
  • DNS ์บ์‹œ TTL ๊ณ ๋ คํ•˜์—ฌ ์„ค๊ณ„
  • ์™ธ๋ถ€ ์„œ๋น„์Šค์˜ ๊ฐ€์šฉ์„ฑ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•„์ˆ˜

2. ClusterIP ํ™œ์šฉ์‹œ

  • ๊ธฐ๋ณธ ์„œ๋น„์Šค ํƒ€์ž…์œผ๋กœ ๋‚ด๋ถ€ ํ†ต์‹ ์— ์ตœ์ 
  • ํฌํŠธ๋ช…์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜์—ฌ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ
  • Health Check์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ์•ˆ์ •์„ฑ ํ™•๋ณด

3. Headless ์„œ๋น„์Šค ๊ตฌ์„ฑ์‹œ

  • StatefulSet๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ์•ˆ์ •์ ์ธ ๋„คํŠธ์›Œํฌ ์•„์ด๋ดํ‹ฐํ‹ฐ ์ œ๊ณต
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํด๋Ÿฌ์Šคํ„ฐ, ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์— ํ•„์ˆ˜
  • DNS ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ ํ™œ์šฉ

๐Ÿ” ์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ

๊ด€๋ จ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฆฌ์†Œ์Šค

์‹ค๋ฌด ์‹œ๋‚˜๋ฆฌ์˜ค


์ž‘์„ฑ์ผ: 2025.11.13
ํƒœ๊ทธ: kubernetes service networking devops ์‹ค์Šต