일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 카오스 엔지니어링
- Kubernetes 인증
- gitops
- Argo
- Kubeflow
- nginx ingress
- kubernetes operator
- gitea
- Kubernetes
- blue/green
- MLflow
- argocd
- seldon core
- 오퍼레이터
- Model Serving
- opensearch
- operator
- Kopf
- Continuous Deployment
- opentelemetry
- mlops
- keda
- Litmus
- CANARY
- argo rollout
- Pulumi
- CI/CD
- knative
- serving
- tekton
- Today
- Total
Kubernetes 이야기
Kopf (소개) 본문
Kubernetes Operator는 내부에 구현된 일부 도메인 로직을 사용하여 특정 종류의 개체를 오케스트레이션하는 일종의 컨트롤러이다.
Operator에 대해서는 아래 글을 참고한다.
https://kmaster.tistory.com/97
Go 언어에서는 Kubebuilder나 OperatorSDK와 같은 프레임워크를 사용한다. Python에서는 Kopf라는 프레임워크가 존재한다.
다른 프레임워크와 마찬가지로 Kopf는 오퍼레이터를 실행하고, 쿠버네티스 클러스터와 통신하고, 쿠버네티스 이벤트를 Kopf 기반 오퍼레이터의 순수 Python 기능으로 마샬링하기 위한 "외부" 툴킷과 "내부" 라이브러리를 모두 제공한다.
아키텍처
주요 역할
- Core : Kopf 기반 운영자가 사용하는 주요 기능으로, Core가 운영자를 움직이게 한다.
- Cogs : 거의 모든 모듈의 프레임워크 전체에서 사용되는 유틸리티이다.
- Kits : 일부 시나리오 및/또는 설정을 위해 운영자 개발자에게 제공되는 유틸리티 및 특수 도구이다. 프레임워크 자체는 이를 사용하지 않는다.
설치
# pipenv --python 3.11
# pipenv install kopf[uvloop]
예제
이번 예제는 Tekton Pipeline 을 실행하는 CRD와 이 CRD를 처리하는 오퍼레이터를 만들어보자. ( 파이프라인을 실행하는 용도이기 때문에 생성만 실습한다. )
Tekton Pipeline 예제는 다음을 참고한다.
https://kmaster.tistory.com/139
실행할 PipelineRun은 다음과 같다.
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: pipelinerun-
spec:
serviceAccountName: tekton-sa
pipelineRef:
name: pipeline
params:
- name: giturl
value: https://github.com/kmaster8/flask-helloworld.git
workspaces:
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
여기서 CRD로 입력받을 값은 github URL이다.
다음과 같은 CRD를 만들어보자.
(CRD 생성법은 다음을 참고한다. https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/)
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: tektonpipelineruns.kmaster8.com
spec:
group: kmaster8.com
names:
kind: TektonPipelineRun
plural: tektonpipelineruns
singular: tektonpipelinerun
shortNames:
- tpr
scope: Namespaced
versions:
- name: v1beta1
schema:
openAPIV3Schema:
properties:
spec:
properties:
giturl:
type: string
required:
- giturl
type: object
status:
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
served: true
storage: true
additionalPrinterColumns:
- name: GitUrl
type: string
jsonPath: .spec.giturl
description: Git repository
생성 후 조회를 해보자.
# k get tektonpipelineruns.kmaster8.com -A
No resources found
이제 다음과 같은 CR을 생성해 보자.
apiVersion: kmaster8.com/v1beta1
kind: TektonPipelineRun
metadata:
name: run1
spec:
giturl: "https://github.com/kmaster8/flask-helloworld.git"
생성 후 조회해 보자.
# k get tektonpipelineruns.kmaster8.com -A
NAMESPACE NAME GITURL
test run1 https://github.com/kmaster8/flask-helloworld.git
상세정보는 다음과 같다.
# k describe tpr run1
Name: run1
Namespace: test
Labels: <none>
Annotations: <none>
API Version: kmaster8.com/v1beta1
Kind: TektonPipelineRun
Metadata:
Creation Timestamp: 2023-02-03T12:54:07Z
Generation: 1
Managed Fields:
API Version: kmaster8.com/v1beta1
Fields Type: FieldsV1
fieldsV1:
f:spec:
.:
f:giturl:
Manager: kubectl-create
Operation: Update
Time: 2023-02-03T12:54:07Z
Resource Version: 221474456
UID: cd8d06fb-5965-4fe6-8494-fccfcf055a9b
Spec:
Giturl: https://github.com/kmaster8/flask-helloworld.git
Events: <none>
이제 Kopf 에서 생성 이벤트를 살펴보자. 아직 아무 쓸모 없는 뼈대이다.
import kopf
@kopf.on.create('kmaster8.com', 'v1beta1', 'tektonpipelineruns')
def create_fn(spec, **kwargs):
print(f"Creating: {spec}")
return {'message': 'done'}
실행을 해보면 다음과 같다.
# kopf run create.py
/root/.local/share/virtualenvs/kopf-cdlYO4Rx/lib/python3.11/site-packages/kopf/_core/reactor/running.py:176: FutureWarning: Absence of either namespaces or cluster-wide flag will become an error soon. For now, switching to the cluster-wide mode for backward compatibility.
warnings.warn("Absence of either namespaces or cluster-wide flag will become an error soon."
[2023-02-03 21:55:27,579] kopf._core.engines.a [INFO ] Initial authentication has been initiated.
[2023-02-03 21:55:27,586] kopf.activities.auth [INFO ] Activity 'login_with_kubeconfig' succeeded.
[2023-02-03 21:55:27,586] kopf._core.engines.a [INFO ] Initial authentication has finished.
Creating: {'giturl': 'https://github.com/kmaster8/flask-helloworld.git'}
[2023-02-03 21:55:28,782] kopf.objects [INFO ] [test/run1] Handler 'create_fn' succeeded.
[2023-02-03 21:55:28,783] kopf.objects [INFO ] [test/run1] Creation is processed: 1 succeeded; 0 failed.
실행 후 상세 정보를 보자.
# k describe tpr run1
Name: run1
Namespace: test
Labels: <none>
Annotations: kopf.zalando.org/last-handled-configuration: {"spec":{"giturl":"https://github.com/kmaster8/flask-helloworld.git"}}
API Version: kmaster8.com/v1beta1
Kind: TektonPipelineRun
Metadata:
Creation Timestamp: 2023-02-03T12:54:07Z
Generation: 2
...
Spec:
Giturl: https://github.com/kmaster8/flask-helloworld.git
Status:
create_fn:
Message: done
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Logging 49s kopf Creation is processed: 1 succeeded; 0 failed.
Normal Logging 49s kopf Handler 'create_fn' succeeded.
Status에 Message: done 이 추가된 것을 확인할 수 있다.
debug 메시지를 보기위해서는 kopf run create.py --verbose 로 실행하면 상세한 로그를 조회할 수 있다.
연산자를 중단하고 다시 시작하면 이미 성공적으로 처리되었으므로 운영자는 개체를 처리하지 않는다.
이제 실제 Tekton Pipeline을 실행하는 로직을 추가해 보자. 먼저 kubernetes api 를 설치해보자.
# pipenv install kubernetes
다음과 같은 생성 함수를 만들어보자.
import kopf
import yaml
import kubernetes.client
from kubernetes.client.rest import ApiException
@kopf.on.create('kmaster8.com', 'v1beta1', 'tektonpipelineruns')
def create_fn(spec, **kwargs):
doc = get_yaml(spec, **kwargs)
kopf.adopt(doc)
try:
api = kubernetes.client.CustomObjectsApi()
group="tekton.dev"
version="v1beta1"
plural="pipelineruns"
crd = api.create_namespaced_custom_object(group=group,version=version,namespace=doc['metadata']['namespace'], plural=plural, body=doc)
return {'children': [crd['metadata']['uid']]}
except ApiException as e:
print("Exception when calling AppsV1Api->create_namespaced_deployment: %s\n" % e)
def get_yaml(spec, **kwargs):
# Create the deployment spec
doc = yaml.safe_load(f"""
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: pipelinerun-
spec:
serviceAccountName: tekton-sa
pipelineRef:
name: pipeline
params:
- name: giturl
value: {spec.get('giturl')}
workspaces:
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
""")
return doc
[실행결과]
다음과 같이 Tekton Pipeline이 실행된 것을 볼 수 있다.
# k get pipelineruns.tekton.dev -A
NAMESPACE NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
test pipelinerun-2dfrg True Succeeded 52s 23s
Status에는 PipelineRun 의 uid가 저장된 것을 볼 수 있다.
...
Status:
create_fn:
Children:
91ab3a95-6920-4659-8307-6104faa451dc
여기서, 생성된 pipelineruns 를 살펴보자.
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"tekton.dev/v1beta1","kind":"Pipeline","metadata":{"annotations":{},"name":"pipeline","namespace":"test"},"spec":{"params":[{"description":"THIS PARAMETER COMING FROM TRIGGER TEMPLATE\n","name":"giturl"}],"tasks":[{"name":"fetch-repo","params":[{"name":"url","value":"$(params.giturl)"}],"taskRef":{"name":"git-clone"},"workspaces":[{"name":"output","workspace":"shared-data"}]},{"name":"build-container-image","params":[{"name":"app_repo","value":"dir:///workspace/output/"},{"name":"container_image","value":"ghcr.io/kmaster8/myimage"},{"name":"container_tag","value":"$(tasks.fetch-repo.results.commit)"}],"runAfter":["fetch-repo"],"taskRef":{"name":"build-kaniko-git"},"workspaces":[{"name":"output","workspace":"shared-data"}]},{"name":"manifest-update","params":[{"name":"imageurl","value":"ghcr.io/kmaster8/myimage"},{"name":"imagetag","value":"$(tasks.fetch-repo.results.commit)"}],"runAfter":["build-container-image"],"taskRef":{"name":"manifest-deploy"}}],"workspaces":[{"description":"This workspace will receive the cloned git repo and be passed\nto the next Task for the repo's README.md file to be read.\n","name":"shared-data"}]}}
creationTimestamp: "2023-02-03T13:56:40Z"
generateName: pipelinerun-
...
ownerReferences:
- apiVersion: kmaster8.com/v1beta1
blockOwnerDeletion: true
controller: true
kind: TektonPipelineRun
name: run1
uid: 5aa4c20d-a991-4993-a006-f9e45f50d799
resourceVersion: "221494762"
uid: f5444346-23cd-4fc0-a1e8-3994503caf87
...
위와 같이 ownerReferences가 추가된 것을 볼 수 있다. 이건 create.py 에서 다음과 같이 넣어주면
kopf.adopt(doc)
ownerReferences 가 추가된다. tektonepipelinerun CR을 삭제하면 pipelinerun CR이 삭제될 것이다.
K8S API 서버에 호출하는 경우는 다음과 같이 할 수 있다.
# curl -k -H "Authorization: Bearer $TOKEN" https://localhost:6443/apis/kmaster8.com/v1beta1 --insecure
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "kmaster8.com/v1beta1",
"resources": [
{
"name": "tektonpipelineruns",
"singularName": "tektonpipelinerun",
"namespaced": true,
"kind": "TektonPipelineRun",
"verbs": [
"delete",
"deletecollection",
"get",
"list",
"patch",
"create",
"update",
"watch"
],
"shortNames": [
"tpr"
],
"storageVersionHash": "kaNzSmQan7Y="
}
]
}
조회
curl -k -H "Authorization: Bearer $TOKEN" https://localhost:6443/apis/kmaster8.com/v1beta1/namespaces/test/tektonpipelineruns --insecure
생성
curl -v -k -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-type: application/yaml" \
--data-binary @cr.yaml \
https://localhost:6443/apis/kmaster8.com/v1beta1/namespaces/test/tektonpipelineruns --insecure
'개발 > python' 카테고리의 다른 글
Python에서 gRPC 구현 (0) | 2023.02.13 |
---|---|
Kopf ( 예제 ) (1) | 2023.02.05 |
Ubuntu에서 pipenv 실행 시 FileNotFoundError 오류 (0) | 2023.01.28 |
Loguru (0) | 2023.01.24 |
Fastapi 모범 사례 (0) | 2023.01.22 |