PHP su Windows Azure : come fare e come funziona

Una delle caratteristiche più interessanti di Windows Azure è quella di poter far girare nel cloud linguaggi e framework diversi da C# e .NET 4.0. Il sistema operativo alla base di ogni macchina virtuale è Windows Server 2008 R2 e quindi potenzialmente qualunque cosa funzioni su Windows Server funziona anche su Azure. E’ possibile quindi installare Apache e Tomcat e far girare una soluzione scritta con Java o far girare applicazioni scritte con PHP sfruttando le estensioni di Internet Information Server 7.0 (se volete saperne di più seguite questo webcast del mio amico Piergiorgio).

In questo post vedremo come creare una semplice applicazione PHP in grado di leggere i dati da SQL Azure utilizzando Eclipse, di come sia possibile testare la soluzione all’interno del Development Fabric di Windows Azure e di come pubblicarla nel cloud.

Cosa ci serve

Prima di cominciare vediamo cosa ci serve per sviluppare, testare e distribuire la nostra applicazione PHP. La prima cosa da installare è la versione 1.3 del Windows Azure SDK. Possiamo utilizzare di fatto qualunque strumento per scrivere le pagine PHP e farne il deployment su Azure a mano (capiremo poi come fare) ma la cosa più semplice (quello che ho fatto io) è quella di utilizare Eclipse. Infine per rendere le cose ancora più semplici ci basta installare i Windows Azure Tools for Eclipse (per installare correttamente tutto seguite le istruzioni qui).

Partiamo

Utilizzando Eclipse creiamo un nuovo progetto e selezioniamo Windows Azure PHP Project.

image

Una volta inserito il nome del progetto apparirà una finestra delle proprietà dedicata ad Azure (aggiunta dai tool installati)

image

A seconda delle opzioni che selezionerete qui il wizard farà sostanzialmente due cose: inserire delle chiavi di configurazione del ServiceConfiguration.cfg file in modo da poterle poi utilizzare all’interno delle nostre pagine e inserirà nel progetto delle pagine di esempio una per ognuna delle opzioni che sceglierete (per esempio se seleziono SQL Azure e Blob verranno aggiunte due pagine chiamate SQLAzureSample.php e BlobSample.php).Potete accedere a queste configurazioni anche in seguito dalla finestra proprietà del progetto:

image

Fate finish e dovreste ottenere una schermata simile a questa (il nome del mio progetto è PHP Azure Test)

image

Il primo test

Siamo pronti per far girare questa applicazione all’interno del Development Fabric di Windows Azure. Assicuratevi prima di tutto di lanciare il Compute Emulator:

image

Ora selezioniamo dal menu di Eclipse Run Windows Azure PHP Project in Development Fabric

image

A questo punto dovremmo vedere all’interno di Eclipse (ad un indirizzo https://127.0.0.1:x/index.php) la pagina che viene creata in automatico dalla funzione phpinfo() . Per assicuraci che veramente stia girando sotto il Development Fabric (non si sa mai Smile) potete aprire il Compute Emulator e vedere la vostra soluzione pubblicata e in esecuzione (vedi figura sotto).

image

Il processo potrebbe darvi un errore di IIS nel caso in cui non abbiate installato il supporto CGI di Internet Information Server 7.0 (necessario per far girare PHP su IIS). Il modo più semplice per installarlo è quello di utilizzare il Microsoft Web Plarform Installer 3.0 ).

Aggiungiamo la nostra pagina di accesso a SQL Azure

Siamo pronti per pubblicare questa applicazione su Windows Azure ma prima di farlo aggiungiamo una pagina nuova che legga dei dati da SQL Azure e li mostri in forma tabellare. Da Eclipse create una nuova pagina php e assicuratevi di cambiare index.php aggiungendo un link alla nuova pagina. Inserite il seguente codice (nel mio caso uso una tabella SQL chiamata AzureApplications con 5 colonne)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">

<head>
<meta content="it" http-equiv="Content-Language" />
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>PHP on Windows Azure</title>
<style type="text/css">
.auto-style1 {
    color: #FFFFFF;
}
.auto-style2 {
    border: 1px solid #9933FF;
}
</style>
</head>

<body style='width: 703px'>

<table style='width: 100%' bgcolor='#9933FF'>
    <tr>
        <td><h2 class="auto-style1">ISV Azure Applications demo with PHP running in Windows Azure</h2></td>
    </tr>
