ื-Go ืืฉ ืืจืืข ืืื ืืคืื ืขื ืฉืคืืช ืืชืื ืืช ืฉืื ืฉืื ืืืืจืื ืืืชืื ืืฆืืจืืช ืขืืืจ Kubernetes. ืืฉ ืืื ืกืืืืช ืืืืืืงืืืืืืช, ืืืื:
- ืืฉ ืืกืืจืช ืืืงื ืืคืืชืื ืืืคืจืืืจืื ื-Go -
SDK ืฉื ืืคืขืื . - ืืืฉืืืื ืืฉื ืื ืืฉืืงืื ืืื Docker ื-Kubernetes ืืชืืืื ื-Go. ืืชืืืช ืืืคืขืื ืฉืื ื-Go ืคืืจืืฉื ืืืืืจ ืืืืชื ืฉืคื ืขื ืืืขืจืืช ืืืงืืืืืืช.
- ืืืฆืืขืื ืืืืืื ืฉื ืืืฉืืื Go ืืืืื ืคืฉืืืื ืืขืืืื ืขื ืื ืืื ืืช ืืืืฅ ืืงืืคืกื.
NB: ืืื, ืืื ืืืชืื ืืฆืืจื ืืฉืื ืืื, ืื ืื ื
ืืื ืื ืื ื ืื ืข ืืื ืืืืื Go ืืืื ืืืกืจ ืืื ืื, ืืืืืื ืคืฉืืืืช, ืืืืืืฆืื? ืืืืืจ ืืกืคืง ืืืืื ืืืฆื ื ืืชื ืืืชืื ืืฆืืจื ืืืื ืืืืฆืขืืช ืืืช ืืฉืคืืช ืืคืืคืืืจืืืช ืืืืชืจ ืฉืืืขื ืื ืืื ืืก DevOps ืืืืจ - ืคืืชืื.
ืืืืจื: ืืืื ืช ืฆืืืื - ืืคืขืื ืขืืชืงืื!
ืืืืืื, ืฉืงืื ืืคืชื ืืฆืืจื ืคืฉืืื ืฉื ืืขืื ืืืขืชืืง ConfigMap ืืืฉืจ ืืืคืืข ืืจืื ืฉืืืช ืืืฉ ืื ืืืฉืจ ืืืช ืืฉืชื ืืฉืืืืช ืืฉืชื ื: ConfigMap ื-Secret. ืื ืงืืืช ืืื ืืขืฉืืช, ืืืคืขืื ืืืื ืืืืืช ืฉืืืืฉื ืืขืืืื ืืืืืช ืืืืื ืฉื ืชืฆืืจืืช ืืคืืืงืฆืืืช (ืขื ืืื ืขืืืื ื-ConfigMap) ืื ืืขืืืื ื ืชืื ืื ืกืืืืื - ืืืฉื ืืคืชืืืช ืืขืืืื ืขื Docker Registry (ืืขืช ืืืกืคืช Secret ืืืจืื ืืฉืืืช).
ืืคืืื, ืื ืฆืจืื ืืืืืช ืืืคืขืื ืืื:
- ืืืื ืืจืืงืฆืื ืขื ืืืคืขืื ืืชืืฆืขืช ืืืืฆืขืืช
ืืืืจืืช ืืฉืืืื ืืืชืืืืช ืืืฉืืช (ืืืื: CRD). - ื ืืชื ืืืืืืจ ืืช ืืืคืขืื. ืืฉื ืื, ื ืฉืชืืฉ ืืืืื ืฉืืจืช ืืคืงืืื ืืืืฉืชื ื ืกืืืื.
- ืืืื ื ืฉื ืชืจืฉืื ื-Docker ื-Helm ืชืืื ื ืื ืฉืืฉืชืืฉืื ืืืืืื ืืงืืืช (ืืืฉ ืืคืงืืื ืืืช) ืืืชืงืื ืืช ืืืคืขืื ืืืฉืืื 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']}
ื ืืืจ: ืืฆืืื ื ืืืฉืื ืืื ืืืคืขืื. ืืืื ืืฉืื, ืขืฉืื ื ืืช ืื ืฉื ืงืจื ืืจื 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 # ะัะพัะผะปะตะฝะธะต ะฟะฐะบะตัะฐ
ืืืงืจ ืืืื
ื-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 ืฉืื...
ืืื ืืชืช ืื ืืื ืืงืจืื ืขื ืืงืื, ืืื ืกื ื ืืืชื
ื .ื. ืืื ืืชื ืขืฆืื ืืืื ืืืชืืืื ืขื ืืืจืืขื Kubernetes ืื ืฉืืชื ืคืฉืื ืจืืื ืืืชืจ ืืืฉืชืืฉ ื-Bash, ืืงืืืืืช ืฉืื ื ืืืื ื ืคืชืจืื ืืืื ืืืืคืก
PPS
ืงืจื ืื ืืืืื ืฉืื ื:
- ยซ
ืืื ืงื ืื ืื ืืืืื ืืฉืืื Kubernetes? ืืืจืื ืขื ืืคืขืื ืชืืกืฃ "; - ยซ
ืืืืจืืช ืขื ืืคืขืื ืืขืืคืช: ืืฆืืจืช ืืืคืจืืืจืื ืขืืืจ Kubernetes ืคืฉืื ื ืขืฉืชื ืงืื ืืืชืจ "; - ยซ
ืืจืืืช ืืืฉืืื ืฉื Kubernetes (ืกืงืืจื ืืืืืช ืืืื ืืืืื) "; - ยซ
ืืชืืืช ืืคืขืื ืขืืืจ Kubernetes ืืืืื ื "; - ยซ
ืืืคืจืืืจืื ืขืืืจ Kubernetes: ืืืฆื ืืืคืขืื ืืืฉืืืื ืกืืืืกืืืื ".
ืืงืืจ: www.habr.com