k8s 이론 공부

Part 3 — 핵심 리소스

Ai와 함께 공부하자 2026. 4. 2. 16:13
728x90
반응형

Chapter 10. YAML 기본 문법

K8s 모든 리소스는 YAML 파일로 정의한다. YAML 읽고 알아야 K8s 있다.

용어: YAML — “YAML Ain’t Markup Language”의 약자. 사람이 읽기 쉬운 데이터 직렬화 형식이다. JSON과 비슷한 역할이지만 들여쓰기로 구조를 표현해서 더 읽기 편하다.

* YAML 핵심 규칙

# 주석은 # 으로 시작

# 키-값 쌍
name: nginx
replicas: 3
enabled: true

# 리스트 (- 로 시작)
ports:
  - 80
  - 443

# 중첩 구조 (들여쓰기 2칸)
metadata:
  name: my-app
  labels:
    app: web
    tier: frontend

# 여러 줄 문자열
description: |
  이것은 여러 줄
  문자열입니다.

주의사항: (Tab) 문자를 쓰면 에러가 난다. 반드시 스페이스(Space) 들여쓰기해야 한다. 보통 2 들여쓰기를 쓴다.

 


 

Chapter 11. K8s 리소스의 공통 구조

모든 K8s 리소스 YAML 4가지 최상위 필드를 가진다:

apiVersion: apps/v1      # 이 리소스가 속한 API 그룹과 버전
kind: Deployment          # 리소스 종류
metadata:                 # 이름, 라벨 등 메타정보
  name: my-app
  namespace: default
  labels:
    app: web
spec:                     # 원하는 상태 (Desired State)
  replicas: 3
  ...
  • apiVersion: 리소스마다 다르다. Pod은 v1, Deployment는 apps/v1, Ingress는 networking.k8s.io/v1. 외울 필요 없이 공식 문서나 kubectl explain 리소스명으로 확인하면 된다.
  • kind: 리소스의 종류. Deployment, Service, Pod, ConfigMap 등.
  • metadata: 리소스의 이름, 네임스페이스, 라벨 등. name은 필수이고, 라벨은 리소스를 그룹핑하고 선택(select)하는 데 핵심적이다.
  • spec: 이 리소스의 원하는 상태를 정의하는 곳. 리소스마다 spec의 내용이 다르다. K8s에서 가장 많은 시간을 보내게 되는 필드이다.

리소스가 생성된 후에는 status 필드가 추가된다. 이것은 K8s 관리하는현재 상태이며, 사용자가 직접 쓰지 않는다.

용어: 라벨(Label) — 리소스에 붙이는 키-값 쌍의 태그다. 예: app: web, env: production. 라벨 자체는 아무 기능이 없지만, 라벨 셀렉터(Label Selector)를 통해 특정 라벨을 가진 리소스만 골라서 작업할 수 있다. K8s에서 리소스 간의 연결은 대부분 라벨 셀렉터로 이루어진다.

 


 

Chapter 12. Pod — K8s 최소 배포 단위

* Pod이란?

용어: Pod — K8s에서 생성하고 관리하는 가장 작은 배포 단위다. 하나 이상의 컨테이너를 포함한다. “Pod”은 고래 떼(a pod of whales)에서 따온 이름이다.

핵심 포인트: K8s 컨테이너를 직접 관리하지 않는다. Pod 통해 컨테이너를 관리한다. 대부분의 경우 Pod 1 = 컨테이너 1개이다.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.25
      ports:
        - containerPort: 80

* Pod의 특징

1) Pod 안의 컨테이너들은 같은 네트워크 공간을 공유한다.

Pod 안에 컨테이너가 2 있으면, 컨테이너는 localhost 서로 통신할 있다. 같은 IP 가진다. , 같은 포트를 쓰면 충돌한다.

2) Pod 안의 컨테이너들은 같은 볼륨을 공유할 있다.

파일을 주고받아야 하는 컨테이너들을 같은 Pod 넣으면 편하다.

3) Pod 일시적(ephemeral)이다.

이것은 매우 중요한 개념이다. Pod 언제든 죽을 있고, 새로 만들어질 있다. 죽은 Pod수리되지 않고 Pod으로 교체된다. Pod 중요한 데이터를 저장하면 Pod 죽을 데이터도 사라진다. 그래서 영구 저장이 필요한 데이터는 별도의 볼륨(PersistentVolume) 사용해야 한다.

4) Pod 고유한 IP 가진다.

클러스터 내에서 Pod 자기만의 IP 가진다. 다른 Pod IP 통신할 있다. 그러나 Pod 재생성되면 IP 바뀐다. 그래서 Pod IP 직접 쓰면 되고, 나중에 배울 Service 통해 접근해야 한다.

* 멀티 컨테이너 Pod 패턴

대부분 Pod 하나에 컨테이너 하나를 넣지만, 특수한 경우 여러 컨테이너를 넣기도 한다.

사이드카(Sidecar) 패턴: 메인 컨테이너 옆에 보조 컨테이너를 붙인다.

  • 예: 메인 앱이 로그를 파일로 남기면, 사이드카 컨테이너가 그 파일을 읽어서 로그 수집 시스템으로 보낸다.
  • 예: 메인 앱 앞에 Envoy 프록시를 붙여서 트래픽을 관리한다 (서비스 메시 패턴).

init 컨테이너: Pod 시작되기 전에 먼저 실행되는 컨테이너. 순서대로 실행되고, 모두 성공해야 메인 컨테이너가 시작된다.

  • 예: DB가 준비될 때까지 기다리기, 설정 파일 다운로드, 권한 설정 등.

 


 

Chapter 13. ReplicaSet — Pod 개수를 유지

용어: ReplicaSet — 지정된 수의 Pod 복제본(replica)이 항상 실행되도록 보장하는 리소스다.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-rs
spec:
  replicas: 3                    # Pod 3개를 유지해
  selector:
    matchLabels:
      app: nginx                 # app=nginx 라벨을 가진 Pod을 관리 대상으로
  template:                      # 새 Pod을 만들 때 이 템플릿 사용
    metadata:
      labels:
        app: nginx               # selector와 일치해야 함!
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
  • selector와 template의 관계:
    • selector는 “어떤 Pod을 내가 관리할 것인가”를 정의하고, template은 “Pod을 새로 만들어야 할 때 어떤 모양으로 만들 것인가”를 정의한다. selector의 라벨과 template의 라벨이 반드시 일치해야 한다.

실무에서는 ReplicaSet 직접 만들지 않는다. 이유는 다음 챕터에서.

 


 

Chapter 14. Deployment — 가장 많이 쓰는 리소스

* Deployment = ReplicaSet + 업데이트 전략

용어: Deployment — ReplicaSet을 관리하면서, 앱의 업데이트(배포)를 안전하게 처리하는 리소스다. 실무에서 워크로드를 배포할 때 가장 많이 사용하는 리소스이다.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
          ports:
            - containerPort: 80
          resources:              # 자원 요청/제한 (실무 필수)
            requests:
              cpu: "100m"         # 최소 CPU (100 밀리코어 = 0.1 코어)
              memory: "128Mi"     # 최소 메모리
            limits:
              cpu: "500m"         # 최대 CPU
              memory: "256Mi"     # 최대 메모리
용어: 밀리코어(millicores) — CPU 자원의 단위다. 1000m = 1코어. “100m”은 0.1코어, “500m”은 0.5코어다.
용어: Mi (Mebibyte) — 메모리 단위다. 1Mi = 1,048,576 bytes (2^20). MB(메가바이트, 1,000,000)와는 약간 다르다. K8s에서는 보통 Mi, Gi를 쓴다.

* requests vs limits

개념은 실무에서 매우 중요하다.

  • requests: 스케줄러가 Pod을 배치할 때 사용하는 값. “이 Pod은 최소한 이만큼의 자원이 필요하다”는 의미. 스케줄러는 requests 만큼의 여유가 있는 노드에 Pod을 배치한다.
  • limits: 컨테이너가 사용할 수 있는 최대 자원. 이 값을 넘으면 CPU는 쓰로틀링(속도 제한)되고, 메모리는 OOMKill(Out Of Memory Kill, 강제 종료)된다.
용어: OOMKill — Out Of Memory Kill. 컨테이너가 메모리 제한(limits)을 초과하면 Linux 커널이 해당 프로세스를 강제로 종료한다. Pod의 상태가 OOMKilled로 표시된다.

실무 : requests limits 설정하지 않으면, Pod 노드의 자원을 무제한으로 사용할 있다. Pod 메모리를 먹어버리면 같은 노드의 다른 Pod들이 죽는다. 반드시 설정하자.

* 롤링 업데이트

Deployment 핵심 기능이다. 이미지 버전을 바꾸면 자동으로 롤링 업데이트가 실행된다.

spec:
  strategy:
    type: RollingUpdate          # 기본값
    rollingUpdate:
      maxSurge: 1                # 최대 1개까지 초과 생성 가능
      maxUnavailable: 0          # 최소 replicas 수 유지 (서비스 중단 0)

업데이트 과정을 쉽게 설명하면:

  1. 새 ReplicaSet(v2)을 만들고 Pod 1개를 생성한다.
  2. v2 Pod이 정상이면, 기존 ReplicaSet(v1)의 Pod 1개를 종료한다.
  3. 반복한다.
  4. 모든 Pod이 v2가 될 때까지 진행한다.

maxSurge: 업데이트 replicas 대비 최대 개까지 추가 Pod 생성할 있는가. : replicas=3, maxSurge=1이면 최대 4개까지 동시에 존재.

maxUnavailable: 업데이트 최소한 개의 Pod 항상 사용 가능해야 하는가. 0이면 서비스 중단 없이 업데이트.

* 롤백

kubectl rollout undo deployment/nginx-deployment        # 직전 버전으로 롤백
kubectl rollout undo deployment/nginx-deployment --to-revision=2  # 특정 버전으로
kubectl rollout history deployment/nginx-deployment     # 히스토리 조회
kubectl rollout status deployment/nginx-deployment      # 현재 상태 확인

 


 

Chapter 15. Service — Pod 안정적으로 접근하기

* 왜 Service가 필요한가

Pod 일시적이다. 죽으면 Pod 생기고, IP 바뀐다. replicas=3이면 3개의 Pod IP 있는데, 어느 것으로 요청을 보내야 할까? Pod 교체될 때마다 IP 추적해야 할까?

문제를 해결하는 것이 Service.

용어: Service — Pod 그룹 앞에 놓이는 안정적인 네트워크 엔드포인트다. 고정된 IP와 DNS 이름을 가지며, 트래픽을 여러 Pod에 분산한다.
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx              # app=nginx 라벨을 가진 Pod들에게 트래픽을 보냄
  ports:
    - protocol: TCP
      port: 80              # Service의 포트 (다른 Pod이 이 포트로 접근)
      targetPort: 80         # 실제 Pod의 컨테이너 포트
  type: ClusterIP            # Service 타입

실무 주의: port targetPort — port Service 자체의 포트이고, targetPort 트래픽이 전달되는 Pod 컨테이너의 포트다. 값이 다를 있다. 예를 들어 Service port=8080으로 들어온 요청을 Pod targetPort=3000으로 전달할 있다. targetPort 잘못 설정하면연결은 되는데 응답이 없는상황이 벌어진다.

* Service 타입

1) ClusterIP (기본값): 클러스터 내부에서만 접근 가능한 IP 부여한다. 외부에서는 접근 불가. 내부 마이크로서비스 통신에 사용한다.

2) NodePort: 클러스터의 모든 노드에 특정 포트(30000~32767) 열어서 외부에서 접근 가능하게 한다. 노드IP:NodePort 접근. 개발/테스트용.

3) LoadBalancer: 클라우드 프로바이더의 로드밸런서를 자동으로 프로비저닝한다. NHN Cloud에서 K8s Service 타입을 LoadBalancer 하면 NHN Cloud 로드밸런서가 자동으로 생긴다. 프로덕션에서 외부 트래픽을 받을 사용한다.

4) ExternalName: 외부 DNS 이름에 대한 별칭(alias) 제공한다. 쓰이지 않지만, 클러스터 외부의 서비스를 K8s 내부에서 접근할 있다.

* Service Discovery (서비스 발견)

Service 만들면 K8s 내부 DNS 자동으로 등록된다. 같은 네임스페이스 안에서는 Service 이름만으로 접근 가능하다.

# 같은 네임스페이스에서
curl http://nginx-service

# 다른 네임스페이스에서
curl http://nginx-service.다른네임스페이스.svc.cluster.local
용어: DNS(Domain Name System) — 도메인 이름(예: google.com)을 IP 주소로 변환하는 시스템이다. K8s 클러스터 안에도 내부 DNS 서버(보통 CoreDNS)가 있어서, Service 이름을 IP로 변환해준다.

 


 

Chapter 16. Namespace — 클러스터를 논리적으로 분리

용어: Namespace (K8s) — 하나의 물리 클러스터를 여러 개의 논리적 클러스터로 나누는 방법이다. Linux namespace와는 다른 개념이다.
apiVersion: v1
kind: Namespace
metadata:
  name: production

# 네임스페이스 안에 리소스 배포
kubectl apply -f deployment.yaml -n production

# 네임스페이스의 리소스 조회
kubectl get pods -n production

# 모든 네임스페이스의 리소스 조회
kubectl get pods --all-namespaces    # 또는 -A

* 기본 Namespace

K8s 클러스터를 만들면 기본으로 개의 namespace 존재한다:

  • default: 별도의 네임스페이스를 지정하지 않으면 여기에 생성된다.
  • kube-system: K8s 시스템 컴포넌트(CoreDNS, kube-proxy 등)가 돌아가는 곳. 건들지 않는 게 좋다.
  • kube-public: 모든 사용자(인증 안 된 사용자 포함)가 읽을 수 있는 공간. 거의 안 쓰인다.
  • kube-node-lease: 노드의 하트비트(살아있음 신호) 정보를 저장하는 곳.

