쿠버네티스 오퍼레이터 구축
43254 단어 kubernetesoperatorsdkgoopenshift
전제 조건:
Operator-sdk를 사용하여 프로젝트 부트스트랩
먼저 쿠버네티스 오퍼레이터란? 오퍼레이터를 사용하면 사용자 정의 리소스를 클러스터에 추가하여 Kubernetes API를 확장할 수 있습니다. 이것은 제가 만들 수 있는 가장 기본적인 연산자입니다. 마이크로 서비스용 포드를 생성하고 마이크로 서비스용 경로를 생성하며 복제본의 양을 지정할 수 있습니다. 내가 취한 모든 단계를 설명하겠습니다. 먼저 생성 명령을 사용하여 연산자 프로젝트를 스캐폴드합니다.
mkdir pod-route
cd pod-route
# --domain example.com is used in the operator-sdk quickstart guide this is used to create the api group, think of package in java.
# This was my first gotcha following my misreading of the docs. You need to be careful when choosing domain name as is difficult to revert after its generated.I will continue with quay.io for now.
# --repo is your git repo where you operator code will live
operator-sdk init --domain quay.io --repo github.com/austincunningham/pod-route
# Add a controller
# --version I use v1alpha1 (this is a Kubernetes API version for early candidates)
# --kind name of Custom Resource
operator-sdk create api --version v1alpha1 --kind Podroute --resource --controller
# build and push the operator image make docker-build docker-push IMG="quay.io/austincunningham/pod-route:v0.0.1"
파일은 this과 같아야 하고 컨테이너repo가 푸시됩니다.
다음으로 내
api/v1alpha1/podroute_types.go
파일 사양PodrouteSpec
을 편집합니다. 스펙은 기본적으로 운영자가 관리하고 싶은 것입니다.// PodrouteSpec defines the desired state of Podroute
type PodrouteSpec struct {
// Image container image string e.g. "quay.io/austincunningham/always200:latest"
// Replicas number of containers to spin up
Image string `json:"image,omitempty"`
Replicas int32 `json:"replicas,omitempty"`
}
유형 파일을 변경한 후 다음 명령을 실행하여 연산자에서 파일을 업데이트해야 합니다.
make generate
make manifests
컨트롤러 로직 추가
이제
controllers/podroute_controller.go
에서 내 조정 논리를 살펴볼 수 있습니다. 포드 및 배포에 대한 일부 RBAC 규칙을 추가하고 클라이언트가 CR(Custom Resource)을 찾기 위해 클러스터에 도착하도록 합니다. 나머지는 생성된 코드입니다.package controllers
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
quayiov1alpha1 "github.com/austincunningham/pod-route/api/v1alpha1"
)
// PodrouteReconciler reconciles a Podroute object
type PodrouteReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=quay.io,resources=podroutes,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=quay.io,resources=podroutes/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=quay.io,resources=podroutes/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 *PodrouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// your logic here
// Create a Custom Resource object for Podroute, quayio part of the name is due to my earlier mistake
cr := &quayiov1alpha1.Podroute{}
// do a kubernetes client get to check if the CR is on the Cluster
err := r.Client.Get(ctx, req.NamespacedName, cr)
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *PodrouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&quayiov1alpha1.Podroute{}).
Complete(r)
}
따라서 가장 먼저 해야 할 일은 기존 배포를 확인하고 존재하지 않는 경우 생성하는 것입니다.
마지막
Reconcile
앞의 return ctrl.Result{}, nil
함수에서 다음과 같이 createDeployment 함수에 대한 호출을 추가합니다. deployment, err := r.createDeployment(cr, r.podRouteDeployment(cr))
if err != nil {
return reconcile.Result{}, err
}
// just logging here to keep Go happy will use later
log.Log.Info("deployment", deployment)
모든 리소스에 대해 이것을 사용할 레이블 기능을 만듭니다.
func labels(cr *quayiov1alpha1.Podroute, tier string) map[string]string {
// Fetches and sets labels
return map[string]string{
"app": "PodRoute",
"podroute_cr": cr.Name,
"tier": tier,
}
}
배포 개체를 만듭니다.
// This is the equivalent of creating a deployment yaml and returning it
// It doesn't create anything on cluster
func (r *PodrouteReconciler) podRouteDeployment(cr *quayiov1alpha1.Podroute) *appsv1.Deployment {
// Build a Deployment
labels := labels(cr, "backend-podroute")
size := cr.Spec.Replicas
podRouteDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-route",
Namespace: cr.Namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: &size,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Image: cr.Spec.Image,
ImagePullPolicy: corev1.PullAlways,
Name: "podroute-pod",
Ports: []corev1.ContainerPort{{
ContainerPort: 8080,
Name: "podroute",
}},
}},
},
},
},
}
// sets the this controller as owner
controllerutil.SetControllerReference(cr, podRouteDeployment, r.Scheme)
return podRouteDeployment
}
기존 배포에 대해 Client.Get을 사용하여 클러스터를 확인한 다음 위에서 만든 배포 개체를 사용하여 클러스터를 만듭니다.
// check for a deployment if it doesn't exist it creates one on cluster using the deployment created in deployment
func (r PodrouteReconciler) createDeployment(cr *quayiov1alpha1.Podroute, deployment *appsv1.Deployment) (*appsv1.Deployment, error) {
// check for a deployment in the namespace
found := &appsv1.Deployment{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: deployment.Name, Namespace: cr.Namespace}, found)
if err != nil {
log.Log.Info("Creating Deployment")
err = r.Client.Create(context.TODO(), deployment)
if err != nil {
log.Log.Error(err, "Failed to create deployment")
return found, err
}
}
return found, nil
}
다음으로 조정 기능에서 배포 복제본이 CR(사용자 지정 리소스)의 번호와 일치하는지 확인하여 주석
log.Log.Info("deployment", deployment)
을 제거하고 다음으로 바꿉니다. // If the spec.Replicas in the CR changes, update the deployment number of replicas
if deployment.Spec.Replicas != &cr.Spec.Replicas {
controllerutil.CreateOrUpdate(context.TODO(), r.Client, deployment, func() error {
deployment.Spec.Replicas = &cr.Spec.Replicas
return nil
})
}
그래서 지금까지 우리는 이미지(컨테이너)와 복제본 수를 가져오고 이에 대한 배포를 생성하는 CR을 가지고 있습니다. 다음으로 서비스 및 경로를 생성합니다. 이들은 배포와 유사한 패턴을 갖습니다. 즉, 경로/서비스 객체를 생성하고 생성되지 않은 경우 존재하는지 확인합니다. 서비스부터 시작하겠습니다. 마지막 반환 전에 reconcile 함수에서 createService 함수 호출을 추가합니다
return ctrl.Result{}, nil
. err = r.createService(cr, r.podRouteService(cr))
if err != nil {
return reconcile.Result{}, err
}
이 함수를 사용하여 서비스 개체를 생성합니다.
// This is the equivalent of creating a service yaml and returning it
// It doesnt create anything on cluster
func (r PodrouteReconciler) podRouteService(cr *quayiov1alpha1.Podroute) *corev1.Service {
labels := labels(cr, "backend-podroute")
podRouteService := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "podroute-service",
Namespace: cr.Namespace,
},
Spec: corev1.ServiceSpec{
Selector: labels,
Ports: []corev1.ServicePort{{
Protocol: corev1.ProtocolTCP,
Port: 8080,
TargetPort: intstr.FromInt(8080),
}},
},
}
controllerutil.SetControllerReference(cr, podRouteService, r.Scheme)
return podRouteService
}
위 서비스 개체에서 서비스를 생성하는 기능 추가
// check for a service if it doesn't exist it creates one on cluster using the service created in podRouteService
func (r PodrouteReconciler) createService(cr *quayiov1alpha1.Podroute, podRouteServcie *corev1.Service) error {
// check for a service in the namespace
found := &corev1.Service{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: podRouteServcie.Name, Namespace: cr.Namespace}, found)
if err != nil {
log.Log.Info("Creating Service")
err = r.Client.Create(context.TODO(), podRouteServcie)
if err != nil {
log.Log.Error(err, "Failed to create Service")
return err
}
}
return nil
}
마지막으로 마지막 전에 조정에서 경로 추가 createRoute 함수 호출
return ctrl.Result{}, nil
err = r.createRoute(cr, r.podRouteRoute(cr))
if err != nil{
return reconcile.Result{}, err
}
경로 개체에 대한 함수 만들기
// This is the equivalent of creating a route yaml file and returning it
// It doesn't create anything on cluster
func (r PodrouteReconciler) podRouteRoute(cr *quayiov1alpha1.Podroute) *routev1.Route {
labels := labels(cr, "backend-podroute")
podRouteRoute := &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Name: "podroute-route",
Namespace: cr.Namespace,
Labels: labels,
},
Spec: routev1.RouteSpec{
To: routev1.RouteTargetReference{
Kind: "Service",
Name: "podroute-service",
},
Port: &routev1.RoutePort{
TargetPort: intstr.FromInt(8080),
},
},
}
controllerutil.SetControllerReference(cr, podRouteRoute, r.Scheme)
return podRouteRoute
}
위의 경로 개체에서 경로를 만드는 기능을 추가하십시오.
// check for a route if it doesn't exist it creates one on cluster using the route created in podRouteRoute
func (r PodrouteReconciler) createRoute(cr *quayiov1alpha1.Podroute, podRouteRoute *routev1.Route) error {
// check for a route in the namespace
found := &routev1.Route{}
err := r.Client.Get(context.TODO(), types.NamespacedName{Name: podRouteRoute.Name, Namespace: cr.Namespace}, found)
if err != nil {
log.Log.Info("Creating Route")
err = r.Client.Create(context.TODO(), podRouteRoute)
if err != nil {
log.Log.Error(err, "Failed to create Route")
return err
}
}
return nil
}
NOTE: imports did change with these code changes
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
routev1 "github.com/openshift/api/route/v1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
quayiov1alpha1 "github.com/austincunningham/pod-route/api/v1alpha1"
)
또한 route는 kubernetes 네이티브가 아니라 openshift이기 때문에 main.go 파일의 체계에 추가해야 했습니다.
if err := routev1.AddToScheme(mgr.GetScheme()); err != nil {
setupLog.Error(err, "failed to add routev1 to scheme")
os.Exit(1)
}
오퍼레이터 테스트
crc start
로 CRC(code ready containers) 시작crc start
INFO Adding crc-admin and crc-developer contexts to kubeconfig...
Started the OpenShift cluster.
The server is accessible via web console at:
https://console-openshift-console.apps-crc.testing
Log in as administrator:
Username: kubeadmin
Password: KUBEADMIN_PASSWORD
Log in as user:
Username: developer
Password: developer
Use the 'oc' command line interface:
$ eval $(crc oc-env)
$ oc login -u developer https://api.crc.testing:6443
kubeadmin으로 CRC 클러스터에 로그인합니다.
oc login -u kubeadmin -p KUBEADMIN_PASSWORD https://api.crc.testing:6443
프로젝트 생성, Makefile에는 우리가 사용할 수 있는 opeator-sdk에 의해 생성된 많은 명령이 있습니다.
oc new-project podroute
# Installs the custom resource definitions onto the cluster
make install
# Create the CR on cluster
oc apply -f - <<EOF
---
apiVersion: quay.io/v1alpha1
kind: Podroute
metadata:
name: test-podroute
namespace: podroute
spec:
image: quay.io/austincunningham/always200:latest
replicas: 3
EOF
# We can then run the operator locally
make run
# Should see something like
2022-06-10T14:41:28.854+0100 INFO Creating Deployment
2022-06-10T14:41:28.980+0100 INFO Creating Service
2022-06-10T14:41:29.114+0100 INFO Creating Route
모두 정상임을 확인할 수 있습니다
# get the servic
oc get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
podroute-service ClusterIP 10.217.5.14 <none> 8080/TCP 4m38s
# get the route
oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
podroute-route podroute-route-podroute.apps-crc.testing podroute-service 8080 None
# should be 3 pod replicas
oc get pods
NAME READY STATUS RESTARTS AGE
pod-route-96b87c455-6sw2h 1/1 Running 0 4m12s
pod-route-96b87c455-ghdm8 1/1 Running 0 4m12s
pod-route-96b87c455-md426 1/1 Running 0 4m12s
# the get route should be alive and return ok
curl http://podroute-route-podroute.apps-crc.testing/get
OK%
참조:
Operator-sdk quickstart guide
Operator-sdk Golang tutorial
Git repo
NOTE: built with operator-sdk v1.15.0
Reference
이 문제에 관하여(쿠버네티스 오퍼레이터 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/austincunningham/build-a-kubernetes-operator-1g08텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)