Published on

EKS에서 Bottlerocket로 노드 하드닝·롤백

Authors

서버 하드닝을 하다 보면 늘 같은 딜레마가 생깁니다. 보안을 강화하려고 설정을 촘촘히 쌓을수록 운영 복잡도가 올라가고, 장애 시 원복이 어려워집니다. 특히 EKS 워커 노드는 커널/컨테이너 런타임/에이전트 조합이 얽혀 있어, 패치 한 번이 곧 전체 노드 신뢰성에 영향을 줍니다.

Bottlerocket은 이 딜레마를 꽤 실용적으로 풀어줍니다. 목적은 단순합니다.

  • 컨테이너 실행에 필요한 최소 OS만 제공해 공격면을 줄이고
  • 이미지 기반(immutable) 업데이트로 예측 가능한 배포/롤백을 가능하게 하며
  • 운영자가 임의로 노드에 접속해 상태를 “손으로” 바꾸는 일을 구조적으로 줄입니다

이 글에서는 EKS에서 Bottlerocket을 선택했을 때 얻는 하드닝 이점과, 업데이트/롤백을 실제로 어떻게 설계하는지(그리고 어디서 자주 막히는지)를 중심으로 정리합니다.

Bottlerocket이 노드 하드닝에 유리한 이유

1) 불변(Immutable) OS와 이미지 기반 업데이트

Bottlerocket은 패키지 매니저로 이것저것 설치하고 누적 변경하는 방식이 아닙니다. OS는 이미지로 배포되고, 업데이트는 “새 이미지로 부팅 슬롯 전환”에 가깝습니다.

  • 변경이 누적되지 않으니 드리프트(노드마다 상태가 달라지는 문제)가 줄어듭니다.
  • 문제가 생기면 이전 슬롯으로 되돌리는 롤백이 명확합니다.

2) 최소 구성, 기본적으로 줄어든 공격면

일반적인 범용 리눅스 AMI는 편의 도구가 많고, 그만큼 공격면도 넓습니다. Bottlerocket은 컨테이너 워크로드에 필요한 구성만 남기고 많은 부분을 제거합니다.

  • 불필요한 데몬/패키지 감소
  • 기본적으로 읽기 전용에 가까운 시스템 파티션 설계
  • 설정은 API를 통해 관리(사람이 SSH로 들어가서 수정하는 흐름을 억제)

3) 운영 접근 모델이 바뀜: SSH 대신 admin 컨테이너

Bottlerocket은 기본적으로 SSH 접근을 전제로 하지 않습니다. 대신 SSM 기반의 admin 컨테이너 또는 제어 채널을 통해 트러블슈팅을 합니다.

이 모델이 불편하게 느껴질 수 있지만, 보안 관점에서는 장점이 큽니다.

  • 노드에 대한 상시 SSH 오픈 필요성이 줄어듭니다.
  • “누가 언제 무엇을 바꿨는지”가 더 통제 가능한 경로로 수렴합니다.

EKS에서 Bottlerocket 도입 아키텍처 선택지

EKS에서 Bottlerocket을 쓰는 대표적인 방식은 아래 2가지입니다.

  1. 관리형 노드 그룹(Managed Node Group, MNG) + Bottlerocket AMI
  2. Karpenter + Bottlerocket AMI

운영 단순성을 우선하면 1)로 시작하는 경우가 많고, 워크로드 변동이 크거나 노드 프로비저닝 최적화가 중요하면 2)가 잘 맞습니다.

이 글은 롤백/하드닝 관점이므로, 먼저 MNG 기준으로 설명하되 Karpenter에서의 차이점도 함께 짚겠습니다.

실전: 관리형 노드 그룹에 Bottlerocket 적용

1) 노드 그룹 생성 예시 (eksctl)

아래 예시는 Bottlerocket 기반 노드 그룹을 만드는 eksctl YAML 예시입니다. 환경에 맞게 clusterName, 서브넷, IAM 설정은 조정하세요.

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: demo-eks
  region: ap-northeast-2

managedNodeGroups:
  - name: br-ng
    amiFamily: Bottlerocket
    instanceType: m6i.large
    desiredCapacity: 3
    minSize: 3
    maxSize: 6
    volumeSize: 50
    labels:
      os: bottlerocket
    taints:
      - key: bottlerocket
        value: "true"
        effect: NoSchedule
    updateConfig:
      maxUnavailable: 1

포인트는 amiFamily: Bottlerocket 입니다. 이 값 하나로 Bottlerocket AMI가 선택됩니다.

  • taints를 걸어두면, 처음에는 특정 워크로드만 옮겨 “점진적 전환”이 가능합니다.
  • updateConfig.maxUnavailable로 업데이트 시 동시에 빠질 노드 수를 제한해 가용성을 지킵니다.

2) 워크로드를 점진적으로 이동시키는 패턴

Bottlerocket으로 옮길 때는 한 번에 전체를 바꾸지 말고, 아래처럼 “스케줄링 제어”를 강하게 추천합니다.

  1. Bottlerocket 노드에 taint를 걸어 기본 스케줄링을 막고
  2. 특정 워크로드에만 tolerations를 넣어 선택적으로 배치

예시:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      tolerations:
        - key: bottlerocket
          operator: Equal
          value: "true"
          effect: NoSchedule
      nodeSelector:
        os: bottlerocket
      containers:
        - name: api
          image: public.ecr.aws/nginx/nginx:stable
          ports:
            - containerPort: 80

이렇게 하면 “하드닝된 노드 풀”을 별도로 운영하면서, 문제 없는 워크로드부터 이관할 수 있습니다.

업데이트 전략: 하드닝과 롤백을 함께 설계하기

Bottlerocket을 쓰는 핵심 가치는 업데이트/롤백이 예측 가능해진다는 점입니다. 하지만 여전히 “노드 업데이트는 곧 파드 재스케줄링”이므로, 클러스터 레벨 전략이 필요합니다.

1) 기본 원칙: 노드 업데이트는 곧 드레인(Drain)이다

노드가 재부팅되거나 교체되면 해당 노드의 파드는 다른 노드로 이동합니다. 따라서 다음이 선행돼야 합니다.

  • PodDisruptionBudget로 최소 가용 파드 수 보장
  • HPA 또는 충분한 레플리카 수
  • 스테이트풀 워크로드는 PV 재부착 시간, 리더 선출 시간 고려

PDB 예시:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: api

2) 롤링 업데이트 vs 블루/그린 노드 풀

운영에서 가장 안전한 방법은 “노드 풀 블루/그린”입니다.

  • 기존 노드 그룹 ng-a 유지
  • 새 Bottlerocket 버전으로 ng-b 생성
  • 워크로드를 ng-b로 점진 이동
  • 문제 없으면 ng-a 축소/삭제
  • 문제 있으면 ng-b만 되돌리면 됨

이 접근은 비용이 일시적으로 증가하지만, 롤백이 가장 단순합니다.

3) 롤백을 빠르게 만드는 체크리스트

롤백은 “기술적으로 가능”보다 “운영 중 즉시 실행 가능”이 중요합니다.

  • 노드 그룹을 IaC로 관리해서, 특정 AMI 버전/릴리스를 즉시 재현 가능하게 하기
  • 애플리케이션 레벨에서 노드 교체 시 영향을 줄이기(연결 드레인, readiness 조정)
  • 장애 시 관측 포인트를 미리 정하기(노드 이벤트, CNI, kubelet, DNS)

특히 EKS에서 네트워크/egress 문제는 업데이트 과정에서 증상이 겹쳐 보일 수 있습니다. 예를 들어 “노드 교체 직후 특정 egress만 502가 난다” 같은 케이스는 원인이 애플리케이션이 아니라 Envoy/NLB 경로나 SNAT/라우팅일 수도 있습니다. 비슷한 유형의 추적 흐름은 다음 글도 참고할 만합니다.

Bottlerocket 운영에서 자주 만나는 함정

1) “노드에 들어가서 고치면 되지”가 잘 안 통한다

Bottlerocket은 의도적으로 그 길을 좁혔습니다. 대신 아래처럼 접근 방식을 바꿔야 합니다.

  • 로그/메트릭을 먼저 본다: CloudWatch, Prometheus, Fluent Bit 등
  • 노드 디버깅은 SSM 기반 admin 컨테이너를 사용한다
  • 변경은 사용자 데이터 또는 설정 API로 선언적으로 한다

즉, 노드에 쌓인 “수동 핫픽스”를 줄이는 대신, 관측과 자동화를 강화해야 합니다.

2) 시간 동기화 이슈는 보안/가용성에 직결

노드 시간 드리프트는 TLS, STS, OIDC 토큰 검증 등에서 실패를 만들 수 있습니다. OS가 바뀌면 “시간 동기화가 당연히 잘 되겠지”라고 가정하기 쉬운데, 실제 장애는 여기서도 납니다.

  • 증상: IRSA 토큰 오류, TLS handshake 실패, 서명 만료
  • 대응: 노드 시간 동기화 상태 확인, 클러스터 전반의 시간 일관성 점검

관련 증상과 해결 흐름은 아래 글이 직접적입니다.

3) 이미지 풀/레지스트리 인증 문제는 노드 교체 시 더 잘 드러난다

노드가 새로 뜨면 파드가 다시 풀링을 시도합니다. 기존 노드에서는 캐시로 버티던 문제가, 교체 타이밍에 한꺼번에 터질 수 있습니다.

  • 증상: ImagePullBackOff, ErrImagePull
  • 체크: ECR 토큰 만료, IRSA, 프라이빗 서브넷 NAT/엔드포인트

이 주제는 아래 글이 실전 체크리스트로 좋습니다.

롤백 시나리오를 실제로 연습하는 방법

롤백은 문서로만 존재하면 의미가 없습니다. “노드 업데이트 후 장애”를 가정하고, 아래를 게임데이로 돌려보는 것을 권합니다.

시나리오 A: 새 Bottlerocket 릴리스 적용 후 네트워크 이상

  1. 새 노드 그룹(또는 MNG 업데이트)로 일부 워크로드 이동
  2. 특정 외부 호출에서 지연/실패 발생
  3. 즉시 롤백: 워크로드를 기존 노드 그룹으로 되돌리고 새 노드 그룹 축소

이때 필요한 준비물:

  • nodeSelector 또는 affinity로 워크로드를 특정 노드 풀로 이동 가능해야 함
  • PDB/HPA가 설정되어 있어야 함
  • 관측 지표(에러율, p95, egress 실패)를 대시보드로 즉시 확인 가능해야 함

시나리오 B: 업데이트 후 일부 노드에서 kubelet 등록 실패

  • 새 노드가 NotReady로 남거나 조인 자체가 안 되는 케이스
  • 해결을 “노드 안에서 수리”로 가기 어렵기 때문에, 빠르게 노드 풀 교체로 수습하는 편이 낫습니다

즉, Bottlerocket에서는 더더욱 “문제 노드를 고쳐서 살리기”보다 “문제 노드를 버리고 재생성”이 운영 철학에 맞습니다.

Karpenter를 쓴다면: 롤백은 더 빠르지만 규칙이 중요

Karpenter 환경에서는 노드가 더 동적으로 생성/종료되므로, Bottlerocket의 장점(불변 이미지, 예측 가능한 노드)을 더 잘 살릴 수 있습니다.

  • 특정 AMI 버전(또는 릴리스 채널)을 명시하고
  • 문제가 생기면 해당 제약을 이전 버전으로 되돌린 뒤
  • 새로 뜨는 노드부터 정상 버전으로 복귀

다만 “너무 동적”이라서 관측/제약이 부실하면 장애가 더 빨리 확산될 수 있습니다. 최소한 아래는 권장합니다.

  • 워크로드별 topologySpreadConstraints로 한 AZ 쏠림 방지
  • 노드 풀 분리(시스템 파드용, 앱용)
  • 서서히 적용할 수 있는 카나리 노드 풀

결론: Bottlerocket은 하드닝의 비용을 ‘업데이트 설계’로 옮긴다

Bottlerocket을 도입하면 노드 자체의 공격면을 줄이고, 수동 변경을 구조적으로 억제할 수 있습니다. 대신 운영의 중심이 다음으로 이동합니다.

  • “노드에 접속해 고치는 운영”에서
  • “선언적으로 배포하고 관측하며, 필요하면 빠르게 교체/롤백하는 운영”으로

정리하면, Bottlerocket은 보안 하드닝을 쉽게 만들어주는 OS라기보다, 하드닝과 롤백을 함께 가능한 형태로 강제하는 OS에 가깝습니다. EKS에서 이 장점을 제대로 누리려면 노드 풀 분리, PDB/HPA, 점진적 스케줄링, 그리고 블루/그린 기반 롤백 루틴을 먼저 갖추는 것이 가장 효과적입니다.

필요하다면 다음 단계로는 admin 컨테이너 기반 디버깅 루틴, Bottlerocket 설정 관리(사용자 데이터/설정 API), 그리고 노드 교체 시 자주 터지는 egress/DNS/레지스트리 이슈에 대한 런북까지 묶어 “노드 하드닝 운영 체계”로 확장하는 것을 추천합니다.