Projektkonfiguráció a Kubernetesen belül és kívül

Nemrég írtam válaszoljon a projekt Dockerben való életére és a kód azon kívüli hibakeresésére, ahol röviden megemlítette, hogy lehet saját konfigurációs rendszert készíteni, hogy a szolgáltatás jól működjön a Kuberben, előhívja a titkokat, és kényelmesen futhasson helyben, akár a Dockeren kívül is. Semmi bonyolult, de a leírt “recept” valakinek hasznos lehet :) A kód Pythonban van, de a logika nincs nyelvhez kötve.

Projektkonfiguráció a Kubernetesen belül és kívül

A kérdés háttere a következő: valamikor volt egy projekt, eleinte egy kis monolit volt közművekkel és szkriptekkel, de idővel egyre nőtt, szétvált szolgáltatásokra, amiket viszont elkezdtek mikroszolgáltatásokra osztani, ill. majd felnagyítva. Eleinte mindez csupasz VPS-en történt, amelyen a kód beállítási és telepítési folyamatait az Ansible segítségével automatizálták, és minden szolgáltatáshoz YAML konfigurációt fordítottak a szükséges beállításokkal és kulcsokkal, valamint egy hasonló konfigurációs fájlt használtak. lokális indítások, ami nagyon kényelmes volt, mert .k ez a konfiguráció egy globális objektumba van betöltve, a projektben bárhonnan elérhető.

Azonban a mikroszolgáltatások számának növekedése, azok kapcsolatai, ill központosított naplózás és felügyelet szükségessége, előrevetítette a Kuberhez való költözést, ami még mindig folyamatban van. Az említett problémák megoldásában nyújtott segítséggel együtt a Kubernetes az infrastruktúra-menedzsment megközelítéseit kínálja, beleértve úgynevezett Titkok и hogyan lehet velük dolgozni. A mechanizmus szabványos és megbízható, szóval bűn nem használni! Ugyanakkor szeretném megtartani a jelenlegi formátumomat a konfigurációval való munkavégzéshez: egyrészt egységesen használni a projekt különböző mikroszolgáltatásaiban, másrészt, hogy a kódot a helyi gépen egyetlen egyszerű eszközzel le tudjam futtatni. konfigurációs fájl.

Ebben a tekintetben a konfigurációs objektum létrehozásának mechanizmusa módosult, hogy a klasszikus konfigurációs fájlunkkal és a Kuber titkaival is működjön. Egy merevebb konfigurációs szerkezetet is megadtak a harmadik Python nyelvén, az alábbiak szerint:

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

Vagyis a végső fogaskerék egy szótár névvel ellátott szakaszokkal, amelyek mindegyike egyszerű típusokból származó értékeket tartalmazó szótár. A szakaszok pedig egy bizonyos típusú erőforrások konfigurációját és hozzáférését írják le. Példa a konfigurációnk egy darabjára:

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"

Ugyanakkor a mező engine adatbázisok telepíthetők SQLite-ra, ill redis állítva mock, megadva a mentendő fájl nevét is – ezeket a paramétereket a rendszer helyesen ismeri fel és dolgozza fel, ami megkönnyíti a kód helyi futtatását hibakereséshez, egységteszthez és egyéb igényekhez. Ez különösen fontos számunkra, mert sok más igény is felmerül - a kódunk egy része különféle analitikai számításokhoz készült, nem csak hangszerelésű szervereken fut, hanem különféle szkriptekkel, illetve elemzők számítógépein is, akiknek át kell dolgozniuk. és hibakeresés bonyolult adatfeldolgozási folyamatokban anélkül, hogy aggasztó háttérproblémák kellenek. Egyébként nem ártana megosztani, hogy a fő eszközeink, köztük a konfigurációs elrendezési kód is telepítve van setup.py – együtt ez egyesíti a kódunkat egyetlen ökoszisztémává, platformtól és felhasználási módtól függetlenül.

A Kubernetes pod leírása így néz ki:

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

Vagyis minden titok egy szakaszt ír le. Maguk a titkok így jönnek létre:

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

Ez együttesen YAML fájlok létrehozását eredményezi az útvonal mentén /etc/secrets/db-main/section_name.yaml

A helyi indításoknál pedig a konfigurációt használják, amely a projekt gyökérkönyvtárában vagy a környezeti változóban megadott útvonalon található. Az ezekért a kényelemért felelős kód a spoilerben látható.

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

A logika itt meglehetősen egyszerű: a projektkönyvtárból és a környezeti változók szerinti elérési utakból kombináljuk a nagy konfigurációkat, a Kuber-titkok kis konfigurációs szakaszait pedig egy kicsit elődolgozzuk. Plusz néhány változó. Megjegyzem, amikor a titkokból keresünk fájlokat, mélységi korlátozást alkalmazunk, mivel a K8s minden titokban létrehoz egy rejtett mappát, ahol maguk a titkok vannak tárolva, és csak egy hivatkozás található magasabb szinten.

Remélem, a leírtak hasznosak lesznek valakinek :) Bármilyen észrevételt, javaslatot a biztonsággal vagy egyéb fejlesztendő területekkel kapcsolatban szívesen fogadunk. A közösség véleménye is érdekes, talán érdemes a ConfigMaps támogatását hozzáadni (projektünk még nem használja), és a kódot GitHubon / PyPI-n közzétenni? Személy szerint úgy gondolom, hogy ezek a dolgok túl egyéniek ahhoz, hogy a projektek univerzálisak legyenek, és egy kis bepillantás mások megvalósításaiba, mint amilyen az itt található, és árnyalatok, tippek és legjobb gyakorlatok megbeszélése, amit remélem látni fogok a megjegyzésekben , elég 😉

A felmérésben csak regisztrált felhasználók vehetnek részt. Bejelentkezés, kérem.

Projektként/könyvtárként publikáljak?

  • 0,0%Igen, a /contribution0-t használnám

  • 33,3%Igen, ez jól hangzik 4

  • 41,7%Nem, kinek kell saját formátumában és igényeinek megfelelően megtennie5

  • 25,0%Eltekintek a válaszadástól3

12 felhasználó szavazott. 3 felhasználó tartózkodott.

Forrás: will.com

Hozzászólás