- Published on
EKS Bottlerocket 노드 SSH 없이 SSM으로 접속·디버깅
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에 SSH로 들어가 /var/log를 뒤지고 systemctl status를 보는 습관은 EKS에서도 쉽게 반복됩니다. 그런데 Bottlerocket을 노드 AMI로 쓰는 순간, "왜 SSH가 안 되지?"라는 질문을 거의 확실히 만나게 됩니다. 결론부터 말하면 정상입니다. Bottlerocket은 컨테이너 호스트에 특화된 최소 OS로 설계되어 SSH 데몬이 기본 제공되지 않고, 대신 SSM(Session Manager), 관리 컨테이너(admin container), control container 같은 방식으로 운영·디버깅을 하도록 유도합니다.
이 글에서는 다음을 목표로 합니다.
- Bottlerocket에서 SSH가 막힌 이유와 "올바른" 접근 모델 이해
- EKS 노드(EC2)에 SSM 접속을 가능하게 하는 필수 조건(네트워크/IAM)
- SSM 접속 후 노드 상태/로그/네트워크를 점검하는 실전 커맨드
- Kubernetes 관점에서 문제를 좁혀가는 디버깅 루틴
> 참고로, EKS 자체 권한/인증 문제로 디버깅이 막힐 때는 Kubernetes 401 Unauthorized 원인별 해결 가이드도 같이 보면 전체 그림을 잡는 데 도움이 됩니다.
Bottlerocket에서 SSH가 없는 이유
Bottlerocket의 운영 철학은 명확합니다.
- Immutable/atomic 업데이트: OS 업데이트를 패키지 단위가 아니라 이미지/슬롯 단위로 안전하게 교체
- 공격 표면 최소화: 불필요한 데몬(SSH 등)을 제거
- 운영 인터페이스 분리: OS 관리는 control/admin 컨테이너 또는 SSM 같은 표준 채널로 제한
즉, "SSH로 들어가서 이것저것 고친다"는 방식 자체가 설계와 충돌합니다. 대신 다음 경로를 씁니다.
- SSM Session Manager: 인바운드 22 포트 없이도 원격 셸/명령 실행
- Admin container: 호스트 파일시스템/툴 접근이 가능한 관리용 컨테이너(필요 시 활성화)
- Control container: Bottlerocket API/설정 변경을 위한 컨테이너
운영 환경에서는 보통 SSM + (필요할 때만) admin container 활성화 조합이 가장 무난합니다.
아키텍처: “노드에 SSH 대신 SSM”이 성립하려면
SSM으로 접속하려면 노드(EC2 인스턴스)가 다음을 만족해야 합니다.
- SSM Agent가 실행 중 (Bottlerocket에는 SSM 연동이 일반적으로 가능)
- 인스턴스에 연결된 IAM Role(Instance Profile) 이 SSM 권한을 가짐
- 네트워크가 SSM 엔드포인트로 아웃바운드 통신 가능
- 퍼블릭 서브넷: 인터넷/NAT 통해
ssm,ec2messages,ssmmessages접근 - 프라이빗 서브넷: NAT 또는 VPC Interface Endpoint(권장)
- 퍼블릭 서브넷: 인터넷/NAT 통해
SSH와 달리 인바운드 보안그룹에 22를 열 필요가 없고, Bastion도 필요 없습니다. 대신 “노드에서 AWS API로 나갈 수 있느냐”가 핵심입니다.
1) 노드 IAM Role에 붙일 SSM 권한
가장 간단한 방법은 AWS 관리형 정책을 붙이는 것입니다.
AmazonSSMManagedInstanceCore
Terraform 예시는 다음과 같습니다.
resource "aws_iam_role_policy_attachment" "node_ssm_core" {
role = aws_iam_role.eks_node_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
직접 최소 권한 정책을 구성할 수도 있지만, 운영 편의성과 변경 대응을 고려하면 노드에는 보통 위 관리형 정책이 실용적입니다.
> IRSA(서비스어카운트)와 노드 Role을 혼동하는 경우가 많습니다. SSM은 노드 인스턴스 Role 권한이 필요합니다. IRSA 관련 권한 꼬임을 점검하려면 EKS IRSA인데 AccessDenied? OIDC·TrustPolicy·SA 점검도 함께 확인하세요.
2) 프라이빗 서브넷에서 SSM이 안 붙을 때: VPC 엔드포인트
프라이빗 서브넷에 NAT가 없거나 egress가 제한된 환경이면 SSM 연결이 실패합니다. 이때는 VPC Interface Endpoint를 권장합니다.
필요한 엔드포인트(리전별):
com.amazonaws.<region>.ssmcom.amazonaws.<region>.ec2messagescom.amazonaws.<region>.ssmmessages
Terraform 예시(요지):
resource "aws_vpc_endpoint" "ssm" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.ssm"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
private_dns_enabled = true
security_group_ids = [aws_security_group.vpce.id]
}
resource "aws_vpc_endpoint" "ec2messages" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.ec2messages"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
private_dns_enabled = true
security_group_ids = [aws_security_group.vpce.id]
}
resource "aws_vpc_endpoint" "ssmmessages" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.ssmmessages"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
private_dns_enabled = true
security_group_ids = [aws_security_group.vpce.id]
}
엔드포인트 SG는 노드에서 443으로 접근할 수 있게 열어야 합니다(대개 같은 VPC CIDR 또는 노드 SG를 소스로).
3) SSM으로 Bottlerocket 노드에 접속하기
콘솔에서 접속
- AWS Systems Manager → Session Manager → Start session
- Managed instances에 노드 인스턴스가 보여야 함
안 보인다면 다음부터 먼저 의심합니다.
- 인스턴스 Role에
AmazonSSMManagedInstanceCore부재 - VPC egress/NAT/VPC Endpoint 미구성
- NACL/SG에서 443 egress 차단
CLI로 접속
로컬에 플러그인이 필요합니다.
- AWS CLI v2
- Session Manager Plugin
접속:
aws ssm start-session --target i-0123456789abcdef0
4) 접속 후 “어디를 봐야 하나”: Bottlerocket 디버깅 루틴
Bottlerocket은 일반 리눅스처럼 systemctl 기반으로 보는 것과 결이 다릅니다. 그래도 관찰 포인트는 크게 4가지입니다.
- 노드가 Ready인지 (kubelet/컨테이너 런타임/네트워크)
- CNI가 정상인지 (특히 NodePort/Pod 통신)
- 컨테이너 런타임(containerd) 상태
- 로그(저널/컨테이너 로그) 수집
(A) Kubernetes에서 먼저 노드 상태를 좁히기
kubectl get nodes -o wide
kubectl describe node <node-name>
kubectl get pods -A -o wide --field-selector spec.nodeName=<node-name>
Ready=False이면describe node의 Conditions/Events가 1차 단서- 특정 워크로드만 죽으면 해당 Pod 이벤트/로그를 먼저
(B) Bottlerocket admin container 활성화(필요 시)
SSM 셸로 들어간 뒤, 더 강력한 디버깅 도구가 필요하면 admin container를 켭니다.
Bottlerocket은 설정이 API 형태이므로(버전에 따라 방식이 다를 수 있지만) 보통 apiclient를 사용합니다. 예:
# admin 컨테이너 활성화
sudo apiclient set \
--json '{"settings":{"host-containers":{"admin":{"enabled":true}}}}'
# 적용 확인
sudo apiclient get --query settings.host-containers
활성화 후에는 admin 컨테이너로 들어가서 익숙한 리눅스 툴들을 사용할 수 있습니다.
sudo enter-admin-container
> 보안 관점에서는 상시 활성화보다 필요할 때만 켜고 작업 후 끄는 패턴을 추천합니다.
(C) 컨테이너 런타임/이미지/태스크 확인
Bottlerocket은 containerd를 사용합니다. admin 컨테이너에서 ctr/crictl로 확인하는 흐름이 일반적입니다.
# (환경에 따라) crictl이 있으면 가장 편함
sudo crictl ps -a
sudo crictl pods
sudo crictl logs <container-id>
# containerd 레벨 확인
sudo ctr -n k8s.io containers list
sudo ctr -n k8s.io tasks list
kubelet 문제인지, 특정 컨테이너만 문제인지 빠르게 나눌 수 있습니다.
(D) 네트워크/CNI 이슈 점검
EKS에서 네트워크 문제는 증상이 다양합니다.
- NodePort만 외부에서 접근 불가
- 특정 노드에서만 Pod 간 통신 실패
- DNS 지연/타임아웃
노드 레벨에서 볼 것은 크게:
- 라우팅/iptables
- ENI/IP 할당 상태
- CNI 플러그인 로그
admin 컨테이너에서:
ip addr
ip route
# 커널/iptables 기반 확인 (환경에 따라 nft/iptables 다를 수 있음)
sudo iptables -S || true
sudo iptables -t nat -S || true
특히 "NodePort만 안 열린다" 류는 CNI/보안그룹/노드 SG 규칙이 얽히는 경우가 많습니다. 이 케이스는 별도 체크리스트로 정리한 EKS에서 NodePort만 안 열릴 때 CNI·SG 점검을 같이 따라가면 원인 분리가 빨라집니다.
(E) 로그 수집: 저널과 Kubernetes 이벤트를 같이 본다
Bottlerocket은 로깅도 일반 배포판과 다를 수 있습니다. 그래도 핵심은 다음 조합입니다.
kubectl describe의 Events- CNI(aws-node), kube-proxy, CoreDNS 같은 시스템 Pod 로그
kubectl -n kube-system get pods -o wide
kubectl -n kube-system logs -l k8s-app=aws-node --tail=200
kubectl -n kube-system logs -l k8s-app=kube-proxy --tail=200
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=200
kube-proxy가 iptables 문제로 CrashLoopBackOff에 빠지는 경우도 종종 있습니다. 이런 경우는 노드의 iptables/커널 모듈과 맞물릴 수 있으니, 증상이 유사하다면 EKS kube-proxy CrashLoopBackOff iptables 오류 해결도 참고하세요.
5) “SSM 접속 자체가 안 된다” 빠른 체크리스트
현장에서 가장 시간을 잡아먹는 건 디버깅이 아니라 SSM 세션이 시작되지 않는 문제입니다. 아래 순서로 보면 대부분 해결됩니다.
IAM
- 노드 인스턴스 프로파일 Role에
AmazonSSMManagedInstanceCore가 붙어 있는가? - (조직 정책) SCP로 SSM이 막혀 있지 않은가?
네트워크
- 노드가
ssm/ec2messages/ssmmessages로 443 egress 가능? - 프라이빗 서브넷이면 NAT 또는 VPC Endpoint 3종이 있는가?
- VPC Endpoint SG가 노드 SG를 소스로 443 허용하는가?
- NACL이 443 및 ephemeral 포트를 막지 않는가?
인스턴스 상태
- 인스턴스가 Running이고, 시스템 상태 체크 통과?
- 시간 동기/메타데이터(IMDS) 접근이 비정상은 아닌가?
6) 운영 팁: Bottlerocket을 “SSH 없이도 편하게” 쓰는 패턴
- 문제 재현/관찰은 Kubernetes 레벨에서 먼저:
describe, 이벤트, 시스템 Pod 로그로 70% 해결 - 노드 접근이 필요하면 SSM을 표준 루트로 고정: 보안그룹/키페어/배스천 운영 부담 감소
- admin 컨테이너는 필요 시만 활성화하고 작업 후 비활성화
- 프라이빗 클러스터는 NAT 대신 VPC Endpoint 3종을 기본 구성으로 템플릿화
마무리
Bottlerocket에서 SSH가 안 되는 건 장애가 아니라 설계입니다. 접근 경로를 SSH에서 SSM으로 바꾸고, 디버깅 관점을 "노드에 들어가서 고친다"에서 "원인을 관찰하고 재배포/롤링으로 복구한다"로 바꾸면 오히려 운영이 단순해집니다.
다음 액션으로는:
- 노드 Role에
AmazonSSMManagedInstanceCore부착 - 프라이빗 서브넷이면 SSM VPC Endpoint 3종 구성
- SSM 세션 표준화 + admin 컨테이너는 필요 시만
이 3가지만 잡아도 Bottlerocket 기반 EKS에서 노드 디버깅 속도가 체감될 정도로 빨라집니다.