Projekta konfigurācija Kubernetes iekÅ”pusē un ārpusē

Es nesen rakstÄ«ju atbilde par projekta dzÄ«vi programmā Docker un atkļūdoÅ”anas kodu ārpus tā, kur viņŔ Ä«si minēja, ka varat izveidot savu konfigurācijas sistēmu, lai pakalpojums Kuber labi darbotos, izvilktu noslēpumus un ērti darbotos lokāli, pat ārpus Docker. Nekas sarežģīts, bet aprakstÄ«tā ā€œrecepteā€ kādam var noderēt :) Kods ir Python, bet loÄ£ika nav piesaistÄ«ta valodai.

Projekta konfigurācija Kubernetes iekÅ”pusē un ārpusē

Jautājuma fons ir Ŕāds: kādreiz bija viens projekts, sākumā tas bija mazs monolÄ«ts ar komunālajiem pakalpojumiem un skriptiem, bet laika gaitā tas pieauga, sadalÄ«jās pa servisiem, kurus savukārt sāka sadalÄ«t mikropakalpojumos un pēc tam palielināts. Sākumā tas viss tika darÄ«ts tukŔā VPS, kurā koda iestatÄ«Å”anas un izvietoÅ”anas procesi tika automatizēti, izmantojot Ansible, un katrs pakalpojums tika kompilēts ar YAML konfigurāciju ar nepiecieÅ”amajiem iestatÄ«jumiem un atslēgām, kā arÄ« tika izmantots lÄ«dzÄ«gs konfigurācijas fails. lokālās palaiÅ”anas, kas bija ļoti ērti, jo .k Ŕī konfigurācija tiek ielādēta globālā objektā, kas ir pieejama no jebkuras vietas projektā.

Taču mikropakalpojumu skaita pieaugums, to pieslēgumi un nepiecieÅ”amÄ«ba pēc centralizētas mežizstrādes un uzraudzÄ«bas, paredzēja pāreju uz Kuber, kas joprojām turpinās. Kopā ar palÄ«dzÄ«bu minēto problēmu risināŔanā Kubernetes piedāvā savas pieejas infrastruktÅ«ras pārvaldÄ«bai, t.sk tā sauktie noslēpumi Šø veidi, kā ar viņiem strādāt. Mehānisms ir standarta un uzticams, tāpēc burtiski grēks to neizmantot! Bet tajā paŔā laikā es vēlētos saglabāt savu paÅ”reizējo formātu darbam ar konfigurāciju: pirmkārt, lai to vienmērÄ«gi izmantotu dažādos projekta mikropakalpojumos, un, otrkārt, lai varētu palaist kodu vietējā maŔīnā, izmantojot vienu vienkārÅ”u konfigurācijas fails.

Å ajā sakarā tika modificēts konfigurācijas objekta konstruÄ“Å”anas mehānisms, lai varētu strādāt gan ar mÅ«su klasisko konfigurācijas failu, gan ar Kuber noslēpumiem. TreŔā Python valodā tika norādÄ«ta arÄ« stingrāka konfigurācijas struktÅ«ra:

Dict[str, Dict[str, Union[str, int, float]]]

Tas nozÄ«mē, ka pēdējais zobrats ir vārdnÄ«ca ar nosauktām sadaļām, no kurām katra ir vārdnÄ«ca ar vienkārÅ”u veidu vērtÄ«bām. Sadaļās ir aprakstÄ«ta noteikta veida konfigurācija un piekļuve tiem resursiem. MÅ«su konfigurācijas daļas piemērs:

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"

Tajā paŔā laikā lauks engine datu bāzes var instalēt SQLite, un redis iestatÄ«ts uz mock, norādot arÄ« saglabājamā faila nosaukumu ā€“ Å”ie parametri tiek pareizi atpazÄ«ti un apstrādāti, kas ļauj ērti palaist kodu lokāli atkļūdoÅ”anai, vienÄ«bu pārbaudei un citām vajadzÄ«bām. Tas mums ir Ä«paÅ”i svarÄ«gi, jo ir daudz citu vajadzÄ«bu - daļa mÅ«su koda ir paredzēta dažādiem analÄ«tiskiem aprēķiniem, tas darbojas ne tikai serveros ar orÄ·estrÄ“Å”anu, bet arÄ« ar dažādiem skriptiem un analÄ«tiÄ·u datoros, kuriem nepiecieÅ”ams strādāt. un atkļūdojiet sarežģītus datu apstrādes cauruļvadus, neradot aizmugursistēmas problēmas. Starp citu, nenāktu par ļaunu pastāstÄ«t, ka mÅ«su galvenie rÄ«ki, tostarp konfigurācijas izkārtojuma kods, tiek instalēti, izmantojot setup.py ā€“ kopā tas apvieno mÅ«su kodu vienā ekosistēmā, kas nav atkarÄ«ga no platformas un lietoÅ”anas metodes.

Kubernetes pāksts apraksts izskatās Ŕādi:

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

Tas ir, katrs noslēpums apraksta vienu sadaļu. PaÅ”i noslēpumi tiek izveidoti Ŕādi:

apiVersion: v1
kind: Secret
metadata:
  name: db-main-secret
type: Opaque
stringData:
  db_main.yaml: |
    engine: sqlite
    filename: main.sqlite3

Kopā tas rada YAML failu izveidi ceļā /etc/secrets/db-main/section_name.yaml

Un vietējai palaiÅ”anai tiek izmantota konfigurācija, kas atrodas projekta saknes direktorijā vai pa ceļu, kas norādÄ«ts vides mainÄ«gajā. Par Ŕīm ērtÄ«bām atbildÄ«go kodu var redzēt 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()

LoÄ£ika Å”eit ir pavisam vienkārÅ”a: mēs apvienojam lielas konfigurācijas no projekta direktorija un ceļus pēc vides mainÄ«gā un mazas konfigurācijas sadaļas no Kuber noslēpumiem, un pēc tam tās nedaudz apstrādājam. Plus daži mainÄ«gie. Es atzÄ«mēju, ka, meklējot failus no noslēpumiem, tiek izmantots dziļuma ierobežojums, jo K8s katrā noslēpumā izveido slēptu mapi, kurā tiek glabāti paÅ”i noslēpumi, un tikai saite atrodas augstākā lÄ«menÄ«.

Ceru, ka aprakstÄ«tais kādam noderēs :) Jebkuri komentāri un ieteikumi saistÄ«bā ar droŔību vai citām uzlaboÅ”anas jomām tiek pieņemti. Interesants ir arÄ« kopienas viedoklis, varbÅ«t ir vērts pievienot atbalstu ConfigMaps (mÅ«su projekts tos vēl neizmanto) un publicēt kodu GitHub / PyPI? Man personÄ«gi liekas, ka Ŕādas lietas ir pārāk individuālas, lai projekti bÅ«tu universāli, un nedaudz palÅ«kojoties uz citu Ä«stenotajiem, kā Å”eit dotajiem, un niansēm, padomiem un paraugpraksēm, ko ceru redzēt komentāros. , pietiek šŸ˜‰

Aptaujā var piedalīties tikai reģistrēti lietotāji. Ielogoties, lūdzu.

Vai man jāpublicē kā projekts/bibliotēka?

  • 0,0%Jā, es izmantotu /contribution0

  • 33,3%Jā, tas izklausās lieliski 4

  • 41,7%Nē, kam tas jādara paÅ”am savā formātā un atbilstoÅ”i savām vajadzÄ«bām5

  • 25,0%Es atturÄ“Å”os atbildēt 3

Nobalsoja 12 lietotāji. 3 lietotāji atturējās.

Avots: www.habr.com

Pievieno komentāru