카테고리 없음

[k8s] Docker&K8s -3

ride-dev 2025. 5. 11. 07:06

0. 들어가면서

 docker & k8s 스터디 세 번째 파트입니다.

이번에는 ubuntu에 homebrew를 설치하여 실습을 진행하도록 하겠습니다.

sudo apt-get install build-essential curl file git

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homeb  rew/install/HEAD/install.sh)"

설치가 완료되면, next steps에 있는 내용을 따릅니다.

(제가 생성한 사용자 계정명은 min 입니다. 생성한 사용자명에 따라 달라집니다)

==> Next steps:
- Run these commands in your terminal to add Homebrew to your PATH:
    echo >> /home/min/.bashrc
    echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/min/.bashrc                                        bashrc
    eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
- Install Homebrew's dependencies if you have sudo access:
    sudo apt-get install build-essential
  For more information, see:
    https://docs.brew.sh/Homebrew-on-Linux
- We recommend that you install GCC:
    brew install gcc
- Run brew help to get started
- Further documentation:
    https://docs.brew.sh

위 내용에 따라 아래 명령어를 실행하도록 합니다.

    echo >> /home/min/.bashrc
    echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/min/.bashrc                                        bashrc
    eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
    
    brew install gcc

 

k8s 관련 라이브러리 및 플러그인을 설치합니다.

# Install Kind
brew install kind
kind --version

# Install kubectl
brew install kubernetes-cli
kubectl version --client=true

# Install Helm
brew install helm
helm version

만약 도커 설치가 필요하다면, 설치를 진행합니다.

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

# 최신 Docker 패키지 설치
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 설치 확인
docker --version

# 권한 부여
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker

# 권한 확인
docker ps

# 부팅 시, 도커 자동 시작
sudo systemctl enable docker.service
sudo systemctl enable containerd.service

아래 스크립트를 통하여 실습 환경을 구성합니다.

(Control Plane 1대 + Worker Node 1대)

# '컨트롤플레인, 워커 노드 1대' 클러스터 배포 : 파드에 접속하기 위한 포트 맵핑 설정
cat <<EOT> kind-2node.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
    listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
    protocol: tcp # Optional, defaults to tcp
  - containerPort: 30001
    hostPort: 30001
EOT

kind create cluster --config kind-2node.yaml

# 배포 확인
kind get clusters
kind get nodes

# 노드 확인
kubectl get nodes -o wide

# 노드에 Taints 정보 확인
kubectl describe node kind-control-plane | grep Taints
Taints:             node-role.kubernetes.io/control-plane:NoSchedule

kubectl describe node kind-worker | grep Taints
Taints:             <none>

# 컨테이너 확인 : 컨테이너 갯수, 컨테이너 이름 확인
# kind yaml 에 포트 맵핑 정보 처럼, 자신의 PC 호스트에 30000 포트 접속 시, 워커노드(실제로는 컨테이너)에 TCP 30000 포트로 연결
# 즉, 워커노드에 NodePort TCP 31000 설정 시 자신의 PC 호스트에서 접속 가능!
docker ps
docker port kind-worker
30000/tcp -> 0.0.0.0:30000
30001/tcp -> 0.0.0.0:30001

kube-ops-view를 설치하여 pod을 실시간으로 확인할 수도 있습니다.

# kube-ops-view
# helm show values geek-cookbook/kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system

# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율)
echo -e "KUBE-OPS-VIEW URL = http://localhost:30000/#scale=2"

1. k8s 가용성 확보

쿠버네티스에서 가용성을 확보하기 위한 여러 전략이 있습니다.

HPA, VPA, CA, ...

https://kubetm.github.io/k8s/08-intermediate-controller/hpa/

해당 전략들에 대해 더 자세히 확인할 수 있도록 Metrics Server를 설치합니다.

쿠버네티스에 내장된 확장 파이프라인의 컨테이너 지표를 수집합니다.

# Metrics Server 설치
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# Metrics Server SSL 무시
kubectl patch deployment metrics-server -n kube-system --type=json \
  -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]'

# Metrics Server 배포 확인
kubectl get pods -n kube-system -l k8s-app=metrics-server

# 쿠버네티스 리소스 자원 사용량 확인
kubectl top node
kubectl top pods -A

# CPU, Memory 내림차순
kubectl top pods -A --sort-by=cpu
kubectl top pods -A --sort-by=memory

1.1 HPA

HPA (Horizontal Pod Autoscaling) : Pod 수평 확장 (스케일 In - Out)

애플리케이션 부하(Load)에 따라 Pod 개수를 자동으로 늘리거나 줄이는 기능

모니터링 대상: CPU, Memory, 사용자 정의 지표

Metrics Server 가 감시한 지표를 활용하여 설정된 임계치를 초과하면 Replica 수 조정

기본 구성

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-sample
spec:
  scaleTargetRef:          # Scale 타겟 지정
    apiVersion: apps/v1
    kind: Deployment
    name: my-app           # Deployment 이름
  minReplicas: 2           # 최소 Pod
  maxReplicas: 10          # 최대 Pod
  metrics:                 # Scale 기준 지표 설정
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50  # CPU 사용률 50% 기준
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 70  # 메모리 사용률 70% 기준

HPA 구성

Deploy배포 사용

cat << EOF >> hpa-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hpa-nginx
  template:
    metadata:
      labels:
        app: hpa-nginx
    spec:
      containers:
      - name: hpa-nginx
        image: nginx
        resources:
          requests:
            cpu: 50m
          limits:
            cpu: 100m
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: hpa-nginx
  labels:
    app: hpa-nginx
spec:
  ports:
  - port: 80
  selector:
    app: hpa-nginx
EOF

cat hpa-nginx.yaml

# Deployment 배포
kubectl apply -f hpa-nginx.yaml

kubectl get deploy,pod

HPA 구성

# HPA 생성
kubectl autoscale deployment hpa-nginx --cpu-percent=50 --min=1 --max=10

# HPA 확인
kubectl get hpa
...
NAME        REFERENCE              TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
hpa-nginx   Deployment/hpa-nginx   cpu: 0%/50%   1         10        1          17s
....

# HPA 상세 정보 확인
kubectl describe hpa
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-nginx
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hpa-nginx
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

Pod 부하 생성

# 터미널 1번
while true; do kubectl get hpa; kubectl top pods; sleep 1s; done

# 터미널 2번
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while true; do wget -q -O- http://hpa-nginx.default.svc.cluster.local; done"

# 실습 종료 후 리소스 삭제
kubectl delete hpa --all
kubectl delete -f hpa-nginx.yaml

1.2. VPA

VPA (Vertical Pod Autoscaling) : Pod 수직 확장 (스케일 Up)

Pod 의 리소스 요청값(Request) 을 자동으로 조정하는 기능

적용 대상 : Request (CPU, Memory)

Pod 의 개수를 늘리는 HPA 와 다르게 Pod 의 리소스 크기를 조정

VPA Recommender 에 의해 최적의 리소스 상태를 찾아서 조정

하나의 Deployment 에 HPA, VPA 를 같이 사용할 수 없음

Kubernetes v1.33 버전 부터는 기본 활성화 상태

but, kind 는 현재 v1.32 까지만 사용가능하므로 별도 controller 설치 필요

기본 구성

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:                   # Scale 대상
    apiVersion: apps/v1
    kind: Deployment
    name: my-app               # Deployment 명칭
  updatePolicy:
    updateMode: "Auto"         # VPA Recommender 에 의해 자동 조정 활성화
  resourcePolicy:
    containerPolicies:
      - containerName: my-app-container # Container 명칭 "*" 사용 가능
        minAllowed:            # 컨테이너가 할당받을 수 있는 최소 리소스
          cpu: "200m"
          memory: "512Mi"
        maxAllowed:            # 컨테이너가 할당받을 수 있는 최대 리소스
          cpu: "2"
          memory: "2Gi"

VPA배포

# EKS Workshop 소스 사용
git clone https://github.com/kubernetes/autoscaler.git

# VPA 배포
cd autoscaler/vertical-pod-autoscaler/
./hack/vpa-up.sh

# VPA Controller 확인
kubectl get pods -n kube-system | grep vpa

# VPA 제거
./hack/vpa-down.sh

VPA 테스트 - 0.1 cpu 를 요청한 2개 Pod 배포 (실제 사용량보다 부족한 상태)

