Onlangs schreef ik , waar ik even noemde dat je een eigen configuratiesysteem kunt maken, zodat de service ook goed in Kubernetes werkt, geheimen ophaalt, en lokaal gemakkelijk te starten is, ook volledig buiten Docker. Niet moeilijk, maar het beschreven 'recept' kan voor iemand nuttig zijn š De code is in Python, maar de logica is niet aan de taal gebonden.

De achtergrond van de vraag is als volgt: er was ooit een project, eerst een kleine monolith met hulpprogramma's en scripts, maar na verloop van tijd groeide het, werd het opgedeeld in services, die op hun beurt weer in microservices werden verdeeld, en daarna ook nog opgeschaald. Aanvankelijk gebeurde dit alles op kale VPS'en, waarbij de processen voor het configureren en uitrollen van code waren geautomatiseerd met Ansible, en voor elke service werd een YAML-config gemaakt met de benodigde instellingen en sleutels, en een soortgelijke configuratiebestanden werden gebruikt voor lokale uitvoeringen, wat erg handig was, aangezien deze configuratie in een globaal object laadt dat vanuit elke plaats in het project toegankelijk is.
Echter, de groei van het aantal microservices, hun onderlinge verbindingen, evenals , maakten de verhuizing naar Kubernetes noodzakelijk, die nog steeds gaande is. Naast de hulp bij het oplossen van de genoemde taken, biedt Kubernetes zijn eigen benaderingen voor infrastructuurbeheer, waaronder en . Het mechanisme is standaard en betrouwbaar, dus het is in wezen een gemis om het niet te gebruiken! Maar tegelijkertijd wil je graag je huidige werkformaat met de configuratie behouden: vooreerst uniform gebruik in verschillende microservices van het project, en ten tweede de mogelijkheid om de code op de lokale machine te draaien met behulp van ƩƩn eenvoudig configuratiebestand.
In dit verband is het mechanisme voor het bouwen van het configuratieobject verbeterd, zodat het zowel met ons klassieke configuratiebestand kan werken als met geheimen uit Kubernetes. Ook werd een striktere structuur voor de configuratie opgelegd, om het in de terminologie van Python 3 zo te zeggen:
Dict[str, Dict[str, Union[str, int, float]]]
Dat wil zeggen, de definitieve configuratie is een woordenboek met benoemde secties, waarbij elke sectie een woordenboek is met waarden van eenvoudige types. De secties beschrijven de configuratie en toegang tot middelen van een bepaald type. Een voorbeeld van een deel van onze configuratie:
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"Bij dit alles is het veld engine van de databases kan worden ingesteld op SQLite, terwijl Cachinglogica is meestal niet zo complex, maar het implementeren ervan in EloquentPostQueries is niet echt correct (tenminste vanwege de het kan worden ingesteld op mock, waarbij ook de bestandsnaam voor opslag moet worden opgegeven; deze parameters worden correct herkend en verwerkt, waardoor het gemakkelijk is om de code lokaal uit te voeren voor debugging, unittests en andere doeleinden. Dit is vooral relevant voor ons, omdat er veel andere behoeften zijn ā een deel van onze code is bedoeld voor verschillende analytische berekeningen en wordt niet alleen uitgevoerd op servers met orkestratie, maar ook door verschillende scripts en op de computers van analisten die complexe dataverwerkingspijplijnen moeten uitwerken en debuggen zonder zich zorgen te maken over backendvraagstukken. Trouwens, het is goed om te delen dat onze belangrijkste tools, inclusief de configuratiecode, worden geĆÆnstalleerd via setup.py ā samen vormt dit onze code in een geĆÆntegreerd ecosysteem, onafhankelijk van het platform en de gebruikswijze.
De beschrijving van de pod in Kubernetes ziet er als volgt uit:
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-secretDat wil zeggen, in elk geheim is ƩƩn sectie beschreven. De geheimen zelf worden als volgt aangemaakt:
apiVersion: v1
kind: Secret
metadata:
name: db-main-secret
type: Opaque
stringData:
db_main.yaml: |
engine: sqlite
filename: main.sqlite3Samen leidt dit tot de creatie van YAML-bestanden op het pad /etc/secrets/db-main/section_name.yaml
En voor lokale uitvoeringen wordt de configuratie gebruikt, die zich in de hoofdmap van het project bevindt of op het pad dat is opgegeven in de omgevingsvariabele. De code die verantwoordelijk is voor dit gemak is te zien in de 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()De logica hier is behoorlijk eenvoudig: we combineren grote configuraties uit de projectdirectory en het pad via de omgevingsvariabele, en kleine configuraties-secties uit de Kubernetes-secret, en vervolgens verwerken we ze iets voor. Plus enkele variabelen. Ik merk op dat bij het zoeken naar bestanden uit secrets een dieptebeperking wordt gebruikt, omdat K8s in elke secret een verborgen map aanmaakt waar de secrets zelf worden opgeslagen, en een niveau hoger is er eenvoudig een link.
Ik hoop dat de beschrijving nuttig zal zijn voor iemand š Alle opmerkingen en aanbevelingen over beveiliging of andere verbeterpunten zijn welkom. Ik ben ook benieuwd naar de mening van de gemeenschap, misschien moeten we ondersteuning voor ConfigMaps toevoegen (die worden momenteel in ons project niet gebruikt) en de code op GitHub / PyPI plaatsen? Persoonlijk denk ik dat zulke dingen te individueel zijn voor projecten om universeel te zijn, en dat het voldoende is om af en toe bij andere implementaties te kijken, zoals die hier genoemd, en de nuances, adviezen en best practices die ik hoop te zien in de reacties te bespreken š
Alleen geregistreerde gebruikers kunnen deelnemen aan de enquĆŖte. , alstublieft.
Moet ik het publiceren als een project/bibliotheek?
0,0%Ja, ik zou het gebruiken/proberen bij te dragen.
33,3%Ja, dat klinkt geweldig.
41,7%Nee, wie het nodig heeft zal het zelf in hun eigen formaat en naar hun eigen behoeften maken.
25,0%Ik onthoud me van een antwoord.
Er hebben 12 gebruikers gestemd. 3 gebruikers hebben zich onthouden.
Bron: habr.com
