728x90

초기

초기 아이디는 'admin' 이고 비밀번호는 아래 명령어로 값을 확인 해서 비밀번호에 넣어서 로그인 해야함

kubectl get secret -n monitoring kube-prom-grafana \
  -o jsonpath="{.data.admin-password}" | base64 -d && echo

 

비밀번호 설정 후

비밀번호 설정 후에는 내부 DB에 저장된 값으로만 로그인 하기 때문에 시크릿 값으로 로그인이 안 됨.

728x90

GHCR Private 이미지 배포 + Self-hosted Runner 기반 CI/CD 구축 과정에서 사용한 명령어들 정리해보았음

 

목차

1. k3s 설치 및 상태 확인
2. kubectl 설정 (kubeconfig)
3. 노드 관리
4. Namespace 관리
5. Secret 관리 (GHCR 인증)
6. Deployment 관리
7. 파드 디버깅
8. 특정 노드에 배포 (nodeSelector)
9. GitHub Actions Self-hosted Runner
10. 자주 발생하는 에러와 해결

 

 

1. k3s 설치 및 상태 확인

k3s 설치

curl -sfL https://get.k3s.io | sh -

 

k3s 설치 스크립트를 다운로드하여 실행. k3s 바이너리, systemd 서비스, 기본 설정 파일들을 자동으로 설치한다.

 

 k3s 서비스 상태 확인

sudo systemctl status k3s

systemd로 등록된 k3s 서비스의 상태를 확인. active (running)이면 정상.

 

k3s 서비스 시작/중지/재시작

  sudo systemctl start k3s    # 시작
  sudo systemctl stop k3s     # 중지
  sudo systemctl restart k3s  # 재시작
  sudo systemctl enable k3s   # 부팅 시 자동 시작

systemd의 service 관리 명령어. k3s는 systemd 서비스로 등록되어 있다.

 

 

Kubectl 설정 (kubeconfig)

kubeconfig 설정

  # 방법 1: 환경변수로 지정
  export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

  # 방법 2: ~/.kube/config로 복사
  mkdir -p ~/.kube
  sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
  sudo chown $USER:$USER ~/.kube/config
  • kubectl은 기본적으로 ~/.kube/config 또는 KUBECONFIG 환경변수에서 클러스터 접속 정보를 읽는다.
  • k3s는 설치 시 /etc/rancher/k3s/k3s.yaml에 kubeconfig를 생성한다.
  • 이 파일에는 API 서버 주소, 인증서, 토큰 등이 들어있다.

연결 테스트

kubectl get nodes

Kubernetes API 서버에 요청을 보내 클러스터의 노드 목록을 조회. 연결이 성공하면 노드 정보가 출력된다.

 

 

3. 노드 관리

노드 목록 확인

  kubectl get nodes
  kubectl get nodes -o wide  # 상세 정보

API 서버에 등록된 모든 노드(워커 + 컨트롤플레인)의 상태를 조회.

 

노드에 라벨 추가

kubectl label node <노드이름> <key>=<value>

# 예시
kubectl label node tws-b deploy-target=frontend
  • 노드에 key-value 형태의 라벨을 추가.
  • Deployment의 nodeSelector에서 이 라벨을 사용해 특정 노드에 파드를 배포할 수 있다.

 

노드 라벨 확인

  kubectl get nodes --show-labels
  kubectl describe node <노드이름> | grep -A 10 Labels

각 노드에 설정된 모든 라벨을 조회.

 

노드 라벨 삭제

kubectl label node <노드이름> <key>-

키 이름 뒤에 '-'를 붙이면 해당 라벨이 삭제된다.

 

 

4. Namespace 관리

Namespace 생성

kubectl create namespace <이름>
  • Kubernetes에서 리소스를 논리적으로 격리하는 단위.
  • 같은 네임스페이스 내의 리소스끼리만 직접 통신 가능.

Namespace 목록 확인

kubectl get namespaces

 

Namespace 삭제

kubectl delete namespace <이름>

해당 네임스페이스와 그 안의 모든 리소스가 삭제됨. 주의 필요~

 

 

5. Secret 관리 (GHCR 인증)

GHCR 인증용 Secret 생성

kubectl create secret docker-registry ghcr-secret \
  --namespace <namespace> \
  --docker-server=ghcr.io \
  --docker-username=<GitHub사용자명> \
  --docker-password=<GitHub_PAT_토큰>
  • docker-registry 타입의 Secret을 생성.
  • 이 Secret에는 Docker/Container Registry 인증 정보가 base64로 인코딩되어 저장됨.
  • k3s의 containerd가 이 정보를 사용해 private 이미지를 pull할 수 있다.

 

Secret 확인

kubectl get secret -n <namespace>
kubectl get secret ghcr-secret -n <namespace> -o yaml

 

ServiceAccount에 Secret 연결

kubectl patch serviceaccount default -n <namespace> \
  -p '{"imagePullSecrets": [{"name": "ghcr-secret"}]}'
  • Kubernetes에서 파드(Pod)는 ServiceAccount의 권한으로 실행됨.
  • ServiceAccount에 imagePullSecrets을 추가하면, 해당 네임스페이스의 모든 파드가 이미지 pull 시 이 Secret을 사용.
  • 기본 ServiceAccount 이름은 "default".

 

Secret 삭제

kubectl delete secret ghcr-secret -n <namespace>

 

Secret 업데이트 (삭제 후 재생성)

kubectl delete secret ghcr-secret -n <namespace>

kubectl create secret docker-registry ghcr-secret \
  --namespace <namespace> \
  --docker-server=ghcr.io \
  --docker-username=<사용자명> \
  --docker-password=<새토큰>

 

 

6. Deployment 관리

Deployment 적용

kubectl apply -f <yaml파일경로>

# 예시
kubectl apply -f backend/k8s/deployment.yaml
  • YAML 파일에 정의된 Deployment 리소스를 클러스터에 생성/업데이트.
  • apply는 선언적(declarative) 방식 - 원하는 상태를 정의하면 Kubernetes가 그 상태로 만들어줌.

 

Deployment 목록 확인

kubectl get deployments -n <namespace>
kubectl get deployments -A  # 모든 네임스페이스

 

Deployment 상세 정보

kubectl describe deployment <이름> -n <namespace>

 

Deployment 삭제

kubectl delete deployment <이름> -n <namespace>

 

이미지 업데이트

kubectl set image deployment/<이름> <컨테이너이름>=<이미지:태그> -n <namespace>

# 예시
kubectl set image deployment/gilldong-project-backend-app \
  time-to-sleep-web=ghcr.io/hongildong/gilldong-project-backend-app:latest \
  -n gilldong-project-backend

Deployment의 컨테이너 이미지를 변경하면 Rolling Update가 트리거되어 파드가 교체됨

 

파드 재시작

kubectl rollout restart deployment/<이름> -n <namespace>
  • Deployment의 spec.template에 변경을 가해 Rolling Update를 강제로 트리거.
  • 기존 파드는 종료되고 새 파드가 생성됨.
  • 이미지를 다시 pull하거나, 설정 변경 사항을 적용할 때 사용.

 

배포 상태 확인

kubectl rollout status deployment/<이름> -n <namespace> --timeout=120s

Rolling Update가 완료될 때까지 대기. 타임아웃 내에 완료되지 않으면 에러 반환.

 

배포 히스토리 확인

kubectl rollout history deployment/<이름> -n <namespace>

 

이전 버전으로 롤백

kubectl rollout undo deployment/<이름> -n <namespace>

 

 

7. 파드 디버깅

파드 목록 확인

kubectl get pods -n <namespace>
kubectl get pods -n <namespace> -o wide  # 노드 정보 포함
kubectl get pods -A  # 모든 네임스페이스

 

파드 상세 정보 (에러 원인 찾기)

kubectl describe pod <파드이름> -n <namespace>
  • 파드의 전체 상태 정보를 출력.
  • Events 섹션에서 스케줄링, 이미지 pull, 컨테이너 시작 과정의 문제를 확인 가능.

 

