![[OSSCA] Terraform Provider SDK 와 Framework 버전 차이를 알아보자](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FcIzKl4%2FbtsIKdh2WfQ%2FAAAAAAAAAAAAAAAAAAAAAHWIJIiTs1zX1qySL2cVGbfZUVVFPfGJF8TdjpIBrixY%2Fimg.webp%3Fcredential%3DyqXZFxpELC7KVnFOS48ylbz2pIh7yKj8%26expires%3D1753973999%26allow_ip%3D%26allow_referer%3D%26signature%3DP64PxEWSt7Hqi7aIkOj4l6Tt5b8%253D)
이후 Terraform Provider를 Framework로 마이그레이션 하는 도중 어떤 부분이 바뀌어야 하는건지 감이 잘 잡히지 않아 공식문서를 번역하며 두 버전의 차이에 대해 알아보고자 한다.
Home - Plugin Development: Plugin Framework Benefits | Terraform | HashiCorp Developer
Compare the prior SDKv2 and the framework for provider development. The framework is easier and more extensible than SDKv2.
developer.hashicorp.com
해당 문서를 참고하면 보다 정확한 차이를 알 수 있을 것이다!
HashiCorp는 Terraform 제공자를 구축하기 위한 두 가지 Go 프로그래밍 언어 소프트웨어 개발 키트(SDK)를 제공한다. 첫 번째는 최신 SDK인 Terraform 플러그인 프레임워크이며, 두 번째는 많은 기존 제공자들이 사용하는 이전 SDK인 SDKv2이다. Terraform 1.x 버전 및 이전 버전에 대해 유지보수하고 있지만, 프레임워크 개선에 집중하기 위해 대부분의 기능 개발은 중단했다.
새로운 제공자 개발에는 SDKv2와 비교하여 상당한 이점을 제공하는 프레임워크 사용을 권장한다. 또한 가능한 경우 기존 제공자를 프레임워크로 마이그레이션하는 것을 추천한다. 대규모 기존 제공자를 관리하는 경우, terraform-plugin-mux를 사용하여 개별 리소스나 데이터 소스를 한 번에 하나씩 프레임워크로 마이그레이션할 수 있다. 자세한 내용은 SDK에서 마이그레이션하기를 참조하면 된다.
이 글은 SDKv2 플러그인에 경험이 있는 개발자를 대상으로 하며, 프레임워크가 어떻게 제공자 개발과 유지보수를 더 쉽게 만드는지 설명한다. 제공자 개발을 처음 시작하는 경우, 대신 프레임워크 문서를 보는 것을 권장한다. 프레임워크의 새로운 또는 개선된 기능의 지속적인 목록은 프레임워크 기능 비교를 참조하면 된다.
간결한 추상화
프레임워크는 명확한 개념과 객체 타입을 사용하여 구현을 더 잘 이해하고 커스터마이즈할 수 있도록 돕는다.
각 타입에 대한 별도 패키지
프레임워크는 각 개념에 대한 기능을 해당 개념에 대한 기능만 제공하는 별도의 패키지로 노출시킨다. 예를 들어, datasource 패키지는 데이터 소스를 구현하기 위한 기능을 포함하고, provider 패키지는 제공자를 구현하기 위한 기능을 포함한다. 이러한 분리는 각 타입을 언제 어떻게 사용해야 하는지 명확하게 만든다.
반면에 SDK는 helper/schema.Resource 타입과 helper/schema.Schema 타입과 같은 추상적이고 재귀적인 타입을 구현해야 한다. schema.Resource 구현은 관리형 리소스, 데이터 소스, 또는 스키마 내의 블록 정의일 수 있다. 이러한 일반적인 추상화는 각 타입의 특정 요구사항을 이해하기 어렵게 만든다. 예를 들어, 데이터 소스는 스키마와 읽기 기능을 필요로 하지만 블록은 스키마만 필요로 한다.
크게 개선된 데이터 접근
프레임워크에서는 SDKv2와 비교하여 데이터가 완전히 노출된다. SDKv2의 대부분의 데이터 처리는 helper/schema.ResourceData 타입을 사용하는데, 이는 작업에 따라 병합된 구성, 계획, 또는 상태 값을 포함할 수 있다. 또한 SDKv2는 Terraform의 null 또는 unknown 값 개념을 명확하게 노출하지 않으며, 일반적으로 null과 unknown 값을 기본 Go 타입의 동일한 제로값으로 반환한다. 예를 들어, SDK는 null이거나 unknown인 schema.TypeString 값에 대해 빈 문자열("")을 반환한다.
프레임워크는 데이터의 소스를 별도로 노출하고 값이 null인지 unknown인지 검사할 수 있게 한다. 자세한 내용과 예시는 프레임워크 기능 비교를 참조하면 된다.
내장 동작에 대한 제어
프레임워크는 SDKv2 제공자에게 혼란이나 주요 문제를 일으킬 수 있는 내장 동작에 대해 더 많은 제어권을 제공한다. 예를 들어, SDKv2에서 Computed: true를 포함하는 속성은 사용자가 구성에서 제거했을 때 자동으로 이전 저장된 상태를 유지했다. 프레임워크는 사용 사례에 따라 해당 동작을 사용할지 여부를 지정할 수 있게 한다.
관용적인 Go 패턴
Go 코딩 표준은 SDKv2 개발 이후로 발전해왔다. 프레임워크는 제공자 코드를 작성할 때 익숙하고 더 현대적인 패턴을 사용할 수 있게 한다. 이러한 설계는 프레임워크를 이해하고 사용하기 더 쉽게 만든다.
선언적 구조체 대신 인터페이스 사용
SDKv2는 주로 helper/schema.Resource 타입과 같은 선언적 구조체 타입을 사용한다. 시간이 지남에 따라 이러한 타입들은 요구사항과 선택적 기능을 구분하기 어려운 기능들을 축적해왔다. SDKv2 타입들은 또한 잘못 구성된 코드와 같은 문제를 런타임에만 표면화시켜, 문제를 피하기 위해 추가적인 테스트와 노력이 필요했다.
다음 SDK 예시에서, 관리형 리소스 구현에는 읽기와 삭제 정의가 누락되어 있다. SDKv2로 제공자를 개발할 때는 이러한 누락을 식별하기 위해 테스트를 작성해야 한다.
// 예시 관리형 리소스 정의
&schema.Resource{
Schema: map[string]*schema.Schema{ /* ... */ },
CreateContext: func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { /* ... */ },
// Read와 Delete가 누락됨
}
선언적 구조체 대신, 프레임워크는 resource.Resource와 resource.ResourceWithImportState와 같은 인터페이스 타입을 노출한다. 이러한 인터페이스 타입들은 필요한 기능을 정의하지 않았을 때 컴파일러 오류를 발생시켜 개발을 더 쉽게 만든다.
다음 예시에서, 관리형 리소스 구현에는 Read()와 Delete()와 같은 resource.Resource 인터페이스에 필요한 메서드가 누락되어 있다. 이 코드는 누락된 메서드로 인해 Go 컴파일러 오류를 발생시킬 것이다.
// 제공자 정의 타입이 프레임워크 인터페이스를 만족하는지 확인
// 타입에 메서드가 누락되어 있으므로, Go 컴파일러는
// 누락된 메서드에 대한 오류를 반환할 것이다.
var _ resource.Resource = &ThingResource{}
// 예시 관리형 리소스 정의를 위한 제공자 정의 타입
type ThingResource struct{}
func (r ThingResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { /* ... */ }
인터페이스를 사용하면 또한 사용 가능한 타입을 넘어 프레임워크의 기능을 확장할 수 있다. 자세한 내용은 확장성 섹션을 참조하면 된다.
요청 및 응답 패턴
Terraform 제공자는 논리적으로 HTTP 서버와 같은 다른 서버들과 유사하다. 그들은 Terraform으로부터 원격 프로시저 호출(RPC) 요청을 받고 각 작업에 대한 응답을 반환한다. SDKv2와 프레임워크는 RPC 요청을 처리하는 데 필요한 로직을 정의하는 데 다른 접근 방식을 사용한다.
구체적으로, 프레임워크는 요청-응답 패턴을 채택하여 RPC 요청에 필요한 대부분의 함수를 노출하고 함수 서명에 요청 또는 응답 동작을 포함시킨다. 이 접근 방식을 통해 프레임워크는 제공자 코드의 메서드 서명을 업데이트할 필요 없이 시간이 지남에 따라 이러한 타입들을 개선할 수 있다. 반면에 SDKv2는 후방 호환성을 유지해야 하는 함수 서명을 가진 필드를 사용하여 기능을 제공했다. 즉, SDK 변경은 시간이 지남에 따라 Create, CreateContext, CreateWithoutTimeout과 같은 필드를 중복하여 이루어졌다. 이러한 서명들은 또한 작업 간에 중복되어 정적 분석과 같은 도구 사용을 더 어렵게 만들었다.
다음 SDKv2 예시에서, CreateContext와 ReadContext 필드는 유사하지만 일반적인 함수 서명을 사용한다. Create와 CreateWithoutTimeout과 같은 다른 필드 구현은 동일한 제공자 기능에 대해 추가적인 발견이나 개발 지식이 필요했다.
// 예시 관리형 리소스 정의
&schema.Resource{
Schema: map[string]*schema.Schema{ /* ... */ },
CreateContext: func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { /* ... */ },
ReadContext: func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { /* ... */ },
// 간결성을 위해 다른 필드는 생략됨
}
다음 프레임워크 예시에서, Create와 Read 메서드는 요청 및 응답 패턴을 보여준다. 프레임워크는 각 작업과 그 사용 가능한 기능 및 데이터에 맞춰 요청 및 응답 타입을 조정한다.
// 예시 관리형 리소스 정의를 위한 제공자 정의 타입
// 간결성을 위해 다른 필수 메서드는 생략됨
type ThingResource struct{}
func (r ThingResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { /* ... */ }
func (r ThingResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { /* ... */ }
컨텍스트 가용성
서버 기반 Go 프로그램은 일반적으로 전체 요청에 걸쳐 데이터를 저장하고 전달할 수 있도록 context.Context를 사용한다. 예를 들어, HTTP 서버는 context.Context를 사용하여 데이터베이스 핸들러를 각 요청과 함께 전달한다. context.Context는 SDKv2의 초기 개발 이후로 Go 표준이 되었으며, Terraform에서 컨텍스트는 고루틴 작업을 시간 초과할 수 있게 하거나 취소 요청을 위한 리소스를 모니터링하는 데 사용할 수 있다.
SDKv2와 프레임워크는 모두 제공자 요청을 위한 context.Context를 지원한다. 그러나 SDKv2는 컨텍스트 매개변수가 있는 필드와 없는 필드를 혼합하여 필드에 컨텍스트를 제공한다. 이 접근 방식은 기능 중복과 전환하기 어려운 필요성 있는 변경을 초래한다. 또한 context.Context의 일부 개념이 아닌 메타 데이터를 제공하는 필드는 컨텍스트 사용을 방해하고 혼란을 일으킬 수 있다.
다음 SDKv2 예시에서, CreateContext와 ReadContext 함수는 컨텍스트를 사용하여 필드 메타 데이터를 제공하지만, meta 매개변수를 사용하여 다른 데이터를 전달해야 한다.
// 예시 관리형 리소스 정의
&schema.Resource{
Schema: map[string]*schema.Schema{ /* ... */ },
CreateContext: func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { /* ... */ },
ReadContext: func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { /* ... */ },
// 간결성을 위해 다른 필드는 생략됨
}
다음 프레임워크 예시에서, Create와 Read 메서드는 컨텍스트를 매개변수로 사용하며, 메타 데이터는 제공자의 논리에 따라 요청 및 응답 타입의 일부로 전달된다. 이 접근 방식은 제공자가 데이터를 처리하는 방법을 표준화하여 코드를 이해하고 유지 관리하기 쉽게 만든다.
// 예시 관리형 리소스 정의를 위한 제공자 정의 타입
// 간결성을 위해 다른 필수 메서드는 생략됨
type ThingResource struct{}
func (r ThingResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { /* ... */ }
func (r ThingResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { /* ... */ }
결론
Terraform 제공자를 구축하기 위한 HashiCorp의 최신 Terraform 플러그인 프레임워크는 기존 SDKv2에 비해 여러 가지 장점을 제공한다. 간결한 추상화, 개선된 데이터 접근, 내장 동작에 대한 제어, 관용적인 Go 패턴을 통해 제공자 개발과 유지보수를 더 쉽게 만든다. 새로운 제공자 개발에는 프레임워크 사용을 권장하며, 기존 제공자는 가능한 경우 프레임워크로 마이그레이션하는 것을 추천한다.
'Infra > Terraform' 카테고리의 다른 글
[OSSCA] Terraform Provider 개발(2) - Go API 제작하기 (0) | 2024.07.30 |
---|---|
[OSSCA] Terraform Provider 개발(1) - Go API 분석하기 (0) | 2024.07.29 |
[OSSCA] Terraform Provider 살펴보기 (2) | 2024.07.23 |
[OSSCA] Terraform으로 NCP 사용하기 (6) | 2024.07.23 |
[OSSCA] NCP를 이용하여 인프라 설정하기 (0) | 2024.07.22 |
보안 전공 개발자지만 대학로에서 살고 싶어요
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!