Fabric 파일에서 대상 호스트를 설정하는 방법


107

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:

Fabric 문서에set_hosts() 표시된대로 작업을 생성하면 env.hosts가 올바르게 설정됩니다. 그러나 이것은 실행 가능한 옵션이 아니며 데코레이터도 아닙니다. 명령 줄에서 호스트를 전달하면 궁극적으로 fabfile을 호출하는 일종의 셸 스크립트가 생성됩니다. 저는 하나의 도구가 작업을 제대로 수행하는 것을 선호합니다.

Fabric 문서에서 'env.hosts는 단순히 Python 목록 객체'라고 말합니다. 내 관찰에 따르면 이것은 사실이 아닙니다.

아무도 여기서 무슨 일이 일어나는지 설명 할 수 있습니까? 배포 할 호스트를 어떻게 설정할 수 있습니까?


나도 같은 문제가 있습니다. 이것에 대한 해결책을 찾았습니까?
Martin M.

: 여러 서버 사용 "팹 -H 스테이징 서버, 프로덕션 서버 배포"... 더 아래에있는 내 대답에 대해 동일한 작업 실행 stackoverflow.com/a/21458231/26510
브래드 공원


이 답변은 패브릭 2+에는 적용되지 않습니다. Stackoverflow 규칙에 더 익숙한 사람이 패브릭 1을 참조하도록 질문 또는 질문 제목을 편집 할 수 있다면 도움이 될 수 있습니다.
Jonathan Berger

답변:


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

이 방법으로 그 일의 좋은 점은 것입니다 testprod기능 이전에 사용 할 수 있는 단지 배포하지, 팹 기능. 매우 유용합니다.


10
패브릭 ( code.fabfile.org/issues/show/138#change-1497 ) 의 버그로 인해 env.user를 설정하는 대신 호스트 문자열 (예 : produser@prod.server.com)에 사용자를 포함하는 것이 좋습니다.
Mikhail Korobov

1
나는 같은 문제가 있었고 이것이 최선의 해결책 인 것 같습니다. dev () 및 prod () 함수에 의해로드되는 YAML 파일에서 호스트, 사용자 및 기타 많은 설정을 정의합니다. (그래서 유사한 프로젝트에 대해 동일한 원단 스크립트를 재사용 할 수있다.)
기독교 Davén

@MikhailKorobov : 귀하의 링크를 따라갈 때 "Nginx에 오신 것을 환영합니다! " 보았습니다 . code.fabfile.org도메인에 대한 모든 요청 에는 이와 같은 응답이 있습니다.
Tadeck

예, 모든 버그가 github로 마이그레이션 된 것 같습니다.
Mikhail Korobov 2012

2
불행히도 이것은 더 이상 작동하지 않는 것처럼 보입니다. fabric은 env.hosts가 이미 정의되어 있지 않으면 작업을 실행하지 않으며 작업으로 정의되지 않으면 fab A B C스타일 에서 함수를 실행하지 않습니다 .
DNelson

77

roledef 사용

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 () 데코레이터를 사용할 수 있습니다.
Tom

2
roledefs는 별도의 작업에서 정의하는 것보다 더 나은 솔루션 인 것처럼 들립니다.
Ehtesh Choudhury 2014 년

아무도 내가 제공된 사용자 이름에 대한 암호를 어떻게 포함시킬 수 있는지 알고 roledef있습니까? 추가 사전 항목 'password': 'some_password'은 무시 된 것으로 보이며 런타임에 프롬프트가 표시됩니다.
Dirk

@Dirk는 사용자 + 호스트 + 포트를 키로, 암호를 값으로 포함하는 사전 인 env.passwords를 사용할 수 있습니다. 예 : env.passwords = { 'user @ host : 22': 'password'}
Jonathan

49

다음은 serverhorror의 대답의 간단한 버전입니다 .

from fabric.api import settings

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

2
워드 프로세서 , 설정 컨텍스트 매니저는 오버라이드 (override)입니다 env처음을 설정하지, 변수를. 내가 사용하는 생각 roledefs을 thomie 제안으로, 무대, 개발 및 테스트와 같은 호스트를 정의하기위한 더 적절하다.
Tony

21

이것에 스스로 붙어 있었지만 마침내 그것을 알아 냈습니다. 당신은 단순히 수 없습니다 에서 env.hosts 구성을 설정 내에서 작업. 각 작업은 지정된 각 호스트에 대해 한 번씩 N 번 실행되므로 설정은 기본적으로 작업 범위를 벗어납니다.

위의 코드를 보면 간단하게 다음과 같이 할 수 있습니다.

@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"
Roman

18

fab 1.5부터 이것은 호스트를 동적으로 설정하는 문서화 된 방법입니다.

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

아래 문서에서 인용하십시오.

동적으로 설정된 호스트 목록과 함께 실행 사용

Fabric의 일반적인 중상급 사용 사례는 런타임시 대상 호스트 목록 조회를 매개 변수화하는 것입니다 (역할 사용으로 충분하지 않은 경우). execute는 다음과 같이 매우 간단하게 만들 수 있습니다.

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. 여기 페이지 하단에 정말 좋은 답변이 많이 있습니다.
Matt Montag

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설정 또는 fabCLI 에서 전달 된 모든 항목이 사용됩니다.


env.hosts를 동적으로 설정하려는 경우 이것이 가장 좋은 대답입니다.
JahMyst

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

왜 그것이 문제인지 설명하기 위해. fab 명령 은 라이브러리의 패브릭을 활용하여 호스트 목록에서 작업을 실행합니다. 작업 내에서 호스트 목록을 변경하려고하면 본질적으로 목록을 반복하는 동안 목록을 변경하려고합니다. 또는 정의 된 호스트가없는 경우 목록을 반복하도록 설정 한 코드가 실행되지 않는 빈 목록을 반복합니다.

env.host_string의 사용은 연결할 호스트를 함수에 직접 지정한다는 점에서만이 동작에 대한 해결 방법입니다. 이로 인해 여러 호스트를 실행하려는 경우 실행 루프를 다시 만들어야한다는 몇 가지 문제가 발생합니다.

사람들이 런타임에 호스트를 설정하는 기능을 만드는 가장 간단한 방법은 모든 호스트 문자열, 사용자 등을 설정하는 별개의 작업으로 env 팝업을 유지하는 것입니다. 그런 다음 배포 작업을 실행합니다. 다음과 같이 보입니다.

fab production deploy

또는

fab staging deploy

스테이징 및 프로덕션은 귀하가 제공 한 작업과 비슷하지만 다음 작업을 직접 호출하지는 않습니다. 이렇게 작동해야하는 이유는 작업이 완료되고 루프 (호스트의 경우, None의 경우 None이지만 해당 지점에서 하나의 루프)를 종료 한 다음 루프를 완료해야하기 때문입니다. 호스트 (이제 이전 작업에서 정의 됨)가 새로워집니다.


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 개의 서버입니다. 다음은 OS 이름을 표시하는 간단한 fabfile.py입니다. fabfile.py는 fab 명령을 실행하는 디렉토리와 동일한 디렉토리에 있어야합니다.

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

태스크를 실행하기 전에 env.hosts를 설정하므로 PROD 기능에 나열된 모든 서버에서 배치 태스크를 실행합니다.


첫 번째 호스트의 배포가 작동했지만 두 번째 호스트의 배포가 실패했다고 가정하면 두 번째 호스트에서만 어떻게 다시 수행합니까?
nos

2

env.hoststring하위 작업을 실행 하기 전에 할당 할 수 있습니다 . 여러 호스트를 반복하려면 루프에서이 전역 변수에 할당하십시오.

여러분과 저에게 안타깝게도 패브릭은이 사용 사례에 맞게 설계되지 않았습니다. 작동 방식을 보려면 http://github.com/bitprophet/fabric/blob/master/fabric/main.py 에서 main함수를 확인하십시오 .


2

다음은 fab my_env_1 my_command사용 을 가능하게하는 또 다른 "summersault"패턴입니다 .

이 패턴을 사용하면 사전을 사용하여 환경을 한 번만 정의하면됩니다. 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.