Deployment prakticky - WebExpo 2011

Letos jsem přednášel na Webexpu na téma deployment. Ve 30 minutách se nedá říci dost, aby jste se dozvěděli všechno tak zkusím v tomto článku shrnout o čem jsem mluvil a přidat nějaké zdroje, které pomůžou v dalších úsilí při automatizaci deploymentu.

Deployment

  • Nasazení dané aplikace na dané prostředí v daném čase s možností vrácení původní verze aplikace pokud možno s minimálními prostředky a náklady.

Configuration Management

  • Služba, která se stará, aby námi zadaná konfigurace byla konzistentní napříč prostředím a na všech strojích a je to jeden nezbytných kroků pro CI.

Continuous Integration

CI pomáhá dramaticky snížit počet chyb při vývoji i v produkci pokud se to dělá dobře a důsledně.

Pro dobré fungovaní Continuous Integration potřebujete automatizovat deployment na prostředí. Na prostředích se často střídají aktuální feature větve vývojářů nebo se tam provádí update z větve pro release či sprint. K tomu se vše provádí několikrát denně. Abychom tohle mohli dělat potřebuje deployment mít plně automatický.

"Automated deployment, tied into good CI discipline, is essential to making this work." - Martin Fowler

Tlačítko push

Deployment musí být tak jednoduchý, aby ho zvládnul kdokoliv, kdo ho potřebuje a má potřebné oprávnění. Zdá se celkem jednoduché, ale není tomu tak. Pokud je potřeba například znalost linuxu (vytváření symlinku, ssh klient apod.), může to být pro část týmu třeba dost velká překážka, aby si něco nasadili a raději řeknou developerovi. On to má přece za minutku nasazené. Ale v tom je problém, pokud developer není po ruce, nebo nemá čas, tak část práce stojí. Obzvláště při agilním vývoji a krátkém sprintu to může být dost znát.

Pokud ještě se deployment dělá ručně, občas se na něco zapomene a jakákoliv ruční práce je zdrojem chyb. Celkově dojde k úspoře nákladů, protože čas ztrávený deploymentem firmě nikdo nezaplatí.

Za tlačítkem push si můžete představit jakýkoliv spouštěč, který vám vyhovuje (git push do příslušné větve, tlačítko na webu apod.).

Pokud nemáte zkušenost s gitem, tak rozvedu ten git push. Ostatní mohou odstavec přeskočit.

Git jako distribuovaný systém funguje jen lokálně a může mít svoje vzdálené kopie repository podobně jako v architektuře klient server (Subversion). Ale nemusí mít jen jedno místo kam se zdrojový kód odesílá pomocí git push. Toho se dá využít a používá se to tak, že kromě serveru kam ukládáte zdrojový kód (u mě například Github.com), který je označený jako origin (git push origin master – odešle kód tam), mohu například přidat místo kde se pushem přímo nasadí (git push heroku master – kód se odešle na cloud Heroku a dojde k jeho nasazení). Obdobně kromě místa, lze použít určité větve a mít na serveru hook script, který detekuje změny a provede nasazení.

Jak to nechat na jiných

Pokud můžete a děláte projekt, kde se můžete dovolit využít hostingu, cloudu, který deployment vyřeší za vás, udělejte to. Ušetří vám to čas i prostředky. Samozřejmě ne vždy to jde, ale pro ty případy tu bude další kapitola.

Heroku.com

Další služby, recenze některých jsem dělal dříve. – http://www.pagodabox.comhttp://phpfog.comhttp://orchestra.io/http://www.engineyard.comhttp://cloudcontrol.com/http://relbit.comhttp://nodester.com/

Samozřejmě jsem nemohl vyzkoušet podrobně každou cloudovou službu, na to nemám rozpočet a ne každá má k dispozici zdarma potřebný program pro vývojáře. Důležité aspoň pro mě je podpora práce z CLI kvůli možnosti automatizace, případně nějaké API, které to umožňuje. A také podpora vlastního nastavení a úkolů, aby jste mohli například automaticky spustit test nebo migraci databáze. Svoje zkušenosti nebo rady a doporučení uveďte do komentářů, budu rád.

Jak si to vyrobit sám

Rozdělení nástrojů pro deployment

Capistrano

