Gom3rye 2025. 7. 2. 17:52
728x90
반응형

리눅스 재부팅후 kubectl get nodes가 안 되면 해결 방법

1. Kubelet 서비스 상태 확인 및 재시작 (가장 중요)

sudo systemctl status kubelet 해서 inactive (dead) 또는 failed 상태이면 sudo systemctl restart kubelet

2. Kubelet 로그 확인

sudo journalctl -u kubelet -n 100 --no-pager

만약 swap을 disable 하라고 나온다면

/etc/fstab에서 swap 항목을 주석 처리해서

sudo nano /etc/fstab
#/swap.img none swap sw 0 0

저장 후 재부팅해도 swap이 켜지지 않도록 설정해야 한다.

이유                                                          설명

성능 문제 swap이 활성화되면 컨테이너 응답이 느려짐
스케줄링 오류 실제 메모리보다 여유롭게 보이는 문제
안정성/예측성 저하 swap으로 인해 자원 관리 예외 발생
공식 권장 사항 프로덕션 환경에서는 swap 비활성 필수

파드 생성

  • yaml 파일 기본 형식
apiVersion: v1
kind: Pod
metadata:
  name: 파드이름
spec:
  containers:
    - name: 컨테이너이름
      image: 이미지이름
apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
    - name: nginx-container
      image: nginx
# pod 생성
kubectl apply -f sample-pod.yaml
# 모든 파드 조회
kubectl get pods
# 파드 상세 조회
kubectl describe pod sample-pod
  • 컨테이너 실행과 관리
    • 쿠버네티스는 컨테이너를 직접 실행하지 않는다.
    • 컨테이너를 생성할 책임은 해당 노드에 설치된 컨테이너 런타임에 맡기는 구조
    • 컨테이너 런타임으로 Docker나 Containerd를 사용한다.
    • 파드는 쿠버네티스가 관리하는 리소스이고 컨테이너는 쿠버네티스 외부에서 관리하는 것이다.
  • stateless & stateful
    • stateless(상태가 없는)는 사용자가 애플리케이션을 사용할 때 상태나 세션을 저장해 둘 필요가 없는 형태이다.
    • stateful은 사용자가 애플리케이션을 사용할 때 상태나 세션을 별도의 데이터베이스에 저장하는 형태이다.
  • 두 개의 컨테이너를 포함하는 파드를 생성(nginx와 redis를 같이 이용하는 파드) 이름은: sample2-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample2-pod
spec:
  containers:
    - name: nginx-container
      image: nginx
    - name: redis-container
      image: redis
# 파드 생성
kubectl apply -f sample2-pod.yaml # pod/sample2-pod created
# 파드 확인
kubectl get pods
  • 실패하는 pod 작성
apiVersion: v1
kind: Pod
metadata:
  name: sample2-pod-fail
spec:
  containers:
    - name: nginx-container-112
      image: nginx:1.16
    - name: nginx-container-113
      image: nginx:1.17 # -> 포트 충돌

# 파드를 확인해보면 하나의 컨테이너는 생성되는데 두 번째 컨테이너가 생성되지 않는다.
# 로그 확인
kubectl logs sample2-pod-fail -c nginx-container-113
# 포트를 바인딩하지 못해서 컨테이너를 생성하지 못했다는 로그 뜬다.
# 2025/07/02 01:14:22 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)

컨테이너 접속과 명령어 실행

  • 생성된 컨테이너에 접속할 때는 kubectl exec 명령어를 사용하는데 가상 터미널(-t)을 생성하고 표준 입력을 패스 쓰루(-i) 하면서 /bin/sh를 실행하면 SSH로 로그인한 상태가 된다.
# sample-pod에 접속
kubectl exec -it sample-pod -- /bin/sh
# 명령어 수행
apt update && apt -y install iproute2 procps

# sample-pod는 컨테이너가 1개였기 때문에 -c 옵션으로 컨테이너를 지정하지 않아도 된다.
# 파드 안에 컨테이너가 2개 이상일 때는 -c 컨테이너이름 으로 지정해야 한다.
kubectl exec -it sample2-pod -c nginx-container -- /bin/sh

# 외부에서 명령어를 실행
kubectl exec -it sample-pod -- /bin/ls --all --classify

# 특수 문자가 포함된 명령어 실행 -> -c 옵션과 "" 사용
kubectl exec -it sample-pod -- /bin/bash -c "ls --all --classify | grep lib"

