เดšเดŸเตเดŸเด•เตเด•เต‚เดŸเตเด•เดณเตเด‚ SDKเดฏเตเด‚ เด‡เดฒเตเดฒเดพเดคเต† เดชเตˆเดคเตเดคเดฃเดฟเดฒเต† Kubernetes เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผ

เดšเดŸเตเดŸเด•เตเด•เต‚เดŸเตเด•เดณเตเด‚ SDKเดฏเตเด‚ เด‡เดฒเตเดฒเดพเดคเต† เดชเตˆเดคเตเดคเดฃเดฟเดฒเต† Kubernetes เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผ

เด†เดณเตเด•เตพ เด•เตเดฌเตผเดจเต†เดฑเตเดฑเดธเดฟเดจเดพเดฏเดฟ เดชเตเดฐเดธเตเดคเดพเดตเดจเด•เตพ เดŽเดดเตเดคเดพเตป เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเดจเตเดจ เดชเตเดฐเต‹เด—เตเดฐเดพเดฎเดฟเด‚เด—เต เดญเดพเดทเด•เดณเดฟเตฝ Go- เดฏเตเด•เตเด•เต เดจเดฟเดฒเดตเดฟเตฝ เด•เตเดคเตเดคเด•เดฏเตเดฃเตเดŸเต. เด‡เดคเดฟเดจเต เดตเดธเตเดคเตเดจเดฟเดทเตเด เดฎเดพเดฏ เด•เดพเดฐเดฃเด™เตเด™เดณเตเดฃเตเดŸเต, เด‰เดฆเดพเดนเดฐเดฃเดคเตเดคเดฟเดจเต:

  1. Go-เดฏเดฟเตฝ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผเดฎเดพเดฐเต† เดตเดฟเด•เดธเดฟเดชเตเดชเดฟเด•เตเด•เตเดจเตเดจเดคเดฟเดจเต เดถเด•เตเดคเดฎเดพเดฏ เด’เดฐเต เดšเดŸเตเดŸเด•เตเด•เต‚เดŸเต เด‰เดฃเตเดŸเต - เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผ เดŽเดธเต.เดกเดฟ.เด•เต†.
  2. Docker, Kubernetes เดชเต‹เดฒเตเดณเตเดณ เด—เต†เดฏเดฟเด‚ เดฎเดพเดฑเตเดฑเตเดจเตเดจ เด†เดชเตเดฒเดฟเด•เตเด•เต‡เดทเดจเตเด•เตพ Go-เดฏเดฟเตฝ เดŽเดดเตเดคเดฟเดฏเดฟเดฐเดฟเด•เตเด•เตเดจเตเดจเต. Go-เดฏเดฟเตฝ เดจเดฟเด™เตเด™เดณเตเดŸเต† เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเดฑเต† เดŽเดดเตเดคเตเด• เดŽเดจเตเดจเดคเดฟเดจเตผเดคเตเดฅเด‚ เด†เดตเดพเดธเดตเตเดฏเดตเดธเตเดฅเดฏเตเดฎเดพเดฏเดฟ เด’เดฐเต‡ เดญเดพเดท เดธเด‚เดธเดพเดฐเดฟเด•เตเด•เตเด• เดŽเดจเตเดจเดพเดฃเต.
  3. Go เด†เดชเตเดฒเดฟเด•เตเด•เต‡เดทเดจเตเด•เดณเตเดŸเต† เด‰เดฏเตผเดจเตเดจ เดชเตเดฐเด•เดŸเดจเดตเตเด‚ เดฌเต‹เด•เตโ€Œเดธเดฟเดจเต เดชเตเดฑเดคเตเดคเต เดธเดฎเดตเดพเดฏเดคเตเดคเดฟเตฝ เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเดฟเดจเตเดณเตเดณ เดฒเดณเดฟเดคเดฎเดพเดฏ เด‰เดชเด•เดฐเดฃเด™เตเด™เดณเตเด‚.

