通過demo學習OpenStack開發——軟件包管理

jopen 8年前發布 | 18K 次閱讀 Python OpenStack 分布式/云計算/大數據

通過demo學習OpenStack開發–軟件包管理

編者按:《通過demo學習OpenStack開發》專欄是劉陳泓的系列文章,專欄通過開發一個demo的形式來介紹一些參與OpenStack項目開發的必要的基礎知識,希望幫助大家入門企業級Python項目的開發和OpenStack項目的開發。劉陳泓主要關注OpenStack的身份認證和計費領域。另外,還對云計算、分布式系統應用和開發感興趣。

為什么寫這個系列

OpenStack 是目前我所知的最大最復雜的基于Python項目。整個OpenStack項目包含了數十個主要的子項目,每個子項目所用到的庫也不盡相同。因此,對于Python初學者和未接觸過OpenStack項目的人Python來說,入門的難度相當大。

幸運的是,OpenStack中的項目有很多共同點。比如,它們有相同的代碼庫結構,都盡可能是用同樣的庫,配置文件和單元測試的規范也都幾乎一樣。因此,通過學習這些共通的部分,我們就可以快速掌握多個OpenStack項目。但是,萬事開頭難,學習這些基礎知識總是痛苦的。不過,學習的難點并不在于這些知識點本身有多難理解,而是這些基礎知識的應用場景和應用效果對初學者來說都是模糊的。這個系列文章的目的就是幫助有需要的人了解OpenStack中一些常見的知識點。理解過程就是通過動手做一個Web application demo來實現的。

這個系列文章會涉及到以下的知識點:

  • 包管理和pbr
  • WSGI, RESTful Service和Pecan框架
  • eventlet
  • SQLAlchymy
  • 單元測試

下面的知識點是不會專門講的,如果有遇到不會的請自學:

  • git

軟件包管理

軟件包管理是每個OpenStack項目的基礎,其目的是用來將項目代碼打包成源碼包或者二進制包進行分發。一個項目的代碼可能會被打包放到PyPI上,這樣你可以通過 pip 命令安裝這個包;也可能會被打包放到項目的軟件倉庫里,這樣你可以通過 apt-get install 或者 yum install 來安裝這個軟件包。

不幸的是,Python在軟件包管理十分混亂,至少歷史上十分混亂。原因有兩個:

  • 一是標準庫提供的軟件包管理功能十分弱,
  • 二是官方沒有提供統一的軟件包管理標準。

對于這個領域,我曾經也是混亂的,只知道使用easy_install和pip來安裝軟件包。不過自從看了 The Hacker’s Guide to Python (《Python高手之路》)之后,算是知道點來龍去脈。

軟件打包工具的歷史

這里我會講一下我知道的Python的軟件打包工具的歷史,我們按照歷史順序來敘述。

distutils (before 2000)

disutils自從1998年起就是Python標準庫的一部分了,不過它在2000年就停止了開發。disutils是最早的Python打包工具和標準,也奠定了對Python軟件進行打包的一個基本工作方式: 使用setup.py文件 。來看一個setup.py文件的例子:

#!/usr/bin/env Python
# -*- coding: utf-8 -*-

from disutils.core import setup

setup(name='webdemo',
      description='A simple web demo.',
      author='author name',
      author_email='author_name@example.com'
      url='http://example.com',
      packages=['webdemo'])

setup.py文件是放在項目根目錄下的:

? ~/programming/Python/webdemo git:(master) ? $ ls
LICENSE  README.md  setup.py  webdemo

然后你就可以使用命令 Python setup.py build 來編譯包,可以使用 Python setup.py install 來安裝這個項目。如果需要幫助,可以通過 Python setup.py --help-commands 來查看支持的命令。

setuptools

disutils停止開發后,setuptools成了繼任者。setuptools提供了很多高級功能,包括自動依賴處理、Egg分發格式以及 easy_install 命令。setuptools的使用方式和disutils差不多,也是以一個setup函數作為入口,只不過該函數來自于setuptools模塊,而且支持更多的參數,比如classifiers, setup_requires等,參數更多意味著功能更多。

后來有一段時間setuptools項目發展開始變得緩慢了,就有人從setuptools項目創建了 distribute 項目。distribute開始支持Python 3等新特性。不過一段時間后,distribute項目又和setuptools項目合并了(2013年3月)。因此,現在已經不存在distribute項目了。

到目前為止,setuptools還是使用最多的打包工具,而且開發很活躍,2015年6月剛剛發布了18.0版本。 setuptools項目的文檔 。OpenStack目前也是使用setuptools庫來執行打包操作,我們下面會詳細點介紹setuptools工具。

disutils2

在setuptools項目發展的過程中,有一個叫disutils2的項目也在并行開發中,其目的是全面取代Python標準庫中的distutils。disutils2的最大改進是將setup函數的參數單獨放到一個setup.cfg的文件中(這些成為包的元數據)。不夠disutils2這個項目缺點很多,而且沒有功能上還不如setuptools項目,所以在2012年的時候,這個項目被廢棄了。

distlib

這個是一個新的打包工具,目標也是取代disutils。不過這個項目的開發進展也不快,到2015年才發布了0.2.0版本。目前還未能并入到Python的標準庫中。不過可以保持關注。 項目文檔地址

在OpenStack中使用打包工具

前面已經提到了,OpenStack也是使用setuptools工具來進行打包,不過為了滿足OpenStack項目的需求,引入了一個輔助工具 pbr 來配合setuptools完成打包工作。

pbr (Python Build Reasonableness)

pbr是一個setuptools的擴展工具,被開發出來的主要目的是為了方便使用setuptools,其項目文檔地址也 在OpenStack官網內

先說一下pbr如何使用:

import setuptools

setuptools.setup(setup_requires=['pbr'], pbr=True)

按照上面的方式就可以配置setuptools工具使用pbr來協助完成打包工作。這里的 setup_requires 參數意思是setup函數在執行之前需要依賴的包的列表。這里的依賴的包的功能可以理解為生成setup的實際參數。你可以看到,當使用pbr的時候,setup函數只有兩個參數,然而實際上 setuptools.setup 函數實際上是 disutils.core.setup 函數,會接收任何參數,這些參數可以通過在調用時指定,也可以通過所依賴的擴展來生成(比如pbr)。

那么OpenStack社區為啥要開發pbr呢?因為setuptools庫使用起來還是有點麻煩,參數太多,而且直接通過指定setup函數的參數的方法實在太不方便了。pbr就是為了方便而生的,它帶了了如下的改進:

  1. 使用setup.cfg文件來提供包的元數據。這個是從disutils2學來的。
  2. 基于requirements.txt文件來實現自動依賴安裝。requirements.txt文件中包含了一個項目所要依賴的庫,這個文件的格式是和pip兼容的。
  3. 利用Sphinx實現文檔自動化。
  4. 基于git history自動生成AUTHORS和ChangeLog文件。
  5. 針對git自動創建文件列表。
  6. 基于git tags的版本號管理。

pbr的版本推導

這里重點說明一下基于git tag的版本號管理這個功能。當使用pbr的時候,版本號有兩種方式: postversioningpreversioning ,postversioning是默認方式。要是用preversioning的方式,則需要設置setup.cfg文件中的 [metadata] 段的 version 字段的值。無論采用哪種方式,版本號都是從git的歷史推理得到的。pbr使用的版本號標準是 Linux/Python Compatible Semantic Versioning 3.0.0 ,簡單的說就是下面這個標準:

Given a version number MAJOR.MINOR.PATCH, increment the:

  1. MAJOR version when you make incompatible API changes,
  2. MINOR version when you add functionality in a backwards-compatible manner,
  3. and PATCH version when you make backwards-compatible bug fixes.

pbr的版本推導按照如下的步驟進行(注意,最終版本號才是軟件包的版本號):

  1. 如果設置version的值為一個給定的版本號,且這個版本號剛好對應一個tag,則這個值就是最終版本號(注意,這里只有簽名的tag才有效)。
  2. 如果不是上面情況,則pbr會找到最近的一個tag,然后為其MINOR值加1得到一個比它大的最小版本號(注意,這個還不是最終版本號)。
  3. 然后pbr會從最近的一個tag開始遍歷所有的git commit,并檢查每個提交的commit message,在commit message中查找 Sem-Ver: 這樣的行:

    • 如果Sem-Ver的值是 bugfix ,則會增加版本號中PATCH部分的值。
    • 如果Sem-Ver的值是 feature 或者 deprecation ,則會增加版本號中MINOR部分的值。
    • 如果Sem-Ver的值是 api-break ,則會增加版本號中MAJOR部分的值。
    • 如果Sem-Ver行不存在,則認為值是 bugfix
    • 如果Sem-Ver的值不在上面列出的范圍內,則會給出警告。

      1. 如果使用的是postversioning的方式,也就是setup.cfg中不指定version的值,則pbr會使用規則3推導出來的值作為目標版本號(只是目標版本號,不是最終版本號)。
      2. 如果使用的是preversioning的方式,也就是setup.cfg中指定了version的值(而且不符合規則1),則會檢查指定的version是否高于規則3推導出來的版本號,如果沒有,則會拋出異常,如果有,則使用指定的版本號作為目標版本號。
      3. 在得到目標版本號之后,開始計算開發版本號。開發版本號的形式如下: MAJOR.MINOR.PATCH.devN 。這里要計算的是 devN 中的 N 。這個值等于從最近的git tag開始的提交數量。計算完開發版本號之后,就得到了最終版本號。

總的來說,從上面的規則計算出來的版本號只有兩種形式,一種是發布版本號(對應到某個tag),另一種是開發版本號。注意:pbr要求tag都是要簽名的,也就是打tag時要使用 git tag -a -s X.Y.Z 的形式。

setup.cfg和requirements.txt

setup.cfg