Instalace – pro instalaci potřebujete ruby (windows – http://rubyinstaller.org/, mac – součást OS) – pokud budete deployment dělat pro ruby aplikaci nepotřebujete samozřejmě railsless-deploy

gem install capistrano capistrano-ext railsless-deploy

Zdrojový kód si stáhněte z githubu.

git clone https://github.com/abtris/webexpo2011-wordpress.git

Zajímají nás tyto soubory:

Capfile
templates/apache.conf.erb
config/deploy/development.rb
config/deploy/production.rb
config/deploy/apache.rb
config/deploy/deploy.rb

které nám umožňují dělat deployment: cap deploy cap development deploy cap production deploy

a configuration management:

cap apache:setup
cap apache:start
cap apache:stop
cap apache:restart

Praktická ukázka Capistrana na videu

Jenkins - jak se to dělá v Jobs.cz

  • v LMC (jobs.cz, prace.cz) je vývoj v Javě a PHP. Pro oboje používáme od roku 2008 na build Hudson dnes Jenkins. Pro Javu se přechází na build postavená na Mavenu a v PHP používáme build napsaný v Antu, který vytváří RPM balíčky pro nasazení CFEngine nebo pomocí skriptu přímo z Jenkins.
  • jak vyrobit RPM pro PHP - ukázka s kódem k dispozici https://github.com/abtris/barcamp2010praha

etc/demoapp.spec

Summary: Demo App
Name: @@PACKAGE_NAME@@
Version: @@VERSION@@
Release: @@BUILD@@
License: Copyright by Ladislav Prskavec
Group: System Environment/Internet
BuildRoot: @@BUILDROOT@@

#topdir
%define _topdir @@TOPDIR@@
%define _target_os Linux

%description
Barcamp 2010 demo app

%install
mkdir -p $RPM_BUILD_ROOT/srv/www/demoApp/@@CURRENT@@
cp -r ${FILENAME}-${VERSION}/* $RPM_BUILD_ROOT/srv/www/demoApp/@@CURRENT@@/

%clean
[ "${RPM_BUILD_ROOT}" != "/" ] && rm -rf ${RPM_BUILD_ROOT}

%files
/srv/www/demoApp/@@CURRENT@@

%defattr(755,root,root)
%dir /srv/
%dir /srv/www
%dir /srv/www/demoApp/
%dir /srv/www/demoApp/@@CURRENT@@/

%post
echo "run: ln -nfs /srv/www/demoApp/@@CURRENT@@/ /srv/www/demoApp/current"

a k tomu příslušný build script

<project name="project_name" default="clean" basedir=".">
<!-- task definition for cycle -->
<taskdef resource="net/sf/antcontrib/antlib.xml"/>
    <description>
        barcamp demo app
    </description>

  <!-- options from properties file -->
  <!-- ${ws} is in Hudson $WORKSPACE -->
  <echo message="reading properties from: ${ws}/etc/build.properties" />
  <echo message="module name ${modulename}" />
  <property file="${ws}/etc/build.properties"/>
  <!-- options from .spec file -->
  <property name="spec_file" value="/etc/${modulename}.spec" />
  <!--  make unique rpm_build_root, solve problems with branches -->
  <exec executable="mktemp" outputproperty="rpm_build_root">
    <arg value="-d" />
    <arg value="-t" />
    <arg value="${modulename}.XXXXXXXXXX" />
  </exec>
  <echo message="RPM Build root set to: ${rpm_build_root}" />
  <!-- set global properties for this build -->
  <property name="build" location="${rpm_build_root}/BUILD"/>
  <property name="rpms" location="${rpm_build_root}/RPMS"/>

 <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
    <mkdir dir="${rpms}/i386"/>
  </target>

 <target name="gitrevision" unless="REV" depends="init">
   <exec executable="git" output="${rpm_build_root}/gitinfo">
     <arg value="rev-list"/>
     <arg value="--all"/>
   </exec>
   <exec executable="wc" output="${rpm_build_root}/wcinfo">
     <arg value="-l" />
     <arg value="${rpm_build_root}/gitinfo" />
   </exec>
   <exec executable="awk" outputproperty="REV">
     <arg value="{ print $1 }"/>
     <arg value="${rpm_build_root}/wcinfo"/>
   </exec>
   <echo message="REV: ${REV}" />
 </target>

 <target name="gitexport" depends="gitrevision">
  <mkdir dir="${build}/${modulename}-${REV}" />
  <copy todir="${build}/${modulename}-${REV}">
    <fileset dir="../">
      <exclude name="../.git/*"/>
    </fileset>
  </copy>
   <echo message="Coping files ..." />
 </target>

 <!-- take spec files  -->
 <target name="spec" depends="gitexport">
        <!-- get spec files -->
        <path id="spec.files">
            <fileset  dir="${build}/${modulename}-${REV}/etc">
                <include name="*.spec"/>
            </fileset>
        </path>
        <!-- convert slashes to unix (necessary for Win) -->
        <pathconvert pathsep="," targetos="unix" property="files" refid="spec.files"/>

        <echo message="Call RPM" />
        <!-- Call RPM for all spec files (task for need antcontrib -->
        <for list="${files}" param="item">
        <sequential>
            <antcall target="replace">
                <param name="spec_file" value="@{item}"/>
            </antcall>
            <antcall target="rpm">
                <param name="spec_file" value="@{item}"/>
            </antcall>
        </sequential>
        </for>

 </target>

  <!-- replace tokens in spec files -->
  <target name="replace">
    <replace file="${spec_file}" token="@@CURRENT@@" value="${major_version}-${REV}"/>
    <replace file="${spec_file}" token="@@TOPDIR@@" value="${rpm_build_root}"/>
    <replace file="${spec_file}" token="${VERSION}" value="${REV}"/>
    <replace file="${spec_file}" token="${FILENAME}" value="${modulename}"/>
    <basename property="spec.filename" file="${spec_file}" suffix=".spec" />
    <echo message="${spec.filename}" />
    <replace file="${spec_file}" token="@@PACKAGE_NAME@@" value="${spec.filename}"/>
    <replace file="${spec_file}" token="@@VERSION@@" value="${major_version}"/>
    <replace file="${spec_file}" token="@@BUILD@@" value="${REV}"/>
    <replace file="${spec_file}" token="@@BUILDROOT@@" value="${rpm_build_root}-${spec.filename}/" />
    <replace file="${spec_file}" token="@" value=""/>
  </target>

 <!-- rpmbuild  -->
 <target name="rpm">
   <echo message="SPEC: ${spec_file}" />
   <exec executable="rpmbuild">
      <arg value="--target=i386"/>
      <arg value="-bb"/>
      <arg value="${spec_file}"/>
   </exec>

 </target>

  <!-- copy RPM files to TOP_DIR and delete tmp dirs -->
  <target name="clean" depends="spec">
    <copy todir="${target_path}">
        <fileset  dir="${rpms}/i386/">
        <include name="*.rpm"/>
    </fileset>
    </copy>  </target>

</project>

Infrastructure As code

Pokud máte k dispozici více prostředí, nebo pokud používáte masivnější virtualizaci jistě víte, že to bez automaticace nejde.

K nejznámějším nástrojům patří CFEngine, Puppet a Chef.

1993 - CFEngine
2002 - CFEngine 2
2005 - Puppet
2009 - CFEngine 3, Chef

O chefu jsem se zmiňoval již dříve, je vhodný právě například pro tvorbu virtualizací ve velkém. Pro příklad uvedu cluster Nostromu, který měl 30 000 jader.

Puppet – http://www.puppetlabs.com – napsaný v Ruby, odvozený od cfengine – klient aktualizuje konfiguraci v pravidelných intervalech a reportuje zpět na server (Puppet Master) – perfektní pro “sysadmins” – deklarativní popis nastaveni

Chef – http://www.opscode.com/chef – v Ruby, odvozený od Puppetu – Ruby DSL – cloud provisioning – imperativní popis, více flexibilní, více pro “vývojáře” – pokud by vás to zajímalo doporučuji zajít na školení Radima Marka (GoodData) – s kódem WEBEXPO2011 by měla být sleva 15%

Slidy

Závěr - co si odnést

  • Existují kvalitní nástroje pro deployment, použijte je!
  • Každý oprávněný uživatel si lehce zjistí informaci o verzi aplikace na příslušném prostředí a jednoduše si nasadí verzi aplikace, kterou potřebuje na prostředí, kde to potřebuje.
  • Jedině automatizací se vyhnete chybám. Každý ruční postup jednou selže.
  • Uspoříte velké náklady, protože čas ztraceny deploymentem nepřináší žádný zisk.

Jenkins - jak na Continuous Integration v PHP


Pochopení a využití procesu Continuous Integration s využitím nástroje Jenkins vám pomůže zvýšit kvalitu softwaru, který vyvíjíte, a zároveň snížit čas na jeho dodávku. Continuous Integration vám umožňuje kontrolovat kvalitu softwaru průběžně po malých částech a minimalizovat tak riziko rozsáhlých chyb, jak tomu bylo v případě klasického vodopádového přístupu.

Comments