Έγραψα πρόσφατα
Το υπόβαθρο της ερώτησης είναι το εξής: κάποτε υπήρχε ένα έργο, στην αρχή ήταν ένας μικρός μονόλιθος με βοηθητικά προγράμματα και σενάρια, αλλά με την πάροδο του χρόνου μεγάλωσε, χωρίστηκε σε υπηρεσίες, οι οποίες με τη σειρά τους άρχισαν να χωρίζονται σε μικροϋπηρεσίες και στη συνέχεια κλιμακώθηκε. Αρχικά, όλα αυτά γίνονταν σε γυμνά VPS, οι διαδικασίες ρύθμισης και ανάπτυξης κώδικα στους οποίους αυτοματοποιήθηκαν χρησιμοποιώντας το Ansible και κάθε υπηρεσία μεταγλωττίστηκε με μια διαμόρφωση YAML με τις απαραίτητες ρυθμίσεις και κλειδιά και ένα παρόμοιο αρχείο διαμόρφωσης χρησιμοποιήθηκε για τοπικές εκκινήσεις, κάτι που ήταν πολύ βολικό, επειδή .k αυτή η διαμόρφωση φορτώνεται σε ένα καθολικό αντικείμενο, προσβάσιμο από οπουδήποτε στο έργο.
Ωστόσο, η αύξηση του αριθμού των μικροϋπηρεσιών, των συνδέσεών τους και
Από αυτή την άποψη, ο μηχανισμός για την κατασκευή ενός αντικειμένου διαμόρφωσης τροποποιήθηκε ώστε να μπορεί να λειτουργεί τόσο με το κλασικό αρχείο ρυθμίσεων όσο και με μυστικά από τον Kuber. Μια πιο άκαμπτη δομή διαμόρφωσης καθορίστηκε επίσης, στη γλώσσα της τρίτης Python, ως εξής:
Dict[str, Dict[str, Union[str, int, float]]]
Δηλαδή, το τελικό cogfig είναι ένα λεξικό με επώνυμες ενότητες, καθεμία από τις οποίες είναι ένα λεξικό με τιμές από απλούς τύπους. Και οι ενότητες περιγράφουν τη διαμόρφωση και την πρόσβαση σε πόρους συγκεκριμένου τύπου. Ένα παράδειγμα ενός κομματιού της διαμόρφωσης μας:
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"
Παράλληλα το χωράφι engine
Οι βάσεις δεδομένων μπορούν να εγκατασταθούν στο SQLite και redis
οριστεί σε mock
, καθορίζοντας επίσης το όνομα του αρχείου προς αποθήκευση - αυτές οι παράμετροι αναγνωρίζονται και επεξεργάζονται σωστά, γεγονός που καθιστά εύκολη την εκτέλεση του κώδικα τοπικά για εντοπισμό σφαλμάτων, δοκιμή μονάδας και οποιεσδήποτε άλλες ανάγκες. Αυτό είναι ιδιαίτερα σημαντικό για εμάς γιατί υπάρχουν πολλές άλλες ανάγκες - μέρος του κώδικά μας προορίζεται για διάφορους αναλυτικούς υπολογισμούς, εκτελείται όχι μόνο σε διακομιστές με ενορχήστρωση, αλλά και με διάφορα σενάρια και σε υπολογιστές αναλυτών που πρέπει να εργαστούν και εντοπισμός σφαλμάτων σύνθετων αγωγών επεξεργασίας δεδομένων χωρίς ανησυχητικά προβλήματα υποστήριξης. Παρεμπιπτόντως, δεν θα ήταν κακό να μοιραστούμε ότι τα κύρια εργαλεία μας, συμπεριλαμβανομένου του κώδικα διάταξης διαμόρφωσης, είναι εγκατεστημένα μέσω setup.py
– μαζί αυτό ενώνει τον κώδικά μας σε ένα ενιαίο οικοσύστημα, ανεξάρτητο από πλατφόρμα και μέθοδο χρήσης.
Η περιγραφή ενός pod Kubernetes μοιάζει με αυτό:
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
Δηλαδή, κάθε μυστικό περιγράφει ένα τμήμα. Τα μυστικά δημιουργούνται ως εξής:
apiVersion: v1
kind: Secret
metadata:
name: db-main-secret
type: Opaque
stringData:
db_main.yaml: |
engine: sqlite
filename: main.sqlite3
Μαζί αυτό έχει ως αποτέλεσμα τη δημιουργία αρχείων YAML κατά μήκος της διαδρομής /etc/secrets/db-main/section_name.yaml
Και για τοπικές εκκινήσεις, χρησιμοποιείται η διαμόρφωση, που βρίσκεται στον ριζικό κατάλογο του έργου ή κατά μήκος της διαδρομής που καθορίζεται στη μεταβλητή περιβάλλοντος. Ο κωδικός που είναι υπεύθυνος για αυτές τις ευκολίες φαίνεται στο 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()
Η λογική εδώ είναι πολύ απλή: συνδυάζουμε μεγάλες ρυθμίσεις παραμέτρων από τον κατάλογο του έργου και διαδρομές ανά μεταβλητή περιβάλλοντος, και μικρές ενότητες διαμόρφωσης από μυστικά Kuber, και στη συνέχεια τις προεπεξεργαζόμαστε λίγο. Συν μερικές μεταβλητές. Σημειώνω ότι κατά την αναζήτηση αρχείων από μυστικά, χρησιμοποιείται περιορισμός βάθους, επειδή το K8s δημιουργεί έναν κρυφό φάκελο σε κάθε μυστικό όπου αποθηκεύονται τα ίδια τα μυστικά και απλώς ένας σύνδεσμος βρίσκεται σε υψηλότερο επίπεδο.
Ελπίζω ότι αυτό που περιγράφεται θα είναι χρήσιμο σε κάποιον :) Οποιεσδήποτε παρατηρήσεις και συστάσεις σχετικά με την ασφάλεια ή άλλους τομείς προς βελτίωση γίνονται δεκτές. Η γνώμη της κοινότητας είναι επίσης ενδιαφέρουσα, ίσως αξίζει να προσθέσουμε υποστήριξη για ConfigMaps (το έργο μας δεν τους χρησιμοποιεί ακόμα) και να δημοσιεύσουμε τον κώδικα στο GitHub / PyPI; Προσωπικά, πιστεύω ότι τέτοια πράγματα είναι πολύ μεμονωμένα για να είναι καθολικά τα έργα, και λίγη ματιά στις υλοποιήσεις άλλων ανθρώπων, όπως αυτή που δίνεται εδώ, και μια συζήτηση για αποχρώσεις, συμβουλές και βέλτιστες πρακτικές, που ελπίζω να δω στα σχόλια , είναι αρκετό 😉
Μόνο εγγεγραμμένοι χρήστες μπορούν να συμμετάσχουν στην έρευνα.
Πρέπει να δημοσιεύσω ως έργο/βιβλιοθήκη;
-
0,0%Ναι, θα χρησιμοποιούσα το /contribution0
-
33,3%Ναι, αυτό ακούγεται υπέροχο4
-
41,7%Όχι, ποιος πρέπει να το κάνει μόνος του στη δική του μορφή και να ταιριάζει στις ανάγκες του5
-
25,0%Θα αποφύγω να απαντήσω3
Ψήφισαν 12 χρήστες. 3 χρήστες απείχαν.
Πηγή: www.habr.com