처음 EC2를 띄운 날. 인스턴스는 'running'인데 SSH가 계속 타임아웃입니다. 한참을 헤매다 알게 됩니다 — 보안그룹에 22번 포트를 안 열어둔 것. 다음 날엔 테스트용으로 가장 큰 타입을 골랐다가 하루 만에 몇 만 원이 나갔습니다. 가상 서버 하나에도 타입·이미지·키·방화벽이라는 네 개의 손잡이가 있고, 어느 하나만 틀려도 못 쓰거나 비싸집니다.
- 1인스턴스 타입을 패밀리·세대·크기로 읽고 워크로드에 맞출 수 있다
- 2AMI가 무엇이고 왜 반복 생성·오토스케일링의 기반인지 안다
- 3키페어로 인스턴스에 안전하게 접속하는 방식을 이해한다
- 4보안그룹(가상 방화벽)으로 접근을 통제하는 원리를 안다
- 5접속 실패·과금 같은 대표 문제의 원인을 좁힐 수 있다
가상 서버의 네 가지 손잡이
인스턴스 타입 — 워크로드에 맞는 옷을 고른다
m5.large 같은 타입은 세 부분으로 읽습니다: 패밀리(m) + 세대(5) + 크기(large).
- t: 버스트형 범용(평소 적게, 잠깐 폭증) — 개발·소규모 웹에 저렴
- m: 균형 범용(CPU:메모리 균형) — 일반 서버
- c: 컴퓨트 최적(CPU↑) — 연산·인코딩
- r: 메모리 최적(RAM↑) — 캐시·인메모리 DB
워크로드 성향과 안 맞는 타입을 고르면 비싸거나 느립니다. CPU를 갈아 넣는 작업에 메모리 최적(r)을 쓰는 건 낭비입니다.
AMI·키페어·보안그룹 — 이미지·열쇠·방화벽
AMI는 인스턴스의 디스크 템플릿입니다. OS와 미리 설치된 소프트웨어가 담겨 있어, 같은 AMI로 동일한 서버를 반복 생성할 수 있습니다. 이게 오토스케일링(오토스케일링 + 로드밸런서)의 전제입니다.
키페어는 SSH 접속용 공개키/개인키 쌍입니다. 공개키는 인스턴스에 심기고, 개인키(.pem)는 내가 보관합니다. 비밀번호 대신 이 키로 신원을 증명합니다.
보안그룹은 인스턴스 앞단의 가상 방화벽입니다. "어떤 출발지에서 어떤 포트로 들어오는 것을 허용할지"를 정합니다. 기본은 전부 차단 — 명시적으로 연 것만 통합니다.
확대
위 그림은 인스턴스 타입·AMI·키페어·보안그룹 네 가지가 중앙 인스턴스를 둘러싸는 구조를 보여줍니다. 어느 하나만 잘못 설정해도 접속 불가 또는 과금 폭탄이 됩니다.
확대
위 그림처럼 같은 인스턴스라도 구매 방식에 따라 비용이 크게 달라집니다. 상시 운영 서버는 예약형으로, 배치·ML 작업은 스팟으로 조합하면 비용을 최적화할 수 있습니다.
확대
위 그림처럼 검증된 AMI를 하나 만들어두면 오토스케일링이 동일한 이미지로 인스턴스를 찍어냅니다. 손으로 서버를 설정하는 '스노우플레이크 서버'는 재현이 안 돼 장애 시 복구가 어렵습니다.
인스턴스를 하나 요청하면 실제로 무슨 일이 일어나나 — 요청부터 Running까지 6단계
콘솔의 Launch 버튼이나 aws ec2 run-instances 한 줄. 잠시 뒤 State가 pending에서 running으로 바뀝니다. 이 짧은 사이에 클라우드는 물리 자리 확보 → VM 배치 → 디스크 복제 → 네트워크 연결 → 부팅·초기화를 순서대로 처리합니다. 이 흐름을 알면 "인스턴스가 안 떠요", "running인데 접속이 안 돼요"를 서로 다른 단계의 문제로 좁혀 진단할 수 있습니다. 앞서 본 네 손잡이(타입·AMI·키·보안그룹)가 각각 이 흐름의 어느 단계에서 쓰이는지도 여기서 드러납니다.
[콘솔/CLI] run-instances (AMI · 타입 · 키페어 · 보안그룹 · 서브넷)
│
① 요청 접수 · 용량 확보 (요청한 AZ에서 그 타입의 물리 자리 찾기)
│
② 하이퍼바이저 배치 (물리 호스트에 VM 슬롯 할당 · 자원 예약)
│
③ 루트 볼륨 프로비저닝 (AMI 스냅샷 → 루트 EBS 볼륨으로 복제)
│
④ 네트워크 연결 (ENI 생성 · 서브넷 IP · 보안그룹 부착)
│
⑤ 부팅 · cloud-init (OS 부팅 → 공개키 심기 · user data 실행)
│
▼
[Running] 상태 running · 상태검사 2/2 통과 → 그제서야 SSH 가능
각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:
| 단계 | 하는 일 | 여기서 막히면 |
|---|---|---|
| ① 용량 확보 | 요청한 AZ에서 그 인스턴스 타입의 물리 자리를 찾는다 | 인기 타입·특정 AZ에 자리 없음 → InsufficientInstanceCapacity로 실행 거부(타입·AZ 바꿔 재시도) |
| ② 하이퍼바이저 배치 | 물리 호스트에 VM 슬롯을 할당하고 CPU·메모리를 예약 | 배치 실패는 대개 ①의 용량 문제로 표면화 |
| ③ 루트 볼륨 프로비저닝 | AMI 스냅샷을 루트 EBS 볼륨으로 복제해 부팅 디스크를 만든다 | AMI가 없거나 다른 리전·권한 없음 → InvalidAMIID / 비공개 AMI 접근 불가 |
| ④ 네트워크 연결 | ENI를 만들어 서브넷 IP를 붙이고 보안그룹을 부착 | 보안그룹이 22를 안 열면 running이어도 SSH가 무응답(timeout) / 퍼블릭 IP·라우팅 없으면 도달 불가 |
| ⑤ 부팅·cloud-init | OS를 부팅하고 cloud-init이 공개키를 심고 user data를 실행 | cloud-init 실패 → 키가 안 심겨 Permission denied / user data 스크립트 오류로 앱 미기동 |
| ⑥ Running·상태검사 | 상태를 running으로 올리고 시스템·인스턴스 상태검사(2/2)를 수행 | 상태검사 1/2 실패 → 부팅·네트워크 문제. 콘솔 시스템 로그로 원인 확인 |
즉 State가 running이라고 접속이 되는 건 아닙니다 — running은 ①~③(자리·배치·디스크)이 끝났다는 뜻이고, 실제 SSH는 ④(보안그룹·퍼블릭 IP)와 ⑤(cloud-init이 키를 심음)가 함께 맞아야 됩니다. 그래서 '인스턴스가 안 뜬다'(①③의 용량·AMI 문제)와 'running인데 접속이 안 된다'(④⑤의 네트워크·초기화 문제)는 완전히 다른 사건이며, 콘솔의 상태검사(2/2)와 시스템 로그(get-console-output)로 어느 단계에서 멈췄는지 좁히는 것이 진단의 출발점입니다.
띄우고, 접속하고, 막기
공식 Amazon Linux 2023 AMI를 조회합니다. AMI는 리전마다 ID가 다르므로 코드에 ID를 박지 말고 조회하거나 SSM 파라미터를 씁니다.
aws ssm get-parameters \
--names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query "Parameters[0].Value" --output text
ami-0abcd1234ef567890
aws ec2 describe-images모두에게 22번을 여는(0.0.0.0/0) 대신, 내 IP만 허용합니다. 공개 SSH는 즉시 봇의 무차별 대입 공격 대상이 됩니다.
MYIP=$(curl -s https://checkip.amazonaws.com)
SG=$(aws ec2 create-security-group --group-name web-sg \
--description "web" --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" \
--protocol tcp --port 22 --cidr "${MYIP}/32"
{ "Return": true, "SecurityGroupRules": [ { "CidrIpv4": "203.0.113.5/32", "FromPort": 22 } ] }
aws ec2 authorize-security-group-ingress조회한 AMI·키페어·보안그룹으로 t3.micro 한 대를 띄웁니다.
aws ec2 run-instances \
--image-id ami-0abcd1234ef567890 \
--instance-type t3.micro \
--key-name my-key \
--security-group-ids "$SG" \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=demo}]'
... "InstanceId": "i-0123456789abcdef0", "State": { "Name": "pending" } ...
aws ec2 run-instances- run-instances 직후 State가 pending→running으로 바뀌는지 — running이어야 접속 시도 가능
- 보안그룹 인바운드 규칙에 0.0.0.0/0 + 22(SSH)나 + 3389(RDP)가 있는지 — 있으면 전 세계에 관리 포트 개방. 즉시 내 IP/32로 축소
- 인스턴스에 태그(Name 등)가 붙어 있는지 — 태그 없는 자원은 나중에 '이게 뭐였지'가 되고 비용 추적도 안 됨(클라우드 비용 최적화)
- 퍼블릭 IP가 할당됐는지 + 어떤 서브넷인지 — 접속하려면 퍼블릭 서브넷·공인 IP·라우팅이 맞아야 함
상황: 인스턴스는 running인데 SSH가 타임아웃(거부가 아니라 무응답).
원인: 거의 항상 네트워크 접근 통제. ① 보안그룹 인바운드에 내 IP의 22번이 없음, ② 인스턴스가 프라이빗 서브넷이거나 공인 IP가 없음, ③ 서브넷 라우팅이 인터넷 게이트웨이로 안 빠짐. 'Connection refused'(즉시 거부)면 포트는 열렸으나 sshd 미동작 등 다른 문제.
진단: 보안그룹 인바운드 규칙 확인 → 인스턴스의 퍼블릭 IP·서브넷 확인 → 라우팅 테이블 확인.
해결: 보안그룹에 현재 내 IP/32 : 22 추가. 인스턴스가 인터넷에서 닿아야 하면 퍼블릭 서브넷+공인 IP+IGW 라우팅을 맞춤 — 이 구조는 VPC와 서브넷에서 자세히 다룹니다. 키 권한 오류(Permissions 0644 too open)면 chmod 400 key.pem.
심화 — 싼 타입에는 조건이 붙어 있다
심화: t 계열의 버스트는 외상이다 — CPU 크레딧의 동작 원리
t 계열이 "가장 저렴"한 데는 조건이 있습니다. t 계열은 CPU를 정가로 쓰는 게 아니라 크레딧을 모아 외상으로 쓰는 타입입니다.
- 기준선(baseline): 상시 보장되는 성능은 일부뿐입니다 — t3.micro는 vCPU당 약 10%, t3.medium은 20% 수준. 기준선 아래로 쓰는 동안 크레딧이 쌓이고, 부하가 튀면 크레딧을 태워 100%까지 버스트합니다.
- 크레딧이 바닥나면: standard 모드에서는 성능이 기준선으로 강제 제한됩니다. CPU 그래프가 100%가 아니라 특정 %에서 평평하게 눌리는 특유의 패턴이 나타납니다. unlimited 모드(t3의 기본값)에서는 제한 대신 초과 크레딧이 과금됩니다 — 성능은 유지되지만 청구서가 조용히 늘어나는 쪽으로 함정이 바뀝니다.
- 판단 지표: CloudWatch의
CPUCreditBalance(잔고)와CPUSurplusCreditsCharged(초과 과금)를 봅니다. 하루 평균 CPU가 기준선을 상시 넘는 워크로드라면 t 계열은 이미 틀린 선택입니다 — 초과 과금이 계속 붙는 t3보다 m 계열 정가가 싸지는 지점이 옵니다.
"가장 싼 타입"의 정확한 뜻은 "가끔만 바쁜 워크로드에 한해 가장 싼 타입"입니다. right-sizing은 크기만이 아니라 과금 모델까지 워크로드에 맞추는 일입니다(클라우드 비용 최적화).
상황: t3.medium 두 대로 돌던 API 서버가 이벤트 트래픽의 처음 30분은 잘 버티다가 갑자기 응답이 밀리기 시작합니다. CPU 그래프는 100%가 아니라 정확히 20% 부근에서 평평 — 과부하처럼 보이지 않아 원인을 못 찾고 헤맵니다.
원인: CPU 크레딧 소진. 이 인스턴스는 standard 모드로 설정돼 있었고, 30분 동안 쌓아둔 크레딧을 태우며 버스트하다 잔고가 0이 되는 순간 기준선(vCPU당 20%)으로 강제 제한된 것입니다. "CPU 여유가 있는데 느리다"처럼 보이는 이유는 그 20%가 곧 상한이기 때문입니다.
진단: CloudWatch에서 CPUCreditBalance가 0으로 떨어진 시각과 지연 급증 시각이 일치하는지 확인 → CPUUtilization이 기준선 %에서 평평한 플래토를 그리는지 확인.
해결: 응급 조치는 unlimited 모드 전환(중단 없이 가능) — 단, 그 순간부터 초과 크레딧 과금이 시작되는 임시방편입니다. 근본 조치는 상시 부하가 기준선을 넘는 워크로드를 m 계열로 옮기고, 이벤트성 폭증은 인스턴스 수로 받는 것(오토스케일링 + 로드밸런서)입니다. 하나 더 — 부하 테스트를 t 계열로 하면 크레딧이 남아 있는 동안만 통과하는 착시가 생기므로, 성능 검증은 고정 성능 타입으로 합니다.
실무에서 보안 감사의 단골 지적이 "0.0.0.0/0으로 열린 SSH/RDP/DB 포트" 입니다. 관리 포트는 내 IP·VPN·배스천(bastion) 경유로만 열고, 가능하면 SSH 대신 세션 매니저(키 없는 접속)를 씁니다.
또한 "인스턴스를 손으로 띄우고 손으로 설정"하는 방식(스노우플레이크 서버)은 재현이 안 돼 사고를 키웁니다. 검증된 AMI + 부팅 스크립트(user data) 또는 IaC(IaC와 Terraform)로 반복 가능하게 만드는 것이 실무 표준입니다.
관련 모듈로 더 깊이:
- 오토스케일링 + 로드밸런서 — 인스턴스를 수요에 맞춰 자동 증감하고 트래픽을 분산하는 법
- 매니지드 컨테이너 — VM 대신 컨테이너로 워크로드를 올리는 ECS·EKS 대안
- 오브젝트·블록·파일 스토리지 — 인스턴스에 붙이는 블록 스토리지와 오브젝트 스토리지의 선택 기준
다음 모듈에서는 이 인스턴스를 한 대가 아니라 수요에 따라 자동으로 늘렸다 줄이고, 앞에서 트래픽을 나눠주는 오토스케일링 + 로드밸런서를 다룹니다.