sayyoon.site
postsguestbook
🚢
SKALACloud

쿠버네티스 심화: 개념 정리

2025.05.17

이전에 배웠던 쿠버네티스 개념을 다시 한 번 짚고, 실제로 배포를 진행하며 쿠버네티스를 확실하게 이해해보자.
목표: 잘 돌아가는 내 서비스를 쿠버네티스 환경에서 돌아갈 수 있게 한다.

뒤로 갈수록 정돈이 덜 된 글. 추후 다듬을 예정

 

1일차

쿠버네티스

컨테이너 기반 애플리케이션을 자동으로 배포.확장.운영할 수 있게 해주는 오케스트레이션 플랫폼이다.
수십~수백 개의 컨테이너를 효율적으로 잘 띄우고, 잘 관리해주는 관리자 역할이라고 보면 된다.

 

동작 구조

  • etcd
    • 쿠버네티스 클러스터의 모든 상태 정보(리소스 상태, 설정 등)를 저장하는 분산 키-밸류 저장소
  • API Server
    • 사용자의 명령(kubectl 등)을 받아 클러스터에 전달하는 쿠버네티스의 관문
    • /api/v1, /apis/apps/v1 같은 REST API 경로로 리소스 접근
  • kubectl
    • 쿠버네티스 클러스터를 조작하는 명령어 툴

 

주요 리소스 역할

리소스 설명
Pod 애플리케이션 컨테이너 실행 단위 (최소 단위)
Deployment Pod의 버전/수량을 선언적으로 관리
ReplicaSet Pod 복제 개수 유지
Service 변하는 Pod IP 대신 고정 접근 경로 제공 및 로드밸런싱
Ingress 도메인/경로 기반 외부 접근 라우팅
ConfigMap / Secret 앱 설정값 / 민감정보 제공
Namespace 리소스 논리적 분리, 팀·환경 단위 격리
ServiceAccount / RBAC API 접근 권한 관리 (Role, ClusterRole)
Volume (스토리지) 여러 컨테이너 간 공유 저장 공간 제공

 

쿠버네티스 두 가지 흐름

쿠버네티스를 처음 배우면, 배포 흐름과 트래픽 흐름이 헷갈릴 수 있다. 아래처럼 나눠서 기억하면 이해가 쉬워진다.

  1. 내 컨테이너를 배포하는 흐름
    • DeploymentReplicaSetPod 생성 (컨테이너 실행) → etcd 기록 (모든 상태 저장)
  2. 내 앱 API에 접근하는 흐름
    • Service(ClusterIP/NodePort) → 필요 시 Ingress 설정 → 최종적으로 POD로 트래픽 전달

 

배포 및 업데이트 전략

  • Rolling Update
    서비스 중단 없이 하나씩 새로운 Pod를 배포하며 점진적으로 업데이트
  • Rollback
    문제가 생기면 즉시 이전 버전으로 복구
  • Blue/Green Deployment
    기존 서비스(Blue)와 새 버전(Green)을 동시에 유지 → 테스트 후 트래픽 전환으로 무중단 배포

 

Pod

쿠버네티스는 컨테이너를 직접 관리하는 것이 아니라, Pod라는 껍데기(?)를 하나 더 씌워서 관리한다.

왜 Pod가 필요한가?

  1. 컨테이너는 1개만 쓰는 게 아니다.
    실제 서비스에서는 앱실행 + 로그수집 + 프록시 설정 + 모니터링 등 여러 개 컨테이너가 동시에 묶여 돌아가야 할 때가 많다.
    이걸 하나로 묶어주는 게 Pod이다. (메인 컨테이너 + 보조 컨테이너 = 사이트카 패턴)

  2. 네트워크/스토리지 단위 제공
    Pod 안에 있는 모든 컨테이너는 같은 IP/같은 볼륨 을 공유한다.
    서로 localhost로 통신이 가능하다. → 컨테이너 간 연결이 훨씬 단순해진다.

  3. 쿠버네티스가 관리하기 쉬움
    쿠버네티스 입장에서는 수십 개 컨테이너를 일일이 관리하는게 아니라 Pod 단위로 뭉쳐서 관리하는게 더 효율적임

활용되는 케이스

  1. 단일 컨테이너 실행 (가장 흔함)
    • 예) fastapi, express, django 같은 단일 API 서버
    • 파드에 하나의 컨테이너만 올림
  2. 사이드카 컨테이너 패턴
    • 메인 컨테이너 + 보조 컨테이너 묶음
    • 예) 메인: Node.js API / 사이드카: 로그 수집기, 모니터링, 인증 프록시
    • 파드 단위로 같이 움직이니 관리가 쉬움
  3. Init Container 사용 (사전 작업)
    • 앱이 실행되기 전에 초기 설정이나 준비 작업이 필요할 때
    • 예) DB 연결 테스트, 설정 파일 다운로드
    • init container 작업이 끝나야 메인 컨테이너가 뜸

Pod의 라이프사이클

쿠버네티스는 파드를 일회용(?)처럼 다룬다.
실패하면 재시작하고, 스케일링하면 새로 생성하고, 업데이트하면 교체하고, 불필요하면 삭제하고..

파드가 죽고 새로 떠도 Deployment가 자동으로 다시 만들어주고,
파드가 바뀌어도 Service가 알아서 새 파드에 연결해주기 때문에
파드가 사라진다고 걱정할 필요는 없다. (매우 정상적이다.)

파드의 상태만 늘 확인하고, 왜 죽었고 왜 안 뜨는지 로그를 확인하는 습관을 가지자

내부 구조

구성 요소 설명
Init Container 메인 컨테이너 실행 전 준비 작업 수행 (예: 초기 설정, 권한 체크 등)
Main Container 실제 애플리케이션 실행 컨테이너
Sidecar Container 보조 역할 수행 (예: 로그 수집, 프록시 등)
Volume 여러 컨테이너 간 공유 데이터 공간 제공

 

Deployment

이제 파드에 대해서는 알 것이다. 그러면 Deployment는 왜 필요하고 어떻게 쓰일까?

Deployment는 파드를 선언적으로, 안정적으로 운영하게 해주는 관리자 느낌이다.
앞서 말했듯이 배포 후 언제든 죽거나 바뀔 수 있는 파드를 자동으로 관리해주는 역할을 한다.

왜 Deployment가 필요할까?

  1. 원하는 개수만큼 Pod 유지

  2. 버전 관리 + 롤링 업데이트

    • 새 이미지 배포하면 순차적으로 교체해서 무중단 배포 가능
    • 문제 생기면 바로 이전 버전으로 롤백 가능
  3. 선언적 관리

    • yaml로 정의하면 쿠버네티스가 알아서 유지시켜줌
  4. 자동 복구

    • 파드가 죽으면 사람의 개입 없이 자동으로 재생성함

실무에서는

  1. 버전 업데이트
    • 이미지 버전을 바꾸면, 쿠버네티스가 순차적으로 새 버전 Pod를 띄워준다.
  2. 문제 생겼을 때 롤백
  3. 스케일 조정

 

Service

왜 Service가 필요할까?

Pod는 유동적인 존재이기 때문에, 죽었다가 다시 뜨면 IP가 바뀌고 스케일을 늘리면 Pod가 여러 개가 된다.
이런 상황에서 사용자가 직접 Pod IP를 연결해야한다면 귀찮을 것이다. 그래서 Service가 등장했다.
Service는 Pod가 계속 바뀌더라고 고정된 이름(DNS)과 고정된 엔드포인트를 제공한다.

Service 동작 원리 살펴보기

  1. Label Selector
    Service는 Label Selector를 사용해 라벨을 가진 Pod에 트래픽을 보내라고 정의한다.
    selector:
     app: my-app
  2. Endpoint
    쿠버네티스는 Selector에 맞는 Pod를 찾아 Endpoint라는 리소스를 자동으로 만든다.
    • 이 Endpoint는 실제 Pod의 IP 목록을 담고 있다.
    • 즉, Service는 고정된 이름을 제공하고, Endpoint는 실제 대상 Pod의 위치(IP)를 기억한다.
  3. 트래픽 전달 & 로드밸런싱
    사용자가 Service를 호출하면
    Service → Endpoint → 여러 Pod 중 하나로 트래픽을 분산시킨다.

Service 타입 종류

서비스 타입 접근 가능 위치 사용 목적
ClusterIP (기본값) 클러스터 내부에서만 접근 가능 마이크로서비스 간 내부 통신
NodePort 외부에서 클러스터 노드의 IP:포트로 접근 가능 테스트나 단순 외부 접근
LoadBalancer 클라우드 Provider의 외부 로드밸런서 생성 (공인 IP 제공) 실서비스용 외부 공개
ExternalName 클러스터 외부 DNS 이름으로 라우팅
  1. ClusterIP

[클러스터 내부 사용자] → [ClusterIP Service] → [Pod] 2. NodePort [외부 사용자] → [노드IP:NodePort] → [NodePort Service] → [Pod] 3. LoadBalancer [외부 사용자] → [클라우드 LoadBalancer IP] → [LoadBalancer Service] → [Pod]

 

실전 서비스 동작 흐름

