0525_Kubernetes_TLS/SSL Termination with Ingress / StatefulSet(STS), Headless Service / AutoScaling, Request & Limit, HPA

HYOJU DO·2022년 5월 25일
0

Kubernetes

목록 보기
11/13
post-thumbnail

TLS/SSL Termination with Ingress


🎈 END to END 암호화
HTTPS Nginx 기본 방식 (5/24)

♢ 단점
각 파드마다

  • 인증서를 개별적으로 관리(갱신)
  • 암호화-복호화 연산 시 CPU 사용량 증가(전력 많이 소모)

공격 트래픽도 암호화되어 전달 ➜ 각 파드마다 별도의 보안 어플리케이션 파드 필요 ➜ 부하 증가 / 보안 장비 관리 어려움

TLS(Transport Layer Security) Termination

  • Internet - TLS Termination Proxy : 암호화 통신
  • TLS Termination Proxy - Private Network : 평문 통신
    (Private Network가 도청 되지않는다는 보장 필요)

♢ 장점

  • 인증서 관리 용이 (TLS 프록시에서만 인증서 관리) ➜ 프록시 성능 중요
  • 개별 서버 암/복호화 x ➜ CPU 리소스 사용량 감소
  • 여러 보안 구성을 비암호화 된 부분에 구성 가능
    보안 장비 세팅 ➜ 비정상적 트래픽, 해킹 등 감시

TLS Proxy for k8s : Ingress

🎈 TLS Termination 실습

ing.spec.tls

♢ 구조도

ingress-tls-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: ingress-tls-secret
type: kubernetes.io/tls
data:
  # base64 x509/nginx-tls.crt -w 0
  tls.crt: |
    LS0tLS1CRUd...
  # base64 x509/nginx-tls.key -w 0
  tls.key: |
    LS0tLS1CRUdJ...

myweb-rs.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myweb-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
      env: dev
  template:
    metadata:
      labels:
        app: web
        env: dev
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb
          ports:
            - containerPort: 8080
              protocol: TCP

myweb-svc-np.yaml

apiVersion: v1
kind: Service
metadata:
  name: myweb-svc-np
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

myweb-ingress-tls.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myweb-ing-tls
spec:
  tls: ✔️
    - hosts:
        - '*.nip.io'
      secretName: ingress-tls-secret
  rules:
    - host: '*.nip.io'
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myweb-svc-np
                port:
                  number: 80

생성

$ kubectl create -f myweb-rs.yaml
$ kubectl create -f myweb-svc-np.yaml
$ kubectl create -f ingress-tls-secret.yaml
$ kubectl create -f myweb-ingress-tls.yaml
or
$ kubectl create -f .

확인

kubectl get svc,ing,rs,po
$ curl -k https://192-168-100-100.nip.io
$ curl -k https://192-168-100-100.nip.io -v # 자세한 결과

➜ TLS : 4way handshake



StatefulSet


🌟 파드의 순서고유성(이름) 보장


애완동물 vs 가축 : Stateful vs Stateless

  • 애완동물(Stateless) ➜ 죽으면 대체할 방법 x, 병 걸리면 치료
    ( 고유성 존재 - 자신만의 데이터를 보유 ex. DB 등 포함)
  • 가축(Stateful) ➜ 죽어도 교체 (대체) 가능
  • Stateless, immutable : 상태가 없는, 불변
    ex. 웹서비스, RS, DS, RC, Deploy
    불변의 이미지로 컨테이너를 만들고 컨테이너 장애 발생 시 컨테이너 교체하는 형태

  • Stateful : 상태가 있는
    다운되면 안되는 필수 or 교유한 정보를 포함한 시스템 서버 (다운가능성 존재)
    ex. RDBMS : 관계형 데이터베이스, STS(StatefulSet, 과거 PetSets)
    보통 Master(R/W) / Slave(Read only) 구조로 구성


Headless Service

클러스터 네트워크 내부에서 서비스에 속한 Pod에 직접 접근 가능한 고유 IP와 DNS 생성
로드밸런싱이 필요없거나 단일 서비스 IP가 필요 없는 경우

작업 디렉토리 : ~/yaml/cont/sts/headless

myweb-rs.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myweb-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
      env: dev
  template:
    metadata:
      labels:
        app: web
        env: dev
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb
          ports:
            - containerPort: 8080
              protocol: TCP

myweb-svc.yaml
Cluster IP type 예시

apiVersion: v1
kind: Service
metadata:
  name: myweb-svc
spec:
  type: ClusterIP ✔️
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

myweb-svc-headless.yaml
Headless Service

  • svc.spec.clusterIP
    Headless Service == Cluster IP를 None 으로 설정 == 서비스의 IP 자체가 없는 것
    ➜ 서비스 질의를 하면 파드의 갯수 만큼 응답
apiVersion: v1
kind: Service
metadata:
  name: myweb-svc-headless
spec:
  type: ClusterIP ✔️
  clusterIP: None ✔️  # Headless Service
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

Client 생성해서 확인

$ kubectl run nettool -it --image ghcr.io/c1t1d0s7/network-multitool --rm
# host myweb-svc
# host myweb-svc-headless
> host myweb-svc
myweb-svc.default.svc.cluster.local has address 10.233.17.120

> host myweb-svc-headless
myweb-svc-headless.default.svc.cluster.local has address 10.233.92.74
myweb-svc-headless.default.svc.cluster.local has address 10.233.90.45
myweb-svc-headless.default.svc.cluster.local has address 10.233.96.93



💡 Deployment/RS vs STS

Deployment/RS

모든 파드가 같은 볼륨 에 마운팅
파드가 가지고 있는 데이터 모두 동일 ➜ 고유성 x


STS

각 파드마다 별도의 볼륨 보유 ➜ 고유성 o
ex. DB 파드



StatefulSet 실습

Master와 Slave를 구별하여 Data Write
➜ 🌟 statefulset + headless 서비스 같이 사용 (필수)

$ kubectl api-resources | grep statefulsets    
statefulsets                      sts          apps/v1                                true         StatefulSet

sts.spec.serviceName sts.spec.updateStrategy 확인

실습 1 : STS + Headless Service

작업 디렉토리 : ~/yaml/cont/sts/sts1

myweb-sts.yaml

apiVersion: apps/v1
kind: StatefulSet ✔️
metadata:
  name: myweb-sts
spec:
  replicas: 3
  serviceName: myweb-svc-headless
  selector:
    matchLabels:
      app: web
      env: dev
  template:
    metadata:
      labels:
        app: web
        env: dev
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb
          ports:
            - containerPort: 8080
              protocol: TCP

myweb-svc-headless.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myweb-rs
spec:
  replicas: 3
apiVersion: v1
kind: Service
metadata:
  name: myweb-svc
spec:
apiVersion: v1
kind: Service
metadata:
  name: myweb-svc-headless
spec:
  type: ClusterIP ✔️
  clusterIP: None ✔️ # Headless Service
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

🎈 scaling

$ kubectl scale myweb-sts --relicas 4 # 증가 ➜ 3번 생성
$ kubectl scale myweb-sts --relicas 2 # 감소 ➜ 3번 삭제 후 2번 삭제
$ kubectl scale myweb-sts --relicas 4 # 2번 생성 후 3번 생성

생성 / 삭제에도 순서 존재


🎈 Test 파드 생성해서 ip 호스팅하기

$ kubectl run nettool -it --image ghcr.io/c1t1d0s7/network-multitool --rm

sts + headless svc
고정적으로 파드 이름 생성

Staless 파드는 삭제되면 다른 이름을 가진 파드 생성
🌟 sts 는 파드를 삭제해도 동일 Node동일 ID동일한 이름 으로 재생성
replicas 순서에 따라 이름 뒤에 서수 (0,1,2,...) 붙음
파드 이름으로 쿼리 전송 가능
파드 특정 가능

> host myweb-sts-0.myweb-svc-headless
> host myweb-sts-1.myweb-svc-headless
> host myweb-sts-2.myweb-svc-headless
> host myweb-sts-3.myweb-svc-headless

$ host : 해당 파드의 ip address 정보 출력

🎈 STS 제한사항

  • 볼륨은 무조건 pvc 형태만 사용 가능 -> StorageClass 사용해 프로비저닝
  • 볼륨은 삭제되지 x ➜ 파드가 삭제되어도 데이터 유지기 위함

실습 2 : PVC 템플릿 🌟

