Nedávno som napísal
Pozadie otázky je takéto: kedysi tu bol jeden projekt, najprv to bol malý monolit s pomôckami a skriptami, ale časom sa rozrástol, rozdelil sa na služby, ktoré sa zase začali rozdeľovať na mikroslužby a potom zväčšené. Najprv sa to všetko robilo na holých VPS, procesy nastavenia a nasadenia kódu boli automatizované pomocou Ansible a každá služba bola zostavená s konfiguráciou YAML s potrebnými nastaveniami a kľúčmi a podobný konfiguračný súbor bol použitý na lokálne spúšťanie, čo bolo veľmi pohodlné, pretože .k táto konfigurácia sa načíta do globálneho objektu, ktorý je prístupný odkiaľkoľvek v projekte.
Rast počtu mikroslužieb, ich pripojení a
V tomto smere bol upravený mechanizmus na zostavenie konfiguračného objektu tak, aby dokázal pracovať ako s naším klasickým konfiguračným súborom, tak aj s tajomstvami od Kuberu. V jazyku tretieho Pythonu bola špecifikovaná aj prísnejšia štruktúra konfigurácie, a to takto:
Dict[str, Dict[str, Union[str, int, float]]]
To znamená, že konečný cogfig je slovník s pomenovanými sekciami, z ktorých každá je slovníkom s hodnotami jednoduchých typov. A sekcie popisujú konfiguráciu a prístup k zdrojom určitého typu. Príklad časti našej konfigurácie:
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"
Zároveň pole engine
databázy je možné nainštalovať na SQLite a redis
nastavený na mock
s uvedením názvu súboru, ktorý sa má uložiť - tieto parametre sú správne rozpoznané a spracované, čo uľahčuje lokálne spustenie kódu na ladenie, testovanie jednotiek a akékoľvek iné potreby. Je to pre nás obzvlášť dôležité, pretože je tu mnoho ďalších potrieb – časť nášho kódu je určená na rôzne analytické výpočty, beží nielen na serveroch s orchestráciou, ale aj s rôznymi skriptami a na počítačoch analytikov, ktorí sa potrebujú prepracovať a ladenie zložitých kanálov spracovania údajov bez obávaných problémov s koncovým zariadením. Mimochodom, nebolo by na škodu sa podeliť o to, že naše hlavné nástroje vrátane kódu rozloženia konfigurácie sa inštalujú cez setup.py
– to spolu spája náš kód do jedného ekosystému, nezávislého od platformy a spôsobu použitia.
Popis modulu Kubernetes vyzerá takto:
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
To znamená, že každé tajomstvo opisuje jednu časť. Samotné tajomstvá sú vytvorené takto:
apiVersion: v1
kind: Secret
metadata:
name: db-main-secret
type: Opaque
stringData:
db_main.yaml: |
engine: sqlite
filename: main.sqlite3
Spoločne to vedie k vytvoreniu súborov YAML pozdĺž cesty /etc/secrets/db-main/section_name.yaml
A pre lokálne spúšťanie sa používa konfigurácia, ktorá sa nachádza v koreňovom adresári projektu alebo pozdĺž cesty špecifikovanej v premennej prostredia. Kód zodpovedný za tieto vymoženosti je vidieť v spojleri.
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 celkom jednoduchá: kombinujeme veľké konfigurácie z adresára projektu a ciest podľa premennej prostredia a malé konfiguračné sekcie z tajomstiev Kuber a potom ich trochu predspracujeme. Plus nejaké premenné. Podotýkam, že pri vyhľadávaní súborov z tajomstiev sa používa obmedzenie hĺbky, pretože K8s vytvára v každom tajomstve skrytý priečinok, kde sú uložené samotné tajomstvá a na vyššej úrovni sa nachádza len odkaz.
Dúfam, že to, čo je popísané, bude pre niekoho užitočné :) Akékoľvek pripomienky a odporúčania týkajúce sa bezpečnosti alebo iných oblastí na zlepšenie sú akceptované. Zaujímavý je aj názor komunity, možno stojí za to pridať podporu pre ConfigMaps (náš projekt ich zatiaľ nepoužíva) a zverejniť kód na GitHub / PyPI? Osobne si myslím, že takéto veci sú príliš individuálne na to, aby boli projekty univerzálne, a trochu nahliadnuť do implementácií iných ľudí, ako je tu uvedený, a diskusia o nuansách, tipoch a osvedčených postupoch, ktoré dúfam uvidím v komentároch , stačí 😉
Do prieskumu sa môžu zapojiť iba registrovaní užívatelia.
Mám publikovať ako projekt/knižnica?
-
0,0%Áno, použil by som /contribution0
-
33,3%Áno, to znie skvele 4
-
41,7%Nie, kto to potrebuje urobiť sám vo svojom vlastnom formáte a podľa svojich potrieb5
-
25,0%Zdržím sa odpovede 3
Hlasovalo 12 užívateľov. 3 používatelia sa zdržali hlasovania.
Zdroj: hab.com