inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

발자국 3주차: 우아하게 종료하기, 우아하게 새로 켜기

kailis
2

  

 

해당 글은 인프런 워밍업 클럽 스터디 4기 - DevOps (쿠버네티스)를 진행하며 깊게 고민해 보려고 한 내용을 담습니다. 개괄적 정리보다 아는 것과의 비교를 통한 이해를 추구합니다.

 

우아하게 종료하기, 우아하게 새로 켜기

 

# 정상적인 프로세스 종료 (권장)
kill 1234
kill -15 1234

# 강제 종료 (SIGTERM이 안 될 때)
kill -9 1234    # SIGKILL

 

내가 서버와 관련된, 특히 프로세스와 관련된 생각을 할 때 가장 먼저 떠올리는 것은 [생명 주기]다.

 

잘 시작하는 것, 잘 살아 있는 것은 무엇보다 중요하지만, 내가 가장 중요하다고 생각하는 나의 가장 큰 관심사는.

 

바로 종료다.

 

잘 돌아가던 것은 잘못된 종료로 인해 한 순간에 와르르 실패할 수 있다.

 

따라서 배포는 기존의 것을 죽이고 → 새로운, 심지어 달라진 것을 올린다는 점에서 매우 중요한 작업이다.

 

지난주에 서버가 "살아있다"는 것의 의미를 깊이 고민했다면, 이번 주는 그 살아있는 서버를 어떻게 종료하고, 어떻게 새로운 코드를 안전하게 배포할 것인가를 파헤쳐봤다.

 

 

DevOps, 거대한 파이프라인

 

DevOps는 개발(Dev)과 운영(Ops)의 경계를 허물고 자동화를 통해 빠르고 안정적인 배포를 추구하는 문화이자 방법론.

 

개발 소스 → 커밋 → GitHub → CI/CD 환경 → 빌드 → 실행 파일 → 컨테이너 이미지 → k8s 배포

 

이 과정의 가장 처음 확인해야 하는 핵심은 무엇일까? 결국 실행 파일을 만드는 것이다.

 

DevOps가 아무리 복잡해 보여도, 개발 → 빌드 → 실행 파일이라는 본질은 변하지 않는다.

 

왜 환경을 나눠야 하는가

 

이에 따라 빌드 환경을 고려하는 것을 잊어서는 안 된다.

 

어디에서 실행하기 위해 빌드하는가? 무엇을 위해 분리하는가?

 

단일 환경으로 모든 것을 처리하려는 시도는 위험하다. 각 환경은 명확한 목적을 가지고 분리되어야 한다.

 

 

운영 환경에서 오픈소스를 도입할 때 이중화 가능 여부를 확인해야 하는 이유는 명확하다.

 

SPOF. 단일 장애점(Single Point of Failure)은 전체 서비스 중단으로 이어질 수 있기 때문이다.

 

예를 들어, 메시지 큐 시스템이 이중화를 지원하지 않는다면, 해당 시스템 장애 시 전체 서비스가 마비될 수 있다.

 

이제 상황에 따른 빌드 파일이 완료되었다면, 실제로 서버에 올릴 시간이다.

 

그냥 말고, 쿠버네티스랑 같이.

 

안 무서워?

쿠버네티스의 자동화된 배포는 분명 편리하지만, 나에게는 중요한 질문이 있었다.

 

"자동화된 배포가 정상적인 통신을 보장할 수 있는가?"

 

이번 주 강의에서는 그 안전장치에 대한 설정값을 보고, [가능성]을 확인하는 과정을 거쳤다.

 

쿠버네티스가 제공하는 안전장치

 

  1. Readiness Probe의 역할

readinessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5

 

트래픽 자체에 대한 [통신 가능성] 확인이다.

 

새 Pod가 트래픽을 받기 전에 정말 준비되었는지 확인한다.

단순히 프로세스가 떴는지가 아니라, DB 연결, 캐시 워밍업, 외부 API 연결 등 실제 서비스에 필요한 모든 것이 준비되었는지 검증할 수 있다.

 

  1. 점진적 롤아웃

strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

 

