集群环境下安装python应用最佳实践

Published On 十二月 05, 2017

category python | tags pyenv virtualenv


在线上部署python应用,有两个难题,一是不同的应用依赖的环境不同,比如python版本,依赖包/模块/库的版本;二是要安装的服务器太多的话从pypi下载速度慢,并且容易失败。本文介绍我在工作中是如何解决这两个问题的,向大家分享批量安装python应用的一些经验。

注:以下操作的前提是保证测试环境和线上环境的操作系统相同。

将python应用制作成包

一个典型的python应用的目录结构如下:

» tree foo
.
├── foo
│   ├── __init__.py
│   └── app.py
└── setup.py

foo/app.py

from __future__ import print_function
import sys
import requests

def download(url):
    return requests.get(url).text

def main():
    if(len(sys.argv) != 2):
        sys.exit('Usage:\nfoo url')
    print(download(sys.argv[1]), end='')

if __name__ == '__main__':
    main()

foo/__init__.py

from app import *

setup.py

"""A setuptools based setup module.

See:
https://packaging.python.org/en/latest/distributing.html
https://github.com/pypa/sampleproject
"""

# Always prefer setuptools over distutils
from setuptools import setup, find_packages

setup(
    name='foo',

    # Versions should comply with PEP440.
    version='0.1.0',

    description='A sample Python project',

    # Author details
    author='Yanxurui',

    # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
    classifiers=[
        # Specify the Python versions you support here.
        'Programming Language :: Python :: 2.7',
    ],

    # You can just specify the packages manually here if your project is
    # simple. Or you can use find_packages().
    packages=find_packages(exclude=['tests']),
    # Alternatively, if you want to distribute just a my_module.py, uncomment
    # this:
    #   py_modules=["my_module"],

    # List run-time dependencies here.  These will be installed by pip when
    # your project is installed.
    install_requires=['requests'],

    # To provide executable scripts, use entry points in preference to the
    # "scripts" keyword. Entry points provide cross-platform support and allow
    # pip to create the appropriate form of executable for the target platform.
    entry_points={
        'console_scripts': [
            'foo=foo:main',
        ],
    },
)

从setup.py可以看出该应用包含一个包和一个可执行文件,并且依赖requests库。 使用entry_points创建了可执行文件foo,它调用foo包的main函数,该函数定义在app.py中。制作成package的优点是源码包含在site-packages中,不需要单独安装,并且生成的可执行文件开头包含了python的路径,不需要手动指定。

传统的安装方法是:

git clone https://github.com/yanxurui/foo.git
cd foo
pip install .
开发的时候将最后一句替换成pip install -e .

这种方式在大部分的情况下工作得很好的,然而当你需要安装或部署python应用到上百台服务器的时候,就会出现本文开头所讨论的两个难题。

使用pyenv+virtualenv搭建python环境

pyenv解决了不同版本python的并存问题,virtualenv/venv使得不同的应用可以创建独立的依赖环境。

CentOS为例,分别在线上环境和测试环境执行以下步骤搭建python环境:

新建用户py

为了避免使用root账号运行python应用,我们新建一个用户py:

useradd py
pwd py # set password for py
su py

安装pyenv+virtualenv

pyenv是一堆shell脚本,通过修改PATH路径来拦截python、pip等命令。pyenv-virtualenv是pyenv的插件,用来管理virtualenv或venv创建的多个虚拟环境,如果不创建env,该插件不依赖于virtualenv也可以切换env。

以下是参考它们github的文档的安装步骤:

# download
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv

# expose pyenv command-line utility
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc

# enable shims and auto-activation of virtualenvs
echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\n  eval "$(pyenv virtualenv-init -)"\nfi' >> ~/.bashrc

# restart shell
exec "$SHELL"

安装python

安装你要使用的python版本,比如安装当前最新版的python2

pyenv install 2.7.14
pyenv会下载python的源码,编译并安装,这个过程耗时比较长。不同版本的python会被安装到PYENV_ROOT/versions目录,因此不会影响系统自带的python。

注:如果编译失败,已经下载的源码会被删除,可以手动下载到缓存$(pyenv root)/cache目录:

wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tar.xz -P $PYENV_ROOT/cache
pyenv install 2.7.14

离线安装python应用

做法是先在测试环境将python应用安装到自己的virtualenv,然后将整个虚拟环境直接copy到线上环境对应python的envs目录。

测试环境:安装应用并打包env

创建virtualenv(假设使用的python是2.7.14):

cd foo
pyenv virtualenv 2.7.14 foo-env
pyenv local foo-env
第一次创建virtualenv需要安装virtualenv工具。

在测试环境安装python应用和依赖包:

pip install .

将虚拟环境制作成压缩包并传到线上服务器:

tar -czPf foo-env.tar.gz ~/.pyenv/versions/2.7.14/envs/foo-env ~/.pyenv/versions/foo-env
scp foo-env.tar.gz py@192.168.3.234:~/
tar的-P选项会保留绝对路径,解压的时候也需要指定该选项。打包的时候包括了指向虚拟环境的软连接。

线上环境:安装env

cd
tar -xzPf foo-env.tar.gz
pyenv rehash
pyenv rehash会为可执行文件foo重新生成shim(垫片),shim位于$(pyenv root)/shims/目录,有意思的是所有可执行文件的shim的内容完全一样。

启动应用可以直接使用全路径

~/.pyenv/versions/foo-env/bin/foo http://example.com
或者创建应用目录并在启动之前进入该目录
mkdir foo
cd foo
pyenv local foo-env
foo http://example.com


qq email facebook github
© 2018 - 晏旭瑞. All rights reserved
Built using pelican