“내가 만든 API 서버”를 쿠버네티스에 배포한다고 가정해보자.

  1. Pod 실행 - 앱이 돌아가는 최소 단위
    가장 먼저 Pod가 실행된다.
    Pod는 컨테이너가 동작하는 최소 단위로, 여기 안에서 내가 만든 애플리케이션이 실제로 동작하게 된다. 예를 들어, my-app-pod라는 이름으로 하나의 Pod가 떴다고 생각해보자.
    하지만 이 Pod는 언제든 재시작될 수 있고, 그때마다 IP가 바뀐다.
    또, 트래픽이 많아지면 Pod를 여러 개로 늘릴 수도 있는데, 이때 각각의 Pod는 또 다른 IP를 갖게 된다. 그렇기 때문에 직접 Pod에 접근하는 건 현실적이지 않다.

  2. Service 생성 - 고정된 진입점 만들기
    이 문제를 해결해주는 게 바로 Service다.
    Service는 하나 이상의 Pod를 라벨(Selector) 을 통해 찾아서 연결해주는 역할을 한다.
    내가 my-app-service라는 이름으로 Service를 만들고, 이 Service가 app: my-app 라벨을 가진 Pod를 바라보게 설정해두면, 사용자는 Pod IP가 바뀌든 말든 항상 my-app-service만 호출하면 된다.
    Pod는 계속 바뀌지만, Service는 고정된 이름으로 접근할 수 있다는 게 핵심이다.

  3. 사용자 호출 - 진짜 트래픽 전달
    이제 사용자가 실제로 http://my-app-service:80 으로 호출하면, 쿠버네티스는 Service를 통해 적절한 Pod로 자동으로 트래픽을 라우팅해준다.
    만약 Pod가 하나가 아닌 여러 개라면, Service가 알아서 로드밸런싱도 해준다. 사용자는 이런 내부 동작을 몰라도 되고, 그냥 고정된 주소만 알고 있으면 된다.

  4. Pod 교체/스케일 조정 - 서비스는 여전히 안정적
    애플리케이션이 업데이트돼서 새로운 Pod가 뜨거나, 트래픽이 많아져서 Pod를 3개, 5개로 늘리더라도 Service는 변경된 Pod를 자동으로 찾아 연결해준다.
    내부 Pod는 바뀌어도, 사용자는 여전히 같은 경로로 서비스에 접근할 수 있다는 게 쿠버네티스 서비스의 진짜 가치다.

Ingress와 연결 - 외부 접근까지 확장하기

여기까지는 클러스터 내부 이야기였다. 하지만 실제 서비스는 외부 사용자가 접속해야 의미가 있다.
이때 사용하는 게 바로 Ingress다. Ingress는 외부 도메인이나 경로를 기준으로 적절한 Service로 트래픽을 연결해주는 역할을 한다.

예를 들어, 사용자가 브라우저에서 https://myapp.com/api 를 호출하면, Ingress가 이 요청을 받아 /api 경로는 my-app-service 로 연결하고, Service는 다시 my-app-pod 로 트래픽을 전달한다.

이렇게 도메인 → Ingress → Service → Pod 흐름이 만들어지는 것이다.

 

나만의 상황 구성 (시나리오)

  • 나: 백엔드 서버를 쿠버네티스에 올림 (Pod + Service 생성 완료)
  • 프론트엔드 팀: 로컬에서 API를 연결하려고 함 (Postman, fetch, axios 등)

필요한 것

  1. Pod + Service만 만든 상태
    👉 내부 클러스터 안에서는 통신 가능”
    👉 하지만 외부(프론트엔드 로컬)에서는 접근 불가
    왜냐면 Service는 내부 IP 만 제공하니까..!

  2. 외부에서 접근 가능하게 하려면?

  • Service 타입을 NodePort or LoadBalancer 로 지정하거나
  • Ingress + 도메인 + 포트 오픈 작업이 추가로 필요

실무에서는

  1. Service + Ingress 설정

    • Ingress에 도메인 또는 고정 IP를 연결하고 포트를 오픈
  2. NodePort 방식

    • 별도 도메인 없이 IP:Port 로 접근 가능
구분 NodePort 방식 Ingress + 도메인 방식
접근 방법 http://<서버IP>:<Port> https://도메인
설정 난이도 비교적 간단 (Service에 NodePort 설정만 하면 됨) 복잡 (Ingress + 도메인 + TLS 인증서 등 설정 필요)
보안/신뢰도 낮음 (보통 http, 공개 노출 위험) 높음 (보통 https, 도메인 + TLS)
외부 노출 제어 노출된 Node의 모든 IP와 포트 접근 가능 도메인, 경로 단위로 세밀한 접근 제어 가능
운영 적합성 개발/테스트용에 적합 운영/프로덕션에 적합
로드밸런서 필요 여부 불필요 (노드 포트 직접 노출) 필요 (Ingress Controller 사용)

