5.3. Go 기반 Operator
5.3.1. Go 기반 Operator를 위한 Operator SDK 튜토리얼
Operator 개발자는 Operator SDK의 Go 프로그래밍 언어 지원을 활용하여 분산형 키-값 저장소인 Memcached에 대한 Go 기반 Operator 예제를 빌드하고 라이프사이클을 관리할 수 있습니다.
Operator 프로젝트의 관련 스캐폴딩 및 테스트 툴을 포함하여 Operator SDK CLI 툴의 Red Hat 지원 버전은 더 이상 사용되지 않으며 향후 OpenShift Dedicated 릴리스에서 제거될 예정입니다. Red Hat은 현재 릴리스 라이프사이클 동안 이 기능에 대한 버그 수정 및 지원을 제공하지만 이 기능은 더 이상 개선 사항을 받지 않으며 향후 OpenShift Dedicated 릴리스에서 제거됩니다.
새 Operator 프로젝트를 생성하는 데 Red Hat 지원 버전의 Operator SDK는 권장되지 않습니다. 기존 Operator 프로젝트가 있는 Operator 작성자는 OpenShift Dedicated 4와 함께 릴리스된 Operator SDK CLI 툴 버전을 사용하여 프로젝트를 유지 관리하고 최신 버전의 OpenShift Dedicated를 대상으로 하는 Operator 릴리스를 생성할 수 있습니다.
Operator 프로젝트의 다음과 같은 관련 기본 이미지는 더 이상 사용되지 않습니다. 이러한 기본 이미지의 런타임 기능 및 구성 API는 버그 수정 및 CVE 문제를 해결하는 데 계속 지원됩니다.
- Ansible 기반 Operator 프로젝트의 기본 이미지
- Helm 기반 Operator 프로젝트의 기본 이미지
지원되지 않는 커뮤니티 유지 관리 버전에 대한 자세한 내용은 Operator SDK(Operator Framework) 를 참조하십시오.
이 프로세스는 Operator 프레임워크의 두 가지 주요 요소를 사용하여 수행됩니다.
- Operator SDK
-
operator-sdk
CLI 툴 및controller-runtime
라이브러리 API - OLM(Operator Lifecycle Manager)
- 클러스터에 대한 Operator의 설치, 업그레이드, RBAC(역할 기반 액세스 제어)
이 튜토리얼에는 OpenShift Container Platform 설명서에서 Go 기반 Operator용 Operator SDK 시작하기 보다 자세히 설명되어 있습니다.
5.3.1.1. 사전 요구 사항
- Operator SDK CLI가 설치됨
-
OpenShift CLI (
oc
) 4 이상이 설치됨 - Go 1.21+
-
dedicated-admin
권한이 있는 계정으로oc
를 사용하여 OpenShift Dedicated 클러스터에 로그인함 - 클러스터가 이미지를 가져올 수 있도록 하려면 이미지를 내보내는 리포지토리를 공개로 설정하거나 이미지 가져오기 보안을 구성해야 합니다.
5.3.1.2. 프로젝트 생성
Operator SDK CLI를 사용하여 memcached-operator
라는 프로젝트를 생성합니다.
프로세스
프로젝트에 사용할 디렉터리를 생성합니다.
$ mkdir -p $HOME/projects/memcached-operator
디렉터리로 변경합니다.
$ cd $HOME/projects/memcached-operator
Go 모듈에 대한 지원을 활성화합니다.
$ export GO111MODULE=on
operator-sdk init
명령을 실행하여 프로젝트를 초기화합니다.$ operator-sdk init \ --domain=example.com \ --repo=github.com/example-inc/memcached-operator
참고operator-sdk init
명령은 기본적으로 Go 플러그인을 사용합니다.operator-sdk init
명령은 Go 모듈과 함께 사용할go.mod
파일을 생성합니다.$GOPATH/src/
외부에서 프로젝트를 생성할 때는 생성된 파일에 유효한 모듈 경로가 필요하기 때문에--repo
플래그가 있어야 합니다.
5.3.1.2.1. PROJECT 파일
operator-sdk init
명령으로 생성된 파일 중에는 Kubebuilder PROJECT
파일이 있습니다. 이어서 프로젝트 루트에서 실행되는 operator-sdk
명령과 help
출력에서는 이 파일을 읽고 프로젝트 유형이 Go임을 확인합니다. 예를 들면 다음과 같습니다.
domain: example.com layout: - go.kubebuilder.io/v3 projectName: memcached-operator repo: github.com/example-inc/memcached-operator version: "3" plugins: manifests.sdk.operatorframework.io/v2: {} scorecard.sdk.operatorframework.io/v2: {} sdk.x-openshift.io/v1: {}
5.3.1.2.2. Manager 정보
Operator의 기본 프로그램은 Manager를 초기화하고 실행하는 main.go
파일입니다. Manager는 모든 CR(사용자 정의 리소스) API 정의에 대한 스키마를 자동으로 등록하고 컨트롤러 및 Webhook를 설정 및 실행합니다.
Manager는 모든 컨트롤러에서 리소스를 조사하는 네임스페이스를 제한할 수 있습니다.
mgr, err := ctrl.NewManager(cfg, manager.Options{Namespace: namespace})
기본적으로 Manager는 Operator가 실행되는 네임스페이스를 조사합니다. 모든 네임스페이스를 조사하려면 namespace
옵션을 비워두면 됩니다.
mgr, err := ctrl.NewManager(cfg, manager.Options{Namespace: ""})
MultiNamespacedCacheBuilder
기능을 사용하여 특정 네임스페이스 세트를 조사할 수도 있습니다.
var namespaces []string 1 mgr, err := ctrl.NewManager(cfg, manager.Options{ 2 NewCache: cache.MultiNamespacedCacheBuilder(namespaces), })
5.3.1.2.3. 다중 그룹 API 정보
API 및 컨트롤러를 생성하기 전에 Operator에 여러 API 그룹이 필요한지 확인하도록 합니다. 이 튜토리얼에서는 단일 그룹 API의 기본 사례를 다루지만 프로젝트의 레이아웃을 변경하여 다중 그룹 API를 지원하려면 다음 명령을 실행하면 됩니다.
$ operator-sdk edit --multigroup=true
이 명령은 다음 예와 유사한 PROJECT
파일을 업데이트합니다.
domain: example.com layout: go.kubebuilder.io/v3 multigroup: true ...
다중 그룹 프로젝트의 경우 API Go 유형 파일은 apis/<group>/<version>/
디렉터리에 생성되고 컨트롤러는 controllers/<group>/
디렉터리에 생성됩니다. 그런 다음 Dockerfile이 적절하게 업데이트됩니다.
추가 리소스
- 다중 그룹 프로젝트로 마이그레이션하는 방법에 대한 자세한 내용은 Kubebuilder 설명서를 참조하십시오.
5.3.1.3. API 및 컨트롤러 생성
Operator SDK CLI를 사용하여 CRD(사용자 정의 리소스 정의) API 및 컨트롤러를 생성합니다.
프로세스
다음 명령을 실행하여 그룹이
cache
이고 버전이v1
, 종류가Memcached
인 API를 생성합니다.$ operator-sdk create api \ --group=cache \ --version=v1 \ --kind=Memcached
메시지가 표시되면 리소스 및 컨트롤러 모두 생성하도록
y
를 입력합니다.Create Resource [y/n] y Create Controller [y/n] y
출력 예
Writing scaffold for you to edit... api/v1/memcached_types.go controllers/memcached_controller.go ...
이 프로세스는 api/v1/memcached_types.go
에 Memcached
리소스 API를 생성하고 controllers/memcached_controller.go
에 컨트롤러를 생성합니다.
5.3.1.3.1. API 정의
Memcached
CR(사용자 정의 리소스)의 API를 정의합니다.
프로세스
spec
및status
가 다음과 같도록api/v1/memcached_types.go
에서 Go 유형 정의를 수정합니다.// MemcachedSpec defines the desired state of Memcached type MemcachedSpec struct { // +kubebuilder:validation:Minimum=0 // Size is the size of the memcached deployment Size int32 `json:"size"` } // MemcachedStatus defines the observed state of Memcached type MemcachedStatus struct { // Nodes are the names of the memcached pods Nodes []string `json:"nodes"` }
리소스 유형에 대해 생성된 코드를 업데이트합니다.
$ make generate
작은 정보*_types.go
파일을 수정한 후에는make generate
명령을 실행하여 해당 리소스 유형에 대해 생성된 코드를 업데이트해야 합니다.위의 Makefile 대상은
controller-gen
유틸리티를 호출하여api/v1/zz_generated.deepcopy.go
파일을 업데이트합니다. 이렇게 하면 API Go 유형 정의에서 모든 종류의 유형에서 구현해야 하는runtime.Object
인터페이스를 구현할 수 있습니다.
5.3.1.3.2. CRD 매니페스트 생성
spec
및 status
필드 그리고 CRD(사용자 정의 리소스 정의) 검증 마커를 사용하여 API를 정의한 후에는 CRD 매니페스트를 생성할 수 있습니다.
프로세스
다음 명령을 실행하여 CRD 매니페스트를 생성하고 업데이트합니다.
$ make manifests
이 Makefile 대상은
controller-gen
유틸리티를 호출하여config/crd/bases/cache.example.com_memcacheds.yaml
파일에서 CRD 매니페스트를 생성합니다.
5.3.1.3.2.1. OpenAPI 검증 정보
매니페스트가 생성될 때 spec.validation
블록의 CRD 매니페스트에 OpenAPIv3 스키마가 추가됩니다. 이 검증 블록을 사용하면 Memcached CR(사용자 정의 리소스)을 생성하거나 업데이트할 때 Kubernetes에서 해당 속성을 검증할 수 있습니다.
마커 또는 주석을 사용하여 API에 대한 검증을 구성할 수 있습니다. 이러한 마커에는 항상 +kubebuilder:validation
접두사가 있습니다.
추가 리소스
API 코드의 마커 사용에 대한 자세한 내용은 다음 Kubebuilder 설명서를 참조하십시오.
- CRD의 OpenAPIv3 검증 스키마에 대한 자세한 내용은 Kubernetes 설명서를 참조하십시오.
5.3.1.4. 컨트롤러 구현
새 API 및 컨트롤러를 생성하면 컨트롤러 논리를 구현할 수 있습니다.
프로세스
이 예제에서는 생성된 컨트롤러 파일
controllers/memcached_controller.go
를 다음 예제 구현으로 교체합니다.예 5.1.
memcached_controller.go
의 예/* Copyright 2020. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package controllers import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "reflect" "context" "github.com/go-logr/logr" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" cachev1 "github.com/example-inc/memcached-operator/api/v1" ) // MemcachedReconciler reconciles a Memcached object type MemcachedReconciler struct { client.Client Log logr.Logger Scheme *runtime.Scheme } // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list; // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // TODO(user): Modify the Reconcile function to compare the state specified by // the Memcached object against the actual cluster state, and then // perform operations to make the cluster state reflect the state specified by // the user. // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.7.0/pkg/reconcile func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { //log := r.Log.WithValues("memcached", req.NamespacedName) log := ctrllog.FromContext(ctx) // Fetch the Memcached instance memcached := &cachev1.Memcached{} err := r.Get(ctx, req.NamespacedName, memcached) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. // Return and don't requeue log.Info("Memcached resource not found. Ignoring since object must be deleted") return ctrl.Result{}, nil } // Error reading the object - requeue the request. log.Error(err, "Failed to get Memcached") return ctrl.Result{}, err } // Check if the deployment already exists, if not create a new one found := &appsv1.Deployment{} err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found) if err != nil && errors.IsNotFound(err) { // Define a new deployment dep := r.deploymentForMemcached(memcached) log.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name) err = r.Create(ctx, dep) if err != nil { log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name) return ctrl.Result{}, err } // Deployment created successfully - return and requeue return ctrl.Result{Requeue: true}, nil } else if err != nil { log.Error(err, "Failed to get Deployment") return ctrl.Result{}, err } // Ensure the deployment size is the same as the spec size := memcached.Spec.Size if *found.Spec.Replicas != size { found.Spec.Replicas = &size err = r.Update(ctx, found) if err != nil { log.Error(err, "Failed to update Deployment", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name) return ctrl.Result{}, err } // Spec updated - return and requeue return ctrl.Result{Requeue: true}, nil } // Update the Memcached status with the pod names // List the pods for this memcached's deployment podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(memcached.Namespace), client.MatchingLabels(labelsForMemcached(memcached.Name)), } if err = r.List(ctx, podList, listOpts...); err != nil { log.Error(err, "Failed to list pods", "Memcached.Namespace", memcached.Namespace, "Memcached.Name", memcached.Name) return ctrl.Result{}, err } podNames := getPodNames(podList.Items) // Update status.Nodes if needed if !reflect.DeepEqual(podNames, memcached.Status.Nodes) { memcached.Status.Nodes = podNames err := r.Status().Update(ctx, memcached) if err != nil { log.Error(err, "Failed to update Memcached status") return ctrl.Result{}, err } } return ctrl.Result{}, nil } // deploymentForMemcached returns a memcached Deployment object func (r *MemcachedReconciler) deploymentForMemcached(m *cachev1.Memcached) *appsv1.Deployment { ls := labelsForMemcached(m.Name) replicas := m.Spec.Size dep := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: m.Name, Namespace: m.Namespace, }, Spec: appsv1.DeploymentSpec{ Replicas: &replicas, Selector: &metav1.LabelSelector{ MatchLabels: ls, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: ls, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{{ Image: "memcached:1.4.36-alpine", Name: "memcached", Command: []string{"memcached", "-m=64", "-o", "modern", "-v"}, Ports: []corev1.ContainerPort{{ ContainerPort: 11211, Name: "memcached", }}, }}, }, }, }, } // Set Memcached instance as the owner and controller ctrl.SetControllerReference(m, dep, r.Scheme) return dep } // labelsForMemcached returns the labels for selecting the resources // belonging to the given memcached CR name. func labelsForMemcached(name string) map[string]string { return map[string]string{"app": "memcached", "memcached_cr": name} } // getPodNames returns the pod names of the array of pods passed in func getPodNames(pods []corev1.Pod) []string { var podNames []string for _, pod := range pods { podNames = append(podNames, pod.Name) } return podNames } // SetupWithManager sets up the controller with the Manager. func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&cachev1.Memcached{}). Owns(&appsv1.Deployment{}). Complete(r) }
예제 컨트롤러는 각
Memcached
CR(사용자 정의 리소스)에 대해 다음 조정 논리를 실행합니다.- Memcached 배포가 없는 경우 생성합니다.
-
배포 크기가
Memcached
CR 사양에 지정된 것과 같은지 확인합니다. -
Memcached
CR 상태를memcached
Pod의 이름으로 업데이트합니다.
다음 하위 섹션에서는 구현 예제의 컨트롤러에서 리소스를 조사하는 방법과 조정 반복문을 트리거하는 방법을 설명합니다. 이러한 하위 섹션을 건너뛰어 Operator 실행으로 직접 이동할 수 있습니다.
5.3.1.4.1. 컨트롤러에서 조사하는 리소스
controllers/memcached_controller.go
의 SetupWithManager()
함수는 해당 컨트롤러에서 보유하고 관리하는 CR 및 기타 리소스를 조사하기 위해 컨트롤러를 빌드하는 방법을 지정합니다.
import ( ... appsv1 "k8s.io/api/apps/v1" ... ) func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&cachev1.Memcached{}). Owns(&appsv1.Deployment{}). Complete(r) }
NewControllerManagedBy()
에서는 다양한 컨트롤러 구성을 허용하는 컨트롤러 빌더를 제공합니다.
For(&cachev1.Memcached{})
는 조사할 기본 리소스로 Memcached
유형을 지정합니다. Memcached
유형에 대한 각 추가, 업데이트 또는 삭제 이벤트의 경우 조정 반복문은 해당 Memcached
오브젝트의 조정 Request
인수(네임스페이스 및 이름 키로 구성됨)로 전송됩니다.
Owns(&appsv1.Deployment{})
는 조사할 보조 리소스로 Deployment
유형을 지정합니다. 이벤트 핸들러는 Deployment
유형, 즉 추가, 업데이트 또는 삭제 이벤트가 발생할 때마다 각 이벤트를 배포 소유자의 조정 요청에 매핑합니다. 이 경우 소유자는 배포가 생성된 Memcached
오브젝트입니다.
5.3.1.4.2. 컨트롤러 구성
기타 여러 유용한 구성을 사용하여 컨트롤러를 초기화할 수 있습니다. 예를 들면 다음과 같습니다.
MaxConcurrentReconciles
옵션을 사용하여 컨트롤러에 대한 최대 동시 조정 수를 설정합니다. 기본값은1
입니다.func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&cachev1.Memcached{}). Owns(&appsv1.Deployment{}). WithOptions(controller.Options{ MaxConcurrentReconciles: 2, }). Complete(r) }
- 서술자를 사용하여 조사 이벤트를 필터링합니다.
-
조정 반복문에 대한 요청을 조정하기 위해 조사 이벤트를 변환하는 방법을 변경하려면 EventHandler 유형을 선택합니다. 기본 및 보조 리소스보다 복잡한 Operator 관계의 경우
EnqueueRequestsFromMapFunc
핸들러를 사용하여 조사 이벤트를 임의의 조정 요청 세트로 변환할 수 있습니다.
5.3.1.4.3. 조정 반복문
모든 컨트롤러에는 조정 반복문을 구현하는 Reconcile()
메서드가 포함된 조정기 오브젝트가 있습니다. 조정 반복문에는 캐시에서 기본 리소스 오브젝트인 Memcached
를 찾는 데 사용되는 네임스페이스 및 이름 키인 Request
인수가 전달됩니다.
import ( ctrl "sigs.k8s.io/controller-runtime" cachev1 "github.com/example-inc/memcached-operator/api/v1" ... ) func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // Lookup the Memcached instance for this reconcile request memcached := &cachev1.Memcached{} err := r.Get(ctx, req.NamespacedName, memcached) ... }
반환 값, 결과, 오류에 따라 요청이 다시 큐에 추가되고 조정 루프가 다시 트리거될 수 있습니다.
// Reconcile successful - don't requeue return ctrl.Result{}, nil // Reconcile failed due to error - requeue return ctrl.Result{}, err // Requeue for any reason other than an error return ctrl.Result{Requeue: true}, nil
Result.RequeueAfter
를 설정하여 유예 기간 후 요청을 다시 큐에 추가할 수 있습니다.
import "time" // Reconcile for any reason other than an error after 5 seconds return ctrl.Result{RequeueAfter: time.Second*5}, nil
주기적으로 CR을 조정하도록 RequeueAfter
를 설정하여 Result
를 반환할 수 있습니다.
조정기, 클라이언트, 리소스 이벤트와의 상호 작용에 대한 자세한 내용은 Controller Runtime Client API 설명서를 참조하십시오.
5.3.1.4.4. 권한 및 RBAC 매니페스트
컨트롤러에서 관리하는 리소스와 상호 작용하려면 특정 RBAC 권한이 필요합니다. 이러한 권한은 다음과 같은 RBAC 마커를 사용하여 지정합니다.
// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list; func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { ... }
config/rbac/role.yaml
의 ClusterRole
오브젝트 매니페스트는 make manifests
명령이 실행될 때마다 controller-gen
유틸리티를 사용하여 이전 마커에서 생성됩니다.
5.3.1.5. 프록시 지원 활성화
Operator 작성자는 네트워크 프록시를 지원하는 Operator를 개발할 수 있습니다. dedicated-admin
역할의 관리자는 OLM(Operator Lifecycle Manager)에서 처리하는 환경 변수에 대한 프록시 지원을 구성합니다. 프록시된 클러스터를 지원하려면 Operator에서 다음 표준 프록시 변수에 대한 환경을 검사하고 해당 값을 Operands에 전달해야 합니다.
-
HTTP_PROXY
-
HTTPS_PROXY
-
NO_PROXY
이 튜토리얼에서는 예제 환경 변수로 HTTP_PROXY
를 사용합니다.
사전 요구 사항
- 클러스터 전체 egress 프록시가 활성화된 클러스터입니다.
프로세스
다음을 포함하도록
controllers/memcached_controller.go
파일을 편집합니다.operator-lib
라이브러리에서proxy
패키지를 가져옵니다.import ( ... "github.com/operator-framework/operator-lib/proxy" )
조정 루프에
proxy.ReadProxyVarsFromEnv
도우미 함수를 추가하고 Operand 환경에 결과를 추가합니다.for i, container := range dep.Spec.Template.Spec.Containers { dep.Spec.Template.Spec.Containers[i].Env = append(container.Env, proxy.ReadProxyVarsFromEnv()...) } ...
config/manager/manager.yaml
파일에 다음을 추가하여 Operator 배포에서 환경 변수를 설정합니다.containers: - args: - --leader-elect - --leader-election-id=ansible-proxy-demo image: controller:latest name: manager env: - name: "HTTP_PROXY" value: "http_proxy_test"
5.3.1.6. Operator 실행
Operator를 빌드하고 실행하려면 Operator SDK CLI를 사용하여 Operator를 번들한 다음 OLM(Operator Lifecycle Manager)을 사용하여 클러스터에 배포합니다.
OpenShift Dedicated 클러스터 대신 OpenShift Container Platform 클러스터에 Operator를 배포하려면 다음 두 가지 추가 배포 옵션을 사용할 수 있습니다.
- Go 프로그램으로 클러스터 외부에서 로컬로 실행합니다.
- 클러스터에서 배포로 실행합니다.
OLM을 사용하는 번들로 Go 기반 Operator를 실행하기 전에 지원되는 이미지를 사용하도록 프로젝트가 업데이트되었는지 확인합니다.
추가 리소스
- 클러스터 외부에서 로컬로 실행 (OpenShift Container Platform 문서)
- 클러스터에서 배포로 실행 (OpenShift Container Platform 문서)
5.3.1.6.1. Operator 번들링 및 Operator Lifecycle Manager를 통한 배포
5.3.1.6.1.1. Operator 번들
Operator 번들 형식은 Operator SDK 및 Operator Lifecycle Manager (OLM)의 기본 패키지 메서드입니다. Operator SDK를 사용하여 Operator 프로젝트를 번들 이미지로 빌드하고 푸시하여 OLM에서 Operator를 사용할 수 있습니다.
사전 요구 사항
- 개발 워크스테이션에 Operator SDK CLI가 설치됨
-
OpenShift CLI (
oc
) v4 이상이 설치됨 - Operator SDK를 사용하여 Operator 프로젝트를 초기화함
- Operator가 Go 기반인 경우 OpenShift Dedicated에서 실행하기 위해 지원되는 이미지를 사용하도록 프로젝트를 업데이트해야 합니다.
프로세스
Operator 프로젝트 디렉터리에서 다음
make
명령을 실행하여 Operator 이미지를 빌드하고 내보냅니다. 액세스할 수 있는 리포지토리를 참조하려면 다음 단계에서IMG
인수를 수정합니다. Quay.io와 같은 리포지토리 사이트에 컨테이너를 저장하기 위해 계정을 받을 수 있습니다.이미지를 빌드합니다.
$ make docker-build IMG=<registry>/<user>/<operator_image_name>:<tag>
참고Operator용 SDK에서 생성한 Dockerfile은
Go 빌드에
대해GOARCH=amd64
를 명시적으로 참조합니다. AMD64 이외의 아키텍처의 경우GOARCH=$TARGETARCH
로 수정할 수 있습니다. Docker는 자동으로-platform
에서 지정한 값으로 환경 변수를 설정합니다. Buildah를 사용하면-build-arg
를 목적으로 사용해야 합니다. 자세한 내용은 다중 아키텍처를 참조하십시오.이미지를 리포지토리로 내보냅니다.
$ make docker-push IMG=<registry>/<user>/<operator_image_name>:<tag>
Operator SDK
generate bundle
및bundle validate
명령을 비롯한 다양한 명령을 호출하는make bundle
명령을 실행하여 Operator 번들 매니페스트를 생성합니다.$ make bundle IMG=<registry>/<user>/<operator_image_name>:<tag>
Operator의 번들 매니페스트는 애플리케이션을 표시, 생성, 관리하는 방법을 설명합니다.
make bundle
명령은 Operator 프로젝트에서 다음 파일 및 디렉터리를 생성합니다.-
ClusterServiceVersion
오브젝트를 포함하는bundle/manifests
라는 번들 매니페스트 디렉터리 -
bundle/metadata
라는 번들 메타데이터 디렉터리 -
config/crd
디렉터리의 모든 CRD(사용자 정의 리소스 정의) -
Dockerfile
bundle.Dockerfile
그런 다음
operator-sdk bundle validate
를 사용하여 이러한 파일을 자동으로 검증하고 디스크상의 번들 표현이 올바른지 확인합니다.-
다음 명령을 실행하여 번들 이미지를 빌드하고 내보냅니다. OLM에서는 하나 이상의 번들 이미지를 참조하는 인덱스 이미지를 통해 Operator 번들을 사용합니다.
번들 이미지를 빌드합니다. 이미지를 내보낼 레지스트리, 사용자 네임스페이스, 이미지 태그에 대한 세부 정보를 사용하여
BUNDLE_IMG
를 설정합니다.$ make bundle-build BUNDLE_IMG=<registry>/<user>/<bundle_image_name>:<tag>
번들 이미지를 내보냅니다.
$ docker push <registry>/<user>/<bundle_image_name>:<tag>
5.3.1.6.1.2. Operator Lifecycle Manager를 사용하여 Operator 배포
OLM(Operator Lifecycle Manager)은 Kubernetes 클러스터에서 Operator 및 관련 서비스를 설치, 업데이트하고 라이프사이클을 관리하는 데 도움이 됩니다. OLM은 기본적으로 OpenShift Dedicated에 설치되고 Kubernetes 확장으로 실행되므로 추가 툴 없이 모든 Operator 라이프사이클 관리 기능에 웹 콘솔과 OpenShift CLI(oc
)를 사용할 수 있습니다.
Operator 번들 형식은 Operator SDK 및 OLM의 기본 패키지 메서드입니다. Operator SDK를 사용하여 OLM에서 번들 이미지를 신속하게 실행하여 올바르게 실행되는지 확인할 수 있습니다.
사전 요구 사항
- 개발 워크스테이션에 Operator SDK CLI가 설치됨
- Operator 번들 이미지를 빌드하여 레지스트리로 내보냄
-
Kubernetes 기반 클러스터에 OLM이 설치됨(
apiextensions.k8s.io/v1
CRD(예: OpenShift Dedicated 4)를 사용하는 경우 v1.16.0 이상) -
dedicated-admin
권한이 있는 계정을 사용하여oc
로 클러스터에 로그인 - Operator가 Go 기반인 경우 OpenShift Dedicated에서 실행하기 위해 지원되는 이미지를 사용하도록 프로젝트를 업데이트해야 합니다.
프로세스
다음 명령을 입력하여 클러스터에서 Operator를 실행합니다.
$ operator-sdk run bundle \1 -n <namespace> \2 <registry>/<user>/<bundle_image_name>:<tag> 3
- 1
run bundle
명령은 유효한 파일 기반 카탈로그를 생성하고 OLM을 사용하여 클러스터에 Operator 번들을 설치합니다.- 2
- 선택 사항: 기본적으로 이 명령은
~/.kube/config
파일의 현재 활성 프로젝트에 Operator를 설치합니다.-n
플래그를 추가하면 설치에 다른 네임스페이스 범위를 설정할 수 있습니다. - 3
- 이미지를 지정하지 않으면 명령에서
quay.io/operator-framework/opm:latest
를 기본 인덱스 이미지로 사용합니다. 이미지를 지정하면 명령에서 번들 이미지 자체를 인덱스 이미지로 사용합니다.
중요OpenShift Dedicated 4.11부터
run bundle
명령은 기본적으로 Operator 카탈로그의 파일 기반 카탈로그 형식을 지원합니다. Operator 카탈로그의 더 이상 사용되지 않는 SQLite 데이터베이스 형식은 계속 지원되지만 향후 릴리스에서 제거됩니다. Operator 작성자는 워크플로우를 파일 기반 카탈로그 형식으로 마이그레이션하는 것이 좋습니다.이 명령은 다음 작업을 수행합니다.
- 번들 이미지를 참조하는 인덱스 이미지를 생성합니다. 인덱스 이미지는 불투명하고 일시적이지만 프로덕션에서 카탈로그에 번들을 추가하는 방법을 정확하게 반영합니다.
- OperatorHub에서 Operator를 검색할 수 있도록 새 인덱스 이미지를 가리키는 카탈로그 소스를 생성합니다.
-
OperatorGroup
,Subscription
,InstallPlan
및 RBAC를 포함한 기타 모든 필수 리소스를 생성하여 Operator를 클러스터에 배포합니다.
5.3.1.7. 사용자 정의 리소스 생성
Operator가 설치되면 Operator에서 현재 클러스터에 제공하는 CR(사용자 정의 리소스)을 생성하여 Operator를 테스트할 수 있습니다.
사전 요구 사항
-
Memcached
CR을 제공하는 Memcached Operator의 예가 클러스터에 설치됨
프로세스
Operator가 설치된 네임스페이스로 변경합니다. 예를 들어
make deploy
명령을 사용하여 Operator를 배포한 경우 다음을 실행합니다.$ oc project memcached-operator-system
다음 사양을 포함하도록
config/samples/cache_v1_memcached.yaml
의 샘플Memcached
CR 매니페스트를 편집합니다.apiVersion: cache.example.com/v1 kind: Memcached metadata: name: memcached-sample ... spec: ... size: 3
CR을 생성합니다.
$ oc apply -f config/samples/cache_v1_memcached.yaml
Memcached
Operator에서 샘플 CR에 대한 배포를 올바른 크기로 생성하는지 확인합니다.$ oc get deployments
출력 예
NAME READY UP-TO-DATE AVAILABLE AGE memcached-operator-controller-manager 1/1 1 1 8m memcached-sample 3/3 3 3 1m
Pod 및 CR 상태를 확인하여 상태가 Memcached Pod 이름으로 업데이트되었는지 확인합니다.
Pod를 확인합니다.
$ oc get pods
출력 예
NAME READY STATUS RESTARTS AGE memcached-sample-6fd7c98d8-7dqdr 1/1 Running 0 1m memcached-sample-6fd7c98d8-g5k7v 1/1 Running 0 1m memcached-sample-6fd7c98d8-m7vn7 1/1 Running 0 1m
CR 상태 확인:
$ oc get memcached/memcached-sample -o yaml
출력 예
apiVersion: cache.example.com/v1 kind: Memcached metadata: ... name: memcached-sample ... spec: size: 3 status: nodes: - memcached-sample-6fd7c98d8-7dqdr - memcached-sample-6fd7c98d8-g5k7v - memcached-sample-6fd7c98d8-m7vn7
배포 크기를 업데이트합니다.
config/samples/cache_v1_memcached.yaml
파일을 업데이트하여Memcached
CR의spec.size
필드를3
에서5
로 변경합니다.$ oc patch memcached memcached-sample \ -p '{"spec":{"size": 5}}' \ --type=merge
Operator에서 배포 크기를 변경하는지 확인합니다.
$ oc get deployments
출력 예
NAME READY UP-TO-DATE AVAILABLE AGE memcached-operator-controller-manager 1/1 1 1 10m memcached-sample 5/5 5 5 3m
다음 명령을 실행하여 CR을 삭제합니다.
$ oc delete -f config/samples/cache_v1_memcached.yaml
이 튜토리얼의 일부로 생성된 리소스를 정리합니다.
make deploy
명령을 사용하여 Operator를 테스트한 경우 다음 명령을 실행합니다.$ make undeploy
operator-sdk run bundle
명령을 사용하여 Operator를 테스트한 경우 다음 명령을 실행합니다.$ operator-sdk cleanup <project_name>
5.3.1.8. 추가 리소스
- Operator SDK에서 생성한 디렉터리 구조에 대한 자세한 내용은 Go 기반 Operator의 프로젝트 레이아웃 을 참조하십시오.
-
클러스터 전체 송신 프록시가 구성된 경우
dedicated-
admin 역할의 관리자는 프록시 설정을 재정의하거나 OLM(Operator Lifecycle Manager)에서 실행되는 특정 Operator에 대한 사용자 정의 CA 인증서를 삽입 할 수 있습니다.
5.3.2. Go 기반 Operator의 프로젝트 레이아웃
operator-sdk
CLI에서는 각 Operator 프로젝트에 대해 다양한 패키지 및 파일을 생성하거나 스캐폴드를 지정할 수 있습니다.
Operator 프로젝트의 관련 스캐폴딩 및 테스트 툴을 포함하여 Operator SDK CLI 툴의 Red Hat 지원 버전은 더 이상 사용되지 않으며 향후 OpenShift Dedicated 릴리스에서 제거될 예정입니다. Red Hat은 현재 릴리스 라이프사이클 동안 이 기능에 대한 버그 수정 및 지원을 제공하지만 이 기능은 더 이상 개선 사항을 받지 않으며 향후 OpenShift Dedicated 릴리스에서 제거됩니다.
새 Operator 프로젝트를 생성하는 데 Red Hat 지원 버전의 Operator SDK는 권장되지 않습니다. 기존 Operator 프로젝트가 있는 Operator 작성자는 OpenShift Dedicated 4와 함께 릴리스된 Operator SDK CLI 툴 버전을 사용하여 프로젝트를 유지 관리하고 최신 버전의 OpenShift Dedicated를 대상으로 하는 Operator 릴리스를 생성할 수 있습니다.
Operator 프로젝트의 다음과 같은 관련 기본 이미지는 더 이상 사용되지 않습니다. 이러한 기본 이미지의 런타임 기능 및 구성 API는 버그 수정 및 CVE 문제를 해결하는 데 계속 지원됩니다.
- Ansible 기반 Operator 프로젝트의 기본 이미지
- Helm 기반 Operator 프로젝트의 기본 이미지
지원되지 않는 커뮤니티 유지 관리 버전에 대한 자세한 내용은 Operator SDK(Operator Framework) 를 참조하십시오.
5.3.2.1. Go 기반 프로젝트 레이아웃
operator-sdk init
명령을 사용하여 생성된 기본 유형의 Go 기반 Operator 프로젝트에는 다음 파일과 디렉터리가 포함됩니다.
파일 또는 디렉터리 | 목적 |
---|---|
|
Operator의 기본 프로그램으로, 모든 CRD(사용자 정의 리소스 정의)를 |
|
CRD의 API를 정의하는 디렉터리 트리입니다. |
|
컨트롤러 구현입니다. |
| CRD, RBAC, 인증서를 포함하여 클러스터에 컨트롤러를 배포하는 데 사용하는 Kubernetes 매니페스트입니다. |
| 컨트롤러를 빌드하고 배포하는 데 사용하는 대상입니다. |
| 컨테이너 엔진에서 Operator를 빌드하는 데 사용하는 지침입니다. |
| CRD 등록, RBAC 설정, Operator를 배포로 배포하는 Kubernetes 매니페스트입니다. |
5.3.3. 최신 Operator SDK 버전을 위한 Go 기반 Operator 프로젝트 업데이트
OpenShift Dedicated 4는 Operator SDK 1.36.1을 지원합니다. 워크스테이션에 1.31.0 CLI가 이미 설치되어 있는 경우 최신 버전을 설치하여 CLI를 1.36.1으로 업데이트할 수 있습니다.
Operator 프로젝트의 관련 스캐폴딩 및 테스트 툴을 포함하여 Operator SDK CLI 툴의 Red Hat 지원 버전은 더 이상 사용되지 않으며 향후 OpenShift Dedicated 릴리스에서 제거될 예정입니다. Red Hat은 현재 릴리스 라이프사이클 동안 이 기능에 대한 버그 수정 및 지원을 제공하지만 이 기능은 더 이상 개선 사항을 받지 않으며 향후 OpenShift Dedicated 릴리스에서 제거됩니다.
새 Operator 프로젝트를 생성하는 데 Red Hat 지원 버전의 Operator SDK는 권장되지 않습니다. 기존 Operator 프로젝트가 있는 Operator 작성자는 OpenShift Dedicated 4와 함께 릴리스된 Operator SDK CLI 툴 버전을 사용하여 프로젝트를 유지 관리하고 최신 버전의 OpenShift Dedicated를 대상으로 하는 Operator 릴리스를 생성할 수 있습니다.
Operator 프로젝트의 다음과 같은 관련 기본 이미지는 더 이상 사용되지 않습니다. 이러한 기본 이미지의 런타임 기능 및 구성 API는 버그 수정 및 CVE 문제를 해결하는 데 계속 지원됩니다.
- Ansible 기반 Operator 프로젝트의 기본 이미지
- Helm 기반 Operator 프로젝트의 기본 이미지
지원되지 않는 커뮤니티 유지 관리 버전에 대한 자세한 내용은 Operator SDK(Operator Framework) 를 참조하십시오.
그러나 기존 Operator 프로젝트에서 Operator SDK 1.36.1과의 호환성을 유지하려면 1.31.0 이후의 중단된 변경 사항에 대한 업데이트 단계가 필요합니다. 1.31.0을 사용하여 이전에 생성되거나 유지 관리되는 Operator 프로젝트에서 업데이트 단계를 수동으로 수행해야 합니다.
5.3.3.1. Operator SDK 1.36.1의 Go 기반 Operator 프로젝트 업데이트
다음 절차에서는 1.36.1과의 호환성을 위해 기존 Go 기반 Operator 프로젝트를 업데이트합니다.
사전 요구 사항
- Operator SDK 1.36.1 설치
- Operator SDK 1.31.0을 사용하여 생성되거나 유지 관리되는 Operator 프로젝트
프로세스
다음 예와 같이 Operator 프로젝트의 Makefile을 편집하여 Operator SDK 버전을
v1.36.1-ocp
로 업데이트합니다.Makefile의 예
# Set the Operator SDK version to use. By default, what is installed on the system is used. # This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. OPERATOR_SDK_VERSION ?= v1.36.1-ocp
RHEL(Red Hat Enterprise Linux) 9 기반 이미지를 사용하도록
kube-rbac-proxy
컨테이너를 업데이트합니다.다음 파일에서
kube-rbac-proxy
컨테이너 항목을 찾습니다.-
config/default/manager_auth_proxy_patch.yaml
-
Operator 프로젝트의
bundle/manifests/<operator_name>.clusterserviceversion.yaml
(예: 튜토리얼의memcached-operator.clusterserviceversion.yaml
)
-
ose-kube-rbac-proxy에서
ose-kube-rbac-proxy
-rhel9v4
로 업데이트합니다.v4
이미지 태그가 있는ose-kube-rbac-proxy-rhel9
풀 사양의 예# ... containers: - name: kube-rbac-proxy image: registry.redhat.io/openshift4/ose-kube-rbac-proxy-rhel9:v4 # ...
이제 Go
/v4
플러그인이 안정적이며 Go 기반 Operator를 스캐폴드할 때 사용되는 기본 버전입니다. Golang v2 및 v3 플러그인에서 새로운 Golang v4 플러그인으로 전환하면 상당한 변경 사항이 추가되었습니다. 이 마이그레이션은 Golang 개발의 진화하는 환경을 반영하여 프로젝트의 기능과 호환성을 개선하도록 설계되었습니다.이러한 변경 사항의 추론에 대한 자세한 내용은 Kubebuilder 설명서의 go/v3 vs go/v4 를 참조하십시오.
v4 플러그인 형식으로의 마이그레이션 프로세스 및 자세한 마이그레이션 단계를 포괄적으로 이해하려면 Kubebuilder 문서에서 수동으로 파일을 업데이트하여 이동/v3에서 go/v4로 마이그레이션을 참조하십시오.
-
kustomize/v2
플러그인은 이제 안정적인 상태이며go/v4
,ansible/v1
,helm/v1
,hybrid/v1-alpha
플러그인을 사용할 때 플러그인 체인에 사용되는 기본 버전입니다. 이 기본 스캐폴드에 대한 자세한 내용은 Kubebuilder 설명서의 Kustomize v2 를 참조하십시오. Operator 프로젝트에서 다중 플랫폼 또는 다중 아키텍처, 빌드를 사용하는 경우 기존
docker-buildx
대상을 프로젝트 Makefile의 다음 정의로 교체합니다.Makefile의 예
docker-buildx: ## Build and push the Docker image for the manager for multi-platform support - docker buildx create --name project-v3-builder docker buildx use project-v3-builder - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile . - docker buildx rm project-v3-builder
1.29를 사용하려면 Operator 프로젝트의 Kubernetes 버전을 업그레이드해야 합니다. 프로젝트 구조, Makefile 및
go.mod
파일을 다음과 같이 변경해야 합니다.중요Go/v3
플러그인은 Kubebuilder에서 더 이상 사용되지 않으므로 Operator SDK도 향후 릴리스에서go/v4
로 마이그레이션되고 있습니다.go.mod
파일을 업데이트하여 종속 항목을 업그레이드합니다.k8s.io/api v0.29.2 k8s.io/apimachinery v0.29.2 k8s.io/client-go v0.29.2 sigs.k8s.io/controller-runtime v0.17.3
다음 명령을 실행하여 업그레이드된 종속 항목을 다운로드합니다.
$ go mod tidy
이제 Kustomize로 빌드된 모든 리소스가 포함된 파일을 생성할 수 있습니다. 이 파일은 종속 항목 없이 이 프로젝트를 설치하는 데 필요합니다. 다음과 같이 변경하여 Makefile을 업데이트합니다.
+ .PHONY: build-installer + build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml
다음과 같이 변경하여 Makefile에서
ENVTEST_K8S_VERSION
변수를 업데이트합니다.- ENVTEST_K8S_VERSION = 1.28.3 + ENVTEST_K8S_VERSION = 1.29.0
Makefile에서 다음 섹션을 제거하십시오.
- GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint - GOLANGCI_LINT_VERSION ?= v1.54.2 - golangci-lint: - @[ -f $(GOLANGCI_LINT) ] || { \ - set -e ;\ - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell dirname $(GOLANGCI_LINT)) $(GOLANGCI_LINT_VERSION) ;\ - }
다음과 같이 변경하여 Makefile을 업데이트합니다.
예 5.2. Makefile 변경
- ## Tool Binaries - KUBECTL ?= kubectl - KUSTOMIZE ?= $(LOCALBIN)/kustomize - CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen - ENVTEST ?= $(LOCALBIN)/setup-envtest - - ## Tool Versions - KUSTOMIZE_VERSION ?= v5.2.1 - CONTROLLER_TOOLS_VERSION ?= v0.13.0 - - .PHONY: kustomize - kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. - $(KUSTOMIZE): $(LOCALBIN) - @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ - echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ - rm -rf $(LOCALBIN)/kustomize; \ - fi - test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION) - - .PHONY: controller-gen - controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. - $(CONTROLLER_GEN): $(LOCALBIN) - test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ - GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) - - .PHONY: envtest - envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. - $(ENVTEST): $(LOCALBIN) - test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + ## Tool Binaries + KUBECTL ?= kubectl + KUSTOMIZE ?= $(LOCALBIN)/kustomize-$(KUSTOMIZE_VERSION) + CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen-$(CONTROLLER_TOOLS_VERSION) + ENVTEST ?= $(LOCALBIN)/setup-envtest-$(ENVTEST_VERSION) + GOLANGCI_LINT = $(LOCALBIN)/golangci-lint-$(GOLANGCI_LINT_VERSION) + + ## Tool Versions + KUSTOMIZE_VERSION ?= v5.3.0 + CONTROLLER_TOOLS_VERSION ?= v0.14.0 + ENVTEST_VERSION ?= release-0.17 + GOLANGCI_LINT_VERSION ?= v1.57.2 + + .PHONY: kustomize + kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. + $(KUSTOMIZE): $(LOCALBIN) + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) + + .PHONY: controller-gen + controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. + $(CONTROLLER_GEN): $(LOCALBIN) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + + .PHONY: envtest + envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. + $(ENVTEST): $(LOCALBIN) + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + + .PHONY: golangci-lint + golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. + $(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,${GOLANGCI_LINT_VERSION}) + + # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist + # $1 - target path with name of binary (ideally with version) + # $2 - package url which can be installed + # $3 - specific version of package + define go-install-tool + @[ -f $(1) ] || { \ + set -e; \ + package=$(2)@$(3) ;\ + echo "Downloading $${package}" ;\ + GOBIN=$(LOCALBIN) go install $${package} ;\ + mv "$$(echo "$(1)" | sed "s/-$(3)$$//")" $(1) ;\ + } + endef
5.3.3.2. 추가 리소스
- Operator SDK 1.31.0의 프로젝트 업그레이드 (OpenShift Dedicated 4.16)
- 번들 형식으로 패키지 매니페스트 프로젝트 마이그레이션