71/120

김건호·2022년 5월 25일
0

TLS/SSL Termination with Ingress

SSL 더이상 사용하지 않음

TLS만 사용 상징적인 의미로 SSL 용어를 사용

end to end 암호화

client <--https--> LB <--https--> Server

TLS Termination


프라이빗 네트워크가 있고 그 안에 암호화를 제공하지 않는 평문 통신구간이 있음
end to end 방식보다 선호 됨

장점

  • 프록시와 서버간의 암복호화를 하지 않아서 서버에 암복호화 부담이 적음
  • 인증서를 서버별로 관리하지 않아도 됨
  • 보안장비를 프록시에만 설정할 수 있음

위 모든 내용은 프라이빗 네트워크를 외부에서 접근할 수 없다는 전제 조건이 필요

end to end가 더 안전한거 같은데 왜 TLS Termination❓

  • 만약 클라이언트가 서버를 공격하는 트래픽을 보낸다면 트래픽을 막을 보안장비(WAF같은)가 필요한데 end to end는 암호화가 서버에서 끝나기때문에 프록시에서 복호화를 하지 않고 무조건 서버로 트래픽을 보내야함, 하지만 TLS Termination은 복호화가 프록시에서 종료되기 때문에 공격하는 트래픽을 서버까지 보내지 않아도 됨 -> TLS Termination이 보다 안전
  • end to end에서 서버마다 보안장비를 설치한다면 관리가 매우 힘듬

쿠버네티스에선 Ingress가 TLS Termination를 지원

ing.spec.tls

ing.spec.tls.hosts -> 디폴트는 모든호스트
도메인
ing.spec.tls.secretName
인증서가 있는 시크릿의 이름을 참조

client <--> ingress <--> svc <--> Pods

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-ing-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
curl -k https://192-168-100-100.nip.io

-v 옵션 TLS 4way Handshacke을 볼 수 있음
-k 옵션 https 사이트를 SSL certificate 검증없이 연결

Statefulset

파드의 순서와 고유성(이름이 고유하다)을 보장

  • 파드의 순서 : sts에 의해서 생성된 파드는 기본적으로 뒤에 서수가 붙게 됨
  • 고유성 : host myweb-sts-0.default.. 파드의 이름으로 쿼리를 보낼 수 있음
    리소스마다 id가 붙는데 id도 똑같은 거로 붙음
    id가 똑같다 -> 배치되는 노드가 똑같다
    하나의 리소스를 만들때 id가 부여되면 이 id는 특정 노드에서만 실행될 수 있음

스테이트풀셋 사용

  • 고유한 네트워크 식별자 (DNS) 파드와 함께쓰면 특정파드는 이름이 동일하다
  • 지속성을 가지고 있는 스토리지(다음)
  • 롤링업데이트
  • 스케일링

Headless Service

apiVersion: v1
kind: Service
metadata:
  name: myweb-svc-headless
spec:
  type: ClusterIP
  clusterIP: None # <-- Headless Service
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

spec.clusterIPnone인 서비스 -> 서비스의 IP가 없음

host myweb-svc-headless

서비스의 IP가 없기 때문에 파드의 IP를 가르킴


sts.spec

serviceName headless 서비스의 이름을 지정

제한사항

  • STS 에 붙이는 스토리지는 반드시 PVC이여야함 -> 스토리지 클래스 존재해야함
    -> 고유성이 있으려면 파드마다 별도의 볼륨을 가져야만함
    RS와 Deployment로 생성된 파드는 하나의 볼륨을 가르킴 -> 고유성이 없음

sts.spec.volumeClaimTemplates

PVC를 만들때 사용할 템플릿
파드가 삭제되어도 볼륨 삭제가 되지 않음 -> 다시 스케일하거나 만들때 고유한 데이터 사용하기 위해

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
              mounthPath: /data
  volumeClaimTemplates: # s가 붙으면 대부분 리스트
    - metadata:
        name: myweb-pvc
    spec:
      accessModes:
        - ReadWriteOnce # 얘는 파드가 자기만의 공간을 사용하기 때문에 굳이 Many상관없음
      resoucres:
        requests:
          storage: 1G
        storageClassName: nfs-client
        

스케일링 시 파드는 줄지만 pv와 pvc는 줄지 않음

sts를 쓰는 상황

데이터베이스 서버에서 마스터(RW) 슬레이어(RO) 같은 구성을 둘 때 쓰기만 가능한 마스터 데이터베이스 파드를 구분해야 할때 사용
https://kubernetes.io/docs/tasks/run-application/run-replicated-stateful-application/

서비스

# 마스터 (RW)
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None
  selector:
    app: mysql
---
# 슬레이어 (RO)
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql

sts

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 3
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Generate mysql server-id from pod ordinal index.
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # Add an offset to avoid reserved server-id=0 value.
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          # Copy appropriate conf.d files from config-map to emptyDir.
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/primary.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/replica.cnf /mnt/conf.d/
          fi          
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      - name: clone-mysql
        image: gcr.io/google-samples/xtrabackup:1.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Skip the clone if data already exists.
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # Skip the clone on primary (ordinal index 0).
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # Clone data from previous peer.
          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
          # Prepare the backup.
          xtrabackup --prepare --target-dir=/var/lib/mysql          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ALLOW_EMPTY_PASSWORD
          value: "1"
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            # Check we can execute queries over TCP (skip-networking is off).
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql

          # Determine binlog position of cloned data, if any.
          if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
            # XtraBackup already generated a partial "CHANGE MASTER TO" query
            # because we're cloning from an existing replica. (Need to remove the tailing semicolon!)
            cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
            # Ignore xtrabackup_binlog_info in this case (it's useless).
            rm -f xtrabackup_slave_info xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            # We're cloning directly from primary. Parse binlog position.
            [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
            rm -f xtrabackup_binlog_info xtrabackup_slave_info
            echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
                  MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
          fi

          # Check if we need to complete a clone by starting replication.
          if [[ -f change_master_to.sql.in ]]; then
            echo "Waiting for mysqld to be ready (accepting connections)"
            until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done

            echo "Initializing replication from clone position"
            mysql -h 127.0.0.1 \
                  -e "$(<change_master_to.sql.in), \
                          MASTER_HOST='mysql-0.mysql', \
                          MASTER_USER='root', \
                          MASTER_PASSWORD='', \
                          MASTER_CONNECT_RETRY=10; \
                        START SLAVE;" || exit 1
            # In case of container restart, attempt this at-most-once.
            mv change_master_to.sql.in change_master_to.sql.orig
          fi

          # Start a server to send backups when requested by peers.
          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
            "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

AutoScaling

Resource Request & Limit

요청: request
제한: limit

요청 =< 제한

QoS(서비스 품질) Class:
1. BestEffort: 가장 나쁨
2. Burstable
3. Guaranteed: 가장 좋음

  • 요청/제한 설정되어 있지 않으면: BestEffort
  • 요청 < 제한: Bustable
  • 요청 = 제한: Guaranteed

pod.spec.containers.resources

  • requests
    - cpu
    - memory
  • limits
    - cpu
    - memory

CPU 요청 & 제한: milicore
ex) 1500m -> 1.5개, 1000m -> 1개
ex) 1.5, 0.1
Memory 요청 & 제한: M, G, T, Mi, Gi, Ti

myweb-reqlim.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-reqlim
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb
      resources:
        requests:
          cpu: 200m
          memory: 200M
        limits:
          cpu: 200m
          memory: 200M
          
 kubectl describe -f myweb-reqlim.yaml         
    Limits:
      cpu:     200m
      memory:  200M
    Requests:
      cpu:        200m
      memory:     200M

request값은 수정이 안됨 -> 리소스값은 수정이 안됨
--force 옵션 붙이면 가능은 함 기존거 삭제하고 새로만듬

노드별 CPU/Memory 사용량 확인

kubectl top nodes

NAME    CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
node1   690m         38%    1297Mi          40%
node2   253m         13%    872Mi           34%
node3   263m         13%    967Mi           37%

파드별 CPU/Memory 사용량 확인

kubectl top pods
kubectl top pods -A

NAME           CPU(cores)   MEMORY(bytes)
myweb-reqlim   0m           1Mi

리소스 모니터링(인프라 모니터링)
Heapster:
-> metric-server: 실시간 cpu/memory 모니터링
-> prometheus: 실시간/이전 cpu/memory/network/disk 모니터링
https://github.com/kubernetes-sigs/metrics-server
https://github.com/kubernetes-retired/heapster

노드별 요청/제한 양 확인

kubectl describe nodes node1
...
Capacity:
  cpu:                2
  ephemeral-storage:  40593612Ki
  hugepages-2Mi:      0
  memory:             3927804Ki
  pods:               110
Allocatable:
  cpu:                1800m
  ephemeral-storage:  37411072758
  hugepages-2Mi:      0
  memory:             3301116Ki
  pods:               110

Non-terminated Pods:
  실행중인 파드들을 확인할 수 있음 11개

실행 할 수 없는 리소스 -> 현재 시스템보다 요청과 제한이 높기 때문에
myweb-big.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-big
spec:
  containers:
    - name: myweb
      image: ghcr.io/c1t1d0s7/go-myweb
      resources:
        limits:
          cpu: 3000m
          memory: 4000M
          
          
Limits:
      cpu:     3
      memory:  4G
    Requests:
      cpu:        3
      memory:     4G

리미트만 설정했는데 -> 리미트와똑같은 자동으로 리퀘스트 생성됨
리퀘스트만 하면 리미트 세팅은 안됨

HPA: Horisontal Pod AutoScaler

https://kubernetes.io/ko/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/

 vagrant@k8s-node1  ~/ac  kubectl api-resources | grep hpa
horizontalpodautoscalers   hpa   autoscaling/v1   true    HorizontalPodAutoscaler

AutoScaling

  • Pod
    - HPA
    - VPA: Vertical Pod Autoscaler
  • Node
    - ClusterAutoScaler 필요하면 워커노드를 더 할당할수잇음
    모든 클라우드가 지원 애드온임

HPA: Deployment, ReplicaSet, StatefulSet의 복제본 개수를 조정

스케일 아웃: 180초
스케인 인: 300초

hpa.spec

KIND:     HorizontalPodAutoscaler
VERSION:  autoscaling/v1

FIELDS:
   maxReplicas  <integer> -required-
    파드의 최대 개수
   minReplicas  <integer>
    파드의 최소 개수
   scaleTargetRef       <Object> -required-
     오토스케일링할 리소스에 관한 내용

   targetCPUUtilizationPercentage       <integer>
     사용률 넘으면 스케일링 할지 어떤 대상에게

hpa.spec.scaleTargetRef

FIELDS:
   apiVersion   <string>
     

   kind <string> -required-
     리소스 종류

   name <string> -required-
     리소스 이름

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:alpine
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 200m
            limits:
              cpu: 200m

HPA를 위해 myweb-deploy 시스템에 최소 request는 설정되여 함

myweb-hpa.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
 vagrant@k8s-node1  ~/ac/hpa  kubectl get deploy,hpa
NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/myweb-deploy             2/2     2            2           52s
deployment.apps/nfs-client-provisioner   0/1     1            0           31h

NAME                                            REFERENCE                 TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/myweb-hpa   Deployment/myweb-deploy   0%/50%    1         10        2          52s

부하

kubectl exec <POD> -- sha256sum /dev/zero

원하는 레플리카 수 구하기

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

ceil = 올림


 vagrant@k8s-node1  ~/ac/hpa  kubectl explain hpa.spec --api-version autoscaling/v2beta2
KIND:     HorizontalPodAutoscaler
VERSION:  autoscaling/v2beta2

RESOURCE: spec <Object>

FILEDS:
  metrics      <[]Object>
  CPU뿐만 아니라 다양한 metric 참조 가능
  
vagrant@k8s-node1  ~/ac/hpa  kubectl explain hpa.spec.metrics --api-version autoscaling/v2beta2  


   external     <Object>
     
   object       <Object>
     
   pods <Object>
   
   resource     <Object>
   
   type <string> -required-
        "ContainerResource",
     "External", "Object", "Pods" or "Resource"

myweb-hpa-v2beta2.yaml

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: myweb-hpa
spec:
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          avarageUtilization: 50
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myweb-deploy
profile
Ken, 🔽🔽 거노밥 유튜브(house icon) 🔽🔽

0개의 댓글