Terraform으로 AWS 등의 인프라를 관리할 때 지금까지는 도구를 소개하려고 설정 파일을 만들고 plan
, apply
를 거쳐서 적용하는 식으로 설명했는데 실제로 사용하려면 보통 이미 사용하고 있는 인프라가 있다. 처음부터 Terraform으로 다 만든 것이 아니라면 AWS에 이미 EC2에 실행한 인스턴스나 VPC, IAM 설정 등이 이미 있을 것이다. 그리고 Terraform으로 사용하더라도 권한관리를 엄격하게 하는 게 아니라면 웹 콘솔이나 다른 도구로 만들게 되는 자원이 있게 마련이다.
Terraform을 제대로 활용하려면 기존에 있는 리소스도 Terraform으로 가져와서 관리하에 두어야 한다.
Terraform의 설정과 상태
Terraform 관련 글을 최근에 계속 쓰고 있지만 상기 차원에서 설명하면 Terraform이 AWS 등의 자원을 관리할 때 크게 설정(configuration)과 상태(state)가 있다. 여기서 설정은 .tf
파일을 얘기하고 상태는 .tfstate
파일을 의미한다.
예를 들어 resource "aws_instance" "server" { }
처럼 HCL로 리소스를 xxx.tf
파일에 정의한다. 이를 terraform plan
으로 비교하고 terraform apply
로 적용을 하면 실제 AWS에 EC2 인스턴스가 시작되고 적용한 내용을 terraform.tfstate
파일에 기록한다. 이렇게 되면 AWS의 자원을 Terraform으로 관리할 수 있게 된다. 이후부터는 이 설정 파일과 상태 파일을 비교하고 실제 AWS에 적용된 내용을 비교해서 어떤 부분을 추가하고 삭제할지를 결정하게 된다.
역으로 이미 있는 자원을 Terraform으로 가져오려면 이 설정과 상태를 다 가져와야 한다는 의미이다.
Terraform import
AWS EC2에 3개의 인스턴스가 있다고 해보자. 이를 Terraform이 관리하도록 가져올 예정이다. 이때 사용하는 명령어가 terraform import
이다.
The current implementation of Terraform import can only import resources into the state. It does not generate configuration. A future version of Terraform will also generate configuration.
문서에 나와 있듯이 현재는 상태(state)에만 가져올 수 있고 설정(configuration)은 만들어 주지 않는다. 나중에는 지원할 거라고 하지만 아직은 관련 소식을 듣지 못해서 근 시일 내에 될지는 모르겠다. 예전 글에서 import
를 어떻게 쓰는지 모르겠다고 했는데 기존 리소스를 가져오면서 이제야 이해를 했다.
Terraform의 aws_instance
문서를 보면 $ terraform import aws_instance.web i-12345678
처럼 사용방법을 볼 수 있다. 각 리소스 별로 import에 대한 설명이 없는 일도 있는데 지금까지 사용해 보면 문서에 없어도 대부분 동작한다.
$ terraform import aws_instance.web i-12345678
위 명령어를 보면 aws_instance.web
는 EC2 인스턴스에 지정하는 이름이다. resource "aws_instance" "web" { }
처럼 .tf
파일을 만들어서 적용할 때처럼 원하는 이름을 지정하면 된다. i-12345678
는 AWS에서 만든 인스턴스 ID다. 다시 풀어서 설명하면 i-12345678
인스턴스를 aws_instance.web
라는 이름으로 가져오겠다는 뜻이다. 그리고 위에서 인스턴스 ID를 지정했듯이 한 번에 딱 한 개만 가져올 수 있으므로 지금처럼 인스턴스가 3개 있으면 3번 가져와야 한다.
$ terraform import aws_instance.server1 i-017eb8d5ec586a067
aws_instance.server1: Importing from ID "i-017eb8d5ec586a067"...
aws_instance.server1: Import complete!
Imported aws_instance (ID: i-017eb8d5ec586a067)
aws_instance.server1: Refreshing state... (ID: i-017eb8d5ec586a067)
Import success! The resources imported are shown above. These are
now in your Terraform state. Import does not currently generate
configuration, so you must do this next. If you do not create configuration
for the above resources, then the next `terraform plan` will mark
them for destruction.
EC2 인스턴스를 성공적으로 가져왔다. 현재 폴더를 보면 다음과 같은 terraform.tfstate
파일이 생긴 걸 볼 수 있다. 기존에 terraform.tfstate
파일이 없었지만, 자동으로 만들어 준다.
{
"version": 3,
"terraform_version": "0.9.6",
"serial": 0,
"lineage": "20c7dcdf-4258-4834-ab4c-54bc1b6fa67e",
"modules": [
{
"path": [
"root"
],
"outputs": {},
"resources": {
"aws_instance.server1": {
...
}
},
"depends_on": []
}
]
}
이어서 나머지 EC2 인스턴스도 가져와 보자.
$ terraform import aws_instance.server2 i-08c157a39fc14205a
aws_instance.server2: Importing from ID "i-08c157a39fc14205a"...
aws_instance.server2: Import complete!
Imported aws_instance (ID: i-08c157a39fc14205a)
aws_instance.server2: Refreshing state... (ID: i-08c157a39fc14205a)
Import success! The resources imported are shown above. These are
now in your Terraform state. Import does not currently generate
configuration, so you must do this next. If you do not create configuration
for the above resources, then the next `terraform plan` will mark
them for destruction.
$ terraform import aws_instance.server3 i-0ad45000df4f10d63
aws_instance.server3: Importing from ID "i-0ad45000df4f10d63"...
aws_instance.server3: Import complete!
Imported aws_instance (ID: i-0ad45000df4f10d63)
aws_instance.server3: Refreshing state... (ID: i-0ad45000df4f10d63)
Import success! The resources imported are shown above. These are
now in your Terraform state. Import does not currently generate
configuration, so you must do this next. If you do not create configuration
for the above resources, then the next `terraform plan` will mark
them for destruction.
이미 terraform.tfstate
가 있으면 알아서 잘 합쳐준다. 여기서 성공했다고 메시지가 나오지만, 마지막에 보면 다음과 같은 문구가 있다.
If you do not create configuration for the above resources, then the next
terraform plan
will mark them for destruction.
설정 파일을 만들지 않으면 terraform plan
을 할 때 지워질 거라는 경고이다.(경고인데 녹색으로 잘 보이지도 않게 알려준다. ㅠ)
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
......
- aws_instance.server1
- aws_instance.server2
- aws_instance.server3
Plan: 0 to add, 0 to change, 3 to destroy.
상태에는 추가되었지만, 설정이 없으므로 Terraform은 이 인스턴스를 지워야 한다고 판단한 것이다. 실제로 여기서 terraform apply
를 하면 인스턴스가 제거되므로 조심해야 한다.
이제 해야 할 일은 위 3개의 인스턴스에 대한 Terraform 설정을 만들어야 한다.
Terraforming
원래 이 설정은 처음 작성할 때와 마찬가지로 손으로 직접 작성해야 한다. 하지만 이를 도와주는 terraforming이라는 Ruby 프로젝트가 있다.(안타깝게도 terraforming은 AWS만 지원한다.) gem install terraforming
로 설치하면 terraforming
이라는 명령어를 쓸 수 있다. EC2 인스턴스를 가져오는 명령어는 terraforming ec2
이다.
$ terraforming ec2
resource "aws_instance" "i-08c157a39fc14205a" {
ami = "ami-923d12f5"
availability_zone = "ap-northeast-1a"
ebs_optimized = false
instance_type = "t2.micro"
monitoring = false
key_name = "keypair"
subnet_id = "subnet-000000"
vpc_security_group_ids = ["sg-000000", "sg-111111"]
associate_public_ip_address = true
private_ip = "10.10.1.13"
source_dest_check = true
root_block_device {
volume_type = "gp2"
volume_size = 20
delete_on_termination = true
}
tags {
}
}
resource "aws_instance" "i-017eb8d5ec586a067" {
ami = "ami-374db956"
availability_zone = "ap-northeast-1a"
ebs_optimized = true
instance_type = "c4.4xlarge"
monitoring = false
key_name = "keypair"
subnet_id = "subnet-000000"
vpc_security_group_ids = ["sg-000000", "sg-111111"]
associate_public_ip_address = true
private_ip = "10.10.1.110"
source_dest_check = true
root_block_device {
volume_type = "gp2"
volume_size = 50
delete_on_termination = true
}
tags {
}
}
resource "aws_instance" "bastion" {
ami = "ami-afb09dc8"
availability_zone = "ap-northeast-1a"
ebs_optimized = false
instance_type = "t2.nano"
monitoring = false
key_name = "keypair"
subnet_id = "subnet-000000"
vpc_security_group_ids = ["sg-000000", "sg-111111"]
associate_public_ip_address = true
private_ip = "10.10.1.112"
source_dest_check = true
root_block_device {
volume_type = "gp2"
volume_size = 8
delete_on_termination = true
}
tags {
"Name" = "bastion"
}
}
생략하면 헷갈릴까 봐 그대로 다 적었다. terraform import
와는 달리 terraforming
은 한 서비스의 자원을 한꺼번에 가져오므로 여기서 필요한 자원을 가져다 써야 한다. 위에서 보듯이 AWS에 설정된 내용을 그대로 가져온 것이므로 이름이나 참조 관계 등은 알아서 지정해 주지 않는다. 처음부터 작성하는 대신 teraforming 한 설정을 바탕으로 작성해서 노력을 조금 줄일 수 있는 정도이다. --tfstate
옵션을 사용하면 설정을 가져오면서 terraform.tfstate
에 추가해주기도 하지만 나는 왠지 terraform import
쪽이 더 신뢰가 가서 이쪽을 사용한다.
당연히 여기서 terraform plan
을 해도 제대로 되지 않는다. 여기서부터는 이제 설정을 실제와 고쳐가면서 terraform plan
을 했을 때 No changes. Infrastructure is up-to-date.
가 나올 때까지 계속 수정하면 된다.
이렇게 기존 인프라를 가져올 때 느낀 점을 얘기하면...
- terraforming이 가져올 때 가끔 못 가져오는 부분이 있다. 이런 부분은 직접 Terraform 문서를 보고 추가해야 한다.
- Terraform에서 지정하지 않으면 기본값을 가질 수가 있다. 이런 부분이 기존 인프라가 다르다면 이를 수정하려고 한다. 이 부분도 문서를 보고 추가해야 한다.
설정을 추가하는 부분이 좀 피곤하지만 계속하다 보면 보통 No changes
상태를 만들 수도 있고 간단한 업데이트 정도는 그냥 적용해서 맞추기도 한다.
Comments