언제 Namespace 나누는가

  • 환경별: development, staging, production
  • 팀별: team-a, team-b
  • 프로젝트별: project-x, project-y

Namespace 나누면 ResourceQuota 네임스페이스별 자원 제한을 있고, RBAC으로 네임스페이스별 접근 권한을 분리할 있다. (Part 5에서 자세히 다룬다.)

 


 

Chapter 17. ConfigMap Secret — 설정과 비밀 관리

* ConfigMap — 설정 데이터 분리

용어: ConfigMap — 설정 데이터를 키-값 쌍으로 저장하는 리소스다. 컨테이너 이미지에서 설정을 분리하여, 같은 이미지를 다른 설정으로 실행할 수 있게 한다.

필요한가? 앱의 DB 주소, 로그 레벨, 기능 플래그 같은 설정을 이미지 안에 하드코딩하면 설정을 바꿀 때마다 이미지를 다시 빌드해야 한다. ConfigMap으로 분리하면 이미지는 그대로 두고 설정만 바꿀 있다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  DATABASE_HOST: "db.example.com"
  DATABASE_PORT: "5432"
  LOG_LEVEL: "info"
  app.properties: |               # 파일 통째로 넣기도 가능
    server.port=8080
    cache.ttl=300

Pod에서 사용하는 방법 2가지:

1) 환경변수로 주입

spec:
  containers:
    - name: app
      image: my-app:1.0
      envFrom:                         # ConfigMap 전체를 환경변수로
        - configMapRef:
            name: app-config
      # 또는 개별 키만
      env:
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: DATABASE_HOST

2) 파일(볼륨)으로 마운트

spec:
  containers:
    - name: app
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config        # 이 경로에 파일로 마운트됨
  volumes:
    - name: config-volume
      configMap:
        name: app-config

 

* Secret — 민감한 데이터 관리

용어: Secret — ConfigMap과 비슷하지만, 비밀번호, API 키, 인증서 등 민감한 데이터를 저장하기 위한 리소스다. 값은 Base64로 인코딩되어 저장된다.
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=           # base64 인코딩된 "admin"
  password: cEBzc3cwcmQ=       # base64 인코딩된 "p@ssw0rd"

주의: Base64암호화가 아니다! Base64단순한 인코딩이다. 누구나 디코딩할 있다. Secret보안은 Base64아니라 K8s의 RBAC(접근 제어)와 etcd암호화(encryption at rest)의존한다. 프로덕션에서는 etcd 암호화를 반드시 활성화해야 한다.

Secret 사용법은 ConfigMap 거의 동일하다 (envFrom → secretRef, volumeMount 동일 패턴).

# Secret을 CLI로 만들기 (Base64 인코딩 자동 처리)
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password='p@ssw0rd'

 


 

Chapter 18. Volume — 데이터 영속성

* Pod은 죽으면 데이터도 사라진다

기본적으로 컨테이너의 파일 시스템은 컨테이너가 죽으면 같이 사라진다. 로그 파일, 업로드된 파일, DB 데이터전부 사라진다. 이것을 해결하는 것이 Volume이다.

Volume 종류

1) emptyDir: Pod 노드에 배치될 생성되는 디렉토리. Pod 삭제되면 같이 사라진다. 같은 Pod 안의 컨테이너끼리 데이터를 공유할 사용한다.

spec:
  containers:
    - name: app
      volumeMounts:
        - name: shared-data
          mountPath: /data
    - name: sidecar
      volumeMounts:
        - name: shared-data
          mountPath: /data
  volumes:
    - name: shared-data
      emptyDir: {}

2) hostPath: 노드(호스트) 파일 시스템 경로를 Pod 마운트한다. 노드의 특정 파일에 접근해야 사용한다. , Pod 다른 노드로 이동하면 이전 노드의 데이터에 접근할 없다.

volumes:
  - name: host-log
    hostPath:
      path: /var/log/app
      type: DirectoryOrCreate     # 없으면 디렉토리 생성

주의: hostPath 보안상 위험할 있다. 노드의 민감한 파일에 접근할 있기 때문이다. 프로덕션에서는 가급적 피하고, 필요하다면 readOnly 마운트하자.

3) PersistentVolume / PersistentVolumeClaim: Pod 죽어도 데이터가 살아있는 영구 볼륨. 이것은 Part 4에서 자세히 다룬다.

728x90
반응형
LIST

'k8s 이론 공부' 카테고리의 다른 글

부록: 핵심 용어 사전 (가나다순)  (0) 2026.04.02
Part 5 — 운영과 확장  (0) 2026.04.02
Part 4 — 네트워킹과 스토리지  (0) 2026.04.02
Part 2 — K8s 아키텍처  (0) 2026.04.02
Part 1 - 왜 k8s 인가?  (0) 2026.04.02