Go로 Composition 함수 작성하기
Go로 Composition 함수 작성하기 (Write a Composition Function in Go)
Composition 함수(줄여서 함수)는 크로스플레인 리소스를 템플릿화하는 사용자 정의 프로그램입니다. 크로스플레인은 복합 리소스(XR)를 생성할 때 생성할 리소스를 결정하기 위해 Composition 함수를 호출합니다. Composition 함수에 대한 자세한 내용은 concepts 페이지를 읽어 보세요.
출처: 문서
본문
일반 목적 프로그래밍 언어를 사용해 리소스를 템플릿화하는 함수를 작성할 수 있습니다. 일반 목적 프로그래밍 언어를 사용하면 함수가 루프(loop)나 조건문(conditional) 같은 고급 로직으로 리소스를 템플릿화할 수 있습니다. 이 가이드는 Go로 Composition 함수를 작성하는 방법을 설명합니다.
중요 이 가이드를 따르기 전에 Composition 함수가 어떻게 동작하는지 익숙해지는 것이 좋습니다.
단계 이해하기 (Understand the steps)
이 가이드는 XBuckets 복합 리소스(XR)를 위한 Composition 함수 작성 방법을 다룹니다.
apiVersion: example.crossplane.io/v1
kind: XBuckets
metadata:
name: example-buckets
spec:
region: us-east-2
names:
- crossplane-functions-example-a
- crossplane-functions-example-b
- crossplane-functions-example-c
XBuckets XR은 리전(region)과 버킷 이름 배열을 가집니다. 함수는 names 배열의 각 항목에 대해 Amazon Web Services(AWS) S3 버킷을 생성합니다.
Go로 함수를 작성하려면:
- 함수 작성에 필요한 도구 설치
- 템플릿에서 함수 초기화
- 함수의 로직을 추가하도록 템플릿 편집
- 함수를 엔드투엔드로 테스트
- 함수를 빌드하고 패키지 저장소에 푸시
이 가이드는 각 단계를 자세히 다룹니다.
함수 작성에 필요한 도구 설치 (Install the tools)
Go로 함수를 작성하려면 다음이 필요합니다.
- Go v1.23 이상. 이 가이드는 Go v1.23을 사용합니다.
- Docker Engine. 이 가이드는 Engine v24를 사용합니다.
- Crossplane CLI v1.17 이상. 이 가이드는 Crossplane CLI v1.17을 사용합니다.
참고 Composition 함수를 빌드하거나 테스트하기 위해 쿠버네티스 클러스터나 크로스플레인 컨트롤 플레인에 접근할 필요는 없습니다.
템플릿에서 함수 초기화 (Initialize the function from a template)
crossplane xpkg init 명령을 사용하여 새 함수를 초기화합니다. 이 명령을 실행하면 GitHub 저장소를 템플릿으로 사용하여 함수를 초기화합니다.
crossplane xpkg init function-xbuckets function-template-go -d function-xbuckets
Initialized package "function-xbuckets" in directory "/home/negz/control/negz/function-xbuckets" from https://github.com/crossplane/function-template-go/tree/91a1a5eed21964ff98966d72cc6db6f089ad63f4 (main)
To get started:
1. Replace `function-template-go` with your function in `go.mod`,
`package/crossplane.yaml`, and any Go imports. (You can also do this
automatically by running the `./init.sh ` script.)
2. Update `input/v1beta1/` to reflect your desired input (and run `go generate`)
3. Add your logic to `RunFunction` in `fn.go`
4. Add tests for your logic in `fn_test.go`
5. Update `README.md`, to be about your function!
Found init.sh script!
Do you want to run it? [y]es/[n]o/[v]iew: y
Function function-xbuckets has been initialised successfully
crossplane xpkg init 명령은 function-xbuckets라는 디렉터리를 생성합니다. 명령을 실행하면 새 디렉터리는 다음과 같아야 합니다.
ls function-xbuckets
Dockerfile LICENSE NOTES.txt README.md example fn.go fn_test.go go.mod go.sum init.sh input main.go package renovate.json
fn.go 파일은 함수의 코드를 추가하는 곳입니다. 템플릿의 다른 파일들에 대해 알아두면 유용합니다.
main.go는 함수를 실행합니다. main.go를 편집할 필요는 없습니다.Dockerfile은 함수 런타임을 빌드합니다. Dockerfile을 편집할 필요는 없습니다.input디렉터리는 함수의 입력 유형을 정의합니다.package디렉터리는 함수 패키지를 빌드하는 데 사용되는 메타데이터를 포함합니다.
팁 Crossplane CLI v1.15부터
crossplane xpkg init은 템플릿 이름을 새 함수 이름으로 바꾸는 것 같은 작업을 자동화하는 초기화 스크립트를 실행할 옵션을 제공합니다.
코드를 추가하기 전에 몇 가지 변경을 해야 합니다.
package/crossplane.yaml을 편집하여 패키지의 이름을 바꿉니다.go.mod를 편집하여 Go 모듈의 이름을 바꿉니다.
패키지 이름을 function-xbuckets로 지정합니다.
모듈 이름은 함수 코드를 어디에 보관할지에 따라 달라집니다. Go 코드를 GitHub에 푸시한다면 GitHub 사용자 이름을 사용할 수 있습니다. 예: module github.com/negz/function-xbuckets.
이 가이드의 함수는 입력 유형을 사용하지 않습니다. 이 함수에서는 input과 package/input 디렉터리를 삭제해야 합니다.
input 디렉터리는 함수가 Composition의 input 필드를 사용하여 입력을 받을 수 있게 해 주는 Go struct를 정의합니다. Composition 함수 문서는 Composition 함수에 입력을 전달하는 방법을 설명합니다.
package/input 디렉터리는 input 디렉터리의 structs에서 생성된 OpenAPI 스키마를 포함합니다.
팁 입력을 사용하는 함수를 작성한다면 함수 요구 사항에 맞게 입력을 편집하세요. 입력의 kind와 API group을 변경하세요.
Input과template.fn.crossplane.io를 사용하지 말고 함수에 의미 있는 값을 사용하세요.input디렉터리의 파일을 편집할 때는go generate를 실행하여 생성된 파일을 업데이트해야 합니다. 자세한 내용은input/generate.go를 참고하세요.
go generate ./...
함수 로직을 추가하도록 템플릿 편집 (Edit the template)
fn.go의 RunFunction 메서드에 함수 로직을 추가합니다. 파일을 처음 열면 "hello world" 함수가 들어 있습니다.
func (f *Function) RunFunction(_ context.Context, req *fnv1.RunFunctionRequest) (*fnv1.RunFunctionResponse, error) {
f.log.Info("Running Function", "tag", req.GetMeta().GetTag())
rsp := response.To(req, response.DefaultTTL)
in := &v1beta1.Input{}
if err := request.GetInput(req, in); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot get Function input from %T", req))
return rsp, nil
}
response.Normalf(rsp, "I was run with input %q", in.Example)
return rsp, nil
}
모든 Go Composition 함수는 RunFunction 메서드를 가집니다. 크로스플레인은 함수를 실행하는 데 필요한 모든 것을 RunFunctionRequest struct에 전달합니다.
함수는 RunFunctionResponse struct를 반환하여 크로스플레인에 구성해야 할 리소스를 알려줍니다.
팁 크로스플레인은 Protocol Buffers를 사용하여
RunFunctionRequest와RunFunctionResponsestruct를 생성합니다. 자세한 스키마는 Buf Schema Registry에서 확인할 수 있습니다.
RunFunction 메서드를 편집하여 다음 코드로 바꿉니다.
func (f *Function) RunFunction(_ context.Context, req *fnv1.RunFunctionRequest) (*fnv1.RunFunctionResponse, error) {
rsp := response.To(req, response.DefaultTTL)
xr, err := request.GetObservedCompositeResource(req)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot get observed composite resource from %T", req))
return rsp, nil
}
region, err := xr.Resource.GetString("spec.region")
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot read spec.region field of %s", xr.Resource.GetKind()))
return rsp, nil
}
names, err := xr.Resource.GetStringArray("spec.names")
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot read spec.names field of %s", xr.Resource.GetKind()))
return rsp, nil
}
desired, err := request.GetDesiredComposedResources(req)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot get desired resources from %T", req))
return rsp, nil
}
_ = v1beta1.AddToScheme(composed.Scheme)
for _, name := range names {
b := &v1beta1.Bucket{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"crossplane.io/external-name": name,
},
},
Spec: v1beta1.BucketSpec{
ForProvider: v1beta1.BucketParameters{
Region: ptr.To[string](region),
},
},
}
cd, err := composed.From(b)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot convert %T to %T", b, &composed.Unstructured{}))
return rsp, nil
}
desired[resource.Name("xbuckets-"+name)] = &resource.DesiredComposed{Resource: cd}
}
if err := response.SetDesiredComposedResources(rsp, desired); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot set desired composed resources in %T", rsp))
return rsp, nil
}
return rsp, nil
}
아래 블록을 확장하여 imports와 함수 로직을 설명하는 주석을 포함한 전체 fn.go를 확인하세요.
전체 fn.go 파일:
package main
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"github.com/upbound/provider-aws/v2/apis/namespaced/s3/v1beta1"
"github.com/crossplane/function-sdk-go/errors"
"github.com/crossplane/function-sdk-go/logging"
fnv1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/request"
"github.com/crossplane/function-sdk-go/resource"
"github.com/crossplane/function-sdk-go/resource/composed"
"github.com/crossplane/function-sdk-go/response"
)
// Function returns whatever response you ask it to.
type Function struct {
fnv1.UnimplementedFunctionRunnerServiceServer
log logging.Logger
}
// RunFunction observes an XBuckets composite resource (XR). It adds an S3
// bucket to the desired state for every entry in the XR's spec.names array.
func (f *Function) RunFunction(_ context.Context, req *fnv1.RunFunctionRequest) (*fnv1.RunFunctionResponse, error) {
f.log.Info("Running Function", "tag", req.GetMeta().GetTag())
// Create a response to the request. This copies the desired state and
// pipeline context from the request to the response.
rsp := response.To(req, response.DefaultTTL)
// Read the observed XR from the request. Most functions use the observed XR
// to add desired managed resources.
xr, err := request.GetObservedCompositeResource(req)
if err != nil {
// You can set a custom status condition on the XR. This
// allows you to communicate with the user.
response.ConditionFalse(rsp, "FunctionSuccess", "InternalError").
WithMessage("Something went wrong.").
TargetComposite()
// You can emit an event regarding the XR. This allows you to
// communicate with the user. Note that events should be used
// sparingly and are subject to throttling
response.Warning(rsp, errors.New("something went wrong")).
TargetComposite()
// If the function can't read the XR, the request is malformed. This
// should never happen. The function returns a fatal result. This tells
// Crossplane to stop running functions and return an error.
response.Fatal(rsp, errors.Wrapf(err, "cannot get observed composite resource from %T", req))
return rsp, nil
}
// Create an updated logger with useful information about the XR.
log := f.log.WithValues(
"xr-version", xr.Resource.GetAPIVersion(),
"xr-kind", xr.Resource.GetKind(),
"xr-name", xr.Resource.GetName(),
)
// Get the region from the XR. The XR has getter methods like GetString,
// GetBool, etc. You can use them to get values by their field path.
region, err := xr.Resource.GetString("spec.region")
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot read spec.region field of %s", xr.Resource.GetKind()))
return rsp, nil
}
// Get the array of bucket names from the XR.
names, err := xr.Resource.GetStringArray("spec.names")
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot read spec.names field of %s", xr.Resource.GetKind()))
return rsp, nil
}
// Get all desired composed resources from the request. The function will
// update this map of resources, then save it. This get, update, set pattern
// ensures the function keeps any resources added by other functions.
desired, err := request.GetDesiredComposedResources(req)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot get desired resources from %T", req))
return rsp, nil
}
// Add v1beta1 types (including Bucket) to the composed resource scheme.
// composed.From uses this to automatically set apiVersion and kind.
_ = v1beta1.AddToScheme(composed.Scheme)
// Add a desired S3 bucket for each name.
for _, name := range names {
// One advantage of writing a function in Go is strong typing. The
// function can import and use managed resource types from the provider.
b := &v1beta1.Bucket{
ObjectMeta: metav1.ObjectMeta{
// Set the external name annotation to the desired bucket name.
// This controls what the bucket will be named in AWS.
Annotations: map[string]string{
"crossplane.io/external-name": name,
},
},
Spec: v1beta1.BucketSpec{
ForProvider: v1beta1.BucketParameters{
// Set the bucket's region to the value read from the XR.
Region: ptr.To[string](region),
},
},
}
// Convert the bucket to the unstructured resource data format the SDK
// uses to store desired composed resources.
cd, err := composed.From(b)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot convert %T to %T", b, &composed.Unstructured{}))
return rsp, nil
}
// Add the bucket to the map of desired composed resources. It's
// important that the function adds the same bucket every time it's
// called. It's also important that the bucket is added with the same
// resource.Name every time it's called. The function prefixes the name
// with "xbuckets-" to avoid collisions with any other composed
// resources that might be in the desired resources map.
desired[resource.Name("xbuckets-"+name)] = &resource.DesiredComposed{Resource: cd}
}
// Finally, save the updated desired composed resources to the response.
if err := response.SetDesiredComposedResources(rsp, desired); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot set desired composed resources in %T", rsp))
return rsp, nil
}
// Log what the function did. This will only appear in the function's pod
// logs. A function can use response.Normal and response.Warning to emit
// Kubernetes events associated with the XR it's operating on.
log.Info("Added desired buckets", "region", region, "count", len(names))
// You can set a custom status condition on the XR. This allows you
// to communicate with the user.
response.ConditionTrue(rsp, "FunctionSuccess", "Success").
TargetComposite()
return rsp, nil
}
이 코드는:
- RunFunctionRequest에서 관찰된 복합 리소스(observed composite resource)를 가져옵니다.
- 관찰된 복합 리소스에서 리전과 버킷 이름을 가져옵니다.
- 각 버킷 이름에 대해 하나의 원하는 S3 버킷을 추가합니다.
- RunFunctionResponse에서 원하는 S3 버킷을 반환합니다.
이 코드는 AWS S3 프로바이더의 v1beta1.Bucket 유형을 사용합니다. Go로 함수를 작성하는 장점 중 하나는 크로스플레인이 프로바이더에서 사용하는 것과 동일한 강력한 타입의 structs로 리소스를 구성할 수 있다는 것입니다.
이 유형을 사용하려면 AWS Provider Go 모듈을 받아야 합니다.
go get github.com/upbound/provider-aws/[email protected]
크로스플레인은 Go로 Composition 함수를 작성하기 위한 소프트웨어 개발 키트(SDK)를 제공합니다. 이 함수는 SDK의 유틸리티를 사용합니다. 특히 request와 response 패키지가 RunFunctionRequest와 RunFunctionResponse 유형을 다루기 쉽게 해 줍니다.
팁 SDK의 Go 패키지 문서를 읽어 보세요.
함수를 엔드투엔드로 테스트 (Test the function end-to-end)
단위 테스트(unit tests)를 추가하고 crossplane composition render 명령을 사용하여 함수를 테스트합니다.
Go는 단위 테스트를 풍부하게 지원합니다. 템플릿에서 함수를 초기화하면 fn_test.go에 일부 단위 테스트가 추가됩니다. 이 테스트들은 Go의 권장 사항을 따릅니다. Go 표준 라이브러리의 pkg/testing과 google/go-cmp만 사용합니다.
테스트 케이스를 추가하려면 TestRunFunction의 cases 맵을 업데이트합니다. 아래 블록을 확장하여 이 함수의 전체 fn_test.go 파일을 확인하세요.
전체 fn_test.go 파일:
package main
import (
"context"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/durationpb"
"github.com/crossplane/crossplane-runtime/v2/pkg/logging"
fnv1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/resource"
)
func TestRunFunction(t *testing.T) {
type args struct {
ctx context.Context
req *fnv1.RunFunctionRequest
}
type want struct {
rsp *fnv1.RunFunctionResponse
err error
}
cases := map[string]struct {
reason string
args args
want want
}{
"AddTwoBuckets": {
reason: "The Function should add two buckets to the desired composed resources",
args: args{
req: &fnv1.RunFunctionRequest{
Observed: &fnv1.State{
Composite: &fnv1.Resource{
// MustStructJSON is a handy way to provide mock
// resources.
Resource: resource.MustStructJSON(`{
"apiVersion": "example.crossplane.io/v1alpha1",
"kind": "XBuckets",
"metadata": {
"name": "test"
},
"spec": {
"region": "us-east-2",
"names": [
"test-bucket-a",
"test-bucket-b"
]
}
}`),
},
},
},
},
want: want{
rsp: &fnv1.RunFunctionResponse{
Meta: &fnv1.ResponseMeta{Ttl: durationpb.New(60 * time.Second)},
Desired: &fnv1.State{
Resources: map[string]*fnv1.Resource{
"xbuckets-test-bucket-a": {Resource: resource.MustStructJSON(`{
"apiVersion": "s3.aws.m.upbound.io/v1beta1",
"kind": "Bucket",
"metadata": {
"annotations": {
"crossplane.io/external-name": "test-bucket-a"
}
},
"spec": {
"forProvider": {
"region": "us-east-2"
}
},
"status": {
"observedGeneration": 0
}
}`)},
"xbuckets-test-bucket-b": {Resource: resource.MustStructJSON(`{
"apiVersion": "s3.aws.m.upbound.io/v1beta1",
"kind": "Bucket",
"metadata": {
"annotations": {
"crossplane.io/external-name": "test-bucket-b"
}
},
"spec": {
"forProvider": {
"region": "us-east-2"
}
},
"status": {
"observedGeneration": 0
}
}`)},
},
},
Conditions: []*fnv1.Condition{
{
Type: "FunctionSuccess",
Status: fnv1.Status_STATUS_CONDITION_TRUE,
Reason: "Success",
Target: fnv1.Target_TARGET_COMPOSITE.Enum(),
},
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
f := &Function{log: logging.NewNopLogger()}
rsp, err := f.RunFunction(tc.args.ctx, tc.args.req)
if diff := cmp.Diff(tc.want.rsp, rsp, protocmp.Transform()); diff != "" {
t.Errorf("%s\nf.RunFunction(...): -want rsp, +got rsp:\n%s", tc.reason, diff)
}
if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" {
t.Errorf("%s\nf.RunFunction(...): -want err, +got err:\n%s", tc.reason, diff)
}
})
}
}
go test 명령을 사용하여 단위 테스트를 실행합니다.
go test -v -cover .
=== RUN TestRunFunction
=== RUN TestRunFunction/AddTwoBuckets
--- PASS: TestRunFunction (0.00s)
--- PASS: TestRunFunction/AddTwoBuckets (0.00s)
PASS
coverage: 52.6% of statements
ok github.com/negz/function-xbuckets 0.016s coverage: 52.6% of statements
Crossplane CLI를 사용하여 이 함수를 사용하는 Composition의 출력을 미리 볼 수 있습니다. 이를 위해 크로스플레인 컨트롤 플레인이 필요하지 않습니다.
function-xbuckets 아래에는 Composite Resource, Composition, Function YAML 파일이 있는 example이라는 디렉터리가 있습니다.
다음 블록을 확장하여 예시 파일을 확인하세요.
xr.yaml, composition.yaml, function.yaml 파일 — 다음 파일들로 crossplane composition render를 실행하여 아래 출력을 재현할 수 있습니다.
xr.yaml 파일은 렌더링할 복합 리소스를 포함합니다.
apiVersion: example.crossplane.io/v1
kind: XBuckets
metadata:
name: example-buckets
spec:
region: us-east-2
names:
- crossplane-functions-example-a
- crossplane-functions-example-b
- crossplane-functions-example-c
composition.yaml 파일은 복합 리소스를 렌더링하는 데 사용할 Composition을 포함합니다.
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: create-buckets
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1
kind: XBuckets
mode: Pipeline
pipeline:
- step: create-buckets
functionRef:
name: function-xbuckets
functions.yaml 파일은 Composition이 파이프라인 단계에서 참조하는 Functions를 포함합니다.
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: function-xbuckets
annotations:
render.crossplane.io/runtime: Development
spec:
# The CLI ignores this package when using the Development runtime.
# You can set it to any value.
package: xpkg.crossplane.io/negz/function-xbuckets:v0.1.0
functions.yaml의 Function은 Development 런타임을 사용합니다. 이것은 crossplane composition render에게 함수가 로컬에서 실행 중임을 알려줍니다. Docker를 사용해 함수를 가져와 실행하는 대신 로컬에서 실행 중인 함수에 연결합니다.
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: function-xbuckets
annotations:
render.crossplane.io/runtime: Development
go run을 사용하여 함수를 로컬에서 실행합니다.
go run . --insecure --debug
경고
insecure플래그는 함수가 암호화나 인증 없이 실행되도록 지시합니다. 테스트와 개발 중에만 사용하세요.
별도의 터미널에서 crossplane composition render를 실행합니다.
crossplane composition render xr.yaml composition.yaml functions.yaml
이 명령은 함수를 호출합니다. 함수가 실행 중인 터미널에서 로그 출력을 볼 수 있어야 합니다.
go run . --insecure --debug
2023-10-31T16:17:32.158-0700 INFO function-xbuckets/fn.go:29 Running Function {"tag": ""}
2023-10-31T16:17:32.159-0700 INFO function-xbuckets/fn.go:125 Added desired buckets {"xr-version": "example.crossplane.io/v1", "xr-kind": "XBuckets", "xr-name": "example-buckets", "region": "us-east-2", "count": 3}
crossplane composition render 명령은 함수가 반환하는 원하는 리소스를 출력합니다.
---
apiVersion: example.crossplane.io/v1
kind: XBuckets
metadata:
name: example-buckets
---
apiVersion: s3.aws.m.upbound.io/v1beta1
kind: Bucket
metadata:
annotations:
crossplane.io/composition-resource-name: xbuckets-crossplane-functions-example-b
crossplane.io/external-name: crossplane-functions-example-b
generateName: example-buckets-
labels:
crossplane.io/composite: example-buckets
ownerReferences:
# Omitted for brevity
spec:
forProvider:
region: us-east-2
---
apiVersion: s3.aws.m.upbound.io/v1beta1
kind: Bucket
metadata:
annotations:
crossplane.io/composition-resource-name: xbuckets-crossplane-functions-example-c
crossplane.io/external-name: crossplane-functions-example-c
generateName: example-buckets-
labels:
crossplane.io/composite: example-buckets
ownerReferences:
# Omitted for brevity
spec:
forProvider:
region: us-east-2
---
apiVersion: s3.aws.m.upbound.io/v1beta1
kind: Bucket
metadata:
annotations:
crossplane.io/composition-resource-name: xbuckets-crossplane-functions-example-a
crossplane.io/external-name: crossplane-functions-example-a
generateName: example-buckets-
labels:
crossplane.io/composite: example-buckets
ownerReferences:
# Omitted for brevity
spec:
forProvider:
region: us-east-2
팁 Composition 함수 테스트에 대해 더 자세히 알아보려면 Composition 함수 문서를 읽어 보세요.
함수를 빌드하고 패키지 레지스트리에 푸시 (Build and push)
함수는 두 단계로 빌드합니다. 먼저 함수의 런타임을 빌드합니다. 이것은 크로스플레인이 함수를 실행하는 데 사용하는 Open Container Initiative(OCI) 이미지입니다. 그런 다음 그 런타임을 패키지에 포함시키고 패키지 레지스트리에 푸시합니다. Crossplane CLI는 기본 패키지 레지스트리로 xpkg.crossplane.io를 사용합니다.
함수는 기본적으로 linux/amd64 같은 단일 플랫폼을 지원합니다. 각 플랫폼에 대해 런타임과 패키지를 빌드한 다음 모든 패키지를 레지스트리의 단일 태그에 푸시하여 여러 플랫폼을 지원할 수 있습니다.
함수를 레지스트리에 푸시하면 크로스플레인 컨트롤 플레인에서 함수를 사용할 수 있습니다. 컨트롤 플레인에서 함수를 사용하는 방법은 Composition 함수 문서를 참고하세요.
각 플랫폼에 대한 런타임을 빌드하려면 Docker를 사용합니다.
docker build . --quiet --platform=linux/amd64 --tag runtime-amd64
sha256:fdf40374cc6f0b46191499fbc1dbbb05ddb76aca854f69f2912e580cfe624b4b
docker build . --quiet --platform=linux/arm64 --tag runtime-arm64
sha256:cb015ceabf46d2a55ccaeebb11db5659a2fb5e93de36713364efcf6d699069af
팁 원하는 태그를 사용할 수 있습니다. 런타임 이미지를 레지스트리에 푸시할 필요는 없습니다. 태그는
crossplane xpkg build에게 포함할 런타임을 알려주는 데만 사용됩니다.
각 플랫폼에 대한 패키지를 빌드하려면 Crossplane CLI를 사용합니다. 각 패키지는 런타임 이미지를 포함합니다.
--package-root 플래그는 crossplane.yaml을 포함하는 package 디렉터리를 지정합니다. 이것은 패키지에 대한 메타데이터를 포함합니다.
--embed-runtime-image 플래그는 Docker로 빌드한 런타임 이미지 태그를 지정합니다.
--package-file 플래그는 패키지 파일을 디스크에 쓸 위치를 지정합니다. 크로스플레인 패키지 파일은 .xpkg 확장자를 사용합니다.
crossplane xpkg build \
--package-root=package \
--embed-runtime-image=runtime-amd64 \
--package-file=function-amd64.xpkg
crossplane xpkg build \
--package-root=package \
--embed-runtime-image=runtime-arm64 \
--package-file=function-arm64.xpkg
팁 크로스플레인 패키지는 특별한 OCI 이미지입니다. 패키지에 대한 자세한 내용은 packages 문서에서 읽어 보세요.
두 패키지 파일을 레지스트리에 푸시합니다. 두 파일을 레지스트리의 한 태그에 푸시하면 linux/arm64와 linux/amd64 호스트에서 모두 실행되는 다중 플랫폼(multi-platform) 패키지가 생성됩니다.
crossplane xpkg push \
--package-files=function-amd64.xpkg,function-arm64.xpkg \
negz/function-xbuckets:v0.1.0
팁 함수를 GitHub 저장소에 푸시하면 템플릿이 GitHub Actions를 사용하여 지속적 통합(CI)을 자동으로 설정합니다. CI 워크플로우는 함수를 lint, test, build합니다. 템플릿이 CI를 구성하는 방법은
.github/workflows/ci.yaml을 읽어 확인할 수 있습니다.