Konfigurasi proyek di dalam dan di luar Kubernetes

Saya baru-baru ini menulis jawaban tentang kehidupan proyek di Docker dan kode debug di luarnya, di mana dia secara singkat menyebutkan bahwa Anda dapat membuat sistem konfigurasi Anda sendiri sehingga layanan berfungsi dengan baik di Kuber, membuka rahasia, dan berjalan dengan nyaman secara lokal, bahkan di luar Docker sama sekali. Tidak ada yang rumit, tetapi "resep" yang dijelaskan mungkin bermanfaat bagi seseorang :) Kodenya menggunakan Python, tetapi logikanya tidak terikat dengan bahasanya.

Konfigurasi proyek di dalam dan di luar Kubernetes

Latar belakang dari pertanyaan ini adalah: dahulu kala ada satu proyek, pada awalnya itu adalah sebuah monolit kecil dengan utilitas dan skrip, tetapi seiring waktu berkembang, dibagi menjadi layanan, yang kemudian mulai dibagi menjadi layanan mikro, dan kemudian ditingkatkan. Pada awalnya, semua ini dilakukan pada VPS kosong, proses pengaturan dan penerapan kode diotomatisasi menggunakan Ansible, dan setiap layanan dikompilasi dengan konfigurasi YAML dengan pengaturan dan kunci yang diperlukan, dan file konfigurasi serupa digunakan untuk peluncuran lokal, yang sangat nyaman, karena .k konfigurasi ini dimuat ke dalam objek global, dapat diakses dari mana saja dalam proyek.

Namun, pertumbuhan jumlah layanan mikro, koneksinya, dan kebutuhan akan pencatatan dan pemantauan terpusat, menandakan perpindahan ke Kuber, yang masih berlangsung. Bersamaan dengan bantuan dalam memecahkan masalah-masalah tersebut, Kubernetes menawarkan pendekatannya sendiri terhadap pengelolaan infrastruktur, termasuk yang disebut Rahasia ΠΈ cara untuk bekerja dengan mereka. Mekanismenya standar dan dapat diandalkan, jadi sungguh dosa jika tidak menggunakannya! Tetapi pada saat yang sama, saya ingin mempertahankan format saya saat ini untuk bekerja dengan konfigurasi: pertama, untuk menggunakannya secara seragam di berbagai layanan mikro proyek, dan kedua, untuk dapat menjalankan kode pada mesin lokal menggunakan satu yang sederhana file konfigurasi.

Dalam hal ini, mekanisme untuk membangun objek konfigurasi telah dimodifikasi agar dapat bekerja dengan file konfigurasi klasik kami dan dengan rahasia dari Kuber. Struktur konfigurasi yang lebih kaku juga ditentukan, dalam bahasa Python ketiga, sebagai berikut:

Dikte[str, Dikte[str, Persatuan[str, int, float]]]

Artinya, cogfig terakhir adalah kamus dengan bagian bernama, yang masing-masing merupakan kamus dengan nilai dari tipe sederhana. Dan bagian menjelaskan konfigurasi dan akses ke sumber daya jenis tertentu. Contoh bagian dari 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 saat yang sama, lapangan engine database dapat diinstal di SQLite, dan redis mulai mock, tentukan juga nama file yang akan disimpan - parameter ini dikenali dan diproses dengan benar, sehingga memudahkan menjalankan kode secara lokal untuk debugging, pengujian unit, dan kebutuhan lainnya. Hal ini sangat penting bagi kami karena masih banyak kebutuhan lainnya - sebagian dari kode kami ditujukan untuk berbagai perhitungan analitis, tidak hanya berjalan di server dengan orkestrasi, tetapi juga dengan berbagai skrip, dan di komputer analis yang perlu mengerjakannya. dan men-debug alur pemrosesan data yang kompleks tanpa mengkhawatirkan masalah backend. Omong-omong, tidak ada salahnya untuk berbagi bahwa alat utama kami, termasuk kode tata letak konfigurasi, diinstal melalui setup.py – secara bersama-sama hal ini menyatukan kode kita ke dalam satu ekosistem, tidak bergantung pada platform dan metode penggunaan.

Deskripsi pod Kubernetes terlihat 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

Artinya, setiap rahasia menjelaskan satu bagian. Rahasianya sendiri dibuat seperti ini:

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

Bersama-sama, hal ini menghasilkan pembuatan file YAML di sepanjang jalur /etc/secrets/db-main/section_name.yaml

Dan untuk peluncuran lokal, konfigurasi digunakan, terletak di direktori root proyek atau di sepanjang jalur yang ditentukan dalam variabel lingkungan. Kode yang bertanggung jawab atas kemudahan ini dapat dilihat di 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()

Logikanya di sini cukup sederhana: kami menggabungkan konfigurasi besar dari direktori proyek dan jalur berdasarkan variabel lingkungan, dan bagian konfigurasi kecil dari rahasia Kuber, lalu memprosesnya sedikit terlebih dahulu. Ditambah beberapa variabel. Saya perhatikan bahwa ketika mencari file dari rahasia, batasan kedalaman digunakan, karena K8s membuat folder tersembunyi di setiap rahasia tempat rahasia itu sendiri disimpan, dan hanya tautan yang terletak di tingkat yang lebih tinggi.

Saya harap apa yang dijelaskan akan bermanfaat bagi seseorang :) Setiap komentar dan rekomendasi mengenai keamanan atau area perbaikan lainnya diterima. Pendapat komunitas juga menarik, mungkin ada baiknya menambahkan dukungan untuk ConfigMaps (proyek kami belum menggunakannya) dan memublikasikan kodenya di GitHub / PyPI? Secara pribadi, menurut saya hal-hal seperti itu terlalu individual untuk proyek menjadi universal, dan sedikit mengintip implementasi orang lain, seperti yang diberikan di sini, dan diskusi tentang nuansa, tip, dan praktik terbaik, yang saya harap dapat dilihat di komentar , sudah cukup πŸ˜‰

Hanya pengguna terdaftar yang dapat berpartisipasi dalam survei. Masuk, silakan.

Haruskah saya menerbitkannya sebagai proyek/perpustakaan?

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

  • 33,3%Ya, kedengarannya bagus4

  • 41,7%Tidak, siapa yang perlu melakukannya sendiri dalam formatnya sendiri dan sesuai dengan kebutuhannya5

  • 25,0%Saya akan menahan diri untuk tidak menjawab3

12 pengguna memilih. 3 pengguna abstain.

Sumber: www.habr.com

Tambah komentar