๐ ์ฟ ๋ฒ๋คํฐ์ค ์๋น์ค ํ์ ๋ณ ์ค์ต ๊ฐ์ด๋
๐ ์๋น์ค ํ์ ๊ฐ๋ ์ ๋ฆฌ
๐ 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 ์ค์ต