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
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 🙂