</table>

/* Costruisco l’intestazione della tabella. */

<p>This demo is using a Worker Role in Windows Azure to host PHP on IIS 7.0 and
is connecting to a SQL Azure database.</p>
<table class="auto-style2" style='width: 100%' border='1'>
    <tr bgcolor="#9933FF">
        <td class="auto-style1">ID</td>
        <td class="auto-style1">Application Name</td>
        <td class="auto-style1">ISV Name</td>
        <td class="auto-style1">Contact</td>
        <td class="auto-style1" style='width: 170px'>Storage Type</td>
    </tr>

<?php

/* Recupero le informazioni sul nome del server, l’utente e la password dal file di configurazione di Windows Azure (vi ricordate la finestra iniziale ?)*/

$host = azure_getconfig('SqlAzureHost');
$dbuser = azure_getconfig('SqlAzureUserName');
$dbpwd = azure_getconfig('SqlAzurePassword');

$dbname = 'isvdemo';

/* Apro la connessione a SQL Server*/

$conn = sqlsrv_connect($host, array("Database" => $dbname,
                                    "UID" => $dbuser,
                                    "PWD" => $dbpwd,
                                    "MultipleActiveResultSets" => '0'));

if ($conn === false)
{
     DisplayErrors();
     die("Couldn't connect to SQL Server on $host");
}

/* Eseguo la query sul tabella AzureApplications*/

$query = "SELECT * FROM AzureApplications;";
$result = sqlsrv_query($conn, $query);

if ($result === false)
{
    print_r(sqlsrv_errors(SQLSRV_ERR_ERRORS));
    die("Couldn't fetch list of Applications");
}
else
{

/* Eseguo una fetch su tutte le righe e costruisco le colonne e le righe*/
    while ($row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC))
    {
        echo "<tr>";
        echo "<td>" . $row["IdApplication"] . "</td>";
        echo "<td>" . $row["ApplicationName"] . "</td>";
        echo "<td>" . $row["IsvName"] . "</td>";
        echo "<td>" . $row["IsvContact"] . "</td>";
        echo "<td>" . $row["ApplicationStorageType"] . "</td>";
        echo "</tr>";
    }
}

sqlsrv_free_stmt($result);
sqlsrv_close($conn);

function DisplayErrors()
{
     $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
     foreach( $errors as $error )
     {
          echo "Error: ".$error['message']."\n";
     }
}
?>
</table>
<br/>
</body>
</html>

Facciamo il Deployment

E’ arrivato il momento di pubblicare la nostra soluzione su Windows Azure. Possiamo farlo direttamente da Eclipse o attraverso il portale windows.azure.com. Vediamo come usare il portale. Prima di tutto dovete avere una sottoscrizione attiva su Windows Azure:

Attivazione di una sottoscrizione Azure
Se siete abbonati MSND potete andare qui per attivare una sottoscrizione gratuita di Windows Azure
Se non siete abbonati potete comunque attivare questa sottoscrizione https://mocp.microsoftonline.com/site/buy/MS-AZR-0001P che vi da la possibilità di testare la nostra piattaforma a costo zero (sempre che non si superino le soglie di utilizzo delle risorse)

Dal portale creiamo un nuovo hosted service che conterrà la nostra soluzione. A questo punto chiediamo a Eclipse di crearci il package da utilizzare per pubblicare la nostra soluzione. Dal menù selezionate Create a Windows Azure Service Package for Windows Azure PHP Project:

image

Dopo qualche secondo vi apparirà una finestra di explorer dove all’intero trovere i due file fondamentali che servono per caricare l’applicazione su Windows Azure : ServiceConfiguration.cscfg e il package che ha estensione cspkg (nel mio caso PHP Azure Test.cspkg). Il file di package contiente tutto quello che serve per mandare in esecuzione la nostra applicazione , le nostre pagine, le librerie .NET, il runtime di PHP e le funzioni PHP per accedere ai servizi di Windows Azure (li vediamo nel dettagio più avanti Smile)

image

Dal portale di Windows Azure selezioniamo il nostro hosted service (che abbiamo creato in precedenza) e selezioniamo New Staging Deployment. Inserite un nome e selezionate i due file descritti in precedenza:

image

