phing i fabric - budowanie i deployment aplikacji webowych
TRANSCRIPT
PHING I FABRICBudowanie i deployment aplikacji webowych
Leszek KrupińskiPHPers #1, 20 maja 2013 r.
Leszek Krupiński@leafnode http://leafnode.pl/
2001: FAQ pl.comp.lang.php
2001: Tłumaczenie podręcznika
We’re hir
ing!
Kilka konferencji, kilka wykładów, jedno szkolenie, kilka artykułów, jeden wywiad.
OBECNIEBezpieczeństwo, scalability, architektura
DO RZECZYDużo slajdów, nie zwracajcie na nie większej uwagi
CO BUDOWAĆ?Generowanie plików statycznych, dynamicznych, minimalizacja,
konfiguracja, parametryzacja, spełnianie wymogów (uprawnienia)...
PO CO AUTOMAT?
BRAK PORZĄDKU WPROWADZA ZMIANY
http://www.flickr.com/photos/jasonpoon/3926723912/© Jason Poon, CC-BY-NC-SA
POWTARZALNOŚĆZa każdym razem brak błędu lub te same błędy
http://www.flickr.com/photos/legofenris/4641828205/© leg0fenris, CC-BY-NC-SA
WIARYGODNE PRZYGOTOWYWANIE
PAKIETÓW WYJŚCIOWYCHhttp://www.flickr.com/photos/snowblink/2228929261/
© Jon Lim, CC-BY-NC-ND
REJESTRACJA BAZY WIEDZYhttp://www.flickr.com/photos/jezpage/4047231133/
© Jeremy Page, CC-BY-NC-ND
PHINGPHing Is Not GNU make
Wzorowany na Apache Ant
CECHY
• Łatwy do rozszerzania
• Składnia XML
• Gotowe taski integrujące z popularnymi pakietami
• Wywoływanie kodu PHP
INSTALACJAPoprzez PEAR:
pear channel-discover pear.phing.infopear install phing/phing
$ phing -v Phing 2.5.0
INSTALACJAPoprzez Composer:
{ "require": { "phing/phing": "2.5.0" }}
$ ./vendor/bin/phing -v Phing 2.5.0
PROSTY PLIK BUILDUZazwyczaj nazywany build.xml
<?xml version="1.0" encoding="UTF-8"?><project name="FooBar" default="dist"> <target name="prepare"> <echo msg="Making directory ./build" /> <mkdir dir="./build" /> </target> <target name="build" depends="prepare"> <echo msg="Copying files to build directory..." /> <echo msg="Copying ./about.php to ./build directory..." /> <copy file="./about.php" tofile="./build/about.php" /> <echo msg="Copying ./browsers.php to ./build directory..." /> <copy file="./browsers.php" tofile="./build/browsers.php" /> <echo msg="Copying ./contact.php to ./build directory..." /> <copy file="./contact.php" tofile="./build/contact.php" /> </target> <target name="dist" depends="build"> <echo msg="Creating archive..." /> <tar destfile="./build/build.tar.gz" compression="gzip"> <fileset dir="./build"> <include name="*" /> </fileset> </tar> <echo msg="Files copied and compressed in build directory OK!" /> </target> </project>
COŚ PROSTSZEGO
<?xml version="1.0" encoding="UTF-8"?> <project name="HelloWorld" default="hello"> <target name="hello" description="Wyświetla powitanie"> <echo msg="Hello, world!" /> </target> </project>
Buildfile: /home/leafnode/phing/1/build.xmlHelloWorld > hello: [echo] Hello, world!BUILD FINISHEDTotal time: 0.0630 seconds
SKŁADOWE PLIKU
• Project
• Target
• Task
• Property
PROJECTGłówny węzeł pliku, zawiera jeden lub więcej celów (targets)
TARGETGrupa zadań wykonywanych jako całość
ZADANIEElement którego wykonanie ma wykonać pewną funkcję
PROPERTYZmienna
<?xml version="1.0" encoding="UTF-8"?> <project name="HelloWorld" default="hello"> <target name="hello" description="Wyświetla powitanie"> <property name="imie" value="Jasio" /> <echo msg="Cześć, ${imie}!" /> </target> </project>
WBUDOWANE PROPERTIESos.name, php.version, user.home itp.
http://www.phing.info/docs/guide/stable/chapters/appendixes/AppendixA-FactSheet.html#BuiltInProperties
ZEWNĘTRZNE PROPERTIESŁatwa konfiguracja buildu
LINIA POLECEŃphing -Dimie=”Zbyszek”
ZEWNĘTRZNY PLIK<property file="./build.properties" />
# Pary klucz/wartość#klucz=wartość# This dir must contain the local applicationbuild.dir=../# Credentials for the database migrationsdb.host=localhostdb.user=userdb.pass=passworddb.name=example# paths to programsprogs.mysql=/usr/bin/mysql
Buildfile: /home/leafnode/phing/3/build.xmlHelloWorld > hello: [property] Loading /home/leafnode/phing/3/./build.properties [echo] Cześć, Jasio!BUILD FINISHEDTotal time: 0.0657 seconds
WIELE TARGETÓW
<?xml version="1.0" encoding="UTF-8"?> <project name="HelloWorld" default="hello"> <target name="hello" description="Wyświetla powitanie"> <property file="./build.properties" /> <echo msg="Cześć, ${imie}!" /> </target> <target name="bye"> <echo msg="Papa!" /> </target> </project>
DOMYŚLNIE WEDŁUG ATRYBUTU
Wskazanie konkretnego targetu:$ phing nazwa_targetu
BLOKOWANIE URUCHAMIANIA RĘCZNEGO
“-” przed nazwą targetu
ZALEŻNOŚCI
<?xml version="1.0" encoding="UTF-8"?> <project name="HelloWorld" default="bye"> <target name="hello" description="Wyświetla powitanie"> <property file="./build.properties" /> <echo msg="Cześć, ${imie}!" /> </target> <target name="bye" depends=”hello”> <echo msg="Papa!" /> </target> </project>
Buildfile: /home/leafnode/phing/5/build.xmlHelloWorld > hello: [property] Loading /home/leafnode/phing/5/./build.properties [echo] Cześć, Jasio!HelloWorld > bye: [echo] papaBUILD FINISHEDTotal time: 0.0797 seconds
WIELE ZALEŻNOŚCIdepends="pre-init, build, package"
RĘCZNE WYWOŁYWANIE TARGETÓW
Np. narzędziowych
<target name="process"> <echo msg="Processing ${data}" /></target> <target name="build"> <phingcall target="process"> <property name="data" value="some data" /> </phingcall> </target>
ZEWNĘTRZNY PLIK
<phing phingfile="utils.xml" target="bazingify"> <property name="bazinga" value="meh" /></phing>
IMPORTOWANIE CAŁEGO PLIKU
<import file="build.utils.xml" />
PRACA NA ZBIORACH PLIKÓW
<target name="copyfiles"> <copy todir="/tmp/deploy"> <fileset dir="." includes="**/*.php" /> </copy> </target>
ZŁOŻONE WARUNKI
<fileset dir="." includes="**/*.php"> <and> <size value="1024" when="more" /> <date datetime="20-05-2013 18:00" when="after" /> </and> </fileset>
FILTRY
<target name="copyfiles"> <property file="./build.properties" /> <copy todir="/tmp/deploy"> <filterchain> <replacetokens begintoken="@@" endtoken="@@"> <token key="DBNAME" value="${db.name}" /> <token key="DBUSER" value="${db.user}" /> <token key="DBPASS" value="${db.pass}" /> </replacetokens> </filterchain> <fileset dir="." includes="**/*.php" /> </copy> </target>
ZMIANA NAZW PLIKÓW
<mapper type="glob" from="*.bat" to="*.txt"/>
<mapper type="regexp" from="^(.*)\.ent\.xml$" to="\1.php"/>
INSTRUKCJE WARUNKOWE
<if> <equals arg1="${foo}" arg2="bar" /> <then> <echo message="The value of property foo is bar" /> </then> <else> <echo message="The value of property foo is not bar" /> </else> </if>
<condition property="isMacOrWindows"> <or> <os family="mac"/> <os family="windows"/> </or> </condition>
PĘTLE
<foreach param="filename" absparam="filename" target="print"> <fileset dir="."> <include name="*.php" /> </fileset> </foreach>
INTERAKCJA<input propertyname="env" defaultValue="dev">Jakie środowisko?</input>
WYWOŁYWANIE FUNKCJI PHP
<php function="strtoupper" returnProperty="builddir"> <param value="${tag}" /></php>
PRZEGLĄD TASKÓW
<-- List the contents of "/home". --><exec command="ls -l" dir="/home" /> <-- Start the make process in "/usr/src/php-4.0". --><exec command="make" dir="/usr/src/php-4.0" /> <-- List the contents of "/tmp" out to a file. --><exec command="ls -l /tmp > foo.out" escape="false" />
<-- Exit with message --><fail message="Failed for some reason!" /><-- Exit if ${errorprop} is defined --><fail if="errorprop" message="Detected error!" /> <-- Exit unless ${dontfail} prop is defined. --><fail unless="dontfail" message="Detected error!" />
<mail tolist="[email protected]" subject="build complete">The build process is a success...</mail>
<chmod file="/home/test/mine.txt" mode="0500" verbose="true" />
<svncopy username="user" password="pass" repositoryurl="svn://localhost/phing/trunk/" todir="svn://localhost/phing/tags/1.0"/> <svnexport repositoryurl="svn://localhost/project/trunk/" todir="/home/michiel/dev"/> <svnlastrevision repositoryurl="svn://localhost/project/trunk/" propertyname="lastrev"/><echo>Last revision: ${lastrev}</echo>
<gitinit repository="${repo.dir.resolved}" /> <gitclone repository="git://github.com/path/to/repo/repo.git" targetPath="${repo.dir.resolved}" /> <gitpull repository="${repo.dir.resolved}" all="true" />
<docblox title="API Documentation" destdir="apidocs" template="new_black"> <fileset dir="./classes"> <include name="**/*.php" /> </fileset> </docblox>
<jsMin targetDir="docroot/script/minified" failOnError="false"> <fileset dir="docroot/script"> <include name="**/*.js"/> </fileset> </jsMin>
<phpunit codecoverage="true"> <formatter type="xml" todir="reports" /> <batchtest> <fileset dir="src"> <include name="**/*Test.php" /> </fileset> </batchtest> </phpunit>
<zip destfile="output.zip"> <fileset dir="build"> <include name="**/*.php" /> </fileset> </zip>
DEPLOYMENTFabric
CZEMU NIE DEPLOYMENT PRZEZ PHING?
Można, ale XML.
ZDALNE POLECENIE
<?xml version="1.0"?><project name="sshtest" default="main"> <target name="main"> <ssh username="username" password="password" host="192.168.1.200" command="pwd" /> </target> </project>
<ssh username="username" password="password" host="192.168.1.200" command="pwd" property="mypwd" display="false" /><echo>The present working directory is ${mypwd}</echo>
KOPIOWANIE PLIKÓW
<scp username="john" password="smith" host="webserver" todir="/www/htdocs/project/"> <fileset dir="test"> <include name="*.html" /> </fileset> </scp>
MIGRACJA BAZY DANYCH
<dbdeploy url="sqlite:${project.basedir}/data/db.sqlite" userid="dbdeploy" password="dbdeploy" dir="${project.basedir}/data/dbdeploy/deltas" />
http://dbdeploy.com/documentation/getting-started/rules-for-using-dbdeploy/
<pdosqlexec src="deploy.sql" url="sqlite:test.db"/>
http://www.liquibase.org/
<liquibase-update jar="/usr/local/lib/liquibase/liquibase.jar" classpathref="/usr/local/lib/liquibase/lib/mysql-connector-java-5.1.15-bin.jar" changelogFile="./changelogTest.xml" username="liquibase" password="liquibase" url="jdbc:mysql://localhost/mydatabase" />
CZEMU NIE CHEF LUB PUPPET?
DUŻO POTRAFIĄ
Kimberly VardemanCC-BY-SA
UŻYWANIE JEST SKOMPLIKOWANE
Frank Bruns-BallhausenCC-BY-SA 3.0/de
WYMAGANE DODATKOWE ZASOBY
http://www.flickr.com/photos/rolohauck/3432135840/© Roland Hauck, CC-BY-NC-SA 2.0
(i wolę Pythona od Ruby’ego ;) )
INSTALACJA
pip install fabric
INSTALACJA PIPeasy-install pip
INSTALACJA EASY-INSTALLapt-get install python-setuptools
WINDOWS
• Ze strony python.org:
• instalator python
• instalator setup-tools
• easy_install pip
• pip install fabric
PLIK WEJŚCIOWYZwykły kod Pythona
fabfile.py
from fabric.api import rundef host_type(): run('uname -s')
$ fab -H localhost,linuxbox host_type[localhost] run: uname -s[localhost] out: Darwin[linuxbox] run: uname -s[linuxbox] out: LinuxDone.Disconnecting from localhost... done.Disconnecting from linuxbox... done.
ARGUMENTY
def hello(name="world"): print("Hello %s!" % name)
$ fab hello:name=JeffHello Jeff!Done.
Polecenie lokalne: local
from fabric.api import localdef test(): local("phing unittest") def commit(): local("git add -p && git commit") def push(): local("git push") def prepare_deploy(): test() commit() push()
Polecenie zdalne: run
def deploy(): deploy_dir = '/var/www/app' with cd(code_dir): run("git pull") run("phing")
DZIAŁANIA INTERAKTYWNE
def deploy(): deploy_dir = '/var/www/app' with settings(warn_only=True): if run("test -d %s" % code_dir).failed: run("git clone user@host:repo.git %s" % code_dir) with cd(code_dir): run("git pull") run("phing")
TASKIfrom fabric.api import task, run@taskdef mytask(): run("a command")
REAKCJE[my_server] run: test -d /srv/django/myprojectWarning: run() encountered an error (return code 1) while executing 'test -d /var/www/app'[my_server] run: git clone user@pass:repo.git /var/www/app[my_server] out: Cloning into /srv/django/myproject...[my_server] out: Password: <enter password>[my_server] out: remote: Counting objects: 6698, done.[my_server] out: remote: Compressing objects: 100% (2237/2237), done.[my_server] out: remote: Total 6698 (delta 4633), reused 6414 (delta 4412)[my_server] out: Receiving objects: 100% (6698/6698), 1.28 MiB, done.[my_server] out: Resolving deltas: 100% (4633/4633), done.[my_server] out:[my_server] run: git pull[my_server] out: Already up-to-date.
PRZYKŁAD
from fabric.api import * from datetime import datetimeenv.hosts = ['appserver']
def get_ts(): d = datetime.now() return d.strftime("%Y-%m-%d_%H-%M") def deploy(): ts = get_ts() with cd("/var/www"): run("mkdir app-"+ts) put(".", "/var/www/app-"+ts) build("/var/www/app-"+ts) with cd("/var/www"): run("rm app") run("ln -s app-"+ts+" app")
PYTANIA?