Terraform과 Ansible로 클러스터를 코드에서 다시 세울 수 있게 만드는 절차
클라우드 리소스는 Terraform, 온프레미스 노드 프로비저닝은 Ansible로 코드화하고, Day-0부터 전체를 재현 가능한지 검증하는 절차 정리.
목표
시리즈 1에서 손으로 실행한 절차(VM 준비, kubeadm init/join, CNI/MetalLB/스토리지 설치)를 코드로 옮긴다. 클라우드 리소스는 Terraform, 온프레미스 노드의 OS 레벨 준비는 Ansible로 나눈다.
개념: IaC(Infrastructure as Code)란 무엇인가
콘솔에 로그인해 클릭으로 서버를 만들거나, SSH로 접속해 손으로 패키지를 설치하면 "지금 이 서버가 왜 이런 상태인지"는 그 작업을 한 사람의 기억에만 남는다. 시간이 지나거나 담당자가 바뀌면 같은 환경을 다시 만들 방법이 없다 - IaC는 인프라의 상태를 코드로 선언해두고, 그 코드를 실행하면 실제 인프라가 그 상태가 되도록 만드는 방식이다. 코드 자체가 곧 "지금 인프라가 어떤 모습이어야 하는지"에 대한 살아있는 문서가 된다.
IaC 도구는 크게 두 방식으로 나뉜다.
- 선언적(declarative): "최종적으로 이런 모습이어야 한다"만 적으면, 도구가 현재 상태와 비교해서 차이만 계산해 적용한다. Terraform이 이 방식이다.
- 절차적(procedural): "이 순서대로 이 작업들을 해라"를 적어두면 그대로 실행한다. Ansible이 이 방식이다.
두 방식 모두에서 **멱등성(idempotency)**이 핵심이다 - 같은 코드를 몇 번을 실행해도 결과가 항상 같아야 한다. "이미 존재하면 건너뛰고, 없으면 만든다"는 판단을 도구가 대신 해주기 때문에, 사람이 "이거 이미 실행했었나?"를 기억할 필요가 없어진다.
역할 분담: 왜 두 도구를 같이 쓰는가
| Terraform | Ansible | |
|---|---|---|
| 대상 | 클라우드 리소스 (VPC, VM 인스턴스, 로드밸런서 등) | 이미 존재하는 온프레미스 서버의 OS 상태 |
| 모델 | 선언적, 상태 파일(state)로 리소스 존재 여부를 추적 | 절차적, 멱등성 있는 태스크를 순서대로 실행 |
| 적합한 이유 | 클라우드 리소스는 "생성/삭제"가 명확한 단위 | 온프레미스 서버는 이미 물리적으로 존재 - "생성"이 아니라 "구성" 대상 |
실제 구축 절차
1. Terraform - 클라우드 리소스 코드화
# terraform/main.tf
terraform {
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
resource "aws_instance" "cloud_worker" {
ami = "ami-0c02fb55956c7d316"
instance_type = "t3.medium"
subnet_id = aws_subnet.public.id
tags = { Name = "hybrid-cloud-worker" }
}
output "cloud_worker_public_ip" {
value = aws_instance.cloud_worker.public_ip
}
terraform init
terraform plan -out=tfplan # 무엇이 바뀌는지 먼저 확인
terraform apply tfplan
state 파일이 하는 역할: Terraform은 .tf 코드만 보고는 "이 리소스가 실제 클라우드에 이미 있는지"를 알 수 없다. terraform.tfstate라는 파일에 "내가 마지막으로 만든 리소스가 무엇이었는지"를 기록해두고, 다음 실행 때 이 state와 코드를 비교해 차이(diff)만 적용한다. state 파일이 사라지거나 실제 상태와 어긋나면 Terraform은 잘못된 판단을 내릴 수 있다.
state 파일은 로컬에 두지 않는다 - 여러 사람/여러 세션에서 같은 인프라를 다루려면 원격 백엔드(S3+DynamoDB 락 등)로 옮겨야 한다. DynamoDB 락은 두 사람이 동시에 apply를 실행해 state가 꼬이는 것을 막는 역할이다.
terraform {
backend "s3" {
bucket = "tfstate-bucket"
key = "cluster/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "tfstate-lock"
}
}
2. Ansible - 온프레미스 노드 프로비저닝
시리즈 1에서 손으로 했던 사전 준비(스왑 비활성화, 커널 모듈, containerd, kubeadm 설치)를 플레이북으로 옮긴다.
# ansible/inventory.ini
[control_plane]
node1 ansible_host=192.168.1.10
[workers]
node2 ansible_host=192.168.1.11
node3 ansible_host=192.168.1.12
# ansible/playbook.yml
- hosts: all
become: true
tasks:
- name: 스왑 비활성화
command: swapoff -a
- name: fstab에서 스왑 항목 주석 처리
replace:
path: /etc/fstab
regexp: '^([^#].*\sswap\s.*)$'
replace: '# \1'
- name: 커널 모듈 로드
modprobe:
name: "{{ item }}"
loop: [overlay, br_netfilter]
- name: containerd 설치
apt:
name: containerd
state: present
- name: kubeadm/kubelet/kubectl 설치
apt:
name: [kubelet, kubeadm, kubectl]
state: present
- hosts: control_plane
become: true
tasks:
- name: kubeadm init 실행 여부 확인
stat: path=/etc/kubernetes/admin.conf
register: kubeadm_done
- name: kubeadm init
command: >
kubeadm init --pod-network-cidr=192.168.0.0/16
--control-plane-endpoint={{ control_plane_vip }}:6443
when: not kubeadm_done.stat.exists
ansible-playbook -i inventory.ini playbook.yml
워커 조인은 컨트롤플레인에서 토큰을 뽑아 워커 그룹에 전달하는 태스크를 추가로 작성한다 - kubeadm token create --print-join-command의 출력을 register로 잡아 워커 호스트에 delegate_to 없이 전달하려면, Ansible의 set_fact + hostvars를 조합하거나 별도 스크립트로 토큰을 파일에 저장해 공유하는 방식을 쓴다.
3. Day-0 재구축 검증
코드화가 끝났다고 선언하기 전에, 실제로 전체를 처음부터 재현할 수 있는지 검증하는 단계가 필요하다.
# 1. 기존 클러스터 완전 파괴
ansible all -i inventory.ini -m command -a "kubeadm reset -f" --become
terraform destroy
# 2. 코드만으로 처음부터 재구축
terraform apply
ansible-playbook -i inventory.ini playbook.yml
# 3. 소요 시간 측정
# destroy 시작 ~ 클러스터가 다시 Ready 상태가 될 때까지 걸린 시간을 기록
이 재구축 중 스크립트로 처리되지 않고 사람이 개입해야 했던 지점(예: 토큰 재생성, 순서 의존성, 수동 확인이 필요했던 단계)을 전부 목록화한다 - 이게 "아직 코드화되지 않은 부분"이다.
검증 방법
terraform plan # apply 없이 실행해도 diff가 없어야("No changes") IaC가 실제 상태를 정확히 반영하는 것
ansible-playbook -i inventory.ini playbook.yml --check # dry-run으로 멱등성 확인 - 두 번째 실행부터는 변경사항이 없어야 함
체크리스트
- Terraform으로 클라우드 리소스 코드화 (원격 state 백엔드 포함)
- Ansible로 온프레미스 노드 프로비저닝 (kubeadm 사전 준비까지 자동화)
- Day-0 재구축 실험: 전체 파괴 후 코드만으로 재현
- 재구축 중 사람이 개입해야 했던 지점 목록화