αα
αα
α»αααααα Go ααΆααα·αααα·ααααΆα
ααα»αααΎααΆααΆααααααααααα·ααΈ αααααα»αααααααΎαααΎαααΎααααΈαααααααα
ααααΈαααααααΆααααααααΆαα Kubernetes α ααΆαα ααα»ααααααααααααααΆααααΏαααα ααΌα
ααΆα
- ααΆααααααααααααααΆαα’αΆαα»ααΆααααααΆααα’αα·ααααααααα·ααααα·αααα
αααα»α Go -
ααααα·ααααα·αα SDK . - αααααα·ααΈααααΆααααααΌαα ααααααΌα ααΆ Docker αα·α Kubernetes ααααΌαααΆαααααααα αααα»α Go α ααΆααααααααααα·ααααα·ααααααα’ααααα αααα»α Go ααΆααααααΆαα·ααΆαααΆααΆααΌα ααααΆααΆαα½αααΉαααααααααα’αααΌα
- ααααΎαααΆαααααααααααααα·ααΈ Go αα·αα§αααααααΆαααααααααΆααααααΎααΆαααΆαα½αααΆαααααααααΆαααα αααα’ααα
NBα ααααα·ααΈααα αααααααααααα
ααααΈαααααααΆαααααααΆαααααα½αααααα’ααααα
αααα»α Go ααΎα
ααα»ααααα
α»ααααΆαααΆααΎα’αααααααΌαααΆαααΆααΆααααΈααΆαααα Go ααααααααααααααΆ α¬αα·ααΆααααααΆααααααΆ ααΆαααΎαααΉαα
α·ααα? α’ααααααααααααΌαα§ααΆα ααααα½αα’αααΈαααααααα’αααα’αΆα
αααααααα
ααααΈαααααααΆαααααααα’αααααααΎααΆααΆααααααα·αααααα»ααα½ααααααααΎαααααααααα·ααααα DevOps ααΉα - αααααααΆαα.
αα½αα αααΆαααΈαααα αααα - ααααα·ααααα·ααα αααα!
ααΆα§ααΆα ααα ααΌααα·α αΆαααΆαααααΎαααα ααααΈαααααααΆαααααΆαααααα½α αααααααΌαααΆααα ααΆα‘αΎαααΎααααΈα αααα ConfigMap ααΆαααα αααααα namespace ααααΈααα α‘αΎα α¬αα ααααααααΆαα»αα½ααααα»αα ααααααΈαααααΆααααααΌαα ConfigMap αα·α Secret α ααΆαααααααααΆααααααα ααααα·ααααα·ααα’αΆα ααΆααααααααααααααΆααααΆαααααΎαα αα α»ααααααααΆαααΆαα αααΎαααααΆαααααααα ααΆαααααααααααααα·ααΈ (αααααΆαα’αΆααααα ConfigMap) α¬αααααΆααααΆαα’αΆααααααα·αααααααααααΆαα - α§ααΆα ααα ααααΆααα α»α αααααΆααααααΎααΆαααΆαα½α Docker Registry (αα αααααααααααΆααααααΆαααα αααα»α namespace)α
α αΎαααΌα αααα, α’αααΈαααααααα·ααααα·ααααα’αα½αααααΆα:
- α’ααααααααααΆαα½αααααα·ααααα·ααααααΌαααΆαα’αα»αααααααααααΎ
αα·αααααααααΆαααααΆαααααα½α (ααα αααα α ααΆ CRD)α - ααααα·ααααα·ααα’αΆα ααααΌαααΆαααααααα ααΆααααααααα ααΎααααΈααααΎααΌα αααα ααΎαααΉαααααΎααααααααΆααααΆααααααααΆ αα·αα’αααααα·ααααΆαα
- ααΆααααααΎααα»α Docker αα·αααααΌαααΆα Helm ααααΌαααΆααα ααΆα‘αΎαααΎααααΈα±ααα’αααααααΎααααΆααα’αΆα ααΆααααα½α (ααΆααααααααααααααααΎααΆααααααααΆαα½α) ααα‘αΎαααααα·ααααα·αααα αααα»αα ααααα Kubernetes αααααα½αααα
αααΈααΈααΈ
ααΎααααΈα±ααααααα·ααααα·ααααΉαααΈααααΆαα’αααΈαααααααΌαααααΎα αα·ααααααααααααααΌαααααΎα ααΎαααααΌααααααα αααΆαααααααΆααααΆααα α αααΆααααΈαα½ααααΉαααααΌαααΆαααααΆαααΆααααα» CRD αααα½αα ααΎ CRD ααααα½αααΆααα·αααα’αααΈαααα?
- ααααααααααΆααααααΎαααΉαααααααα (ConfigMap α¬ Secret)α
- αααααΈαααααα ααααααααααααΆααα½ααααα·ααα α
- αααααα·ααΈααααΎααααααΎαααΉααααααααααααΆααα αααα»α 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
α αΎαααΎαααΉααααααΎαααΆααααΆαα α
αααΆααααΆαααα - ααΎααααΈααααααααααα»αα
ααααααααααααΆαα½αααααα default
ConfigMap ααΆααα’αααααααΆαααααΆαααΌα
ααΆ copyrator: "true"
:
apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
name: main-rule
labels:
module: copyrator
ruleType: configmap
selector:
copyrator: "true"
namespace: default
αα½α
ααΆααα αΎα! α₯α‘αΌαααα ααΎαααααΌαααα½αααΆαααααααΆαα’αααΈα
αααΆααααααααΎαα α’αα»ααααΆαα±αααααα»αααααΎααΆαααααα»αααααΆααααΆααΎαααΉααα·ααααααααααΎαα
ααΆαα Cluster 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α
α’αααααα·ααααΆα α¬αααααΆαα·? ααΎαααααΆααα’αα!
α αΌααααααα ααΆαααααααα ααΆααααααααααααα·ααααα·ααααααΆααα ααΆααα·ααΈααΆαααααααΆααΌαααααΆαα ααα½αααΈααααα»αααΆαααααααα ααΆαααααααααααααα·ααΈα
- ααααΎαααααΎααααααΆααααΆααααααααΆ;
- ααααΎα’αααααα·ααααΆαα
αααααΎααααααΆααααΆααααααααΆα’αα»ααααΆαα±ααα’αααα’αΆαααΆααααααααΆαααααααααα αααααΆαααΆαααΆαααααααααααα·αααααα αα·ααα»ααααΆαα αααααΆααααααααααΆααααα 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 α’αααα’αΆα αααααααααααΆαααααΆααααα’αααΈαααα αααα»ααα»αααΆααααΆαααΆααααα½αα α§ααΆα ααα ααΎαβα’αΆα βααα½αβααΆαβααααααΆαβα’αααΈβααα αβαααααβαααβααβαααα»αβααααΎαααΆαβααΆαα½αβααΉαβααΆαβααααΆαααΆβααΌα βααΆαβαααααα
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)
αααααα·ααααΆααααΆααααΊαα½α
ααΆααα αΎα! α₯α‘αΌααααααΎαααααΌαααα
ααααΆααα’ααααααα
αααα»ααααα
αα Python αα½αα ααΎααααα
αα―αααΆα 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"]
ααΆαααΆαααααααΆααααααΆααααααα·ααααα·ααααααΆααααααΆαααααα
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 α ααΆααΆααα·αααΆαα ααΆαα ααααΆαααααααααΎααααΈααΈαα ααααΎαα αα αααα’ααΆαα ααΆααΉαα’αΆα ααααΎαααΆαα αααΆααααΆα αααΎα ααααΎααΆααααα»ααααααααα‘αΆαα αααΎα αααα―αααΆααα αααα½ααα·αα·αααααΆαααααΆααααααΌααα αααα»α CRDs ααααααΆ...
ααΎααααΈβα±ααβα’αααβααΎαβααΆααααβα
αααΆααβα’αααΈβααΌαβααα ααΎαβααΆαβααΆααβααΆβα
αΌα
PS α αΎαααααα·αααΎα’ααααααα·αααααααα»αααΆααααααααΆαααΆαα½αααααΉαααα·ααΆααα Kubernetes α¬α’αααααΆαααααααΆαααΉαααΆαααααΎααααΆαα Bash αα ααΆααΈααααααΎαααΆααααα
ααααααααααΆααααααααααα½α
ααΆααααα
αααα»ααααααα
PPS
ααΌαα’αΆαααααααα ααΎαααααααααααΎαα
- Β«
ααΎααΆααΆααααα½α αα·αααΆααααα½ααααα»αααΆααααα αα ααααα Kubernetes αα? αααααΆαβααααα·ααααα·αα addon Β» - Β«
ααΆαααααΆαααα-ααααα·ααααα·ααα ααΆααααααΎαααααα·ααααα·αααααααΆαα Kubernetes ααΆααααααΆααααα½αααΆααα»α Β» - Β«
ααΆααααααΈα αα·ααααααα Kubernetes (αααΆαααΆαααααΌαα αα·αααΈααα’αΌ) Β» - Β«
ααΆααααααααααα·ααααα·αααααααΆαα Kubernetes αα αααα»α Golang Β» - Β«
ααααα·ααααα·αααααααΆαα Kubernetesα ααααααααΎαααΆααααααα·ααΈαααααΆαααααΆαααΆα "α
ααααα: www.habr.com