Nuxtstop

For all things nuxt.js

Beginner’s CI/CD Guide: Deploy Laravel8 to Google Cloud Run and Cloud SQL via GitHub Actions

Beginner’s CI/CD Guide: Deploy Laravel8 to Google Cloud Run and Cloud SQL via GitHub Actions
7 0

Application deployment is one of the important dimensions, including all of the steps, processes of installing, configuring, and enabling a specific application or set of applications. However, I didn’t figure out well what is continuous integration.

For learning it, I started to deploy a Laravel8 application to Google Cloud Run, develop and deploy highly scalable containerized applications on a fully managed serverless platform. But pieces of information about it scattered out on WEB, and I was desperate to understand it, wasting time.

So I will share with you as easily as possible the way of deployment to Google Cloud Run and Google Cloud SQL via GitHub Actions. Let’s get started!

Directory architecture

The next figure shows us the directory architecture of the git repository.

.
├── .github/
│   └── workflows
│       └── deploy.yml # yml file to run scripts in Github Actions
├── env/
├── docker/
│   ├── 000-default.conf
│   ├── Dockerfile
│   └── php.ini
├── sh/ # useful shell files
├── src/ # the directory installed Laravel application
└── docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

1. Build and run the Laravel application on Docker compose

1-1. Clone a repository from GitHub

In this time, we will proceed as the next repository. Under the directory you like, let’s clone it initially and move there.

$ git clone git@github.com:uuta/cloud-run-laravel8.git
$ cd cloud-run-laravel8
Enter fullscreen mode Exit fullscreen mode

Build and run the Laravel application on Docker compose to make sure of working out well.

$ cp env/.env.example env/.env.local
$ docker-compose up -d --build
...
...
...
Creating cloud-run-laravel8_db_1  ... done
Creating cloud-run-laravel8_app_1 ... done
Time: 0h:00m:24s
Enter fullscreen mode Exit fullscreen mode

The APP_KEY in src/.env would be empty, so create it in a docker container.

$ docker-compose exec app bash
$ php artisan route:clear && \
php artisan cache:clear && \
php artisan config:clear && \
php artisan view:clear && \
php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

A Laravel app key would be generated.

# before
APP_KEY=
# after
APP_KEY=base64:<key>
Enter fullscreen mode Exit fullscreen mode

Alright, we'll see the default page of Laravel to access http://localhost:8080/ after the installation.

Image description

How about migration? If you execute the migration command after entering the app container, maybe you can see a message such as the next.

$ docker-compose exec app bash
$ php artisan migrate
Nothing to migrate.
Enter fullscreen mode Exit fullscreen mode

The reason why migration has already finished is to be executed while building a docker image based on Dockerfile. Let’s take a look at Dockerfile.local for a while.

FROM composer:latest as build
WORKDIR /app
COPY . /app

FROM php:8.0-apache
COPY docker/php.ini /usr/local/etc/php/
RUN apt update
RUN apt install -y git
RUN apt install -y vim
RUN apt install -y zip unzip
RUN docker-php-ext-install bcmath pdo_mysql

# composer install
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
EXPOSE 8080
COPY --from=build /app /var/www
COPY docker/000-default.conf /etc/apache2/sites-available/000-default.conf
RUN chmod 777 -R /var/www

WORKDIR /
RUN echo "Listen 8080" >> /etc/apache2/ports.conf
RUN chown -R www-data:www-data /var/www
RUN a2enmod rewrite

# Make the file executable, or use "chmod 777" instead of "chmod +x"
RUN chmod +x /var/www/sh/laravel/laravel.local.sh

# This will run the shell file at the time when container is up-and-running successfully (and NOT at the BUILD time)
ENTRYPOINT ["/var/www/sh/laravel/laravel.local.sh"]
Enter fullscreen mode Exit fullscreen mode

At the end of this file, there are codes to execute a shell command.

RUN chmod +x /var/www/sh/laravel/laravel.local.sh
...
ENTRYPOINT ["/var/www/sh/laravel/laravel.local.sh"]
Enter fullscreen mode Exit fullscreen mode

So let’s take a look at sh/laravel/laravel.local.sh file. composer install and migrate commands are executed here.

#!/bin/bash

