Konfiguracija projekta unutar i izvan Kubernetesa

Nedavno sam napisao odgovor o životu projekta u Dockeru i kodu za otklanjanje grešaka izvan njega, gdje je ukratko spomenuo da možete napraviti svoj vlastiti konfiguracijski sistem tako da usluga dobro radi u Kuberu, otkriva tajne i radi prikladno lokalno, čak i izvan Dockera. Ništa komplikovano, ali opisani “recept” može nekome biti od koristi :) Kod je u Pythonu, ali logika nije vezana za jezik.

Konfiguracija projekta unutar i izvan Kubernetesa

Pozadina pitanja je sljedeća: nekada je postojao jedan projekat, isprva je bio mali monolit sa uslužnim programima i skriptama, ali je vremenom rastao, podijeljen na servise, koji su se pak počeli dijeliti na mikroservise, i zatim povećao. Isprva se sve ovo radilo na golom VPS-u, procesi postavljanja i postavljanja koda na kojem su bili automatizirani pomoću Ansible-a, a svaki servis je kompajliran sa YAML konfiguracijom sa potrebnim postavkama i ključevima, a sličan konfiguracijski fajl je korišten za lokalna lansiranja, što je bilo vrlo zgodno, jer se .k ova konfiguracija učitava u globalni objekat, dostupan s bilo kojeg mjesta u projektu.

Međutim, rast broja mikroservisa, njihovih veza i potreba za centralizovanom evidencijom i nadzorom, nagovijestio je prelazak na Kuber, koji je još uvijek u toku. Uz pomoć u rješavanju navedenih problema, Kubernetes nudi svoje pristupe upravljanju infrastrukturom, uključujući takozvane tajne и načina rada sa njima. Mehanizam je standardan i pouzdan, tako da je bukvalno grijeh ne koristiti ga! Ali u isto vrijeme, želio bih zadržati svoj trenutni format za rad s konfiguracijom: prvo, da ga ujednačeno koristim u različitim mikroservisima projekta, i drugo, da mogu pokrenuti kod na lokalnom računalu koristeći jedan jednostavan konfiguracioni fajl.

U tom smislu, mehanizam za konstruisanje konfiguracionog objekta je modifikovan kako bi mogao da radi i sa našim klasičnim konfiguracionim fajlom i sa tajnama iz Kubera. Navedena je i rigidnija konfiguraciona struktura, na jeziku trećeg Pythona, na sledeći način:

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

Odnosno, konačna cogfig je rečnik sa imenovanim sekcijama, od kojih je svaki rečnik sa vrednostima iz jednostavnih tipova. A odjeljci opisuju konfiguraciju i pristup resursima određenog tipa. Primjer dijela naše konfiguracije:

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"

Istovremeno, polje engine baze podataka se mogu instalirati na SQLite, i redis postavljeno na mock, navodeći i naziv datoteke za spremanje - ovi parametri su ispravno prepoznati i obrađeni, što olakšava lokalno pokretanje koda za otklanjanje grešaka, testiranje jedinica i bilo koje druge potrebe. Ovo nam je posebno važno jer postoje mnoge druge potrebe - dio našeg koda je namijenjen raznim analitičkim proračunima, radi ne samo na serverima sa orkestracijom, već i sa raznim skriptama, te na računarima analitičara koji treba da rade kroz i otklanjanje grešaka u složenim cevovodima za obradu podataka bez brige o pozadinskim problemima. Usput, ne bi škodilo da podijelimo da su naši glavni alati, uključujući kod za izgled konfiguracije, instalirani putem setup.py – zajedno ovo ujedinjuje naš kod u jedinstveni ekosistem, neovisno o platformi i načinu korištenja.

Opis Kubernetes pod izgleda ovako:

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

To jest, svaka tajna opisuje jedan odjeljak. Same tajne se kreiraju ovako:

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

Ovo zajedno rezultira stvaranjem YAML datoteka duž putanje /etc/secrets/db-main/section_name.yaml

A za lokalna pokretanja koristi se konfiguracija koja se nalazi u korijenskom direktoriju projekta ili duž putanje specificirane u varijabli okruženja. Kod koji je odgovoran za ove pogodnosti može se vidjeti u spojleru.

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()

Logika je ovde prilično jednostavna: kombinujemo velike konfiguracije iz direktorijuma projekta i putanje po varijabli okruženja, i male konfiguracione sekcije iz Kuber tajni, a zatim ih malo predobradimo. Plus neke varijable. Napominjem da se prilikom traženja fajlova iz tajni koristi ograničenje dubine, jer K8s u svakoj tajni kreira skriveni folder u kojem su pohranjene same tajne, a samo link se nalazi na višem nivou.

Nadam se da će ovo što je opisano nekome biti od koristi :) Svi komentari i preporuke u vezi sigurnosti ili drugih oblasti za poboljšanje se prihvataju. Zanimljivo je i mišljenje zajednice, možda je vrijedno dodati podršku za ConfigMaps (naš projekat ih još ne koristi) i objaviti kod na GitHub/PyPI? Osobno smatram da su takve stvari previše individualne da bi projekti bili univerzalni, i malo zavirivanja u tuđe implementacije, kao što je ova ovdje navedena, i rasprava o nijansama, savjetima i najboljim praksama, koje se nadam da ću vidjeti u komentarima , dosta je 😉

Samo registrovani korisnici mogu učestvovati u anketi. Prijavite semolim.

Da li da objavim kao projekat/biblioteku?

  • 0,0%Da, koristio bih /contribution0

  • 33,3%Da, to zvuči odlično4

  • 41,7%Ne, ko treba da to uradi sam u svom formatu i da odgovara svojim potrebama5

  • 25,0%Uzdržaću se od odgovora3

Glasalo je 12 korisnika. 3 korisnika je bila uzdržana.

izvor: www.habr.com

Dodajte komentar