작업 디렉토리 : ~/yaml/cont/sts/sts2

myweb-sts-vol.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: myweb-sts-vol
spec:
  replicas: 3
  serviceName: myweb-svc-headless
  selector:
    matchLabels:
      app: web
      env: dev
  template:
    metadata:
      labels:
        app: web
        env: dev
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb:alpine
          ports:
            - containerPort: 8080
              protocol: TCP
          volumeMounts:
            - name: myweb-pvc
              mountPath: /data
  volumeClaimTemplates: ✔️
    - metadata:
        name: myweb-pvc
      spec:
        accessModes:
          - ReadWriteOnce ✔️
        resources: 
          requests:
            storage: 1G
        storageClassName: nfs-client

🎈 STS spec 구성요소

  • sts.spec.template : pod를 생성하기 위한 탬플릿
  • sts.spec.volumeClaimTemplates : pvc 생성을 위한 템플릿
  • sts.spec.volumeClaimTemplates.spec : pvc 설정 탬플릿
    - apiversion, kind 필수 x
    - ReadWriteOnce : 하나의 노드에서 해당 볼륨이 읽기-쓰기로 마운트

생성 확인

$ kubectl get sts,po,pv,pvc
NAME                             READY   AGE
statefulset.apps/myweb-sts-vol   1/3     6s

NAME                                          READY   STATUS    RESTARTS   AGE
pod/myweb-sts-vol-0 ✔️                        1/1     Running   0          6s
pod/myweb-sts-vol-1 ✔️                        0/1     Pending   0          1s
pod/nfs-client-provisioner-758f8cd4d6-svrqw   1/1     Running   0          15h

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                               STORAGECLASS   REASON   AGE
✔️persistentvolume/pvc-3b0546a7-a439-4b60-b552-3f0b562f87ca   1G         RWO            Delete           Bound    default/myweb-pvc-myweb-sts-vol-0   nfs-client              5s
✔️persistentvolume/pvc-969eb7dd-e3be-4084-909a-cc660892fdc2   1G         RWO            Delete           Bound    default/myweb-pvc-myweb-sts-vol-1   nfs-client              0s

NAME                                              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
✔️persistentvolumeclaim/myweb-pvc-myweb-sts-vol-0   Bound    pvc-3b0546a7-a439-4b60-b552-3f0b562f87ca   1G         RWO            nfs-client     6s
✔️persistentvolumeclaim/myweb-pvc-myweb-sts-vol-1   Bound    pvc-969eb7dd-e3be-4084-909a-cc660892fdc2   1G         RWO            nfs-client     1s

pv 개수 == pvc 개수
파드를 delete해도 pv, pvc는 없어지지 않고 파드만 삭제
파드 scaling 하면 해당 이름에 바운딩 되어있던 pvc-pv에 다시 연결


볼륨 고유성 확인

$ kubectl exec myweb-sts-vol-0 -it -- sh
>cd /data
>ls
>touch a b c 
>ls
>>a b c
>exit
$ kubectl exec myweb-sts-vol-1 -it -- sh
>cd /data
>ls
# a b c 없음
>touch x y z
>ls
>>x y z

파드 지웠다가 연결해도 원래 pvc, pv에 그대로 연결하기 때문에 동일 데이터 출력

$ kubectl delete po myweb-sts-vol-1 # 삭제 
$ kubectl get po # 삭제 확인
# 삭제 된 파드와 동일한 이름의 파드 자동 재생성
$ kubectl exec myweb-sts-vol-1 -it -- sh # 연결
>cd /data
>ls
>>x y z   # ➜ 기존 볼륨의 데이터 그대로 존재



실습 3 : STS로 MySQL 구성

🎈 구조

One Master-Multi Slave
➜ RW 가능한 Master DB + 나머지 RO Slave DB

백업을 다른 디스크가 아니라 다른 DB 에 하는 형태
클라이언트는 기본적으로 마스터만 사용
Master가 죽으면 Slave DB를 Master로 승격하여 접속 가능

💡 왜 RS / Deploy를 사용하지 않을까?
RDBMS는 같은 볼륨을 사용하는 구조가 기본적으로 불가함


🎈 생성 과정

실습 과정 상세 기술 페이지 ➜ 들어가서 yaml 파일 내용 확인

작업 디렉토리 : ~/yaml/cont/sts/sts3

파일 다운 받은 후 수정
STS yaml 파일 다운

$ wget https://k8s.io/examples/application/mysql/mysql-statefulset.yaml

mysql-statefulset.yaml

...
80 requests:
81   cpu: 300m
82   memory: 300M
...
167  storageClassName: nfs-client # 이전에 생성해놓았던 sc

Service yaml 파일 다운

$ kubectl apply -f https://k8s.io/examples/application/mysql/mysql-services.yaml

mysql-services.yaml

# Headless service for stable DNS entries of StatefulSet members.
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None
  selector:
    app: mysql
---
# Client service for connecting to any MySQL instance for reads.
# For writes, you must instead connect to the primary: mysql-0.mysql.
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql

서비스 2개(일반/headless) 만들고 같은 레이블 사용 ➜ 같은 파드 가리킴
마스터-슬레이브 구분 위해 headless 서비스 사용

ConfigMap yaml 파일 다운

$ wget https://k8s.io/examples/application/mysql/mysql-configmap.yaml

mysql-configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
  labels:
    app: mysql
data:
  primary.cnf: |
    # Apply this config only on the primary.
    [mysqld]
    log-bin
    # datadir=/var/lib/mysql    
  replica.cnf: |
    # Apply this config only on replicas.
    [mysqld]
    super-read-only
    # datadir=/var/lib/mysql   

✔️ mysql-configmap.yaml에서 datadir=/var/lib/mysql/mysql 부분 둘 다 삭제

ConfigMap, sts, svc 생성

$ kubectl create -f mysql-configmap.yaml   
$ kubectl create -f mysql-statefulset.yaml 

생성 확인

$ kubectl get sts,po,pv,pvc,svc,ep,cm                             
NAME                     READY   AGE
statefulset.apps/mysql   3/3     8m12s

NAME                                          READY   STATUS    RESTARTS        AGE
pod/mysql-0                                   2/2     Running   0               8m11s
pod/mysql-1                                   2/2     Running   1 (3m40s ago)   5m53s
pod/mysql-2                                   2/2     Running   0               3m35s
pod/nfs-client-provisioner-758f8cd4d6-svrqw   1/1     Running   0               16h

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   REASON   AGE
persistentvolume/pvc-7088e9d6-85e2-4689-86ec-7b58e40a5b07   10Gi       RWO            Delete           Bound    default/data-mysql-0   nfs-client              8m11s
persistentvolume/pvc-9707b15f-a3e1-48ca-be82-c65e039d6ca9   10Gi       RWO            Delete           Bound    default/data-mysql-2   nfs-client              3m35s
persistentvolume/pvc-ef570914-bf39-49cc-b847-7dcf538ceebc   10Gi       RWO            Delete           Bound    default/data-mysql-1   nfs-client              5m53s

NAME                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/data-mysql-0   Bound    pvc-7088e9d6-85e2-4689-86ec-7b58e40a5b07   10Gi       RWO            nfs-client     8m12s
persistentvolumeclaim/data-mysql-1   Bound    pvc-ef570914-bf39-49cc-b847-7dcf538ceebc   10Gi       RWO            nfs-client     5m53s
persistentvolumeclaim/data-mysql-2   Bound    pvc-9707b15f-a3e1-48ca-be82-c65e039d6ca9   10Gi       RWO            nfs-client     3m35s

NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)                      AGE
service/kubernetes     ClusterIP      10.233.0.1      <none>            443/TCP                      3d1h
service/mysql          ClusterIP      None            <none>            3306/TCP                     8m12s
service/mysql-read     ClusterIP      10.233.8.236    <none>            3306/TCP                     8m12s
service/nginx-svc-lb   LoadBalancer   10.233.24.166   192.168.100.240   80:32404/TCP,443:31238/TCP   15h

NAME                                                    ENDPOINTS                                               AGE
endpoints/k8s-sigs.io-nfs-subdir-external-provisioner   <none>                                                  16h
endpoints/kubernetes                                    192.168.100.100:6443                                    3d1h
endpoints/mysql                                         10.233.90.47:3306,10.233.92.81:3306,10.233.96.95:3306   8m12s
endpoints/mysql-read                                    10.233.90.47:3306,10.233.92.81:3306,10.233.96.95:3306   8m12s
endpoints/nginx-svc-lb                                  <none>                                                  15h

