Configuration ng proyekto sa loob at labas ng Kubernetes

Nagsulat ako kamakailan sagot tungkol sa buhay ng proyekto sa Docker at pag-debug ng code sa labas nito, kung saan maikling binanggit niya na maaari kang gumawa ng sarili mong configuration system upang gumana nang maayos ang serbisyo sa Kuber, kumukuha ng mga sikreto, at maginhawang tumakbo nang lokal, kahit na sa labas ng Docker sa kabuuan. Walang kumplikado, ngunit ang inilarawan na "recipe" ay maaaring maging kapaki-pakinabang sa isang tao :) Ang code ay nasa Python, ngunit ang lohika ay hindi nakatali sa wika.

Configuration ng proyekto sa loob at labas ng Kubernetes

Ang background ng tanong ay ito: noong unang panahon ay mayroong isang proyekto, sa una ito ay isang maliit na monolith na may mga kagamitan at script, ngunit sa paglipas ng panahon ay lumago ito, nahahati sa mga serbisyo, na nagsimulang hatiin sa mga microservice, at tapos pinalaki. Sa una, ang lahat ng ito ay ginawa sa hubad na VPS, ang mga proseso ng pag-set up at pag-deploy ng code kung saan ay awtomatiko gamit ang Ansible, at ang bawat serbisyo ay pinagsama-sama sa isang YAML config na may mga kinakailangang setting at key, at isang katulad na config file ang ginamit para sa mga lokal na paglulunsad, na napakaginhawa, dahil .k ang config na ito ay na-load sa isang pandaigdigang bagay, na naa-access mula sa kahit saan sa proyekto.

Gayunpaman, ang paglaki sa bilang ng mga microservice, ang kanilang mga koneksyon, at pangangailangan para sa sentralisadong pag-log at pagsubaybay, naglalarawan ng paglipat sa Kuber, na kasalukuyang isinasagawa. Kasama ang tulong sa paglutas ng mga nabanggit na problema, nag-aalok ang Kubernetes ng mga diskarte nito sa pamamahala ng imprastraktura, kasama na tinatawag na Secrets ΠΈ mga paraan upang makipagtulungan sa kanila. Ang mekanismo ay karaniwan at maaasahan, kaya literal na kasalanan ang hindi gamitin ito! Ngunit sa parehong oras, nais kong mapanatili ang aking kasalukuyang format para sa pagtatrabaho sa config: una, gamitin ito nang pantay-pantay sa iba't ibang mga microservice ng proyekto, at pangalawa, upang mapatakbo ang code sa lokal na makina gamit ang isang simpleng config file.

Kaugnay nito, binago ang mekanismo para sa pagbuo ng object ng pagsasaayos upang magawang gumana pareho sa aming klasikong config file at sa mga lihim mula sa Kuber. Ang isang mas mahigpit na istraktura ng config ay tinukoy din, sa wika ng ikatlong Python, tulad ng sumusunod:

Dict[str, Dict[str, Union[str, int, float]]]

Iyon ay, ang panghuling cogfig ay isang diksyunaryo na may pinangalanang mga seksyon, ang bawat isa ay isang diksyunaryo na may mga halaga mula sa mga simpleng uri. At inilalarawan ng mga seksyon ang pagsasaayos at pag-access sa mga mapagkukunan ng isang partikular na uri. Isang halimbawa ng isang piraso ng aming config:

adminka:
  django_secret: "ExtraLongAndHardCode"

db_main:
  engine: mysql
  host: 256.128.64.32
  user: cool_user
  password: "SuperHardPassword"

redis:
  host: 256.128.64.32
  pw: "SuperHardPassword"
  port: 26379

smtp:
  server: smtp.gmail.com
  port: 465
  email: [email protected]
  pw: "SuperHardPassword"

Kasabay nito, ang patlang engine ang mga database ay maaaring mai-install sa SQLite, at redis itakda sa mock, na tinukoy din ang pangalan ng file na ise-save - ang mga parameter na ito ay tama na kinikilala at naproseso, na ginagawang madali upang patakbuhin ang code nang lokal para sa pag-debug, pagsubok ng unit at anumang iba pang pangangailangan. Ito ay lalong mahalaga para sa amin dahil maraming iba pang mga pangangailangan - bahagi ng aming code ay inilaan para sa iba't ibang mga analytical na kalkulasyon, ito ay tumatakbo hindi lamang sa mga server na may orkestrasyon, kundi pati na rin sa iba't ibang mga script, at sa mga computer ng mga analyst na kailangang magtrabaho sa pamamagitan ng at i-debug ang mga kumplikadong pipeline sa pagproseso ng data nang hindi nababahala sa mga isyu sa backend. Sa pamamagitan ng paraan, hindi masakit na ibahagi na ang aming mga pangunahing tool, kasama ang config layout code, ay na-install sa pamamagitan ng setup.py – sama-samang pinag-iisa nito ang ating code sa iisang ecosystem, independiyente sa platform at paraan ng paggamit.

Ang paglalarawan ng isang Kubernetes pod ay ganito ang hitsura:

containers:
  - name : enter-api
    image: enter-api:latest
    ports:
      - containerPort: 80
    volumeMounts:
      - name: db-main-secret-volume
        mountPath: /etc/secrets/db-main

volumes:
  - name: db-main-secret-volume
    secret:
      secretName: db-main-secret

Ibig sabihin, ang bawat lihim ay naglalarawan ng isang seksyon. Ang mga lihim mismo ay nilikha tulad nito:

apiVersion: v1
kind: Secret
metadata:
  name: db-main-secret