NB: เดตเดดเดฟเดฏเดฟเตฝ, Go- เตฝ เดจเดฟเด™เตเด™เดณเตเดŸเต† เดธเตเดตเดจเตเดคเด‚ เดชเตเดฐเดธเตเดคเดพเดตเดจ เดŽเด™เตเด™เดจเต† เดŽเดดเตเดคเดพเด‚, เดžเด™เตเด™เตพ เด‡เดคเดฟเดจเด•เด‚ เดตเดฟเดตเดฐเดฟเดšเตเดšเดฟเดฐเดฟเด•เตเด•เตเดจเตเดจเต เดตเดฟเดฆเต‡เดถ เดŽเดดเตเดคเตเดคเตเด•เดพเดฐเตเดŸเต† เดžเด™เตเด™เดณเตเดŸเต† เดตเดฟเดตเตผเดคเตเดคเดจเด™เตเด™เดณเดฟเดฒเตŠเดจเตเดจเดฟเตฝ.

เดŽเดจเตเดจเดพเตฝ เดธเดฎเดฏเด•เตเด•เตเดฑเดตเต‹ เดฒเดณเดฟเดคเดฎเดพเดฏเดฟ เดชเดฑเดžเตเดžเดพเตฝ เดชเตเดฐเต‡เดฐเดฃเดฏเต‹ เด•เดพเดฐเดฃเด‚ Go เดชเด เดฟเด•เตเด•เตเดจเตเดจเดคเดฟเตฝ เดจเดฟเดจเตเดจเต เดจเดฟเด™เตเด™เดณเต† เดคเดŸเดฏเตเดจเตเดจเต†เด™เตเด•เดฟเดฒเต‹? เดฎเดฟเด•เตเด•เดตเดพเดฑเตเด‚ เดŽเดฒเตเดฒเดพ DevOps เดŽเดžเตเดšเดฟเดจเต€เดฏเตผเด•เตเด•เตเด‚ เด…เดฑเดฟเดฏเดพเดตเตเดจเตเดจ เดเดฑเตเดฑเดตเตเด‚ เดœเดจเดชเตเดฐเดฟเดฏเดฎเดพเดฏ เดญเดพเดทเด•เดณเดฟเดฒเตŠเดจเตเดจเต เด‰เดชเดฏเต‹เด—เดฟเดšเตเดšเต เดจเดฟเด™เตเด™เตพเด•เตเด•เต เดŽเด™เตเด™เดจเต† เด’เดฐเต เดจเดฒเตเดฒ เดชเตเดฐเดธเตเดคเดพเดตเดจ เดŽเดดเตเดคเดพเด‚ เดŽเดจเตเดจเดคเดฟเดจเตเดฑเต† เด’เดฐเต เด‰เดฆเดพเดนเดฐเดฃเด‚ เดฒเต‡เด–เดจเด‚ เดจเตฝเด•เตเดจเตเดจเต - เดชเตˆเดคเตเดคเตบ.

เดฎเต€เดฑเตเดฑเต: เด•เต‹เดชเตเดชเดฟเดฏเตผ - เด•เต‹เดชเตเดชเดฟ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผ!

เด’เดฐเต เด‰เดฆเดพเดนเดฐเดฃเดฎเดพเดฏเดฟ, เด’เดฐเต เดชเตเดคเดฟเดฏ เดจเต†เดฏเดฟเด‚เดธเตโ€Œเดชเต†เดฏเตโ€Œเดธเต เดฆเตƒเดถเตเดฏเดฎเดพเด•เตเดฎเตเดชเต‹เดดเต‹ เด…เดฒเตเดฒเต†เด™เตเด•เดฟเตฝ เดฐเดฃเตเดŸเต เดŽเดจเตเดฑเดฟเดฑเตเดฑเดฟเด•เดณเดฟเตฝ เด’เดจเตเดจเต เดฎเดพเดฑเตเดฎเตเดชเต‹เดดเต‹ เด•เต‹เตบเดซเดฟเด—เตเดฎเดพเดชเตเดชเต เดชเด•เตผเดคเตเดคเดพเตป เดฐเต‚เดชเด•เตฝเดชเตเดชเดจ เดšเต†เดฏเตโ€Œเดค เดฒเดณเดฟเดคเดฎเดพเดฏ เด’เดฐเต เดชเตเดฐเดธเตเดคเดพเดตเดจ เดตเดฟเด•เดธเดฟเดชเตเดชเดฟเด•เตเด•เตเดจเตเดจเดคเต เดชเดฐเดฟเด—เดฃเดฟเด•เตเด•เตเด•: เด•เต‹เตบเดซเดฟเด—เตเดฎเดพเดชเตเดชเตเด‚ เดฐเดนเดธเตเดฏเดตเตเด‚. เด’เดฐเต เดชเตเดฐเดพเดฏเต‹เด—เดฟเด• เด•เดพเดดเตเดšเดชเตเดชเดพเดŸเดฟเตฝ, เด†เดชเตเดฒเดฟเด•เตเด•เต‡เดทเตป เด•เต‹เตบเดซเดฟเด—เดฑเต‡เดทเดจเตเด•เดณเตเดŸเต† เดฌเตพเด•เตเด•เต เด…เดชเตเดกเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเดฟเดจเต‹ (เด•เต‹เตบเดซเดฟเด—เตเดฎเดพเดชเตเดชเต เด…เดชเตเดกเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเดฟเดฒเต‚เดŸเต†) เด…เดฒเตเดฒเต†เด™เตเด•เดฟเตฝ เดฐเดนเดธเตเดฏ เดกเดพเดฑเตเดฑ เด…เดชเตเดกเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเดฟเดจเต‹ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผเด•เตเด•เต เด‰เดชเดฏเต‹เด—เดชเตเดฐเดฆเดฎเดพเด•เตเด‚ - เด‰เดฆเดพเดนเดฐเดฃเดคเตเดคเดฟเดจเต, เดกเต‹เด•เตเด•เตผ เดฐเดœเดฟเดธเตเดŸเตเดฐเดฟเดฏเดฟเตฝ เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเดฟเดจเตเดณเตเดณ เด•เต€เด•เตพ (เดจเต†เดฏเดฟเด‚เดธเตเดชเต‡เดธเดฟเตฝ เดฐเดนเดธเตเดฏเด‚ เดšเต‡เตผเด•เตเด•เตเดฎเตเดชเต‹เตพ).

