使用Fabric在DigitalOcean部署Mezzanine站点遇rsync错误求助
我正在尝试部署Mezzanine站点,遵循官方教程的CASE1(部署至新服务器)操作:
- 步骤1-3执行正常;
- 执行步骤4「Run fab all」时,流程完成了virtualenv的安装并创建了项目目录,随后出现以下错误:
[1xx.xx.xxx.xx] out: New python executable in /home/~/.virtualenvs/blog_iq/bin/python
[1xx.xx.xxx.xx] out: Installing setuptools, pip, wheel...done.
[1xx.xx.xxx.xx]] out:
[1xx.xx.xxx.xx]] rsync_project: rsync --exclude ".pyc" --exclude ".pyo" --exclude "*.db" --exclude ".DS_Store" --exclude ".coverage" --exclude "local_settings.py" --exclude "/static" --exclude "/.git" --exclude "/.hg" -pthrvz --rsh='ssh -p 22 ' C:\Users~\Root\2blog\blog_iq\ user@1xx.xxx.xx:/home/user/mezzanine/blog_iq[localhost] local: rsync --exclude ".pyc" --exclude ".pyo" --exclude "*.db" --exclude ".DS_Store" --exclude ".coverage" --exclude "local_settings.py" --exclude "/static" --exclude ".git" --exclude "/.hg" -pthrvz --rsh='ssh -p 22 ' C:\Users~\Root\2blog\blog_iq\ user@1xx.xxx.xx:/home/user/mezzanine/blog_iq
The source and destination cannot both be remote. rsync error: syntax or usage error (code 1) at main.c(1292) [Receiver=3.1.2]
Fatal error: local() encountered an error (return code 1) while executing 'rsync --exclude ".pyc" --exclude ".pyo" --exclude "*.db" --exclude ".DS_Store" --exclude ".coverage" --exclude "local_settings.py" --exclude "/static" --exclude ".git" --exclude ".hg" -pthrvz --rsh='ssh -p 22 ' C:\Users~\Root\2blog\blog_iq\ user@1xx.xxx.xx:/home/user/mezzanine/blog_iq
Aborting. Disconnecting from 1xx.xxx.xx... done.
我找到了fabfile.py中生成该错误的相关代码片段:
################ # Config setup # ################ if not hasattr(env, "proj_app"): env.proj_app = real_project_name("blog_iq") conf = {} if sys.argv[0].split(os.sep)[-1] in ("fab", "fab-script.py"): # Ensure we import settings from the current dir try: conf = import_module("%s.settings" % env.proj_app).FABRIC try: conf["HOSTS"][0] except (KeyError, ValueError): raise ImportError except (ImportError, AttributeError): print("Aborting, no hosts defined.") exit() env.db_pass = conf.get("DB_PASS", None) env.admin_pass = conf.get("ADMIN_PASS", None) env.user = conf.get("SSH_USER", getuser()) env.password = conf.get("SSH_PASS", None) env.key_filename = conf.get("SSH_KEY_PATH", None) env.hosts = conf.get("HOSTS", [""]) env.proj_name = conf.get("PROJECT_NAME", env.proj_app) env.venv_home = conf.get("VIRTUALENV_HOME", "/home/%s/.virtualenvs" % env.user) env.venv_path = join(env.venv_home, env.proj_name) env.proj_path = "/home/%s/mezzanine/%s" % (env.user, env.proj_name) env.manage = "%s/bin/python %s/manage.py" % (env.venv_path, env.proj_path) env.domains = conf.get("DOMAINS", [conf.get("LIVE_HOSTNAME", env.hosts[0])]) env.domains_nginx = " ".join(env.domains) env.domains_regex = "|".join(env.domains) env.domains_python = ", ".join(["'%s'" % s for s in env.domains]) env.ssl_disabled = "#" if len(env.domains) > 1 else "" env.vcs_tools = ["git", "hg"] env.deploy_tool = conf.get("DEPLOY_TOOL", "rsync") env.reqs_path = conf.get("REQUIREMENTS_PATH", None) env.locale = conf.get("LOCALE", "en_US.UTF-8") env.num_workers = conf.get("NUM_WORKERS", "multiprocessing.cpu_count() * 2 + 1") env.secret_key = conf.get("SECRET_KEY", "") env.nevercache_key = conf.get("NEVERCACHE_KEY", "") if not env.secret_key: print("Aborting, no SECRET_KEY setting defined.") exit() # Remote git repos need to be "bare" and reside separated from the project if env.deploy_tool == "git": env.repo_path = "/home/%s/git/%s.git" % (env.user, env.proj_name) else: env.repo_path = env.proj_path
以及rsync上传的代码:
def rsync_upload(): """ Uploads the project with rsync excluding some files and folders. """ excludes = ["*.pyc", "*.pyo", "*.db", ".DS_Store", ".coverage", "local_settings.py", "/static", "/.git", "/.hg"] local_dir = os.getcwd() + os.sep return rsync_project(remote_dir=env.proj_path, local_dir=local_dir, exclude=excludes)
问题原因分析
这个错误的核心是rsync被错误地当作本地命令执行了,而不是通过Fabric的rsync_project函数正确处理远程目标。从错误输出的[localhost] local:前缀能看出,Fabric在本地机器上运行了rsync命令,但命令里同时指定了Windows格式的本地路径和远程服务器路径,这违反了rsync的规则——rsync不允许在本地执行时同时使用远程路径作为目标(它会误把远程路径识别成另一个远程源)。
本质问题出在Windows环境的路径格式(盘符、反斜杠)和Fabric的rsync处理逻辑不兼容,导致rsync_project没有正确识别远程目标,退化成了本地执行命令。
解决方案
方案1:修改rsync_upload函数,显式指定远程主机
修改rsync_upload函数,给rsync_project明确指定远程主机,避免Fabric在本地执行rsync:
def rsync_upload(): """ Uploads the project with rsync excluding some files and folders. """ excludes = ["*.pyc", "*.pyo", "*.db", ".DS_Store", ".coverage", "local_settings.py", "/static", "/.git", "/.hg"] local_dir = os.getcwd() + os.sep # 显式指定远程主机,确保rsync_project知道目标是远程服务器 return rsync_project( remote_dir=env.proj_path, local_dir=local_dir, exclude=excludes, host=env.hosts[0] )
方案2:调整本地路径格式,适配Windows环境
Windows的反斜杠路径会被rsync识别为转义字符,导致路径解析错误,把反斜杠替换成正斜杠即可:
def rsync_upload(): """ Uploads the project with rsync excluding some files and folders. """ excludes = ["*.pyc", "*.pyo", "*.db", ".DS_Store", ".coverage", "local_settings.py", "/static", "/.git", "/.hg"] # 将Windows路径的反斜杠替换为正斜杠,避免rsync解析错误 local_dir = os.getcwd().replace("\\", "/") + "/" return rsync_project(remote_dir=env.proj_path, local_dir=local_dir, exclude=excludes)
方案3:改用git作为部署工具(推荐)
如果rsync的Windows兼容性问题难以彻底解决,切换到git部署会更稳定。修改项目settings中的FABRIC配置,把DEPLOY_TOOL改成git:
# 在你的项目settings.py中找到FABRIC配置段 FABRIC = { "DEPLOY_TOOL": "git", # 替换原有的"rsync" # 其他配置保持不变... }
确保本地项目已经初始化git仓库并commit所有更改,Fabric的fab all会自动在远程服务器创建对应的bare仓库并完成部署。
验证步骤
修改完成后,保存文件并重新执行:
fab all
如果是修改了rsync_upload函数,确认fabfile.py保存后再执行;如果切换到git部署,先确保本地git仓库状态正常。
内容的提问来源于stack exchange,提问作者J. Khoury