정리

Service : 사용자가 접근할 수 있는 고정된 진입점 제공 + 로드밸런싱 담당
Endpoint : 실제 Pod들의 위치(IP) 를 기억
Ingress : 외부 도메인/경로 → Service 연결 담당
Pod : 애플리케이션이 실제로 돌아가는 실행 단위

2일차

Ingress

k8s 클러스터 내부로 들어오는 외부 요청을 라우팅(경로 지정)하는 역할

왜 필요한가?

  1. IP 없이도 도메인으로 접근 가능하게 해줌
    • 여러 서비스가 하나의 IP(도메인) 로 노출될 수 있음
  2. 경로(/api, /admin 등) 기반 라우팅
    • 여러 서비스가 경로에 따라 나뉘어 서비스될 수 있음
  3. TLS(HTTPS) 보안 연결 제공
    • 인증서를 통해 HTTPS 보안 연결 가능
  4. 로드밸런싱
    • 여러 Pod 중 하나를 자동으로 분산 처리 가능

동작 구조

클러스터 외부 → 인그레스 컨트롤러 → 서비스 → 파드

  1. 사용자가 https://exdomain.com/api 요청
  2. Ingress Controller가 요청 수신
  3. Ingress에 정의된 규칙에 따라 어떤 서비스로 보낼지 결정
  4. 서비스가 해당 Pod로 트래픽 전달

Ingress vs Service

인그레스에 대해 간략하게 알아보았다면, 인그레스와 서비스의 차이가 궁금해질 것이다.

Service

클러스터 내부 네트워크 연결 담당 (Pod 접근용)

Ingress

외부 → 클러스터 진입 경로 담당 (도메인/경로 라우팅)

즉, 인그레스만 만들어서 외부 접근이 되는 게 아니고, 반드시 서비스와 연결되어 있어야 한다.
외부 접근을 위해서는 둘 다 필요한 관계,,

Ingress Controller란?

  • Ingress 리소스(yaml)를 실제로 처리하는 소프트웨어
  • Ingress 리소스는 그냥 설정일 뿐, 이를 실제로 네트워크 레벨에서 적용하는 건 컨트롤러의 역할

대표 도구로 Nginx, Traefik, Istio 등이 있다.

 

TLS란? (Transport Layer Security)

네트워크 통신을 암호화하여 도청.위조 방지를 보장하는 표준 프로토콜

개인정보 보호, 위변조 방지, 신뢰성 확보(웹 브라우저 자물쇠 아이콘)을 위해서 TLS가 필요하다.

동작 원리

시나리오 : 사용자가 https 웹사이트 접속

  1. 서버 인증서 제공
    • 사용자가 사이트 접속 시 example.com 서버가 인증서를 사용자에게 제공
  2. 사용자(브라우저)는 CA 검증
    • 서버가 제공한 인증서가 공신력 있는 CA가 발급한 것인지 확인
  3. 키 교환 & 암호화 통신 시작
    • 검증이 완료되면, 서버-사용자 간 암호화된 세션을 생성하고 안전하게 통신 시작

CA (Certification Authority) 역할

신뢰 - 전 세계 브라우저/운영체제가 신뢰하는 인증서 발급 기관 인증서 발급 - 도메인 소유자 확인 후 SSL/TLS 인증서 발급 예시 - Let’s Encrypt(무료), GlobalSign, DigiCert 등

CA 프로세스

  1. 인증서 요청 (CSR 생성)
    • 사용자가 공개키 기반으로 인증서 요청
  2. 도메인 소유 확인
    • CA가 사용자의 도메인 소유 여부 검증
  3. 인증서 발급
    • 서명된 인증서 반환
  4. 인증서 설치
    • 서버에 설치, HTTPS 활성화

cert-manager란?

k8s 환경에서 TLS 인증서 발급/갱신을 자동화해주는 컨트롤러

매번 후동 발급/설치가 불편하고, 자동 갱신이 필요하기(Let’s Encrypt는 90일 만료) 때문에 cert-manager가 필요하다.

동작 흐름

  1. ClusterIssuer/Issuer 정의
    • 어느 CA와 연결할지 설정
  2. Certificate 리소스 정의
    • 발급받을 도메인 지정
  3. 자동 발급 & Secret 저장
    • CA와 통신하여 TLS 인증서 발급 & Secret으로 저장
  4. Ingress에서 Secret 사용
    • TLS 암호화 활성화
[사용자 브라우저]
    ↓ HTTPS 접속
 [Ingress Controller (TLS)]  ← Secret (인증서 저장)
    ↓
 [Service]
    ↓
 [Pod]

[실습] k8s에 myapp 배포하기

© Powered by danmin