เด…เด™เตเด™เดจเต†เดฏเดพเดฃเต†เด™เตเด•เดฟเตฝ, เด’เดฐเต เดจเดฒเตเดฒ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผเด•เตเด•เต เดŽเดจเตเดคเต เดตเต‡เดฃเด‚:

  1. เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเดฑเตเดฎเดพเดฏเตเดณเตเดณ เด‡เดŸเดชเต†เดŸเตฝ เด‰เดชเดฏเต‹เด—เดฟเดšเตเดšเดพเดฃเต เดจเดŸเดคเตเดคเตเดจเตเดจเดคเต เด•เดธเตเดฑเตเดฑเด‚ เดฑเดฟเดธเต‹เดดเตเดธเต เดจเดฟเตผเดตเดšเดจเด™เตเด™เตพ (เด‡เดจเดฟ CRD เดŽเดจเตเดจเดฑเดฟเดฏเดชเตเดชเต†เดŸเตเดจเตเดจเต).
  2. เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเดฑเต† เด•เตเดฐเดฎเต€เด•เดฐเดฟเด•เตเด•เดพเตป เด•เดดเดฟเดฏเตเด‚. เด‡เดคเต เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเดฟเดจเต, เดžเด™เตเด™เตพ เด•เดฎเดพเตปเดกเต เดฒเตˆเตป เดซเตเดฒเดพเด—เตเด•เดณเตเด‚ เดชเดฐเดฟเดธเตเดฅเดฟเดคเดฟ เดตเต‡เดฐเดฟเดฏเดฌเดฟเดณเตเด•เดณเตเด‚ เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเด‚.
  3. เดกเต‹เด•เตเด•เตผ เด•เดฃเตเดŸเต†เดฏเตโ€Œเดจเดฑเดฟเดจเตเดฑเต†เดฏเตเด‚ เดนเต†เตฝเด‚ เดšเดพเตผเดŸเตเดŸเดฟเดจเตเดฑเต†เดฏเตเด‚ เดฌเดฟเตฝเดกเต เดฐเต‚เดชเด•เตฝเดชเตเดชเดจ เดšเต†เดฏเตโ€Œเดคเดฟเดฐเดฟเด•เตเด•เตเดจเตเดจเดคเดฟเดจเดพเตฝ เด‰เดชเดฏเต‹เด•เตเดคเดพเด•เตเด•เตพเด•เตเด•เต เด…เดตเดฐเตเดŸเต† เด•เตเดฌเตผเดจเต†เดฑเตเดฑเดธเต เด•เตเดฒเดธเตเดฑเตเดฑเดฑเดฟเดฒเต‡เด•เตเด•เต เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเดฑเต† เดŽเดณเตเดชเตเดชเดคเตเดคเดฟเตฝ (เด…เด•เตเดทเดฐเดพเตผเดคเตเดฅเดคเตเดคเดฟเตฝ เด’เดฐเต เด•เดฎเดพเตปเดกเต เด‰เดชเดฏเต‹เด—เดฟเดšเตเดšเต) เด‡เตปเดธเตเดฑเตเดฑเดพเตพ เดšเต†เดฏเตเดฏเดพเตป เด•เดดเดฟเดฏเตเด‚.

เดธเดฟ.เด†เตผ.เดกเดฟ

เดŽเดจเตเดคเต เด‰เดฑเดตเดฟเดŸเด™เตเด™เดณเดพเดฃเต เดคเดฟเดฐเดฏเต‡เดฃเตเดŸเดคเต†เดจเตเดจเตเด‚ เดŽเดตเดฟเดŸเต†เดฏเดพเดฃเต เดจเต‹เด•เตเด•เต‡เดฃเตเดŸเดคเต†เดจเตเดจเตเด‚ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผเด•เตเด•เต เด…เดฑเดฟเดฏเดพเตป, เดžเด™เตเด™เตพ เด…เดตเดจเตเดตเต‡เดฃเตเดŸเดฟ เด’เดฐเต เดจเดฟเดฏเดฎเด‚ เดธเดœเตเดœเต€เด•เดฐเดฟเด•เตเด•เต‡เดฃเตเดŸเดคเตเดฃเตเดŸเต. เด“เดฐเต‹ เดจเดฟเดฏเดฎเดตเตเด‚ เด’เดฐเตŠเดฑเตเดฑ CRD เด’เดฌเตเดœเด•เตเดฑเตเดฑเดพเดฏเดฟ เดชเตเดฐเดคเดฟเดจเดฟเดงเต€เด•เดฐเดฟเด•เตเด•เตเด‚. เดˆ เดธเดฟเด†เตผเดกเดฟเด•เตเด•เต เดŽเดจเตเดคเต เดซเต€เตฝเดกเตเด•เตพ เด‰เดฃเตเดŸเดพเดฏเดฟเดฐเดฟเด•เตเด•เดฃเด‚?

  1. เดฑเดฟเดธเต‹เดดเตเดธเต เดคเดฐเด‚, เดžเด™เตเด™เตพ เดคเดฟเดฐเดฏเตเดจเตเดจ (ConfigMap เด…เดฒเตเดฒเต†เด™เตเด•เดฟเตฝ เดฐเดนเดธเตเดฏเด‚).
  2. เดจเต†เดฏเดฟเด‚เดธเตเดชเต‡เดธเตเด•เดณเตเดŸเต† เดชเดŸเตเดŸเดฟเด•, เด…เดคเดฟเตฝ เดตเดฟเดญเดตเด™เตเด™เตพ เดธเตเดฅเดฟเดคเดฟเดšเต†เดฏเตเดฏเดฃเด‚.
  3. เดธเต†เดฒเด•เตเดŸเตผ, เด…เดคเดฟเดฒเต‚เดŸเต† เดจเดฎเตเดฎเตพ เดจเต†เดฏเดฟเด‚เดธเตเดชเต‡เดธเดฟเตฝ เด‰เดฑเดตเดฟเดŸเด™เตเด™เตพเด•เตเด•เดพเดฏเดฟ เดคเดฟเดฐเดฏเตเด‚.

