رویکرد سیستمی به متغیرها در Ansible

ansible devops codestyle

سلام! اسم من هست دنیس کالیوژنی من به عنوان مهندس در بخش اتوماسیون فرآیند توسعه کار می کنم. هر روز، برنامه‌های کاربردی جدید در صدها سرور کمپین پخش می‌شوند. و در این مقاله، من تجربه خود را از استفاده از Ansible برای این اهداف به اشتراک می گذارم.

این راهنما راهی برای سازماندهی متغیرها در یک استقرار ارائه می دهد. این راهنما برای کسانی طراحی شده است که قبلاً از نقش ها در کتاب های بازی خود استفاده می کنند و مطالعه می کنند بهترین شیوه هااما با مشکلات مشابه مواجه می شوید:

  • با یافتن متغیری در کد ، نمی توان بلافاصله متوجه شد که مسئولیت آن چیست.
  • چندین نقش وجود دارد، و متغیرها باید با یک مقدار مرتبط شوند، اما کار نمی کند.
  • در توضیح نحوه عملکرد منطق متغیرهای موجود در کتاب‌های بازی خود برای دیگران مشکل دارید

ما در پروژه های شرکت خود با این مشکلات مواجه شدیم که در نتیجه به قوانین قالب بندی متغیرها در کتاب های بازی خود رسیدیم که تا حدودی این مشکلات را حل کرد.

رویکرد سیستمی به متغیرها در Ansible

متغیرها در نقش ها

Role یک شیء سیستم استقرار جداگانه است. مانند هر شی از سیستم، باید یک رابط برای تعامل با بقیه سیستم داشته باشد. متغیرهای نقش چنین رابطی هستند.

به عنوان مثال، نقش را در نظر بگیرید api، که یک برنامه جاوا را روی سرور نصب می کند. چه متغیرهایی دارد؟

رویکرد سیستمی به متغیرها در Ansible

متغیرهای نقش را می توان بر اساس نوع به 2 نوع تقسیم کرد:

1. Свойства
    a) независимые от среды
    б) зависимые от среды
2. Связи
    a) слушатели 
    б) запросы внутри системы
    в) запросы в среду

ویژگی های متغیر متغیرهایی هستند که رفتار یک نقش را تعریف می کنند.

متغیرهای پرس و جو متغیرهایی هستند که مقدار آنها برای تعیین منابع خارج از نقش استفاده می شود.

شنوندگان متغیر متغیرهایی هستند که مقدار آنها برای تشکیل متغیرهای پرس و جو استفاده می شود.

از سوی دیگر، 1a، 2a، 2b متغیرهایی هستند که به محیط (آهن، منابع خارجی و غیره) بستگی ندارند و می‌توانند با مقادیر پیش‌فرض در نقش پیش‌فرض پر شوند. با این حال، متغیرهایی مانند 1.b و 2.c را نمی‌توان با مقادیری غیر از «مثال» پر کرد، زیرا بسته به محیط، از پایه به پایه تغییر می‌کنند.

سبک کد

  • نام متغیر باید با نام نقش شروع شود. این کار باعث می شود که در آینده به راحتی بفهمیم که متغیر از چه نقشی است و مسئولیت آن چیست.
  • هنگام استفاده از متغیرها در نقش‌ها، باید حتماً از اصل کپسوله‌سازی پیروی کنید و از متغیرهای تعریف شده در خود نقش یا در نقش‌هایی استفاده کنید که نقش فعلی به آن بستگی دارد.
  • از استفاده از فرهنگ لغت برای متغیرها خودداری کنید. Ansible به شما اجازه نمی دهد که به راحتی مقادیر فردی را در فرهنگ لغت نادیده بگیرید.

    مثالی از یک متغیر بد:

    myrole_user:
        login: admin
        password: admin

    در اینجا login متغیر میانه و رمز عبور متغیر وابسته است. ولی
    از آنجایی که آنها در یک فرهنگ لغت ترکیب شده اند، باید آن را به طور کامل مشخص کنید
    همیشه. که بسیار ناخوشایند است. اینجوری بهتره:

    myrole_user_login: admin
    myrole_user_password: admin

متغیرها در کتاب های بازی استقرار

هنگام کامپایل یک دفترچه راه اندازی (که از این پس به عنوان کتاب بازی نامیده می شود)، به این قانون پایبند هستیم که باید در یک مخزن جداگانه قرار گیرد. درست مانند نقش ها: هر کدام در مخزن git خاص خود. این به شما امکان می دهد تا متوجه شوید که نقش ها و کتاب بازی اشیاء مستقل متفاوتی از سیستم استقرار هستند و تغییرات در یک شی نباید بر عملکرد شی دیگر تأثیر بگذارد. این با تغییر مقادیر پیش فرض متغیرها به دست می آید.

هنگام کامپایل یک کتاب بازی، به طور خلاصه، می توان مقادیر پیش فرض متغیرهای نقش را در دو مکان نادیده گرفت: در متغیرهای کتاب بازی و در متغیرهای موجودی.

mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   └── myapi.yml               # Файл переменных свойств группы myapi
└── inventories                 #
    └── prod                    # Каталог окружения prod
        ├── prod.ini            # Инвентори файл
        └── group_vars          # Каталог для переменных инвентори
            └── myapi           #
                ├── vars.yml    # Средозависимые переменные группы myapi
                └── vault.yml   # Секреты (всегда средозависимы) *

* - متغیرها و خزانه ها

تفاوت این است که متغیرهای playbook همیشه هنگام فراخوانی playbookهایی که در همان سطح با آن قرار دارند استفاده می شود. این بدان معنی است که این متغیرها برای تغییر مقادیر پیش فرض متغیرهایی که به محیط بستگی ندارند عالی هستند. برعکس، متغیرهای موجودی فقط برای یک محیط خاص، که برای متغیرهای خاص محیط ایده آل است، استفاده می شود.

مهم است که توجه داشته باشید که اولویت متغیر به شما اجازه نمی دهد که متغیرها را ابتدا در متغیرهای کتاب بازی و سپس به طور جداگانه در همان موجودی تعریف کنید.

این بدان معنی است که در این مرحله باید تصمیم بگیرید که آیا متغیر وابسته به محیط است یا نه و آن را در جای مناسب قرار دهید.

به عنوان مثال، در یک پروژه، متغیر مسئول فعال کردن SSL برای مدت طولانی وابسته به محیط بود، زیرا ما نمی‌توانستیم SSL را به دلایلی خارج از کنترل خود در یکی از پایه‌ها فعال کنیم. پس از رفع این مشکل، مستقل شد و به متغیرهای playbook منتقل شد.

متغیرهای ویژگی برای گروه ها

بیایید مدل خود را در شکل 1 با افزودن 2 گروه از سرورها با یک برنامه جاوای متفاوت، اما با تنظیمات متفاوت، گسترش دهیم.

رویکرد سیستمی به متغیرها در Ansible

تصور کنید کتاب بازی در این مورد چگونه خواهد بود:

- hosts: myapi
  roles:
    - api

- hosts: bbauth
  roles:
    - auth

- hosts: ghauth
  roles:
    - auth

ما سه گروه در playbook داریم، بنابراین توصیه می‌شود به‌طور هم‌زمان به تعداد فایل‌های گروهی در متغیرهای موجودی group_vars و متغیرهای playbook ایجاد کنید. یک فایل گروهی در این مورد، شرح یکی از اجزای برنامه شما در playbook است. هنگامی که فایل گروه را در متغیرهای playbook باز می کنید، بلافاصله تمام تفاوت های رفتار پیش فرض نقش های اختصاص داده شده به گروه را مشاهده می کنید. در متغیرهای موجودی: تفاوت در رفتار گروه از غرفه به غرفه.

سبک کد

  • سعی کنید به هیچ وجه از متغیرهای host_vars استفاده نکنید، زیرا آنها سیستم را توصیف نمی کنند، بلکه فقط یک مورد خاص است که در دراز مدت منجر به این سوال می شود: "چرا این میزبان با بقیه متفاوت است؟"، پاسخ این است که پیدا کردن همیشه آسان نیست

متغیرها را پیوند دهید

با این حال، این در مورد متغیرهای ویژگی است، اما در مورد متغیرهای پیوند چطور؟
تفاوت آنها در این است که باید در گروه های مختلف ارزش یکسانی داشته باشند.

در ابتدا وجود داشت ایده از یک ساختار هیولایی از فرم استفاده کنید:
hostvars[groups['bbauth'][0]]['auth_bind_port']، اما بلافاصله رها شد
چون ایراداتی داره اول، حجیم بودن. دوم، وابستگی به یک میزبان خاص در گروه. ثالثاً، اگر نمی‌خواهیم خطای متغیر تعریف نشده دریافت کنیم، لازم است قبل از شروع استقرار، اطلاعات را از همه میزبان ها جمع آوری کنیم.

در نتیجه تصمیم گرفته شد از متغیرهای پیوند استفاده شود.

متغیرها را پیوند دهید متغیرهایی هستند که به playbook تعلق دارند و برای پیوند دادن اشیاء سیستم مورد نیاز هستند.

متغیرهای پیوند در متغیرهای کلی سیستم پر می شوند group_vars/all/vars و با حذف تمام متغیرهای شنونده از هر گروه و اضافه کردن نام گروهی که شنونده از آن حذف شده است به ابتدای متغیر تشکیل می شود.

بدین ترتیب یکنواختی و عدم تلاقی نام ها تضمین می شود.

بیایید سعی کنیم متغیرها را از مثال بالا متصل کنیم:

رویکرد سیستمی به متغیرها در Ansible

تصور کنید که متغیرهایی داریم که به یکدیگر وابسته هستند:

# roles/api/defaults:
# Переменная запроса
api_auth1_address: "http://example.com:80"
api_auth2_address: "http://example2.com:80"

# roles/auth/defaults:
# Переменная слушатель
auth_bind_port: "20000"

بیایید آن را در متغیرهای مشترک قرار دهیم group_vars/all/vars همه شنوندگان، و نام گروه را به نام اضافه کنید:

# group_vars/all/vars
bbauth_auth_bind_port: "20000"
ghauth_auth_bind_port: "30000"

# group_vars/bbauth/vars
auth_bind_port: "{{ bbauth_auth_bind_port }}"

# group_vars/ghauth/vars
auth_bind_port: "{{ ghauth_auth_bind_port }}"

# group_vars/myapi/vars
api_auth1_address: "http://{{ bbauth_auth_service_name }}:{{ bbauth_auth_bind_port }}"
api_auth2_address: "http://{{ ghauth_auth_service_name }}:{{ ghauth_auth_bind_port }}"

حال با تغییر مقدار کانکتور مطمئن می شویم که درخواست به همان پورت می رود.

سبک کد

  • از آنجایی که نقش‌ها و گروه‌ها اشیاء مختلف سیستم هستند، باید نام‌های متفاوتی داشته باشند تا متغیرهای پیوند به طور دقیق نشان دهند که متعلق به یک گروه سرور خاص هستند، نه به یک نقش در سیستم.

فایل های محیطی

نقش ها می توانند از فایل هایی استفاده کنند که از محیطی به محیط دیگر متفاوت است.

گواهینامه های SSL نمونه ای از این فایل ها هستند. آنها را به عنوان متن ذخیره کنید
در یک متغیر خیلی راحت نیست. اما ذخیره مسیر به آنها در داخل یک متغیر راحت است.

برای مثال از متغیر استفاده می کنیم api_ssl_key_file: "/path/to/file".

از آنجایی که واضح است که گواهی کلید از محیطی به محیط دیگر تغییر می کند، این یک متغیر وابسته به محیط است، به این معنی که باید در فایل قرار داشته باشد.
group_vars/myapi/vars موجودی متغیرها، و حاوی مقدار "برای مثال".