apiVersion: "autoscaling.k8s.io/v1"
kind: VerticalPodAutoscaler
metadata:
  name: hamster-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: hamster
  resourcePolicy:
    containerPolicies:
      - containerName: '*'
        minAllowed:
          cpu: 100m
          memory: 50Mi
        maxAllowed:
          cpu: 1
          memory: 500Mi
        controlledResources: ["cpu", "memory"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hamster
spec:
  selector:
    matchLabels:
      app: hamster
  replicas: 2
  template:
    metadata:
      labels:
        app: hamster
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 65534 # nobody
      containers:
        - name: hamster
          image: registry.k8s.io/ubuntu-slim:0.14
          resources:
            requests:
              cpu: 100m
              memory: 50Mi
          command: ["/bin/sh"]
          args:
            - "-c"
            - "while true; do timeout 0.5s yes >/dev/null; sleep 0.5s; done"
# 터미널 1번
while true;
 do date "+%Y-%m-%d %H:%m:%S";
 kubectl get pod -l app=hamster; 
 kubectl get vpa; 
 kubectl describe pod | grep "Requests:" -A2; 
 echo "=============="; 
 sleep 5s; 
done

# 터미널 2번
kubectl apply -f examples/hamster.yaml        

# 자원 삭제
kubectl delete -f examples/hamster.yaml

 

2. 어플리케이션 변수 관리

2.1. ConfigMap

ConfigMap을 통해,

쿠버네티스 애플리케이션의 구성파일이나 환경설정을 Key-Value 형태로 관리할 수 있습니다.

애플리케이션의 설정 정보를 외부에서 관리하고 Pod 와 컨테이너에서 참조할 수 있습니다.

기본구성은 다음과 같습니다.

# ConfigMap 샘플 구성
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config # ConfigMap 명칭
data:
  key1: value1    # Key : Value 형태 값 주입
  key2: value2
# ConfigMap 사용 예시
apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: my-container
      image: my-image
      env:
        - name: MY_CONFIG_KEY  # 컨테이너에서 사용할 변수 Key 값
          valueFrom:
            configMapKeyRef:
              name: my-config  # 사용할 ConfigMap의 이름
              key: key1        # ConfigMap 내의 키 -> 값: value1

ConfigMap 기본 활용

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
data:
  DBNAME: mydatabase
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-configmap
spec:
  containers:
  - image: nginx
    name: nginx-configmap
    env:
    - name: DB
      valueFrom:
        configMapKeyRef:
          name: mysql
          key: DBNAME
EOF

# 오브젝트 확인
kubectl get cm,pod

# 상세 정보 조회
kubectl describe cm mysql
kubectl describe pod nginx-configmap

# pod 내부 변수 확인
kubectl exec -it nginx-configmap -- /bin/bash -c env
...
DB=mydatabase
...

# 리소스 삭제
kubectl delete pod --all
kubectl delete cm nginx-configmap

ConfigMap 으로 설정 파일 관리

# 테스트 파일 생성
cat << EOF >> config-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-configmap-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-configmap
  template:
    metadata:
      labels:
        app: nginx-configmap
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config-volume
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
      volumes:
      - name: config-volume
        configMap:
          name: nginx-config
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx-configmap
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 31001
  type: NodePort
EOF

cat << EOF >> configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    events {}
    http {
        server {
            listen 80;
            location / {
                return 200 'Hello from nginx configmap!';
            }
        }
    }
EOF

리소스를 배포하여 구성관리가 진행되고 있는지 확인합니다.

kubectl apply -f configmap.yaml -f config-deploy.yaml

#
kubectl get cm,deploy,pod
kubectl describe deploy
...
    Mounts:
      /etc/nginx/nginx.conf from config-volume (rw,path="nginx.conf")
  Volumes:
   config-volume:
    Type:          ConfigMap (a volume populated by a ConfigMap)
    Name:          nginx-config
    Optional:      false
...

# Nginx 접속
open http://localhost:31001

# Nginx ConfigMap 변경
vim configmap.yaml
...
return 200 'Modify from nginx configmap!';
...

#
kubectl apply -f configmap.yaml

# pod 재시작
kubectl rollout restart deploy nginx-configmap-deploy

# 리소스 삭제
kubectl delete -f configmap.yaml -f config-deploy.yaml

ConfigMap 변경 시 Pod를 수동으로 재배포해주어야 하는 불편함이 있습니다.

Reloader를 통해 이를 자동화할 수 있습니다.

https://github.com/stakater/Reloader/tree/master

 

GitHub - stakater/Reloader: A Kubernetes controller to watch changes in ConfigMap and Secrets and do rolling upgrades on Pods wi

A Kubernetes controller to watch changes in ConfigMap and Secrets and do rolling upgrades on Pods with their associated Deployment, StatefulSet, DaemonSet and DeploymentConfig – [✩Star] if you'...

github.com

(ConfigMap, Secret 의 변동 사항을 주기적으로 확인해서 자동으로 Rollout 을 해주는 오픈소스 도구)

2.2. Secret

secret는 쿠버네티스에서 민감 정보를 안전하게 관리하는 객체입니다.

(비밀번호, 토큰, SSH 키 등)

ConfigMap 과 달리 Base64 로 인코딩된 형태로 데이터 저장합니다.

주로 비밀번호, API 키 등의 민감정보 저장에 사용합니다.

ConfigMap 과 동일하게 Pod 의 환경 변수나 파일로 주입 가능합니다.

기본구성

# Secret 샘플
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  username: bXl1c2Vy  # base64로 인코딩된 값
  password: bXlwYXNzd29yZA==  # base64로 인코딩된 값
# Secret 사용 예시
apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: my-container
      image: my-image
      env:
        - name: DB_USER        # Container 에서 사용할 변수명
          valueFrom:
            secretKeyRef:
              name: my-secret  # 사용할 Secret의 이름
              key: username    # Secret 내의 키
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: my-secret  # 사용할 Secret의 이름
              key: password    # Secret 내의 키
...
# 마운트 방법
      volumeMounts:
        - name: secret-volume     # Volume 명칭
          mountPath: /etc/secrets # 컨테이너 내부 마운트 위치
  volumes:
    - name: secret-volume         # Volume 명칭
      secret:
        secretName: my-secret     # 사용할 Secret의 이름

활용

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: secret-test
type: Opaque
data:
  username: YWRtaW4=  # 'admin'을 base64 인코딩한 값
  password: cGFzc3dvcmQ=  # 'password'를 base64 인코딩한 값
EOF

# Base64 인코딩 방법
echo -n 'admin' | base64
echo -n 'password' | base64

#
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: secret-pod
spec:
  containers:
  - name: nginx
    image: nginx
    env:
    - name: DB_USER
      valueFrom:
        secretKeyRef:
          name: secret-test
          key: username
    - name: DB_PASS
      valueFrom:
        secretKeyRef:
          name: secret-test
          key: password
EOF

#
kubectl get pod,secret

# 상세 정보 조회
kubectl describe secret secret-test
kubectl describe pod secret-pod

# pod 내부 변수 확인
kubectl exec -it secret-pod -- /bin/bash -c env
...
DB_USER=admin
DB_PASS=password
...

# 리소스 삭제
kubectl delete pod --all
kubectl delete secret secret-test

단, base64가 완벽히 암호화된 것이 아니기에 주의가 필요합니다.

따라서, 기존 Base64 로 인코딩 되어 저장하는 Secret 이 아닌 암호화 되어 저장하는 Sealed Secret을 사용할 수 있습니다.

Sealed Secret 은,

Code Repository (Git) 에 암호화된 Secret 이 올라가기 때문에,

GitOps Flow 를 그대로 사용할 수 있는 장점이 있습니다.

Client Side(암호화) - Server Side (복호화)

# Mac Brew 설치 (Client-Side)
brew install kubeseal

# Server-Side 설치 - SealedSecret Controller
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install my-release sealed-secrets/sealed-secrets

# SealedSecret Controller 조회
kubectl get pods -l app.kubernetes.io/name=sealed-secrets

# Secret 생성
kubectl create secret generic mysecret \
  --from-literal hello=world \
  --dry-run=client \
  -o yaml > mysecret.yaml

# 
cat mysecret.yaml

# base64 디코딩
grep 'hello:' mysecret.yaml | awk '{print $2}' | base64 --decode

# Sealed Secret 생성
cat mysecret.yaml | \
  kubeseal \
   --controller-name my-release-sealed-secrets \
   --controller-namespace default -o yaml > mysealed-secret.yaml

# Sealed Secret 암호화 적용 확인
cat mysealed-secret.yaml
grep 'hello:' mysealed-secret.yaml | awk '{print $2}' | base64 --decode

#
kubectl apply -f mysealed-secret.yaml
kubectl get secret

# secret 확인
kubectl get secret mysecret -o json
kubectl get secret mysecret -o jsonpath="{.data.hello}" | base64 -d

3. 네트워크

네트워킹은 쿠버네티스의 핵심으로,

구성 요소는 아래와 같습니다.

https://kubernetes.io/docs/concepts/cluster-administration/networking/

Service

외부와 접하는 단일 엔드포인트

서비스 뒷단의 애플리케이션으로 외부 트래픽을 전송

ClusterIP

쿠버네티스 클러스터 범위의 가상 IP 부여

클러스터 내부에서만 ClusterIP 로 접근 가능

서비스 타입을 지정하지 않을 경우 기본값

# 어플리케이션 배포
cat << EOF >> cluster-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: cluster-pod-1
  labels:
    app: cluster-pod
spec:
  containers:
  - name: container
    image: traefik/whoami
---
apiVersion: v1
kind: Pod
metadata:
  name: cluster-pod-2
  labels:
    app: cluster-pod
spec:
  containers:
  - name: container
    image: traefik/whoami
EOF

# Test 파드
cat << EOF >> netshoot-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: netshoot-pod
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
EOF

# ClusterIP 서비스 생성
cat <<EOF>> cluster-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: cluster-svc
spec:
  type: ClusterIP
  selector:
    app: cluster-pod
  ports:
    - name: cluster
      port: 8080
      targetPort: 80
EOF

# 배포
kubectl apply -f cluster-pod.yaml -f cluster-svc.yaml -f netshoot-pod.yaml
# 파드 대역 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'
...
10.244.0.0/24 10.244.1.0/24
...

# SVC 대역 확인
kubectl -n kube-system get pods -l component=kube-controller-manager -o yaml | grep service-cluster-ip-range
...
--service-cluster-ip-range=10.96.0.0/16
...

# 확인
kubectl get pod -o wide
...
NAME                                         READY   STATUS    RESTARTS   AGE     IP            NODE          NOMINATED NODE   READINESS GATES
cluster-pod-1                                1/1     Running   0          8m20s   10.244.1.66   kind-worker   <none>           <none>
cluster-pod-2                                1/1     Running   0          8m20s   10.244.1.65   kind-worker   <none>           <none>
netshoot-pod                                 1/1     Running   0          8m20s   10.244.1.67   kind-worker   <none>           <none>
...

# 서비스 확인
kubectl get svc cluster-svc
...
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
cluster-svc   ClusterIP   10.96.138.114   <none>        8080/TCP   9m18s
...

# Endpoint 확인 (Pod IP:Port)
kubectl get endpoints cluster-svc
...
NAME                                ENDPOINTS                       AGE
cluster-svc                         10.244.1.65:80,10.244.1.66:80   9m37s
...

NodePort

고정 포트로 각 노드(Host) 의 포트를 노출

클러스터 외부에서 노드(Host) 의 IP:Port 를 통해 접근 가능

# 어플리케이션 배포
cat << EOF > nodeport-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodeport-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nodeport-deploy
  template:
    metadata:
      labels:
        app: nodeport-deploy
    spec:
      containers:
      - name: container
        image: traefik/whoami
EOF

# ClusterIP 서비스 생성
cat <<EOF> nodeport-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: nodeport-svc
spec:
  type: NodePort
  selector:
    app: nodeport-deploy
  ports:
    - name: nodeport-svc
      port: 80          # 서비스 포트 (Cluster 내부에서 사용)
      targetPort: 80    # 실제 컨테이너 포트
      nodePort: 31001   # 외부에서 접근할 NodePort
EOF
# 생성
kubectl apply -f nodeport-pod.yaml -f nodeport-svc.yaml

# 확인
kubectl get pod,svc
...
NAME                   TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/nodeport-svc   NodePort    10.96.66.182   <none>        80:31001/TCP   100s
...

kubectl get endpoints nodeport-svc
NAME           ENDPOINTS                       AGE
nodeport-svc   10.244.1.10:80,10.244.1.11:80   2m50s
# 노드의 Port 로 curl 요청
curl http://localhost:31001
curl -s http://localhost:31001 | grep Hostname

# 100 번 반복 호출
for i in {1..100}; do curl -s http://localhost:31001 | grep Hostname; done | sort | uniq -c | sort -nr
...
58 Hostname: nodeport-deploy-59b68567d7-6h562
42 Hostname: nodeport-deploy-59b68567d7-k2cpb
...

LoadBalance

외부 로드 밸런서를 지원하는 클라우드 공급자 상에서 활용

AWS, Azure, GCP 등의 LB 서비스와 쿠버네티스 서비스를 연결

 

Ingress

https://kubernetes.io/ko/docs/concepts/services-networking/ingress/

Ingress는 클러스터 외부에서

내부 서비스로 트래픽을 라우팅 하는 방법을 제공하는 리소스입니다.

클러스터 내부 서비스 (ClusterIP, NodePort, LoadBalancer) 를 외부로 노출 (HTTP / HTTPS)하는

Web Proxy 역할을 담당합니다.

Ingress 를 사용하기 위해서는 Ingress Controller 가 필요합니다.

대표적인 Ingress Controller는

Nginx Ingress Controller, Cloud Provider Ingress Controllers 입니다. 

주요기능

1. 호스트 기반 라우팅

호스트 이름 (도메인)을 기반으로 트래픽 라우팅 가능

api.example.com / www.example.com 을 각각 다른 Service 리소스와 연결

2. 경로 기반 라우팅

요청 경로 기반으로 트래픽 라우팅 가능

/growth , /log 경로를 각각 다른 Service 리소스와 연결

3. TLS 설정

TLS 인증서를 활용하여 HTTPS 구성 가능

4. 로드 밸런싱

내부 서비스에 대한 로드 밸런싱

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: default
spec:
  rules:
  - host: example.com       # Domain Host
    http:
      paths:
      - path: /service1     # URL Path (example.com/service1)
        pathType: Prefix
        backend:
          service:
            name: service1  # /service1 로 들어온 트래픽을 전송할 service 명
            port:
              number: 80
      - path: /service2     # URL Path (example.com/service2)
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80    # /service2 로 들어온 트래픽을 전송할 service 명

Nginx Ingress Controller 설치

# 기존 리소스 삭제
kind delete cluster

# kind cluster 재배포
kind create cluster --config kind-2node.yaml

# Nginx Ingress Controller 설치
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

# Service 타입 변경
kubectl patch svc ingress-nginx-controller -n ingress-nginx -p \
'{"spec":{"type":"NodePort","ports":[{"port":80,"targetPort":80,"nodePort":31000},{"port":443,"targetPort":443,"nodePort":31001}]}}'

# Nginx Ingress Controller 리소스 확인
kubectl get -n ingress-nginx svc,deploy,pod
# Growth 서비스
cat << EOF > svc-growth.yaml
apiVersion: v1
kind: Service
metadata:
  name: growth-service
spec:
  selector:
    app: growth
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: growth-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: growth
  template:
    metadata:
      labels:
        app: growth
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: growth-html
              mountPath: /usr/share/nginx/html
      volumes:
        - name: growth-html
          configMap:
            name: growth-html
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: growth-html
data:
  index.html: |
    <html>
    <body>
      <h1>hello growth</h1>
    </body>
    </html>
EOF

kubectl apply -f svc-growth.yaml
# Log 서비스
cat << EOF > svc-log.yaml
apiVersion: v1
kind: Service
metadata:
  name: log-service
spec:
  selector:
    app: log
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: log-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: log
  template:
    metadata:
      labels:
        app: log
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: log-html
              mountPath: /usr/share/nginx/html
      volumes:
        - name: log-html
          configMap:
            name: log-html
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: log-html
data:
  index.html: |
    <html>
    <body>
      <h1>hello log</h1>
    </body>
    </html>

EOF

kubectl apply -f svc-log.yaml
# 배포 확인
kubectl get pod,svc,cm

# ConfigMap 확인
kubectl describe cm growth-html
kubectl describe cm log-html
cat << EOF > ingress-sample.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: growth-log-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: / # /growth, /log 요청을 서비스로 전달할 때 접두사 제거. ex) /growth -> growth-service '/'
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /growth
        pathType: Prefix
        backend:
          service:
            name: growth-service
            port:
              number: 80
      - path: /log
        pathType: Prefix
        backend:
          service:
            name: log-service
            port:
              number: 80
EOF

kubectl apply -f ingress-sample.yaml
kubectl get ing,svc

#
kubectl describe ingress growth-log-ingress
# growth 경로 호출
curl http://localhost:31000/growth
...
<html>
<body>
  <h1>hello growth</h1>
</body>
</html>
...

# Log 경로 호출
curl http://localhost:31000/log
...
<html>
<body>
  <h1>hello log</h1>
</body>
</html>
...

 

이제 실습 환경을 제거합니다.

kind get clusters

kind delete cluster

 

728x90
반응형