Konfigurasi projek di dalam dan di luar Kubernetes

Saya baru-baru ini menulis jawapan tentang kehidupan projek dalam Docker dan kod penyahpepijatan di luarnya, di mana beliau secara ringkas menyebut bahawa anda boleh membuat sistem konfigurasi anda sendiri supaya perkhidmatan ini berfungsi dengan baik dalam Kuber, mengeluarkan rahsia dan berjalan dengan mudah secara tempatan, walaupun di luar Docker sama sekali. Tiada apa-apa yang rumit, tetapi "resipi" yang diterangkan mungkin berguna kepada seseorang :) Kod itu dalam Python, tetapi logiknya tidak terikat dengan bahasa.

Konfigurasi projek di dalam dan di luar Kubernetes

Latar belakang kepada soalan ini ialah: pada suatu masa dahulu terdapat satu projek, pada mulanya ia adalah monolit kecil dengan utiliti dan skrip, tetapi dari masa ke masa ia berkembang, dibahagikan kepada perkhidmatan, yang seterusnya mula dibahagikan kepada perkhidmatan mikro, dan kemudian diperbesarkan. Pada mulanya, semua ini dilakukan pada VPS kosong, proses penyediaan dan penggunaan kod yang diautomatikkan menggunakan Ansible, dan setiap perkhidmatan telah disusun dengan konfigurasi YAML dengan tetapan dan kunci yang diperlukan, dan fail konfigurasi yang serupa digunakan untuk pelancaran tempatan, yang sangat mudah, kerana .k konfigurasi ini dimuatkan ke dalam objek global, boleh diakses dari mana-mana sahaja dalam projek.

Walau bagaimanapun, pertumbuhan dalam bilangan perkhidmatan mikro, sambungannya dan keperluan pembalakan dan pemantauan berpusat, membayangkan perpindahan ke Kuber, yang masih dalam proses. Bersama-sama dengan bantuan dalam menyelesaikan masalah yang disebutkan, Kubernetes menawarkan pendekatannya kepada pengurusan infrastruktur, termasuk kononnya Rahsia ΠΈ cara untuk bekerja dengan mereka. Mekanisme ini adalah standard dan boleh dipercayai, jadi ia benar-benar berdosa untuk tidak menggunakannya! Tetapi pada masa yang sama, saya ingin mengekalkan format semasa saya untuk bekerja dengan konfigurasi: pertama, untuk menggunakannya secara seragam dalam perkhidmatan mikro projek yang berbeza, dan kedua, untuk dapat menjalankan kod pada mesin tempatan menggunakan satu mudah. fail konfigurasi.

Dalam hal ini, mekanisme untuk membina objek konfigurasi telah diubah suai untuk dapat berfungsi dengan fail konfigurasi klasik kami dan dengan rahsia daripada Kuber. Struktur konfigurasi yang lebih tegar juga ditentukan, dalam bahasa Python ketiga, seperti berikut:

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

Iaitu, cogfig terakhir ialah kamus dengan bahagian bernama, setiap satunya adalah kamus dengan nilai daripada jenis mudah. Dan bahagian menerangkan konfigurasi dan akses kepada sumber jenis tertentu. Contoh sekeping konfigurasi kami:

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"

Pada masa yang sama, padang engine pangkalan data boleh dipasang pada SQLite, dan redis ditetapkan untuk mock, menyatakan juga nama fail untuk disimpan - parameter ini diiktiraf dan diproses dengan betul, yang memudahkan untuk menjalankan kod secara setempat untuk penyahpepijatan, ujian unit dan sebarang keperluan lain. Ini amat penting bagi kami kerana terdapat banyak keperluan lain - sebahagian daripada kod kami bertujuan untuk pelbagai pengiraan analitikal, ia berjalan bukan sahaja pada pelayan dengan orkestrasi, tetapi juga dengan pelbagai skrip, dan pada komputer penganalisis yang perlu bekerja melalui dan nyahpepijat saluran pemprosesan data yang kompleks tanpa membimbangkan isu bahagian belakang. Dengan cara ini, tidak salah untuk berkongsi bahawa alat utama kami, termasuk kod susun atur konfigurasi, dipasang melalui setup.py – bersama-sama ini menyatukan kod kami menjadi satu ekosistem, bebas daripada platform dan kaedah penggunaan.

Perihalan pod Kubernetes kelihatan seperti ini:

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

Iaitu, setiap rahsia menerangkan satu bahagian. Rahsia itu sendiri dicipta seperti ini:

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

Bersama-sama ini menghasilkan penciptaan fail YAML di sepanjang laluan /etc/secrets/db-main/section_name.yaml

Dan untuk pelancaran tempatan, konfigurasi digunakan, terletak dalam direktori akar projek atau sepanjang laluan yang ditentukan dalam pembolehubah persekitaran. Kod yang bertanggungjawab untuk kemudahan ini boleh dilihat dalam 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()

Logiknya di sini agak mudah: kami menggabungkan konfigurasi besar daripada direktori projek dan laluan mengikut pembolehubah persekitaran, dan bahagian konfigurasi kecil daripada rahsia Kuber, dan kemudian memprosesnya sedikit. Ditambah beberapa pembolehubah. Saya perhatikan bahawa apabila mencari fail dari rahsia, had kedalaman digunakan, kerana K8s mencipta folder tersembunyi dalam setiap rahsia di mana rahsia itu sendiri disimpan, dan hanya pautan terletak pada tahap yang lebih tinggi.

Saya harap apa yang diterangkan akan berguna kepada seseorang :) Sebarang komen dan cadangan mengenai keselamatan atau bidang lain untuk penambahbaikan diterima. Pendapat komuniti juga menarik, mungkin ia patut menambah sokongan untuk ConfigMaps (projek kami belum menggunakannya lagi) dan menerbitkan kod di GitHub / PyPI? Secara peribadi, saya berpendapat bahawa perkara sedemikian terlalu individu untuk projek menjadi universal, dan sedikit mengintip pelaksanaan orang lain, seperti yang diberikan di sini, dan perbincangan tentang nuansa, petua dan amalan terbaik, yang saya harap dapat dilihat dalam ulasan , cukuplah πŸ˜‰

Hanya pengguna berdaftar boleh mengambil bahagian dalam tinjauan. Log masuk, Sama-sama.

Perlukah saya menerbitkan sebagai projek/perpustakaan?

  • 0,0% Ya, saya akan menggunakan /contribution0

  • 33,3% Ya, bunyinya bagus4

  • 41,7% Tidak, siapa yang perlu melakukannya sendiri dalam format mereka sendiri dan untuk memenuhi keperluan mereka5

  • 25,0% Saya akan menahan diri daripada menjawab3

12 pengguna mengundi. 3 pengguna berpantang.

Sumber: www.habr.com

Tambah komen