फैब्रिक फ़ाइल में लक्ष्य होस्ट कैसे सेट करें


107

मैं अपने वेब ऐप कोड को विकास, मंचन और उत्पादन सर्वर पर तैनात करने के लिए फैब्रिक का उपयोग करना चाहता हूं। मेरा फैबाइल:

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() में दिखाए गए कार्य को बनाता हूं, तो env.hosts ठीक से सेट हो जाता है। हालांकि, यह एक व्यवहार्य विकल्प नहीं है, न ही एक डेकोरेटर है। कमांड लाइन पर मेजबानों को पास करने से अंततः कुछ प्रकार की शेल स्क्रिप्ट का परिणाम होगा जो कि फाफिले को बुलाता है, मैं एक एकल उपकरण को ठीक से काम करना पसंद करूंगा।

फैब्रिक डॉक्स में यह कहा गया है कि 'env.hosts केवल एक पायथन सूची ऑब्जेक्ट है'। मेरी टिप्पणियों से, यह केवल सच नहीं है।

क्या कोई समझा सकता है कि यहां क्या चल रहा है? मैं होस्ट को कैसे परिनियोजित करने के लिए सेट कर सकता हूं?


मुझे भी यही समस्या है, क्या आपने इसका कोई हल खोजा है?
मार्टिन एम।

कई सर्वरों के खिलाफ एक ही कार्य को चलाने के लिए, "फैब-एच स्टेजिंग-सर्वर, प्रोडक्शन-सर्वर की तैनाती" का उपयोग करें ... नीचे मेरे उत्तर में और अधिक: stackoverflow.com/a/21458231/26510
ब्रैड पार्क्स


यह उत्तर फैब्रिक 2+ पर लागू नहीं होता है। अगर कोई स्टाकेवरफ्लो कन्वेंशन से अधिक परिचित है, तो कपड़े को संदर्भित करने के लिए प्रश्न या प्रश्न शीर्षक को संपादित कर सकता है 1 यह सहायक हो सकता है।
जोनाथन बर्जर

जवाबों:


128

मैं प्रत्येक वातावरण के लिए एक वास्तविक कार्य घोषित करके ऐसा करता हूं। उदाहरण के लिए:

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कार्यों से पहले इस्तेमाल किया जा सकता है किसी भी , फैब समारोह बस को तैनात नहीं। यह अविश्वसनीय रूप से उपयोगी है।


10
फैब्रिक में एक बग के कारण ( code.fabfile.org/issues/show/138#change-1497 ) यह बेहतर है कि आप एनवियर्स को सेट करने के बजाय होस्ट स्ट्रिंग (जैसे produser@prod.server.com) में शामिल करें।
मिखाइल कोरोबोव

1
मुझे भी यही समस्या थी, और यह सबसे अच्छा समाधान लगता है। मैं होस्ट, उपयोगकर्ता और कई अन्य सेटिंग्स को एक YAML फ़ाइल में परिभाषित करता हूं जो कि देव () और ठेस () फ़ंक्शन द्वारा लोड की गई है। (ताकि मैं इसी तरह की परियोजनाओं के लिए एक ही फैब्रिक स्क्रिप्ट का पुन: उपयोग कर
सकूं

@MikhailKorobov: जब मैंने आपके लिंक का अनुसरण किया, तो मैंने " nginx में आपका स्वागत है! " देखा। code.fabfile.orgडोमेन के सभी अनुरोधों की प्रतिक्रियाएं ऐसी हैं।
ताडेक

हाँ, ऐसा लगता है कि सभी कीड़े गिथब में चले गए थे।
मिखाइल कोरोबोव

2
दुर्भाग्य से, ऐसा लगता है कि यह अब काम नहीं करता है - कपड़े पहले से परिभाषित किए गए env.hosts के बिना कार्य नहीं चलाएंगे, और fab A B Cकार्य के रूप में परिभाषित किए बिना शैली में फ़ंक्शन नहीं चलाएंगे ।
DNelson

77

Roledefs का उपयोग करें

from fabric.api import env, run

env.roledefs = {
    'test': ['localhost'],
    'dev': ['user@dev.example.com'],
    'staging': ['user@staging.example.com'],
    'production': ['user@production.example.com']
} 

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

-R के साथ भूमिका चुनें:

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

7
या यदि कार्य हमेशा एक ही भूमिका पर चलाया जाता है, तो आप कार्य पर @roles () डेकोरेटर का उपयोग कर सकते हैं।
टॉम

2
अलग-अलग कार्यों में उन्हें परिभाषित करने की तुलना में रोल्डफ्स जैसी आवाज़ एक बेहतर समाधान है।
एहतेश चौधरी

क्या किसी को पता है कि मैं प्रदान किए गए उपयोगकर्ता नाम के लिए एक पासवर्ड कैसे शामिल कर सकता हूं roledef? एक और शब्दकोश प्रविष्टि 'password': 'some_password'को नजरअंदाज कर दिया जाता है और रनटाइम पर एक संकेत देता है।
डर्क

@Dirk आप env.passwords का उपयोग कर सकते हैं, जो एक शब्दकोष है जिसमें मूल्य के रूप में उपयोगकर्ता + होस्ट + पोर्ट कुंजी और पासवर्ड के रूप में है। Eg env.passwords = {'उपयोगकर्ता @ होस्ट: 22': 'पासवर्ड'}
जोनाथन

49

यहाँ सर्वरहेयर के उत्तर का एक सरल संस्करण है :

from fabric.api import settings

def mystuff():
    with settings(host_string='192.0.2.78'):
        run("hostname -f")

2
प्रति डॉक्स , सेटिंग्स संदर्भ प्रबंधक ओवरराइड करने के लिए है envउन्हें शुरू में स्थापित करने के लिए चर, नहीं। मुझे लगता है कि रोमिफ़ का उपयोग करते हुए , जैसा कि थोमई ने सुझाव दिया है, मंच, देव और परीक्षण जैसे मेजबानों को परिभाषित करने के लिए अधिक उपयुक्त है।
टोनी

21

इस पर खुद अटक गया था, लेकिन अंत में यह समझ में आया। आप बस एक कार्य के भीतर से env.hosts कॉन्फ़िगरेशन सेट नहीं कर सकते । प्रत्येक कार्य को एन बार निष्पादित किया जाता है, एक बार निर्दिष्ट प्रत्येक होस्ट के लिए, इसलिए सेटिंग मूल रूप से कार्य क्षेत्र से बाहर है।

ऊपर अपने कोड को देखते हुए, आप बस यह कर सकते हैं:

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

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

def deploy():
    # do stuff...

जो ऐसा लगता है कि यह वही करेगा जो आप चाहते हैं।

या आप वैश्विक स्कोप में कुछ कस्टम कोड लिख सकते हैं, जो आपके कार्यों को परिभाषित करने से पहले तर्कों को मैन्युअल रूप से पार्स करता है, और env.hosts सेट करता है। कुछ कारणों के लिए, यह वास्तव में है कि मैंने कैसे स्थापित किया है।


एक रास्ता मिल गया from fabric.api import env:; env.host_string = "dev"
रोमन

18

फैब 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)

3
+1। यहाँ पृष्ठ के नीचे की ओर बहुत अच्छे जवाब हैं।
मैट मोंटैग

10

कुछ अन्य उत्तर के विपरीत, यह है संशोधित करने के लिए संभव 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सीएलआई से पारित होता है उसका उपयोग किया जाएगा।


यदि आप गतिशील रूप से env.hosts सेट करना चाहते हैं तो यह सबसे अच्छा उत्तर है।
जहमीस्ट

9

आपको 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, ))

मिठाई। मैंने यहां एक अन्य उत्तर में कोड का एक सरल संस्करण पोस्ट किया है।
tobych

9

यह बताने के लिए कि यह एक मुद्दा भी क्यों है। कमांड फेब होस्टल सूचियों पर कार्यों को चलाने के लिए लाइब्रेरी का उपयोग कर रहा है। यदि आप किसी कार्य के अंदर होस्ट सूची को बदलने और बदलने का प्रयास करते हैं, तो आप संभावित रूप से उस पर पुनरावृति करते हुए एक सूची को बदलने का प्रयास कर रहे हैं। या ऐसे मामले में जहां आपके पास कोई मेजबानों को परिभाषित नहीं किया गया है, एक खाली सूची पर लूप करें जहां कोड जहां आप सूची को लूप पर सेट करते हैं, कभी निष्पादित नहीं होता है।

Env.host_string का उपयोग केवल इस व्यवहार के लिए चारों ओर एक काम है कि यह सीधे उन कार्यों को निर्दिष्ट कर रहा है जिनके साथ कनेक्ट करने के लिए होस्ट करता है। इसमें कुछ समस्याओं का कारण बनता है, यदि आप एक से अधिक मेजबानों को निष्पादित करना चाहते हैं, तो आप निष्पादन लूप को रीमेक करेंगे।

सबसे सरल तरीका है कि लोग रन टाइम पर मेजबानों को सेट करने की क्षमता रखते हैं, एक अलग कार्य के रूप में एनवी पॉपुलैटिंग को रखना है, जो सभी होस्ट स्ट्रिंग्स, उपयोगकर्ताओं, आदि को सेट करता है, फिर वे तैनाती कार्य चलाते हैं। यह इस तरह दिख रहा है:

fab production deploy

या

fab staging deploy

जहां मंचन और उत्पादन आपके द्वारा दिए गए कार्यों की तरह हैं, लेकिन वे अगले कार्य को स्वयं नहीं कहते हैं। इसका कारण इस तरह से काम करना है, यह है कि कार्य को खत्म करना है, और लूप को बाहर करना है (मेजबान के, एनवी मामले में कोई नहीं, लेकिन यह उस बिंदु पर एक का एक लूप है), और फिर लूप खत्म हो गया है मेजबानों (अब पूर्ववर्ती कार्य द्वारा परिभाषित) नए सिरे से।


3

आपको मॉड्यूल स्तर पर env.hosts को संशोधित करने की आवश्यकता है, किसी कार्य फ़ंक्शन के भीतर नहीं। मैंने वही गलती की।

from fabric.api import *

def _get_hosts():
    hosts = []
    ... populate 'hosts' list ...
    return hosts

env.hosts = _get_hosts()

def your_task():
    ... your task ...

3

यह बहुत सरल है। बस env.host_string वैरिएबल को इनिशियलाइज़ करें और इस होस्ट पर निम्न सभी कमांड निष्पादित किए जाएंगे।

from fabric.api import env, run

env.host_string = 'user@exmaple.com'

def foo:
    run("hostname -f")

3

मैं कपड़े के लिए पूरी तरह से नया हूं, लेकिन कई मेजबानों पर एक ही कमांड चलाने के लिए फैब्रिक प्राप्त करना (जैसे एक कमांड में कई सर्वर पर तैनात करना), आप चला सकते हैं:

fab -H staging-server,production-server deploy 

जहां स्टेजिंग-सर्वर और प्रोडक्शन-सर्वर 2 सर्वर हैं, जिनके खिलाफ आप तैनाती कार्रवाई चलाना चाहते हैं। यहाँ एक सरल fabfile.py है जो OS नाम प्रदर्शित करेगा। ध्यान दें कि fabfile.py उसी निर्देशिका में होना चाहिए जहाँ आप फ़ैब कमांड चलाते हैं।

from fabric.api import *

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

यह कम से कम 1.8.1 कपड़े के साथ काम करता है।


3

इसलिए, मेजबानों को सेट करने के लिए, और सभी मेजबानों पर कमांड चलाने के लिए, आपको इसके साथ शुरू करना होगा:

def PROD():
    env.hosts = ['10.0.0.1', '10.0.0.2']

def deploy(version='0.0'):
    sudo('deploy %s' % version)

एक बार जब वे परिभाषित होते हैं, तो कमांड लाइन पर कमांड चलाते हैं:

fab PROD deploy:1.5

PROD फ़ंक्शन में सूचीबद्ध सभी सर्वरों में परिनियोजन कार्य क्या चलाएगा, क्योंकि यह कार्य चलाने से पहले env.hosts सेट करता है।


माना कि पहले होस्ट पर काम किया गया था, लेकिन दूसरे पर एक असफल रहा, मैं इसे केवल दूसरे पर कैसे करूं?
ओपन स्कूल

2

आप env.hoststringएक सबटैक निष्पादित करने से पहले असाइन कर सकते हैं । यदि आप कई मेजबानों पर पुनरावृति करना चाहते हैं तो इस वैश्विक चर को एक लूप में असाइन करें।

दुर्भाग्य से आपके और मेरे लिए, कपड़े इस उपयोग के मामले के लिए डिज़ाइन नहीं किए गए हैं। की जाँच करें mainपर समारोह http://github.com/bitprophet/fabric/blob/master/fabric/main.py देखने के लिए कि यह कैसे काम करता है।


2

यहां एक और "सारांश" पैटर्न है जो fab my_env_1 my_commandउपयोग को सक्षम करता है :

इस पैटर्न के साथ, हमें केवल एक शब्दकोश का उपयोग करते हुए वातावरण को परिभाषित करना होगा। env_factoryके कीनेम के आधार पर कार्य करता है ENVS। मैंने ENVSअपनी खुद की डायरेक्टरी और फाइल secrets.config.pyको फैब्रिक कोड से अलग करने के लिए रखा।

दोष यह है कि, जैसा कि लिखा गया है, @taskडेकोरेटर को जोड़ने से यह टूट जाएगा ।

नोट: हम देर से बाध्यकारी होने के कारण कारखाने में उपयोग def func(k=k):करते हैं । हम इस समाधान के साथ रनिंग मॉड्यूल प्राप्त करते हैं और फ़ंक्शन को परिभाषित करने के लिए इसे पैच करते हैं।def func():

secrets.config.py

ENVS = {
    'my_env_1': {
        'HOSTS': [
            'host_1',
            'host_2',
        ],
        'MY_OTHER_SETTING': 'value_1',
    },
    'my_env_2': {
        'HOSTS': ['host_3'],
        'MY_OTHER_SETTING': 'value_2'
    }
}

fabfile.py

import sys
from fabric.api import env
from secrets import config


def _set_env(env_name):
    # can easily customize for various use cases
    selected_config = config.ENVS[env_name]
    for k, v in selected_config.items():
        setattr(env, k, v)


def _env_factory(env_dict):
    for k in env_dict:
        def func(k=k):
            _set_env(k)
        setattr(sys.modules[__name__], k, func)


_env_factory(config.ENVS)

def my_command():
    # do work

0

वर्तमान में भूमिकाओं का उपयोग करना "उचित" और "सही" करने का तरीका माना जाता है और यही आपको करना चाहिए।

उस ने कहा, यदि आप सबसे अधिक हैं जो आप "पसंद करेंगे" या "इच्छा" एक "मुड़ सिस्टर" या मक्खी पर लक्ष्य प्रणालियों को स्विच करने की क्षमता है।

केवल मनोरंजन के उद्देश्यों के लिए!) निम्नलिखित उदाहरण दिखाता है कि कई लोग एक जोखिम भरा विचार कर सकते हैं, और फिर भी किसी तरह पूरी तरह से संतोषजनक, पैंतरेबाज़ी जो कुछ इस तरह से जाती है:

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.