เดจเดฎเตเด•เตเด•เต 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 เดชเต‹เดฒเตเดณเตเดณ เดฒเต‡เดฌเดฒเตเด•เตพ เด‰เดณเตเดณ เดŽเดฒเตเดฒเดพ เด•เต‹เตบเดซเดฟเด—เตเดฎเดพเดชเตเดชเตเด‚ copyrator: "true":

apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
  name: main-rule
  labels:
    module: copyrator
ruleType: configmap
selector:
  copyrator: "true"
namespace: default

เดคเดฏเตเดฏเดพเดฑเดพเดฃเต! เด‡เดจเดฟ เดŽเด™เตเด™เดจเต†เดฏเต†เด™เตเด•เดฟเดฒเตเด‚ เดจเดฎเตเดฎเตเดŸเต† เดญเดฐเดฃเดคเตเดคเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเตเดณเตเดณ เดตเดฟเดตเดฐเด™เตเด™เตพ เดฒเดญเดฟเด•เตเด•เดฃเด‚. เด•เตเดฒเดธเตเดฑเตเดฑเตผ API เดธเต†เตผเดตเดฑเดฟเดฒเต‡เด•เตเด•เต เดžเด™เตเด™เตพ เดธเตเดตเดฏเด‚ เด…เดญเตเดฏเตผเดคเตเดฅเดจเด•เตพ เดŽเดดเตเดคเตเด•เดฏเดฟเดฒเตเดฒเต†เดจเตเดจเต เดžเดพเตป เด‰เดŸเตป เดคเดจเตเดจเต† เด’เดฐเต เดฑเดฟเดธเตผเดตเต‡เดทเตป เดจเดŸเดคเตเดคเดŸเตเดŸเต†. เด‡เดคเต เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเดฟเดจเต, เดžเด™เตเด™เตพ เด’เดฐเต เดฑเต†เดกเดฟเดฎเต†เดฏเตเดกเต เดชเตˆเดคเตเดคเตบ เดฒเตˆเดฌเตเดฐเดฑเดฟ เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเด‚ kubernetes-client:

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']}

เดฎเดฟเด•เดšเตเดšเดคเต: เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผเด•เตเด•เดพเดฏเดฟ เด’เดฐเต เดจเดฟเดฏเดฎเด‚ เดจเต‡เดŸเดพเตป เดžเด™เตเด™เตพเด•เตเด•เต เด•เดดเดฟเดžเตเดžเต. เดเดฑเตเดฑเดตเตเด‚ เดชเตเดฐเดงเดพเดจเดฎเดพเดฏเดฟ, เดžเด™เตเด™เตพ เด•เตเดฌเตผเดจเต†เดฑเตเดฑเดธเต เดตเดดเดฟ เดŽเดจเตเดจเต เดตเดฟเดณเดฟเด•เตเด•เตเดจเตเดจเดคเต เดšเต†เดฏเตเดคเต.

เดชเดฐเดฟเดธเตเดฅเดฟเดคเดฟ เดตเต‡เดฐเดฟเดฏเดฌเดฟเดณเตเด•เตพ เด…เดฒเตเดฒเต†เด™เตเด•เดฟเตฝ เดชเดคเดพเด•เด•เตพ? เดžเด™เตเด™เตพ เดŽเดฒเตเดฒเดพเด‚ เดŽเดŸเตเด•เตเด•เตเดจเตเดจเต!