한 번에 하나씩만 교체하므로, 문제 발생 시 영향 범위를 최소화한다.

 

  1. 자동 롤백 메커니즘 새 버전의 Pod가 계속 실패하면 쿠버네티스가 자동으로 이전 버전을 유지한다.

 

그럼에도 남는 나의 두려움

 

물론 스프링 서버 단계에서 많은 걸 보완할 수 있다. 그리고 위의 것들로 많은 걸 보완할 수 있다.

 

하지만 하나하나 톺아 보면서 조금 더 든든하게 정리해 보자.

 

  1. 데이터 일관성 문제

 

이미 DB에 쓰인 데이터는 어떻게 할 것인가? 쿠버네티스는 애플리케이션 레벨의 트랜잭션을 관리하지 않는다.

 

이에 따라 생각한 나의 해결 방안은, 기존 서버를 [기존 프로세스가 실행될 때까지만] 살아 있게 하는 것이었다.

 

그리고 신규 서버로 인입되는 데이터에 대해서 확실한 표시를 하는 것.

 

그 부분을 쿠버네티스 매핑과 연결해 달라고, 클로드한테 부탁했는데.

 

 

 

  1. 로컬 캐시 불일치 :

    구 버전과 신 버전이 동시에 실행되면서 캐시 데이터가 불일치할 수 있다.

 

 

다음으로 떠오른 것은 로컬 캐시. 우리는 서버의 가용성을 위해 어떠한 정보들을 저장해 둔다.

 

이런 부분에 대해서 떠오른 해결 방안 또한 무효화와 버저닝이었다.

 

기존 캐시에 대해서 소멸 시간을 설정한 만큼 기존 서버를 살려 둘 수도 있다.

 

또한 새로운 캐시에 대해서는 버전 처리를 할 수 있다.

 

사실 가장 좋은 건 로컬 캐시에 이런 [사용자성] 데이터를 넣지 않는 편이 아닐까 싶지만.

 

이번 부분도 쿠버네티스 매핑과 연결해 보자면.

 

 

 

  1. 세션 문제 사용자가 구 버전에서 신 버전으로 넘어갈 때 세션이 유실될 수 있다.

     

 

 

그리고 세션. 아무래도 서버가 사라질 때 기존 서버에 붙어 있던 세션들도 함께 끊겨 버릴 수 있으니까.

 

이 부분도 똑같이 해당 프로세스가 마칠 때까지의 Sticky Session이 떠올랐다.

 

또는 세션 자체의 Store를 밖에 두는 것. 상황에 따른 의사결정이 필요한 시점 같다.

 

이번에도 클로드의 힘을 빌려 보자면.

 

 

이제 이 프로세스의 장점을 담아, 자동 배포를 위한 젠킨스를 실행해 보자.

 

Jenkins 파이프라인 구축 여정

 

1단계: 기본 구성

 

처음에는 Jenkins UI를 통해 각 단계를 개별 Job으로 구성할 수 있다.

 

 

처음엔 Jenkins UI에서 클릭클릭하며 Job을 만든다.

GitHub에서 소스 가져오고, Gradle로 빌드하고, Docker 이미지 만들고, kubectl로 배포하고.

 

각 단계마다 "Build Now" 버튼을 눌러야 했다. 수동으로 트리거해야 했고, 전체 흐름을 파악하기 어려웠다.

 

2단계: Pipeline으로의 전환

 

그러다 Pipeline의 시작. Jenkins Pipeline은 코드로 파이프라인을 정의하는 방식이다.

 

 

시각적인 Stage View가 제공되고, 각 단계별 소요 시간이 보이고, 무엇보다 Jenkinsfile로 버전 관리가 가능하다.

 

3단계: Blue/Green 배포 구현

 

다음부터는 배포 방식.

 

Blue/Green 배포는 두 개의 동일한 환경을 준비하고 트래픽을 한 번에 전환하는 방식이다.

 

 

 

Blue/Green 배포 - 쿠버네티스와 전통적 방식의 차이

 

사실 이 배포 방식에 대해서는 이해하고 있었지만, k8s에서 어떻게 진행하는지를 한 번 더 비교해 보고 싶었다.

 

그리고 정리한 것은 다음과 같았다.

 

전통적인 Blue/Green

 

기존의 블루/그린 전략은 다음과 같다.

 

 

쿠버네티스의 Blue/Green

 

# Service의 selector만 변경
selector:
  app: myapp
  version: green  # blue → green

 

그리고 쿠버네티스의 블루/그린.

 

핵심 차이점

 

image

이제 전체 배포 방식에 대해 비교해 보고 싶었고, 그 내용이 다음과 같았다.

내가 기존에 알고 있던 카나리를 포함해서.

 

배포 전략의 선택 기준

 

 

image

 

Jenkins 파이프라인으로 배포 자동화를 구축했지만, 곧 새로운 문제를 마주한다.

개발, 검증, 운영 환경마다 다른 설정값들. 서비스가 늘어날수록 관리해야 할 YAML 파일이 기하급수적으로 증가한다.

 

Helm과 Kustomize

 

Helm

Helm은 쿠버네티스의 패키지 매니저다. apt나 yum처럼 복잡한 애플리케이션을 쉽게 설치하고 관리할 수 있게 해준다.

 

핵심 개념

 

Helm은 이건 마치 프로그래밍의 함수와 같았다.

템플릿이라는 함수에 values라는 매개변수를 전달하면, 원하는 YAML이 생성되는 방식.

 

Kustomize란?

 

Kustomize는 YAML 파일을 직접 수정하지 않고 패치를 통해 커스터마이징하는 도구다.

kubectl에 내장되어 있어 별도 설치가 불필요하다.

 

핵심 개념

 

Kustomize의 접근법은 더 직관적이었다. "기본 YAML은 그대로 두고, 환경별로 다른 부분만 덮어쓰자"는. 

 

이 두 가지를 언제 선택해야 할까?

 

선택 기준

 

Helm을 선택해야 할 때

 

Kustomize를 선택해야 할 때

 

특히 와닿았던 부분은 "대부분의 오픈소스가 Helm 차트로 제공된다"는 점이었다.

실제로 Prometheus, Grafana, Redis 등을 설치할 때 Helm을 사용하지 않으면 수십 개의 YAML을 직접 관리해야 한다.

 

반면 작은 마이크로서비스 하나를 여러 환경에 배포할 때는 Kustomize의 단순함이 빛을 발할 것이다.

템플릿 문법을 배울 필요 없이 바로 사용할 수 있으니까.

 

이처럼 배포 방식이 단순해질수록, 그 배포를 자동화하는 CI/CD 도구의 선택도 함께 고민해야 한다.

 

어떤 도구를 선택하느냐에 따라 개발 생산성과 운영 효율이 크게 달라지기 때문이다.

 

CI/CD 도구 선택의 지혜

 

  1. 커뮤니티 활성도: Google Trends, GitHub Stars, Stack Overflow 질문 수

  2. 유지보수 지원: 상용 지원이 필요한가? 내부 전문가가 있는가?

  3. 인프라 요구사항: 온프레미스 필수인가? 클라우드 네이티브가 가능한가?

  4. 보안 요구사항: 외부 서비스 사용이 가능한가? 에어갭 환경인가?

 

예를 들어, 금융권처럼 보안이 중요한 환경에서는 GitHub Actions보다 Jenkins나 Tekton같은 온프레미스 솔루션이 적합하다.

 

반면 스타트업처럼 빠른 구축이 중요하다면 GitHub Actions가 더 나은 선택일 수 있다.

 

  

마무리하며

 

이번 주를 통해 배포는 더 이상 두려운 작업이 아닌, 체계적인 프로세스임을 배웠다.

 

하지만 쿠버네티스의 자동화된 배포가 만능은 아니다. 데이터 일관성, 버전 호환성, 상태 관리 등 여전히 개발자가 신경 써야 할 부분들이 많다.

 

적절한 도구와 전략을 선택하되, 그 한계를 어떻게 극복할지 명확히 인지하고 있어야 한다.

 

다음 주에는 ArgoCD를 통한 GitOps, 그리고 더 고도화된 배포 자동화를 다룬다고 한다.

 

배포의 여정은 계속된다!

답변 1

1

일프로

이번에도 잘 봤어요^^