jeg skrev nylig
Bakgrunnen for spørsmålet er denne: Det var en gang i tiden ett prosjekt, først var det en liten monolitt med verktøy og skript, men over tid vokste det, delt inn i tjenester, som igjen begynte å bli delt inn i mikrotjenester, og deretter oppskalert. Til å begynne med ble alt dette gjort på bare VPS, prosessene med å sette opp og distribuere kode som ble automatisert med Ansible, og hver tjeneste ble kompilert med en YAML-konfigurasjon med nødvendige innstillinger og nøkler, og en lignende konfigurasjonsfil ble brukt for lokale lanseringer, noe som var veldig praktisk, fordi .k denne konfigurasjonen er lastet inn i et globalt objekt, tilgjengelig fra hvor som helst i prosjektet.
Imidlertid er veksten i antall mikrotjenester, deres tilkoblinger og
I denne forbindelse ble mekanismen for å konstruere et konfigurasjonsobjekt modifisert for å kunne fungere både med vår klassiske konfigurasjonsfil og med hemmeligheter fra Kuber. En mer rigid konfigurasjonsstruktur ble også spesifisert, på språket til den tredje Python, som følger:
Dict[str, Dict[str, Union[str, int, flyte]]]
Det vil si at det endelige tannhjulet er en ordbok med navngitte seksjoner, som hver er en ordbok med verdier fra enkle typer. Og seksjoner beskriver konfigurasjonen og tilgangen til ressurser av en bestemt type. Et eksempel på en del av konfigurasjonen vår:
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
satt til mock
, spesifiserer også navnet på filen som skal lagres - disse parameterne gjenkjennes og behandles riktig, noe som gjør det enkelt å kjøre koden lokalt for feilsøking, enhetstesting og andre behov. Dette er spesielt viktig for oss fordi det er mange andre behov - en del av koden vår er ment for ulike analytiske beregninger, den kjører ikke bare på servere med orkestrering, men også med ulike skript, og på datamaskinene til analytikere som trenger å jobbe seg gjennom. og feilsøk komplekse databehandlingsrørledninger uten å bekymre deg for backend-problemer. Forresten, det ville ikke skade å dele at hovedverktøyene våre, inkludert konfigurasjonslayoutkoden, er installert via setup.py
– sammen forener dette koden vår til ett enkelt økosystem, uavhengig av plattform og bruksmåte.
Beskrivelsen av en Kubernetes-pod ser slik ut:
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 si at hver hemmelighet beskriver én seksjon. Selve hemmelighetene er laget slik:
apiVersion: v1
kind: Secret
metadata:
name: db-main-secret
type: Opaque
stringData:
db_main.yaml: |
engine: sqlite
filename: main.sqlite3
Sammen resulterer dette i opprettelsen av YAML-filer langs banen /etc/secrets/db-main/section_name.yaml
Og for lokale lanseringer brukes konfigurasjonen, plassert i rotkatalogen til prosjektet eller langs banen spesifisert i miljøvariabelen. Koden som er ansvarlig for disse bekvemmelighetene, kan sees 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 ganske enkel: vi kombinerer store konfigurasjoner fra prosjektkatalogen og stier etter miljøvariabel, og små konfigurasjonsseksjoner fra Kuber-hemmeligheter, og forbehandler dem deretter litt. Pluss noen variabler. Jeg legger merke til at når du søker etter filer fra hemmeligheter, brukes en dybdebegrensning, fordi K8s lager en skjult mappe i hver hemmelighet hvor selve hemmelighetene er lagret, og bare en lenke er plassert på et høyere nivå.
Jeg håper det som er beskrevet vil være nyttig for noen :) Eventuelle kommentarer og anbefalinger angående sikkerhet eller andre forbedringsområder aksepteres. Fellesskapets mening er også interessant, kanskje det er verdt å legge til støtte for ConfigMaps (prosjektet vårt bruker dem ikke ennå) og publisere koden på GitHub / PyPI? Personlig synes jeg at slike ting er for individuelle til at prosjekter kan være universelle, og en liten titt på andres implementeringer, som den som er gitt her, og en diskusjon av nyanser, tips og beste praksis, som jeg håper å se i kommentarfeltet. , er nok 😉
Kun registrerte brukere kan delta i undersøkelsen.
Bør jeg publisere som et prosjekt/bibliotek?
-
0,0%Ja, jeg ville brukt /contribution0
-
33,3%Ja, det høres bra ut 4
-
41,7%Nei, hvem trenger å gjøre det selv i sitt eget format og for å passe deres behov5
-
25,0%Jeg vil avstå fra å svare3
12 brukere stemte. 3 brukere avsto.
Kilde: www.habr.com