NAME                         DATA   AGE
configmap/kube-root-ca.crt   1      3d1h
configmap/mysql              2      8m12s
configmap/nginx-tls-config   1      15h

NAME                                     PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
storageclass.storage.k8s.io/nfs-client   k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate           false                  16h

🎈 자동 동기화 Test

Test용 nettool 생성 (Cluster IP type이기 때문)

kubectl run nettool -it --image ghcr.io/c1t1d0s7/network-multitool --rm

1) host 출력 값 개수 다른 것 확인

> host mysql
mysql.default.svc.cluster.local has address 10.233.96.95
mysql.default.svc.cluster.local has address 10.233.92.81
mysql.default.svc.cluster.local has address 10.233.90.47
 
> host mysql-read
mysql-read.default.svc.cluster.local has address 10.233.8.236

2) 데이터베이스 생성 동기화 확인

# Master DB 접속
> mysql -h mysql-0.mysql -u root 

# env로 MYSQL_ALLOW_EMPTY_PASSWORD를 지정해주었기 때문에 패스워드 없이 접속 가능 -> 이 것 때문에 보안에 취약하므로 test용으로만 사용할 것
> MySQL [(none)]> show databases;

> MySQL [(none)]> create database encore; # DB 생성

> MySQL [(none)]> show databases;
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| encore✔️               |
| mysql                  |
| performance_schema     |
| sys                    |
| xtrabackup_backupfiles |
+------------------------+
6 rows in set (0.007 sec)

>MySQL [(none)]> exit
# RO DB 접속
> mysql -h mysql-1.mysql -u root

> MySQL [(none)]> show databases;
# Master DB에서 만들었던 encore DB가 존재
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| encore✔️               |
| mysql                  |
| performance_schema     |
| sys                    |
| xtrabackup_backupfiles |
+------------------------+
6 rows in set (0.009 sec)

> MySQL [(none)]> drop database encore;
# 읽기 전용이므로 drop 불가 ✔️
ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement

> MySQL [(none)]> exit
# Master DB 접속
> mysql -h mysql-0.mysql -u root 

> MySQL [(none)]> show databases;
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| encore✔️               |
| mysql                  |
| performance_schema     |
| sys                    |
| xtrabackup_backupfiles |
+------------------------+
6 rows in set (0.054 sec)

> MySQL [(none)]> drop database encore; # ✔️ DB 삭제
Query OK, 0 rows affected (0.075 sec)

> MySQL [(none)]> show databases;
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| mysql                  |
| performance_schema     |
| sys                    |
| xtrabackup_backupfiles |
+------------------------+
5 rows in set (0.009 sec)

> MySQL [(none)]> exit
# RO DB 접속
> mysql -h mysql-1.mysql -u root

> MySQL [(none)]> show databases;
# encore DB가 삭제되어있음 ✔️
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| mysql                  |
| performance_schema     |
| sys                    |
| xtrabackup_backupfiles |
+------------------------+
5 rows in set (0.006 sec)

> MySQL [(none)]> exit

3) 레코드 생성 후 동기화 확인

# Master DB 접속
> mysql -h mysql-0.mysql -u root 

> MySQL [(none)]> create database encore;
Query OK, 1 row affected (0.016 sec)

> MySQL [(none)]> use encore;
Database changed

# message라는 테이블 생성
> MySQL [encore]> create table encore.message (message VARCHAR(50));
Query OK, 0 rows affected (0.110 sec)

> MySQL [encore]> show tables;
+------------------+
| Tables_in_encore |
+------------------+
| message          |
+------------------+
1 row in set (0.009 sec)

# message 테이블에 내용 입력
> MySQL [encore]> insert into encore.message values ("hello mysql");
Query OK, 1 row affected (0.161 sec)

# message 테이블 내용 확인
> MySQL [encore]> select * from message;
+-------------+
| message     |
+-------------+
| hello mysql |
+-------------+
1 row in set (0.001 sec)

> MySQL [encore]> exit
# RO DB 접속
> mysql -h mysql-1.mysql -u root

> MySQL [(none)]> use encore
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed

# 동기화 되어 테이블 생성되어있음 ✔️
> MySQL [encore]> show tables;
+------------------+
| Tables_in_encore |
+------------------+
| message          |
+------------------+
1 row in set (0.006 sec)

> MySQL [encore]> select * from message;
+-------------+
| message     |
+-------------+
| hello mysql |
+-------------+
1 row in set (0.002 sec)

💡 새로 만들어진 파드에도 레코드 정상적으로 동기화되어 만들어지기 때문에 바로 쿼리 가능

4) 볼륨 마운트 위치 확인
작업 디렉토리 : ~

$ sudo -i
> cd /nfsvolume/
> ls
deault-data-mysql-0-pvc-xxx deault-data-mysql-1-pvc-xxx deault-data-mysql-2-pvc-xxx
> cd deault-data-mysql-0-pvc-xxx
> ls
mysql
> cd mysql
> ls ➜ 여기 내용이 /var/lib/mysql에 마운트

➕ mysql Replicas 스케일링

$ kubectl scale sts mysql --replicas 2

➕ sharding 기법을 쓰면 모든 데이터베이스를 동기화 할 필요는 x
아주 큰 사이즈의 DB 하나만 두고 계속 백업 (백업 필수)
➜ 가장 싸고 간단



Auto Scaling


Resource Request & Limit

리소스 관리(제한)

  • 요청 : request
  • 제한 : limit
    ➜ 요청 / 제한 / 둘 다(보통 권장) 지정 가능

🎈 QoS(Quality of Service, 서비스 품질) Class

  • BestEffort : 가장 나쁨
  • Burstable
  • Guaranteed : 가장 좋음

리소스 할당 우선순위 : Guaranteed > Burstable >BestEffort

  • 요청, 제한 설정 x : BestEffort
  • 요청 < 제한 : Burstable
  • 요청 == 제한 : Guaranteed (Best👍)

🎈 요청 및 제안 특성

요청
스케줄러는 kubelet이 요청한만큼 사용할 수 있도록 보장
➜ 다 사용하든 다 사용하지 않든 상관 x
➜ 요청 용량을 무조건 확보 해서 제공 🌟

제한
최대 로 늘려서 사용할 수 있는 용량
미리 확보 x -> 다른 파드의 request 양에 따라 달라짐

  • pod.spec.containers.resources.requests
    키-벨류 형태

    • cpu
    • memory
  • pod.spec.containers.resources.limits

    • cpu
    • memory

🎈 CPU 요청 & 제한
millicore 단위
ex. 1500m ➜ 1.5개, 1000m ➜ 1개
Memory 요청 & 제한 단위 : M, G, T, T, Mi, Gi, Ti

🎈 Request & Limit 실습

➕ request 만 생성하면 limit 는 자동 세팅되지 x

myweb-reqlim.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-reqlit
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb
      resources:
        requests: ✔️
          cpu: 200m 
          memory: 200M 
        limits: ✔️
          cpu: 200m 
          memory: 200M 

파드 생성

$ kubectl create -f myweb-reqlim.yaml

request == limit

$ kubectl describe po myweb-reqlit
...
QoS Class:                   Guaranteed ✔️
...

request의 memory를 100m으로 수정
request < limit

$ kubectl replace -f myweb-reqlim.yaml --force   

--force 옵션 : 수정 불가인 것을 삭제했다가 다시 만드는 것 (업데이트 x)

$ kubectl describe po myweb-reqlit
...
QoS Class:                   Burstable ✔️
...

pod.spec.containers.resources 필드 삭제
request > limit

$ kubectl replace -f myweb-reqlim.yaml --force   
$ kubectl describe po myweb-reqlit
...
QoS Class:                   BestEffort ✔️
...

각 노드의 CPU / Memory 사용량 확인
$ kubectl top nodes

$ kubectl top nodes                           
NAME    CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
node1   769m         42%    1701Mi          52%   -> 커널이 사용하는 양 빼고 생각
node2   385m         20%    934Mi           36%       
node3   412m         21%    1117Mi          43%    

각 Node에 떠있는 파드 개수 확인
$ kubectl get po -A -o wide | grep [노드 이름] | wc -l
metrics-server 라는 Add-On이 존재하기 때문에 확인 가능
스토리지가 없기 때문에 현재 시점의 리소스 사용량만 확인 가능

$ kubectl get po -A -o wide | grep node1 | wc -l
8
$ kubectl get po -A -o wide | grep node2 | wc -l
10
$ kubectl get po -A -o wide | grep node3 | wc -l
10

🎈 리소스 모니터링

💡 인프라 모니터링 != 애플리케이션 모니터링

  • 리소스 모니터링( == 인프라 모니터링 )
    ➜ 리소스 사용량만 확인

    • metrics-server : 실시간 모니터링
    • Prometheus : 실시간,이전 cpu/memory/network/disk 모니터링
  • 애플리케이션 모니터링

    • http, https 오류 번호 모니터링
    • mysql 쿼리 날렸을 때 성공했는지
    • select 몇 번 성공했는지
    • ...

kube-system NS의 metrics server에 의해 리소스 모니터링 가능

$ kubectl get po -n kube-system  
...
metrics-server-c57c76cf4-lslwb            1/1     Running   6 (7h32m ago)     2d3h
...

Node1 상세 정보 확인
➜ 현재 사용 가능한 용량, 현재 존재하는 파드 수, CPU Request/Limit, 스토리지 등에 대한 정보 확인 가능

$ kubectl describe node node1
...
Capacity:
  cpu:                2
  ephemeral-storage:  40593612Ki
  hugepages-2Mi:      0
  memory:             3927804Ki
  pods:               110 # 노드당 파드 최대 갯수 110개
...
# 현재 존재하는 파드 수
Non-terminated Pods:          (8 in total)
# 리소스 요청 / 제한 확인 - 모델 유추 가능
  Namespace                   Name                              CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                              ------------  ----------  ---------------  -------------  ---
  ingress-nginx               ingress-nginx-controller-6pdbx    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d16h
  kube-system                 calico-node-gztj8                 150m (8%)     300m (16%)  64M (1%)         500M (14%)     2d16h
  kube-system                 kube-apiserver-node1              250m (13%)    0 (0%)      0 (0%)           0 (0%)         2d16h
  kube-system                 kube-controller-manager-node1     200m (11%)    0 (0%)      0 (0%)           0 (0%)         2d16h
  kube-system                 kube-proxy-48xd9                  0 (0%)        0 (0%)      0 (0%)           0 (0%)         37h
  kube-system                 kube-scheduler-node1              100m (5%)     0 (0%)      0 (0%)           0 (0%)         2d16h
  kube-system                 nodelocaldns-zwssl                100m (5%)     0 (0%)      70Mi (2%)        170Mi (5%)     2d16h
  metallb-system              speaker-nkzm8                     0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d16h
...
ephemeral-storage  0 (0%)          0 (0%) # 임시 스토리지 (## 와 동일)

파드별 리소스 사용량 확인

$ kubectl top pods

실행할 수 없는 리소스
➜ 감당 불가능한 리소스
myweb-big.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-reqlit
spec:
  containers:
    - name: myweb-big
      image: ghcr.io/c1t1d0s7/go-myweb
      resources:
        requests:
          cpu: 100m
          memory: 200M
        limits:
          cpu: 3000m ✔️ 
          memory: 4000M ✔️



HPA : Horizontal Pod AutoScaler

Scale IN/OUT

🎈 AutoScaling

  • Pod
    • HPA : Deployment / RS / STS 복제본 개수(spec.replicas) 조정
    • VPA : Vertical Pod Autoscaling (request / limit Add-On으로 자동 구현, 잘 사용 x)
  • Node
    • ClusterAutoScaler : Worker Node 자동으로 할당 가능 (Add-On)
      ➜ AWS EKS에서 구현해 볼 예정

🎈 HPA 리소스 정보

$ kubectl api-resources | grep hpa 
horizontalpodautoscalers          hpa          autoscaling/v1                         true         HorizontalPodAutoscaler

hpa.spec.maxReplicas ➜ integer
최대 복제 개수

hpa.spec.scaleTargetRef

  • kind : (required) string
  • name : (required) string

hpa.spec.targetCPUUtilizationPercentage ➜ integer
cpu가 얼마를 넘어가면 스케일링을 하는가


🎈 레플리카 수 계산

