What is Fabric ?
What is Fabric ? Fabric là một thư viện Python mạnh mẽ cho phép bạn thực hiện các công việc deploy cũng như các tác vụ admin môi trường phát triển đến các server production. Nói một cách cụ thể thì Fabric cho phép chúng ta: Chạy một hàm Python bất kỳ từ command line Cho phép ta chạy các lệnh ...
What is Fabric ?
Fabric là một thư viện Python mạnh mẽ cho phép bạn thực hiện các công việc deploy cũng như các tác vụ admin môi trường phát triển đến các server production. Nói một cách cụ thể thì Fabric cho phép chúng ta:
- Chạy một hàm Python bất kỳ từ command line
- Cho phép ta chạy các lệnh shell thông qua SSH một các dễ dàng và mang hơi hướng của Python (Pythonic) Về cơ bản, mọi người dùng đều sử dụng Fabric như là một công cụ để tự động hoá các công việc hàng ngày.
Cùng "cưỡi ngựa xem hoa" sơ qua một vòng xem Fabric có thể làm được những gì nhé.
Cài đặt
Như thường lệ với bất kì một package nào, ta có thể cài đặt thông qua pip (cài đặt trên system-wide hoặc trong virtual enviroment)
sudo pip install fabric
Hello World bằng Fabric
Tạo một file fabfile.py đơn giản với nội dung như sau:
from fabric.api import * def hello(): print("Hello world!")
Và chạy:
(venv)[vigo@ubuntu ~/lab/pvp-game (master)]$ fab hello Hello world! Done.
Vậy còn chạy lệnh trên shell thì sao ? Ta thêm một function nữa:
from fabric.api import * env.hosts = ['vigo@localhost'] def hello(): print("Hello world!") def uname(): run("uname -a")
và thực hiện trên localhost
(venv)[vigo@ubuntu ~/lab/pvp-game (master)]$ fab uname [vigo@localhost] Executing task 'uname' [vigo@localhost] run: uname -a [vigo@localhost] Login password for 'vigo': [vigo@localhost] out: Linux ubuntu 3.13.0-58-generic #97-Ubuntu SMP Wed Jul 8 02:56:15 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux [vigo@localhost] out: Done. Disconnecting from localhost... done.
Biến env chứa các thiết lập môi trường cho fabfile của chúng ta. Ở đây ta đã chỉnh định username cũng như host mà ta muốn chạy trên đó. Hãy tham khảo thêm cách thiết lập host tại http://docs.fabfile.org/en/1.10/usage/execution.html#defining-host-lists
Các lệnh có thể thực hiện trong Fabric
Về cơ bản thì có các lệnh sau:
- run (fabric.operations.run) : chạy một lệnh trên shell của remote host
- sudo (fabric.operations.sudo) : chạy một lệnh trên shell của remote host dưới sudo
- local (fabric.operations.local) : chạy một lệnh trên shell của local host
- get (fabric.operations.get) : dùng để lấy một file từ trên remote host về local host
- put (fabric.operations.put) : ngược lại với get, dùng để đưa một file từ local host sang remote host
- prompt (fabric.operations.prompt) : Đưa ra câu hỏi cho người dùng và lấy vào dữ liệu (tương tự raw_input)
- reboot (fabric.operations.reboot) : khởi động lại server và chờ boot up lại trong khoảng thời gian đã được chỉ định
Deploy một app
Sau đây ta sẽ thử tự động hoá công việc deploy một site được viết bằng Flask lên một server. Để có thể đóng gói một app ta cần có file setup.py. Đây là source của setup.py của app demo: https://github.com/vigov5/pvp-game
Cấu trúc thư mục của project như sau:
├── app │ ├── forms.py │ ├── __init__.py │ ├── models.py │ ├── static │ ├── templates │ ├── views.py ├── build_question.py ├── config.py ├── config.pyc ├── db_create.py ├── db_downgrade.py ├── db_migrate.py ├── db_repository │ ├── __init__.py │ ├── manage.py │ ├── migrate.cfg │ ├── README │ └── versions ├── db_upgrade.py ├── fabfile.py ├── LICENSE ├── MANIFEST.in ├── README ├── README.md ├── requirements.txt ├── run.py ├── server.py ├── setup.py
Ta cấu hình file setup.py như sau:
from setuptools import setup, find_packages setup( # Application name: name="pvp_game", # Version number (initial): version="0.1.2", # Application author details: author="Tien Nguyen Anh", author_email="nguyenanhtien2210@gmail.com", # Packages packages=find_packages(), # Include additional files into the package include_package_data=True, # Details url="http://pypi.python.org/pypi/PvPGame_v010/", # license="LICENSE", description="web socket quiz game", long_description=open("README").read(), zip_safe=False, # Dependent packages (distributions) install_requires=[ 'Babel==1.3', 'Fabric==1.10.2', 'Flask==0.10.1', 'Flask-Admin==1.0.8', 'Flask-Babel==0.9', 'Flask-Login==0.2.11', 'Flask-SQLAlchemy==1.0', 'Flask-WTF==0.9.5', 'Flask-WhooshAlchemy==0.55', 'Jinja2==2.8', 'MarkupSafe==0.23', 'MySQL-python==1.2.5', 'SQLAlchemy==0.9.7', 'Tempita==0.5.2', 'WTForms==1.0.5', 'Werkzeug==0.10.4', 'Whoosh==2.7.0', 'argparse==1.2.1', 'backports.ssl-match-hostname==3.4.0.2', 'blinker==1.4', 'certifi==2015.4.28', 'decorator==4.0.0', 'ecdsa==0.13', 'flup==1.0.2', 'itsdangerous==0.24', 'paramiko==1.15.2', 'pbr==0.11.0', 'pycrypto==2.6.1', 'pytz==2014.4', 'six==1.9.0', 'speaklater==1.3', 'sqlalchemy-migrate==0.9.1', 'tornado==4.0', 'wsgiref==0.1.2' ], )
Ta chỉ định các package cần cài đặt tại install_requires đi kèm với version tương ứng, tương tự như ở trong file requirements.txt. Ngoài package chính ta còn sử dụng các srip init nên cần có thêm file MANIFEST.in (đi kèm với thiết lập include_package_data=True) để kèm thêm các script này vào trong package. Bạn có thể tham khảo cách tạo package tại https://www.digitalocean.com/community/tutorials/how-to-package-and-distribute-python-applications hoặc https://docs.python.org/2/distutils/index.html
include db_create.py db_downgrade.py db_upgrade.py config.py build_question.py requirements.txt run.py recursive-include app/templates * recursive-include app/static * recursive-include db_repository *
Chúng ta sẽ định nghĩa một func pack dùng để tạo một zip file dùng cho việc deploy:
def pack(): local('python setup.py sdist --formats=gztar', capture=False)
Chạy func này sẽ sinh ra một file tar.gz ở trong thư mục dist:
(venv)[vigo@ubuntu ~/lab/pvp-game (master)]$ fab pack [vigo@localhost] Executing task 'pack' [localhost] local: python setup.py sdist --formats=gztar running sdist running egg_info writing requirements to pvp_game.egg-info/requires.txt writing pvp_game.egg-info/PKG-INFO writing top-level names to pvp_game.egg-info/top_level.txt writing dependency_links to pvp_game.egg-info/dependency_links.txt writing pbr to pvp_game.egg-info/pbr.json reading manifest file 'pvp_game.egg-info/SOURCES.txt' reading manifest template 'MANIFEST.in' writing manifest file 'pvp_game.egg-info/SOURCES.txt' running check creating pvp_game-0.1.2 creating pvp_game-0.1.2/app creating pvp_game-0.1.2/app/static creating pvp_game-0.1.2/app/static/css ... truncated ... hard linking db_repository/__init__.py -> pvp_game-0.1.2/db_repository hard linking db_repository/manage.py -> pvp_game-0.1.2/db_repository hard linking db_repository/migrate.cfg -> pvp_game-0.1.2/db_repository hard linking db_repository/versions/__init__.py -> pvp_game-0.1.2/db_repository/versions hard linking pvp_game.egg-info/PKG-INFO -> pvp_game-0.1.2/pvp_game.egg-info hard linking pvp_game.egg-info/SOURCES.txt -> pvp_game-0.1.2/pvp_game.egg-info hard linking pvp_game.egg-info/dependency_links.txt -> pvp_game-0.1.2/pvp_game.egg-info hard linking pvp_game.egg-info/not-zip-safe -> pvp_game-0.1.2/pvp_game.egg-info hard linking pvp_game.egg-info/pbr.json -> pvp_game-0.1.2/pvp_game.egg-info hard linking pvp_game.egg-info/requires.txt -> pvp_game-0.1.2/pvp_game.egg-info hard linking pvp_game.egg-info/top_level.txt -> pvp_game-0.1.2/pvp_game.egg-info Writing pvp_game-0.1.2/setup.cfg Creating tar archive removing 'pvp_game-0.1.2' (and everything under it) Done.
Các bước cần làm trước khi deploy lần đầu
- Khi deploy lên server lần đầu tiên, có thể bạn sẽ cần cài đặt thêm một số denpency cho các package cần cài đặt, ví dụ như MySQL-python. Ta có thể cài đặt các package đó thông qua lệnh:
def pre_install(): sudo('apt-get install python-dev libmysqlclient-dev')
Deploy code lên một server đang chạy
Ta thiết lập hàm deploy trong fabfile.py như sau:
def deploy(): dist = local('python setup.py --fullname', capture=True).strip() put('dist/%s.tar.gz' % dist, '/tmp/pvp_game.tar.gz') # now we're on the remote host from here on out! sudo('rm -rf /tmp/pvp_game') run('mkdir /tmp/pvp_game') with cd('/tmp/pvp_game'): run('tar zxf /tmp/pvp_game.tar.gz') run('mv ' + dist + '/* .') sudo('/var/www/pvp_game/venv/bin/python setup.py install') sudo('rm -rf /tmp/pvp_game /tmp/pvp_game.tar.gz') # alert Apache to reload the project sudo('touch /var/www/pvp_game/run.py')
Các lệnh trong hàm khá là dễ hiểu. Ta sẽ copy file tar.gz của package vào thư mục tạm, sau dó cài đặt package và cập nhật script run.py
Còn đây là cấu hình Apache của server:
<VirtualHost *:80> ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined <Directory /var/www/pvp_game> Require all granted AllowOverride All Order allow,deny Allow from all </Directory> WSGIDaemonProcess pvp_game python-path=/var/www/pvp_game:/var/www/pvp_game/venv/lib/python2.7/site-packages WSGIProcessGroup pvp_game WSGIScriptAlias / /var/www/pvp_game/run.py </VirtualHost>
Việc còn lại là deploy thôi