파드 로그 확인

kubectl logs <파드이름> -n <namespace>
kubectl logs <파드이름> -n <namespace> --tail=100  # 마지막 100줄
kubectl logs <파드이름> -n <namespace> -f  # 실시간 스트림

컨테이너의 stdout/stderr 로그를 조회.

 

파드 내부 접속

kubectl exec -it <파드이름> -n <namespace> -- /bin/sh

파드 내부 컨테이너에 직접 접속하여 디버깅.

 

파드 강제 삭제

kubectl delete pod <파드이름> -n <namespace> --force --grace-period=0
  • 일반 삭제가 안 될 때 강제 삭제.
  • grace-period=0은 종료 유예 시간 없이 즉시 삭제.

 

 

8. 특정 노드에 배포 (nodeSelector)

개념

기본적으로 Kubernetes 스케줄러가 파드를 어느 노드에 배치할지 결정한다. 특정 노드에 배포하려면 nodeSelector 또는 nodeAffinity를 사용.

 

방법 1: nodeSelector (간단)

1단계: 노드에 라벨 추가 (Control Plane에서 실행)

kubectl label node tws-b deploy-target=frontend

 

2단계: Deployment YAML에 nodeSelector 추가

spec:
  template:
    spec:
      nodeSelector:
        deploy-target: backend  # 또는 kubernetes.io/hostname: tws-b
      containers:
        - name: hongildong-backend-app
          ...

 

방법 2: nodeName (가장 단순)

spec:
  template:
    spec:
      nodeName: tws-b  # 노드 이름 직접 지정
      containers:
        - name: hongildong-backend-app
          ...

 

방법 3: nodeAffinity (유연함)

spec:
  template:
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: kubernetes.io/hostname
                    operator: In
                    values:
                      - tws-b

 

 

9. GitHub Actions Self-hosted Runner

개념

  • GitHub Actions의 runs-on: [self-hosted, k3s-deploy]는 라벨이 k3s-deploy인 self-hosted runner에서 job을 실행한다.
  • k3s 서버에 runner를 설치하면 kubectl 명령어를 직접 실행할 수 있다.

 

Runner 설정 명령어

./config.sh --url https://github.com/<owner>/<repo> \
    --token <RUNNER_TOKEN> \
    --labels k3s-deploy
  • --url: 연결할 GitHub 저장소
  • --token: GitHub에서 발급한 runner 등록 토큰
  • --labels: Runner 식별용 라벨 (워크플로우에서 이 라벨로 runner 지정)

 

Runner 서비스 관리

# Runner 설치 경로로 이동
cd ~/actions-runner

# 서비스로 등록
sudo ./svc.sh install

# 서비스 시작
sudo ./svc.sh start

# 상태 확인
sudo ./svc.sh status

# 중지
sudo ./svc.sh stop

 

Runner 토큰 발급 위치

1. GitHub → Repository → Settings
2. Actions → Runners → New self-hosted runner
3. 토큰과 설정 명령어가 표시됨

 

 

10. 자주 발생하는 에러와 해결

에러 1: connection refused - did you specify the right host or port?

  • 원인: kubectl이 k3s API 서버에 연결하지 못함 (kubeconfig 미설정)
  • 해결:
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
# 또는
mkdir -p ~/.kube && sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config

 

에러 2: ErrImagePull / ImagePullBackOff + 403 Forbidden

  • 원인: Private 이미지에 인증 없이 접근 시도
  • 해결:
# 1. GitHub PAT 토큰 생성 (read:packages 권한 필요)
# 2. Secret 생성
kubectl create secret docker-registry ghcr-secret \
  --namespace <namespace> \
  --docker-server=ghcr.io \
  --docker-username=<사용자명> \
  --docker-password=<PAT_토큰>

# 3. ServiceAccount 연결
kubectl patch serviceaccount default -n <namespace> \
  -p '{"imagePullSecrets": [{"name": "ghcr-secret"}]}'

# 4. 파드 재시작
kubectl rollout restart deployment/<이름> -n <namespace>

 

에러 3: namespaces "xxx" not found

  • 원인: Namespace가 없는 상태에서 리소스 생성 시도
  • 해결:
kubectl create namespace <이름>

 

에러 4: timed out waiting for the condition

  • 원인: Rolling Update가 지정된 시간 내에 완료되지 않음
  • 해결:
# 파드 상태 확인
kubectl get pods -n <namespace> -o wide

# 상세 에러 확인
kubectl describe pod <파드이름> -n <namespace>

# 타임아웃 늘리기
kubectl rollout status deployment/<이름> -n <namespace> --timeout=300s

 

에러 5: secrets "xxx" already exists

    • 원인: 이미 같은 이름의 Secret이 존재
    • 해결:
# 삭제 후 재생성
kubectl delete secret ghcr-secret -n <namespace>
kubectl create secret docker-registry ghcr-secret ...

 

 

배포 요약 아키텍처

'서버 > 클라우드 컴퓨팅' 카테고리의 다른 글

Hypervisor vs Container  (1) 2024.09.05
Hadoop: MapReduce + HDFS  (0) 2021.12.16
MapReduce: Fault Tolerance, Locality, Large-Scale Indexing  (0) 2021.12.16
MapReduce: Programming Model  (0) 2021.12.16
MapReduce  (0) 2021.12.16
728x90

요약

  • 현상: Kafka 컨테이너 CPU 사용량이 10초 주기로 3% → 140% 급증 후 복구 반복.
  • 원인: 10초 주기 Docker Healthcheck가 무거운 Java 기반 명령어를 실행하며 JVM 컴파일러(C2 Compiler) 부하 유발.
  • 해결: 헬스체크 방식을 자바 실행(kafka-broker-api-versions)에서 네트워크 체크(nc -z)로 변경 및 JVM 옵션 최적화.

 

원인 분석: 왜 10초마다 튀었나?

  1. 문제의 발단: docker-compose의 healthcheck 설정. 
    • test: ["CMD-SHELL", "kafka-broker-api-versions ..."]
    • 이 명령어는 실행될 때마다 새로운 Java 프로세스를 띄웁니다.
  2. JVM의 과도한 열정 (JIT Compilation):
    • 자바는 실행될 때 코드를 최적화하기 위해 C2 Compiler 스레드를 가동합니다.
    • 10초마다 새로운 자바 프로세스가 뜨니, 그때마다 CPU는 최적화 작업을 위해 풀가동(140%)하게 됩니다.
  3. 리소스 낭비: - 단순히 "살아있니?"라고 묻기 위해 카프카 전체를 구동하는 수준의 무거운 명령어를 실행하면서 배보다 배꼽이 더 큰 상황이 발생했습니다.

문제의 부분

healthcheck:
  test: [ "CMD-SHELL", "kafka-broker-api-versions --bootstrap-server localhost:{포트번호} || exit 1" ]
  interval: 10s

 

해결 과정 (Step-by-Step)

Step 1. 가벼운 헬스체크로 교체

자바 프로세스를 띄우지 않고, 포트가 열려있는지만 확인하는 nc 명령어로 교체했습니다. 이 작업만으로도 CPU 스파이크의 90%가 사라집니다.

YAML
 
healthcheck:
  test: ["CMD-SHELL", "nc -z localhost:{포트번호} || exit 1"]
  interval: 10s

Step 2. JVM 옵션 최적화

컴파일러 스레드가 날뛰지 않도록 제한하고, 코드 캐시를 확보했습니다.

  • -XX:-TieredCompilation: 단계별 컴파일을 꺼서 컴파일러 부하를 단순화.
  • -XX:ReservedCodeCacheSize=256M: 최적화된 코드를 담을 공간을 충분히 확보.
  • -XX:CICompilerCount=2: 컴파일러 스레드 개수를 제한하여 CPU 독점 방지.

 

결과

140%까지 튀던 CPU가 3~5% 내외에서 안정적으로 유지됨.

+ Recent posts