Python 高手编程系列三千三百五十五:常见约定与实践
有一套部署的常见约定与实践可能不是每个开发者都知道但对做过运营的人来说都是显而易见的。正如在本章引言中所说即使你不负责代码部署和运营但了解其中一点内容也是很重要的因为这可以让你在开发过程中做出更好的设计决策。文件系统层次结构可能出现在你脑海中最显而易见的约定可能就是关于文件系统层次结构和用户命名。如果你在本书中寻找这方面的建议那你要失望了。当然存在一个文件系统层次结构标准Filesystem Hierarchy StandardFHS它定义了 Unix 和类似 Unix 的操作系统中的目录结构和目录内容但很难找到一个真正的 OS 发行版与 FHS 完全兼容。如果系统设计者和程序员都不能遵守这些标准那么很难期望系统管理员能够做到这一点。根据我的经验我见过应用代码被部署到几乎所有可能的地方包括根文件系统级别的非标准自定义目录。做出这些决定的人对这种做法几乎总是有非常强有力的论据。在这件事上我能给你的唯一建议如下。● 明智地选择避免出现意外。● 在项目所有可用的基础设施中保持一致。● 尽量在组织内部你所在的公司保持一致。真正有用的是将你的项目约定文档化。只是要记住要确保每位感兴趣的团队成员都可以访问这份文档并且让所有人都知道这份文档的存在。隔离第 1 章中已经讨论过隔离的原因以及推荐的工具。对于部署来说只需要补充一件重要的事情。你应该始终隔离每个应用版本的项目依赖。在实践中无论何时部署应用的新版本你都应该为这个版本创建一个新的隔离环境使用 virtualenv 或 venv。旧的环境应该在主机上保留一段时间万一出现问题你可以轻松回滚到应用的某个历史版本。为每个版本创建新的环境有助于管理其干净的状态且有助于符合提供的依赖关系列表。新环境的意思是在文件系统中创建一个新的目录树而不是更新已经存在的文件。不幸的是这可能使执行一些操作更加困难例如优雅地重新加载服务如果就地更新环境的话会更容易实现。使用进程管理工具我们通常希望远程服务器上的应用永不停止。如果是 web 应用它的 HTTP 服务器进程将会无限期地等待新的连接和请求只有发生不可恢复的错误时才会退出。当然不可能在 shell 中手动运行它并保持永不停止的 SSH 连接。使用 nohup、screen或 tmux 来半守护进程并不是选项之一。这么做就如同服务设计失败。你需要的是某种进程管理工具它可以启动并管理你的应用进程。在选择合适的工具之前你需要确保它具有以下功能。● 如果服务退出的话则重启服务。● 可靠地跟踪其状态。● 捕获其 stdout/stder 流用于日志。● 运行具有特定用户/组权限的进程。● 配置系统环境变量。大多数 Unix 和 Linux 发行版都有一些用于进程管理的内置工具/子系统例如 initd脚本、upstart 和 runit。不幸的是在大多数情况下它们并不适合于运行用户级的应用代码并且很难维护。编写可靠的 init.d 脚本尤其是一项挑战因为它需要编写大量的 Bash 脚本做好是很难的。一些 Linux 发行版例如 Gentoo对 init.d 脚本采用了重新设计的方法因此编写要容易得多。不管怎样只是为了一个进程管理工具就将你自己局限于特定的操作系统发行版这不是一个好主意。在 Python 社区中管理应用进程的两个常用工具是 Supervisorhttp://supervisord.org和 Circushttps://circus.readthedocs.org/en/latest/。它们在配置和使用方面非常相似。Circus比 Supervisor 要年轻一些因为创建它就是为了解决后者的一些缺点。它们都可以用简单的类似 INI 文件的配置格式进行配置。它们不仅可以运行 Python 进程还可以被配置来管理任何应用。很难说哪一个更好因为它们都提供非常相似的功能。不管怎样Supervisor 无法在 Python 3 中运行所以它不会得到我们的祝福。虽然在Supervisor 的管理下运行 Python 3 进程并不是一个问题但我将以此为借口只介绍 Circus配置的示例。假设我们想要在 Circus 的管理下使用 gunicorn 网络服务器来运行 webxample 应用本章前面介绍过。在生产环境中我们可能会在合适的系统级进程管理工具initd、upstart 和 runit下运行 Circus尤其是从系统包仓库中安装 Circus 的情况。为了简单起见我们将在本地虚拟环境中运行。允许我们在 Circus 中运行应用的最小配置文件这里的文件名为 circus.ini如下所示[watcher:webxample]cmd /path/to/venv/dir/bin/gunicorn webxample.conf.wsgi:applicationnumprocesses 1下面可以用这个配置文件作为执行参数来运行 circus 进程$ circusd circus.ini2016-02-15 08:34:34 circus[1776] [INFO] Starting master on pid 17762016-02-15 08:34:34 circus[1776] [INFO] Arbiter now waiting for commands2016-02-15 08:34:34 circus[1776] [INFO] webxample started[2016-02-15 08:34:34 0100] [1778] [INFO] Starting gunicorn 19.4.5[2016-02-15 08:34:34 0100] [1778] [INFO] Listening at:http://127.0.0.1:8000 (1778)[2016-02-15 08:34:34 0100] [1778] [INFO] Using worker: sync[2016-02-15 08:34:34 0100] [1781] [INFO] Booting worker with pid: 1781现在你可以用 circusctl 命令来运行交互式会话并利用简单的命令来控制所有被管理的进程。下面是这样的会话示例$ circusctlcircusctl 0.13.0webxample: active(circusctl) stop webxampleok(circusctl) statuswebxample: stopped(circusctl) start webxampleok(circusctl) statuswebxample: active当然上述两种工具都有更多可用的功能。它们的文档中对所有这些功能进行了解释所以在做出选择之前你应该仔细阅读这些内容。