راحت ترین راه در این مورد، قرار دادن فایل کلید در مخزن playbook در طول مسیر است
files/prod/certs/myapi.key، سپس مقدار متغیر خواهد بود:
api_ssl_key_file: "prod/certs/myapi.key". راحتی در این واقعیت نهفته است که افرادی که مسئول استقرار سیستم در یک پایه خاص هستند نیز مکان اختصاصی خود را در مخزن برای ذخیره پرونده های خود دارند. در عین حال، امکان تعیین مسیر مطلق گواهی بر روی سرور وجود دارد، در صورتی که گواهینامه ها توسط سیستم دیگری عرضه شوند.

چندین غرفه در یک محیط

اغلب نیاز به استقرار چندین پایه تقریباً یکسان در یک محیط با حداقل تفاوت وجود دارد. در این حالت، متغیرهای وابسته به محیط را به آنهایی که در این محیط تغییر نمی کنند و متغیرهایی که تغییر می کنند تقسیم می کنیم. و ما دومی را مستقیماً در خود پرونده های موجودی بیرون می آوریم. پس از این دستکاری، ایجاد موجودی دیگری به طور مستقیم در فهرست محیطی امکان پذیر می شود.

از موجودی group_vars مجددا استفاده می کند و همچنین می تواند برخی از متغیرها را مستقیماً برای خود دوباره تعریف کند.

ساختار فهرست نهایی برای پروژه استقرار:

mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── files                       # Каталог для файлов деплоя
│   ├── prod                    # Католог для средозависимых файлов стенда prod
│   │   └── certs               # 
│   │       └── myapi.key       #
│   └── test1                   # Каталог для средозависимых файлов стенда test1
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   ├── myapi.yml               # Файл переменных свойств группы myapi
│   ├── bbauth.yml              # 
│   └── ghauth.yml              #
└── inventories                 #
    ├── prod                    # Каталог окружения prod
    │   ├── group_vars          # Каталог для переменных инвентори
    │   │   ├── myapi           #
    │   │   │   ├── vars.yml    # Средозависимые переменные группы myapi
    │   │   │   └── vault.yml   # Секреты (всегда средозависимы)
    │   │   ├── bbauth          # 
    │   │   │   ├── vars.yml    #
    │   │   │   └── vault.yml   #
    │   │   └── ghauth          #
    │   │       ├── vars.yml    #
    │   │       └── vault.yml   #
    │   └── prod.ini            # Инвентори стенда prod
    └── test                    # Каталог окружения test
        ├── group_vars          #
        │   ├── myapi           #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   ├── bbauth          #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   └── ghauth          #
        │       ├── vars.yml    #
        │       └── vault.yml   #
        ├── test1.ini           # Инвентори стенда test1 в среде test
        └── test2.ini           # Инвентори стенда test2 в среде test

جمع بندی

پس از سازماندهی متغیرها مطابق با مقاله: هر فایل دارای متغیرها وظیفه خاصی را بر عهده دارد. و از آنجایی که پرونده دارای وظایف خاصی است ، امکان تعیین یک مسئول برای صحت هر پرونده وجود دارد. به عنوان مثال، توسعه‌دهنده استقرار سیستم مسئول پر کردن صحیح متغیرهای playbook می‌شود، در حالی که مدیری که جایگاه او در موجودی توضیح داده شده است، مسئول مستقیم پر کردن موجودی متغیرها است.

Roles تبدیل به یک واحد توسعه مستقل با رابط خاص خود شد که به توسعه‌دهنده نقش اجازه می‌داد تا ویژگی‌ها را توسعه دهد نه اینکه نقش را متناسب با سیستم تنظیم کند. این موضوع به ویژه برای نقش های مشترک برای همه سیستم ها در یک کمپین صادق بود.

مدیران سیستم دیگر نیازی به درک کد استقرار ندارند. تنها چیزی که از آنها برای استقرار موفقیت آمیز لازم است، پر کردن فایل های متغیرهای محیطی است.

ادبیات

  1. اسناد

نویسنده

کالیوژنی دنیس الکساندرویچ

منبع: www.habr.com

اضافه کردن نظر