home..

인그레스, 스토리지

kubernetes ingress storage msa mont-kim aws eks kops PKOS gasida CloudNet

3주차 Ingress, Storage

Ingress

인그레스는 클러스터 내부에 존재하는 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출하는 역할을 한다.

인그레스 종류 인그레스의 구현방법은 여러가지가 있고 사용편의성도 다르다

인그레스 리소스를 생성했을때 처리를 담당하는 컨트롤러다. 어떤 L7 로드밸런서를 생성할지는 이 인그레스 컨트롤러에 따라 달라진다

또한 인그레스는 다음과같이 크게 두가지로 분류된다

클러스터 외부 로드밸런서를 사용한 인그레스와

클러스터 외부 로드밸런서를 사용한 인그레스

클라우드에서 사용하는 클러스터 외부 로드밸런서를 사용한 인그레스의 경우 인그레스 리소스 생성만으로 로드밸런서의 가상IP가 할당되어 사용 할 수 있다.

따라서 인그레스의 트래픽은 ALB가 트래픽을 수신한 후 ALB에서 SSL 터미네이션이라 경로 기반 라우팅을 통해 Nodeport에 트래픽을 전송함으로써 대상 파드까지 도달한다

순서를 간략하게 하면 다음 두단계를 통해 전달된다.

  1. 클라이언트
  2. L7 로드밸런서 (NodePort 경유)
  3. 목적지 파드

클러스터 내부 인그레스용 파드를 배포하는 인그레스

대표적으로 ingress nginx controller가 존재한다.

클러스터 내부에 인그레스용 파드를 배포하는 인그레스 패턴은 인그레스 리소스에 정의한 L7 수준의 로드밸런싱 처리를 하기 위해 인그레스용 파드를 클러스터 내부에 생성해야한다.

또 생성한 인그레스용 파드르에 대해 클러스터 외부에서 접속할수있도록 별도로 인그레스용 파드에 LoadBalancer 서비스를 생성하는등의 준비가 필요하

그리고 인그레스용 파드가 SSL 터미네이션이나 경록로 기반 라우팅과 같은 L7 수준의 처리를 하기위해 부하에 따른 파드 레플리카 수의 오토스케일링도 고려해야한다.

인그레스용 파드에 ngnix를 사용한 nginx ingress 의 경우 로드밸런서가 nginx 파드까지 전송하고, 그다음에는 nginx가 L7 수준의 처리를 수행할 파드에 전송한다. 이때 nginx 파드에서 대상 파드까지는 nodeport를 통과하지않고 직접 파드 ip 주소로 전송된다.

순서를 간략하게하면 다음 세단계를 통해 전달된다.

  1. 클라이언트
  2. L4 로드밸런서 (type loadbalancer)
  3. nginx pod(nignx ingress controller)
  4. 목적지 파드

이번 실습의 경우 클라우드상에서 실습을 진행하기때문에

LoadBalancer를 이용하여 AWS Load Balncer Controller + ingress (ALB) IP 로 동작을 구현했다.

온프레미스에서의 Ingress는 대표적으로 ingress ngnix controller를 제일 많이 사용하는듯 하다.

# Load Balancer IAM 부여
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --role-name nodes.$KOPS_CLUSTER_NAME

# External DNS IAM 부여
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name nodes.$KOPS_CLUSTER_NAME

kops edit cluster
-----
spec:
  certManager:
    enabled: true
  awsLoadBalancerController:
    enabled: true
  externalDns:
    provider: external-dns
-----

kops update cluster --yes && echo && sleep 3 && kops rolling-update cluster

사전에 생성했던 IAM Policy를 부여하고

kops 클러스터 정보를 수정후 업데이트를 해주면 권한을 부여할 수 있다.

과제 1

Ingress(with 도메인, 단일 ALB 사용)에 PATH /mario 는 mario 게임 접속하게 설정하고, /tetris 는 tetris 게임에 접속하게 설정하고, SSL 적용 후 관련 스샷 올려주세요

우선 mario, tetris 코드는

MSA 샘플 애플리케이션 (13종)

에 공개되어있는 코드를 이용했다.

  • Mario

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: mario
        namespace: games
        labels:
          app: mario
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: mario
        template:
          metadata:
            labels:
              app: mario
          spec:
            containers:
            - name: mario
              image: pengbai/docker-supermario
      ---
      apiVersion: v1
      kind: Service
      metadata:
         name: mario
         namespace: games
         annotations:
           alb.ingress.kubernetes.io/healthcheck-path: /mario/index.html
      spec:
        selector:
          app: mario
        ports:
        - port: 80
          protocol: TCP
          targetPort: 8080
        type: NodePort
        externalTrafficPolicy: Local
    
  • Tetris

      # tetris.yaml
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: tetris
        namespace: games
        labels:
          app: tetris
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: tetris
        template:
          metadata:
            labels:
              app: tetris
          spec:
            containers:
            - name: tetris
              image: bsord/tetris
      ---
      apiVersion: v1
      kind: Service
      metadata:
         name: tetris
         namespace: games
      	 annotations:
           alb.ingress.kubernetes.io/healthcheck-path: /tetris/index.html
      spec:
        selector:
          app: tetris
        ports:
        - port: 80
          protocol: TCP
          targetPort: 80
        type: NodePort
    

mario와 tetris 서비스와 인그레스로 loadbalancer를 생성할때

loadbalancer에서 health check를 진행한다.

이때 health check에 사용되는 index file의 위치를 별도로 지정해주지않으면 헬스체크에 실패하기때문에

각 서비스들로 별도의 annotation을 지정해 헬스체크파일의 위치를 선언해준다.

  • Ingress

      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        namespace: games
        name: games-ingress
        annotations:
          alb.ingress.kubernetes.io/scheme: internet-facing
          alb.ingress.kubernetes.io/target-type: ip
          alb.ingress.kubernetes.io/certificate-arn: ${CERT_ARN}
          alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
          alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
          alb.ingress.kubernetes.io/healthcheck-port: traffic-port
          alb.ingress.kubernetes.io/healthcheck-interval-seconds: '15'
          alb.ingress.kubernetes.io/healthcheck-timeout-seconds: '5'
          alb.ingress.kubernetes.io/success-codes: '200'
          alb.ingress.kubernetes.io/healthy-threshold-count: '2'
          alb.ingress.kubernetes.io/unhealthy-threshold-count: '2'
      spec:
        ingressClassName: alb
        rules:
          - host: ${WEBDOMAIN}
            http:
              paths:
              - path: /tetris
                pathType: Prefix
                backend:
                  service:
                    name: tetris
                    port:
                      number: 80
              - path: /mario
                pathType: Prefix
                backend:
                  service:
                    name: mario
                    port:
                      number: 80
    

HTTPS 인증서를 사용하기위해 CERT_ARN 키를 이용하고

https 관련 코드들을 추가한다.

kubectl create namespace games
kubectl namespace games

# 애플리케이션 배포
kubectl apply -f mario.yaml
kubectl apply -f tetris.yaml

# 인증서 가져오기
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`

# External DNS 등록하기
WEBDOMAIN=game.mont-kim.copm

WEBDOMAIN=$WEBDOMAIN envsubst < ingress.yaml | kubectl apply -f -

CERT_ARN 동적으로 선언할경우, CERT_ARN이 여러개 등록되어있을때 오동작 할 수 있다.

이경우 수동으로 등록을 진행해야합니다

위에 말씀드린 Ingress의 특징대로

Storage

과제 2

hostPath 고치기

hostpath 형태의 데이터 저장소의 문제점

  • 파드 배포된 워커노드 drain 해서 문제 확인 → 다시 원복
  • local-path.yaml

      apiVersion: v1
      kind: Namespace
      metadata:
        name: local-path-storage
        
      ---
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: local-path-provisioner-service-account
        namespace: local-path-storage
        
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRole
      metadata:
        name: local-path-provisioner-role
      rules:
        - apiGroups: [ "" ]
          resources: [ "nodes", "persistentvolumeclaims", "configmaps" ]
          verbs: [ "get", "list", "watch" ]
        - apiGroups: [ "" ]
          resources: [ "endpoints", "persistentvolumes", "pods" ]
          verbs: [ "*" ]
        - apiGroups: [ "" ]
          resources: [ "events" ]
          verbs: [ "create", "patch" ]
        - apiGroups: [ "storage.k8s.io" ]
          resources: [ "storageclasses" ]
          verbs: [ "get", "list", "watch" ]
        
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRoleBinding
      metadata:
        name: local-path-provisioner-bind
      roleRef:
        apiGroup: rbac.authorization.k8s.io
        kind: ClusterRole
        name: local-path-provisioner-role
      subjects:
        - kind: ServiceAccount
          name: local-path-provisioner-service-account
          namespace: local-path-storage
        
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: local-path-provisioner
        namespace: local-path-storage
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: local-path-provisioner
        template:
          metadata:
            labels:
              app: local-path-provisioner
          spec:
        
            nodeSelector:
              kubernetes.io/hostname: "i-0d6c79429bae74919"
            tolerations:
              - effect: NoSchedule
                key: node-role.kubernetes.io/control-plane
                operator: Exists
            serviceAccountName: local-path-provisioner-service-account
            containers:
              - name: local-path-provisioner
                image: rancher/local-path-provisioner:v0.0.23
                imagePullPolicy: IfNotPresent
                command:
                  - local-path-provisioner
                  - --debug
                  - start
                  - --config
                  - /etc/config/config.json
                volumeMounts:
                  - name: config-volume
                    mountPath: /etc/config/
                env:
                  - name: POD_NAMESPACE
                    valueFrom:
                      fieldRef:
                        fieldPath: metadata.namespace
            volumes:
              - name: config-volume
                configMap:
                  name: local-path-config
        
      ---
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: local-path
      provisioner: rancher.io/local-path
      volumeBindingMode: WaitForFirstConsumer
      reclaimPolicy: Delete
        
      ---
      kind: ConfigMap
      apiVersion: v1
      metadata:
        name: local-path-config
        namespace: local-path-storage
      data:
        config.json: |-
          {
                  "nodePathMap":[
                  {
                          "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
                          "paths":["/data/local-path"]
                  }
                  ]
          }
        setup: |-
          #!/bin/sh
          set -eu
          mkdir -m 0777 -p "$VOL_DIR"
        teardown: |-
          #!/bin/sh
          set -eu
          rm -rf "$VOL_DIR"
        helperPod.yaml: |-
          apiVersion: v1
          kind: Pod
          metadata:
            name: helper-pod
          spec:
            containers:
            - name: helper-pod
              image: busybox
              imagePullPolicy: IfNotPresent
    
  • localpath-fail.yaml

      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: localpath-claim
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 2Gi
        storageClassName: "local-path"
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: date-pod
        labels:
          app: date
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: date
        template:
          metadata:
            labels:
              app: date
          spec:
            terminationGracePeriodSeconds: 3
            containers:
            - name: app
              image: centos
              command: ["/bin/sh"]
              args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
              volumeMounts:
              - name: pod-persistent-volume
                mountPath: /data
            volumes:
            - name: pod-persistent-volume
              persistentVolumeClaim:
                claimName: localpath-claim
    

위에 첨부한 local-path.yaml파일을 통해

local path를 위한 사전작업을 실행한다.

Deployment에 nodeselector와 configmap의 데이터 path는 별도로 수정이 필요하다

kubectl apply -f ~/pkos/3/localpath-fail.yaml

위에 첨부한 yaml파일은 local-path를 이용한

# 모니터링
watch kubectl get pod,pv,pvc -owide

# 디플로이먼트
kubectl apply -f ~/pkos/3/localpath-fail.yaml

# 배포 확인
kubectl exec deploy/date-pod -- cat /data/out.txt

# 파드가 배포된 워커노드 변수 지정
PODNODE=$(kubectl get pod -l app=date -o jsonpath={.items[0].spec.nodeName})
echo $PODNODE
i-0898acf5c6da94a3e

# 파드가 배포된 워커노드에 장애유지 보수를 위한 drain 설정
kubectl drain $PODNODE --force --ignore-daemonsets --delete-emptydir-data && kubectl get pod -w

# 상태 확인
kubectl get node
kubectl get deploy/date-pod
kubectl describe pod -l app=date | grep Events: -A5

# local-path 스토리지클래스에서 생성되는 PV 에 Node Affinity 설정 확인
kubectl describe pv
...
Node Affinity:
  Required Terms:
    Term 0:        kubernetes.io/hostname in [i-0898acf5c6da94a3e]
...

# 파드가 배포된 워커노드에 장애유지 보수를 완료 후 uncordon 정상 상태로 원복 Failback
kubectl uncordon $PODNODE && kubectl get pod -w
kubectl exec deploy/date-pod -- cat /data/out.txt
  • 다음 실습을 위해서 파드와 PVC 삭제

결국 hostpath로 pv를 이용할경우

  1. 노드가 바뀔경우 데이터 저장소를 읽을수 없는 문제가 발생한다.

그리고 아마도 같은 path에 중복으로 hostpath를 생성할경우 데이터가 중복될것이라 예상한다.

Untitled

볼륨

Untitled

공식 홈페이지에서도 단일노드에서 임시적으로 사용하는경우에만 권하고있으며

사용자체를 권장하는편이 아니다.

가급적으로 사용을 할경우 ReadOnly 옵션을 이용해서 데이터를 읽는경우에 사용을 권장하는데,

이경우엔 Configmap을 이용하는게 더 편할지도 모르겠다

kubestr 성능테스트

Kubestr

Kubestr을 이용해 스토리지의 성능테스트를 진행 할 수 있다.

단위는 IOPS로 스토리지별 성능차이를 확인 할 수 있다.

이번 실습은 NVME SSD로 프로비저닝되어 local-path를 확인하기위해 C5d.Large로 프로비저닝을 진행했다.

설치

wget https://github.com/kastenhq/kubestr/releases/download/v0.4.36/kubestr_0.4.36_Linux_amd64.tar.gz
tar xvfz kubestr_0.4.36_Linux_amd64.tar.gz && mv kubestr /usr/local/bin/ && chmod +x /usr/local/bin/kubestr

성능지표

rrqm/s : 디바이스 큐에 대기중인 초당 읽기 요청 건수

wrqm/s : 디바이스 큐에 대기중인 초당 쓰기 요청 건수

r/s : 디바이스에 요청한 초당 읽기 요청 건수

rMB/s : 디스크에 읽은 MB 수

wMB/s : 디스크에서

await: 디바이스에서 처리에 필요한 평균 IO시간 (평균응답시간)

Read test

curl -s -O https://raw.githubusercontent.com/wikibook/kubepractice/main/ch10/fio-read.fio
kubestr fio -f fio-read.fio -s local-path --size 10G

명령어를 실행하면 내부에 pod이 생겨 10G 사이즈를 읽는데 걸리는 속도를 측정한다.

Untitled


curl -s -O https://raw.githubusercontent.com/wikibook/kubepractice/main/ch10/fio-write.fio
kubestr fio -f fio-write.fio -s local-path --size 10G

Untitled

읽기와 쓰기 모두 IOPS가 3023이 나왔는데

4K 디스크 블록 기준 보편적으로 우리가 사용하는 MB단위로 환산을하면

3203 IOPS * 4KiB 1024 = 12.5MiB

nvme ssd를 이용했지만 물리적인 PC에서 체감가능한 속도와는 차이가 꽤 있는듯하다.

분명 Read는 더 높은 성능이 나온다고 했던거같은데…

항상 그런것은 아닌가보다

과제 3

목표 : AWS EBS를 PVC로 사용 후 온라인 볼륨 증가 후 관련 스샷 올려주세요

AWS EBS를 PVC로 사용후 볼륨 증가시키기

  • awsebs-pvc.yaml

      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: ebs-claim
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 4Gi
    
  • absebs-pod.yaml

      apiVersion: v1
      kind: Pod
      metadata:
        name: app
      spec:
        terminationGracePeriodSeconds: 3
        containers:
        - name: app
          image: centos
          command: ["/bin/sh"]
          args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
          volumeMounts:
          - name: persistent-storage
            mountPath: /data
        volumes:
        - name: persistent-storage
          persistentVolumeClaim:
            claimName: ebs-claim
    

AWS EBS를 4Gi 로 생성해 이 PV를 붙인 POD를 생성한다

# 파드 내에서 볼륨 정보 확인
kubectl exec -it app -- sh -c 'df -hT --type=ext4'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme1n1   ext4  3.9G   16M  3.8G   1% /data
/dev/root      ext4  124G  4.9G  120G   4% /etc/hosts

이제 볼륨을 10기가로 증설해본다!

kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'

다시 볼륨정보를 확인해보면

10기가로 늘어난것을 확인 할 수 있다.

Untitled

다만 주의점은 한번 패치된 볼륨은 6시간동안 변경이 제한된다.

과제 4

목표 : AWS Volume SnapShots 실습 후 관련 스샷 올려주세요

snapshot controller 설치

# kOps 클러스터 편집
kops edit cluster
-----
spec:
  snapshotController:
    enabled: true
-----

kops update cluster --yes && sleep 3 && kops rolling-update cluster

볼륨 snapshot을 생성하는덴 snapshotcontroller와 certmanager가 활성화 되어있어야한다.

certmanager는 위에서 생성했기때문에 snapshotController만 추가한다

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/examples/kubernetes/snapshot/manifests/classes/snapshotclass.yaml

kubectl get volumesnapshotclass

공식 사이트에있는 aws-ebs-csi 드라이버를 이용해 볼륨 스냅샷을 만든다.

volumesnapshot.yaml

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: ebs-volume-snapshot
spec:
  volumeSnapshotClassName: csi-aws-vsc
  source:
    persistentVolumeClaimName: ebs-claim

snapshot 파일을 실행시켜 스냅샷을 생성한다.

Untitled

Untitled

snapshot 생성이 완료되면 기존의 pod을 삭제해본다

kubectl delete pod app && kubectl delete pvc ebs-claim

이제 다시 snapshot을 사용해 pv를 생성한다.

  • ebs-snapshot-restored-claim.yaml

      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: ebs-snapshot-restored-claim
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 4Gi
        dataSource:
          name: ebs-volume-snapshot
          kind: VolumeSnapshot
          apiGroup: snapshot.storage.k8s.io
    
  • ebs-snapshot-restored-pod.yaml

      apiVersion: v1
      kind: Pod
      metadata:
        name: app
      spec:
        containers:
        - name: app
          image: centos
          command: ["/bin/sh"]
          args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
          volumeMounts:
          - name: persistent-storage
            mountPath: /data
        volumes:
        - name: persistent-storage
          persistentVolumeClaim:
            claimName: ebs-snapshot-restored-claim(
    

Untitled

pv와 pod를 모두 복구한 snapshot을 생성 할 수 있다.

© 2024 mont kim   •  Powered by Soopr   •  Theme  Moonwalk