
 kubectl describe pod 파드이름
 # 할당된 리소스를 확인할 수 있다.
 
 kubectl top pods
 # 현재 파드가 사용하고 있는 cpu,memory양을 알 수 있다.
 
 kubectl top nodes
 # 현재 노드가 사용하고 있는 cpu,memory양을 알 수 있다.
 
 kubectl describe node kube-node1
 # 노드에 할당된 리소스를 확인할 수 있다
 
 kubectl exec 파드이름 -- sha256sum /dev/zero
 # 해당 파드에 강제로 리소스의 부하를 준다  
 # 쓰레기 값을 계속 넣어서 용량을 계속해서 잡아먹는다
1 . 리소스 제한 예시
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests: # 컨테이너가 할당 받을 수 있는 최소 리소스 
        memory: "64Mi"
        cpu: "250m"
      limits: # 컨테이너가 할당 받을 수 있는 최대 리소스
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
2 . limit만 설정한 예시
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod-lim
spec:
  containers:
  - name: myapp
    image: ghcr.io/c1t1d0s7/go-myweb:alpine
    resources:
      limits:
        cpu: 0.5
        memory: 20Mi
Pod Quality of Service Classes
kubectl describe pod 파드이름
# 파드의 QOS의 클래스를 확인할 수 있다
1 . BestEffort
2 . Burstable
3 . Guaranteed
kubelet이 리소스 부족으로 압박을 받기 시작하면 가장 먼저 종료하는 것은 BestEffort이고 그 다음은 Burstable을 종료시킨다.
제한된 리소스에 따라 종료시킬 파드의 우선순위를 고려해서 파드를 생성해야 한다.
kubectl get limits
# 생성한 리밋레인지 보기
kubectl describe limits 리밋레인지 이름
# 리밋레인지에 설정된 값 보기
1 . 리밋 레인지 예시
apiVersion: v1
kind: LimitRange
metadata:
  name: myapp-limitrange
spec:
  limits:
  - type: Pod # 파드 단위로 제한
    min:
      cpu: 50m
      memory: 5Mi
    max:
      cpu: 1
      memory: 1Gi
  - type: Container # 컨테이너 단위로 제한 
    defaultRequest: # 기본 request
      cpu: 100m
      memory: 10Mi 
    default: # 기본 limit 
      cpu: 200m
      memory: 100Mi
    min:
      cpu: 50m
      memory: 5Mi
    max:
      cpu: 1
      memory: 1Gi
    maxLimitRequestRatio:
      cpu: 4
      memory: 10
  - type: PersistentVolumeClaim # 볼륨 단위로 제한 
    min:
      storage: 10Mi
    max:
      storage: 1Gi
-> 리밋레인지에 리소스의 범위를 설정하고 해당 리소스를 생성하게되면 해당 범위에서만 생성이 가능하게 한다.
-> 범위를 넘어가는 리소스는 생성되지 않는다.
-> 컨테이너의 default 섹션은 만약 컨테이너를 생성할때 limit과 request를 설정하지 않으면 자동으로 해당 default의 값으로 limit과 request를 설정한다.
kubectl get quota # 생성한 쿼터 보기
1 . request와 limit을 지정한 쿼터
apiVersion: v1
kind: ResourceQuota
metadata:
  name: myapp-quota-cpumem
spec:
  hard:
    requests.cpu: 500m
    requests.memory: 200Mi
    limits.cpu: 1000m
    limits.memory: 1Gi
2 . 생성가능한 리소스의 개수를 제한한 쿼터
apiVersion: v1
kind: ResourceQuota
metadata:
  name: myapp-quota-object
spec:
  hard:
    pods: 10
    replicationcontrollers: 2
    secrets: 10
    configmaps: 10
    persistentvolumeclaims: 5
    services: 5
    services.loadbalancers: 1
    services.nodeports: 2
    nfs-client.storageclass.storage.k8s.io/persistentvolumeclaims: 2
-> Deployment/RC/Sts등의 리소스를 통해 파드를 생성할 때 Replicas를 지정한다.
-> HPA가 이 Replicas의 수를 자동으로 조정해주는 구조이다.
-> HPA가 Replicas의 수를 조정하는 알고리즘 
📒 HPA개념 참조
kubectl get hpa
# 생성한 hpa확인
1 . HPA사용 예시
apiVersion: autoscaling/v2 # 버전 유의 
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa-cpu
spec:
  scaleTargetRef: # replica수를 조정할 대상 설정 
    apiVersion: apps/v1
    kind: Deployment
    name: myapp-deploy-hpa
  minReplicas: 2 # replica 최소값
  maxReplicas: 10 # replica 최대값
  metrics: # replica수를 조정할 때 참조할 지표 설정 
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70 # 70퍼센트를 기준으로 스케일링 
1-1 . 디플로이먼트 생성
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy-hpa
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp-deploy-hpa
  template:
    metadata:
      labels:
        app: myapp-deploy-hpa
    spec:
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb:alpine
        resources:
          requests: # metrics로 참조할 cpu사용률
            cpu: 50m
            memory: 5Mi
          limits:
            cpu: 100m
            memory: 20Mi
        ports:
        - containerPort: 8080
tmux # 여러 터미널을 띄우기 위해 tmux를 킨다
ctrl + b + " # 창을 세개로 분할한다
watch -n1 -d kubectl get hpa # hpa의 정보를 실시간으로 본다 
watch -n1 -d kubectl top pods # 파드의 cpu사용률을 실시간으로 본다 
kubectl exec 파드1 이름 -- sha256sum /dev/zero # 하나의 파드에 임의로 cpu용량 부하를 준다
-> 만약 임의로 한 파드에 용량의 부하를 줬을 경우 파드는 최대 limit값이 100까지 cpu를 사용할 것이고, 이것은 100/150 = 66%
이기 때문에 70%를 도달하지못해 scailing을 하지 않는다.
kubectl exec 파드2 이름 -- sha256sum /dev/zero
# 추가로 하나의 파드에 임의로 cpu용량 부하를 준다 
-> 만약 임의로 또 한 파드에 용량의 부하를 줬을 경우 3개의 파드 총 cpu사용률을 200이 될것이고, 200/150 = 133%이므로 replica의 수를 증가시킨다.
만약 용량 부하가 걸린 파드를 삭제해서 cpu사용률을 낮추게 되면
autoscailing에 의해 파드의 개수를 최소개수까지 낮출것이다.
하지만 파드의 개수가 바로 변화되지 않는것을 알 수 있다.
이유는 바로 stabilizationWindowSeconds 때문이다.
파드를 노드에 배치하는 방법
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-rs-nn
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp-rs-nn
  template:
    metadata:
      labels:
        app: myapp-rs-nn
    spec:
      nodeName: kube-node1 # 특정 노드 지정
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb:alpine
-> 생성된 파드는 지정한 node1에만 배치된다
-> nodename은 특정 노드만 지정해서 사용해야하기 때문에 유연하지 않다. 따라서 보통 사용하지 않고 nodeselector를 사용한다.
kubectl label node kube-node1 gpu=lowend
kubectl label node kube-node2 gpu=highend
kubectl label node kube-node3 gpu=highend
# 각 노드에 레이블 설정 
kubectl get nodes -L gpu
# 설정한 gpu확인 
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-rs-ns
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp-rs-ns
  template:
    metadata:
      labels:
        app: myapp-rs-ns
    spec:
      nodeSelector: # label을 선택한다.
        gpu: highend # 해당 레이블을 가진 노드들을 선택 
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb:alpine
-> 노드 셀렉터에 의해 gpu: highend 레이블을 가진 node2,node3에만 파드가 배치된다.
-> nodename과 달리 노드이름을 지정하는 것이 아니라, 레이블을 통해 노드를 선택하기 때문에 유연하게 여러 노드를 선택할 수 있다.
kubectl label node kube-node1 gpu-model=3080
kubectl label node kube-node2 gpu-model=2080
kubectl label node kube-node3 gpu-model=1660
# 노드에 레이블 설정 
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-rs-nodeaff
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp-rs-nodeaff
  template:
    metadata:
      labels:
        app: myapp-rs-nodeaff
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution: # 요청(hard)
            nodeSelectorTerms: # 노드의 레이블 선택, 반드시 하나를 선택해야하기 때문에 가중치가 없다 
            - matchExpressions:
              - key: gpu-model
                operator: In
                values:
                - '3080'
                - '2080'
          preferredDuringSchedulingIgnoredDuringExecution: # 요청(soft)
          - weight: 10 # 선호하는 정도, 여러개의 선호가 있다면 가중치로 순위를 매김  
            preference: 
              matchExpressions: # 노드의 레이블 선택 
              - key: gpu-model 
                operator: In
                values:
                - titan
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb:alpine
- 요청(soft) : 특정 노드를 선호하지만 되면 배치되고 아니여도 상관 x
 - 요청(hard) : 특정 노드에 반드시 배치되어야 한다.
 
-> 요청한 노드가 더이상 파드를 받을 수 없거나, 해당 레이블을 가진 노드가 없다면 파드는 생성되지 않는다.
1 . cache 파드 생성
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-rs-aff-cache
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp-rs-aff-cache
      tier: cache
  template:
    metadata:
      labels:
        app: myapp-rs-aff-cache
        tier: cache # 파드에 레이블 설정 
    spec:
      affinity:
        podAntiAffinity: # 파드가 서로 배척한다 
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: tier
                operator: In
                values:
                - cache
            topologyKey: "kubernetes.io/hostname"
            # 노드의 hostname으로 구역을 나눈다
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb:alpine
1-1 . front 파드 생성
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-rs-aff-front
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp-rs-aff-front
      tier: frontend
  template:
    metadata:
      labels:
        app: myapp-rs-aff-front
        tier: frontend # 파드에 레이블 설정 
    spec:
      affinity:
        podAntiAffinity: # 파드가 서로를 배척한다 
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: tier
                operator: In
                values:
                - frontend
            topologyKey: "kubernetes.io/hostname"
        podAffinity: # 파드가 붙어서 노드에 배치된다 
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: tier
                operator: In
                values:
                - cache 
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb:alpine
-> 하나의 노드에 cache파드와 front파드가 한 쌍으로 묶여서 배치된다. 다른 노드에도 마찬가지로 한 쌍으로 묶여서 배치된다.
kubectl taint node kube-node1 env=production:NoSchedule
# taint 생성 
kubectl taint node kube-node1 env-
# taint 해제 
kubectl get nodes kube-control1 -o jsonpath='{.spec.taints}'
# 해당 노드의 taint 보기
1 . 위의 podAffinity / podAntiAffinity실습의 연장선이다.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-rs-tol
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp-rs-tol
      tier: backend
  template:
    metadata:
      labels:
        app: myapp-rs-tol
        tier: backend
    spec:
      affinity:
        podAntiAffinity: # cache 레이블을 가진 파드를 배척 
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: tier
                operator: In
                values:
                - cache
            topologyKey: "kubernetes.io/hostname"
      tolerations: # 톨러레이션 부여 
      - key: env 
        operator: Equal
        value: production
        effect: NoSchedule
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb:alpine
-> 위의 podAffinity / podAntiAffinity실습에 의해서 node2,node3에 각각 cache,front 레이블을 가진 한쌍을 배치했었다.
-> podAntiAffinity설정에 의해 cache레이블을 가진 파드를 배척하기 때문에 node2,3에는 배치될 수 없다.
-> 따라서 node1에만 배치될 수 있는데 node1에 taint를 설정했기 때문에 taint가 가진 정책에 부합하는 tolerations를 가져야 된다.
-> taint정책에 부합하는 key,value를 가진 tolerations를 부여했기 때문에 node1에 배치되는 것을 볼 수 있다.
kubectl cordon kube-node3
-> 해당 노드에 스케줄링되는 것을 막는다.
kubectl drain kube-node3
kubectl drain kube-node3 --ignore-daemonsets
-> 해당 노드에 있는 모든 파드들을 퇴거(evict) 시킨다.
-> 하지만 데몬셋이 관리하는 파드들은 복제본 컨트롤러가 아니기 때문에 퇴거시키지 못한다. 따라서 --ignore옵션을 사용한다.
-> 다른 복제본 컨트롤러가 관리하는 파드들은 지워져도 다른 노드에 자동으로 생성되기 때문에 문제가 안된다 .
kubectl uncordon kube-node3 # 커든 해제 
💡 drain을 하게 되면 자동으로 커든이 되기때문에 작업이 끝나고나면 커든을 해제해야 한다.