Cách đặt máy chủ đích trong tệp Fabric


107

Tôi muốn sử dụng Fabric để triển khai mã ứng dụng web của mình tới các máy chủ phát triển, dàn dựng và sản xuất. Fabfile của tôi:

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

Đầu ra mẫu:

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

Khi tôi tạo một set_hosts()tác vụ như được hiển thị trong tài liệu Vải , env.hosts được đặt đúng cách. Tuy nhiên, đây không phải là một lựa chọn khả thi, thợ trang trí cũng vậy. Chuyển các máy chủ trên dòng lệnh cuối cùng sẽ dẫn đến một số loại kịch bản shell gọi fabfile, tôi muốn có một công cụ duy nhất thực hiện công việc đúng cách.

Nó nói trong tài liệu Fabric rằng 'env.hosts chỉ đơn giản là một đối tượng danh sách Python'. Từ quan sát của tôi, điều này đơn giản là không đúng.

Bất cứ ai có thể giải thích những gì đang xảy ra ở đây? Làm cách nào để đặt máy chủ để triển khai?


Tôi có cùng một vấn đề, bạn đã tìm thấy bất kỳ giải pháp cho điều này?
Martin M.

để chạy các nhiệm vụ tương tự đối với nhiều máy chủ, sử dụng "-H fab dàn-server, triển khai sản xuất-server" ... chi tiết trong câu trả lời của tôi dưới đây: stackoverflow.com/a/21458231/26510
Brad Parks


Câu trả lời này không áp dụng cho vải 2+. Nếu ai đó quen thuộc hơn với các quy ước Stackoverflow có thể chỉnh sửa câu hỏi hoặc tiêu đề câu hỏi để tham chiếu đến kết cấu 1, điều đó có thể hữu ích.
Jonathan Berger

Câu trả lời:


128

Tôi làm điều này bằng cách khai báo một chức năng thực tế cho mỗi môi trường. Ví dụ:

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

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

def deploy():
    ...

Sử dụng các chức năng trên, tôi sẽ nhập như sau để triển khai cho môi trường thử nghiệm của mình:

fab test deploy

... và những thứ sau để triển khai vào sản xuất:

fab prod deploy

Điều thú vị khi làm theo cách này là các hàm testprodcó thể được sử dụng trước bất kỳ hàm fab nào , không chỉ triển khai. Nó vô cùng hữu ích.


10
Do một lỗi trong kết cấu ( code.fabfile.org/issues/show/138#change-1497 ), tốt hơn nên đưa người dùng vào chuỗi máy chủ (như produser@prod.server.com) thay vì đặt env.user.
Mikhail Korobov

1
Tôi đã gặp vấn đề tương tự, và đây có vẻ là giải pháp tốt nhất. Tôi xác định máy chủ, người dùng và nhiều cài đặt khác trong tệp YAML được tải bởi các hàm dev () và prod (). (Để tôi có thể sử dụng lại cùng một tập lệnh Fabric cho các dự án tương tự.)
Christian Davén

@MikhailKorobov: Khi tôi theo liên kết của bạn, tôi thấy " Chào mừng bạn đến với nginx! ". Tất cả các yêu cầu đối với code.fabfile.orgmiền đều có phản hồi như vậy.
Tadeck

Vâng, có vẻ như tất cả các lỗi đã được chuyển sang github.
Mikhail Korobov

2
Thật không may, có vẻ như điều này không còn hoạt động - vải sẽ không chạy các tác vụ mà không có env.hosts đã được xác định và sẽ không chạy các chức năng fab A B Ctheo kiểu mà chúng không được xác định là tác vụ.
DNelson

77

Sử dụng 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')

Chọn vai trò với -R:

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

7
Hoặc nếu nhiệm vụ luôn được chạy trên cùng một vai trò, bạn có thể sử dụng trình trang trí @roles () cho tác vụ.
Tom

2
Có vẻ như roledefs là một giải pháp tốt hơn là xác định chúng trong các nhiệm vụ riêng biệt.
Ehtesh Choudhury

Có ai biết cách tôi có thể bao gồm mật khẩu cho tên người dùng được cung cấp trong một roledefkhông? Một mục từ điển khác 'password': 'some_password'dường như bị bỏ qua và dẫn đến lời nhắc trong thời gian chạy.
Dirk

@Dirk bạn có thể sử dụng env.passwords là một từ điển có người dùng + máy chủ + cổng làm khóa và mật khẩu làm giá trị. Ví dụ: env.passwords = {'user @ host: 22': 'password'}
Jonathan

49

Đây là một phiên bản đơn giản hơn của câu trả lời của serverhorror :

from fabric.api import settings

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

2
Theo tài liệu , trình quản lý ngữ cảnh cài đặt dùng để ghi đè envcác biến chứ không phải để đặt chúng ban đầu. Tôi nghĩ rằng sử dụng roledefs , như thomie đã đề xuất, thích hợp hơn để xác định các máy chủ như stage, dev và test.
Tony

21

Bản thân tôi đã bị mắc kẹt về điều này, nhưng cuối cùng đã tìm ra nó. Đơn giản là bạn không thể đặt cấu hình env.hosts từ bên trong một tác vụ. Mỗi tác vụ được thực thi N lần, một lần cho mỗi Máy chủ được chỉ định, vì vậy, về cơ bản cài đặt nằm ngoài phạm vi tác vụ.

Nhìn vào mã của bạn ở trên, bạn có thể chỉ cần thực hiện điều này:

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

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

def deploy():
    # do stuff...

Có vẻ như nó sẽ làm những gì bạn đang có ý định.

Hoặc bạn có thể viết một số mã tùy chỉnh trong phạm vi toàn cục để phân tích cú pháp các đối số theo cách thủ công và đặt env.hosts trước khi hàm tác vụ của bạn được xác định. Vì một vài lý do, đó thực sự là cách tôi thiết lập.


Đã tìm ra cách : from fabric.api import env; env.host_string = "dev"
Roman

18

Vì fab 1.5, đây là một cách được lập thành văn bản để thiết lập động các máy chủ.

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

Trích dẫn từ tài liệu dưới đây.

Sử dụng thực thi với danh sách máy chủ được thiết lập động

Một trường hợp sử dụng phổ biến từ trung cấp đến nâng cao cho Fabric là tham số hóa tra cứu danh sách máy chủ đích của một người trong thời gian chạy (khi việc sử dụng Vai trò không đủ). thực thi có thể làm cho điều này cực kỳ đơn giản, như vậy:

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. Rất nhiều câu trả lời thực sự tốt ở cuối trang ở đây.
Matt Montag

10

Trái với một số câu trả lời khác, nó có thể sửa đổi các envbiến môi trường trong một nhiệm vụ. Tuy nhiên, điều này envsẽ chỉ được sử dụng cho các tác vụ tiếp theo được thực thi bằng fabric.tasks.executehàm.

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

Nếu không bao gồm các nhiệm vụ phụ execute(...), envcài đặt cấp mô-đun của bạn hoặc bất kỳ cài đặt nào được chuyển từ fabCLI sẽ được sử dụng.


Đây là câu trả lời tốt nhất nếu bạn muốn đặt env.hosts động.
JahMyst

9

Bạn cần đặt host_stringmột ví dụ sẽ là:

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

Ngọt. Tôi đã đăng một phiên bản mã đơn giản hơn trong một câu trả lời khác ở đây.
tobych

9

Để giải thích tại sao nó thậm chí là một vấn đề. Lệnh fab đang tận dụng cấu trúc thư viện để chạy các tác vụ trên danh sách máy chủ. Nếu bạn thử và thay đổi danh sách máy chủ bên trong một tác vụ, thì chắc chắn bạn đang cố gắng thay đổi một danh sách trong khi lặp lại nó. Hoặc trong trường hợp bạn không có máy chủ nào được xác định, hãy lặp qua một danh sách trống trong đó mã nơi bạn đặt danh sách lặp lại không bao giờ được thực thi.

Việc sử dụng env.host_string chỉ giải quyết được hành vi này ở chỗ nó chỉ định trực tiếp cho các chức năng mà máy chủ sẽ kết nối với. Điều này gây ra một số vấn đề trong đó bạn sẽ phải tạo lại vòng lặp thực thi nếu bạn muốn có một số máy chủ để thực thi.

Cách đơn giản nhất mà mọi người tạo ra khả năng đặt máy chủ tại thời điểm chạy, là giữ cho việc phổ biến env như một nhiệm vụ riêng biệt, thiết lập tất cả các chuỗi máy chủ, người dùng, v.v. Sau đó, họ chạy tác vụ triển khai. Nó trông như thế này:

fab production deploy

hoặc là

fab staging deploy

Nơi dàn dựng và sản xuất giống như các nhiệm vụ bạn đã đưa ra, nhưng chúng không tự gọi nhiệm vụ tiếp theo. Lý do nó phải hoạt động như vậy, là nhiệm vụ phải hoàn thành và thoát ra khỏi vòng lặp (của máy chủ, trong trường hợp env là Không, nhưng đó là vòng lặp của một tại thời điểm đó), và sau đó vòng lặp kết thúc các máy chủ (bây giờ được xác định bởi nhiệm vụ trước đó) một lần nữa.


3

Bạn cần sửa đổi env.hosts ở cấp độ mô-đun, không phải trong một hàm tác vụ. Tôi đã gây ra lỗi tương tự.

from fabric.api import *

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

env.hosts = _get_hosts()

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

3

Nó rất đơn giản. Chỉ cần khởi tạo biến env.host_string và tất cả các lệnh sau sẽ được thực thi trên máy chủ này.

from fabric.api import env, run

env.host_string = 'user@exmaple.com'

def foo:
    run("hostname -f")

3

Tôi hoàn toàn mới đối với vải, nhưng để khiến vải chạy các lệnh giống nhau trên nhiều máy chủ (ví dụ: triển khai đến nhiều máy chủ, trong một lệnh), bạn có thể chạy:

fab -H staging-server,production-server deploy 

trong đó staging-serverproduction-server là 2 máy chủ mà bạn muốn chạy hành động triển khai. Đây là một fabfile.py đơn giản sẽ hiển thị tên hệ điều hành. Lưu ý rằng fabfile.py phải nằm trong cùng thư mục với nơi bạn chạy lệnh fab.

from fabric.api import *

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

Điều này ít nhất có tác dụng với vải 1.8.1.


3

Vì vậy, để thiết lập các máy chủ và có các lệnh chạy trên tất cả các máy chủ, bạn phải bắt đầu với:

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

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

Khi chúng được xác định, hãy chạy lệnh trên dòng lệnh:

fab PROD deploy:1.5

Điều gì sẽ chạy tác vụ triển khai trên tất cả các máy chủ được liệt kê trong hàm PROD, vì nó đặt env.hosts trước khi chạy tác vụ.


Giả sử việc triển khai trên máy chủ đầu tiên hoạt động nhưng máy chủ thứ hai không thành công, làm cách nào để thực hiện lại chỉ trên máy chủ thứ hai?
nos

2

Bạn có thể gán cho env.hoststringtrước khi thực hiện một nhiệm vụ con. Gán cho biến toàn cục này trong một vòng lặp nếu bạn muốn lặp qua nhiều máy chủ.

Thật không may cho bạn và tôi, vải không được thiết kế cho trường hợp sử dụng này. Kiểm tra mainchức năng tại http://github.com/bitprophet/fnai/blob/master/fainst/main.py để xem nó hoạt động như thế nào.


2

Đây là một mẫu "mùa hè" khác cho phép fab my_env_1 my_commandsử dụng:

Với mẫu này, chúng ta chỉ phải xác định môi trường một lần bằng từ điển. env_factorytạo các hàm dựa trên tên khóa của ENVS. Tôi đặt ENVSvào thư mục và tệp riêng của nó secrets.config.pyđể tách cấu hình khỏi mã vải.

Hạn chế là, như đã viết, việc thêm trình @tasktrang trí sẽ phá vỡ nó .

Ghi chú: Chúng tôi sử dụng def func(k=k):thay vì def func():trong nhà máy vì ràng buộc muộn . Chúng tôi nhận được mô-đun đang chạy với giải pháp này và vá nó để xác định chức năng.

secret.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

Sử dụng vai trò hiện được coi là cách làm "phù hợp" và "đúng đắn" và là điều bạn "nên" làm.

Điều đó nói rằng, nếu bạn giống với hầu hết những gì bạn "muốn" hoặc "mong muốn" là khả năng thực hiện một "hệ thống đồng bộ xoắn" hoặc chuyển đổi hệ thống mục tiêu một cách nhanh chóng.

Vì vậy, chỉ dành cho mục đích giải trí (!), Ví dụ sau đây minh họa những gì nhiều người có thể coi là một hành động mạo hiểm, nhưng bằng cách nào đó hoàn toàn thỏa mãn, diễn ra như thế này:

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

Sau đó chạy:

fab perform_sumersault
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.