由于OpenStack項目都使用了setuptools和pbr來執行打包工作,因此項目的元數據都放在setup.cfg文件中。我們以keystone項目的setup.cfg文件為例來說明這個文件里一般會包含什么內容。以下是寫這篇文章時最新的keystone項目的setup.cfg文件的內容(以#開頭的是我加的注釋):

[metadata]  # 元數據段
name = keystone  # 軟件包名稱
version = 8.0.0  # 軟件包版本號,還可以指定preversoining, postversioning等值,具體的作用看pbr的文檔。
summary = OpenStack Identity  # 簡介
description-file =  # 指定README文件
    README.rst
author = OpenStack  # 作者
author-email = OpenStack-dev@lists.OpenStack.org  # 作者郵件
home-page = http://www.OpenStack.org/  # 主頁
classifier =  # 包的分類,下面具體說
    Environment :: OpenStack
    Intended Audience :: Information Technology
    Intended Audience :: System Administrators
    License :: OSI Approved :: Apache Software License
    Operating System :: POSIX :: Linux
    Programming Language :: Python
    Programming Language :: Python :: 2
    Programming Language :: Python :: 2.7

[files]  # 文件段
packages =  # 包名稱
    keystone

[global]  # 全局段
setup-hooks =  # 指定安裝hook
    pbr.hooks.setup_hook


[egg_info]  # 指定egg信息
tag_build =
tag_date = 0
tag_svn_revision = 0

[build_sphinx]  # 文檔build相關信息
all_files = 1
build-dir = doc/build
source-dir = doc/source

[compile_catalog]
directory = keystone/locale
domain = keystone

[update_catalog]
domain = keystone
output_dir = keystone/locale
input_file = keystone/locale/keystone.pot

[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = keystone/locale/keystone.pot
copyright_holder = OpenStack Foundation
msgid_bugs_address = https://bugs.launchpad.net/keystone

# NOTE(dstanek): Uncomment the [pbr] section below and remove the ext.apidoc
# Sphinx extension when https://launchpad.net/bugs/1260495 is fixed.
[pbr]  # pbr本身的配置
warnerrors = True
autodoc_tree_index_modules = True

[entry_points]  # 指定入口點
console_scripts =  # 指定要生成的可執行文件
    keystone-all = keystone.cmd.all:main
    keystone-manage = keystone.cmd.manage:main

# 下面是其他entry_points內容,主要用于指定不同功能的擴展,和打包無關。
...

( 上面有些未注釋的部分我目前還不太清楚,后續補充,可以先參考 PEP301 )

這里說說一下classifier這個參數。這個參數是用來指定一個軟件包的分類、許可證、允許運行的操作系統、允許運行的Python的版本的信息。這些信息是在一個叫trove的項目。關于Python和trove的關系, 請參考

你可以在PyPI上找到完整的classifier值列表, 地址 。另外,你也可以通過setuptools的命令來獲取這個列表,在項目根目錄下執行: Python setup.py register --list-classifiers 。

requirements.txt

這個文件指定了一個項目依賴的包有哪些,并且支出了依賴的包的版本需求,可以看看keystone項目的requirements.txt:

# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.

pbr<2.0,>=0.11
WebOb>=1.2.3
eventlet>=0.17.4
greenlet>=0.3.2
PasteDeploy>=1.5.0
Paste
Routes!=2.0,>=1.12.3
cryptography>=0.8.2 # Apache-2.0
six>=1.9.0
SQLAlchemy<1.1.0,>=0.9.7
sqlalchemy-migrate>=0.9.6
stevedore>=1.5.0 # Apache-2.0
passlib
Python-keystoneclient>=1.6.0
keystonemiddleware>=1.5.0
oslo.concurrency>=2.1.0 # Apache-2.0
oslo.config>=1.11.0 # Apache-2.0
oslo.messaging!=1.12.0,>=1.8.0 # Apache-2.0
oslo.db>=1.10.0 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0
oslo.log>=1.2.0 # Apache-2.0
oslo.middleware!=2.0.0,>=1.2.0 # Apache-2.0
oslo.policy>=0.5.0 # Apache-2.0
oslo.serialization>=1.4.0 # Apache-2.0
oslo.service>=0.1.0 # Apache-2.0
oslo.utils>=1.6.0 # Apache-2.0
oauthlib>=0.6
pysaml2>=2.4.0
dogpile.cache>=0.5.3
jsonschema!=2.5.0,<3.0.0,>=2.0.0
pycadf>=0.8.0
msgpack-Python>=0.4.0

軟件包歸檔格式

Python的軟件包一開始是沒有官方的標準分發格式的。比如Java有jar包或者war包作為分發格式,Python則什么都沒有。后來不同的工具都開始引入一些比較通用的歸檔格式。比如,setuptools引入了Egg格式。但是,這些都不是官方支持的,存在元數據和包結構彼此不兼容的問題。因此,為了解決這個問題,PEP 427定義了新的分發包標準,名為Wheel。目前pip和setuptools工具都支持Wheel格式。這里我們簡單總結一下常用的分發格式:

  • tar.gz格式:這個就是標準壓縮格式,里面包含了項目元數據和代碼,可以使用 Python setup.py sdist 命令生成。
  • .egg格式:這個本質上也是一個壓縮文件,只是擴展名換了,里面也包含了項目元數據以及源代碼。這個格式由setuptools項目引入。可以通過命令 Python setup.py bdist_egg 命令生成。
  • .whl格式:這個是Wheel包,也是一個壓縮文件,只是擴展名換了,里面也包含了項目元數據和代碼,還支持免安裝直接運行。whl分發包內的元數據和egg包是有些不同的。這個格式是由PEP 427引入的。可以通過命令 Python setup.py bdist_wheel 生成。

.egg-info和.dist-info目錄

如果你到系統中安裝Python庫的路徑下看看,就能看到很多名稱以.egg-info或者以.dist-info結尾的目錄。這些目錄的內容就是這個庫的元數據,是從庫的分發包中拷貝出來的。其中.egg-info類型的目錄來自于Egg格式的分發包,.dist-info類型的目錄來自于Wheel格式的分發包。

軟件包的安裝

安裝工具

上面已經提到了,setuptools項目提供了一個軟件包安裝工具 esay_install 。easy_install支持從軟件歸檔文件中或者從PyPI上安裝軟件包,不過這個工具并不好用,比如缺少卸載功能等,因此并不流行,現在更多的都是使用pip工具。

pip項目提供了很好的軟件包安裝方式,并且已經被包含到Python 3.4中,可以從PyPI、tarball或者Wheel歸檔中安裝和卸載軟件按包。關于pip常見的用法,這里就不贅述了( pip install, pip uninstall, pip search, … )。

安裝路徑

軟件包的安裝路徑依賴于操作系統、Python版本和安裝方式。

  • 在Debian系的系統上(比如Ubuntu)

    • 使用 apt-get install 從系統軟件源安裝

      • Python 2.7: /usr/lib/Python2.7/dist-packages
      • Python 3.4: /usr/lib/Python3.4/dist-packages
    • 使用 pip install 命令安裝

      • Python 2.7: /usr/local/lib/Python2.7/dist-packages
      • Python 3.4: /usr/local/lib/Python3.4/dist-packages
    • 在virtualenv中使用 pip install 安裝

      • Python 2.7: lib/Python2.7/site-packages
      • Python 3.4: lib/Python3.4/site-packages
  • 在CentOS系的系統上

    • 使用 yum install 命令安裝

      • Python 2.7: /usr/lib/Python2.7/site-packages

以開發模式安裝

pip的安裝命令可以使用-e選項,用來從本地代碼目錄或者版本庫URL來安裝一個開發版本的庫。采用這種方式的時候,在安裝目錄下只會創建一個包含軟件包信息的文件,真正的代碼不會安裝到系統目錄下。

Webdemo的打包管理

學習過包管理相關的知識后,我們就要以OpenStack的方法來創建一個我們自己的項目。這個項目的名稱是Webdemo,就是一個簡單的Web服務器。這個項目會貫穿這個系列文章。在本文中,我們首先要創建Webdemo的項目框架并添加軟件包管理相關的內容。

項目目錄結構

? ~/programming/Python/webdemo git:(master) ? $ tree .
.
├── LICENSE
├── README.md
├── requirement.txt
├── setup.cfg
├── setup.py
└── webdemo
    └── __init__.py

1 directory, 6 files

這個是一個最簡單的Python項目目錄:

  • 源代碼放在子目錄Webdemo/下
  • 然后包含了軟件包管理的所需的文件:setup.py, setup.cfg, requirements.txt
  • LICENSE和README

軟件包管理相關

首先是setup.py,就是這么簡單:

#!/usr/bin/env Python
# -*- coding: utf-8 -*-

import setuptools


# In Python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.Python.org/issue15881#msg170215
try:
        import multiprocessing  # noqa
except ImportError:
        pass


setuptools.setup(
    setup_requires=['pbr'], pbr=True)

然后是setup.cfg:

[metadata]
name = webdemo
version = 0.0.1
summary = Web Application Demo
description-file = README.md
author = author
author-email = author@example.com
classifier =
    Environment :: Web Environment
    Intended Audience :: Developers
    Intended Audience :: Education
    License :: OSI Approved :: GNU General Public License v2 (GPLv2)
    Operating System :: POSIX :: Linux
    Programming Language :: Python
    Programming Language :: Python :: 2
    Programming Language :: Python :: 2.7

[global]
setup-hooks =
    pbr.hooks.setup_hook

[files]
packages =
    webdemo

[entry_points]
console_scripts =

只包含最基本的信息。接下來是requirements.txt文件:

# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.

pbr<2.0,>=0.11

目前只依賴于pbr庫。源代碼目錄下現在只有一個空的 init.py 文件。我們已經搭建好了這個最簡單的項目框架了。首先,我們要把這些代碼提交到git庫,然后打上 tag 0.0.1

? ~/programming/Python/webdemo git:(master) ? $ git log --oneline
697427c Add packaging information
2cbbf4d Initial commit
? ~/programming/Python/webdemo git:(master) ? $ git tag -a -s 0.0.1
? ~/programming/Python/webdemo git:(master) ? $ git tag
0.0.1

然后就可以使用 Python setup.py sdist 命令來生成一個0.0.1版本的源碼歸檔了:

? ~/programming/Python/webdemo git:(master) ? $ Python setup.py sdist
running sdist
[pbr] Writing ChangeLog
[pbr] Generating ChangeLog
[pbr] Generating AUTHORS
running egg_info
writing pbr to webdemo.egg-info/pbr.json
writing webdemo.egg-info/PKG-INFO
writing top-level names to webdemo.egg-info/top_level.txt
writing dependency_links to webdemo.egg-info/dependency_links.txt
writing entry points to webdemo.egg-info/entry_points.txt
[pbr] Processing SOURCES.txt
[pbr] In git context, generating filelist from git
warning: no previously-included files found matching '.gitreview'
warning: no previously-included files matching '*.pyc' found anywhere in distribution
writing manifest file 'webdemo.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt

running check
warning: check: missing required meta-data: url

creating webdemo-0.0.1
creating webdemo-0.0.1/webdemo
creating webdemo-0.0.1/webdemo.egg-info
making hard links in webdemo-0.0.1...
hard linking AUTHORS -> webdemo-0.0.1
hard linking ChangeLog -> webdemo-0.0.1
hard linking LICENSE -> webdemo-0.0.1
hard linking README.md -> webdemo-0.0.1
hard linking requirement.txt -> webdemo-0.0.1
hard linking setup.cfg -> webdemo-0.0.1
hard linking setup.py -> webdemo-0.0.1
hard linking webdemo/__init__.py -> webdemo-0.0.1/webdemo
hard linking webdemo.egg-info/PKG-INFO -> webdemo-0.0.1/webdemo.egg-info
hard linking webdemo.egg-info/SOURCES.txt -> webdemo-0.0.1/webdemo.egg-info
hard linking webdemo.egg-info/dependency_links.txt -> webdemo-0.0.1/webdemo.egg-info
hard linking webdemo.egg-info/entry_points.txt -> webdemo-0.0.1/webdemo.egg-info
hard linking webdemo.egg-info/not-zip-safe -> webdemo-0.0.1/webdemo.egg-info
hard linking webdemo.egg-info/pbr.json -> webdemo-0.0.1/webdemo.egg-info
hard linking webdemo.egg-info/top_level.txt -> webdemo-0.0.1/webdemo.egg-info
copying setup.cfg -> webdemo-0.0.1
Writing webdemo-0.0.1/setup.cfg
Creating tar archive
removing 'webdemo-0.0.1' (and everything under it)
? ~/programming/Python/webdemo git:(master) ? $ ls dist
webdemo-0.0.1.tar.gz
? ~/programming/Python/webdemo git:(master) ? $ ls
AUTHORS  ChangeLog  dist  LICENSE  README.md  requirement.txt  setup.cfg  setup.py  webdemo  webdemo.egg-info

驗證成功,在 dist/ 目錄下生成了一個0.0.1版本的源碼歸檔,同時生成了如下的文件和目錄: AUTHORS, ChangeLog, webdemo.egg-info

來自: http://www.infoq.com/cn/articles/OpenStack-demo-packagemanagement

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!