# initialize laravel
cd /var/www/src
composer install --optimize-autoloader
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Run Laravel migration
php artisan migrate

# Run Apache in "foreground" mode (the default mode that runs in Docker)
apache2-foreground
Enter fullscreen mode Exit fullscreen mode

The reason why I dare to make this file is to migrate to Google Cloud SQL in the production environment. According to the next page, the Google Cloud SQL connection is not available during the build phase. Thus, I had to put these commands out for execution after building successfully. In the local environment, this application attempts to connect with the local database instead of Google Cloud SQL.

1-2. More detail of Dockerfile

Let’s see Dockerfile.local again. I can’t explain everything, but I’ll try to add descriptions briefly. To build an image on Google Cloud Run, shell scripts expose port of 8080.

EXPOSE 8080
...
RUN echo "Listen 8080" >> /etc/apache2/ports.conf
Enter fullscreen mode Exit fullscreen mode

This command copies 000-default.conf to configure the Apache WEB server.

COPY docker/000-default.conf /etc/apache2/sites-available/000-default.conf
Enter fullscreen mode Exit fullscreen mode

2. Deploy Laravel application to Google Cloud Run

Now let's deploy the Laravel application from the local side. First, let docker containers be down.

$ docker-compose down
Enter fullscreen mode Exit fullscreen mode

2-1. Set up GCP with console

Execute the following commands with checking project name in GCP.

$ gcloud auth login
$ PROJECT_NAME=(<Project name in GCP>)
PROJECT_ID=$(gcloud projects list --format 'value(projectId)' --filter name=$PROJECT_NAME)
export PROJECT_ID
gcloud config set project $PROJECT_ID
gcloud services enable \
    containerregistry.googleapis.com \
    cloudresourcemanager.googleapis.com \
    iam.googleapis.com
Enter fullscreen mode Exit fullscreen mode

Image description

2-2. Create an authorized service account

Create a service account and give it Owner privileges. Download the key, save it as laravel8_google_cloud_run_key.json, set it as an environment variable, and authorize it as a service account. The following command makes it possible.

$ gcloud iam service-accounts create deployer \
    --display-name "deployer"
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:deployer@${PROJECT_ID}.iam.gserviceaccount.com \
  --role roles/owner
export GOOGLE_APPLICATION_CREDENTIALS=credentials/laravel8_google_cloud_run_key.json
gcloud iam service-accounts keys create $GOOGLE_APPLICATION_CREDENTIALS \
  --iam-account deployer@${PROJECT_ID}.iam.gserviceaccount.com
gcloud auth activate-service-account --key-file=$GOOGLE_APPLICATION_CREDENTIALS
Enter fullscreen mode Exit fullscreen mode

In the above example command, laravel8_google_cloud_run_key.json is stored in the credentials directory.

export GOOGLE_APPLICATION_CREDENTIALS=~/credentials/laravel8_google_cloud_run_key.json
Enter fullscreen mode Exit fullscreen mode

2-3. Build docker image

Build docker image instead of docker-compose command.

$ export SERVICE_NAME=docker-laravel8-run
docker build -f docker/Dockerfile.prod -t asia.gcr.io/${PROJECT_ID}/${SERVICE_NAME} .
Enter fullscreen mode Exit fullscreen mode

Run a docker container to work out correctly on the local environment.

$ docker run --init -d -e PORT=8080 -p 8080:8080 asia.gcr.io/${PROJECT_ID}/${SERVICE_NAME}
Enter fullscreen mode Exit fullscreen mode

Make sure the local page works correctly.

Image description

2-4. Push the docker image to Container Registry

$ gcloud auth configure-docker
docker push asia.gcr.io/${PROJECT_ID}/${SERVICE_NAME}
Enter fullscreen mode Exit fullscreen mode

You’ll make sure to register a repository named docker-laravel8.

Image description

2-5. Enable Cloud SQL Admin API

To connect Google Cloud Run and Google Cloud SQL, try to enable Cloud SQL Admin API.

Image description

3. Google Cloud SQL

Create a Google Cloud SQL instance temporarily for test. I’ll create an instance like the following example in this sample, but you can modify configuration for your environment. This process might take a long time.

$ DATABASE_NAME=(docker-laravel8-run)
gcloud sql instances create $DATABASE_NAME \
  --database-version=MYSQL_8_0 \
  --region=us-central1 \
  --storage-size=100 \
  --storage-type=HDD \
  --tier=db-f1-micro \
  --root-password=homestead
Enter fullscreen mode Exit fullscreen mode

Image description

It would be better to create a database named laravel.

$ gcloud sql databases create laravel \
--instance=$DATABASE_NAME
Enter fullscreen mode Exit fullscreen mode

3-1. Deploy to Google Cloud Run

Let’s try to deploy to Google Cloud Run. Replace <Connection name> to the Connection name in Google Cloud SQL and execute the following command.

$ SQL_CONNECTION_NAME=(<Connection name>)
gcloud run deploy ${SERVICE_NAME} \
--image asia.gcr.io/"${PROJECT_ID}"/"${SERVICE_NAME}" \
--region asia-east1 \
--platform=managed \
--add-cloudsql-instances ${SQL_CONNECTION_NAME} \
--allow-unauthenticated
Enter fullscreen mode Exit fullscreen mode

Image description

A URL will be generated after deploying. Make sure to be shown the page for Laravel8 correctly.

Image description

Image description

4. Deploy from GitHub Actions

4-1. Brief description

Now we’ve deployed the Laravel application successfully from the Local environment. Then, let’s consider deploying from GitHub Actions. I’m assumed that GitHub Actions runs commands to deploy when commits are pushed to the master or main branch. In this example, the deploy command is defined in .github/workflows/deploy.yml file. And the trigger for deployment is set when pushing to master.

name: cloud-run-laravel8-deploy
on:
  push:
    branches:
      - master
Enter fullscreen mode Exit fullscreen mode

Let’s see some more. This remote repository has no .env file to configure the Laravel application by the .gitignore file. So we should put .env file only when deploying. Then, by encoding an environment variable decoded as base64 of the .env file on the local, it’s available to deploy.

- name: Decode .env file
  run: |
    echo ${{ secrets.TEST_ENV_FILE }} | base64 --decode > src/.env
Enter fullscreen mode Exit fullscreen mode

There are six environment variables in .github/workflows/deploy.yml file.

  • secrets.TEST_GCP_EMAIL: deploy email
  • secrets.TEST_GCP_CREDENTIALS: the content of credentials/laravel8_google_cloud_run_key.json in your local environment
  • secrets.TEST_ENV_FILE: env/.env.prod
  • secrets.TEST_GCP_PROJECT: project id
  • secrets.TEST_GCP_APPLICATION: service name
  • secrets.TEST_SQL_CONNECTION_NAME: SQL connection name

4-2. Change remote repository

Then, change the remote branch to test-cloud-run . The name can be anything you like.

$ git config remote.origin.url
git@github.com:uuta/cloud-run-laravel8.git
$ git remote set-url origin git@github.com:uuta/test-cloud-run.git
$ git config remote.origin.url
Enter fullscreen mode Exit fullscreen mode

4-3. Enter the environment variables

Enter the environment variables on GitHub like this.

Image description

TEST_GCP_EMAIL can be obtained from the Google Cloud Run UI.

Image description

Modifying TEST_ENV_FILE is somewhat tricky. First of all, copy the .env.example file as .env.prod.

$ cp env/.env.local env/.env.prod
Enter fullscreen mode Exit fullscreen mode

Modify the .env.prod file as follows, and don't forget to add DB_SOCKET.

APP_NAME=Laravel
APP_ENV=production
APP_KEY=<your app key>
APP_DEBUG=false
APP_URL=<your app url>

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=homestead
DB_SOCKET=/cloudsql/<connection name to Google Cloud SQL>
Enter fullscreen mode Exit fullscreen mode

By the following command, values in the env file will transfer to string encoded Base64. So you can copy and paste it as the value of TEST_ENV_FILE.

$ cat env/.env.prod | base64 | pbcopy
Enter fullscreen mode Exit fullscreen mode

4-4. Git push, and then deploy automatically

$ git push
Enter fullscreen mode Exit fullscreen mode

After pushing the master repository, it will automatically start deploying in your remote repository. Please check if it succeeds.

Image description

You made it!

References