Fabric + Capistrano 를 이용 하여 Python 자동화 배 치 를 실현 합 니 다.
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 에 익숙해 지면 볼 수 있 음):
Capistrano 와 같은 디 렉 터 리 구조:
Capistrano 에서 생 성 한 같은 형식의 로 그 를 모방 합 니 다:
스 크 립 트 내용 은 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--
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Python의 None과 NULL의 차이점 상세 정보그래서 대상 = 속성 + 방법 (사실 방법도 하나의 속성, 데이터 속성과 구별되는 호출 가능한 속성 같은 속성과 방법을 가진 대상을 클래스, 즉 Classl로 분류할 수 있다.클래스는 하나의 청사진과 같아서 하나의 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.