LXC简介

LXC (Linux Containters) 是一种基于内核容器属性的用户空间接口。 它被认为介于chroot和完全虚拟化之间,其目标为创建一个不需要独立内核,但近可能接近标准Linux安装的环境。

其特性如下:

由于没有完全虚拟化CPU,也没有虚拟化硬盘,其性能是与物理机接近的。实际经验,我发现在LXC中编译C/C++源码(如构建基于C/C++源码的RPM或DEB)的性能是VirtualBox的3倍。强烈建议使用LXC代替KVM和VirtualBox作为RPM或DEB的构建环境。

以下操作基于Ubuntu 12.04,那么需要安装LXC包:

sudo apt-get install -y lxc

创建LXC

以创建一个名为precise的Ubuntu 12.04容器为例。

需要创建一个基础的配置文件。由于创建LXC完成后,不再需要该配置文件(可以删除),故该文件的名字和路径没有特殊要求。这里命名为precise.conf,放在当前路径下:

lxc.network.type = veth
lxc.network.flags = up
lxc.network.name = eth0
lxc.network.link = lxcbr0

lxcbr0为由LXC包创建的虚拟网桥,通过ifconfig可以知道其IP地址10.0.3.1,网段10.0.3.1/24,容器将通过lxcbr0与外界通信。

如此,可以开始创建容器了:

sudo lxc-create -n precise -f precise.conf -t ubuntu -- -r precise
  • -n指定容器名,这里为precise。
  • -f指定基础配置文件,即上一步骤创建的precise.conf。
  • -t指定模板名,这里必须为ubuntu(创建Ubuntu 12.04)。每个模板名,对应一个脚本,它们存放在/usr/lib/lxc/templates目录(文件名形如lxc-<模板名>)中。
  • –以后的参数被传递给模板脚本;
  • -r为ubuntu模板脚本的参数,表示Ubuntu发行版代号,这里必须为precise(它是12.04的发行代号)。

创建过程可能会比较漫长。通过阅读/usr/lib/lxc/templates/lxc-ubuntu,不难发现创建ubuntu容器主要依靠deboostrap来完成。另一方面,变量MIRROR和SECURITY_MIRROR决定了镜像的设置,它们默认为:

Python常量

与C/C++不同,Python在语法上并没有定义常量,尽管PEP 8定义了常量的命名规范为大写字母和下划线组成。

在实际项目中,常量首次赋值后, 无法阻止其他代码对其进行修改或删除。

现存的办法

幸运的是该问题在2001年就有人给出了解决方案Constants in Python,基本内容如下:

class _const:
    class ConstError(TypeError):
        pass

    def __setattr__(self, name, value):
        if self.__dict__.has_key(name):
            raise self.ConstError, "Can't rebind const instance attribute (%s)" % name

        self.__dict__[name] = value

import sys
sys.modules[__name__] = _const()

其大致含义为:

  • 通过_const的__setattr__对象方法判断该对象是否存在属性name,若存在则抛出自定义异常ConstError,否则创建该属性。
  • 将_const实例化的对象赋值sys.modules[__name__],const模块被绑定成_const对象。__name__在首次载入const过程中为’const’,而sys.modules是模块名与已加载模块的dict。

如何使用const模块呢?

import const
const.magic = 23

若再次赋值const.magic,

const.magic = 88

则将抛出ConstError的异常。

如何避免常量被删除?

实际项目中,常量并不希望被其他代码删除。在_const类中加入:

    def __delattr__(self, name):
        if self.__dict__.has_key(name):
            raise self.ConstError, "Can't unbind const const instance attribute (%s)" % name

        raise AttributeError, "const instance has no attribute '%s'" % name

如此,删除已定义的常量(假设const.magic已经赋值):

定制无源码安装Python模块

Python的distutilssetuptools都是为开源项目设计的,Python模块分发和安装都包含该模块的源代码。实际公司工作多为闭源项目,Python模块的安装是不能包核心源代码的。

过去对distutils和setuptools的一知半解,为了达到闭源的目的,我通过书写Makefile来编译Python源码为.pyc或.pyo,完全绕开distutils和setuptools的限制。权宜之计虽然解决了一时之急,然总是让我追求标准和完美的心感到不安。为此,最近我花了一些时间来阅读distutils文档和部分源代码,终于找到了相对地道的解决办法。

根据Extending Distutils的描述,继承distutils.cmd.Command的子类,如distutils.command.build_py.build_py,并重载已有的方法来达到扩展的目的。

根据Creating a new Distutils command描述子类必须定义如下方法:

  • Command.initialize_options()
  • Command.finalize_options()
  • Command.run()
  • Command.sub_commands()

并且命令install由install_lib和install_headers等子命令构成。

我的目的不是扩展Distutils的install命令,而是改变其行为,避免其安装源码。其实,只需要改变install_lib的行为就足够了。

