Symfony on Google Cloud Run

By | November 11, 2019

Nowadays, two of the biggest trends in application development have been the rise of serverless and containerization. This year, Google announced a new product called Cloud Run that is designed to bring the two together. At the same time, Google also announced Cloud Run for GKE which is specifically designed to run on Google’s version of Kubernetes. Likewise, I develop some of my applications and I refer them to survive on Cloud Run. I assume you have a Symfony 4 application as well. Let’s start.

First of all, you need to read the container runtime contract page. There are some standards to follow the ways for Cloud Run.

https://cloud.google.com/run/docs/reference/container-contract

The crucial thing is that your application is supposed to listen 8080 port for now. It’s the beta and in the future, this situation might be changed. Therefore, I’ve created an Nginx conf to listen to 8080.

server {
    listen       8080;
    server_name  localhost;
    root  /usr/share/nginx/html/public;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/run/php-fpm7.2/php-fpm.sock;
        #fastcgi_pass 127.0.0.1:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        internal;
    }

    location ~ \.php$ {
        return 404;
    }

    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

Also, I’ve created a Docker file to handle container issues.

FROM phpearth/php:7.2-nginx

COPY . /usr/share/nginx/html

COPY default.conf /etc/nginx/conf.d/default.conf

COPY php-fpm.conf /etc/php/7.2/php-fpm.d/www.conf

COPY php.ini /etc/php.ini

RUN apk add --no-cache composer

WORKDIR /usr/share/nginx/html

RUN mkdir -p /run/nginx && chown -R nginx:nginx /run/nginx && chown -R nginx:nginx /usr/share/nginx/html \
           && composer install -vvv \
           && mkdir -p /usr/share/nginx/html/var && chmod -R 777 /usr/share/nginx/html/var \
           && chown -R nginx:nginx /usr/share/nginx/html/var

I’ve created my own php.ini file.

[PHP]
engine = On
short_open_tag = On
precision = 14
output_buffering = Off
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = 100
disable_functions = dl
disable_classes =
zend.enable_gc = On
expose_php = Off
max_execution_time = 30
max_input_time = 60
memory_limit = 512M
error_reporting = E_ALL & ~E_NOTICE
display_errors = On
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
html_errors = On
error_log = error_l
variables_order = "EGPCS"
request_order = "GP"
register_argc_argv = On
auto_globals_jit = On
post_max_size = 64M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
default_charset = "UTF-8"
include_path = ".:/opt/php72/lib/php"
doc_root =
user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 64M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = On
default_socket_timeout
cli_server.color
date.timezone = Asia/Riyadh
pdo_mysql.cache_size = 2000
pdo_mysql.default_soc
sendmail_path = /usr/sbin/sendmail -t
mail.add_x_header
sql.safe_mode =
odbc.allow_persistent = Off
odbc.check_persistent = Off
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode
ibase.allow_persistent = 1
ibase.max_persistent = -1
ibase.max_links = -1
ibase.timestampformat = "%Y-%m-%d %H:%M:%S"
ibase.dateformat = "%Y-%m-%d"
ibase.timeformat = "%H:%M"
mysqli.max_persistent = -1
mysqli.allow_persistent = Off
mysqli.max_links = -1
mysqli.cache_size = 2000
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect =
mysqlnd.collect_statistics = Off
mysqlnd.collect_memory_statistics =
pgsql.allow_persistent = Off
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice
bcmath.scale
session.save_handler = files
session.save_path = "/tmp"
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1440
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.hash_function = 0
session.hash_bits_per_character = 5
url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=,fields"
zend.assertions
tidy.clean_output =
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit
ldap.max_links

And, we need to create a php-fpm.conf to figure out the PHP workers.

[www]

user = www-data
group = www-data

listen = /run/php-fpm7.2/php-fpm.sock

listen.owner = nginx
listen.group = nginx
listen.mode = 0660

pm = static
pm.start_servers = 1
pm.max_children = 5
pm.min_spare_servers = 1
pm.max_spare_servers = 5

You are going to have this structure in the end.

At this point, we need to create a Docker image into Google Cloud in the Container Registry service. Therefore, we run the following command.

gcloud builds submit --tag gcr.io/[Google-Cloud-Project]/[Image-Name]:[Version]

After creating the image, we will able to use that image like this.

gcloud beta run deploy [Service-Name] --image gcr.io/[Google-Cloud-Project]/[Image-Name]:[Version] --region europe-west1 --platform managed

By running this command, we push our earlier image to create a container on Cloud Run. When you look over the Google Cloud dashboard, you are going to reach all of the details about your service.

As you can recall, we have learned to automate this process the following post.

https://dev.to/_mertsimsek/continuously-build-test-and-deploy-with-symfony-on-google-cloud-5a6i

You also change like this the cloudbuild.yaml file to get autonomous for Cloud Run.

steps:
  # build the container image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'gcr.io/$PROJECT_ID/[Service-Name]', '.']
    # push the container image to Container Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'gcr.io/$PROJECT_ID/[Service-Name]']
    # Deploy container image to Cloud Run
  - name: 'gcr.io/cloud-builders/gcloud'
    args: ['beta', 'run', 'deploy', '[Service-Name]', '--image', 'gcr.io/$PROJECT_ID/[Service-Name]', '--region', 'europe-west1', '--platform', 'managed']
images:
  - gcr.io/$PROJECT_ID/[Service-Name]

Conclusion

Google wants us to focus only on our codes. Each Cloud Run service has a unique endpoint and auto-scales deployed containers. Therefore, we don’t need to suffer about the rest of the code. I hope, I succeed to change your ideas about to survive on Google Cloud platform.