ENTRYPOINT 명령과 CMD 명령

  • Dockerfile로 이미지를 생성할 때는 ENTRYPOINT 명령과 CMD 명령을 사용해서 컨테이너 실행 시 명령어를 정의하는데 쿠버네티스에서는 ENTRYPOINT를 command로, CMD를 args라고 한다.
  • 컨테이너를 실행할 때 도커 이미지의 ENTRYPOINT와 CMD를 덮어씌기 하려면 파드 정의 내용 중 spec.containers[].command와 spec.containers[].args를 지정한다.
  • 실습
    • nginx 컨테이너가 1시간 정도 대기한 후 실행되도록 하는 yaml 파일 작성 (sample-entrypoint.yaml)
    apiVersion: v1
    kind: Pod
    metadata:
      name: sample-entrypoint
    spec:
      containers:
        - name: nginx-container-112
          image: nginx
          command: ["/bin/sleep"] # entrypoint 설정하는 것
          args: ["3600"] # command
          
    # pod 생성
    kubectl apply -f sample-entrypoint.yaml
    # 컨테이너에 접속
    kubectl exec -it sample-entrypoint -- /bin/sh
    # 자원 소모량 확인
    apt update & apt -y install procps
    ps aux
    

파드 이름 제한

  • 이용 가능한 문자는 영문 소문자와 숫자
  • 이용 가능한 기호는 - 와 .
  • 시작과 끝은 영문 소문자

호스트의 네트워크 구성을 사용한 파드 기동

  • 쿠버네티스에서 기동하는 파드에 할당된 IP 주소를 쿠버네티스 노드의 호스트 IP 주소와 범위가 달라 외부에서는 접근할 수 없는 IP 주소가 할당된다.
  • 호스트의 네트워크를 사용하는 설정을 활성화하려면 프로세스를 기동하는 것과 같은 네트워크 구성(IP 주소, DNS 설정, host 설정 등)으로 파드를 기동시킬 수 있다.
  • hostNetwork를 사용한 파드는 쿠버네티스 노드의 IP 주소를 사용하는 관계로 포트 번호 충돌을 방지하기 위해서 기본적으로 사용하지 않고 사용할 때는 Edge 환경에서의 사용이나 호스트 측의 네트워크를 감시 또는 제어하는 특수한 애플리케이션에서만 사용하도록 한다.
  • 실습
# IP 확인
# Pod의 IP를 확인
kubectl get pods
kubectl get pod 파드이름 -o wide
kubectl get pod sample-pod -o wide

# Node의 IP를 확인
kubectl get node 노드이름 -o wide
kubectl get node minikube -o wide

# 기본적으로 Pod의 IP와 Node의 IP는 다르다.
# But, hostNetwork를 사용하게 되면 pod가 node의 ip를 가지게 된다.
# host network 사용을 위한 pod 생성(sample-hostnetwork.yaml)
apiVersion: v1
kind: Pod
metadata:
  name: sample-hostnetwork
spec:
  hostNetwork: true # Pod이 실행되는 노드의 네트워크 스택을 그대로 사용하겠다." 즉, 그 Pod이 올라간 노드의 IP 주소, 포트, DNS 설정 등을 그대로 공유
  containers:
    - name: nginx-container
      image: nginx
      
# 확인해보면 sample-hostnetwork의 IP가 192.168.56.101(workernode1의 IP)로 바뀐 걸 볼 수 있다.

  • 여러 Pod가 hostNetwork를 쓰면, 같은 IP에서 포트 번호 겹칠 수 있어서 일반적으로는 안 쓰고,
    • 시스템 감시 도구 (노드 네트워크 모니터링할 때)
    • 로그 수집기 / 보안 에이전트 등 의 상황에서 사용한다.

Pod DNS 설정과 서비스 디스커버리

  • DNS 서버에 대한 설정을 하고자 하는 경우라면 spec.dnsPolicy에 추가

설정 값

  • ClusterFirst(기본값)
    • 파드는 클러스터 내부 DNS를 사용해 이름을 해석한다.
    • 서비스 디스커버리나 클러스터 내부 로드밸런싱에 사용한다.
    • 클러스터 내부의 DNS 서버에 질의를 하고 클러스터 내부 DNS에서 해석이 안되는 도메인에 대해서는 업스트림 DNS 서버에 질의
    • 실습
    # sample-dnspolicy-clusterfirst.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: sample-dnspolicy-clusterfirst
    spec:
      dnsPolicy: ClusterFirst
      containers:
        - name: nginx-container
          image: nginx
          
    # pod 생성
    kubectl apply -f sample-dnspolicy-clusterfirst.yaml
    # dns(nameserver) 설정 파일 확인 (내가 이름을 쓰면 어디로 가는지)
    kubectl exec -it sample-dnspolicy-clusterfirst -- cat /etc/resolv.conf

  • None
    • 클러스터 외부 DNS 서버를 참조하는 경우에 사용
    • DNS 서버를 수동으로 설정하려면 spec.dnsPolicy: None으로 설정하고 dnsConfig에 설정하고자 하는 값을 직접 설정한다.
    • 외부 DNS를 설정하게 되면 클러스터 내부 DNS를 사용한 디스커버리는 사용할 수 없게 되므로 주의!
    • 실습
    # sample-dnspolicy-none.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: sample-dnspolicy-none
    spec:
      dnsPolicy: None
      dnsConfig:
        nameservers:
          - 8.8.8.8
          - 8.8.4.4
        searches:
          - example.com # 사이트 하나 연결해주고
        options:
          - name: ndots
            value: "5"
      containers:
        - name: nginx-container
          image: nginx
          
    # pod 생성
    kubectl apply -f sample-dnspolicy-none.yaml
    # dns(nameserver) 설정 파일 확인 (내가 이름을 쓰면 어디로 가는지)
    kubectl exec -it sample-dnspolicy-none -- cat /etc/resolv.conf
    

→ 적절한 dnsConfig가 없으면 기본적으로 pod끼리 이름 기반 통신 못함

  • Default
    • 쿠버네티스 노드의 DNS 설정을 그대로 상속받는 경우에 default를 설정
    • 조심할 점은 dnsPolicy의 기본값은 Default가 아니라는 것!
    • 쿠버네티스 노드의 DNS 설정을 상속받게 되면 클러스터 내부의 DNS를 사용한 서비스 디스커버리를 사용할 수 없다. (= 파드의 이름으로 통신할 수 없다.)
    • 얘는 default만 설정해주면 된다.
  • ClusterFirstWithHostNet
    • hostNetwork를 사용한 파드에 클러스터 내부의 DNS를 참조하고자 하는 경우 설정한다.
    • hostNetwork를 사용하면 기본값인 ClusterFirst의 설정 값이 무시되므로 클러스터 내부의 DNS도 사용하고자 하는 경우에는 이 옵션을 이용한다.

정적 호스트 이름 설정

  • 리눅스에서는 DNS로 호스트 이름을 해석하기 전에 /etc/hosts 파일로 정적 호스트 이름을 해석한다.
  • 쿠버네티스에서는 파드 내부 모든 컨테이너의 /etc/hosts를 변경하는 기능이 준비되어 있으며 spec.hostAliases로 지정하여 설정한다.
    • 쿠버네티스에서는 /etc/hosts를 직접 쓸 필요가 없다!
  • 실습 (정말로 /etc/hosts 파일이 변경되는 지)
# sample-hostaliases.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-hostaliases
spec:
  hostAliases:
    - ip: 8.8.8.8
      hostnames:
        - google-dns # 8.8.8.8 대신에 google-dns라는 이름을 쓰겠다.
        - google-public-dns
  containers:
    - name: nginx-container
      image: nginx
      
# pod 생성
kubectl apply -f sample-hostaliases.yaml
# 호스트 이름 변경 확인 (/etc/hosts) 컨테이너 내부 호스트 설정을 어떻게 하는지 알아둬야 한다.
kubectl -it exec sample-hostaliases -- cat /etc/hosts

작업 디렉토리 설정

  • 컨테이너에서 동작하는 애플리케이션의 작업 디렉토리는 도커 파일의 WORKDIR 명령 설정을 따르지만 쿠버네티스에서는 spec.containers[].workingDir로 덮어쓸 수 있다.
  • 특정 스크립트 등이 배치된 볼륨을 파드에 마운트할 때 그 스크립트가 배치된 디렉토리로 이동한 후 실행하고 싶은 경우가 있는데 이는 작업 디렉토리를 변경하는 기능을 사용하여 해결할 수 있다.
  • 컨테이너에 workingDir을 설정한 경우 프로세스가 실행되는 디렉토리가 변경된 것을 확인할 수 있다.
  • 실습
# sample-workingdir.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-workingdir
spec:
  containers:
    - name: nginx-container
      image: nginx
      workingDir: /tmp
      
# pod 생성
kubectl apply -f sample-workingdir.yaml
# 현재 디렉토리 확인
kubectl exec -it sample-workingdir -- pwd # /tmp
# 이렇게 현재 작업 디렉토리를 변경할 수 있다.

ReplicaSet / ReplicationController

  • ReplicaSet은 일정한 개수의 동일한 Pod가 항상 실행되도록 관리하는데 이 기능이 필요한 이유는 서비스의 지속성 때문이다.
  • 노드의 하드웨어에서 발생하는 장애 등의 이유로 pod를 사용할 수 없을 때 다른 노드에서 pod를 다시 생성해서 사용자에게 중단 없는 서비스를 제공할 수 있다.
  • 원래 파드의 레플리카를 생성하는 리소스의 이름은 레플리케이션 컨트롤러였는데 레플리카셋을 추가하고 일부 기능을 추가했다. (ReplicationController는 좀 예전거, recaplica set을 사용하도록 권장)
  • 실습
# sample-replicaset.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: sample-replicaset
spec:
  replicas: 3 # 원하는 개수
  selector: # 이름을 만드는 것
    matchLabels: # ReplicaSet이 어떤 Pod를 관리할지 결정
      app: sample-app
  template:
    metadata:
      labels: # Pod에 실제로 붙는 라벨
        app: sample-app
    spec: # 컨테이너 만드는 방법: spec안에 template안에 다시 한 번 spec를 넣기
      containers:
        - name: nginx-container
          image: nginx
      
# 리소스 생성
kubectl apply -f sample-replicaset.yaml
# 잘 만들어졌는지 확인
kubectl get replicaset -o wide
# replicaset으로 만들어진 파드 조회 (labels 이름을 주면서)
kubectl get pods -l app=sample-app -o wide
# pod의 이름은 **레플리카셋이름-임의의문자열** 로 생성된다.

파드 정지와 자동화된 복구

  • 레플리카셋에서는 노드나 파드에 장애가 발생했을 때 지정한 파드 수를 유지하기 위해서 다른 노드에서 파드를 기동시켜 주기 때문에 장애시에도 영향을 받지 않는다.
  • 이것이 쿠버네티스의 중요한 컨셉 중 하나인 자동화된 복구 기능이다.
  • 실습
# pod 이름 확인
kubectl get pods -l app=sample-app -o wide
# pod 하나 삭제
kubectl delete pod sample-replicaset-mnbbc
# 바로 다시 확인
kubectl get pods -l app=sample-app -o wide
# 하나가 삭제됨과 동시에 다른 하나가 만들어진걸 확인할 수 있다.

# 증감 이력 확인
kubectl describe replicaset sample-replicaset
# 3개 만들어졌는데 하나를 지우고 하나가 또 생겨났으니까 4개의 이력이 있다.

레플리카셋과 레이블

  • 레플리카셋은 쿠버네티스가 파드를 모니터링해서 파드 수를 조정한다.
    • 레플리카셋은 metadata.labels를 기준으로 파드를 관리한다.
    • 쿠버네티스는 app=sample-app 라벨이 붙은 파드를 찾아서 개수를 센다. (레이블을 기준으로 파드를 찾아서 개수 셈)
  • 모니터링은 특정 레이블을 가진 파드 수를 계산하는 형태로 이루어진다.
  • 레플리카 수가 부족하면 매니페스트에 기술된 spec.template 부분에 정의된 방식으로 새 파드를 생성하고 레플리카 수가 많을 경우 레이블이 일치하는 파드 중 하나를 삭제한다.
  • spec.selector.matchLabels: 어떤 파드를 관리할지 기준
  • spec.template.metadata.labels: 새로 만드는 파드에 붙일 라벨 → 이 두 개는 반드시 일치해야 한다! 그래야 레플리카셋이 제대로 작동하기 때문에 (두 개의 레이블이 일치하지 않으면 파드는 생성되지 않는다.)
  • 실습- 동일한 레이블을 가진 파드를 레플리카셋 외부에서 생성
# sample-external-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: external-sample-app-pod # pod 이름
  labels:
    app: sample-app   # ⬅️ 레플리카셋의 selector와 일치시킴
spec:
  containers:
    - name: nginx-container
      image: nginx
      
# pod 생성
kubectl apply -f sample-external-pod.yaml
# 잘 만들어졌는지 확인
kubectl get pods # 얼뜻 보면 비슷하다.

# 레플리카셋이 관리 중인지 확인
kubectl describe rs sample-replicaset
# Pods Status 목록에 external-sample-app-pod이 포함되어 있다면, 레플리카셋이 이 파드를 감지해서 관리 대상으로 삼고 있다는 것
# But, 이 경우에는 먼저 만들어진 replicaset이 3개가 있기 때문에 만들어지지 않는다.
# 만약 생성된 경우 label 확인 
kubectl get pod external-sample-app-pod --show-labels
  • 결과
    • 레플리카셋은 app: sample-app 라벨이 붙은 파드 수를 세기 때문에, 이 외부 파드도 포함해 기존 파드 중 일부를 삭제할 수 있다.
    • 예를 들어 replicas: 3인데 내가 외부에서 1개 더 만들면, 레플리카셋은 4개가 된 걸로 인식하고 하나를 삭제해 총 3개로 맞춘다.

스케일링

방법1 - 매니페스트를 수정해서 kubectl apply -f 명령을 수행한다.

# sample-replicaset.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: sample-replicaset
spec:
  replicas: 2 # 3 -> 2로 수정!
  selector:
    matchLabels: # ReplicaSet이 어떤 Pod를 관리할지 결정
      app: sample-app
  template:
    metadata:
      labels: # Pod에 실제로 붙는 라벨
        app: sample-app # 위의 sample-app이랑 같아야 한다.
    spec:
      containers:
        - name: nginx-container
          image: nginx
      
# 리소스 생성
kubectl apply -f sample-replicaset-pod.yaml
# 잘 만들어졌는지 확인
kubectl get pods # 3개에서 2개만 만들어진 걸 확인할 수 있다.

방법2 - scale 명령을 이용한다.

kubectl scale replicaset sample-replicaset --replicas 5
# replicaset.apps/sample-replicaset scaled

일치성 기준 조건과 집합성 기준 조건

  • 레플리카의 제어 조건은 서비스 중단 예정인 레플리케이션 컨트롤러의 일치성 기준 셀렉터였지만 레플리카셋에서는 좀 더 강화된 집합 기준 셀렉터를 사용해서 유연한 제어도 가능하다.
    • 일치성 기준: =, !=만 사용
    • 집합성 기준: in, notin, exists 등도 사용 가능
    • 일치성 기준 조건은 app=sample-app 과 지정하는 것

Deployment

파드, 파드, 파드 … ← ReplicaSet ← Deployment 순서로 관리

  • 여러 레플리카셋을 관리하여 롤링 업데이트나 롤백 등을 구현하는 리소스
  • Deployment가 레플리카셋을 관리하고 레플리카셋이 파드를 관리하는 관계
  • ReplicaSet의 상위 개념으로 Pod의 개수를 유지할 뿐 아니라 배포 작업을 좀 더 세분화해서 관리할 수 있다.
  • 디플로이먼트를 사용하면 신규 레플리카셋에 컨테이너가 기동되었는지 헬스 체크는 통과했는지를 확인하면서 전환 작업이 진행되고 레플리카셋의 이행 과정에서 파드 수에 대한 상세한 지정도 가능하다.
  • 쿠버네티스에서 가장 권장하는 컨테이너 기동 방법
  • 하나의 파드를 기동만 한다 하더라도 디플로이먼트 사용을 권장한다.
    • 파드로만 배포를 하게 되면 파드에 장애가 발생했을 때 자동으로 파드가 다시 생성되지 않고 레플리카셋으로만 배포를 한 경우에는 롤링 업데이트 등의 기능을 사용할 수 없기 때문이다.
  • 실습
# 지금까지 만든 레플리카셋과 파드 삭제
kubectl delete replicaset sample-replicaset
kubectl delete pod sample-replicaset-pod

# sample-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx

# 리소스 생성
kubectl apply -f sample-deployment.yaml
# 확인
kubectl get deployment -o wide
# 리소스 삭제
kubectl delete deployment sample-deployment
# 업데이트 변경 이력을 저장하는 옵션을 사용해서 리소스를 생성
kubectl apply -f sample-deployment.yaml --record=true

# ✅--record=true 대체 방법: change-cause를 기록해주기
# 방법 1: kubectl annotate 사용
kubectl annotate deployment sample-deployment kubernetes.io/change-cause="Updated image to nginx:1.21"
# 이렇게 직접 변경 이력을 설명하는 주석(annotation)을 달 수 있다.

# 방법 2: kubectl apply 후 수동으로 annotate
kubectl apply -f sample-deployment.yaml
kubectl annotate deployment sample-deployment kubernetes.io/change-cause="Apply latest update for bug fix"

# 💡 rollout history 확인 방법
kubectl rollout history deployment sample-deployment
# 이 명령어로 디플로이먼트의 변경 이력과 change-cause 주석을 확인할 수 있다.

# 이미지 업데이트
kubectl set image deployment sample-deployment nginx-container=nginx:1.16 --record
# 이미지 확인
kubectl describe pod 파드이름
kubectl describe pod sample-deployment-dcf97f6fd-4798s

# rollout 해보기 (배포가 완료되었는지 확인)
kubectl rollout status deployment sample-deployment
# rollout을 하게 되면 안정적으로 업데이트되도록 하고 문제 발생시 rollback 할 수 있다.
# replicaset까지는 이 기능이 없다.

# 업데이트를 하고 rollout을 하게 되면 이전 버전의 replicaset이 남아 있어서 롤백 가능하다.
kubectl get replicasets

→ deployment가 제일 상위 개념이니까 deployment를 만들었더니 replicaset과 pods가 만들어졌다.

ReplicaSet이 생성되는 조건

  • 디플로이먼트에서 변경이 발생하면 레플리카셋이 생성되는데 변경에는 레플리카 수의 변경은 포함되지 않고 생성된 파드의 내용이 필요하다.
  • spec.template에 변경이 있으면 생성된 파드의 설정이 변경되기 때문에 레플리카셋을 신규로 생성하고 롤링 업데이트를 수행한다.
  • 매니페스트를 쿠버네티스에 등록하고 레플리카셋의 정의를 보면 spec.template 아래의 해시값을 계산하고 이 값을 사용한 레이블로 관리한다.

  • 수작업으로 이미지 등을 이전 버전으로 재변경해서 해시값이 동일해진 경우는 레플리카셋을 신규로 생성하지 않고 기존 레플리카셋을 재사용한다.

변경 롤백

  • 디플로이먼트에는 롤백 기능이 있다.
  • 롤백 기능의 실체는 현재 사용 중인 레플리카셋의 전환
  • 디플로이먼트가 생성한 기존 레플리카셋은 레플리카 수가 0인 상태로 남아있기 때문에 레플리카 수를 변경시켜 다시 사용할 수 있는 상태가 된다.
  • 변경 이력을 확인할 때는 kubectl rollout history 명령어를 사용하는데 CHANGE CAUSE 부분은 디플로이먼트를 생성할 때 --record 옵션을 사용하면 이력 내용이 있지만 --record 옵션을 사용하지 않았다면 <none>이 된다.
  • 변경 이력 확인
kubectl rollout history deployment sample-deployment
kubectl annotate deployment sample-deployment kubernetes.io/change-cause="Updated image to nginx"
  • 버전에 대한 상세 이력 확인
kubectl rollout history deployment sample-deployment --revision 넘버
kubectl rollout history deployment sample-deployment --revision 3
  • 이전 리비전 버전으로 롤백
    • 실제 환경에서는 아무래도 롤백 기능을 사용하는 경우는 많지 않다.
    • CI/CD 파이프라인에서 롤백을 하는 경우 kubectl rollout 명령어보다 이전 매니페스트를 다시 kubectl apply 명령어로 실행해서 적용하는 것이 호환성면에서 더 좋다.
    • spec.template을 같은 내용으로 돌렸을 경우에는 pod-template-hash값이 같기 때문에 kubectl rollout처럼 기존에 있던 레플리카 셋의 파드가 기동된다.
kubectl rollout undo deployment sample-deployment
# 이미지 다시 확인
kubectl describe pod sample-deployment-556b965888-gvk65
  • 변경 일시 중지
    • 일반적으로 Deployment를 업데이트 하면 바로 적용되어 업데이트 처리가 실행되지만 안전을 위해서 디플로이먼트에 대한 업데이트를 하더라도 바로 적용되지 않는 것을 원하기도 한다. → pause, resume 명령 이용
    • 실습
    # 업데이트 일시 중지
    kubectl rollout pause deployment sample-deployment
    
    # 이미지 업데이트
    kubectl set image deployment sample-deployment nginx-container=nginx:1.16
    # deployment.apps/sample-deployment image updated
    
    # 업데이트 상태 확인
    kubectl rollout status deployment sample-deployment
    # Waiting for deployment "sample-deployment" rollout to finish: 0 out of 3 new replicas have been updated...
    # 업데이트 대기 중이라는 메시지가 출력
    
    # 바로 이전 리비전으로 롤백 -> 안 됨 (아직 변경 내역이 반영이 안됨)
    kubectl rollout undo deployment sampl-deployment
    
    # 일시 정지 해제 (아주 특별한 경우)
    kubectl rollout resume deployment sample-deployment
    

🎬 관련된 명령어

명령어 설명

kubectl rollout status deployment/<이름> 배포가 잘 진행되고 있는지 상태 확인
kubectl rollout history deployment/<이름> 이전 버전 기록 보기 (change-cause 포함 가능)
kubectl rollout undo deployment/<이름> 실수했을 때 이전 버전으로 되돌리기
kubectl rollout restart deployment/<이름> 파드를 재시작해서 갱신하기
  • 트러블슈팅으로 알게 된 것
    • 쿠버네티스 스케쥴러가 파드를 노드에 배치
    • kubectl describe 명령어는 파드의 상태뿐만 아니라, 파드가 생성되고 실행되기까지의 모든 이벤트 이력을 보여주기 때문에 문제 해결에 아주 유용한 도구
    • Flannel은 Kubernetes 클러스터에서 Pod들이 서로 통신할 수 있도록 해주는 네트워크 플러그인(CNI) (Kubernetes에서 각 Pod는 자체 IP 주소를 가지고 있고, 서로 통신할 수 있어야 함)
      • Pod IP 주소를 할당해줌
      • 노드 간 네트워크 경로를 설정
      • 보통 Overlay Network (예: VXLAN)을 사용해서 노드 간 Pod 트래픽을 전달함
      • ex)
        • Pod A (Node 1, IP: 10.244.1.2) ↔ Pod B (Node 2, IP: 10.244.2.3)
        • Flannel이 이런 트래픽을 터널링하여 통신 가능하게 함
    • Kubernetes는 파드나 서비스 등을 네임스페이스별로 격리해서 관리
    • Entrypoint: 컨테이너 실행 시 가장 먼저 실행되는 명령어
      • ENTRYPOINT는 반드시 실행됨
      • CMD는 기본값일 뿐, 나중에 오버라이드할 수 있음
    • 🧱 Kubernetes 리소스 구조 (간단 도식)
    • Cluster └── Namespace (ex. default, kube-system, kube-flannel, ...) └── Pod └── Container(s)
    쿠버네티스 클러스터 장애 대응 기록 📝
    • kubectl get nodes 명령어 실행 시 노드가 정상적으로 보이지 않음.
    • kubelet 서비스가 불안정하게 계속 재시작되는 상태.

    🐛 문제 1: Kubelet, 스왑(Swap) 활성화로 인한 실행 실패
    • 에러 로그 (journalctl -u kubelet)
    • E0702 00:13:21.116326 ... failed to run Kubelet: running with swap on is not supported, please disable swap!
    • 원인 쿠버네티스는 노드의 리소스를 정확하게 관리하고 예측하기 위해 기본적으로 스왑 메모리 사용을 허용하지 않습니다. 노드에 스왑이 활성화되어 있어 Kubelet 서비스가 시작되지 못하고 계속 실패했습니다.
    • 해결 과정
      1. sudo swapoff -a 명령어로 즉시 스왑을 비활성화.
      2. sudo vi /etc/fstab 파일을 수정하여 재부팅 후에도 스왑이 활성화되지 않도록 설정.
      3. sudo systemctl restart kubelet으로 Kubelet 서비스를 재시작하여 정상 구동 확인.

    🐛 문제 2: Flannel CNI 미설치로 인한 파드(Pod) 생성 불가
    • 증상 Kubelet은 정상화되었으나, 파드들이 네트워크를 할당받지 못해 제대로 생성되지 않음.Bash
    • # Flannel 관련 리소스가 전혀 없는 것을 확인 kubectl get pods -n kube-system -l app=flannel No resources found in kube-system namespace.
    • 원인 파드 간의 통신을 담당하는 CNI(Container Network Interface) 플러그인이 클러스터에 설치되지 않았습니다.
    • 해결 과정
      1. kubectl apply -f <https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml> 명령어로 Flannel CNI를 클러스터에 설치.

    🐛 문제 3: Flannel 파드 CrashLoopBackOff 발생
    • 증상 Flannel 데몬셋은 생성되었으나, 일부 노드의 Flannel 파드가 Running 되지 못하고 CrashLoopBackOff 상태를 반복.
    • NAME READY STATUS RESTARTS AGE kube-flannel-ds-dsxtx 0/1 CrashLoopBackOff 5 3m33s kube-flannel-ds-jsvqv 0/1 CrashLoopBackOff 5 3m33s kube-flannel-ds-pqd8j 1/1 Running 0 3m33s
    • 에러 로그 (kubectl logs <파드이름> -n kube-flannel)
    • E0702 00:56:55.813646 ... Failed to check br_netfilter: stat /proc/sys/net/bridge/bridge-nf-call-iptables: no such file or directory
    • 원인 컨테이너의 브릿지 네트워크 트래픽을 iptables가 처리하는 데 필요한 br_netfilter 리눅스 커널 모듈이 활성화되지 않았습니다.
    • 해결 과정
      1. 문제 노드 특정: kubectl describe pod <파드이름> -n kube-flannel 명령어를 실행. Events 섹션의 Scheduled 메시지를 통해 에러가 workernode1에서 발생하고 있음을 확인함.
        Normal Scheduled 85s default-scheduler Successfully assigned kube-flannel/kube-flannel-ds-l22gc to workernode1`
      2. `Events: Type Reason Age From Message
      3. 모든 노드에 커널 설정 적용: masternode 뿐만 아니라 workernode1, workernode2를 포함한 모든 클러스터 노드에 접속하여 아래 커널 설정을 적용.Bash

        2. 재부팅 시 자동 로드

        echo br_netfilter | sudo tee /etc/modules-load.d/k8s.confcat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 EOFsudo sysctl --system`
      4. 4. 시스템에 즉시 적용
      5. 3. 커널 파라미터 활성화
      6. `# 1. 모듈 로드 sudo modprobe br_netfilter
      7. 파드 재시작: 문제가 있던 Flannel 파드를 삭제하여 데몬셋이 정상적으로 파드를 다시 생성하도록 조치.
  • 🎯 최초 증상