类install_lib存在于/usr/lib/python2.7/distutils/command/install_lib.py文件中,它的方法run源码如下:

    def run(self):
        # Make sure we have built everything we need first
        self.build()

        # Install everything: simply dump the entire contents of the build
        # directory to the installation directory (that's the beauty of
        # having a build directory!)
        outfiles = self.install()

        # (Optionally) compile .py to .pyc
        if outfiles is not None and self.distribution.has_pure_modules():
            self.byte_compile(outfiles)

与先编译再安装的直觉相反,编译生成pyc并不发生在build方法中,而是install方法执行后。所以,若重载build方法(实际调用build_py命令),则install和byte_compile都需要修改,工作量较大且复杂度较高。

Flake8简介

Flake8包装了下列工具:

它综合上述三者的功能,在简化操作的同时,还提供了扩展开发接口。

安装

这里仅介绍Ubuntu的安装方法,其他安装方法见Flake8官网。

  • 添加ppa:cjohnston/flake8。Ubuntu 12.04、12.10和13.04官方源仅提供pep8的包,而该PPA不仅提供了最新的python-flake8包,还提供最新的pep8包。Ubuntu 13.10和14.04默认已经提供最新的pep8和python-flake8,所以可以跳过这一步。
sudo add-apt-repository ppa:likemartinma/python
sudo apt-get update
sudo apt-get -y --force-yes dist-upgrade
  • 安装python-flake8
sudo apt-get install python-flake8

使用

  • 递归检查当前目录的所有Python文件:
flake8 .
  • 检查指定文件
flake8 foo.py bar.py
  • 通过setup.py检查工程的所有Python文件:
python setup.py flake8

为了保证其在其他环境中正确运行,需要将flake8增加到setup_requires中,例如:

setup(
    name="project",
    packages=["project"],

    setup_requires=[
        "flake8"
    ]
)
  • 由于默认禁用代码条件复杂度检查,需要通过–max-complexity激活该功能:
flake8 --max-complexity 12 .

该功能对于发现代码过度复杂非常有用,根据Thomas J. McCabe, Sr(Cyclomatic complexity的创造者)研究,代码复杂度不宜超过10,而Flake8官网建议值为12。

PEP 8总结

下述内容主要源于PEP 8 – Style Guide for Python Code

最大的行长度

  • 所有行不超过79个字符。
  • docstring或comment应不超过72字符

补齐

  • 每个补齐级别为4个空格。
  • 当一行操作最大行长度时,应尽可能按照各种括号作为纵向对齐的参照物(可以适当增加括号),如:
from bottle import (get, post, delete, error, run, default_app, HTTPError,
                    request, response, static_file)

# Aligned with opening delimiter
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# More indentation included to distinguish this from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)
  • 多行情况下,关闭括号可以出现在一行开始,如:
my_dict = {
    'hello': 'foo',
    'world': 'bar',
}

my_list = [
    1, 2, 3,
    4, 5, 6,
]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

实际工作中,不同开发语言存在不同的补齐风格要求。可以在.vimrc中针对python设置:

git-buildpackage示例(二)

《git-buildpackage示例(一)》中,我介绍了如何利用git-buildpackage为Ubuntu已有包做一个补丁包的办法。

当时,我的补丁是基于tolua 5.1.3版本。一段时间后,tolua的作者释放了5.1.4版本。问题出现了,如何将我的补丁合并到5.1.4版本中呢?

下面我将继续使用git-buildpackage来解决合并上游新版本的问题:

  • 下载tolua 5.1.4源码包(假设放在git工作目录上层):
wget http://www.tecgraf.puc-rio.br/~celes/tolua/tolua-5.1.4.tar.gz
cd tolua
git-import-orig -u 5.1.4 ../tolua-5.1.4.tar.gz
  • 手动解决遇到的冲突(如src/bin/Makefile)并提交更新:
git commit
  • 这时,运行
git log --format=%d:%s

输出:

 (HEAD, master):Merge commit 'upstream/5.1.4'
 (upstream/5.1.4, upstream):Imported Upstream version 5.1.4
 (debian/5.1.3-2):Fix relocation R_X86_64_32 against '.rodata' can not be used when making a shared object
 (debian/5.1.3-1):Imported Debian patch 5.1.3-1
 (upstream/5.1.3):Imported Upstream version 5.1.3

upstream/5.1.4分支被创建,且将其合并到master分支中。如此,master分支合并完毕,接下来将合并debian的patches。

  • 重整patch-queue:
gbp-pq rebase

手动解决遇到的冲突:

git rm -f src/bin/toluabind.c
git rebase --continue
  • 导出patch-queue(至master分支)
git clean -df
gbp-pq export
  • 指定版本号5.1.4-1自动生成snapshot的debian/changelog:
git-dch -a -S -N 5.1.4-1
git add debian/changelog
git add debian/patches/0001-mkdir-for-tolua-lib-archive-and-remove-temp-files.patch
git commit -m "Update patches from debian/5.1.3-1"

测试构建新的deb包。为了避免污染当前环境,这里指定git首先导出源码至../tolua-build目录: