Fa poc vaig escriure
El rerefons de la pregunta és el següent: hi havia una vegada un projecte, al principi era un petit monòlit amb utilitats i scripts, però amb el temps va anar creixent, dividit en serveis, que al seu torn es van començar a dividir en microserveis i després s'ha ampliat. Al principi, tot això es va fer en VPS nu, els processos de configuració i desplegament de codi en els quals es van automatitzar mitjançant Ansible, i cada servei es va compilar amb una configuració YAML amb la configuració i les claus necessàries, i es va utilitzar un fitxer de configuració similar per a llançaments locals, que era molt convenient, perquè .k aquesta configuració es carrega en un objecte global, accessible des de qualsevol lloc del projecte.
Tanmateix, el creixement del nombre de microserveis, les seves connexions i
En aquest sentit, es va modificar el mecanisme per construir un objecte de configuració per poder funcionar tant amb el nostre fitxer de configuració clàssic com amb secrets de Kuber. També es va especificar una estructura de configuració més rígida, en el llenguatge del tercer Python, de la següent manera:
Dict[str, Dict[str, Unió[str, int, float]]]
És a dir, el cogfig final és un diccionari amb seccions anomenades, cadascuna de les quals és un diccionari amb valors de tipus simples. I les seccions descriuen la configuració i l'accés a recursos d'un determinat tipus. Un exemple d'una peça de la nostra configuració:
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"
Al mateix temps, el camp engine
les bases de dades es poden instal·lar a SQLite i redis
ajustat a mock
, especificant també el nom del fitxer a desar: aquests paràmetres es reconeixen i es processen correctament, cosa que facilita l'execució del codi localment per a la depuració, les proves d'unitat i qualsevol altra necessitat. Això és especialment important per a nosaltres perquè hi ha moltes altres necessitats: part del nostre codi està pensat per a diversos càlculs analítics, no només s'executa en servidors amb orquestració, sinó també amb diversos scripts i en els ordinadors dels analistes que necessiten treballar-hi. i depureu canalitzacions complexes de processament de dades sense preocupar-vos de problemes de backend. Per cert, no estaria malament compartir que les nostres eines principals, inclòs el codi de disseny de configuració, s'instal·len mitjançant setup.py
– junts, això uneix el nostre codi en un únic ecosistema, independent de la plataforma i el mètode d'ús.
La descripció d'un pod de Kubernetes és així:
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
És a dir, cada secret descriu una secció. Els propis secrets es creen així:
apiVersion: v1
kind: Secret
metadata:
name: db-main-secret
type: Opaque
stringData:
db_main.yaml: |
engine: sqlite
filename: main.sqlite3
En conjunt, això resulta en la creació de fitxers YAML al llarg del camí /etc/secrets/db-main/section_name.yaml
I per als llançaments locals, s'utilitza la configuració, situada al directori arrel del projecte o al llarg del camí especificat a la variable d'entorn. El codi responsable d'aquestes comoditats es pot veure al spoiler.
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()
La lògica aquí és bastant senzilla: combinem configuracions grans del directori del projecte i camins per variable d'entorn, i petites seccions de configuració dels secrets de Kuber, i després les preprocessem una mica. A més d'algunes variables. Tinc en compte que quan es busquen fitxers a partir de secrets, s'utilitza una limitació de profunditat, perquè K8s crea una carpeta oculta a cada secret on s'emmagatzemen els propis secrets i només es troba un enllaç a un nivell superior.
Espero que el que es descriu sigui útil a algú :) S'accepten tots els comentaris i recomanacions sobre seguretat o altres àrees de millora. L'opinió de la comunitat també és interessant, potser val la pena afegir suport per a ConfigMaps (el nostre projecte encara no els utilitza) i publicar el codi a GitHub / PyPI? Personalment, crec que aquestes coses són massa individuals perquè els projectes siguin universals, i una mica d'ull a les implementacions d'altres persones, com la que es mostra aquí, i una discussió de matisos, consells i bones pràctiques, que espero veure en els comentaris. , n'hi ha prou 😉
Només els usuaris registrats poden participar en l'enquesta.
He de publicar com a projecte/biblioteca?
-
0,0%Sí, faria servir /contribution0
-
33,3%Sí, sona genial4
-
41,7%No, qui ha de fer-ho ells mateixos en el seu propi format i segons les seves necessitats5
-
25,0%M'abstindré de respondre3
Han votat 12 usuaris. 3 usuaris es van abstenir.
Font: www.habr.com