
클릭 한 번으로 서버를 날렸다: IaC가 필요한 이유
AWS 콘솔에서 EC2 인스턴스를 정리하다가, 실수로 프로덕션 DB 서버를 종료했습니다. 식은땀을 흘리며 복구했던 그날의 경험을 통해, 왜 인프라를 코드로 관리해야 하는지(Infrastructure as Code), 테라폼(Terraform)이 어떻게 우리를 구원하는지 이야기합니다.

AWS 콘솔에서 EC2 인스턴스를 정리하다가, 실수로 프로덕션 DB 서버를 종료했습니다. 식은땀을 흘리며 복구했던 그날의 경험을 통해, 왜 인프라를 코드로 관리해야 하는지(Infrastructure as Code), 테라폼(Terraform)이 어떻게 우리를 구원하는지 이야기합니다.
서버를 끄지 않고 배포하는 법. 롤링, 카나리, 블루-그린의 차이점과 실제 구축 전략. DB 마이그레이션의 난제(팽창-수축 패턴)와 AWS CodeDeploy 활용법까지 심층 분석합니다.

ChatGPT는 질문에 답하지만, AI Agent는 스스로 계획하고 도구를 사용해 작업을 완료한다. 이 차이가 왜 중요한지 정리했다.

새벽엔 낭비하고 점심엔 터지는 서버 문제 해결기. '택시 배차'와 '피자 배달' 비유로 알아보는 오토 스케일링과 서버리스의 차이, 그리고 Spot Instance를 활용한 비용 절감 꿀팁.

내 서버가 해킹당하지 않는 이유. 포트와 IP를 검사하는 '패킷 필터링'부터 AWS Security Group까지, 방화벽의 진화 과정.

개발자라면 누구나 한 번쯤 겪는다는 '그 사건'은 평화로운 금요일 오후에 터졌습니다. 퇴근을 앞두고, AWS 비용 절감을 위해 안 쓰는 테스트 서버들을 정리하고 있었습니다.
"음, test-db-01, dev-api-server... 다 지워야지."
AWS 콘솔에서 체크박스를 탁탁 찍고, 'Terminate(종료)' 버튼을 눌렀습니다.
그리고 상쾌한 기분으로 슬랙을 켰는데, 알림이 쉴 새 없이 울리기 시작했습니다.
"🚨 [Critical] Production Database Connection Failed" "고객님들이 로그인이 안 된대요!!" "결제 오류 500 에러 속출!!"
등줄기에 식은땀이 흘렀습니다. 다시 AWS 콘솔을 확인했습니다.
아뿔싸. 제가 지운 건 test-db-01이 아니라 prod-db-01이었습니다.
이름이 비슷했던 탓에 실수를 한 거죠.
복구하는 데 꼬박 3시간이 걸렸습니다. 수동 설정이 얼마나 위험한지 온몸으로 느꼈습니다. 그리고 뼈저리게 느꼈습니다. "사람의 손가락은 믿을 게 못 된다. 인프라는 절대 클릭으로 관리하면 안 된다."
우리는 코드를 짤 때는 Git으로 버전 관리를 철저히 합니다. 누가 언제 무엇을 바꿨는지 다 기록되죠. 그런데 왜 인프라는 마우스 클릭(ClickOps)으로 관리할까요?
AWS 콘솔에서 수작업으로 인프라를 만들면 이런 문제들이 생깁니다.
이 모든 문제를 해결하는 유일한 길은 인프라를 코드로 관리하는 것 (Infrastructure as Code, IaC)입니다.
IaC 도구는 많지만, 저는 테라폼(Terraform)을 가장 좋아합니다. (물론 Pulumi나 CloudFormation도 훌륭합니다.) 테라폼은 우리가 원하는 인프라의 최종 상태(Desired State)를 코드로 정의하면, 알아서 그 상태를 만들어줍니다.
# main.tf
resource "aws_instance" "app_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "Production-Server"
# 이름을 코드로 박아두니 헷갈릴 일이 없습니다.
}
}
resource "aws_s3_bucket" "log_bucket" {
bucket = "my-company-logs"
acl = "private"
}
이제 "서버 만들어줘" 대신, 터미널에 딱 두 단어만 치면 됩니다.
terraform plan: "내가 이렇게 만들 건데, 맞는지 확인해볼래?" (미리보기)terraform apply: "좋아, 진행시켜!" (적용)이 과정은 마치 코드 리뷰와 같습니다.
"어? 김대리, 지금 프로덕션 DB 인스턴스 타입 바꾸는 코드가 들어있네? 이거 의도한 거야?"
terraform plan 결과를 보고 동료가 리뷰해줄 수 있습니다. 제 실수(DB 삭제)는 여기서 걸러졌을 겁니다.
IaC를 처음 접하면 가장 헷갈리는 게 바로 상태 파일(terraform.tfstate)입니다.
테라폼은 AWS에 실제로 뭐가 깔려있는지를 이 JSON 파일에 기록해둡니다. 일종의 '장부'죠.
그런데 팀원 A와 팀원 B가 동시에 테라폼을 돌리면 어떻게 될까요? A가 장부를 고치고 있는데 B가 와서 덮어써 버리면(Race Condition), 인프라는 엉망진창이 됩니다.
그래서 원격 상태 저장소(Remote State Backend)와 잠금(Locking) 기능이 필수입니다.
보통 AWS를 쓸 때는 이렇게 구성합니다.
tfstate 파일을 안전한 S3 버킷에 저장합니다. 팀원 모두가 이 파일을 봅니다.terraform apply를 실행하면, DynamoDB 테이블에 "현재 작업 중"이라고 표시(Lock)를 합니다. 다른 사람은 작업이 끝날 때까지 기다려야 합니다.terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-locks" # 잠금용 테이블
encrypt = true
}
}
이제 "야, 나 지금 배포하니까 너 건드리지 마!"라고 슬랙으로 소리칠 필요가 없습니다. 테라폼이 알아서 막아주니까요.
IaC를 도입해도 가끔 누군가(주로 급한 상사나 개발자)가 AWS 콘솔에 들어가서 몰래 설정을 바꿀 때가 있습니다. "급해서 보안 그룹 포트 잠깐 열었어." 이걸 드리프트(Drift, 설정 표류)라고 합니다. 코드는 닫혀있는데 실제로는 열려있는 위험한 상태죠.
테라폼은 이걸 기가 막히게 잡아냅니다.
$ terraform plan
# 결과:
# aws_security_group.web_sg will be updated in-place
~ ingress {
- cidr_blocks = ["0.0.0.0/0"] # 누군가 몰래 추가한 거 발견!
+ cidr_blocks = ["10.0.0.0/16"] # 원래 코드로 복구하겠다
}
매일 아침 terraform plan을 자동으로 돌려서(Cronjob), 코드와 실제 인프라가 다른지 감시하는 시스템을 만들 수도 있습니다. 이를 Drift Detection이라고 합니다.
덕분에 "누가 바꿨어?" 하는 범인 찾기 놀이를 할 필요가 없어졌습니다.
처음 IaC를 도입하면 스파게티 코드를 짜기 쉽습니다. 이것만 지켜도 평타는 칩니다.
dev, stage, prod를 폴더별로 나누세요. 하나의 main.tf에 모든 환경을 때려 박으면, 개발 서버 고치려다 운영 서버 날립니다. (terraform workspace 기능도 있지만, 폴더 분리가 더 직관적입니다.)module "web_server" { ... } 처럼 함수 호출하듯이 재사용할 수 있습니다..gitignore에 .tfstate 포함: 만약 로컬에서 상태 파일을 관리한다면 절대 깃에 올리지 마세요. DB 비밀번호 같은 민감 정보가 평문으로 들어있습니다. (원격 백엔드 사용을 강력 권장합니다.)보통 Terraform으로 뼈대를 만들고, Ansible로 내부 소프트웨어를 설정하는 조합을 많이 씁니다.
혼자서 테라폼을 쓸 때는 terraform apply를 로컬에서 돌려도 되지만, 팀 단위에서는 위험합니다.
누군가 로컬 버전을 업데이트 안 하고 돌리면, 이전 상태로 덮어씌워질 수 있거든요.
이때 등장하는 것이 GitOps와 Atlantis입니다.
Terraform을 위한 CI/CD 봇입니다.
terraform plan 결과를 PR 댓글로 달아줍니다.atlantis apply라고 댓글을 달면?apply를 실행하고, 결과를 댓글로 남긴 뒤 PR을 머지(Merge)합니다.개발자 로컬에는 AWS 키가 필요 없습니다. 모든 권한은 Atlantis 서버에만 있으면 됩니다. 보안과 협업 두 마리 토끼를 잡는 거죠.
IaC의 궁극적인 목표는 불변 인프라입니다.
서버에 들어가서 apt-get update를 치고 설정 파일을 고치는 건 가변(Mutable) 방식입니다. 시간이 지날수록 서버마다 상태가 달라집니다(Configuration Drift).
불변 인프라는 "서버를 고치지 않고, 새로 찍어냅니다." 보안 패치가 필요하면? 패치된 새 이미지를 만들어서 기존 서버를 날리고 새 서버(Blue/Green)로 교체합니다. 이러면 "어? 어제는 됐는데 오늘은 왜 안 되지?" 같은 유령 버그가 사라집니다.
IaC가 만능은 아닙니다. 러닝 커브(Learning Curve)가 꽤 높습니다. HCL(HashiCorp Configuration Language)이라는 새로운 언어를 배워야 하고, 모듈(Module)화 구조도 고민해야 합니다.
그리고 "기존 인프라 가져오기(Import)"가 정말 고통스럽습니다.
이미 손으로 잔뜩 만들어 놓은 AWS 리소스들을 테라폼 코드로 옮기려면 terraform import 명령어를 써야 하는데, 이게 꽤나 노가다 작업입니다. (요즘은 generated 블록으로 좀 편해지긴 했습니다.)
하지만 그 고통을 감수할 가치는 충분합니다.
새벽 3시에 서버가 터졌을 때, 수전증 걸린 손으로 AWS 콘솔을 클릭하는 대신, 침착하게 terraform apply 한 방으로 복구할 수 있다는 안심감. 이것만으로도 IaC를 할 이유는 차고 넘칩니다.
DevOps계에는 유명한 비유가 있습니다. "서버를 애완동물(Pets)처럼 키우지 말고, 가축(Cattle)처럼 다뤄라."
제 prod-db-01 서버는 애완동물이었습니다. 제가 하나하나 정성껏 설정을 만져준 탓에, 제가 아니면 아무도 못 건드리는 존재가 되었죠. 그래서 지워졌을 때 그렇게 절망했던 겁니다.
이제 제 서버들은 가축입니다. 테라폼 코드로 언제든 똑같이 찍어낼 수 있습니다.
화재가 나서 데이터센터가 날아가도, 저는 커피 한 잔 마시며 다른 리전(Region)에 terraform apply를 날릴 겁니다.
여러분의 인프라는 애완동물인가요, 가축인가요? 더 이상 클릭 한 번에 인생을 걸지 마세요. 코드로 기록하고, 코드로 보호하세요.