Ansible vs Terraform 차이점과 선택 가이드
Terraform은 인프라 프로비저닝을 담당하고 Ansible은 서버 내부 설정을 구성합니다. 두 도구의 상태 관리 방식 차이와 인프라 구축 시 발생하는 데이터 삭제 위험을 방지하는 terraform plan 및 apply 활용법을 상세히 설명합니다.
Ansible과 Terraform을 한 문장으로 비교
Ansible과 Terraform은 동일한 작업을 수행하는 두 도구 사이의 선택 문제가 아닙니다. Terraform은 서버, 디스크, 네트워크, DNS 레코드와 같은 인프라가 무엇인지 선언합니다. Ansible은 이미 존재하는 머신 내부의 상태, 즉 패키지, 사용자, 설정 파일, 실행 중인 서비스가 무엇인지 선언합니다. Terraform은 VPS를 생성하고, Ansible은 그 VPS를 웹 서버로 만듭니다.
두 도구 모두 선언적이며, 둘 다 IaC(Infrastructure as Code)라고 불립니다. 진정한 차이는 무엇을 기억하느냐에 있습니다. Terraform은 코드의 모든 리소스를 API를 통해 생성된 실제 객체와 매핑하는 상태 파일을 작성하므로, 코드에서 5줄을 삭제하면 서버 1대를 제거해야 한다는 것을 파악할 수 있습니다. Ansible은 실행 사이에 아무것도 기억하지 않습니다. SSH로 연결하여 머신을 검사하고, 플레이북과 일치하지 않는 부분만 변경합니다.
이러한 차이점 하나가 이 가이드의 나머지 내용을 설명하며, 왜 두 작업을 하나의 도구로 섞어서 사용하는 것이 잘못된 방식인지도 보여줍니다.
Terraform의 실제 동작 방식
Terraform은 프로바이더 플러그인을 통해 API와 통신합니다. 프로바이더의 레지스트리 페이지에는 작성 가능한 리소스 유형이 정의되어 있으므로, 서로 다른 호스트의 서버는 서로 다른 리소스 이름과 인수를 가집니다.
resource "cloud_server" "web" {
name = "web1"
image = "ubuntu-24.04"
type = "small"
}
output "web_ip" {
value = cloud_server.web.ipv4_address
}cloud_server를 프로바이더 문서에 명시된 리소스 유형으로 교체하십시오. output 블록은 이 가이드에서 가장 중요한 부분이며, 주소가 Terraform을 떠나는 방식이기 때문입니다.
terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplanterraform init는 프로바이더를 다운로드하고 잠금 파일을 작성합니다. terraform plan는 코드와 상태 파일 간의 차이점을 출력하며, Plan: 1 to add, 0 to change, 0 to destroy.과 같은 줄로 끝납니다. 해당 줄을 매번 확인하십시오. 일부 인수는 제자리에서 변경할 수 없으며, 계획 결과에는 속성 옆에 # forces replacement이 표시되고 그 뒤에 1 to add, 0 to change, 1 to destroy이 나타납니다. 해당 계획을 적용하면 서버가 삭제되고 새로운 빈 서버가 생성되는데, 이것이 사용자가 안전하다고 생각했던 데이터를 잃게 되는 방식입니다.
단순히 terraform apply를 실행하는 대신 계획을 파일로 저장하고 해당 파일을 적용하면, 검토한 내용이 그대로 실행됨을 보장할 수 있습니다. 두 명령어 사이에 다른 사람이 인프라를 변경했을 수도 있기 때문입니다.
terraform.tfstate은 메모리 역할을 합니다. 이를 잃어버리면 Terraform은 해당 서버가 사용자의 것임을 인식하지 못하며, 다음 적용 시 중복 생성을 시도합니다. 두 명 이상의 사용자가 명령어를 실행하는 즉시 원격 백엔드에 저장하십시오. 동시에 적용을 시도하면 다음과 같은 결과가 발생합니다:
Error: Error acquiring the state lockOpenTofu는 Terraform의 포크 버전으로 동일한 명령어와 파일 형식을 사용합니다. 2026년 7월 기준으로, terraform 대신 tofu을 입력해도 이 가이드의 모든 내용은 동일하게 작동합니다.
Ansible의 실제 동작 방식
Ansible은 에이전트나 API가 필요하지 않습니다. 대상 서버에 SSH 연결을 맺고 작은 Python 모듈을 복사하여 실행한 뒤, 작업을 마치면 해당 모듈을 삭제합니다. SSH 접속과 sudo 권한으로 접근할 수 있는 모든 대상은 Ansible로 구성할 수 있습니다.
- name: Base web server
hosts: web
become: true
tasks:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Ensure nginx is running at boot
ansible.builtin.service:
name: nginx
state: started
enabled: trueansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.ymlping 모듈은 플레이북 디버깅을 시작하기 전에 SSH, Python, sudo가 정상적으로 작동하는지 확인합니다. 정상적인 결과는 web1 | SUCCESS => {"ping": "pong"}입니다. --check --diff 실행은 Ansible에서 계획과 가장 유사한 기능입니다. 실제 변경을 수행하지 않고 변경될 내용을 보고하지만, 이전 작업에 의존하는 태스크는 실제 변경이 일어나지 않았기 때문에 체크 모드에서 잘못된 결과를 보고할 수 있습니다.
모든 실행은 ok=6 changed=2 unreachable=0 failed=0과 같은 요약으로 끝납니다. 동일한 플레이북을 두 번 실행해 보십시오. 두 번째 실행에서는 changed=0이 보고되어야 합니다. 매번 변경됨(changed)으로 보고되는 태스크는 멱등성(idempotency)을 갖추지 못한 것이며, 보통 실제 모듈을 사용해야 할 상황에서 command이나 shell 태스크를 사용했을 때 발생합니다. 이 분야가 처음이라면 단일 VPS에서 첫 번째 Ansible 플레이북 작성하기부터 시작하여 점진적으로 확장해 보십시오.
두 도구의 기능이 겹치는 영역과 충돌하는 지점
Terraform은 remote-exec provisioner를 사용하여 새 서버에서 명령을 실행할 수 있습니다. HashiCorp의 공식 문서조차 provisioner를 최후의 수단으로 부릅니다. 여기에는 타당한 이유가 있습니다.
provisioner는 리소스가 생성될 때만 실행됩니다. 스크립트를 수정해도 기존 서버에는 아무런 변화가 없습니다. Terraform의 관점에서는 리소스가 이미 코드와 일치하기 때문입니다. provisioner 단계는 terraform plan에 나타나지 않으므로 코드 리뷰 시 이를 확인할 방법이 없습니다. 스크립트가 실패하면 Terraform은 해당 리소스를 tainted 상태로 표시하며, 다음 apply 시 정상 작동하던 서버를 삭제하고 다시 생성합니다.
실패 시점 또한 적절하지 않습니다. 공급자(provider)는 API가 서버 생성을 알리는 즉시 서버가 생성되었다고 보고하지만, 실제 운영체제는 여전히 부팅 중이며 sshd는 아직 대기 상태가 아닐 수 있습니다.
Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refusedAnsible은 정반대의 유혹을 줍니다. 클라우드 모듈로 서버를 생성할 수 있으며, 소수의 머신을 다룰 때는 이 방식이 작동합니다. 하지만 이 경우 의존성 그래프와 상태 파일(state file)을 포기해야 합니다. Ansible은 리소스를 생성하지만, 플레이북에서 해당 작업을 삭제해도 리소스는 계속 실행되며 비용이 청구됩니다. 해당 리소스가 사용자의 소유라는 기록이 어디에도 남지 않기 때문입니다.
이로부터 도출되는 규칙은 다음과 같습니다. API를 통해 생성하고 삭제하는 객체는 Terraform이 관리하게 하고, 부팅된 운영체제 내부의 모든 것은 Ansible이 관리하게 하십시오.
핸드오프(Handoff) 작업 완료
핸드오프는 통합이 아닌 경계입니다. Terraform은 작업을 마치고 주소를 게시한 뒤 종료됩니다. Ansible은 그 주소부터 시작합니다.
terraform apply -auto-approve
terraform output -raw web_ip
printf '[web]\n%s ansible_user=root\n' "$(terraform output -raw web_ip)" > inventory.ini
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.ymlterraform output -raw은 따옴표나 JSON 래퍼 없이 하나의 값을 출력하며, 이는 셸 치환 내부에서 사용하기에 적합합니다. 여러 대의 서버를 다룰 때는 terraform output -json를 사용하여 인벤토리를 구성하십시오. -raw는 단일 문자열, 숫자 또는 불리언 값만 처리할 수 있기 때문입니다.
두 도구 사이의 ping 단계는 유지할 가치가 있습니다. 이 단계는 "Terraform이 잘못된 주소를 반환함"과 "플레이북에 버그가 있음"이라는 문제를 분리해 줍니다. 플레이북이 새로운 서버에 처음으로 접근하는 도구라면, 두 문제는 동일하게 보일 수 있습니다.
Terraform 상태를 Ansible 인벤토리로 읽기
인벤토리 파일을 전혀 작성하고 싶지 않다면 cloud.terraform 컬렉션을 사용하여 상태를 직접 읽을 수 있습니다.
ansible-galaxy collection install cloud.terraform플레이북 옆에 terraform.yml 파일을 작성하십시오:
plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infraansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.yml이 방법을 사용하기 전에 알아두어야 할 두 가지 사항이 있습니다. 이 플러그인은 project_path을 대상으로 terraform show를 실행하므로, 해당 디렉터리는 이미 초기화되어 있어야 하며 그렇지 않으면 플러그인이 실패합니다. 또한 이 플러그인은 서버 리소스에서 호스트를 임의로 생성하지 않습니다. 대신 Terraform 코드에서 Ansible 프로바이더를 사용하여 선언한 ansible_host 및 ansible_group 리소스를 읽어 들입니다. 이를 추가하기 전까지는 ansible-inventory --graph에 아무것도 나타나지 않습니다.
일반적인 인벤토리 파일은 디버깅이 더 쉽고 모든 프로바이더와 호환됩니다. 이 플러그인은 인벤토리가 수십 대 이상의 머신으로 늘어나고 수동 편집으로 인해 오타가 발생하기 시작할 때 유용합니다. 이는 한 대의 제어 머신에서 여러 Linux 서버 관리하기가 단순한 습관을 넘어 실제 워크플로우가 되는 시점과 같습니다.
Terraform이 정말로 필요한가?
이 글을 읽는 대부분의 사용자는 아직 Terraform이 필요하지 않습니다. 인프라를 생성하고 삭제하는 작업이 반복될 때 비로소 Terraform의 가치가 드러납니다. 제어판을 통해 VPS를 하나 주문하고 2년 동안 유지할 계획이라면, Terraform은 단 한 번 일어날 일을 기술하는 도구일 뿐이며, 절대 잃어버려서는 안 되는 상태 파일(state file)만 하나 더 늘어날 뿐입니다.
환경을 자주 재구축해야 하거나, 스테이징 환경이 운영 환경과 정확히 일치해야 할 때, 혹은 여러 사람이 인프라를 변경하여 삭제 작업 전에 검토 가능한 계획이 필요할 때 Terraform을 고려하십시오. 관리 대상이 서버를 넘어 DNS 레코드, 로드 밸런서, 공급자 API에 존재하는 방화벽 규칙까지 확장될 때도 마찬가지입니다.
서버의 수명이 길고 개수가 적으며, 매일 하는 고민이 "이 서버가 존재하는가"가 아니라 "이 서버가 올바르게 설정되었는가"라면 Ansible만 사용하는 것이 좋습니다. 새로운 서버를 보안 강화하는 단일 플레이북은 새 VPS에서 보내는 첫 10분과 동일한 역할을 수행하며, 다음 서버에서도 동일하게 실행할 수 있다는 장점이 있습니다.
학습 순서는 이 기준을 따릅니다. Ansible은 보유한 첫 번째 서버부터 즉각적인 이득을 줍니다. 반면 Terraform은 환경을 세 번째로 재구축할 때부터 그 진가를 발휘합니다.
핸드오프 과정에서 발생하는 문제
서버가 준비되지 않았습니다. Terraform은 성공하지만 Ansible은 즉시 실패합니다.
fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.10 port 22: Connection refused", "unreachable": true}API가 sshd가 리스닝 상태가 되기 전에 주소를 반환했습니다. 고정된 sleep 시간을 추가하는 대신 포트가 열릴 때까지 대기하십시오. Ansible에는 이를 위한 ansible.builtin.wait_for_connection가 있으며, 플레이의 첫 번째 작업으로 실행하면 됩니다.
호스트 키가 변경되었습니다. 서버를 삭제하고 다시 생성하면, 새로운 서버가 동일한 주소에서 새로운 키로 응답합니다.
Host key verification failed.ssh-keygen -R 203.0.113.10을 사용하여 오래된 항목을 제거하십시오. Terraform으로 재구축을 수행할 때 빈번하게 발생하는 현상이며, 데이터가 저장된 머신에서 재구축을 자주 하지 말아야 하는 이유이기도 합니다.
Sudo가 실패합니다. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"}은 해당 호스트에서 become: true에 비밀번호가 필요함을 의미합니다. 배포 사용자에게 비밀번호 없는 sudo를 설정하거나 --ask-become-pass를 전달하십시오.
Terraform이 수정하지 않은 항목을 삭제하려고 합니다. 계획(plan)에 작성하지 않은 변경 사항이 나타난다면, 이는 실제 인프라가 코드와 일치하지 않는 드리프트(drift) 현상입니다. 보통 누군가 제공자의 웹 패널에서 설정을 변경했을 때 발생합니다. terraform plan -refresh-only을 실행하여 차이점을 직접 확인한 뒤, 코드와 실제 리소스 중 어느 쪽이 잘못되었는지 판단하십시오. 한 줄씩 설명할 수 없는 파괴적인 계획은 절대 적용하지 마십시오.
Ansible이 실행할 때마다 변경(changed)되었다고 보고합니다. creates나 when 가드가 없는 shell 작업은 무조건 실행됩니다. 이는 단순히 보기 좋지 않은 문제가 아닙니다. 서버가 원하는 상태에 도달했음을 알리는 신호로 changed=0를 더 이상 사용할 수 없게 되기 때문입니다.
FAQ
Terraform이 Ansible을 대체할 수 있습니까?
서버 내부 구성 관리 용도로는 불가능합니다. Terraform은 remote-exec 프로비저너를 통해 스크립트를 호출할 수 있지만, 이는 리소스 생성 시점에만 실행되며 terraform plan에 나타나지 않습니다. 또한 스크립트 실패 시 리소스가 오염되어 다음 apply 시점에 삭제 후 재생성(destroy and rebuild)이 예약됩니다. Terraform에는 nginx가 이미 설치되어 있는지 확인하고 설치되어 있다면 아무 작업도 하지 않는 모듈과 같은 기능이 없습니다. Terraform으로 머신을 생성한 뒤 Ansible로 넘겨주는 방식을 사용하십시오.
Ansible이 Terraform을 대체할 수 있습니까?
오랫동안 유지되는 소수의 서버를 운영한다면 가능합니다. Ansible에는 서버를 생성하는 클라우드 모듈이 있으며, VPS 인스턴스 2대를 주문하고 계속 유지하는 정도라면 충분합니다. 하지만 상태 파일(state file)과 의존성 그래프(dependency graph)를 잃게 됩니다. 플레이북에서 작업을 제거해도 리소스는 계속 실행되며 비용이 발생하는데, 이는 Ansible이 해당 리소스를 생성했다는 기록을 남기지 않기 때문입니다. Terraform을 사용했다면 삭제 계획(destroy plan)이 수립되었을 것입니다.
무엇을 먼저 배워야 합니까?
현재 서버를 보유하고 있다면 Ansible을 먼저 배우십시오. 첫 번째 머신부터 즉각적인 효과를 볼 수 있고 SSH 외에는 아무것도 필요하지 않으며, 수동으로 주문한 서버에도 기술을 바로 적용할 수 있습니다. Terraform은 환경을 반복적으로 재구축하거나 DNS 레코드, 방화벽 규칙 등 서버 외의 프로바이더 리소스를 관리할 때 더 큰 이점을 제공합니다.
Terraform에서 생성한 새 서버의 IP를 어떻게 Ansible로 전달합니까?
Terraform 코드에 output을 선언하고 apply 후에 읽어오십시오. terraform output -raw web_ip은 셸 치환을 위해 순수 값만 출력하며, terraform output -json는 여러 호스트가 있을 때 모든 출력을 한꺼번에 보여줍니다. 이 값을 인벤토리 파일에 기록하거나 cloud.terraform 컬렉션을 설치한 뒤 ansible-inventory -i terraform.yml --graph이 프로젝트 디렉터리를 가리키도록 설정하십시오.
Terraform 작업이 끝나자마자 플레이북이 실패하는 이유는 무엇입니까?
프로바이더는 API가 서버 생성 완료를 보고하는 즉시 성공으로 처리하지만, 실제 운영체제는 부팅 중인 상태이므로 초기 몇 초 동안은 SSH 연결이 거부됩니다. 이때 발생하는 오류는 Connection refused과 함께 나타나는 UNREACHABLE!입니다. 부팅 시간은 이미지와 플랜에 따라 다르므로 임의로 대기 시간을 설정하지 말고, 플레이의 첫 번째 작업으로 ansible.builtin.wait_for_connection를 사용하십시오.