Premiamo ok e aspettiamo. Windows Azure caricherà il package con la nostra soluzione, configurerà la macchina virtuale e manderà in esecuzione la nostra applicazione. Azure creerà anche un link (in questo caso con un guid perchè siamo in staging e non in deployment, nel mio caso https://35b17f4399c14618a4a839ca32591da6.cloudapp.net/, se decidessimo di pubblicare la soluzione in produzione riceveremo il link che abbiamo deciso durante la creazione dell’hosted service, nel mio caso https://phprun.cloudapp.net/index.php) che vi permetterà di accedere all’applicazione. Questo è il mio risultato:

image

Ma come funziona ?

Ora che abbiamo una soluzione funzionante su Azure vediamo un pò più da vicino come funziona, come Eclipse costruisce il file di package per fare il deployment e come viene attivato PHP sulla macchina virtuale. Come detto in precedenze, il file di package contiene tutto quello che serve per far funzionare il nostro software nel cloud. Il file è in formato zip ed è criptato per questione di sicurezza. Il file viene creato da Eclipse utilizzando il programma a riga di comando chiamato cspack.exe presente nel SDK di Windows Azure. Possiamo però vedere in realtà cosa c’è dentro il package analizzando i folder della nostra soluzione.

Infatti esiste una copia non zippata del file di package nel folder ServiceDefinition.csx presente sotto il folder della nostra soluzione. Vediamo cosa c’è dentro e capiremo come funziona il tutto.

image

Qui a fianco trovate la rappresentazione della gerarchia di folder al di sotto della cartealla ServiceDefinition.csx.Dentro questo folder troviamo il file ServiceDefinition.csdef che contiente le configurazioni del nostro servizio, come per esempio gli end point o la dimensione della macchina virtuale.Entrando dentro il folder roles/phpdemo troviamo :

1- un folder approot checontiene tutti i file della nostra soluzione, le librerie .NET, il runtime PHP e le librerie PHP per accedere a Windows Azue.
2 – un file __entrypoint.txt che indica al fabbric controller quel’è la dll che manda in esecuzione il nostro ruolo, nel mio caso webrole.dll che si trova nel folder approot/bin

Dentro approot troviamo :
1 - un folder bin che contiene la nostra dll e le librerie .NET di Windows Azure.
2 – un folder php che contiene tutto il runtime di PHP compreso php.exe
3 – un folder WindowsAzureSDKForPHP che contiene le funzioni php per accedere ai servizi di Windows Azure come i blob o le api di diagnostica
4 – tutti i file php che fanno parte della nostra soluzione più il file web.config che è fondamentale perchè sarà lui a dire a IIS di eseguire PHP in modalità fastCGI.

Tutto questi file vengono copiati nella macchina virtuale che viene creata su Azure mantenendo la stessa gerarchia. Dentro il file cspack possiamo inserire tutte le risorse che ci servono come per esempio le immagini o anche dei programmi che potremmo eseguire direttamente dalla nostra applicazione. A questo punto manca un solo passo: abbiamo i file nella macchina virtuale, abbiamo copiato il runtime di php e le librerie, abbiamo configurato i firewall per aprire le porte verso l’esterno, abbiamo indicato che tipo di macchina virtuale e quanti front end. Dobbiamo dire ad IIS di non rispondere lui alle pagine php (cosa che tenterebbe di fare ricevendo richieste sulla porta 80) ma di passare la richiesta alla cgi di php chiamata php-cgi.exe.

In realtà è già tutto pronto e configurato per noi nel file web.config che riporto sotto. Andiamo a dire a IIS che esiste un handler che deve essere invocato per tutte le richieste su file con estensione php e che nel caso la risorsa non fosse specificata quella di default si chiamerà index.php

 

<system.webServer>

   <handlers>
     <clear />
     <add name="PHP via FastCGI"
          path="*.php"
          verb="*"
          modules="FastCgiModule"
          scriptProcessor="%RoleRoot%\approot\php\php-cgi.exe"
          resourceType="Unspecified" />
     <add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="Either" requireAccess="Read" />
   </handlers>
   
   <defaultDocument>
     <files>
       <clear />
       <add value="index.php" />
     </files>
   </defaultDocument>
</system.webServer>

Ogni volta che l’utente chiederà una pagina php attraverso l’url creato da Windows Azure, IIS girerà le richieste alla cgi che le manderà in esecuzione permettendoci quindi di utilizzare Windows Azure per tutte le nostre soluzioni scritte in PHP.