원하는 레플리카 수 = ceil[현재 레플리카 수 * ( 현재 메트릭 값 / 원하는 메트릭 값 )]

ceil : 올림 함수(천장 함수)
가정 ➜ 현재 cpu : 100% / 원하는 cpu 퍼센트 : 50% / 현재 레플리카 : 3개 / maxReplicas : 10개
➜ 원하는 레플리카 수 : 6개
✔️ 주의! maxReplicas 수 넘지 x


🎈 스케일링의 안정성 (thrashing or flapping)
아무 것도 안하는 상태로 70% 이하 가 되면 최소까지 scale in
안정화 윈도우 사용
이전의 목표 상태를 추론하고 워크로드 수의 원치 않는 변화를 방지 (기본 셋팅 : 300초)


🎈 HPA 실습

myweb-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myweb-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb
          ports:
            - containerPort: 8080
          resources:     ✔️# 이 부분 없었다가 2차 오류 발생 후 수정함
            requests:
              cpu: 200m
            limits:
              cpu: 200m   

myhpa.yaml

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler ✔️
metadata:
  name: myweb-hpa
spec:
  minReplicas: 1
  maxReplicas: 10 ✔️
  targetCPUUtilizationPercentage: 50 ✔️
  scaleTargetRef: ✔️
    apiVersion: apps/v1 # 추가했어야했음 ✔️
    kind: Deployment
    name: myweb-deploy

Deployment, HPA 생성

$ kubectl create -f mydeploy.yaml -f myhpa.yaml 

생성 확인

$ kubectl get deploy,rs,po,hpa
...
# hpa
NAME                                            REFERENCE                 TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/myweb-hpa   Deployment/myweb-deploy   <unknown>/50%   1         10        0          18s

REPLICAS : 현재 복제본 개수


🎈 오류

1차 오류 : TARGETS : unknown 상태가 바뀌지 않는 오류 발생
➜ hpa 이벤트 로그 확인
원인 : apiVersion: apps/v1 추가했어야했음

$  kubectl describe hpa myweb-hpa
Events:
  Type     Reason          Age                  From                       Message
  ----     ------          ----                 ----                       -------
  Warning  FailedGetScale  8s (x10 over 2m23s)  horizontal-pod-autoscaler  no matches for kind "Deployment" in group ""

2차 오류 : apiVersion 수정하고도 TARGETS : unknown 상태 바뀌지 x
원인 : request 지정 되어있지 않았기 때문(missing request for cpu)

 Type     Reason                        Age               From                       Message
  ----     ------                        ----              ----                       -------
  ...
  horizontal-pod-autoscaler  invalid metrics (1 invalid out of 1),
  first error is: failed to get cpu utilization: missing request for cpu ✔️

🌟 limit는 없어도 되지만 request 지정은 필수


배포한 파드 리소스 사용량 확인

$ kubectl top pods   
NAME                                      CPU(cores)   MEMORY(bytes)   
myweb-deploy-6dcf6c95c6-kg8gx             0m           1Mi             
myweb-deploy-6dcf6c95c6-vtwvj             0m           1Mi             

부하 걸기

$ kubectl exec myweb-deploy-6dcf6c95c6-kg8gx -- sha256sum /dev/zero

cpu 부하 확인

$ kubectl top pods
NAME                                      CPU(cores)   MEMORY(bytes)   
myweb-deploy-6dcf6c95c6-kg8gx             198m ✔️      2Mi             
myweb-deploy-6dcf6c95c6-vtwvj             0m           1Mi             

실시간 파드 스케일링 확인
➜ 다른 터미널에서 진행

$ watch kubectl get po,hpa

💡 Scale IN / Scale OUT

  • Scale IN : 300초
  • Scale OUT : 180초

🎈 v2beta2 버전 AutoScaling

myhpa-v2-beta2.yaml

apiVersion: autoscaling/v2beta2 ✔️
kind: HorizontalPodAutoscaler
metadata:
  name: myweb-hpa-beta2
spec:
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
  targetCPUUtilizationPercentage: 50
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myweb-deploy

Pending : 스케줄링 중 / 이미지 풀링 중 / 볼륨 연결 중

profile
Be on CLOUD nine! ☁️ ( 수정 예정 == 📌)

0개의 댓글