Go Π²ΠΎ ΠΌΠΎΠΌΠ΅Π½ΡΠΎΠ² ΠΈΠΌΠ° ΠΌΠΎΠ½ΠΎΠΏΠΎΠ» Π½Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΡΠΊΠΈΡΠ΅ ΡΠ°Π·ΠΈΡΠΈ ΡΡΠΎ Π»ΡΡΠ΅ΡΠΎ ΠΈΠ·Π±ΠΈΡΠ°Π°Ρ Π΄Π° ΠΏΠΈΡΡΠ²Π°Π°Ρ ΠΈΠ·ΡΠ°Π²ΠΈ Π·Π° Kubernetes. ΠΠΎΡΡΠΎΡΠ°Ρ ΠΎΠ±ΡΠ΅ΠΊΡΠΈΠ²Π½ΠΈ ΠΏΡΠΈΡΠΈΠ½ΠΈ Π·Π° ΠΎΠ²Π°, ΠΊΠ°ΠΊΠΎ ΡΡΠΎ ΡΠ΅:
- ΠΠΎΡΡΠΎΠΈ ΠΌΠΎΡΠ½Π° ΡΠ°ΠΌΠΊΠ° Π·Π° ΡΠ°Π·Π²ΠΎΡ Π½Π° ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΈ Π²ΠΎ Go -
ΠΠΏΠ΅ΡΠ°ΡΠΎΡ SDK . - ΠΠΏΠ»ΠΈΠΊΠ°ΡΠΈΠΈΡΠ΅ ΡΡΠΎ ΠΌΠ΅Π½ΡΠ²Π°Π°Ρ ΠΈΠ³ΡΠΈ ΠΊΠ°ΠΊΠΎ Docker ΠΈ Kubernetes ΡΠ΅ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈ Π²ΠΎ Go. ΠΠ° Π³ΠΎ Π½Π°ΠΏΠΈΡΠ΅ΡΠ΅ Π²Π°ΡΠΈΠΎΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ Π²ΠΎ Go Π·Π½Π°ΡΠΈ Π΄Π° Π·Π±ΠΎΡΡΠ²Π°ΡΠ΅ ΠΈΡΡ ΡΠ°Π·ΠΈΠΊ ΡΠΎ Π΅ΠΊΠΎΡΠΈΡΡΠ΅ΠΌΠΎΡ.
- ΠΠΈΡΠΎΠΊΠΈ ΠΏΠ΅ΡΡΠΎΡΠΌΠ°Π½ΡΠΈ Π½Π° Go Π°ΠΏΠ»ΠΈΠΊΠ°ΡΠΈΠΈΡΠ΅ ΠΈ Π΅Π΄Π½ΠΎΡΡΠ°Π²Π½ΠΈ Π°Π»Π°ΡΠΊΠΈ Π·Π° ΡΠ°Π±ΠΎΡΠ° ΡΠΎ ΠΈΡΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½ΠΎΡΡ Π½Π°Π΄Π²ΠΎΡ ΠΎΠ΄ ΠΊΡΡΠΈΡΠ°ΡΠ°.
NB: ΠΠ°ΡΠ΅ΠΌ, ΠΊΠ°ΠΊΠΎ Π΄Π° Π½Π°ΠΏΠΈΡΠ΅ΡΠ΅ ΡΠΎΠΏΡΡΠ²Π΅Π½Π° ΠΈΠ·ΡΠ°Π²Π° Π²ΠΎ Go, Π½ΠΈΠ΅
ΠΠΎ, ΡΡΠΎ Π°ΠΊΠΎ ΡΡΠ΅ ΡΠΏΡΠ΅ΡΠ΅Π½ΠΈ Π΄Π° Π½Π°ΡΡΠΈΡΠ΅ Go ΠΏΠΎΡΠ°Π΄ΠΈ Π½Π΅Π΄ΠΎΡΡΠ°ΡΠΎΠΊ Π½Π° Π²ΡΠ΅ΠΌΠ΅ ΠΈΠ»ΠΈ, Π΅Π΄Π½ΠΎΡΡΠ°Π²Π½ΠΎ ΠΊΠ°ΠΆΠ°Π½ΠΎ, ΠΌΠΎΡΠΈΠ²Π°ΡΠΈΡΠ°? Π‘ΡΠ°ΡΠΈΡΠ°ΡΠ° Π΄Π°Π²Π° ΠΏΡΠΈΠΌΠ΅Ρ Π·Π° ΡΠΎΠ° ΠΊΠ°ΠΊΠΎ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π½Π°ΠΏΠΈΡΠ΅ΡΠ΅ Π΄ΠΎΠ±ΡΠ° ΠΈΠ·ΡΠ°Π²Π° ΠΊΠΎΡΠΈΡΡΠ΅ΡΡΠΈ Π΅Π΄Π΅Π½ ΠΎΠ΄ Π½Π°ΡΠΏΠΎΠΏΡΠ»Π°ΡΠ½ΠΈΡΠ΅ ΡΠ°Π·ΠΈΡΠΈ ΡΡΠΎ Π³ΠΎ Π·Π½Π°Π΅ ΡΠΊΠΎΡΠΎ ΡΠ΅ΠΊΠΎΡ ΠΈΠ½ΠΆΠ΅Π½Π΅Ρ Π½Π° DevOps - ΠΠ°ΡΡΠΎΠ½.
ΠΠ°ΠΏΠΎΠ·Π½Π°ΡΡΠ΅: ΠΠΎΠΏΠΈΡ - ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ Π·Π° ΠΊΠΎΠΏΠΈΡΠ°ΡΠ΅!
ΠΠ°ΠΊΠΎ ΠΏΡΠΈΠΌΠ΅Ρ, ΡΠ°Π·ΠΌΠΈΡΠ»Π΅ΡΠ΅ Π·Π° ΡΠ°Π·Π²ΠΎΡ Π½Π° Π΅Π΄Π½ΠΎΡΡΠ°Π²Π½Π° ΠΈΠ·ΡΠ°Π²Π° Π΄ΠΈΠ·Π°ΡΠ½ΠΈΡΠ°Π½Π° Π΄Π° ΠΊΠΎΠΏΠΈΡΠ° ConfigMap ΠΈΠ»ΠΈ ΠΊΠΎΠ³Π° ΡΠ΅ ΡΠ΅ ΠΏΠΎΡΠ°Π²ΠΈ Π½ΠΎΠ² ΠΈΠΌΠ΅Π½ΡΠΊΠΈ ΠΏΡΠΎΡΡΠΎΡ ΠΈΠ»ΠΈ ΠΊΠΎΠ³Π° ΡΠ΅ ΡΠ΅ ΠΏΡΠΎΠΌΠ΅Π½ΠΈ Π΅Π΄Π΅Π½ ΠΎΠ΄ Π΄Π²Π°ΡΠ° Π΅Π½ΡΠΈΡΠ΅ΡΠ°: ConfigMap ΠΈ Secret. ΠΠ΄ ΠΏΡΠ°ΠΊΡΠΈΡΠ½Π° Π³Π»Π΅Π΄Π½Π° ΡΠΎΡΠΊΠ°, ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΡ ΠΌΠΎΠΆΠ΅ Π΄Π° Π±ΠΈΠ΄Π΅ ΠΊΠΎΡΠΈΡΠ΅Π½ Π·Π° ΠΌΠ°ΡΠΎΠ²Π½ΠΎ Π°ΠΆΡΡΠΈΡΠ°ΡΠ΅ Π½Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈΡΠ΅ Π½Π° Π°ΠΏΠ»ΠΈΠΊΠ°ΡΠΈΠΈΡΠ΅ (ΡΠΎ Π°ΠΆΡΡΠΈΡΠ°ΡΠ΅ Π½Π° ConfigMap) ΠΈΠ»ΠΈ Π·Π° Π°ΠΆΡΡΠΈΡΠ°ΡΠ΅ Π½Π° ΡΠ°ΡΠ½ΠΈΡΠ΅ ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ - Π½Π° ΠΏΡΠΈΠΌΠ΅Ρ, ΠΊΠ»ΡΡΠ΅Π²ΠΈ Π·Π° ΡΠ°Π±ΠΎΡΠ° ΡΠΎ Docker Registry (ΠΏΡΠΈ Π΄ΠΎΠ΄Π°Π²Π°ΡΠ΅ Secret Π²ΠΎ ΠΈΠΌΠ΅Π½ΡΠΊΠΈΠΎΡ ΠΏΡΠΎΡΡΠΎΡ).
ΠΠ½Π°ΡΠΈ, ΡΡΠΎ ΡΡΠ΅Π±Π° Π΄Π° ΠΈΠΌΠ° Π΄ΠΎΠ±Π°Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ:
- ΠΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΡΠ°ΡΠ° ΡΠΎ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΡ ΡΠ΅ Π²ΡΡΠΈ ΡΠΎ ΠΊΠΎΡΠΈΡΡΠ΅ΡΠ΅
ΠΡΠΈΠ»Π°Π³ΠΎΠ΄Π΅Π½ΠΈ Π΄Π΅ΡΠΈΠ½ΠΈΡΠΈΠΈ Π·Π° ΡΠ΅ΡΡΡΡΠΈ (Π²ΠΎ Π½Π°ΡΠ°ΠΌΠΎΡΠ½ΠΈΠΎΡ ΡΠ΅ΠΊΡΡ CRD). - ΠΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΡ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°. ΠΠ° Π΄Π° Π³ΠΎ Π½Π°ΠΏΡΠ°Π²ΠΈΡΠ΅ ΠΎΠ²Π°, ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠΈΠΌΠ΅ Π·Π½Π°ΠΌΠ΅Π½ΡΠ° Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π½Π°ΡΠ° Π»ΠΈΠ½ΠΈΡΠ° ΠΈ ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ Π½Π° ΠΎΠΊΠΎΠ»ΠΈΠ½Π°ΡΠ°.
- ΠΠ·Π³ΡΠ°Π΄Π±Π°ΡΠ° Π½Π° Π³ΡΠ°ΡΠΈΠΊΠΎΠ½ΠΎΡ Π·Π° ΠΊΠΎΠ½ΡΠ΅ΡΠ½Π΅Ρ ΠΈ Π₯Π΅Π»ΠΌ ΠΠΎΠΊΠ΅Ρ Π΅ Π΄ΠΈΠ·Π°ΡΠ½ΠΈΡΠ°Π½Π° ΡΠ°ΠΊΠ° ΡΡΠΎ ΠΊΠΎΡΠΈΡΠ½ΠΈΡΠΈΡΠ΅ ΠΌΠΎΠΆΠ°Ρ Π»Π΅ΡΠ½ΠΎ (Π±ΡΠΊΠ²Π°Π»Π½ΠΎ ΡΠΎ Π΅Π΄Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π°) Π΄Π° Π³ΠΎ ΠΈΠ½ΡΡΠ°Π»ΠΈΡΠ°Π°Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΡ Π²ΠΎ Π½ΠΈΠ²Π½ΠΈΠΎΡ ΠΊΠ»Π°ΡΡΠ΅Ρ Kubernetes.
CRD
ΠΠ° Π΄Π° Π·Π½Π°Π΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΡ ΠΊΠΎΠΈ ΡΠ΅ΡΡΡΡΠΈ Π΄Π° Π±Π°ΡΠ° ΠΈ ΠΊΠ°Π΄Π΅ Π΄Π° Π±Π°ΡΠ°, ΡΡΠ΅Π±Π° Π΄Π° ΠΌΡ ΠΏΠΎΡΡΠ°Π²ΠΈΠΌΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ. Π‘Π΅ΠΊΠΎΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ ΡΠ΅ Π±ΠΈΠ΄Π΅ ΠΏΡΠ΅ΡΡΡΠ°Π²Π΅Π½ΠΎ ΠΊΠ°ΠΊΠΎ Π΅Π΄ΠΈΠ½ΡΡΠ²Π΅Π½ ΠΎΠ±ΡΠ΅ΠΊΡ CRD. ΠΠ°ΠΊΠ²ΠΈ ΠΏΠΎΠ»ΠΈΡΠ° ΡΡΠ΅Π±Π° Π΄Π° ΠΈΠΌΠ° ΠΎΠ²ΠΎΡ CRD?
- Π’ΠΈΠΏ Π½Π° ΡΠ΅ΡΡΡΡΠΈ, ΡΡΠΎ ΡΠ΅ Π³ΠΎ Π±Π°ΡΠ°ΠΌΠ΅ (ConfigMap ΠΈΠ»ΠΈ Secret).
- Π‘ΠΏΠΈΡΠΎΠΊ Π½Π° ΠΈΠΌΠ΅Π½ΡΠΊΠΈ ΠΏΡΠΎΡΡΠΎΡΠΈ, Π²ΠΎ ΠΊΠΎΡ ΡΡΠ΅Π±Π° Π΄Π° ΡΠ΅ Π»ΠΎΡΠΈΡΠ°Π°Ρ ΡΠ΅ΡΡΡΡΠΈΡΠ΅.
- ΠΠ·Π±ΠΎΡΠ½ΠΈΠΊ, ΡΠΎ ΡΡΠΎ ΡΠ΅ Π±Π°ΡΠ°ΠΌΠ΅ ΡΠ΅ΡΡΡΡΠΈ Π²ΠΎ ΠΈΠΌΠ΅Π½ΡΠΊΠΈΠΎΡ ΠΏΡΠΎΡΡΠΎΡ.
ΠΡΠ΄Π΅ Π΄Π° Π³ΠΎ ΠΎΠΏΠΈΡΠ΅ΠΌΠ΅ 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
Π Π½ΠΈΠ΅ ΡΠ΅ Π³ΠΎ ΡΠΎΠ·Π΄Π°Π΄Π΅ΠΌΠ΅ Π²Π΅Π΄Π½Π°Ρ Π΅Π΄Π½ΠΎΡΡΠ°Π²Π½ΠΎ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ β Π·Π° ΠΏΡΠ΅Π±Π°ΡΡΠ²Π°ΡΠ΅ Π²ΠΎ ΠΈΠΌΠ΅Π½ΡΠΊΠΈΠΎΡ ΠΏΡΠΎΡΡΠΎΡ ΡΠΎ ΠΈΠΌΠ΅ΡΠΎ default
ΡΠΈΡΠ΅ ConfigMap ΡΠΎ Π΅ΡΠΈΠΊΠ΅ΡΠΈ ΠΊΠ°ΠΊΠΎ copyrator: "true"
:
apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
name: main-rule
labels:
module: copyrator
ruleType: configmap
selector:
copyrator: "true"
namespace: default
ΠΠΎΠ΄Π³ΠΎΡΠ²Π΅Π½ΠΈ! Π‘Π΅Π³Π° ΡΡΠ΅Π±Π° Π½Π΅ΠΊΠ°ΠΊΠΎ Π΄Π° Π΄ΠΎΠ±ΠΈΠ΅ΠΌΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ Π·Π° Π½Π°ΡΠ΅ΡΠΎ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ. ΠΠΎΠ·Π²ΠΎΠ»Π΅ΡΠ΅ ΠΌΠΈ Π²Π΅Π΄Π½Π°Ρ Π΄Π° Π½Π°ΠΏΡΠ°Π²Π°ΠΌ ΡΠ΅Π·Π΅ΡΠ²Π°ΡΠΈΡΠ° Π΄Π΅ΠΊΠ° Π½Π΅ΠΌΠ° ΡΠ°ΠΌΠΈΡΠ΅ Π΄Π° ΠΏΠΈΡΡΠ²Π°ΠΌΠ΅ Π±Π°ΡΠ°ΡΠ° Π΄ΠΎ ΠΊΠ»Π°ΡΡΠ΅ΡΠΎΡ API-ΡΠ΅ΡΠ²Π΅Ρ. ΠΠ° Π΄Π° Π³ΠΎ Π½Π°ΠΏΡΠ°Π²ΠΈΡΠ΅ ΠΎΠ²Π°, ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠΈΠΌΠ΅ Π³ΠΎΡΠΎΠ²Π° Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° 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']}
ΠΠ΄Π»ΠΈΡΠ½ΠΎ: ΡΡΠΏΠ΅Π°Π²ΠΌΠ΅ Π΄Π° Π΄ΠΎΠ±ΠΈΠ΅ΠΌΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΠΎ Π·Π° ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΡ. Π ΡΡΠΎ Π΅ Π½Π°ΡΠ²Π°ΠΆΠ½ΠΎ, Π³ΠΎ Π½Π°ΠΏΡΠ°Π²ΠΈΠ²ΠΌΠ΅ ΠΎΠ½Π° ΡΡΠΎ ΡΠ΅ Π½Π°ΡΠ΅ΠΊΡΠ²Π° ΠΡΠ±Π΅ΡΠ½Π΅ΡΡΠΊΠΈ Π½Π°ΡΠΈΠ½.
ΠΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ Π½Π° ΠΆΠΈΠ²ΠΎΡΠ½Π°ΡΠ° ΡΡΠ΅Π΄ΠΈΠ½Π° ΠΈΠ»ΠΈ Π·Π½Π°ΠΌΠ΅Π½ΡΠ°? ΠΠΈΠ΅ Π·Π΅ΠΌΠ°ΠΌΠ΅ ΡΓ¨!
ΠΡΠ΄Π΅ Π΄Π° ΠΏΡΠ΅ΠΌΠΈΠ½Π΅ΠΌΠ΅ Π½Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡΠ°ΡΠ° Π½Π° Π³Π»Π°Π²Π½ΠΈΠΎΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ. ΠΠΎΡΡΠΎΡΠ°Ρ Π΄Π²Π° ΠΎΡΠ½ΠΎΠ²Π½ΠΈ ΠΏΡΠΈΡΡΠ°ΠΏΠΈ Π·Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°ΡΠ΅ Π½Π° Π°ΠΏΠ»ΠΈΠΊΠ°ΡΠΈΠΈ:
- ΠΊΠΎΡΠΈΡΡΠ΅ΡΠ΅ ΠΎΠΏΡΠΈΠΈ Π·Π° ΠΊΠΎΠΌΠ°Π½Π΄Π½Π°ΡΠ° Π»ΠΈΠ½ΠΈΡΠ°;
- ΠΊΠΎΡΠΈΡΡΠ΅ΡΠ΅ ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ Π½Π° ΠΎΠΊΠΎΠ»ΠΈΠ½Π°ΡΠ°.
ΠΠΏΡΠΈΠΈΡΠ΅ Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π½Π°ΡΠ° Π»ΠΈΠ½ΠΈΡΠ° Π²ΠΈ ΠΎΠ²ΠΎΠ·ΠΌΠΎΠΆΡΠ²Π°Π°Ρ ΠΏΠΎΡΠ»Π΅ΠΊΡΠΈΠ±ΠΈΠ»Π½ΠΎ Π΄Π° Π³ΠΈ ΡΠΈΡΠ°ΡΠ΅ ΠΏΠΎΡΡΠ°Π²ΠΊΠΈΡΠ΅, ΡΠΎ ΠΏΠΎΠ΄Π΄ΡΡΠΊΠ° ΠΈ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΡΠ° Π·Π° ΡΠΈΠΏΠΎΠ²ΠΈ Π½Π° ΠΏΠΎΠ΄Π°ΡΠΎΡΠΈ. Π‘ΡΠ°Π½Π΄Π°ΡΠ΄Π½Π°ΡΠ° Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π½Π° ΠΠ°ΡΡΠΎΠ½ ΠΈΠΌΠ° ΠΌΠΎΠ΄ΡΠ» 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, ΠΌΠΎΠΆΠ΅ΡΠ΅ Π»Π΅ΡΠ½ΠΎ Π΄Π° ΠΏΡΠ΅Π½Π΅ΡΠ΅ΡΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ Π·Π° ΡΡΠ»ΡΠ³Π°ΡΠ° Π·Π° ΠΏΠΎΠ΄Π»ΠΎΠ³Π°ΡΠ° Π²Π½Π°ΡΡΠ΅ Π²ΠΎ ΠΊΠΎΠ½ΡΠ΅ΡΠ½Π΅ΡΠΎΡ. ΠΠ° ΠΏΡΠΈΠΌΠ΅Ρ, ΠΌΠΎΠΆΠ΅ΠΌΠ΅ Π΄Π° Π΄ΠΎΠ±ΠΈΠ΅ΠΌΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ Π·Π° ΠΈΠΌΠ΅Π½ΡΠΊΠΈΠΎΡ ΠΏΡΠΎΡΡΠΎΡ Π²ΠΎ ΠΊΠΎΡ ΡΠ°Π±ΠΎΡΠΈ ΠΏΠΎΠ΄Π»ΠΎΠ³Π°ΡΠ° ΡΠΎ ΡΠ»Π΅Π΄Π½Π°Π²Π° ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΈΡΠ°:
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. ΠΡΠ΄Π΅ Π΄Π° Π³ΠΎ ΡΠΏΡΠΎΠ²Π΅Π΄Π΅ΠΌΠ΅ Π½Π° ΡΠ»Π΅Π΄Π½ΠΈΠΎΠ² Π½Π°ΡΠΈΠ½:
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)
ΠΠ»Π°Π²Π½Π°ΡΠ° Π»ΠΎΠ³ΠΈΠΊΠ° Π΅ ΠΏΠΎΠ΄Π³ΠΎΡΠ²Π΅Π½Π°! Π‘Π΅Π³Π° ΡΡΠ΅Π±Π° Π΄Π° Π³ΠΎ ΡΠΏΠ°ΠΊΡΠ²Π°ΠΌΠ΅ ΡΠ΅ΡΠΎ ΠΎΠ²Π° Π²ΠΎ Π΅Π΄Π΅Π½ ΠΠ°ΡΡΠΎΠ½ ΠΏΠ°ΠΊΠ΅Ρ. ΠΠ° ΠΏΠΎΠ΄Π³ΠΎΡΠ²ΡΠ²Π°ΠΌΠ΅ Π΄Π°ΡΠΎΡΠ΅ΠΊΠ°ΡΠ° 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 ΠΈΠΌΠ° ΡΠ²ΠΎΡΠ° Π²Π΅ΡΠ·ΠΈΡΠ°. ΠΠΎΠ²Π΅ΡΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ Π·Π° ΠΊΠΎΠΌΠΏΠ°ΡΠΈΠ±ΠΈΠ»Π½ΠΎΡΡΠ° ΠΏΠΎΠΌΠ΅ΡΡ Π²Π΅ΡΠ·ΠΈΠΈΡΠ΅ Π½Π° ΠΊΠ»ΠΈΠ΅Π½ΡΠΎΡ ΠΈ Π²Π΅ΡΠ·ΠΈΠΈΡΠ΅ Π½Π° ΠΡΠ±Π΅ΡΠ½Π΅ΡΠ΅Ρ ΠΌΠΎΠΆΠ΅ Π΄Π° Π½Π°ΡΠ΄Π΅ΡΠ΅ Π²ΠΎ
Π‘Π΅Π³Π° Π½Π°ΡΠΈΠΎΡ ΠΏΡΠΎΠ΅ΠΊΡ ΠΈΠ·Π³Π»Π΅Π΄Π° Π²Π°ΠΊΠ°:
copyrator
βββ copyrator
β βββ cli.py # ΠΠΎΠ³ΠΈΠΊΠ° ΡΠ°Π±ΠΎΡΡ Ρ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΎΠΉ
β βββ constant.py # ΠΠΎΠ½ΡΡΠ°Π½ΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΡ ΠΏΡΠΈΠ²ΠΎΠ΄ΠΈΠ»ΠΈ Π²ΡΡΠ΅
β βββ load_crd.py # ΠΠΎΠ³ΠΈΠΊΠ° Π·Π°Π³ΡΡΠ·ΠΊΠΈ CRD
β βββ operator.py # ΠΡΠ½ΠΎΠ²Π½Π°Ρ Π»ΠΎΠ³ΠΈΠΊΠ° ΡΠ°Π±ΠΎΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠ°
βββ setup.py # ΠΡΠΎΡΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅ΡΠ°
ΠΠΎΠΊΠ΅Ρ ΠΈ Π₯Π΅Π»ΠΌ
Dockerfile ΡΠ΅ Π±ΠΈΠ΄Π΅ Π½Π΅Π²Π΅ΡΠΎΡΠ°ΡΠ½ΠΎ Π΅Π΄Π½ΠΎΡΡΠ°Π²Π½Π°: Π·Π΅ΠΌΠ΅ΡΠ΅ ΡΠ° ΠΎΡΠ½ΠΎΠ²Π½Π°ΡΠ° ΡΠ»ΠΈΠΊΠ° ΠΎΠ΄ ΠΏΠΈΡΠΎΠ½-Π°Π»ΠΏΡΠΊΠΈ ΠΈ ΠΈΠ½ΡΡΠ°Π»ΠΈΡΠ°ΡΡΠ΅ Π³ΠΎ Π½Π°ΡΠΈΠΎΡ ΠΏΠ°ΠΊΠ΅Ρ. ΠΠ° ΡΠ° ΠΎΠ΄Π»ΠΎΠΆΠΈΠΌΠ΅ Π½Π΅ΡΠ·ΠΈΠ½Π°ΡΠ° ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΡΠ° Π΄ΠΎ ΠΏΠΎΠ΄ΠΎΠ±ΡΠΈ Π²ΡΠ΅ΠΌΠΈΡΠ°:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]
Π Π°ΡΠΏΠΎΡΠ΅Π΄ΡΠ²Π°ΡΠ΅ΡΠΎ Π·Π° ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΎΡ Π΅ ΠΈΡΡΠΎ ΡΠ°ΠΊΠ° ΠΌΠ½ΠΎΠ³Ρ Π΅Π΄Π½ΠΎΡΡΠ°Π²Π½ΠΎ:
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...
ΠΠ° Π΄Π° Π²ΠΈ Π΄Π°Π΄Π΅ΠΌΠ΅ ΠΏΠΎΠ΄Π΅ΡΠ°Π»Π΅Π½ ΠΏΠΎΠ³Π»Π΅Π΄ Π½Π° ΠΊΠΎΠ΄ΠΎΡ, Π³ΠΎ ΡΡΠ°Π²ΠΈΠ²ΠΌΠ΅
Π.Π‘. Π Π°ΠΊΠΎ ΡΡΠ΅ ΠΏΡΠ΅ΠΌΠ½ΠΎΠ³Ρ ΠΌΡΠ·Π»ΠΈΠ²ΠΈ Π΄Π° ΡΠ΅ Π·Π°Π½ΠΈΠΌΠ°Π²Π°ΡΠ΅ ΡΠΎ Π½Π°ΡΡΠ°Π½ΠΈ Π½Π° ΠΡΠ±Π΅ΡΠ½Π΅Ρ ΠΈΠ»ΠΈ Π΅Π΄Π½ΠΎΡΡΠ°Π²Π½ΠΎ ΡΡΠ΅ ΠΏΠΎΠ²Π΅ΡΠ΅ Π½Π°Π²ΠΈΠΊΠ½Π°ΡΠΈ Π΄Π° ΠΊΠΎΡΠΈΡΡΠΈΡΠ΅ Bash, Π½Π°ΡΠΈΡΠ΅ ΠΊΠΎΠ»Π΅Π³ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡΠ²ΠΈΡΠ° Π³ΠΎΡΠΎΠ²ΠΎ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π²ΠΎ ΡΠΎΡΠΌΠ°
PPS
ΠΡΠΎΡΠΈΡΠ°ΡΡΠ΅ ΠΈ Π½Π° Π½Π°ΡΠΈΠΎΡ Π±Π»ΠΎΠ³:
- Β«
ΠΠ°Π»ΠΈ Π΅ Π»Π΅ΡΠ½ΠΎ ΠΈ ΠΏΠΎΠ³ΠΎΠ΄Π½ΠΎ Π΄Π° ΡΠ΅ ΠΏΠΎΠ΄Π³ΠΎΡΠ²ΠΈ ΠΊΠ»Π°ΡΡΠ΅Ρ ΠΡΠ±Π΅ΡΠ½Π΅ΡΠ΅Ρ? ΠΠ±ΡΠ°Π²ΡΠ²Π°ΠΌΠ΅ Π΄ΠΎΠ΄Π°ΡΠΎΠΊ-ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ "; - Β«
ΠΠΎΠ²Π΅Π΄ΡΠ²Π°ΡΠ΅ Π½Π° ΡΠΊΠΎΠ»ΠΊΠ°-ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ: ΡΠΎΠ·Π΄Π°Π²Π°ΡΠ΅ΡΠΎ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡΠΈ Π·Π° Kubernetes ΡΡΠΎΡΡΠΊΡ ΡΡΠ°Π½Π° ΠΏΠΎΠ»Π΅ΡΠ½ΠΎ "; - Β«
ΠΡΠΎΡΠΈΡΡΠ²Π°ΡΠ΅ ΠΈ Π΄ΠΎΠΏΠΎΠ»Π½ΡΠ²Π°ΡΠ΅ Π½Π° Kubernetes (ΠΏΡΠ΅Π³Π»Π΅Π΄ ΠΈ Π²ΠΈΠ΄Π΅ΠΎ ΠΈΠ·Π²Π΅ΡΡΠ°Ρ) "; - Β«
ΠΠΈΡΡΠ²Π°ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ Π·Π° ΠΡΠ±Π΅ΡΠ½Π΅ΡΠ΅Ρ Π²ΠΎ ΠΠΎΠ»Π°Π½Π³ "; - Β«
ΠΠΏΠ΅ΡΠ°ΡΠΎΡΠΈ Π·Π° Kubernetes: ΠΊΠ°ΠΊΠΎ Π΄Π° ΡΡΠ°ΡΡΡΠ²Π°ΡΠ΅ Π΄ΡΠΆΠ°Π²Π½ΠΈ Π°ΠΏΠ»ΠΈΠΊΠ°ΡΠΈΠΈ ".
ΠΠ·Π²ΠΎΡ: www.habr.com