type: Opaque
stringData:
  db_main.yaml: |
    engine: sqlite
    filename: main.sqlite3

Magkasama ito ay nagreresulta sa paglikha ng mga YAML file kasama ang landas /etc/secrets/db-main/section_name.yaml

At para sa mga lokal na paglulunsad, ginagamit ang config, na matatagpuan sa root directory ng proyekto o kasama ang landas na tinukoy sa variable ng kapaligiran. Ang code na responsable para sa mga kaginhawaan na ito ay makikita sa spoiler.

config.py

__author__ = 'AivanF'
__copyright__ = 'Copyright 2020, AivanF'

import os
import yaml

__all__ = ['config']
PROJECT_DIR = os.path.abspath(__file__ + 3 * '/..')
SECRETS_DIR = '/etc/secrets'
KEY_LOG = '_config_log'
KEY_DBG = 'debug'

def is_yes(value):
    if isinstance(value, str):
        value = value.lower()
        if value in ('1', 'on', 'yes', 'true'):
            return True
    else:
        if value in (1, True):
            return True
    return False

def update_config_part(config, key, data):
    if key not in config:
        config[key] = data
    else:
        config[key].update(data)

def parse_big_config(config, filename):
    '''
    Parse YAML config with multiple section
    '''
    if not os.path.isfile(filename):
        return False
    with open(filename) as f:
        config_new = yaml.safe_load(f.read())
        for key, data in config_new.items():
            update_config_part(config, key, data)
        config[KEY_LOG].append(filename)
        return True

def parse_tiny_config(config, key, filename):
    '''
    Parse YAML config with a single section
    '''
    with open(filename) as f:
        config_tiny = yaml.safe_load(f.read())
        update_config_part(config, key, config_tiny)
        config[KEY_LOG].append(filename)

def combine_config():
    config = {
        # To debug config load code
        KEY_LOG: [],
        # To debug other code
        KEY_DBG: is_yes(os.environ.get('DEBUG')),
    }
    # For simple local runs
    CONFIG_SIMPLE = os.path.join(PROJECT_DIR, 'config.yaml')
    parse_big_config(config, CONFIG_SIMPLE)
    # For container's tests
    CONFIG_ENVVAR = os.environ.get('CONFIG')
    if CONFIG_ENVVAR is not None:
        if not parse_big_config(config, CONFIG_ENVVAR):
            raise ValueError(
                f'No config file from EnvVar:n'
                f'{CONFIG_ENVVAR}'
            )
    # For K8s secrets
    for path, dirs, files in os.walk(SECRETS_DIR):
        depth = path[len(SECRETS_DIR):].count(os.sep)
        if depth > 1:
            continue
        for file in files:
            if file.endswith('.yaml'):
                filename = os.path.join(path, file)
                key = file.rsplit('.', 1)[0]
                parse_tiny_config(config, key, filename)
    return config

def build_config():
    config = combine_config()
    # Preprocess
    for key, data in config.items():
        if key.startswith('db_'):
            if data['engine'] == 'sqlite':
                data['filename'] = os.path.join(PROJECT_DIR, data['filename'])
    # To verify correctness
    if config[KEY_DBG]:
        print(f'** Loaded config:n{yaml.dump(config)}')
    else:
        print(f'** Loaded config from: {config[KEY_LOG]}')
    return config

config = build_config()

Ang lohika dito ay medyo simple: pinagsama namin ang malalaking config mula sa direktoryo ng proyekto at mga landas ayon sa variable ng kapaligiran, at maliliit na seksyon ng config mula sa mga lihim ng Kuber, at pagkatapos ay i-preprocess ang mga ito nang kaunti. Dagdag pa ang ilang mga variable. Tandaan ko na kapag naghahanap ng mga file mula sa mga lihim, ginagamit ang isang malalim na limitasyon, dahil ang K8s ay lumilikha ng isang nakatagong folder sa bawat lihim kung saan ang mga lihim mismo ay nakaimbak, at isang link lamang ang matatagpuan sa mas mataas na antas.

Umaasa ako na kung ano ang inilarawan ay magiging kapaki-pakinabang sa isang tao :) Anumang mga komento at rekomendasyon tungkol sa seguridad o iba pang mga lugar para sa pagpapabuti ay tinatanggap. Ang opinyon ng komunidad ay kawili-wili din, marahil ito ay nagkakahalaga ng pagdaragdag ng suporta para sa ConfigMaps (hindi pa ginagamit ng aming proyekto ang mga ito) at i-publish ang code sa GitHub / PyPI? Sa personal, sa palagay ko ang mga bagay na ito ay masyadong indibidwal para sa mga proyekto upang maging pangkalahatan, at isang maliit na pagsilip sa mga pagpapatupad ng ibang tao, tulad ng ibinigay dito, at isang talakayan ng mga nuances, mga tip at pinakamahusay na kasanayan, na inaasahan kong makita sa mga komento , sapat na πŸ˜‰

Ang mga rehistradong user lamang ang maaaring lumahok sa survey. Mag-sign in, pakiusap

Dapat ba akong mag-publish bilang isang proyekto/library?

  • 0,0%Oo, gagamitin ko ang /contribution0

  • 33,3%Oo, maganda iyan4

  • 41,7%Hindi, sino ang kailangang gumawa nito sa sarili nilang format at para umayon sa kanilang mga pangangailangan5

  • 25,0%Pipigilan kong sumagot3

12 mga gumagamit ang bumoto. 3 user ang umiwas.

Pinagmulan: www.habr.com

Magdagdag ng komento