Nasazujeme Symfony do AWS

by on 4.4.2016

Po teoretickém úvodu v minulém článku bych se rád věnoval něčemu praktičtějšímu, a to deploymentu aplikace do cloudového prostředí v Amazonu. Představím hned dva způsoby, jakými je možné to vyřešit.

První přístup se hodí pro NEškálované aplikace běžící na jednom stroji, kde chceme nasazovat bez výpadků. Mnou popisovaný postup využívá nástroj Magallanes, který poskytuje funkce specifické pro Symfony.

Druhý přístup je mnohem sofistikovanější a využívá službu CodeDeploy od Amazonu. Hodí se pro škálované aplikace, které běží na variabilním počtu instancí a kde je možné dílčí instance vypnout po dobu nasazování (rolling deployment).

Netvrdím, že jsou to jediné přístupy, jak nasazovat aplikace do AWS. Fanoušci Dockeru použijí nejspíš úplně jinou cestu. Chci představit možnosti pro vývojáře, kteří Docker využít z nějakého důvodu nechtějí.

Metoda č. 1 – Magallanes

Magallanes je open source knihovna napsaná v PHP, kterou lze získat buď ve formě .phar souboru nebo přes Composer. Nebudu zde rozebírat možnosti její instalace ani základní obsluhu. Tento úkol plní dokumentace přímo na webu nástroje. Místo toho chci ukázat její reálné použití pro nasazování do AWS.

Nástroj používáme při vývoji Marketing mineru, kde se (dočasně) stará o deployment uživatelského rozhraní aplikace. To běží na Symfony 2.8 s adresářovou strukturou verze 3.0 (pro snadnější upgrade), takže se některé adresáře mohou lišit vůči aplikacím na starších verzích Symfony.

Magallanes umožňuje dva základní druhy nasazování – rsync a releases. Při prvním se pouze zkopírují změněné soubory na vzdálený server a nic dalšího se neděje. Pokud použijeme releases, bude nástroj na serveru udržovat specifikovaný počet verzí aplikace a při deploymentu pouze přepne symlink na novou verzi. Stejně tak funguje rollback, kterým se můžeme vrátit na libovolnou verzi aplikace (za předpokladu, že je ještě na serveru).

# deploy nové verze aplikace
bin/mage deploy to:production

# návrat na poslední verzi aplikace
bin/mage releases rollback --release=0 to:production

Konfigurační soubor Symfony aplikace pak může vypadat například takto:

deployment:
  user: marketingminer
  from: ./
  to: /var/www/marketingminer
  excludes:
    - app/config/parameters.yml
    - var/cache/*
    - var/logs/*
    - var/tmp/*
    - vendor/*
    - web/bundles/*
    - mage.phar
  env: prod
releases:
  enabled: true
  max: 2
  symlink: current
  directory: releases
hosts:
  - X.X.X.X
tasks:
  pre-deploy:
  on-deploy:
    - config-symlink
    - composer-install
    - composer/generate-autoload
    - symfony2/assets-install: {env: prod}
    - symfony2/cache-clear: {env: prod}
    - symfony2/cache-warmup: {env: prod}
    - symlink-prod-log
  post-release:
  post-deploy:

V sekci deployment se neděje nic až tak zajímavého. Jenom si povšimněte, že se z nasazování vylučuje soubor s konfigurací parameters.yml, který bude na vzdáleném serveru jiný. Vyloučený je i phar soubor s Magallanes, který není na vzdáleném serveru potřeba.

V sekci releases pozornému čtenáři neuniklo, že používáme pouze dvě verze aplikace. Je to z důvodu úspory místa na pevném disku (kvůli Doctrine a jejích 260 MB). Postačuje to pro situaci, kdy by nějaká změna web shodila a bylo nutné se vrátit zpátky na poslední funkční verzi.

V sekci tasks jsou specifikované skripty, které se spustí po zkopírování souborů na vzdálený server. Každý skript se znovu připojuje přes SSH na server (sdílení spojení není aktuálně možné), takže se vyplatí držet počet skriptů na minimu. Většina tasků je zabudovaná do Magallanes. Jsou tam ale i dva tasky, které jsou naše vlastní – config-symlinksymlink-prod-log. Řeší dva problémy, které vznikají při použití releases – zajištění konfiguračního souboru a ukládání logů – které si trochu rozebereme.

Konfigurace aplikace

Některé soubory nechceme mít v repozitáři, kde mohou padnout do nepovolaných rukou. Týká se to hlavně souboru parameters.yml, který obsahuje hesla do databáze, URL endpointů AWS služeb nebo přihlašovací údaje k externím službám. Existuje více cest, jak tento soubor dostat na vzdálený server bezpečnou cestou.

Můžeme ho na server nakopírovat přes SSH do nějaké sdílené složky a po nasazení vytvořit symlink do složky aplikace. Tento přístup funguje do té doby, než potřebujeme v konfiguraci udělat nějakou změnu. Pak je totiž nutné se znovu připojit na server a soubor tam změnit. To jde dělat jen u neškálovaných aplikací.

Jak jsme to tedy vyřešili? Použili jsme úložiště Amazon S3, kde máme privátní bucket, do kterého má přístup pouze určitý uživatel. Po zkopírování souborů na server se tento soubor zkopíruje ze vzdáleného úložiště do složky aplikace:

aws s3 cp s3://PRIVATE-BUCKET/parameters.yml app/config/parameters.yml

Předpokladem je, že na serveru máme nainstalovaný a nakonfigurovaný CLI tool od AWS (což je u Amazon Linuxu automatické).

Někdy je nutné přidat do příkazu ještě přepínač s regionem, kde je soubor uložený:

aws s3 cp --region=eu-central-1 SOURCE DESTINATION

Pokud tedy dojde ke změně/přidání nějakých konfiguračním parametrů, stačí změnit jeden soubor a změna se při příštím deploymentu zpropaguje na všechny servery.

Ukládání logů aplikace

Symfony aplikace od verze 3.0 používají pro logy, cache a dočasné soubory složku /var. My ale nechceme logy ukládat do adresáře v rámci dílčí verze aplikace. Při smazání dané verze bychom přišli i o logy. Mnohem lepší je ukládat je do sdílené složky někde mimo adresář aplikace, kde mohou zůstat napořád.

Pokud se rozhodneme pro složku /var/log, může celý skript vypadat takto:

namespace Task;

use Exception;
use Mage\Task\AbstractTask;
use Mage\Task\ErrorWithMessageException;
use Mage\Task\SkipException;

class SymlinkProdLog extends AbstractTask
{
   public function run() {
      if ($this-&gt;runCommandRemote(<strong>
           "cd var &amp;&amp; rm -r logs/ &amp;&amp; ln -s /var/log/marketingminer logs"</strong>
         ) === false)
      {
         return false;
      } else {
         return true;      
      } 
   }

   public function getName() {
      return "Symlink prod logs directory to /var/log";
   }
}

Metoda č. 2 – AWS CodeDeploy

Abychom završili vendor lock-in u Amazonu, můžeme začít používat i pro deployment jeho službu, a sice AWS CodeDeploy.

Jakmile začneme naši aplikaci škálovat dostaví se komplikace. Najednou nemůžeme použít cílený deployment (viz metoda č.1), který nasadí kód pouze na určité servery (identifikované doménou nebo IP adresou). Potřebujeme něco, co si poradí s deployem do auto-scaling groups, tzn. skupin instancí stejného typu. Potřebujeme něco, co pozná, zda byl deploy úspěšný, a může se tedy provést na všech instancích ve skupině. Úplně ideální by bylo, kdyby takové řešení nasadilo aktuální verzi aplikace i na nově vzniklý stroji v rámci autoscallingu. A toto všechno nám umožňuje AWS CodeDeploy.

Při vývoji Marketing mineru byla právě tato služba největším zdrojem šedivých vlasů, rozbitých klávesnic a hektolitrů kafe. Není úplně triviální ji používat pro deploy PHP aplikací, které fungují úplně jinak než Java aplikace. I když sám Amazon poskytuje dokumentaci, návody a use cases, tak to byla trnitá cesta.

Ale mám pro vás dobrou zprávu – vy už toto martirium podstupovat nemusíte. Můžete využít můj nástroj, který se postará o většinu věcí sám. Na vás zůstane pouze nastavení aplikace v CodeDeploy, instalace PHP SDK pro AWS a úprava některých skriptů podle požadavků vaší aplikace.

Veškerý kód a dokumentaci naleznete na mém GitHubu – saterond/code-deploy-command.

Shrnutí

Nasazování PHP aplikací do AWS se dá udělat mnoha způsoby a je na vývojáři a potřebách jeho aplikace, který si vybere. Můžete využít cílený deployment za pomoci nástroje Magallanes, který se hodí pro neškálované aplikace. Pokud ale provozujete škálovanou aplikaci běžící na více strojích, tak potřebujete nějaké sofistikovanější řešení jako je např. AWS CodeDeploy nebo Kubernetes (pokud preferujete Docker).

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *