Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

기존에 사용 중인 인프라를 Terraform으로 가져오기

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 인스턴스

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 상태를 만들 수도 있고 간단한 업데이트 정도는 그냥 적용해서 맞추기도 한다.

2017/05/30 03:17 2017/05/30 03:17