Konfiguracija projekta unutar i van Kubernetes-a

Nedavno sem napisal odgovor o življenju projekta v Dockerjih in odpravljanju napak zunaj njega, kjer sem na kratko omenil, da je možno ustvariti svoj konfiguracijski sistem, da storitev v Kubernetes deluje dobro, potegne skrivnosti in se lokalno udobno zažene, tudi povsem zunaj Dockerja. Nič težkega, vendar lahko opisan "recept" koristi nekomu 🙂 Koda je napisana v Pythonu, vendar logika ni vezana na jezik.

Konfiguracija projekta unutar i van Kubernetes-a

Predzgodba vprašanja je taka: živel je en projekt, na začetku je bil majhen monolit z orodji in skripti, a je skozi čas rasel, delil se je na storitve, ki so se nato delile na mikrostoritve, nato pa se je še skaliral. Sprva je vse to potekalo na golih VPS-ih, procesi nastavljanja in uvajanja kode pa so bili avtomatizirani s pomočjo Ansible, vsaki storitvi pa je bil pripravljen YAML-config z potrebnimi nastavitvami in ključi, podobna konfiguracijska datoteka pa je bila uporabljena za lokalne zagon, kar je bilo zelo priročno, saj se ta konfiguracija naloži v globalni objekt, dostopen iz katerega koli mesta v projektu.

Vendar pa rast števila mikrostoritev, njihovih povezav in tudi potreba po centraliziranem beleženju in nadzoru, so napovedali selitev v Kubernetes, ki je še vedno v teku. Poleg pomoči pri reševanju omenjenih nalog, Kubernetes ponuja svoje pristope k upravljanju infrastrukture, vključno z t.i. Skrivnostmi in metodami dela z njimi. Mehanizem je standarden in zanesljiv, zato bi bilo dobesedno greh, če ga ne bi izkoristili! Vendar pa bi želeli ohraniti trenutni format dela z konfiguracijo: najprej, enotno ga uporabljati v različnih mikrostoritev projekta, in drugič, imeti možnost zagnati kodo na lokalnem računalniku z uporabo ene preproste konfiguracijske datoteke.

Zaradi tega je bil mehanizem za gradnjo konfiguracijskega objekta prilagojen tako, da lahko deluje tako z našo klasično konfiguracijo datoteko, kot tudi s skrivnostmi iz Kubernetesa. Prav tako je bila določena strožja struktura konfiguracije, govorimo o tretjem Pythenu, takole:

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

Torej, končna konfiguracija je slovar z imenovanimi sekcijami, vsaka od njih pa je slovar z vrednostmi osnovnih tipov. In sekcije opisujejo konfiguracijo in dostop do virov določene vrste. Primer dela 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: info@test.com
  pw: "SuperHardPassword"

V tem času, polje engine baze podatkov lahko nastavite na SQLite, medtem ko redis nastavite na mock, določitvijo še imena datoteke za shranjevanje, – ti parametri se pravilno prepoznajo in obdelujejo, kar omogoča enostavno zagon kode lokalno za odpravljanje napak, enotno testiranje in vse druge potrebe. To je za nas še posebej pomembno, saj teh drugih potreb imamo veliko – del naše kode je namenjene raznolikim analitičnim izračunom, deluje ne le na strežnikih z orkestracijo, ampak tudi z različnimi skripti in na računalnikih analitikov, ki morajo obdelovati in odpravljati zapletene vodovode obdelave podatkov brez skrbi o backend vprašanjih. Mimogrede, ni odveč omenjati, da se naši osnovni orodja, vključno s kodo za konfiguracijo, nameščajo preko setup.py – skupaj to združuje našo kodo v enotno ekosistem, neodvisen od platforme in načina uporabe.

Opis poda v Kubernetesu izgleda tako:

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

Torej, v vsakem sekretu je opisano eno poglavje. Sami sekreti se ustvarjajo tako:

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

Skupaj to vodi do ustvarjanja YAML-datotek na poti /etc/secrets/db-main/section_name.yaml

In za lokalne zagon uporabljamo konfiguracijo, ki se nahaja v korenski mapi projekta ali na poti, navedeni v okoljski spremenljivki. Koda, odgovorna za te udobnosti, je na voljo v spoilerju.

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 tu dokaj jednostavna: spajamo velike konfiguracije iz direktorija projekta i putanje po varijabli okruženja, te male konfiguracije-sekcije iz Kuberovih tajni, a zatim ih malo predobradimo. Uz to, neke varijable. Napominjem da se pri pretraživanju datoteka iz tajni koristi ograničenje dubine, jer K8s u svakoj tajni stvara još jedan skriveni folder gdje se same tajne i čuvaju, dok je na višem nivou samo poveznica.

Upam, da bo to, kar sem opisal, nekomu v pomoč 🙂 Sprejemam vse komentarje in priporočila glede varnosti ali drugih vidikov izboljšanja. Zanima me tudi mnenje skupnosti, morda bi bilo smiselno dodati podporo za ConfigMaps (v našem projektu trenutno niso uporabljeni) in oblikovati kodo na GitHubu / PyPI? Osebno menim, da so take stvari preveč individualne za projekte, da bi bile univerzalne, in dovolj je, da malo pokukamo k drugim izvedbam, kot je ta, ter razpravimo o podrobnostih, nasvetih in najboljših praksah, kar upam, da bom videl v komentarjih 😉

Samo registrovani korisnici mogu učestvovati u anketi. Prijavite se, molim vas.

Ali bi morali objaviti kot projekt / knjižnico?

  • 0,0%Da, bi uporabljal / prispeval.

  • 33,3%Da, zveni odlično.

  • 41,7%Ne, kdo to rabi, bo naredil sam v svojem formatu in za svoje potrebe.

  • 25,0%Se vzdržim odgovora.

Glasovalo je 12 uporabnikov. 3 uporabniki so se vzdržali.

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster