Go Π½Π° Π΄Π°Π½Π½ΡΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ ΡΠ²Π»ΡΠ΅ΡΡΡ ΠΌΠΎΠ½ΠΎΠΏΠΎΠ»ΠΈΡΡΠΎΠΌ ΡΡΠ΅Π΄ΠΈ ΡΠ·ΡΠΊΠΎΠ² ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΡ, ΠΊΠΎΡΠΎΡΡΠ΅ Π»ΡΠ΄ΠΈ Π²ΡΠ±ΠΈΡΠ°ΡΡ Π΄Π»Ρ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΠ² Π΄Π»Ρ Kubernetes. Π’ΠΎΠΌΡ Π΅ΡΡΡ ΡΠ°ΠΊΠΈΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΠΈΠ²Π½ΡΠ΅ ΠΏΡΠΈΡΠΈΠ½Ρ, ΠΊΠ°ΠΊ:
- Π‘ΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ ΠΌΠΎΡΠ½Π΅ΠΉΡΠΈΠΉ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊ Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΠ² Π½Π° Go β
Operator SDK . - ΠΠ° Go Π½Π°ΠΏΠΈΡΠ°Π½Ρ ΡΠ°ΠΊΠΈΠ΅ Β«ΠΏΠ΅ΡΠ΅Π²Π΅ΡΠ½ΡΠ²ΡΠΈΠ΅ ΠΈΠ³ΡΡΒ» ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ, ΠΊΠ°ΠΊ Docker ΠΈ Kubernetes. ΠΠΈΡΠ°ΡΡ ΡΠ²ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ Π½Π° Go β Π³ΠΎΠ²ΠΎΡΠΈΡΡ Ρ ΡΠΊΠΎΡΠΈΡΡΠ΅ΠΌΠΎΠΉ Π½Π° ΠΎΠ΄Π½ΠΎΠΌ ΡΠ·ΡΠΊΠ΅.
- ΠΡΡΠΎΠΊΠ°Ρ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ Π½Π° Go ΠΈ ΠΏΡΠΎΡΡΡΠ΅ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΡ Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ concurrency Β«ΠΈΠ· ΠΊΠΎΡΠΎΠ±ΠΊΠΈΒ».
NB: ΠΡΡΠ°ΡΠΈ, ΠΊΠ°ΠΊ Π½Π°ΠΏΠΈΡΠ°ΡΡ ΡΠ²ΠΎΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ Π½Π° Go, ΠΌΡ
ΠΠΎ ΡΡΠΎ, Π΅ΡΠ»ΠΈ ΠΈΠ·ΡΡΠ°ΡΡ Go Π²Π°ΠΌ ΠΌΠ΅ΡΠ°Π΅Ρ ΠΎΡΡΡΡΡΡΠ²ΠΈΠ΅ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ ΠΈΠ»ΠΈ, Π±Π°Π½Π°Π»ΡΠ½ΠΎ, ΠΌΠΎΡΠΈΠ²Π°ΡΠΈΠΈ? Π ΡΡΠ°ΡΡΠ΅ ΠΏΡΠΈΠ²Π΅Π΄Π΅Π½ ΠΏΡΠΈΠΌΠ΅Ρ ΡΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΏΠΈΡΠ°ΡΡ Π΄ΠΎΠ±ΡΠΎΡΠ½ΡΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ ΠΎΠ΄ΠΈΠ½ ΠΈΠ· ΡΠ°ΠΌΡΡ
ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΡΡ
ΡΠ·ΡΠΊΠΎΠ², ΠΊΠΎΡΠΎΡΡΠΉ Π·Π½Π°Π΅Ρ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΠ°ΠΆΠ΄ΡΠΉ DevOps-ΠΈΠ½ΠΆΠ΅Π½Π΅Ρ, β Python.
ΠΡΡΡΠ΅ΡΠ°ΠΉΡΠ΅: ΠΠΎΠΏΠΈΡΠ°ΡΠΎΡ β ΠΊΠΎΠΏΠΈΡΠΎΠ²Π°Π»ΡΠ½ΡΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ!
ΠΠ»Ρ ΠΏΡΠΈΠΌΠ΅ΡΠ° ΡΠ°ΡΡΠΌΠΎΡΡΠΈΠΌ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΡ ΠΏΡΠΎΡΡΠΎΠ³ΠΎ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ°, ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½Π½ΠΎΠ³ΠΎ Π΄Π»Ρ ΠΊΠΎΠΏΠΈΡΠΎΠ²Π°Π½ΠΈΡ ConfigMap Π»ΠΈΠ±ΠΎ ΠΏΡΠΈ ΠΏΠΎΡΠ²Π»Π΅Π½ΠΈΠΈ Π½ΠΎΠ²ΠΎΠ³ΠΎ namespace, Π»ΠΈΠ±ΠΎ ΠΏΡΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΈ ΠΎΠ΄Π½ΠΎΠΉ ΠΈΠ· Π΄Π²ΡΡ ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ: ConfigMap ΠΈ Secret. Π‘ ΡΠΎΡΠΊΠΈ Π·ΡΠ΅Π½ΠΈΡ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΠΏΠΎΠ»Π΅Π·Π΅Π½ Π΄Π»Ρ ΠΌΠ°ΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΉ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ (ΠΏΡΡΠ΅ΠΌ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ConfigMap) ΠΈΠ»ΠΈ ΠΆΠ΅ Π΄Π»Ρ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΡΠ΅ΠΊΡΠ΅ΡΠ½ΡΡ Π΄Π°Π½Π½ΡΡ β Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΊΠ»ΡΡΠ΅ΠΉ Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ Docker Registry (ΠΏΡΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ Secret’Π° Π² namespace).
ΠΡΠ°ΠΊ, ΡΡΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±ΡΡΡ Ρ Ρ ΠΎΡΠΎΡΠ΅Π³ΠΎ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ°:
- ΠΠ·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΠΌ ΠΎΡΡΡΠ΅ΡΡΠ²Π»ΡΠ΅ΡΡΡ ΠΏΡΠΈ ΠΏΠΎΠΌΠΎΡΠΈ
Custom Resource Definitions (Π΄Π°Π»Π΅Π΅ β CRD). - ΠΠΏΠ΅ΡΠ°ΡΠΎΡ ΠΌΠΎΠΆΠ΅Ρ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°ΡΡΡΡ. ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΠ»Π°Π³ΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ ΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ.
- Π‘Π±ΠΎΡΠΊΠ° Docker-ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ° ΠΈ Helm-ΡΠ°ΡΡΠ° ΠΏΡΠΎΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΡΡ ΡΠ°ΠΊ, ΡΡΠΎΠ±Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΠΈ ΠΌΠΎΠ³Π»ΠΈ Π»Π΅Π³ΠΊΠΎ (Π±ΡΠΊΠ²Π°Π»ΡΠ½ΠΎ ΠΎΠ΄Π½ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ) ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ Π² ΡΠ²ΠΎΠΉ Kubernetes-ΠΊΠ»Π°ΡΡΠ΅Ρ.
CRD
Π§ΡΠΎΠ±Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ Π·Π½Π°Π», ΠΊΠ°ΠΊΠΈΠ΅ ΡΠ΅ΡΡΡΡΡ ΠΈ Π³Π΄Π΅ Π΅ΠΌΡ ΠΈΡΠΊΠ°ΡΡ, Π½Π°ΠΌ Π½ΡΠΆΠ½ΠΎ Π·Π°Π΄Π°ΡΡ Π΄Π»Ρ Π½Π΅Π³ΠΎ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ. ΠΠ°ΠΆΠ΄ΠΎΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ Π±ΡΠ΄Π΅Ρ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΎ Π² Π²ΠΈΠ΄Π΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ° CRD. ΠΠ°ΠΊΠΈΠ΅ ΠΏΠΎΠ»Ρ Π΄ΠΎΠ»ΠΆΠ½Ρ Π±ΡΡΡ Ρ ΡΡΠΎΠ³ΠΎ CRD?
- Π’ΠΈΠΏ ΡΠ΅ΡΡΡΡΠ°, ΠΊΠΎΡΠΎΡΡΠΉ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΊΠ°ΡΡ (ConfigMap ΠΈΠ»ΠΈ Secret).
- Π‘ΠΏΠΈΡΠΎΠΊ namespace’ΠΎΠ², Π² ΠΊΠΎΡΠΎΡΡΡ Π΄ΠΎΠ»ΠΆΠ½Ρ Π½Π°Ρ ΠΎΠ΄ΠΈΡΡΡΡ ΡΠ΅ΡΡΡΡΡ.
- Selector, ΠΏΠΎ ΠΊΠΎΡΠΎΡΠΎΠΌΡ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΊΠ°ΡΡ ΡΠ΅ΡΡΡΡΡ Π² namespace’Π΅.
ΠΠΏΠΈΡΠ΅ΠΌ CRD:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: copyrator.flant.com
spec:
group: flant.com
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
plural: copyrators
singular: copyrator
kind: CopyratorRule
shortNames:
- copyr
validation:
openAPIV3Schema:
type: object
properties:
ruleType:
type: string
namespaces:
type: array
items:
type: string
selector:
type: string
Π ΡΡΠ°Π·Ρ ΠΆΠ΅ ΡΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΠΏΡΠΎΡΡΠΎΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ β Π½Π° ΠΏΠΎΠΈΡΠΊ Π² namespace’Π΅ Ρ ΠΈΠΌΠ΅Π½Π΅ΠΌ default
Π²ΡΠ΅Ρ
ConfigMap c label’Π°ΠΌΠΈ Π²ΠΈΠ΄Π° copyrator: "true"
:
apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
name: main-rule
labels:
module: copyrator
ruleType: configmap
selector:
copyrator: "true"
namespace: default
ΠΠΎΡΠΎΠ²ΠΎ! Π’Π΅ΠΏΠ΅ΡΡ Π½ΡΠΆΠ½ΠΎ ΠΊΠ°ΠΊ-ΡΠΎ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ Π½Π°ΡΠ΅ΠΌ ΠΏΡΠ°Π²ΠΈΠ»Π΅. Π‘ΡΠ°Π·Ρ ΠΎΠ³ΠΎΠ²ΠΎΡΡΡΡ, ΡΡΠΎ ΡΠ°ΠΌΠΎΡΡΠΎΡΡΠ΅Π»ΡΠ½ΠΎ ΠΏΠΈΡΠ°ΡΡ Π·Π°ΠΏΡΠΎΡΡ ΠΊ API Server ΠΊΠ»Π°ΡΡΠ΅ΡΠ° ΠΌΡ Π½Π΅ Π±ΡΠ΄Π΅ΠΌ. ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ Π²ΠΎΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΡ Π³ΠΎΡΠΎΠ²ΠΎΠΉ Python-Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΎΠΉ
import kubernetes
from contextlib import suppress
CRD_GROUP = 'flant.com'
CRD_VERSION = 'v1'
CRD_PLURAL = 'copyrators'
def load_crd(namespace, name):
client = kubernetes.client.ApiClient()
custom_api = kubernetes.client.CustomObjectsApi(client)
with suppress(kubernetes.client.api_client.ApiException):
crd = custom_api.get_namespaced_custom_object(
CRD_GROUP,
CRD_VERSION,
namespace,
CRD_PLURAL,
name,
)
return {x: crd[x] for x in ('ruleType', 'selector', 'namespace')}
Π ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ΅ ΡΠ°Π±ΠΎΡΡ ΡΡΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° ΠΏΠΎΠ»ΡΡΠΈΠΌ ΡΠ»Π΅Π΄ΡΡΡΠ΅Π΅:
{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}
ΠΡΠ»ΠΈΡΠ½ΠΎ: Π½Π°ΠΌ ΡΠ΄Π°Π»ΠΎΡΡ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ Π΄Π»Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ°. Π ΡΠ°ΠΌΠΎΠ΅ Π³Π»Π°Π²Π½ΠΎΠ΅ β ΠΌΡ ΡΡΠΎ ΡΠ΄Π΅Π»Π°Π»ΠΈ, ΡΡΠΎ Π½Π°Π·ΡΠ²Π°Π΅ΡΡΡ, Kubernetes way.
ΠΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ ΠΈΠ»ΠΈ ΡΠ»Π°Π³ΠΈ? ΠΠ΅ΡΠ΅ΠΌ Π²ΡΡ!
ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ ΠΊ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ°. ΠΡΡΡ Π΄Π²Π° Π±Π°Π·ΠΎΠ²ΡΡ ΠΏΠΎΠ΄Ρ ΠΎΠ΄Π° ΠΊ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ:
- ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ;
- ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ.
ΠΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡΡ ΡΡΠΈΡΡΠ²Π°ΡΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ Π±ΠΎΠ»Π΅Π΅ Π³ΠΈΠ±ΠΊΠΎ, Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΎΠΉ ΠΈ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠ΅ΠΉ ΡΠΈΠΏΠΎΠ² Π΄Π°Π½Π½ΡΡ
. Π ΡΡΠ°Π½Π΄Π°ΡΡΠ½ΠΎΠΉ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅ Python’Π° Π΅ΡΡΡ ΠΌΠΎΠ΄ΡΠ»Ρ argparser
, ΠΊΠΎΡΠΎΡΡΠΌ ΠΌΡ ΠΈ Π²ΠΎΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΡ. ΠΠΎΠ΄ΡΠΎΠ±Π½ΠΎΡΡΠΈ ΠΈ ΠΏΡΠΈΠΌΠ΅ΡΡ Π΅Π³ΠΎ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠ΅ΠΉ Π΄ΠΎΡΡΡΠΏΠ½Ρ Π²
ΠΠΎΡ ΠΊΠ°ΠΊ Π΄Π»Ρ Π½Π°ΡΠ΅Π³ΠΎ ΡΠ»ΡΡΠ°Ρ Π±ΡΠ΄Π΅Ρ Π²ΡΠ³Π»ΡΠ΄Π΅ΡΡ ΠΏΡΠΈΠΌΠ΅Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΡΡΠΈΡΡΠ²Π°Π½ΠΈΡ ΡΠ»Π°Π³ΠΎΠ² ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ:
parser = ArgumentParser(
description='Copyrator - copy operator.',
prog='copyrator'
)
parser.add_argument(
'--namespace',
type=str,
default=getenv('NAMESPACE', 'default'),
help='Operator Namespace'
)
parser.add_argument(
'--rule-name',
type=str,
default=getenv('RULE_NAME', 'main-rule'),
help='CRD Name'
)
args = parser.parse_args()
Π‘ Π΄ΡΡΠ³ΠΎΠΉ ΡΡΠΎΡΠΎΠ½Ρ, ΠΏΡΠΈ ΠΏΠΎΠΌΠΎΡΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ Π² Kubernetes ΠΌΠΎΠΆΠ½ΠΎ Π»Π΅Π³ΠΊΠΎ ΠΏΠ΅ΡΠ΅Π½Π΅ΡΡΠΈ ΡΠ»ΡΠΆΠ΅Π±Π½ΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ pod’Π΅ Π²Π½ΡΡΡΡ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°. ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ namespace, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ Π·Π°ΠΏΡΡΠ΅Π½ pod, ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΈΠ΅ΠΉ:
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ΠΠΎΠ³ΠΈΠΊΠ° ΡΠ°Π±ΠΎΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ°
Π§ΡΠΎΠ±Ρ ΠΏΠΎΠ½ΠΈΠΌΠ°ΡΡ, ΠΊΠ°ΠΊ ΡΠ°Π·Π΄Π΅Π»ΠΈΡΡ ΠΌΠ΅ΡΠΎΠ΄Ρ Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ ConfigMap ΠΈ Secret, Π²ΠΎΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΡ ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΡΠΌΠΈ ΠΊΠ°ΡΡΠ°ΠΌΠΈ. Π’ΠΎΠ³Π΄Π° ΠΌΡ ΡΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ½ΡΡΡ, ΠΊΠ°ΠΊΠΈΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ Π½Π°ΠΌ Π½ΡΠΆΠ½Ρ Π΄Π»Ρ ΡΠ»Π΅ΠΆΠ΅Π½ΠΈΡ ΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΎΠ±ΡΠ΅ΠΊΡΠ°:
LIST_TYPES_MAP = {
'configmap': 'list_namespaced_config_map',
'secret': 'list_namespaced_secret',
}
CREATE_TYPES_MAP = {
'configmap': 'create_namespaced_config_map',
'secret': 'create_namespaced_secret',
}
ΠΠ°Π»Π΅Π΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠΎΠ»ΡΡΠ°ΡΡ ΡΠΎΠ±ΡΡΠΈΡ ΠΎΡ API server. Π Π΅Π°Π»ΠΈΠ·ΡΠ΅ΠΌ ΡΡΠΎ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ:
def handle(specs):
kubernetes.config.load_incluster_config()
v1 = kubernetes.client.CoreV1Api()
# ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ ΠΌΠ΅ΡΠΎΠ΄ Π΄Π»Ρ ΡΠ»Π΅ΠΆΠ΅Π½ΠΈΡ Π·Π° ΠΎΠ±ΡΠ΅ΠΊΡΠ°ΠΌΠΈ
method = getattr(v1, LIST_TYPES_MAP[specs['ruleType']])
func = partial(method, specs['namespace'])
w = kubernetes.watch.Watch()
for event in w.stream(func, _request_timeout=60):
handle_event(v1, specs, event)
ΠΠΎΡΠ»Π΅ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΡΠΎΠ±ΡΡΠΈΡ ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΠΌ ΠΊ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ Π»ΠΎΠ³ΠΈΠΊΠ΅ Π΅Π³ΠΎ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ:
# Π’ΠΈΠΏΡ ΡΠΎΠ±ΡΡΠΈΠΉ, Π½Π° ΠΊΠΎΡΠΎΡΡΠ΅ Π±ΡΠ΄Π΅ΠΌ ΡΠ΅Π°Π³ΠΈΡΠΎΠ²Π°ΡΡ
ALLOWED_EVENT_TYPES = {'ADDED', 'UPDATED'}
def handle_event(v1, specs, event):
if event['type'] not in ALLOWED_EVENT_TYPES:
return
object_ = event['object']
labels = object_['metadata'].get('labels', {})
# ΠΡΠ΅ΠΌ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΡ ΠΏΠΎ selector'Ρ
for key, value in specs['selector'].items():
if labels.get(key) != value:
return
# ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ Π°ΠΊΡΠΈΠ²Π½ΡΠ΅ namespace'Ρ
namespaces = map(
lambda x: x.metadata.name,
filter(
lambda x: x.status.phase == 'Active',
v1.list_namespace().items
)
)
for namespace in namespaces:
# ΠΡΠΈΡΠ°Π΅ΠΌ ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΠ΅, ΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ namespace
object_['metadata'] = {
'labels': object_['metadata']['labels'],
'namespace': namespace,
'name': object_['metadata']['name'],
}
# ΠΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΌΠ΅ΡΠΎΠ΄ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ/ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΎΠ±ΡΠ΅ΠΊΡΠ°
methodcaller(
CREATE_TYPES_MAP[specs['ruleType']],
namespace,
object_
)(v1)
ΠΡΠ½ΠΎΠ²Π½Π°Ρ Π»ΠΎΠ³ΠΈΠΊΠ° Π³ΠΎΡΠΎΠ²Π°! Π’Π΅ΠΏΠ΅ΡΡ Π½ΡΠΆΠ½ΠΎ ΡΠΏΠ°ΠΊΠΎΠ²Π°ΡΡ Π²ΡΡ ΡΡΠΎ Π² ΠΎΠ΄ΠΈΠ½ Python package. ΠΡΠΎΡΠΌΠ»ΡΠ΅ΠΌ ΡΠ°ΠΉΠ» setup.py
, ΠΏΠΈΡΠ΅ΠΌ ΡΡΠ΄Π° ΠΌΠ΅ΡΠ°ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ΅:
from sys import version_info
from setuptools import find_packages, setup
if version_info[:2] < (3, 5):
raise RuntimeError(
'Unsupported python version %s.' % '.'.join(version_info)
)
_NAME = 'copyrator'
setup(
name=_NAME,
version='0.0.1',
packages=find_packages(),
classifiers=[
'Development Status :: 3 - Alpha',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
author='Flant',
author_email='[email protected]',
include_package_data=True,
install_requires=[
'kubernetes==9.0.0',
],
entry_points={
'console_scripts': [
'{0} = {0}.cli:main'.format(_NAME),
]
}
)
NB: ΠΠ»ΠΈΠ΅Π½Ρ kubernetes Π΄Π»Ρ Python ΠΈΠΌΠ΅Π΅Ρ ΡΠ²ΠΎΡ Π²Π΅ΡΡΠΈΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅. ΠΠΎΠ΄ΡΠΎΠ±Π½Π΅Π΅ ΠΎ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΠΈ Π²Π΅ΡΡΠΈΠΉ ΠΊΠ»ΠΈΠ΅Π½ΡΠ° ΠΈ Π²Π΅ΡΡΠΈΠΉ Kubernetes ΠΌΠΎΠΆΠ½ΠΎ ΡΠ·Π½Π°ΡΡ ΠΈΠ·
Π‘Π΅ΠΉΡΠ°Ρ Π½Π°Ρ ΠΏΡΠΎΠ΅ΠΊΡ Π²ΡΠ³Π»ΡΠ΄ΠΈΡ ΡΠ°ΠΊ:
copyrator
βββ copyrator
β βββ cli.py # ΠΠΎΠ³ΠΈΠΊΠ° ΡΠ°Π±ΠΎΡΡ Ρ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΎΠΉ
β βββ constant.py # ΠΠΎΠ½ΡΡΠ°Π½ΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΡ ΠΏΡΠΈΠ²ΠΎΠ΄ΠΈΠ»ΠΈ Π²ΡΡΠ΅
β βββ load_crd.py # ΠΠΎΠ³ΠΈΠΊΠ° Π·Π°Π³ΡΡΠ·ΠΊΠΈ CRD
β βββ operator.py # ΠΡΠ½ΠΎΠ²Π½Π°Ρ Π»ΠΎΠ³ΠΈΠΊΠ° ΡΠ°Π±ΠΎΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ°
βββ setup.py # ΠΡΠΎΡΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅ΡΠ°
Docker ΠΈ Helm
Dockerfile Π±ΡΠ΄Π΅Ρ Π΄ΠΎ Π±Π΅Π·ΠΎΠ±ΡΠ°Π·ΠΈΡ ΠΏΡΠΎΡΡΡΠΌ: Π²ΠΎΠ·ΡΠΌΠ΅ΠΌ Π±Π°Π·ΠΎΠ²ΡΠΉ ΠΎΠ±ΡΠ°Π· python-alpine ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΠΌ Π½Π°Ρ ΠΏΠ°ΠΊΠ΅Ρ. ΠΠ³ΠΎ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΡ ΠΎΡΠ»ΠΎΠΆΠΈΠΌ Π΄ΠΎ Π»ΡΡΡΠΈΡ Π²ΡΠ΅ΠΌΠ΅Π½:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]
Deployment Π΄Π»Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ° ΡΠΎΠΆΠ΅ ΠΎΡΠ΅Π½Ρ ΠΏΡΠΎΡΡ:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
spec:
selector:
matchLabels:
name: {{ .Chart.Name }}
template:
metadata:
labels:
name: {{ .Chart.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: privaterepo.yourcompany.com/copyrator:latest
imagePullPolicy: Always
args: ["--rule-type", "main-rule"]
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
serviceAccountName: {{ .Chart.Name }}-acc
ΠΠ°ΠΊΠΎΠ½Π΅Ρ, Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ ΡΠΎΠ·Π΄Π°ΡΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΡΡ ΡΠΎΠ»Ρ Π΄Π»Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ° Ρ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΡΠΌΠΈ ΠΏΡΠ°Π²Π°ΠΌΠΈ:
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Chart.Name }}-acc
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: {{ .Chart.Name }}
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["secrets", "configmaps"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: {{ .Chart.Name }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ .Chart.Name }}
subjects:
- kind: ServiceAccount
name: {{ .Chart.Name }}
ΠΡΠΎΠ³
ΠΠΎΡ ΡΠ°ΠΊ, Π±Π΅Π· ΡΡΡΠ°Ρ Π°, ΡΠΏΡΠ΅ΠΊΠ° ΠΈ ΠΈΠ·ΡΡΠ΅Π½ΠΈΡ Go, ΠΌΡ ΡΠΌΠΎΠ³Π»ΠΈ ΡΠΎΠ±ΡΠ°ΡΡ ΡΠ²ΠΎΠ΅Π³ΠΎ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΠΎΠ³ΠΎ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ° Π΄Π»Ρ Kubernetes Π½Π° Python. ΠΠΎΠ½Π΅ΡΠ½ΠΎ, Π΅ΠΌΡ Π΅ΡΡ Π΅ΡΡΡ ΠΊΡΠ΄Π° ΡΠ°ΡΡΠΈ: Π² Π±ΡΠ΄ΡΡΠ΅ΠΌ ΠΎΠ½ ΡΠΌΠΎΠΆΠ΅Ρ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΏΡΠ°Π²ΠΈΠ», ΡΠ°Π±ΠΎΡΠ°ΡΡ Π² Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΡΠΎΠΊΠΎΠ², ΡΠ°ΠΌΠΎΡΡΠΎΡΡΠ΅Π»ΡΠ½ΠΎ ΠΌΠΎΠ½ΠΈΡΠΎΡΠΈΡΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΡΠ²ΠΎΠΈΡ CRDβ¦
Π§ΡΠΎΠ±Ρ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡΠ»ΠΎ ΠΏΠΎΠ±Π»ΠΈΠΆΠ΅ ΠΏΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡΡΡΡ Ρ ΠΊΠΎΠ΄ΠΎΠΌ, ΠΌΡ ΡΠ»ΠΎΠΆΠΈΠ»ΠΈ Π΅Π³ΠΎ Π²
P.S. Π Π΅ΡΠ»ΠΈ Π²Π°ΠΌ Π»Π΅Π½Ρ ΡΠ°Π·Π±ΠΈΡΠ°ΡΡΡΡ Ρ ΡΠΎΠ±ΡΡΠΈΡΠΌΠΈ Kubernetes ΠΈΠ»ΠΈ ΠΆΠ΅ Π²Π°ΠΌ ΠΏΠΎΠΏΡΠΎΡΡΡ ΠΏΡΠΈΠ²ΡΡΠ½Π΅Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Bash β Π½Π°ΡΠΈ ΠΊΠΎΠ»Π»Π΅Π³ΠΈ ΠΏΡΠΈΠ³ΠΎΡΠΎΠ²ΠΈΠ»ΠΈ Π³ΠΎΡΠΎΠ²ΠΎΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π² Π²ΠΈΠ΄Π΅
P.P.S.
Π§ΠΈΡΠ°ΠΉΡΠ΅ ΡΠ°ΠΊΠΆΠ΅ Π² Π½Π°ΡΠ΅ΠΌ Π±Π»ΠΎΠ³Π΅:
- Β«
ΠΠΎΡΠΎΠ²ΠΈΡΡ Kubernetes-ΠΊΠ»Π°ΡΡΠ΅Ρ ΠΏΡΠΎΡΡΠΎ ΠΈ ΡΠ΄ΠΎΠ±Π½ΠΎ? ΠΠ½ΠΎΠ½ΡΠΈΡΡΠ΅ΠΌ addon-operator Β»; - Β«
ΠΡΠ΅Π΄ΡΡΠ°Π²Π»ΡΠ΅ΠΌ shell-operator: ΡΠΎΠ·Π΄Π°Π²Π°ΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΡ Π΄Π»Ρ Kubernetes ΡΡΠ°Π»ΠΎ Π΅ΡΡ ΠΏΡΠΎΡΠ΅ Β»; - Β«
Π Π°ΡΡΠΈΡΡΠ΅ΠΌ ΠΈ Π΄ΠΎΠΏΠΎΠ»Π½ΡΠ΅ΠΌ Kubernetes (ΠΎΠ±Π·ΠΎΡ ΠΈ Π²ΠΈΠ΄Π΅ΠΎ Π΄ΠΎΠΊΠ»Π°Π΄Π°) Β»; - Β«
ΠΠΈΡΠ΅ΠΌ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ° Π΄Π»Ρ Kubernetes Π½Π° Golang Β»; - Β«
ΠΠΏΠ΅ΡΠ°ΡΠΎΡΡ Π΄Π»Ρ Kubernetes: ΠΊΠ°ΠΊ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ stateful-ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ Β».
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com