Projektkonfiguraasje binnen en bûten Kubernetes

Ik koartlyn skreau antwurd oer projektlibben yn Docker en debuggen fan koade dêrbûten. Neat yngewikkeld, mar it beskreaune "resept" kin nuttich wêze foar immen :) De koade is yn Python, mar de logika is net bûn oan 'e taal.

Projektkonfiguraasje binnen en bûten Kubernetes

De eftergrûn fan 'e fraach is dizze: eartiids wie d'r ien projekt, earst wie it in lytse monolyt mei nutsbedriuwen en skripts, mar yn 'e rin fan' e tiid groeide it, ferdield yn tsjinsten, dy't op har beurt begon te wurde ferdield yn mikrotsjinsten, en dan opskaald. Earst waard dit alles dien op bleate VPS, de prosessen foar it ynstellen en ynsetten fan koade wêrop't automatisearre waarden mei Ansible, en elke tsjinst waard kompilearre mei in YAML-konfiguraasje mei de nedige ynstellings en kaaien, en in ferlykbere konfiguraasjetriem waard brûkt foar lokale lansearringen, dat wie tige handich, omdat .k dizze config wurdt laden yn in globale foarwerp, tagonklik fan oeral yn it projekt.

De groei fan it oantal mikrotsjinsten, har ferbiningen, en need foar sintralisearre logging en tafersjoch, foarsjoen fan in ferhuzing nei Kuber, dy't noch oan 'e gong is. Tegearre mei help by it oplossen fan de neamde problemen biedt Kubernetes har oanpak foar ynfrastruktuerbehear, ynklusyf saneamde Secrets и manieren om mei har te wurkjen. It meganisme is standert en betrouber, dus it is letterlik in sûnde om it net te brûken! Mar tagelyk wol ik myn hjoeddeistige formaat behâlde foar it wurkjen mei de konfiguraasje: earst, om it unifoarm te brûken yn ferskate mikrotsjinsten fan it projekt, en twad, om de koade op 'e lokale masine út te fieren mei ien ienfâldige konfiguraasjetriem.

Yn dit ferbân waard it meganisme foar it bouwen fan in konfiguraasjeobjekt oanpast om sawol mei ús klassike konfiguraasjetriem as mei geheimen fan Kuber te wurkjen. In mear rigide konfiguraasjestruktuer waard ek spesifisearre, yn 'e taal fan' e tredde Python, as folget:

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

Dat is, de lêste cogfig is in wurdboek mei neamde seksjes, elk fan dat is in wurdboek mei wearden fan ienfâldige typen. En seksjes beskriuwe de konfiguraasje en tagong ta boarnen fan in bepaald type. In foarbyld fan in stik fan ús konfiguraasje:

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"

Tagelyk, it fjild engine databases kinne wurde ynstallearre op SQLite, en redis Klear foar mock, spesifisearje ek de namme fan it te bewarjen bestân - dizze parameters wurde korrekt erkend en ferwurke, wat it maklik makket om de koade lokaal út te fieren foar debuggen, ienheidstesten en alle oare behoeften. Dit is foaral wichtich foar ús, om't d'r in protte oare behoeften binne - in diel fan ús koade is bedoeld foar ferskate analytyske berekkeningen, it rint net allinich op servers mei orkestraasje, mar ek mei ferskate skripts, en op 'e kompjûters fan analysten dy't moatte wurkje troch en debug komplekse gegevensferwurkingspipelines sûnder soargen oer backend-problemen. Trouwens, it soe gjin sear dwaan om te dielen dat ús wichtichste ark, ynklusyf de konfiguraasje-yndielingskoade, binne ynstalleare fia setup.py - tegearre dit ferieniget ús koade yn ien ekosysteem, ûnôfhinklik fan platfoarm en metoade fan gebrûk.

De beskriuwing fan in Kubernetes-pod sjocht der sa út:

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

Dat is, elk geheim beskriuwt ien seksje. De geheimen sels binne sa makke:

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

Mei-elkoar resultearret dit yn it meitsjen fan YAML-bestannen lâns it paad /etc/secrets/db-main/section_name.yaml

En foar lokale lansearringen wurdt de konfiguraasje brûkt, lizzend yn 'e root-map fan it projekt of lâns it paad spesifisearre yn' e omjouwingsfariabele. De koade ferantwurdlik foar dizze gemak kin sjoen wurde yn 'e 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()

De logika hjir is frij simpel: wy kombinearje grutte konfiguraasjes út 'e projektmap en paden troch omjouwingsfariabele, en lytse konfiguraasje-seksjes fan Kuber-geheimen, en ferwurkje se dan in bytsje. Plus guon fariabelen. Ik merk op dat by it sykjen nei triemmen út geheimen, wurdt in djipte beheining brûkt, omdat K8s makket in ferburgen map yn elk geheim dêr't de geheimen sels wurde opslein, en krekt in keppeling leit op in heger nivo.

Ik hoopje dat wat beskreaun wurdt nuttich wêze foar immen :) Alle opmerkings en oanbefellings oangeande feiligens of oare gebieten foar ferbettering wurde akseptearre. De miening fan 'e mienskip is ek nijsgjirrich, miskien is it wurdich stipe ta te foegjen foar ConfigMaps (ús projekt brûkt se noch net) en publisearje de koade op GitHub / PyPI? Persoanlik fyn ik dat sokke dingen te yndividueel binne foar projekten om universeel te wêzen, en in bytsje sjoch nei de ymplemintaasje fan oaren, lykas de hjir jûn, en in diskusje oer nuânses, tips en best practices, dy't ik hoopje te sjen yn 'e kommentaren , is genôch 😉

Allinnich registrearre brûkers kinne meidwaan oan 'e enkête. Ynlogge, asjebleaft.

Moat ik publisearje as projekt/bibleteek?

  • 0,0%Ja, ik soe /contribution0 brûke

  • 33,3%Ja, dat klinkt geweldich 4

  • 41,7%Nee, wa moat it sels dwaan yn in eigen opmaak en by har behoeften5

  • 25,0%Ik sil my ûnthâlde fan antwurd3

12 brûkers stimden. 3 brûkers ûntholden har.

Boarne: www.habr.com

Add a comment