Resource 개발하기 (VPC)
- attribute의 값들을 사용자가 terraform config를 통해서 원하는 값을 입력할 때, 유효한 값들이 정해져 있는 attribute들은 ValidateFunc을 통해서 사전의 terraform core에서 이러한 값들이 유효한지 검증할 수 있음
- ValidateFunc 같은 경우에, terraform SDK에서 제공하는 것을 사용할 수 있음, 만약 제공해주는 것이 없다면 직접 정의해서 사용
- 사용자가 적절하지 않은 입력값을 주면, terraform plan에서 error를 발생시킨다.
CRUD Func 구현
- Terraform core가 해당 구현체를 통해 인프라를 생성/수정/삭제를 함
- Read operation을 통해 state(.tfstate)를 refresh 함
- 가장 중요한 것은 CRUD Func을 구현하는 것
- 사용자가 작성한 config 파일을 terraform core가 state와 비교해서 생성을 하거나 삭제하는 명령들을 rpc를 통해서 terraform provider에게 전달하게 됨
- 해당 명령을 받으면, 각각의 리소스에 있는 Create, Read, Update, Delete 함수를 통해서 NAVER Cloud API를 호출해서 수행
- 해당 함수를 구현하고, resource 타입에서 해당 함수를 연결해주면 동작을 수행할 수 있게 된다.
- 최근에 구현되는 api에는 Context를 많이 사용하게 되어있다.
- 오래된 api에는 Context를 쓰지 않는 경우도 있다.
CRUD Func Parameters - meta
- ConfigureFunc(Provider)에서 정의 한 meta 정보를 사용할 수 있다. (리젼 코드)
- Context
- ResourceData
- meta
- config에서 사용자가 입력한 설정 관련 값들을 meta를 이용해서 가져올 수 있음
- vpc 생성할 때, RegionCode가 필요한데, 이것은 사용자가 설정한 region과 관련한 정보에서 얻어올 수 있음
config := meta.(*ProviderConfig)
- meta를 ProviderConfig 타입으로 변환하고 가져옴
CRUD Func Parameters - ResourceData
- 사용자가 config 파일에서 vpc를 선언했을 때, name에 대한 값, cidr_block에 대한 값을 정의함
- 이러한 사용자가 입력한 값을 가져올 때, ResourceData 타입을 활용
d.Get("name").(string) // string 타입 변환
d.Get(String("ipv4_cidr_block").(string) // string 타입 변환
- 위의 코드와 같은 방식으로 사용자 입력 값을 가져올 수 있음
- 코드를 작성하면서 많이 쓰이는 부분 중 하나이다.
Resource 구현 방식
Create 구현
- 인프라를 생성하고, ID를 설정
- 마지막으로 Read 함수를 호출해서 state(.tfstate)를 업데이트 할 수 있도록 한다.
- Client를 통해서 NAVER Cloud에 Create API를 사용해서 vpc를 실제로 생성하는 부분
- 해당 결과 값을 resp(response)로 받게 된다.
- 해당 resp에는 VpcNo(id)가 있는데, 이를 이용해서 ResourceData에서 값 설정 가능
- ResourceData에서 SetId로 설정을 해주면, 최종적으로 결과물이 state에 저장된다.
- 끝났을 때, Read함수를 호출
Read 구현
- state(.tfstate)를 sync 하는데 사용
- ID는 Unique한 값이어야 함
- 실제 생성된 vpc에 대한 값을 읽어오는 API를 호출하고, 그 vpc에 대한 값을 instance로 가져온다.
- instance가 없으면 예외 처리
- 있으면 d(ResourceData)에 값들을 설정해줌
- 설정을 해주면, 실제로 terraform SDK에서 terraform으로 전달이 되고, terraform core에서 값들을 저장
Update 구현
- d(ResourceData)로 update 유무를 판단
- 예제에서는 vpc의 이름을 다른 이름으로 변경했을 때이다.
- HasChange에서 name이 변경되었는지 확인 가능
- name을 변경했다면, return 값이 true
- UpdateVpc API를 호출해서 변경 가능
- 여기서도 마찬가지로, 변경 사항을 state에 저장하기 위해 Read 함수 호출
Delete 구현
- terraform destroy 명령으로 삭제 또는 config에 주석 처리를 해서 terraform core에서 변경사항을 확인했을 때, resource가 삭제되었다고 판단되었을 때 Delete 함수 호출
- Terraform destroy 또는 Diff 결과 삭제 될 경우 호출
- 속성이 ForceNew인 경우, 삭제 후 생성 하기 위해 사용
- Delete는 id 정보만 있으면 된다.
- 해당 리소스의 id 정보를 얻어와서 Delete API를 호출하고 끝난다.
컨트리뷰션을 진행하면서 어느 정도 익숙해지면, 이러한 Resource 구현도 참여할 수 있다. 새로운 resource를 구현하지 않더라도, 기존의 resource 구현된 것에서 동작 방식을 수정해볼 수도 있다.
Terraform provider 만들기
Testing
Terraform SDK에서 test 해볼 수 있는 여러가지 구현체들을 제공해준다. 크게 보았을 때는 다음의 4가지 단계로 진행된다.
- PreChecking : provider 리젼 설정 여부, 입력 Validation
- 테스트 수행 전 provider가 동작할 수 있게 하는 환경 설정
- TestSteps : 실제 인프라 생성 또는 변경 테스트
- 실제 테스트가 동작하는 부분
- Destroy : 테스트가 종료된 후, 제거하는 역할
- test에서 생성된 vpc 리소스 삭제
- 이 과정은 terraform SDK에서 자동 수행
- CheckDestory : 잘 삭제가 되었는지 검증
- Delete 함수를 잘못 작성하면 리소스 삭제가 잘 안된다.
- 의존성에 문제가 있어서 삭제가 안되는 경우도 있음
- 이렇게 여러 가지 상황을 체크해볼 수 있다.
Test 코드 구현 예시
- 함수가 Test로 시작한다.
- random 함수를 통해서 cidr 블럭 값 정의, 이름도 정의
- test 코드 작성에서
- PreCheck, Providers, CheckDestroy는 정의된 것 사용
- Steps는 직접 구현하기
- Config에서는 실제 terraform config 파일 작성하는 것처럼 작성
- name과 cidr은 앞에서 정의한 값 사용
- 해당 Config가 작성이 완료되면, 잘 작성되었는지 Check에서 확인
Testing
- resource.ParalleTest와 같은 병렬 테스트도 지원 (하지만, 동시성을 고려해야 함)
- TF_ACC=true 환경 변수를 통해, 실제 인프라에 반영되는 Acceptance Tests 수행
- 작성한 test 코드를 수행하기 위해서는 TF_ACC=true를 설정해줘야 test 수행 가능
- Acceptance Test가 통과했다는 것은 최소한으로 어느 정도의 기준을 통과했다는 것
- dev_overrides를 사용하면, required_providers 설정하고 사용 시 Binary 파일 SUM 체크 무시
- dev_overrides는 ****terraform command를 사용해서 구현한 내용을 terraform config 파일을 가지고 plan, apply를 수행한다고 했을 때 설정할 수 있는 값
한가지 더 참고해야할 점
- NAVER Cloud 리소스 중에서 생성할 수 있는 개수가 제한되어있는 것들이 있다.
- 따라서 테스트 내용과 무관하게 테스트가 실패하는 경우도 있다. 이런 부분들은 주의할 필요가 있다.
Debugging
- TF_LOG=DEBUG를 통해 로깅 하자
- 디버깅을 위해 각 operation 마다 Logging 하는 것이 중요
- 로그를 잘 남겨두는 것도 개발에 도움이 된다.
- 에러 발생 시 request와 response가 잘 나오도록
Documentation
- /docs 내의 Markdown 파일 수정
- /docs/resources
- Doc Preview Tool 제공
- registry.terraform.io/tools/doc-preview
- 작성한 Markdown이 잘 작성되어있는지 확인할 수 있는 페이지
- tfplugindocs을 사용하여 자동화 가능
Release
- Travis CI/CD 적용
- gorelease를 통해 바이너리 배포
Appendix & Tips
비동기 API 및 상태 처리
Resource graph
Terraform core에서는 Resource 간의 종속성 그래프를 생성
- vpc는 vpc 정보만 있으면 되므로 의존성 없음
- subnet은 vpc 정보가 필요하기 때문에 의존성을 가짐
- server는 subnet 정보가 필요하기 때문에 의존성을 가짐
- 만약 의존성을 가시적으로 보고 싶다면 graph를 확인하면 된다.
StateChangeConf
Terraform SDK에서 제공하는 구현체이다. Pending에서 Target 상태가 될 때 까지 대기
Q. 언제 생성하는가?
A. VPC 생성을 예시로 들어보자.
- vpc를 생성하게 되면 처음에는 init 상태가 되고, run 상태가 될 때까지 시간이 좀 걸린다.
- vpc상태가 run 상태가 될 때 까지 대기를 하기 위해, StateChangeConf를 사용한다.
- run 상태가 될 때 까지 기다리는 이유는 vpc가 의존성이 있는 subnet을 생성한다고 했을 때, subnet이 해당 vpc 정보를 사용해야한다. 하지만, vpc가 아직 생성이 완료가 안된 상태에서 subnet을 생성하고자 하면, error 발생한다.
- 따라서, run 상태가 되어서 vpc를 쓸 수 있는 상태인지 확인하고 그 다음 단계를 수행하기 위해서 상태를 체크하면 된다.
실제 구현
- Pending : 기다릴 상태
- 예시로 설명하자면, INIT, CREATING 상태라면 기다리겠다는 의미
- Target : 최종적으로 기다리는 상태
- 예시로 설명하면, RUN 상태가 될 때까지 기다리겠다는 의미
- Refresh : 구현체를 어떻게 가져오는지
- Timeout : 최대 대기 시간
- Delay : 몇 초 주기로 확인?
- 이런 시간들은 리소스의 특성에 따라서 다르게 구성된다.
동시성 이슈 처리
Parallel walk
- Terraform은 최대 10개의 노드를 병렬로 사용
- -parallelism=n 옵션을 통해 동시 노드 조절 가능 plan, apply, destroy
- 하나의 인프라의 여러 요청이 동시에 들어올 때
- 예) 하나의 ACG 또는 Network ACL에 여러 룰 들이 추가 될 때
Retry
- 해당 자원에 접근이 일시적으로 어려울 경우 재시도
- 리소스 특성에 따라서 실패하였을 때, 재시도가 불가능한 경우가 있다.
- 재시도가 가능한 경우도 있다.
- 이 경우, retry를 통해 재시도를 해볼 수 있는 코드 구현 가능
순환 참조 (Circular dependency) 이슈
- 두 Resource간 서로 참조하려고 할 때 Circular dependency 이슈 발생
- 순환 참조하는 리소스는 생성 불가
해결 방법
- 리소스를 분리해서 참조하기
- 자주 발생하지는 않겠지만, 이러한 순환 참조는 만들면 안된다.
Tip 1. Filter 기능 제공
- datasource는 필터 기능 제공
- API 단에서 지원하는 필터 기능은 제한적
- 사용자에게 많은 속성의 필터링 제공
Tip 2. 공통 함수를 재사용 하자
재사용 하기 좋은 함수들
- Marshall & Unmarshall functions
- Flatten 함수들 (API Model → Resource schemas)
- Get functions (Resource와 DataSource, StateChangeConf에서 사용)
Tip 3. Go Context
Context 구현
- Go에서 동시성을 처리할 때, Context 사용
- context.Context를 GO SDK에 전달하여 User cancellation 발생 시 불필요한 요청을 중단
- API를 호출할 때, Context를 같이 전달함
Q. 왜 Context를 같이 전달하는가?
A. 사용자가 요청을 수행하다가, 중간에 수행을 취소하고 싶은 경우가 생긴다.
- Context가 없는 경우에는 vpc가 끝날 때 까지 기다려야 한다.
- Context를 사용하면, 요청을 바로 처리할 수 있다.
Terraform에서 기본적으로 제공하는 API들이 Context가 같이 있음. 따라서 Context를 직접 다룰 일은 많지 않다.
주의해야 할 점
Framework vs SDK
- Terraform Provider Plugin은 hashcorp에서 제공하는 SDK를 사용하여 작성
- SDKv2 : Ncloud 및 대부분의 provider가 사용하는 SDK, 하지만 더 이상 추가 개발되지 않음
- Framework : Hashcorp에서 기존 SDK의 단점을 개선해서 새롭게 작성한 SDK
- AWS 등 여러 provider들이 새롭게 추가하는 리소스 및 기존 리소스를 Framework로 포팅 중
- SDKv2와 Framework가 동작하는 방식은 동일하나, 제공하는 API 및 구현이 달라짐
- SDKv2는 any 타입으로 정의된 인터페이스를 사용할 때마다 정확한 타입으로 변환해야 하는 부분이 많아서 개발하기 불편했으나, Framework는 명시적인 타입을 사용하여 개발 편의성 증가 (런타임 에러 → 컴파일 에러)
- 컨트리뷰션 시 기존 리소스는 SDKv2로 개발되어 있어 개선시 SDKv2 내용을 참고, 신규 리소스 추가 작업 시 framework를 사용하여 개발 필요
따라서 이러한 부분에서도 컨트리뷰션 활동이 가능하다.
OSSCA Terraform on NAVER Cloud
"이원철 멘토님"의 Terraform 프로바이더 개발 세미나 2 내용을
terraform provider 개발 시 참고하기 위해 공부한 내용을 정리한 글입니다.
'개발 > Terraform on NaverCloud' 카테고리의 다른 글
오픈 소스 기여하기 : panic 원인 파악하기 (0) | 2023.08.01 |
---|---|
오픈 소스 기여하기 : terraform-provider-ncloud 이슈 선정 (0) | 2023.08.01 |
Terraform으로 NAVER Cloud Server 만들기 (0) | 2023.07.25 |
Terraform 프로바이더 개발 세미나 1 (0) | 2023.07.19 |
Terraform 구조 및 사용 방법 이해 (0) | 2023.07.17 |