Fabric dosyasında hedef ana bilgisayarlar nasıl ayarlanır


107

Web uygulaması kodumu geliştirme, hazırlama ve üretim sunucularına dağıtmak için Fabric'i kullanmak istiyorum. Benim fabrikam:

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

Örnek çıktı:

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

Fabric belgelerindeset_hosts() gösterildiği gibi bir görev oluşturduğumda env.hosts düzgün ayarlandı. Ancak bu geçerli bir seçenek değil, dekoratör de değil. Ana bilgisayarları komut satırına aktarmak, sonuçta fabfile'ı çağıran bir tür kabuk betiği ile sonuçlanacaktır, tek bir aracın işi düzgün bir şekilde yapmasını tercih ederim.

Fabric belgelerinde "env.hosts basitçe bir Python liste nesnesidir" yazıyor. Gözlemlerime göre, bu kesinlikle doğru değil.

Burada neler olduğunu kimse açıklayabilir mi? Ana bilgisayarı dağıtılacak şekilde nasıl ayarlayabilirim?


Bende de aynı sorun var, buna bir çözüm buldunuz mu?
Martin M.

aynı görevi birden çok sunucuda çalıştırmak için "fab -H staging-server, production-server deploy" kullanın ... aşağıdaki
Brad Parks


Bu cevap, kumaş 2+ için geçerli değildir. Stackoverflow kurallarına daha aşina olan biri, kumaş 1'e atıfta bulunmak için soruyu veya soru başlığını düzenleyebilirse, bu yardımcı olabilir.
Jonathan Berger

Yanıtlar:


128

Bunu her ortam için gerçek bir işlev bildirerek yapıyorum. Örneğin:

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

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

def deploy():
    ...

Yukarıdaki işlevleri kullanarak, test ortamıma dağıtmak için aşağıdakileri yazardım:

fab test deploy

... ve üretime dağıtmak için aşağıdakiler:

fab prod deploy

Bu şekilde yapmanın güzel yanı, testve prodişlevlerinin herhangi bir fab işlevinden önce kullanılabilmesidir , yalnızca dağıtılmadan değil. İnanılmaz derecede faydalıdır.


10
Yapıdaki bir hata nedeniyle ( code.fabfile.org/issues/show/138#change-1497 ) env.user'ı ayarlamak yerine kullanıcıyı ana bilgisayar dizesine (produser@prod.server.com gibi) dahil etmek daha iyidir.
Mikhail Korobov

1
Aynı sorunu yaşadım ve bu en iyi çözüm gibi görünüyor. Dev () ve prod () işlevleri tarafından yüklenen bir YAML dosyasında ana bilgisayarları, kullanıcıyı ve diğer birçok ayarı tanımlıyorum. (Böylece aynı Fabric komut dosyasını benzer projeler için yeniden kullanabilirim.)
Christian Davén

@MikhailKorobov: Bağlantınızı takip ettiğimde "nginx'e hoş geldiniz! " code.fabfile.orgEtki alanına gönderilen tüm istekler buna benzer yanıtlara sahiptir.
Tadeck

Evet, görünüşe göre tüm hatalar github'a taşınmış.
Mikhail Korobov

2
Ne yazık ki, bu artık işe yaramıyor gibi görünüyor - Fabric, env.hosts önceden tanımlı olmadan görevleri çalıştırmaz ve görevler fab A B Colarak tanımlanmadan stildeki işlevleri çalıştırmaz .
DNelson

77

Roledef kullanın

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 ile rol seçin:

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

7
Veya görev her zaman aynı rolde çalıştırılıyorsa, görevde @roller () dekoratörünü kullanabilirsiniz.
Tom

2
Roledef'ler, onları ayrı görevlerde tanımlamaktan daha iyi bir çözüm gibi görünüyor.
Ehtesh Choudhury

Sağlanan kullanıcı adı için bir şifreyi bir içine nasıl ekleyebileceğimi bilen var mı roledef? Başka bir sözlük girişi 'password': 'some_password'göz ardı edilmiş gibi görünüyor ve çalışma zamanında bir uyarıya yol açıyor.
Dirk

@Dirk anahtar olarak user + host + port ve değer olarak şifre içeren bir sözlük olan env.passwords kullanabilirsiniz. Ör. Env.passwords = {'kullanıcı @ ana bilgisayar: 22': 'şifre'}
Jonathan

49

İşte serverhorror cevabının daha basit bir versiyonu :

from fabric.api import settings

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

2
Başına dokümanlar , ayarlar bağlam yöneticisi geçersiz kılmaya yönelik olduğunu envbaşlangıçta onları ayarlanması için değil, değişkenleri. Kullanıyorum düşünüyorum roledefs thomie önerildiği gibi, sahne, dev ve test gibi ana tanımlamak için daha uygundur.
Tony

21

Buna kendim takılmıştım, ama sonunda anladım. Sadece edemez gelen env.hosts yapılandırmasını ayarlamak içinde bir görev. Her görev, belirtilen her Ana Bilgisayar için bir kez olmak üzere N kez yürütülür, bu nedenle ayar temelde görev kapsamının dışındadır.

Yukarıdaki kodunuza bakarak şunu yapabilirsiniz:

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

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

def deploy():
    # do stuff...

Bu da niyet ettiğin şeyi yapacak gibi görünüyor.

Veya genel kapsamda bağımsız değişkenleri manuel olarak ayrıştıran ve görev işleviniz tanımlanmadan önce env.hosts ayarlayan bazı özel kodlar yazabilirsiniz. Birkaç nedenden ötürü, aslında benimkini böyle kurdum.


Bir yol buldum from fabric.api import env:; env.host_string = "dev"
Roman

18

Fab 1.5'ten beri bu, ana bilgisayarları dinamik olarak ayarlamak için belgelenmiş bir yoldur.

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

Aşağıdaki dokümandan alıntı yapın.

Dinamik olarak ayarlanmış ana bilgisayar listeleri ile yürütmeyi kullanma

Fabric için yaygın bir orta ila ileri düzey kullanım örneği, çalışma zamanında kişinin hedef ana bilgisayar listesinin aramasını parametrelendirmektir (Rollerin kullanımı yeterli olmadığında). execute bunu şu şekilde son derece basit hale getirebilir:

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. Sayfanın alt kısmına doğru birçok gerçekten iyi yanıt var.
Matt Montag

10

Diğer bazı cevaplar aksine, olduğu değiştirmek mümkün envbir görev içinde ortam değişkenleri. Ancak, bu envyalnızca fabric.tasks.executeişlev kullanılarak yürütülen sonraki görevler için kullanılacaktır .

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

Alt görevleri sarmadan execute(...), modül düzeyindeki envayarlarınız veya CLI'den geçirilen her şey fabkullanılacaktır.


Env.hosts'u dinamik olarak ayarlamak istiyorsanız bu en iyi yanıttır.
JahMyst

9

host_stringBir örnek belirlemeniz gerekir:

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

Tatlı. Kodun daha basit bir versiyonunu burada başka bir cevapta yayınladım.
tobych

9

Bunun neden bir sorun olduğunu açıklamak için. Fab komutu , ana bilgisayar listelerindeki görevleri çalıştırmak için kitaplıktan yararlanıyor. Bir görevin içindeki ana bilgisayar listesini değiştirmeye çalışır ve değiştirirseniz, aslında üzerinde yinelerken bir listeyi değiştirmeye çalışırsınız. Veya tanımlanmış bir ana makinenizin olmadığı durumda, listeyi döngü için ayarladığınız kodun asla çalıştırılmadığı boş bir liste üzerinden döngü yapın.

Env.host_string kullanımı, yalnızca hangi ana bilgisayarların bağlanacağını işlevlere doğrudan belirtmesi açısından bu davranış için bir çözümdür. Bu, çalıştırılacak birkaç ana makineye sahip olmak istiyorsanız, yürütme döngüsünü yeniden oluşturacağınız için bazı sorunlara neden olur.

İnsanların ana bilgisayarları çalışma zamanında ayarlayabilmesinin en basit yolu, ortamın tüm ana bilgisayar dizelerini, kullanıcıları vb. Ayarlayan ayrı bir görev olarak doldurulmasını sağlamaktır. Sonra konuşlandırma görevini çalıştırırlar. Şöyle görünüyor:

fab production deploy

veya

fab staging deploy

Hazırlama ve prodüksiyon, verdiğiniz görevler gibi olduğunda, ancak bir sonraki görevi kendileri olarak adlandırmazlar. Bunun bu şekilde çalışması gerekmesinin nedeni, görevin bitmesi ve döngüden çıkması (ana bilgisayarlarda, env durumunda Yok, ancak bu noktada bir döngüdür) ve sonra döngüye sahip olmasıdır. ana bilgisayarlar (şimdi önceki görev tarafından tanımlanmıştır) yeniden.


3

Env.hosts'u bir görev işlevi içinde değil, modül düzeyinde değiştirmeniz gerekir. Ben de aynı hatayı yaptım.

from fabric.api import *

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

env.hosts = _get_hosts()

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

3

Çok basit. Sadece env.host_string değişkenini başlatın ve aşağıdaki komutların tümü bu ana bilgisayarda çalıştırılacaktır.

from fabric.api import env, run

env.host_string = 'user@exmaple.com'

def foo:
    run("hostname -f")

3

Yapı konusunda tamamen yeniyim, ancak aynı komutları birden fazla ana bilgisayarda (örneğin, birden çok sunucuya tek bir komutla dağıtmak için) çalıştıracak yapı elde etmek için şunları çalıştırabilirsiniz:

fab -H staging-server,production-server deploy 

burada hazırlama sunucusu ve üretim sunucusu , dağıtım eylemini ona karşı çalıştırmak istediğiniz 2 sunucudur . İşte işletim sistemi adını gösterecek basit bir fabfile.py. Fabfile.py dosyasının, fab komutunu çalıştırdığınız dizinde olması gerektiğini unutmayın.

from fabric.api import *

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

Bu, en azından 1.8.1 kumaş ile çalışır.


3

Bu nedenle, ana bilgisayarları ayarlamak ve komutların tüm ana bilgisayarlarda çalışmasını sağlamak için şununla başlamalısınız:

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

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

Bunlar tanımlandıktan sonra, komut satırında komutu çalıştırın:

fab PROD deploy:1.5

Görevi çalıştırmadan önce env.hosts'u ayarladığından, PROD işlevinde listelenen tüm sunucularda dağıtım görevini ne çalıştırır.


İlk ana bilgisayardaki dağıtımın işe yaradığını, ancak ikincisindekinin başarısız olduğunu varsayalım, bunu yalnızca ikincisinde nasıl tekrar yaparım?
no


2

İşte fab my_env_1 my_commandkullanımı mümkün kılan başka bir "yaz atışı" kalıbı :

Bu kalıpla, ortamları sadece bir kez sözlük kullanarak tanımlamamız gerekir. env_factoryanahtar adlarına göre işlevler oluşturur ENVS. Yapılandırmayı kumaş kodundan ayırmak ENVSiçin kendi dizinini ve dosyasını koydum secrets.config.py.

Dezavantajı, yazıldığı gibi, @taskdekoratörün eklenmesinin onu kırmasıdır . .

Notlar: Geç bağlama nedeniyle fabrikada def func(k=k):yerine kullanıyoruz . Çalışan modülü bu çözümle alıyoruz ve işlevi tanımlamak için yamalıyoruz.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

Rolleri kullanmak şu anda bunu yapmanın "uygun" ve "doğru" yolu olarak kabul edilmektedir ve bunu "yapmanız gereken" şeydir.

Bununla birlikte, "istediğiniz" veya "arzu ettiğiniz" çoğu şey gibiyseniz, "bükülmüş bir sistem" gerçekleştirme veya anında hedef sistemleri değiştirme becerisidir.

Bu nedenle, yalnızca eğlence amaçlı (!) Aşağıdaki örnek, birçok kişinin riskli ve yine de bir şekilde tam anlamıyla tatmin edici bir manevra olarak değerlendirebileceği bir şeyi göstermektedir:

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

Sonra koşuyor:

fab perform_sumersault
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.