Projekto konfigūracija Kubernetes viduje ir išorėje

Neseniai rašiau atsakymas apie projekto gyvenimą „Docker“ ir kodo derinimo už jos ribų, kur jis trumpai paminėjo, kad galite susikurti savo konfigūravimo sistemą, kad paslauga gerai veiktų Kubere, ištrauktų paslaptis ir patogiai veiktų vietoje, net visai ne Docker. Nieko sudėtingo, bet aprašytas “receptas” gal kam nors pravers :) Kodas Python, bet logika nesusieta su kalba.

Projekto konfigūracija Kubernetes viduje ir išorėje

Klausimo pagrindas yra toks: kažkada buvo vienas projektas, iš pradžių tai buvo nedidelis monolitas su komunalinėmis paslaugomis ir scenarijais, bet laikui bėgant jis išaugo, dalijo į paslaugas, kurios savo ruožtu pradėjo skirstyti į mikropaslaugas ir tada padidintas. Iš pradžių visa tai buvo daroma naudojant tuščią VPS, kurio kodo nustatymo ir diegimo procesai buvo automatizuoti naudojant Ansible, o kiekviena paslauga buvo sudaryta naudojant YAML konfigūraciją su reikalingais nustatymais ir raktais, o panašus konfigūracijos failas buvo naudojamas lokalūs paleidimai, o tai buvo labai patogu, nes .k ši konfigūracija įkeliama į globalų objektą, pasiekiamą iš bet kurios projekto vietos.

Tačiau mikropaslaugų, jų jungčių skaičiaus augimas ir centralizuoto registravimo ir stebėjimo poreikis, numatė perėjimą prie Kuber, kuris vis dar vyksta. Kartu su pagalba sprendžiant minėtas problemas, Kubernetes siūlo savo infrastruktūros valdymo metodus, įskaitant vadinamosios paslaptys и būdų, kaip su jais dirbti. Mechanizmas yra standartinis ir patikimas, todėl tiesiog nuodėmė jo nenaudoti! Tačiau tuo pat metu norėčiau išlaikyti dabartinį darbo su konfigūravimo formatą: pirma, vienodai jį naudoti įvairiose projekto mikroservisuose ir, antra, turėti galimybę paleisti kodą vietiniame kompiuteryje naudojant vieną paprastą konfigūracijos failą.

Šiuo atžvilgiu konfigūracijos objekto kūrimo mechanizmas buvo pakeistas, kad būtų galima dirbti tiek su mūsų klasikiniu konfigūracijos failu, tiek su Kuber paslaptimis. Taip pat buvo nurodyta griežtesnė konfigūracijos struktūra trečiojo Python kalba:

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

Tai reiškia, kad galutinis krumpliaratis yra žodynas su pavadintais skyriais, kurių kiekvienas yra žodynas su paprastų tipų reikšmėmis. O skyriuose aprašoma tam tikro tipo išteklių konfigūracija ir prieiga prie jų. Mūsų konfigūracijos dalies pavyzdys:

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"

Tuo pačiu ir laukas engine duomenų bazes galima įdiegti SQLite ir redis nustatytas į mock, nurodant ir failo, kurį norite išsaugoti, pavadinimą – šie parametrai yra teisingai atpažįstami ir apdorojami, todėl kodą lengva paleisti vietoje, kad būtų galima derinti, išbandyti vienetus ir bet kokius kitus poreikius. Mums tai ypač svarbu, nes yra daug kitų poreikių – dalis mūsų kodo skirta įvairiems analitiniams skaičiavimams, veikia ne tik serveriuose su orkestravimu, bet ir su įvairiais scenarijais, ir analitikų kompiuteriuose, kuriems reikia dirbti. ir derinti sudėtingus duomenų apdorojimo vamzdynus, nesukeldami rūpesčių dėl foninių problemų. Beje, nepakenktų pasidalinti, kad pagrindiniai mūsų įrankiai, įskaitant konfigūracijos išdėstymo kodą, yra įdiegti per setup.py – kartu tai sujungia mūsų kodą į vieną ekosistemą, nepriklausomą nuo platformos ir naudojimo būdo.

Kubernetes ankšties aprašymas atrodo taip:

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

Tai yra, kiekviena paslaptis apibūdina vieną skyrių. Pačios paslaptys sukuriamos taip:

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

Kartu tai sukuria YAML failus kelyje /etc/secrets/db-main/section_name.yaml

O vietiniams paleidimams naudojama konfigūracija, esanti projekto šakniniame kataloge arba aplinkos kintamajame nurodytu keliu. Už šiuos patogumus atsakingą kodą galima pamatyti spoileryje.

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 čia gana paprasta: sujungiame dideles konfigūracijas iš projekto katalogo ir kelius pagal aplinkos kintamąjį, o mažas konfigūracijos sekcijas iš Kuber paslapčių, tada šiek tiek iš anksto apdorojame jas. Plius kai kurie kintamieji. Pastebiu, kad ieškant failų iš paslapčių, naudojamas gylio apribojimas, nes K8s kiekvienoje paslaptyje sukuria paslėptą aplanką, kuriame saugomos pačios paslaptys, o tik nuoroda yra aukštesniame lygyje.

Tikiuosi, kas aprašyta bus kam nors naudinga :) Priimamos bet kokios pastabos ir rekomendacijos dėl saugumo ar kitų tobulinimo sričių. Bendruomenės nuomonė taip pat įdomi, galbūt verta pridėti ConfigMaps palaikymą (mūsų projektas jų dar nenaudoja) ir paskelbti kodą GitHub / PyPI? Asmeniškai manau, kad tokie dalykai yra per daug individualūs, kad projektai būtų universalūs, ir šiek tiek žvilgtelėjimas į kitų žmonių įgyvendinimus, kaip čia pateiktas, ir niuansų, patarimų ir geriausios praktikos aptarimas, kurį tikiuosi pamatyti komentaruose , užtenka 😉

Apklausoje gali dalyvauti tik registruoti vartotojai. Prisijungti, Prašau.

Ar turėčiau skelbti kaip projektą / biblioteką?

  • 0,0%Taip, naudočiau /contribution0

  • 33,3%Taip, tai skamba puikiai 4

  • 41,7%Ne, kas turi tai padaryti patiems savo formatu ir pagal savo poreikius5

  • 25,0%Susilaikysiu nuo atsakymo3

Balsavo 12 vartotojų. 3 vartotojai susilaikė.

Šaltinis: www.habr.com

Добавить комментарий