python - كيفية ضبط مضيفات الهدف في ملف Fabric




host (10)

أرغب في استخدام Fabric لنشر رمز تطبيق الويب الخاص بي إلى خوادم التطوير والتخطيط والإنتاج. بلدي fabfile:

def deploy_2_dev():
  deploy('dev')

def deploy_2_staging():
  deploy('staging')

def deploy_2_prod():
  deploy('prod')

def deploy(server):
  print 'env.hosts:', env.hosts
  env.hosts = [server]
  print 'env.hosts:', env.hosts

إخراج العينة:

host:folder user$ fab deploy_2_dev
env.hosts: []
env.hosts: ['dev']
No hosts found. Please specify (single) host string for connection:

عندما أقوم بإنشاء مهمة set_hosts() كما هو موضح في مستندات Fabric ، يتم تعيين env.hosts بشكل صحيح. ومع ذلك ، هذا ليس خيارا قابلا للتطبيق ، ولا هو الديكور. تمرير المضيفين على سطر الأوامر سيؤدي في نهاية المطاف إلى نوع من برنامج نصي shell الذي يستدعي fabfile ، أفضل استخدام أداة واحدة لتنفيذ المهمة بشكل صحيح.

مكتوب في مجلدات Fabric أن 'env.hosts هو ببساطة كائن قائمة بايثون'. من ملاحظاتي ، هذا ببساطة غير صحيح.

يمكن لأي شخص أن يفسر ما يجري هنا؟ كيف يمكنني تعيين المضيف على النشر؟


أفعل ذلك عن طريق إعلان وظيفة فعلية لكل بيئة. فمثلا:

def test():
    env.user = 'testuser'
    env.hosts = ['test.server.com']

def prod():
    env.user = 'produser'
    env.hosts = ['prod.server.com']

def deploy():
    ...

باستخدام الوظائف المذكورة أعلاه ، أود كتابة ما يلي لنشرها في بيئة الاختبار الخاصة بي:

fab test deploy

... وما يلي للانتشار في الإنتاج:

fab prod deploy

الشيء الجميل في القيام بهذه الطريقة هو أنه يمكن استخدام وظائف test و prod قبل أي وظيفة القوات المسلحة البوروندية ، وليس فقط نشرها. انها مفيدة بشكل لا يصدق.


أنا جديد تمامًا في صناعة القماش ، ولكن للحصول على قماش لتشغيل الأوامر نفسها على مضيفين متعددين (على سبيل المثال ، النشر إلى عدة خوادم ، في أمر واحد) ، يمكنك تشغيل:

fab -H staging-server,production-server deploy 

حيث خادم التدريج وخادم الإنتاج هما خادمان تريد تشغيل إجراء النشر ضدهما. إليك fabfile.py بسيطًا سيعرض اسم نظام التشغيل. لاحظ أنه يجب أن يكون fabfile.py في نفس الدليل حيث تقوم بتشغيل الأمر fab.

from fabric.api import *

def deploy():
    run('uname -s')

هذا يعمل مع النسيج 1.8.1 على الأقل.


استخدم roledefs

from fabric.api import env, run

env.roledefs = {
    'test': ['localhost'],
    'dev': ['[email protected]'],
    'staging': ['[email protected]'],
    'production': ['[email protected]']
} 

def deploy():
    run('echo test')

اختر الدور مع -R:

$ fab -R test deploy
[localhost] Executing task 'deploy'
...

انه بسيط جدا. مجرد تهيئة متغير env.host_string وسيتم تنفيذ جميع الأوامر التالية على هذا المضيف.

from fabric.api import env, run

env.host_string = '[email protected]'

def foo:
    run("hostname -f")

تحتاج إلى تعيين host_string على سبيل المثال سيكون:

from fabric.context_managers import settings as _settings

def _get_hardware_node(virtualized):
    return "localhost"

def mystuff(virtualized):
    real_host = _get_hardware_node(virtualized)
    with _settings(
        host_string=real_host):
        run("echo I run on the host %s :: `hostname -f`" % (real_host, ))

على عكس بعض الأجوبة الأخرى ، من الممكن تعديل متغيرات بيئة env داخل المهمة. ومع ذلك ، سيتم استخدام هذه env فقط للمهام اللاحقة التي تم تنفيذها باستخدام وظيفة fabric.tasks.execute .

from fabric.api import task, roles, run, env
from fabric.tasks import execute

# Not a task, plain old Python to dynamically retrieve list of hosts
def get_stressors():
    hosts = []
    # logic ...
    return hosts

@task
def stress_test():
    # 1) Dynamically generate hosts/roles
    stressors = get_stressors()
    env.roledefs['stressors'] = map(lambda x: x.public_ip, stressors)

    # 2) Wrap sub-tasks you want to execute on new env in execute(...)
    execute(stress)

    # 3) Note that sub-tasks not nested in execute(...) will use original env
    clean_up()

@roles('stressors')
def stress():
    # this function will see any changes to env, as it was wrapped in execute(..)
    run('echo "Running stress test..."')
    # ...

@task
def clean_up():
    # this task will NOT see any dynamic changes to env

دون التفاف المهام الفرعية في execute(...) ، سيتم استخدام إعدادات env مستوى الوحدة النمطية الخاصة بك أو أي ما تم تمريره من fab CLI.


