Projekkonfigurasie binne en buite Kubernetes

Ek het onlangs geskryf antwoord oor projeklewe in Docker en ontfoutingskode daarbuite, waar hy kortliks genoem het dat jy jou eie konfigurasiestelsel kan maak sodat die diens goed in Kuber werk, geheime optel en gerieflik plaaslik loop, selfs buite Docker heeltemal. Niks ingewikkeld nie, maar die beskryfde "resep" kan vir iemand nuttig wees :) Die kode is in Python, maar die logika is nie gekoppel aan die taal nie.

Projekkonfigurasie binne en buite Kubernetes

Die agtergrond van die vraag is dit: eens op 'n tyd was daar een projek, eers was dit 'n klein monoliet met nutsprogramme en skrifte, maar met verloop van tyd het dit gegroei, in dienste verdeel, wat weer in mikrodienste verdeel is, en toe opgeskaal. Aanvanklik is dit alles op blote VPS gedoen, die prosesse van die opstel en ontplooiing van kode waarop geoutomatiseer is met behulp van Ansible, en elke diens is saamgestel met 'n YAML-konfigurasie met die nodige instellings en sleutels, en 'n soortgelyke konfigurasielêer is gebruik vir plaaslike bekendstellings, wat baie gerieflik was, want .k hierdie konfigurasie is gelaai in 'n globale voorwerp, toeganklik vanaf enige plek in die projek.

Die groei in die aantal mikrodienste, hul verbindings, en behoefte aan gesentraliseerde aantekening en monitering, het 'n skuif na Kuber voorspel, wat steeds aan die gang is. Saam met hulp om die genoemde probleme op te los, bied Kubernetes sy benaderings tot infrastruktuurbestuur, insluitend sogenaamde geheime и maniere om met hulle te werk. Die meganisme is standaard en betroubaar, so dit is letterlik 'n sonde om dit nie te gebruik nie! Maar terselfdertyd wil ek graag my huidige formaat behou om met die konfigurasie te werk: eerstens, om dit eenvormig in verskillende mikrodienste van die projek te gebruik, en tweedens, om die kode op die plaaslike masjien te kan laat loop met een eenvoudige config lêer.

In hierdie verband is die meganisme vir die bou van 'n konfigurasie-objek gewysig om beide met ons klassieke konfigurasielêer en met geheime van Kuber te kan werk. 'n Meer rigiede konfigurasiestruktuur is ook gespesifiseer, in die taal van die derde Python, soos volg:

Dict[str, Dict[str, Unie[str, int, float]]]

Dit wil sê, die finale koggel is 'n woordeboek met genoemde afdelings, wat elkeen 'n woordeboek is met waardes van eenvoudige tipes. En afdelings beskryf die konfigurasie en toegang tot hulpbronne van 'n sekere soort. 'n Voorbeeld van 'n stukkie van ons konfigurasie:

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"

Terselfdertyd, die veld engine databasisse kan op SQLite geïnstalleer word, en redis stel na mock, en spesifiseer ook die naam van die lêer om te stoor - hierdie parameters word korrek herken en verwerk, wat dit maklik maak om die kode plaaslik te laat loop vir ontfouting, eenheidstoetsing en enige ander behoeftes. Dit is veral belangrik vir ons omdat daar baie ander behoeftes is - 'n deel van ons kode is bedoel vir verskeie analitiese berekeninge, dit loop nie net op bedieners met orkestrasie nie, maar ook met verskeie skrifte, en op die rekenaars van ontleders wat moet deurwerk. en ontfout komplekse dataverwerkingspyplyne sonder om agterkopprobleme te bekommer. Terloops, dit sal nie skade doen om te deel dat ons hoofnutsmiddels, insluitend die konfigurasie-uitlegkode, via setup.py – saam verenig dit ons kode in 'n enkele ekosisteem, onafhanklik van platform en metode van gebruik.

Die beskrywing van 'n Kubernetes-peul lyk soos volg:

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

Dit wil sê, elke geheim beskryf een afdeling. Die geheime self word so geskep:

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

Saam lei dit tot die skepping van YAML-lêers langs die pad /etc/secrets/db-main/section_name.yaml

En vir plaaslike bekendstellings word die config gebruik, geleë in die wortelgids van die projek of langs die pad gespesifiseer in die omgewingsveranderlike. Die kode wat vir hierdie geriewe verantwoordelik is, kan in die bederf gesien word.

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

Die logika hier is redelik eenvoudig: ons kombineer groot konfigurasies uit die projekgids en paaie volgens omgewingsveranderlike, en klein konfigurasieafdelings van Kuber-geheime, en verwerk dit dan 'n bietjie vooraf. Plus 'n paar veranderlikes. Ek let daarop dat wanneer daar na lêers van geheime gesoek word, 'n dieptebeperking gebruik word, want K8s skep 'n versteekte vouer in elke geheim waar die geheime self gestoor word, en net 'n skakel is op 'n hoër vlak geleë.

Ek hoop wat beskryf word sal nuttig wees vir iemand :) Enige kommentaar en aanbevelings rakende sekuriteit of ander areas vir verbetering word aanvaar. Die gemeenskap se mening is ook interessant, miskien is dit die moeite werd om ondersteuning vir ConfigMaps by te voeg (ons projek gebruik dit nog nie) en om die kode op GitHub / PyPI te publiseer? Persoonlik dink ek dat sulke dinge te individueel is vir projekte om universeel te wees, en 'n bietjie loer na ander mense se implementerings, soos die een wat hier gegee word, en 'n bespreking van nuanses, wenke en beste praktyke, wat ek hoop om in die kommentaar te sien. , is genoeg 😉

Slegs geregistreerde gebruikers kan aan die opname deelneem. Meld aan, asseblief.

Moet ek as 'n projek/biblioteek publiseer?

  • 0,0%Ja, ek sal /contribution0 gebruik

  • 33,3%Ja, dit klink wonderlik4

  • 41,7%Nee, wie moet dit self doen in hul eie formaat en om by hul behoeftes te pas5

  • 25,0%Ek sal my daarvan weerhou om te antwoord3

12 gebruikers het gestem. 3 gebruikers het buite stemming gebly.

Bron: will.com

Voeg 'n opmerking