Cấu hình dự án bên trong và bên ngoài Kubernetes

Gần đây tôi đã viết câu trả lời về vòng đời dự án trong Docker và gỡ lỗi mã bên ngoài nó, trong đó anh ấy đề cập ngắn gọn rằng bạn có thể tạo hệ thống cấu hình của riêng mình để dịch vụ hoạt động tốt trong Kuber, tìm ra bí mật và chạy cục bộ một cách thuận tiện, thậm chí hoàn toàn bên ngoài Docker. Không có gì phức tạp, nhưng “công thức” được mô tả có thể hữu ích với ai đó :) Mã này bằng Python, nhưng logic không gắn liền với ngôn ngữ.

Cấu hình dự án bên trong và bên ngoài Kubernetes

Nền tảng của câu hỏi là: ngày xưa có một dự án, ban đầu nó là một khối nhỏ với các tiện ích và tập lệnh, nhưng theo thời gian, nó phát triển, chia thành các dịch vụ, sau đó bắt đầu được chia thành microservice và sau đó mở rộng quy mô. Lúc đầu, tất cả điều này được thực hiện trên VPS trần, các quy trình thiết lập và triển khai mã được tự động hóa bằng Ansible và mỗi dịch vụ được biên dịch bằng cấu hình YAML với các cài đặt và khóa cần thiết, đồng thời sử dụng một tệp cấu hình tương tự cho khởi chạy cục bộ, rất thuận tiện vì .k cấu hình này được tải vào một đối tượng chung, có thể truy cập được từ mọi nơi trong dự án.

Tuy nhiên, sự tăng trưởng về số lượng vi dịch vụ, kết nối của chúng và nhu cầu ghi nhật ký và giám sát tập trung, báo trước việc chuyển sang Kuber, vẫn đang được tiến hành. Cùng với việc hỗ trợ giải quyết các vấn đề đã đề cập, Kubernetes đưa ra các phương pháp tiếp cận quản lý cơ sở hạ tầng, bao gồm cái gọi là Bí mật и cách để làm việc với họ. Cơ chế này là tiêu chuẩn và đáng tin cậy, vì vậy thật tội lỗi nếu không sử dụng nó! Nhưng đồng thời, tôi muốn duy trì định dạng hiện tại của mình để làm việc với cấu hình: thứ nhất, sử dụng nó một cách thống nhất trong các vi dịch vụ khác nhau của dự án và thứ hai, có thể chạy mã trên máy cục bộ bằng một cách đơn giản tập tin cấu hình.

Về vấn đề này, cơ chế xây dựng đối tượng cấu hình đã được sửa đổi để có thể hoạt động với cả tệp cấu hình cổ điển của chúng tôi và với các bí mật từ Kuber. Cấu trúc cấu hình cứng nhắc hơn cũng được chỉ định, bằng ngôn ngữ của Python thứ ba, như sau:

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

Nghĩa là, cogfig cuối cùng là một từ điển có các phần được đặt tên, mỗi phần là một từ điển có các giá trị từ các kiểu đơn giản. Và các phần mô tả cấu hình và quyền truy cập vào các tài nguyên thuộc một loại nhất định. Một ví dụ về một phần cấu hình của chúng tôi:

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"

Đồng thời, lĩnh vực engine cơ sở dữ liệu có thể được cài đặt trên SQLite và redis đặt thành mock, cũng chỉ định tên của tệp cần lưu - các tham số này được nhận dạng và xử lý chính xác, giúp dễ dàng chạy mã cục bộ để gỡ lỗi, kiểm tra đơn vị và bất kỳ nhu cầu nào khác. Điều này đặc biệt quan trọng đối với chúng tôi vì có nhiều nhu cầu khác - một phần mã của chúng tôi dành cho các phép tính phân tích khác nhau, nó không chỉ chạy trên các máy chủ có sự điều phối mà còn với nhiều tập lệnh khác nhau và trên máy tính của các nhà phân tích cần xử lý. và gỡ lỗi các quy trình xử lý dữ liệu phức tạp mà không phải lo lắng về các vấn đề phụ trợ. Nhân tiện, sẽ không có gì đáng tiếc khi chia sẻ rằng các công cụ chính của chúng tôi, bao gồm mã bố cục cấu hình, được cài đặt qua setup.py – điều này cùng nhau hợp nhất mã của chúng tôi thành một hệ sinh thái duy nhất, độc lập với nền tảng và phương pháp sử dụng.

Mô tả của nhóm Kubernetes trông như thế này:

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

Nghĩa là, mỗi bí mật mô tả một phần. Bản thân những bí mật được tạo ra như thế này:

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

Điều này cùng nhau dẫn đến việc tạo các tệp YAML dọc theo đường dẫn /etc/secrets/db-main/section_name.yaml

Và đối với các lần khởi chạy cục bộ, cấu hình được sử dụng, nằm trong thư mục gốc của dự án hoặc dọc theo đường dẫn được chỉ định trong biến môi trường. Mã chịu trách nhiệm về những tiện ích này có thể được nhìn thấy trong phần giới thiệu.

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

Logic ở đây khá đơn giản: chúng tôi kết hợp các cấu hình lớn từ thư mục dự án và các đường dẫn theo biến môi trường cũng như các phần cấu hình nhỏ từ các bí mật Kuber, sau đó xử lý trước chúng một chút. Cộng với một số biến. Tôi lưu ý rằng khi tìm kiếm tệp từ bí mật, giới hạn độ sâu sẽ được sử dụng, vì K8s tạo một thư mục ẩn trong mỗi bí mật nơi lưu trữ bí mật và chỉ một liên kết được đặt ở cấp cao hơn.

Tôi hy vọng những gì được mô tả sẽ hữu ích cho ai đó :) Mọi nhận xét và đề xuất liên quan đến bảo mật hoặc các lĩnh vực khác cần cải thiện đều được chấp nhận. Ý kiến ​​của cộng đồng cũng rất thú vị, có lẽ đáng để bổ sung hỗ trợ cho ConfigMaps (dự án của chúng tôi chưa sử dụng chúng) và xuất bản mã trên GitHub/PyPI? Cá nhân, tôi nghĩ rằng những thứ như vậy quá riêng biệt để các dự án có thể mang tính phổ quát và xem qua một chút cách triển khai của người khác, như cách được đưa ra ở đây và thảo luận về các sắc thái, mẹo và phương pháp hay nhất mà tôi hy vọng sẽ thấy trong phần nhận xét , thế là đủ 😉

Chỉ những người dùng đã đăng ký mới có thể tham gia khảo sát. Đăng nhập, xin vui lòng.

Tôi có nên xuất bản dưới dạng dự án/thư viện không?

  • 0,0%Có, tôi sẽ sử dụng /contribution0

  • 33,3%Vâng, điều đó nghe tuyệt vời4

  • 41,7%Không, ai cần tự làm việc đó theo hình thức riêng và phù hợp với nhu cầu của họ5

  • 25,0%Tôi sẽ không trả lời3

12 người dùng bình chọn. 3 người dùng bỏ phiếu trắng.

Nguồn: www.habr.com

Thêm một lời nhận xét