كان عالقاً على هذا بنفسي ، لكن أحسبته أخيرًا. أنت ببساطة لا يمكن ضبط تكوين env.hosts من داخل مهمة. يتم تنفيذ كل مهمة N مرة ، مرة واحدة لكل مضيف محدد ، بحيث يكون الإعداد خارج نطاق المهام بشكل أساسي.

بالنظر إلى شفرتك أعلاه ، يمكنك ببساطة إجراء ذلك:

@hosts('dev')
def deploy_dev():
    deploy()

@hosts('staging')
def deploy_staging():
    deploy()

def deploy():
    # do stuff...

التي يبدو أنها ستفعل ما تنوي فعله.

أو يمكنك كتابة بعض الأكواد المخصصة في النطاق العام الذي يوزع الوسيطات يدويًا ، ويعين env.hosts قبل تعريف مهمة المهمة. لعدة أسباب ، هذه هي الطريقة التي قمت من خلالها بضبط المنجم.


لتوضيح سبب كونها مشكلة. يقوم الأمر fab باستخدام بنية المكتبة لتشغيل المهام في قوائم المضيف. إذا حاولت تغيير قائمة المضيف داخل إحدى المهام ، فأنت تحاول أن تحاول تغيير قائمة أثناء التكرار أكثر من ذلك. أو في حالة عدم وجود تعريف للمضيف ، قم بالالتفاف فوق قائمة فارغة حيث لا يتم تنفيذ الكود الذي قمت بتعيين القائمة عليه.

استخدام env.host_string هو حل بديل لهذا السلوك فقط من حيث أنه يحدد مباشرة الوظائف التي يستضيفها المتصلون بها. هذا يسبب بعض المشاكل في ذلك سوف تكون إعادة إنشاء حلقة التنفيذ إذا كنت تريد أن يكون لديك عدد من المضيفين للتنفيذ.

إن أبسط طريقة لجعل الناس قادرين على تعيين المضيفين في وقت التشغيل ، هو الحفاظ على populatiing env كمهمة متميزة ، والتي تقوم بإعداد كل سلاسل المضيف ، والمستخدمين ، إلخ. ثم يقومون بتشغيل مهمة النشر. تبدو هكذا:

fab production deploy

أو

fab staging deploy

حيث يشبه التدريج والإنتاج المهام التي قدمتها ، ولكنهما لا يتصلان بالمهمة التالية نفسها. السبب في أنه يجب أن يعمل مثل هذا ، هو أن المهمة يجب أن تنتهي ، وخروج من الحلقة (من المضيفين ، في حالة env لا شيء ، ولكنها حلقة واحدة في هذه النقطة) ، ثم يكون الحلقات على المضيفين (الآن معرفة من قبل المهمة السابقة) من جديد.


منذ القوات المسلحة البوروندية 1.5 هذه هي طريقة موثقة لضبط المضيفين بشكل حيوي.

http://docs.fabfile.org/en/1.7/usage/execution.html#dynamic-hosts

اقتبس من الوثيقة أدناه.

استخدام التنفيذ مع قوائم مضيفات محددة ديناميكيًا

إن حالة الاستخدام الشائعة المتوسطة إلى المتقدمة للنسيج هي إضفاء طابع المعلمة على البحث في قائمة المضيف المستهدف في وقت التشغيل (عندما لا يكون استخدام الأدوار كافياً). التنفيذ يمكن أن يجعل هذا بسيط للغاية ، مثل:

from fabric.api import run, execute, task

# For example, code talking to an HTTP API, or a database, or ...
from mylib import external_datastore

# This is the actual algorithm involved. It does not care about host
# lists at all.
def do_work():
    run("something interesting on a host")

# This is the user-facing task invoked on the command line.
@task
def deploy(lookup_param):
    # This is the magic you don't get with @hosts or @roles.
    # Even lazy-loading roles require you to declare available roles
    # beforehand. Here, the sky is the limit.
    host_list = external_datastore.query(lookup_param)
    # Put this dynamically generated host list together with the work to be
    # done.
    execute(do_work, hosts=host_list)

يعتبر استخدام الأدوار حاليًا هو الطريقة "الصحيحة" و "الصحيحة" للقيام بذلك وهو ما يجب عليك فعله.

ومع ذلك ، إذا كنت مثل معظم ما "تريد" أو "الرغبة" هو القدرة على أداء "النظام الملتوي" أو تبديل الأنظمة الهدف على الطاير.

لذا ، لأغراض الترفيه فقط (!) يوضح المثال التالي ما قد يعتبره الكثيرون مناورة محفوفة بالمخاطر ، ومع ذلك ، بطريقة مرضية تمامًا ، والتي تسير على هذا النحو:

env.remote_hosts       = env.hosts = ['10.0.1.6']
env.remote_user        = env.user = 'bob'
env.remote_password    = env.password = 'password1'
env.remote_host_string = env.host_string

env.local_hosts        = ['127.0.0.1']
env.local_user         = 'mark'
env.local_password     = 'password2'

def perform_sumersault():
    env_local_host_string = env.host_string = env.local_user + '@' + env.local_hosts[0]
    env.password = env.local_password
    run("hostname -f")
    env.host_string = env.remote_host_string
    env.remote_password = env.password
    run("hostname -f")

ثم الجري:

fab perform_sumersault




fabric