🧩 용어 설명

1. 클러스터 (Cluster)

  • Kubernetes 전체를 의미하는 가장 큰 단위
  • 여러 노드(Master + Worker)로 구성됨

2. 네임스페이스 (Namespace)

  • 클러스터 내부를 논리적으로 구분하는 공간
  • 예: default, kube-system, kube-public, dev, prod
  • 마치 하나의 Kubernetes 클러스터 안에서 여러 개의 프로젝트나 팀이 격리된 환경에서 작업할 수 있도록 해줌

3. 파드 (Pod)

  • Kubernetes에서 가장 작은 배포 단위
  • 하나 이상의 컨테이너를 포함할 수 있음
  • 보통은 컨테이너 하나만 들어 있음

4. 컨테이너 (Container)

  • 실제로 애플리케이션이 실행되는 단위
  • Docker, containerd 등을 통해 실행됨
  • 같은 Pod 안에 있는 컨테이너들은:
    • 같은 네트워크(IP) 사용
    • 같은 스토리지 볼륨 공유 가능

🧭 요약 비교

항목 외부 접근 내부 접근 라우팅 방식 사용 예

hostPort ✅ 노드 직접 ✅ 가능 파드 ↔ 노드 포트 바인딩 디버깅, 데몬앱
nodePort ✅ 모든 노드 ✅ 가능 kube-proxy가 라우팅 테스트용 외부 서비스
ClusterIP 클러스터 내부 IP 마이크로서비스
Ingress ✅ 도메인 기반 도메인 + 경로 웹 서비스, API Gateway

🔚 어떤 걸 써야 할까?

  • 내부 통신만 필요하면 → ClusterIP
  • 외부 노출 + 간단한 테스트 → NodePort
  • 웹 서비스나 도메인 라우팅이 필요 → Ingress
  • 특수한 경우/데몬앱/로우레벨 통신 → hostPort
728x90
반응형