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

Nemrég írtam választ a projekt életéről Dockerekkel és a kód hibakereséséről azon kívül, ahol említést nyert, hogy lehet létrehozni egy saját konfiguráló rendszert, hogy a szolgáltatás jól működjön Kubernetesen is, lehúzza a titkokat, és helyben kényelmesen elinduljon, beleértve azt is, hogy teljesen a Docker nélkül. Nem bonyolult, de a leírt "recept" hasznos lehet valakinek 🙂 A kód Pythonban van, de a logika nem kötődik a nyelvhez.

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

A kérdés előzménye a következő: volt egyszer egy projekt, kezdetben egy kis monolit volt segédprogramokkal és szkriptekkel, de idővel nőtt, szolgáltatásokra oszlott, amelyek viszont mikroszolgáltatásokra váltak, majd még skálázták is. Eleinte mindez csupasz VPS-eken futott, a kód beállításának és telepítésének folyamatai automatizálva lettek Ansible segítségével, és minden szolgáltatáshoz elkészült egy YAML konfiguráció a szükséges beállításokkal és kulcsokkal, és hasonló konfigurációs fájlt használtunk a helyi futtatásokhoz, ami nagyon kényelmes volt, mert ez a konfiguráció betöltődött egy globális objektumba, amely bárhonnan elérhető a projektben.

Azonban a mikroszolgáltatások számának növekedése, a közöttük fennálló kapcsolatok, valamint a központosított naplózás és monitorozás iránti igény, a Kubernetesbe való migrációt jelezte, ami még mindig folyamatban van. A fenti feladatok megoldásának segítségével a Kubernetes a saját megközelítéseit kínálja az infrastruktúra kezelésére, beleértve az úgynevezett Titkokat és és a velük való munkavégzés módjait. A mechanizmus standard és megbízható, ezért szinte bűn nem használni! De közben szeretném megőrizni a jelenlegi munkafolyamatomat a konfigurációval: egyrészt egységesen használni különböző mikroszolgáltatásokban a projektben, másrészt pedig lehetőséget szeretnék kapni a kód helyi gépen történő futtatására egy egyszerű konfigurációs fájllal.

Ezen a téren a konfiguráló objektum megépítési mechanizmusát úgy módosítottuk, hogy képes legyen együttműködni a klasszikus konfigurációs fájunkkal, valamint a Kubernetes titkaival is. Továbbá szigorúbb konfigurációs struktúrát állítottunk be, harmadik Python nyelvi kifejezéssel élve, így:

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

Tehát a végső konfiguráció egy névvel ellátott szekciókat tartalmazó szótár, ahol minden egyes szekció egy szótár az egyszerű típusú értékekből. A szekciók a konfigurációt és a hozzáféréseket írják le bizonyos típusú erőforrásokhoz. Íme egy részlet a konfigurációnkból:

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"

A mező motor adatbázis SQLite-ra telepíthető, míg redis beállítható mock, megadva a mentéshez szükséges fájlnevet – ezek a paraméterek megfelelően felismerésre és feldolgozásra kerülnek, ami lehetővé teszi, hogy a kódot egyszerűen futtassuk helyben hibakeresés, egységtesztelés és egyéb célokra. Ez különösen fontos számunkra, mivel sok más célunk van – kódunk egy része különféle elemzési számításokra van tervezve, és nemcsak orchestration-alapú szervereken fut, hanem különböző szkriptek alatt, és az elemzők számítógépein, akiknek bonyolult adatfeldolgozó csatornákat kell kidolgozniuk és tesztelniük anélkül, hogy a backend kérdésekkel kellene foglalkozniuk. Érdemes megemlíteni azt is, hogy fő eszközeink, beleértve a konfigurációs fájl összeállításához szükséges kódot, a setup.py – ez együtt hozza össze a kódunkat egy egységes ökoszisztémába, amely független a platformtól és a használati módtól.

A Kubernetes-ben az 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

Tehát minden titokban egy szekció leírása található. A titkok létrehozása így történik:

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

Ez pedig YAML-fájlok létrehozásához vezet az alábbi útvonalon /etc/secrets/db-main/section_name.yaml

Helyi indításokhoz a projekt gyökérkönyvtárában vagy a környezeti változóban megadott úton található konfigurációt használják. Azok a kódok, amelyek ezekért a kényelmi funkciókért felelősek, a spoilerben láthatóak.

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 projekt könyvtárából és a környezeti változóból származó nagy konfigurációkat, valamint a Kuber titkaiból származó kis konfigurációs szekciókat egyesítjük, majd egy kicsit előfeldolgozzuk őket. Emellett néhány változó. Megjegyzem, hogy a titkokból származó fájlok keresésekor mélységi korlátozást alkalmazunk, mivel a K8s minden titkon belül létrehoz egy rejtett mappát, ahol a titkokat tárolják, míg egy szinttel feljebb egyszerűen csak egy hivatkozás található.

Remélem, amit leírtam, valakinek hasznos lesz 🙂 Minden véleményt és ajánlást szívesen fogadok a biztonságról vagy más fejlesztési lehetőségekről. Érdekelne a közösség véleménye is, talán érdemes lenne támogatni a ConfigMap-eket (a projektünkben ez még nem használatos), és a kódot GitHubon / PyPI-n publikálni? Személy szerint úgy gondolom, hogy ezek a dolgok túl egyediak a projektekhez, hogy általánosak legyenek, és elég annyi, hogy egy kicsit leselkedjünk a mások megvalósításaira, mint amit itt bemutattam, és megvitassuk a részleteket, tanácsokat és bevált gyakorlatokat, amit remélek, hogy a hozzászólásokban látni fogok 😉

Csak a regisztrált felhasználók vehetnek részt a szavazáson. Kérem, jelentkezzen be., kérem.

Érdemes lenne projektként / könyvtárként publikálni?

  • 0,0%Igen, használnám / hozzájárulnék.

  • 33,3%Igen, jól hangzik.

  • 41,7%Nem, aki akarja, az saját formátumban és igényeiknek megfelelően megcsinálja.

  • 25,0%Tartózkodom a válaszadástól.

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

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster