Laravel sur Google App Engine

Passer un site Laravel sur Google App Engine ne se fait pas en 5 minutes. On ne parle pas ici de basculer un site sur une machine virtuelle type Compute Engine, mais bien de tout virtualiser sous forme de container, et de ne plus s’occuper de l’infrastructure. Voyons ensemble les différents éléments à prendre en compte pour effectuer cette transition.

Les dépendances:

Effectuez avant toute chose l’import de ces dépendances:
composer require google/cloud-logging
composer require google/cloud-error-reporting
composer require superbalist/laravel-google-cloud-storage
composer require nunomaduro/collision

Cela vous permettra d’accéder aux buckets (fichiers qui peuvent être modifiés).
Voici un tutoriel qui vous aidera à accéder aux fichiers.
Pensez à rajouter dans la partie scripts, le code suivant pour recompiler le cache.

"post-install-cmd": [
	"chmod -R 755 bootstrap\/cache",
	"php artisan config:cache",
	"php artisan route:cache"
]

Les fichiers:

Avec GAE, vous n’avez plus accès au système de fichiers (il est en lecture seule) et ça implique pas mal de contraintes. Votre projet ne doit pas dépasser les 10 000 fichiers. Il faut donc lui indiquer quels sont les répertoires pour créer des fichiers cache. On verra plus tard comment.

Autre chose, votre répertoire principal se situera dans /srv, ainsi si vous devez accéder à un fichier, vous pourrez lui indiquer sous cette forme /srv/config/key.json, mais normalement, vous devrez passer par des buckets pour tout fichier que vous devrez enregistrer.

En 1er lieu, vous devrez donc, modifier votre fichier bootstrap/app.php, et lui rajouter juste avant la fin:
$app->useStoragePath(env(‘APP_STORAGE’, base_path() . ‘/storage’));

Vous devez également modifier ou se situe le cache dans
config/views.php
‘compiled’ => storage_path(),//realpath(storage_path(‘framework/views’)),

Le déploiement:

Avant tout, il vous faudra installer le google cloud SDK. Ensuite, une fois celui ci relié à votre projet du cloud, il vous faudra écrire ce fichier à la racine du projet app.yaml, et lancer le déploiement avec la commande gcloud app deploy.

La partie runtime indique de partir sur un container basé sur PHP 7.2. Vous avez moyen de partir sur des environnements plus souples qu’on nomme flex ou custom, mais votre déploiement ne prendra pas 1 minutes mais plutôt 15 ! Donc, restez simple et prenez les images optimisées par Google.
Ensuite, on lui indique d’activer Stackdriver pour les logs, et on lui passe toutes les variables d’environnements qu’on avait définit dans notre fichier local .env via le fichier app.yaml.

runtime: php72

runtime_config:
    document_root: public
    enable_stackdriver_integration: true

# Skip ".env", which is for local development (skip files is in .gcloudignore file)

env_variables:
    # Put production environment variables here.
    APP_URL: "https://VOTRE_PROJET_ID.appspot.com"
    APP_LOG: errorlog
    APP_KEY: VOTRE_APP_KEY
    APP_DEBUG: true
    APP_STORAGE: /tmp
    VIEW_COMPILED_PATH: /tmp
    SESSION_DRIVER: cookie
    CACHE_DRIVER: file
    LOG_CHANNEL: stackdriver
    
    ## Set these environment variables according to your CloudSQL configuration.
    FILESYSTEM_CLOUD: "gcs"
    FILESYSTEM_DRIVER: "gcs"
    GOOGLE_CLOUD_KEY_FILE: "/srv/config/key.json"
    GOOGLE_CLOUD_PROJECT_ID: VOTRE_PROJET_ID
    GOOGLE_CLOUD_STORAGE_BUCKET: "VOTRE_PROJET_ID.appspot.com"
    GOOGLE_CLOUD_BIGQUERY_DEFAULT_DATASET: shopping    
    
    ##Database
    DB_CONNECTION: mysql
    DB_HOST: XX.XX.XX.XX
    DB_DATABASE: shopping
    DB_USERNAME: root
    DB_PASSWORD: XXXXXXX
    DB_SOCKET: "/cloudsql/VOTRE_PROJET_ID:europe-west1:mysql-shopping"
  
beta_settings:
    # for Cloud SQL, set this value to the Cloud SQL connection name,
    # e.g. "project:region:cloudsql-instance"
    cloud_sql_instances: "VOTRE_PROJET_ID:europe-west1:mysql-shopping"
  
handlers:
- url: /favicon\.ico
  upload: public/favicon\.ico$
  static_files: public/favicon.ico

- url: /css
  static_dir: public/css

- url: /fonts
  static_dir: public/fonts

- url: /webfonts
  static_dir: public/webfonts
  
- url: /images
  static_dir: public/images

- url: /js
  static_dir: public/js

- url: /vendor
  static_dir: public/vendor

- url: /.*
  secure: always
  script: auto

Le fichier .gcloudignore à la racine aussi permet d’indiquer quels fichiers il ne faut pas prendre en compte (ca permet de ne pas dépasser la limite des 10000):

.gcloudignore
.git
.gitignore
.env
node_modules/

# PHP Composer dependencies:
/vendor/

Pour finir, il suffit de déployer l’application avec la commande gcloud app deploy.

Les tâches planifiées:

Attention à cette librairie qui peut fonctionner sur des projets en Laravel 5.1 ou inférieur, mais qui est aujourd’hui obsolète. L’idée n’était pas mauvaise (réadapter l’environnement App Engine pour Laravel, mais le code ne fonctionne plus vraiment).
https://github.com/shpasser/GaeSupportL5

A gauche, la liste des tâches en cours d’éxecution. A droite, la liste des tâches planifiées.

Les tâches planifiées doivent être indiqués dans un fichier cron.yaml. Voici la structure du mien. Il faut le déployer de cette manière: gcloud app deploy cron.yaml. A noter que celles ci ont un time out de 10 minutes maximum. Dans certains cas, il faut alors découper les tâches en micro tâches, et donc recréer de nouvelles tâches planifiées…

cron:
- description: "import des produits"
  url: /cron
  schedule: every 24 hours from 00:00 to 02:00
- description: "melange des produits"
  url: /shuffle
  schedule: every 1 hours 

Si vous utilisez les jobs, vous devrez également avoir ce fichier à la racine: additional-supervisord.conf avec ce contenu:

[program:queue-worker]
process_name=%(program_name)s_%(process_num)02d
command = php %(ENV_APP_DIR)s/artisan queue:work --sleep=3 --tries=3
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile = /dev/stderr
stderr_logfile_maxbytes=0
user = www-data
autostart = true
autorestart = true
priority = 5
stopwaitsecs = 20

Enfin, le système de base de Laravel pour les tâches planifiées ne fonctionne pas. Pour qu’il remarche, vous devez donc commencer par activer l’API Google Cloud Task, puis passer vos jobs crons sous forme de commandes. Ensuite, il vous faut un objet tel que celui ci pour appeler les commandes – ex: $job = new GcloudTask(« refresh:importrename »,[]);

<?php

namespace App;

class GcloudTask 
{
	public function __construct($command, $args=[]){
		$payload =["command"=>$command, "args"=>$args];
		$client = new \Google_Client();
		$client->setAuthConfigFile(env("GOOGLE_CLOUD_KEY_FILE"));
		$client->addScope("https://www.googleapis.com/auth/cloud-platform");
		$taskClient = new \Google_Service_CloudTasks($client);
		$appEngineHttpRequest = new \Google_Service_CloudTasks_AppEngineHttpRequest();
		$appEngineHttpRequest->setHttpMethod("POST");
		$appEngineHttpRequest->setBody(base64_encode(json_encode($payload)));
		$appEngineHttpRequest->setRelativeUri("/gcloud_task");
		
		$task = new \Google_Service_CloudTasks_Task();
		$task->setAppEngineHttpRequest($appEngineHttpRequest);
		
		$createTaskRequest = new \Google_Service_CloudTasks_CreateTaskRequest();
		$createTaskRequest->setTask($task);
		
		$queueName = "projects/".env("GOOGLE_CLOUD_PROJECT_ID")."/locations/".env("GOOGLE_CLOUD_LOCATION")."/queues/default";

		$response = $taskClient->projects_locations_queues_tasks->create($queueName, $createTaskRequest);

	}
}

Enfin dans votre fichier routes/web.php, vous devrez avoir le retour de l’url :
Route::post(‘/gcloud_tasks’, ‘Controller@gcloud_tasks’);
qui permet d’appeler la commande en question (à vous de rajouter une clé si vous voulez quelque chose de sécurisé et de rajouter l’url dans les exceptions csrf).

	//Lancement d une commande depuis les tasks Google Cloud
	public function gcloud_tasks(Request $request){
		$req = json_decode($req->getContent(),true);
		if (isset($req["command"])){
			if ("" != $req["command"]){
				\Illuminate\Support\Facades\Artisan::call($req["command"], $req["args"]);
			}
		}
	}

Logger avec Stackdriver:

Pour commencer, modifiez la fonction report dans app\Exceptions\Handler.php afin de détecter dans quel environnement vous êtes.
Pour écrire dans les logs, faites syslog(LOG_INFO, « TEST »);
Source: https://www.sentinelstand.com/article/deploying-running-laravel-on-google-app-engine

if (isset($_SERVER['GAE_SERVICE'])) {
   if ($this->shouldReport($exception)) {
    Bootstrap::exceptionHandler($exception);
   }
} else {
    // Standard behavior
    parent::report($exception);
}

Conclusion:

Pour ma première application App Engine, j’ai environ doublé le temps que j’avais mis à écrire l’application complète en PHP (3 semaines). Les cours que j’avais pris via Coursera m’ont beaucoup aidé à y voir plus clair. La logique diffère vraiment pas mal, mais force est de constater qu’une fois que ca fonctionne, l’application gagne en robustesse. Ensuite, libre à vous de déployer des versions, et de migrer seulement 10% de votre trafic, de revenir en arrière… La difficulté de codage se transforme alors en souplesse de déploiement, et c’est vraiment agréable de ne plus revenir sur son projet pour avoir à faire de la maintenance 🙂

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.