jeg skrev for nylig
Baggrunden for spørgsmålet er som følger: der var engang et projekt, først var det en lille monolit med hjælpeprogrammer og scripts, men med tiden voksede det, opdelt i tjenester, som igen begyndte at blive opdelt i mikrotjenester, og så også skaleret. Til at begynde med blev det hele gjort på bare VPS, processerne for opsætning og implementering af koden, som blev automatiseret ved hjælp af Ansible, og en YAML-konfiguration blev kompileret for hver tjeneste med de nødvendige indstillinger og nøgler, og en lignende konfigurationsfil blev brugt til lokale lanceringer, hvilket var meget bekvemt, t .til denne konfiguration indlæses i et globalt objekt, tilgængeligt fra hvor som helst i projektet.
Men væksten i antallet af mikrotjenester, deres forbindelser, samt
I denne henseende er mekanismen til opbygning af konfigurationsobjektet blevet forbedret på en sådan måde, at den kan arbejde både med vores klassiske konfigurationsfil og med hemmeligheder fra Kuber. En mere stiv konfigurationsstruktur blev også indstillet, på sproget i den tredje Python, som dette:
Dict[str, Dict[str, Union[str, int, float]]]
Det vil sige, at den endelige konfiguration er en ordbog med navngivne sektioner, som hver er en ordbog med værdier fra simple typer. Og afsnit beskriver konfigurationen og adgangen til ressourcer af en bestemt type. Et eksempel på et stykke af vores konfiguration:
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"
Samtidig feltet engine
databaser kan installeres på SQLite, og redis
tune ind mock
, der også angiver navnet på filen, der skal gemmes - disse parametre genkendes og behandles korrekt, hvilket gør det nemt at køre koden lokalt til fejlfinding, enhedstest og andre behov. Dette gælder især for os, fordi der er mange andre behov - en del af vores kode er designet til forskellige analytiske beregninger, den kører ikke kun på servere med orkestrering, men også på forskellige scripts og på computere til analytikere, der skal træne og debug komplekse databehandlingspipelines uden at bekymre sig om backend-spørgsmål. Det ville i øvrigt ikke være overflødigt at dele, at vores hovedværktøjer, inklusive konfigurationslinkkoden, er installeret via setup.py
– tilsammen forener dette vores kode i et enkelt økosystem, der er uafhængigt af platformen og den måde, den bruges på.
Beskrivelsen af en pod i Kubernetes ser sådan ud:
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
Det vil sige, at der er beskrevet et afsnit i hver hemmelighed. Selve hemmelighederne er skabt sådan:
apiVersion: v1
kind: Secret
metadata:
name: db-main-secret
type: Opaque
stringData:
db_main.yaml: |
engine: sqlite
filename: main.sqlite3
Tilsammen resulterer dette i oprettelsen af YAML-filer langs stien /etc/secrets/db-main/section_name.yaml
Og til lokale lanceringer bruges konfigurationen, placeret i projektets rodbibliotek eller langs stien angivet i miljøvariablen. Koden, der er ansvarlig for disse bekvemmeligheder, kan ses i spoileren.
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()
Logikken her er ret simpel: vi kombinerer store konfigurationer fra projektbiblioteket og sti efter miljøvariabel, og små konfigurationssektioner fra Kubers hemmeligheder, og så forbehandler vi dem lidt. Plus nogle variabler. Jeg bemærker, at når man søger efter filer fra hemmeligheder, bruges en dybdegrænse, fordi K8s opretter endnu en skjult mappe i hver hemmelighed, hvor selve hemmelighederne er gemt, og blot et link er placeret et niveau højere.
Jeg håber, at det beskrevne vil være nyttigt for nogen 🙂 Eventuelle kommentarer og anbefalinger vedrørende sikkerhed eller andre punkter til forbedringer accepteres. Fællesskabets mening er også interessant, måske er det værd at tilføje support til ConfigMaps (de er ikke brugt i vores projekt endnu) og indsende koden på GitHub / PyPI? Personligt synes jeg, at sådanne ting er for individuelle til, at projekter kan være universelle, og kigger lidt på andres implementeringer, som den her, og diskussionen om nuancer, tips og bedste praksis, som jeg håber at se i kommentarerne , er nok 😉
Kun registrerede brugere kan deltage i undersøgelsen.
Skal jeg udgive som et projekt/bibliotek?
-
0,0 %Ja, jeg ville bruge /contributyl0
-
33,3 %Ja, det lyder godt
-
41,7 %Nej, hvem skal selv gøre det i deres eget format og for at passe til deres behov5
-
25,0 %Undlad at svare 3
12 brugere stemte. 3 brugere undlod at stemme.
Kilde: www.habr.com