Конфигурација пројекта унутар и изван Кубернетеса

Недавно сам писао одговор о животу пројекта у Доцкер-у и коду за отклањање грешака изван њега, где је укратко поменуо да можете да направите сопствени конфигурациони систем тако да услуга добро функционише у Куберу, открива тајне и ради повољно локално, чак и ван Доцкер-а. Ништа компликовано, али описани „рецепт“ може некоме да буде од користи :) Код је у Питхон-у, али логика није везана за језик.

Конфигурација пројекта унутар и изван Кубернетеса

Позадина питања је следећа: некада је постојао један пројекат, у почетку је био мали монолит са услужним програмима и скриптама, али је временом растао, подељен на сервисе, који су заузврат почели да се деле на микросервисе, и затим увећан. У почетку се све ово радило на голом ВПС-у, процеси подешавања и постављања кода на који су били аутоматизовани помоћу Ансибле-а, а сваки сервис је компајлиран са ИАМЛ конфигурацијом са потребним подешавањима и кључевима, а слична конфигурациона датотека је коришћена за локална лансирања, што је било веома згодно, јер се .к ова конфигурација учитава у глобални објекат, доступан са било ког места у пројекту.

Међутим, раст броја микросервиса, њихових веза и потреба за централизованом евиденцијом и праћењем, наговестио је прелазак на Кубер, који је још увек у току. Заједно са помоћи у решавању поменутих проблема, Кубернетес нуди своје приступе управљању инфраструктуром, укључујући такозване Тајне и начини рада са њима. Механизам је стандардан и поуздан, тако да је буквално грех не користити га! Али у исто време, желео бих да задржим свој тренутни формат за рад са конфигурацијом: прво, да га уједначено користим у различитим микросервисима пројекта, и друго, да могу да покренем код на локалној машини помоћу једне једноставне конфигурациони фајл.

С тим у вези, механизам за конструисање конфигурационог објекта је модификован тако да може да ради и са нашим класичним конфигурационим фајлом и са тајнама из Кубера. Такође је специфицирана ригиднија конфигурациона структура, на језику трећег Питхон-а, на следећи начин:

Дицт[стр, Дицт[стр, Унион[стр, инт, флоат]]]

То јест, коначна цогфиг је речник са именованим секцијама, од којих је сваки речник са вредностима из једноставних типова. А одељци описују конфигурацију и приступ ресурсима одређеног типа. Пример дела наше конфигурације:

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 базе података се могу инсталирати на СКЛите, и redis подешен на mock, наводећи и име датотеке коју треба сачувати - ови параметри се исправно препознају и обрађују, што олакшава локално покретање кода за отклањање грешака, тестирање јединица и било које друге потребе. Ово је посебно важно за нас јер постоје многе друге потребе – део нашег кода је намењен разним аналитичким прорачунима, ради не само на серверима са оркестрацијом, већ и са разним скриптама, као и на рачунарима аналитичара који треба да раде кроз и отклањање грешака у сложеним цевоводима за обраду података без бриге о позадинским проблемима. Узгред, не би шкодило да поделимо да су наши главни алати, укључујући код за изглед конфигурације, инсталирани преко setup.py – заједно ово уједињује наш код у јединствен екосистем, независно од платформе и начина коришћења.

Опис Кубернетес под изгледа овако:

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

Ово заједно резултира креирањем ИАМЛ датотека дуж путање /etc/secrets/db-main/section_name.yaml

А за локална покретања користи се конфигурација која се налази у основном директоријуму пројекта или дуж путање наведеног у променљивој окружења. Код који је одговоран за ове погодности може се видети у спојлеру.

цонфиг.пи

__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()

Логика је овде прилично једноставна: комбинујемо велике конфигурације из директоријума пројекта и путање по променљивој окружења, и мале конфигурационе одељке из Кубер тајни, а затим их мало обрадимо. Плус неке варијабле. Напомињем да се при тражењу фајлова из тајни користи ограничење дубине, јер К8с у свакој тајни креира скривени фолдер у коме се чувају саме тајне, а само линк се налази на вишем нивоу.

Надам се да ће ово описано некоме бити од користи :) Сви коментари и препоруке у вези са безбедношћу или другим областима за побољшање се прихватају. Занимљиво је и мишљење заједнице, можда је вредно додати подршку за ЦонфигМапс (наш пројекат их још не користи) и објавити код на ГитХуб / ПиПИ? Лично сматрам да су такве ствари превише индивидуалне да би пројекти били универзални, и мало завиривања у туђе имплементације, попут ове која је овде дата, и дискусија о нијансама, саветима и најбољим праксама, које се надам да ћу видети у коментарима , довољно је 😉

Само регистровани корисници могу учествовати у анкети. Пријавите се, Добродошао си.

Да ли треба да објавим као пројекат/библиотеку?

  • 100%Да, користио бих /цонтрибутион0

  • 100%Да, то звучи одлично4

  • 100%Не, ко треба да то уради сам у свом формату и да одговара својим потребама5

  • 100%Уздржаћу се од одговора3

Гласало је 12 корисника. 3 корисника су била уздржана.

Извор: ввв.хабр.цом

Додај коментар