Hiljuti kirjutasin , kus mainisin korra, et on võimalik luua oma konfiguratsioonisüsteem, et teenus töötaks hästi ka Kubernetesis, tõmbaks saladusi ja käivitataks mugavalt ka lokaalselt, sealhulgas täiesti väljaspool Dockeri. Midagi keerulist ei ole, aga kirjeldatud "retsept" võib kellelegi abiks olla 🙂 Kood on Pythonis, kuid loogika ei ole keelele omane.

Küsimuse taust on järgmine: elas-oli üks projekt, alguses oli see väike monoliit utiliitide ja skriptidega, kuid aja jooksul kasvas, jagunes teenusteks, mis omakorda hakkasid jagunema mikroteenusteks ja seejärel veel ka skaleeruma. Alguses tehti see kõik tühjades VPS-des, nende seadistamise ja koodi juurutamise protsessid olid automatiseeritud Ansible'i abil ning igale teenusele koostati YAML-konfiguratsioon vajalike seadistuste ja võtmetega, ja sarnast konfigureerimisfaili kasutati lokaalseteks käivitamisteks, mis oli väga mugav, kuna see konfigureerimisfail laaditi globaalsetesse objektidesse, mis on saadaval igas projekti kohas.
Kuid mikroteenuste arvu, nende omavaheliste seoste ja samuti , eeldasid kolimist Kubernetesse, mis on endiselt protsessis. Kui abistatakse nende ülesannete lahendamisel, pakub Kubernetes ka oma lähenemisviise infrastruktuuri haldamiseks, sealhulgas ja . Mehhanism on standardselt usaldusväärne, seega on tõeliselt patt seda mitte kasutada! Kuid samas tahaks säilitada oma praeguse töövormi konfiguratsiooni osas: esiteks, ühtlaselt kasutada seda erinevates projekti mikroteenustes, ja teiseks, saada käivitada koodi kohaliku masina peal, kasutades ühte lihtsat konfigureerimisfaili.
Seetõttu oli objekti konfiguratsiooni mehhanismi kohandatud nii, et see suudaks töötada nii meie klassikalise konfigureerimisfailiga kui ka Kubernetist saadud saladustega. Samuti kehtestati rangem konfiguratsiooni struktuur, rääkides kolmandast Pythonist, selline:
Dict[str, Dict[str, Union[str, int, float]]]
See tähendab, et lõplik konfiguratsioon on sõnastik nimetatud sektsioonidega, millest igaüks on sõnastik lihtsate tüüpide väärtustega. Ja sektsioonid kirjeldavad konfiguratsiooni ja juurdepääsu teatud tüüpi ressurssidele. Näide meie konfiguratsiooni osast:
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: info@test.com
pw: "SuperHardPassword"Sel juhul väli engine andmebaasi saab seadistada SQLite'le, kuid redis konfigureerida mock, määrates ka salvestamiseks faili nime – need parameetrid tuvastatakse ja töödeldakse õigesti, mis võimaldab koodi lihtsalt kohapeal käivitada, et debugida, teha üksusteste ja muid vajadusi. See on meile eriti oluline, kuna neid muid vajadusi on palju – osa meie koodist on mõeldud mitmesuguste analüütiliste arvutuste tegemiseks, seda käivitatakse mitte ainult orkestreerimisega serverites, vaid ka erinevate skriptide ja analüütikute arvutites, kes peavad tegelema ja debugima keerulisi andmete töötlemise torujuhtmeid, mitte muretsema backend-küsimustele. Üks huvitav asi, mida jagada, on see, et meie peamised tööriistad, sealhulgas konfi-kodeerimise kood, paigaldatakse läbi setup.py – need ühtlustavad meie koodi üheks ökosüsteemiks, mis ei sõltu platvormist ja kasutusviisist.
Kuberneteses on poda kirjeldus järgmine:
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-secretSee tähendab, et iga saladuse sees on kirjas üks sektsioon. Saladusi luuakse nii:
apiVersion: v1
kind: Secret
metadata:
name: db-main-secret
type: Opaque
stringData:
db_main.yaml: |
engine: sqlite
filename: main.sqlite3See kõik viib XML-failide loomisele teel /etc/secrets/db-main/section_name.yaml
Ja kohalike käivituste jaoks kasutatakse konfi, mis asub projekti juurkataloogis või teel, mis on määratud keskkonnamuutuja kaudu. Kood, mis sellele mugavusele vastab, on nähtav spoileris.
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()Loogika on siin üsna lihtne: koondame suurte konfiguratsioonifailide sealt, kus on projekti direktor, ja teelt keskkonnamuutuja kaudu, ning väikeste sektsioonide konfiguratsioonid Kuberneteisest, seejärel töötleme neid natuke ette. Lisaks mõned muutujad. Tahan märkida, et salajaste failide otsimisel kasutatakse sügavuse piirangut, kuna K8s loob igas salas veel ühe peidetud kausta, kus saladused ise asuvad, ja ühe taseme võrra ülespoole asub lihtsalt link.
Loodan, et antud teave on kellelegi kasulik 🙂 Ootame igasuguseid kommentaare ja soovitusi turvalisuse või muude parenduste osas. Samuti huvitab, mida arvab kogukond, kas võiks lisada ConfigMapide toe (meie projektis need hetkel ei ole kasutuses) ja kas koodi tuleks vormistada GitHubis / PyPI-s? Isiklikult arvan, et sellised asjad on projektide jaoks liiga individuaalsed, et olla universaalsed, ja piisab, kui heita pilk teiste elluviimisele, nagu siin toodud, ning arutada nüansse, soovitusi ja parimaid praktikaid, mida loodan näha kommentaarides 😉
Ainult registreeritud kasutajad saavad küsitluses osaleda. , palun.
Kas tasub avaldada projektina / teegina?
0,0%Jah, ma kasutaksin / panustaksin
33,3%Jah, see kõlab hästi
41,7%Ei, kellel on vajadus, teeb ise oma formaadis ja endale sobivalt
25,0%Pean vastamata jääma
Hääletas 12 kasutajat. 3 kasutajat hoidusid.
Allikas: habr.com