เดจเดฎเตเด•เตเด•เต เดชเตเดฐเดงเดพเดจ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผ เด•เต‹เตบเดซเดฟเด—เดฑเต‡เดทเดจเดฟเดฒเต‡เด•เตเด•เต เดชเต‹เด•เดพเด‚. เด†เดชเตเดฒเดฟเด•เตเด•เต‡เดทเดจเตเด•เตพ เด•เตเดฐเดฎเต€เด•เดฐเดฟเด•เตเด•เตเดจเตเดจเดคเดฟเดจเต เดฐเดฃเตเดŸเต เด…เดŸเดฟเดธเตเดฅเดพเดจ เดธเดฎเต€เดชเดจเด™เตเด™เดณเตเดฃเตเดŸเต:

  1. เด•เดฎเดพเตปเดกเต เดฒเตˆเตป เด“เดชเตเดทเดจเตเด•เตพ เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเด•;
  2. เดชเดฐเดฟเดธเตเดฅเดฟเดคเดฟ เดตเต‡เดฐเดฟเดฏเดฌเดฟเดณเตเด•เตพ เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเด•.

เด•เดฎเดพเตปเดกเต เดฒเตˆเตป เด“เดชเตโ€Œเดทเดจเตเด•เตพ, เดกเดพเดฑเตเดฑเดพ เดคเดฐเด‚ เดชเดฟเดจเตเดคเตเดฃเดฏเตเด‚ เดฎเต‚เดฒเตเดฏเดจเดฟเตผเดฃเตเดฃเดฏเดตเตเด‚ เด‰เดชเดฏเต‹เด—เดฟเดšเตเดšเต เด•เตเดฐเดฎเต€เด•เดฐเดฃเด™เตเด™เตพ เด•เต‚เดŸเตเดคเตฝ เดตเดดเด•เตเด•เดคเตเดคเต‹เดŸเต† เดตเดพเดฏเดฟเด•เตเด•เดพเตป เดจเดฟเด™เตเด™เดณเต† เด…เดจเตเดตเดฆเดฟเด•เตเด•เตเดจเตเดจเต. เดชเตˆเดคเตเดคเดฃเดฟเดจเตเดฑเต† เดธเตเดฑเตเดฑเดพเตปเดกเต‡เตผเดกเต เดฒเตˆเดฌเตเดฐเดฑเดฟเดฏเดฟเตฝ เด’เดฐเต เดฎเตŠเดกเตเดฏเต‚เตพ เด‰เดฃเตเดŸเต 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()

เดฎเดฑเตเดตเดถเดคเตเดคเต, เด•เตเดฌเตผเดจเต†เดฑเตเดฑเดธเดฟเดฒเต† เดŽเตปเดตเดฏเต‹เตบเดฎเต†เดจเตเดฑเต เดตเต‡เดฐเดฟเดฏเดฌเดฟเดณเตเด•เตพ เด‰เดชเดฏเต‹เด—เดฟเดšเตเดšเต, เด•เดฃเตเดŸเต†เดฏเตโ€Œเดจเดฑเดฟเดจเตเดณเตเดณเดฟเดฒเต† เดชเต‹เดกเดฟเดจเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเตเดณเตเดณ เดธเต‡เดตเดจ เดตเดฟเดตเดฐเด™เตเด™เตพ เดจเดฟเด™เตเด™เตพเด•เตเด•เต เดŽเดณเตเดชเตเดชเดคเตเดคเดฟเตฝ เด•เตˆเดฎเดพเดฑเดพเดจเดพเด•เตเด‚. เด‰เดฆเดพเดนเดฐเดฃเดคเตเดคเดฟเดจเต, เดชเต‹เดกเต เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจ เดจเต†เดฏเดฟเด‚เดธเตโ€Œเดชเต†เดฏเตโ€Œเดธเดฟเดจเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเตเดณเตเดณ เดตเดฟเดตเดฐเด™เตเด™เตพ เด‡เดจเดฟเดชเตเดชเดฑเดฏเตเดจเตเดจ เดจเดฟเตผเดฎเตเดฎเดพเดฃเดคเตเดคเดฟเดฒเต‚เดŸเต† เดจเดฎเตเด•เตเด•เต เดฒเดญเดฟเด•เตเด•เตเด‚:

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 เด•เตเดฒเดฏเดจเตเดฑเดฟเดจเต เด…เดคเดฟเดจเตเดฑเต‡เดคเดพเดฏ เดชเดคเดฟเดชเตเดชเต เด‰เดฃเตเดŸเต. เด•เตเดฒเดฏเดจเตเดฑเต เดชเดคเดฟเดชเตเดชเตเด•เดณเตเด‚ เด•เตเดฌเต†เตผเดจเต†เดฑเตเดฑเดธเต เดชเดคเดฟเดชเตเดชเตเด•เดณเตเด‚ เดคเดฎเตเดฎเดฟเดฒเตเดณเตเดณ เด…เดจเตเดฏเต‹เดœเตเดฏเดคเดฏเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเตเดณเตเดณ เด•เต‚เดŸเตเดคเตฝ เดตเดฟเดตเดฐเด™เตเด™เตพ เด•เดฃเตเดŸเต†เดคเตเดคเดพเดจเดพเด•เตเด‚ เด…เดจเตเดฏเต‹เดœเตเดฏเดค เดฎเต†เดŸเตเดฐเดฟเด•เตเดธเต.

เด‡เดชเตเดชเต‹เตพ เดžเด™เตเด™เดณเตเดŸเต† เดชเตเดฐเต‹เดœเด•เตเดฑเตเดฑเต เด‡เดคเตเดชเต‹เดฒเต† เด•เดพเดฃเดชเตเดชเต†เดŸเตเดจเตเดจเต:

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 }}

เดซเดฒเด‚

เด…เด™เตเด™เดจเต†เดฏเดพเดฃเต, เดญเดฏเดชเตเดชเต†เดŸเดพเดคเต†, เดจเดฟเดจเตเดฆเดฟเด•เตเด•เดพเดคเต†, เด—เต‹ เดชเด เดฟเด•เตเด•เดพเดคเต†, เดชเตˆเดคเตเดคเดฃเดฟเตฝ เด•เตเดฌเตผเดจเต†เดฑเตเดฑเดธเดฟเดจเดพเดฏเดฟ เดžเด™เตเด™เดณเตเดŸเต† เดธเตเดตเดจเตเดคเด‚ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเดฑเต† เดจเดฟเตผเดฎเตเดฎเดฟเด•เตเด•เดพเตป เดžเด™เตเด™เตพเด•เตเด•เต เด•เดดเดฟเดžเตเดžเต. เดคเต€เตผเดšเตเดšเดฏเดพเดฏเตเด‚, เด‡เดคเดฟเดจเต เด‡เดจเดฟเดฏเตเด‚ เดตเดณเดฐเดพเตป เด‡เดŸเดฎเตเดฃเตเดŸเต: เดญเดพเดตเดฟเดฏเดฟเตฝ เด‡เดคเดฟเดจเต เด’เดจเตเดจเดฟเดฒเดงเดฟเด•เด‚ เดจเดฟเดฏเดฎเด™เตเด™เตพ เดชเตเดฐเต‹เดธเดธเตเดธเต เดšเต†เดฏเตเดฏเดพเดจเตเด‚ เด’เดจเตเดจเดฟเดฒเดงเดฟเด•เด‚ เดคเตเดฐเต†เดกเตเด•เดณเดฟเตฝ เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เดพเดจเตเด‚ เด…เดคเดฟเดจเตเดฑเต† CRD-เด•เดณเดฟเดฒเต† เดฎเดพเดฑเตเดฑเด™เตเด™เตพ เดธเตเดตเดคเดจเตเดคเตเดฐเดฎเดพเดฏเดฟ เดจเดฟเดฐเต€เด•เตเดทเดฟเด•เตเด•เดพเดจเตเด‚ เด•เดดเดฟเดฏเตเด‚ ...

เดจเดฟเด™เตเด™เตพเด•เตเด•เต เด•เต‹เดกเต เด…เดŸเตเดคเตเดคเดฑเดฟเดฏเดพเตป, เดžเด™เตเด™เตพ เด…เดคเต เด‰เตพเดชเตเดชเต†เดŸเตเดคเตเดคเดฟเดฏเดฟเดŸเตเดŸเตเดฃเตเดŸเต เดชเตŠเดคเต เดถเต‡เด–เดฐเด‚. เดชเตˆเดคเตเดคเตบ เด‰เดชเดฏเต‹เด—เดฟเดšเตเดšเต เดจเดŸเดชเตเดชเดฟเดฒเดพเด•เตเด•เดฟเดฏ เด•เต‚เดŸเตเดคเตฝ เด—เตเดฐเตเดคเดฐเดฎเดพเดฏ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผเดฎเดพเดฐเตเดŸเต† เด‰เดฆเดพเดนเดฐเดฃเด™เตเด™เตพ เดจเดฟเด™เตเด™เตพเด•เตเด•เต เดตเต‡เดฃเดฎเต†เด™เตเด•เดฟเตฝ, mongodb เดตเดฟเดจเตเดฏเดธเดฟเด•เตเด•เตเดจเตเดจเดคเดฟเดจเต เดฐเดฃเตเดŸเต เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผเดฎเดพเดฐเดฟเดฒเต‡เด•เตเด•เต เดจเดฟเด™เตเด™เดณเตเดŸเต† เดถเตเดฐเดฆเตเดง เดคเดฟเดฐเดฟเด•เตเด•เดพเด‚ (ะฟะตั€ะฒั‹ะน ะธ เดฐเดฃเตเดŸเดพเดฎเดคเตเดคเต‡เดคเต).

PS เดจเดฟเด™เตเด™เตพเด•เตเด•เต เด•เตเดฌเตผเดจเต†เดฑเตเดฑเดธเต เด‡เดตเดจเตเดฑเตเด•เตพ เด•เตˆเด•เดพเดฐเตเดฏเด‚ เดšเต†เดฏเตเดฏเดพเตป เดฎเดŸเดฟเดฏเดพเดฃเต†เด™เตเด•เดฟเดฒเต‹ เดฌเดพเดทเต เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เดพเตป เดจเดฟเด™เตเด™เตพเด•เตเด•เต เด•เต‚เดŸเตเดคเตฝ เดถเต€เดฒเดฎเดพเดฃเต†เด™เตเด•เดฟเตฝ, เดžเด™เตเด™เดณเตเดŸเต† เดธเดนเดชเตเดฐเดตเตผเดคเตเดคเด•เตผ เดซเต‹เดฎเดฟเตฝ เด’เดฐเต เดฑเต†เดกเดฟเดฎเต†เดฏเตเดกเต เดชเดฐเดฟเดนเดพเดฐเด‚ เดคเดฏเตเดฏเดพเดฑเดพเด•เตเด•เดฟเดฏเดฟเดŸเตเดŸเตเดฃเตเดŸเต. เดทเต†เตฝ เด“เดชเตเดชเดฑเต‡เดฑเตเดฑเตผ (เดžเด™เตเด™เตพ เดชเตเดฐเด–เตเดฏเดพเดชเดฟเดšเตเดšเต เด…เดคเต เดเดชเตเดฐเดฟเดฒเดฟเตฝ).

เดชเดฟเดชเดฟเดŽเดธเต

เดžเด™เตเด™เดณเตเดŸเต† เดฌเตเดฒเต‹เด—เดฟเดฒเตเด‚ เดตเดพเดฏเดฟเด•เตเด•เตเด•:

เด…เดตเดฒเด‚เดฌเด‚: www.habr.com

เด’เดฐเต เด…เดญเดฟเดชเตเดฐเดพเดฏเด‚ เดšเต‡เตผเด•เตเด•เตเด•