Konfigurimi i projektit brenda dhe jashtë Kubernetes

Kohët e fundit kam shkruar përgjigje për jetën e projektit në Docker dhe korrigjimin e kodit jashtë tij, ku ai përmendi shkurtimisht se ju mund të krijoni sistemin tuaj të konfigurimit në mënyrë që shërbimi të funksionojë mirë në Kuber, të nxjerrë sekrete dhe të funksionojë në mënyrë të përshtatshme në nivel lokal, madje edhe jashtë Docker krejtësisht. Asgjë e komplikuar, por "receta" e përshkruar mund të jetë e dobishme për dikë :) Kodi është në Python, por logjika nuk është e lidhur me gjuhën.

Konfigurimi i projektit brenda dhe jashtë Kubernetes

Sfondi i pyetjes është ky: një herë e një kohë kishte një projekt, në fillim ishte një monolit i vogël me shërbime dhe skripta, por me kalimin e kohës u rrit, u nda në shërbime, të cilat nga ana e tyre filluan të ndaheshin në mikroshërbime, dhe pastaj u përshkallëzua. Në fillim, e gjithë kjo u bë në VPS të zhveshur, proceset e konfigurimit dhe vendosjes së kodit në të cilin u automatizuan duke përdorur Ansible, dhe secili shërbim u përpilua me një konfigurim YAML me cilësimet dhe çelësat e nevojshëm, dhe një skedar i ngjashëm konfigurimi u përdor për niset lokale, gjë që ishte shumë e përshtatshme, sepse .k ky konfigurim ngarkohet në një objekt global, i aksesueshëm nga kudo në projekt.

Megjithatë, rritja e numrit të mikroshërbimeve, lidhjeve të tyre dhe nevoja për prerje dhe monitorim të centralizuar, paralajmëroi një lëvizje në Kuber, e cila është ende në progres. Së bashku me ndihmën në zgjidhjen e problemeve të përmendura, Kubernetes ofron qasjet e saj për menaxhimin e infrastrukturës, duke përfshirë të ashtuquajturat Sekrete и mënyra për të punuar me ta. Mekanizmi është standard dhe i besueshëm, kështu që është fjalë për fjalë një mëkat të mos e përdorni! Por në të njëjtën kohë, unë do të doja të ruaj formatin tim aktual për të punuar me konfigurimin: së pari, ta përdor atë në mënyrë uniforme në mikroshërbime të ndryshme të projektit, dhe së dyti, të jem në gjendje të ekzekutoj kodin në makinën lokale duke përdorur një të thjeshtë skedari i konfigurimit.

Në këtë drejtim, mekanizmi për ndërtimin e një objekti konfigurimi u modifikua për të qenë në gjendje të punojë si me skedarin tonë të konfigurimit klasik ashtu edhe me sekretet nga Kuber. Një strukturë më e ngurtë konfigurimi u specifikua gjithashtu, në gjuhën e Python-it të tretë, si më poshtë:

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

Kjo do të thotë, cogfig-u përfundimtar është një fjalor me seksione të emërtuara, secila prej të cilave është një fjalor me vlera nga lloje të thjeshta. Dhe seksionet përshkruajnë konfigurimin dhe aksesin në burimet e një lloji të caktuar. Një shembull i një pjese të konfigurimit tonë:

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"

Në të njëjtën kohë, fusha engine bazat e të dhënave mund të instalohen në SQLite, dhe redis vendosur në mock, duke specifikuar edhe emrin e skedarit që do të ruhet - këto parametra njihen dhe përpunohen saktë, gjë që e bën të lehtë ekzekutimin e kodit në nivel lokal për korrigjimin e gabimeve, testimin e njësisë dhe çdo nevojë tjetër. Kjo është veçanërisht e rëndësishme për ne sepse ka shumë nevoja të tjera - një pjesë e kodit tonë është menduar për llogaritje të ndryshme analitike, ai funksionon jo vetëm në serverë me orkestrim, por edhe me skripta të ndryshëm, dhe në kompjuterët e analistëve që duhet të punojnë përmes dhe korrigjoni tubacionet komplekse të përpunimit të të dhënave pa shqetësuese problemet e backend. Nga rruga, nuk do të dëmtonte të ndajmë që mjetet tona kryesore, duke përfshirë kodin e paraqitjes së konfigurimit, janë instaluar nëpërmjet setup.py – së bashku kjo bashkon kodin tonë në një ekosistem të vetëm, të pavarur nga platforma dhe mënyra e përdorimit.

Përshkrimi i një pod Kubernetes duket si ky:

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

Kjo do të thotë, çdo sekret përshkruan një pjesë. Vetë sekretet krijohen si kjo:

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

Së bashku kjo rezulton në krijimin e skedarëve YAML përgjatë rrugës /etc/secrets/db-main/section_name.yaml

Dhe për nisjet lokale, përdoret konfigurimi, i vendosur në direktorinë rrënjë të projektit ose përgjatë shtegut të specifikuar në variablin e mjedisit. Kodi përgjegjës për këto lehtësi mund të shihet në 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()

Logjika këtu është mjaft e thjeshtë: ne kombinojmë konfigurime të mëdha nga direktoria e projektit dhe shtigjet sipas ndryshores së mjedisit, dhe seksione të vogla konfigurimi nga sekretet e Kuber, dhe më pas i përpunojmë ato pak. Plus disa variabla. Vërej se kur kërkoni skedarë nga sekretet, përdoret një kufizim i thellësisë, sepse K8s krijon një dosje të fshehur në secilën sekret ku ruhen vetë sekretet, dhe vetëm një lidhje ndodhet në një nivel më të lartë.

Shpresoj që ajo që përshkruhet do të jetë e dobishme për dikë :) Çdo koment dhe rekomandim në lidhje me sigurinë ose fusha të tjera për përmirësim pranohet. Mendimi i komunitetit është gjithashtu interesant, ndoshta ia vlen të shtohet mbështetje për ConfigMaps (projekti ynë nuk i përdor ende ato) dhe të publikojë kodin në GitHub / PyPI? Personalisht, mendoj se gjëra të tilla janë shumë individuale që projektet të jenë universale, dhe pak vështrim në zbatimet e njerëzve të tjerë, si ky i dhënë këtu, dhe një diskutim për nuancat, këshillat dhe praktikat më të mira, të cilat shpresoj t'i shoh në komente. , mjafton 😉

Vetëm përdoruesit e regjistruar mund të marrin pjesë në anketë. Hyni, te lutem

A duhet të botoj si projekt/bibliotekë?

  • 0,0%Po, do të përdorja /contribution0

  • 33,3%Po, kjo tingëllon mirë4

  • 41,7%Jo, kush duhet ta bëjë vetë në formatin e tij dhe për t'iu përshtatur nevojave të tij5

  • 25,0%Do të përmbahem nga përgjigjja3

12 përdorues votuan. 3 përdorues abstenuan.

Burimi: www.habr.com

Shto një koment