Fabric + Capistrano 를 이용 하여 Python 자동화 배 치 를 실현 합 니 다.

17697 단어 PythonFabricLinux
Fabric 은 (대량) 배치 와 시스템 (대량) 관 리 를 위 한 Python 라 이브 러 리 와 명령 행 도구 입 니 다. Fabric 에 대한 소 개 는 다음 을 참고 하 십시오.http://www.fabfile.org/。
  Capistrano 는 Ruby 언어 로 작 성 된 원 격 서버 자동화 및 배치 도구 입 니 다. Capistrano 에 대한 소 개 는 참고 하 십시오.http://capistranorb.com/。
  본 고 는 Python 언어 와 일부 Linux 또는 Windows 시스템 명령 만 사용 하고 Fabric 모듈 과 Capistrano 의 배치 방향 을 통 해 Linux 플랫폼 과 Windows 플랫폼 의 자동화 부 에서 대량으로 배치 하거나 대량 시스템 관리 (대량 실행 명령, 파일 대량 업로드 등) 를 실현 한다. 그 중에서 Fabric 부분 은 Fabric 의 모듈 을 이용한다.Capistrano 부분 은 Python 언어 로 Capistrano 의 배치 사고방식 에 따라 '다시 쓰기 (Python Capistrano 실현)' 한다.
  Capistrano 에 대한 '재 작성' 설명Capistrano 는 Ruby 언어 로 쓴 것 으로 많은 응용 을 배치 하 는 데 큰 장점 을 가진다. 개인 적 으로 디자인 의 가장 좋 은 부분 은 바로 디 렉 터 리 구조 라 고 생각한다.디 렉 터 리 구조의 상세 한 정 보 는 참고 할 수 있 습 니 다.http://capistranorb.com/documentation/getting-started/structure/#。이 디 렉 터 리 구 조 는 모든 배치 버 전의 백업 과 스크롤 백 을 쉽게 실현 할 수 있 습 니 다. 이전에 Bash Shell 로 '재 작성' 한 적 이 있 습 니 다. 본 고 를 참고 할 수 있 습 니 다. 이번 에는 Python 으로 재 작성 하 는 것 과 같 습 니 다 (Capistrano 는 아직도 많은 정수 가 있 습 니 다. 본 고 는 벽돌 을 던 져 옥 을 끌 어 올 리 고 다른 것 은 나중에 발굴 하 는 것 과 같 습 니 다).셸 스 크 립 트 는 Fabric 과 같은 대량 작업 이 쉽 지 않 기 때문이다.
  본문 demo 는https://github.com/DingGuodong/GoogleHostsFileForLinux.git 에서×××Google 의 스 크 립 트 를 방문 하여 지정 한 서버 에 업로드 합 니 다. Windows 작업 의 경우 로 컬 에서 Capistrano 디 렉 터 리 구 조 를 생 성 한 다음 git 프로젝트 를 로 컬 로 복제 하여 스 크 립 트 파일 을 repo 디 렉 터 리 에서 추출 하여 current 디 렉 터 리 아래 에 놓 습 니 다. current 는 release 디 렉 터 리 아래 에 있 는 플 로 피 링크 입 니 다.(Windows 에서 테스트 하 는 것 이 이상 할 수 있 습 니 다. Windows 에서 소프트 연결 을 할 수 없 기 때문에 Python 으로 단축 키 를 만 드 는 방법 을 찾 을 수 없습니다) 이 스 크 립 트 를 Fabric 의 put 명령 을 통 해 지정 한 원 격 서버 에 업로드 합 니 다.
  demo 는 스 크 립 트 의 위 치 를 TODO 에서 찾 을 수 있 습 니 다. fabric 은 fab 명령 + 스 크 립 트 를 통 해 실행 해 야 하기 때문에 스 크 립 트 의 마지막 에 terminal debug () 함 수 를 사용 하여 스 크 립 트 를 실 행 했 습 니 다. python 과 fabric 에 익숙 하 다 면 다음 스 크 립 트 를 pycharm 에 넣 고 열 고 스 크 립 트 를 살짝 보 았 습 니 다. 즉시 설명 이 없습니다.(어떤 사람 은 좋 은 코드 는 주석 을 쓰 지 않 는 다 고 했 습 니 다. 코드 를 잘 쓰 지 는 못 했 지만 적어도 이 목 표를 향 해 노력 해 야 합 니 다) 잘 볼 수 있 습 니 다. 사실 Fabric 과 Capistrano 를 진정 으로 알 게 된 후에 이 스 크 립 트 를 다시 읽 거나 이 글 을 보 는 것 은 확실히 보편적 이 라 고 생각 할 것 입 니 다.
  스 크 립 트 의 부분 설명 (시간 적 인 이 유 는 펴 지 않 고, Fabric 과 Capistrano 에 익숙해 지면 볼 수 있 음):
  • 이 스 크 립 트 파일 의 시작 줄 설정 은 config 라 는 사전 이 있 습 니 다. 주로 deploy to, repo url 과 branch, keep releases 를 설명 하 는 데 사 용 됩 니 다.
  • env 변 수 는 Fabric 에 호스트 정 보 를 설명 하 는 데 사 용 됩 니 다.
  • 실행 결과:
    利用Fabric+Capistrano实现Python自动化部署_第1张图片
    Capistrano 와 같은 디 렉 터 리 구조:
    利用Fabric+Capistrano实现Python自动化部署_第2张图片
    Capistrano 에서 생 성 한 같은 형식의 로 그 를 모방 합 니 다:
    利用Fabric+Capistrano实现Python自动化部署_第3张图片
    스 크 립 트 내용 은 GitHub 에서 가 져 올 수 있 습 니 다:https://github.com/DingGuodong/LinuxBashShellScriptForOps/blob/master/projects/autoOps/pythonSelf/pyCapistrano.py
    스 크 립 트 내용 은 다음 과 같 습 니 다.
    #!/usr/bin/python
    # encoding: utf-8
    # -*- coding: utf8 -*-
    """
    Created by PyCharm.
    File:               LinuxBashShellScriptForOps:TestGit.py
    User:               Guodong
    Create Date:        2016/8/24
    Create Time:        9:40
     """
    from fabric.api import *
    from fabric.main import main
    from fabric.colors import *
    from fabric.context_managers import *
    from fabric.contrib.console import confirm
    import os
    import sys
    import re
    import getpass
    
    config = {
        "deploy_to": '/var/www/my_app_name',
        "scm": 'git',
        "repo_url": 'https://github.com/DingGuodong/GoogleHostsFileForLinux.git',
        "branch": 'master',
        "log_level": 'debug',
        "keep_releases": 10
    }
    
    env.roledefs = {
        'test': ['[email protected]:22', ],
        'nginx': ['[email protected]:22', '[email protected]:22', ],
        'db': ['[email protected]:22', '[email protected]:22', ],
        'sit': ['[email protected]:22', '[email protected]:22', '[email protected]:22', ],
        'uat': ['[email protected]:22', '[email protected]:22', '[email protected]:22', ],
        'all': ["10.6.28.27", "10.6.28.28", "10.6.28.35", "10.6.28.46", "10.6.28.93", "10.6.28.125", "10.6.28.135"]
    }
    
    env.user = "root"
    env.hosts = ["10.6.28.27", "10.6.28.28", "10.6.28.35", "10.6.28.46", "10.6.28.93", "10.6.28.125", "10.6.28.135"]
    
    
    def win_or_linux():
        # os.name ->(sames to) sys.builtin_module_names
        if 'posix' in sys.builtin_module_names:
            os_type = 'Linux'
        elif 'nt' in sys.builtin_module_names:
            os_type = 'Windows'
        return os_type
    
    
    def is_windows():
        if "windows" in win_or_linux().lower():
            return True
        else:
            return False
    
    
    def is_linux():
        if "linux" in win_or_linux().lower():
            return True
        else:
            return False
    
    
    class Capistrano(object):
        class SCM(object):
            class Git(object):
                def __init__(self):
                    self.repo_url = None
                    self.name = None
                    self.branch = None
                    self.repo_path = None
                    self.user = None
    
                def set(self, repo_url, branch=None, repo_path=None):
                    if repo_url is None:
                        abort("You must specify a repository to clone.")
                    else:
                        self.repo_url = repo_url
                    if branch is None:
                        self.branch = "master"
                    else:
                        self.branch = branch
    
                    pattern = re.compile(r"(\w+)(?=\.git$)")
                    match = pattern.search(repo_url)
                    if match:
                        paths = match.group()
                    else:
                        paths = None
                    if repo_path is not None and not os.path.exists(repo_path):
                        try:
                            os.mkdir(repo_path)
                        except IOError:
                            repo_path = os.path.join(os.path.dirname(__file__), paths)
                    elif repo_path is None:
                        repo_path = ""
                    self.repo_path = os.path.abspath(repo_path)
    
                def clone(self):
                    local("git clone --branch %s %s %s" % (self.branch, self.repo_url, self.repo_path))
    
                def check(self):
                    with lcd(self.repo_path):
                        return local("git ls-remote --heads %s" % self.repo_url, capture=True)
    
                def pull(self):
                    with lcd(self.repo_path):
                        if os.path.exists(os.path.join(self.repo_path, ".git")):
                            local("git pull origin %s" % self.branch)
                        else:
                            self.clone()
                            self.pull()
    
                def update(self):
                    pass
    
                def status(self):
                    with lcd(self.repo_path):
                        local("git status")
    
                def branch(self):
                    with lcd(self.repo_path):
                        local("git rev-parse --abbrev-ref HEAD", capture=True)
    
                def long_id(self):
                    with lcd(self.repo_path):
                        return local("git rev-parse HEAD", capture=True)
    
                def short_id(self):
                    with lcd(self.repo_path):
                        return local("git rev-parse --short HEAD", capture=True)
    
                def fetch_revision(self):
                    with lcd(self.repo_path):
                        return local("git rev-list --max-count=1 %s" % self.branch, capture=True)
    
                def user(self):
                    if is_linux():
                        self.user = "%s(%s)" % (os.getlogin(), os.getuid())
                    if is_windows():
                        import getpass
                        self.user = getpass.getuser()
    
        class DSL(object):
            class Paths(object):
                def __init__(self):
                    self.deploy_to = config['deploy_to']
                    self.current = None
    
                # TODO(Guodong Ding) fetch 'deploy_to' from config file or dict
                def deploy_path(self):
                    return os.path.abspath(self.deploy_to)
    
                def current_path(self):
                    current_directory = "current"
                    return os.path.join(self.deploy_path(), current_directory)
    
                def releases_path(self):
                    return os.path.join(self.deploy_path(), "releases")
    
                def set_release_path(self):
                    import datetime
                    timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
                    self.current = os.path.join(self.releases_path(), timestamp)
                    return os.path.join(self.releases_path(), timestamp)
    
                def shared_path(self):
                    return os.path.join(self.deploy_path(), "shared")
    
                def repo_path(self):
                    return os.path.join(self.deploy_path(), "repo")
    
                def revision_log(self):
                    return os.path.join(self.deploy_path(), "revisions.log")
    
                def __paths(self):
                    return self.releases_path(), self.repo_path(), self.shared_path()
    
                def makepaths(self):
                    for directory in self.__paths():
                        if not os.path.exists(directory):
                            os.makedirs(directory)
    
                def make_release_dirs(self):
                    os.makedirs(self.set_release_path())
    
                def make_current(self):
                    if is_linux():
                        if os.path.exists(self.current_path()) and os.path.islink(self.current_path()):
                            os.unlink(self.current_path())
                        os.symlink(self.current, self.current_path())
                    if is_windows():
                        if os.path.exists(self.current_path()):
                            import shutil
                            shutil.rmtree(self.current_path())
                        try:
                            local("ln -sd %s %s" % (self.current, self.current_path()))
                        except Exception:
                            raise NotImplementedError
    
                def update_revision_log(self, branch=None, sid=None, release=None, by=None):
                    print blue("Log details of the deploy")
                    with open(self.revision_log(), 'a') as f:
                        f.write("Branch %s (at %s) deployed as release %s by %s
    " % (branch, sid, release, by))             def cleanup(self):                 keep_releases = config['keep_releases']                 releases = local("ls -xtr %s" % self.releases_path(), capture=True).split()                 # print releases[-keep_releases:]                 if len(releases) > keep_releases:                     for release in releases[0:(len(releases) - keep_releases)]:                         local("rm -rf %s" % os.path.join(self.releases_path(), release))             @staticmethod             def __get_file_last_line(inputfile):                 filesize = os.path.getsize(inputfile)                 blocksize = 1024                 with open(inputfile, 'rb') as f:                     last_line = ""                     if filesize > blocksize:                         maxseekpoint = (filesize // blocksize)                         f.seek((maxseekpoint - 1) * blocksize)                     elif filesize:                         f.seek(0, 0)                     lines = f.readlines()                     if lines:                         lineno = 1                         while last_line == "":                             last_line = lines[-lineno].strip()                             lineno += 1                     return last_line             def rollback(self):                 print blue("Revert to previous release timestamp")                 revision_log_message = self.__get_file_last_line(self.revision_log())                 last_release = None                 import re                 s = re.compile(r"release (.*) by")                 match = s.search(revision_log_message)                 if match:                     last_release = match.groups()[0]                 else:                     abort("Can NOT found rollback release in revision log files, %s." % self.revision_log())                 if os.path.exists(last_release):                     print yellow("Symlink previous release to current")                 else:                     abort("Can NOT found rollback release on filesystem.")                 if is_linux():                     if os.path.exists(self.current_path()) and os.path.islink(self.current_path()):                         os.unlink(self.current_path())                     os.symlink(last_release, self.current_path())                 if is_windows():                     if os.path.exists(self.current_path()):                         import shutil                         shutil.rmtree(self.current_path())                     try:                         local("ln -sd %s %s" % (last_release, self.current_path()))                     except Exception:                         raise NotImplementedError     class Application(object):         class Deploy(object):             def __init__(self):                 self.P = Capistrano.DSL.Paths()                 self.G = Capistrano.SCM.Git()             def deploy(self):                 # TODO(Guodong Ding): core job here, this is a deploy demo                 with lcd(self.P.current_path()):                     try:                         src = os.path.join(self.P.repo_path(), "replaceLocalHostsFileAgainstGfw.sh")                         local_path = os.path.join(self.P.current_path(), "hosts")                         remote_path = "/tmp/replaceLocalHostsFileAgainstGfw.sh"                         with open(src, 'r') as f:                             content = f.read()                         with open(local_path, "w") as f:                             f.write(content)                         if os.path.getsize(local_path):                             print red("upload files to remote hosts")                             put(local_path, remote_path)                             run("chmod +x %s" % remote_path)                             run("ls -al %s" % remote_path)                             print red("deploy test demo successfully!")                     except IOError:                         raise NotImplementedError             def run(self):                 print blue("Do deploy procedure.")                 self.P.makepaths()                 self.G.set(config["repo_url"], repo_path=self.P.repo_path())                 self.G.pull()                 self.P.make_release_dirs()                 self.P.make_current()                 self.deploy()                 self.P.update_revision_log(self.G.branch, self.G.short_id(), self.P.current, getpass.getuser())                 self.P.cleanup()                 print green("Deploy successfully!") @roles("test") def test_deploy():     c = Capistrano.Application.Deploy()     c.run() def terminal_debug(defName):     command = "fab -i c:\Users\Guodong\.ssh\exportedkey201310171355\                 -f %s \                 %s" % (__file__, defName)     os.system(command)     sys.exit(0) if __name__ == '__main__':     if len(sys.argv) == 1 and is_windows():         terminal_debug("test_deploy")     sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])     print red("Please use 'fab -f %s'" % " ".join(str(x) for x in sys.argv[0:]))     sys.exit(1)

    Fabric 의 추가 사용 은 GitHub 의 다른 파일 을 참고 할 수 있 습 니 다.https://github.com/DingGuodong/LinuxBashShellScriptForOps/blob/master/projects/autoOps/pythonSelf/fabfile.py 이 파일 은 Fabric 에 대한 예제 가 더 많 으 며 Fabric 의 용 도 를 더 잘 설명 할 수 있 습 니 다.
    마지막 으로 Python 은 훌륭 한 프로 그래 밍, 스 크 립 트 언어 라 고 할 수 밖 에 없습니다. 정말 편리 합 니 다. 짧 은 며칠 만 에 유용 한 스 크 립 트 를 만 들 수 있 습 니 다. 운영 자로 서 프로 그래 밍 을 배척 할 필요 가 없습니다. 프로 그래 밍 은 더 좋 은 운영 을 위 한 것 입 니 다. 이 GitHub 프로젝트 에 계속 관심 을 가 져 보 세 요.(https://github.com/DingGuodong/LinuxBashShellScriptForOps) 이 프로젝트 는 지속 적 으로 보완 되 고 더 많은 유용 한 Shell, Python 프로 그래 밍 과 운영 에 관 한 지식 과 파일 을 축적 할 것 입 니 다.
    --end--

    좋은 웹페이지 즐겨찾기