Jul 17, 2020 by Thibault Debatty | 7187 views
https://cylab.be/blog/85/dockerize-your-laravel-app-part-2-gitlab-and-multi-stage-build
When dockerizing an application, the main goal is to keep images small. Hence the build process should be split in 2 steps:
To keep your image small, the first step should not be part of the Dockerfile, so you can avoid that the final image contains unnecessary development and compilation tools. In our previous blog post we described how to build the Docker image. For this blog post, we will focus on the first step. Generally speaking, there are two ways to build the app:
If you wish to use GitLab to automatically build your docker images, you also have two possibilities to build the app: using GitLab jobs or using Docker multi-stage build. In this blog post we will briefly describe the "gitlab jobs" approach to explain its advantages and drawback, then we will focus on the multi-stage build method.
To build you app with GitLab jobs, the idea is to:
vendor
, public/js
and public/css
) so they can be used by other jobsThe main advantage of this method is that you can use GitLab cache for the downloaded composer and npm libraries. So once it is working correctly, it's efficient and fast.
The drawback is that you will end up with quite a lot of commands in your gitlab-ci.yml
, which can be quite complicated to test and debug locally.
The idea of a docker multi-stage build is that you can define multiple steps in your Dockerfile. Here is a typical example for a Laravel application:
#### Step 1 : composer
FROM cylab/php74 AS composer
# copy source files to /var/www/html
COPY . /var/www/html
WORKDIR /var/www/html
RUN composer install --no-dev --optimize-autoloader
#### Step 2 : node
FROM node AS node
COPY . /var/www/html
WORKDIR /var/www/html
RUN npm --version && npm install && npm run prod
#### Step 3 : the actual docker image
FROM php:7.4-apache
### PHP
# we may need some other php modules, but we can first check the enabled
# modules with
# docker run -it --rm php:7.4-apache php -m
# RUN docker-php-ext-install mbstring
# if we want to use MySQL database to run the production app
# and opcache for performance
RUN docker-php-ext-install mysqli pdo pdo_mysql opcache
# if we want to use Redis as cache or sessions server
RUN pecl install -o -f redis
&& rm -rf /tmp/pear
&& docker-php-ext-enable redis
# from Docker PHP documentation
# https://hub.docker.com/_/php
# use production php.ini
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
### Apache
# change the document root to /var/www/html/public
RUN sed -i -e "s/html/html/public/g"
/etc/apache2/sites-enabled/000-default.conf
# enable apache mod_rewrite
RUN a2enmod rewrite
### Laravel application
# copy source files
COPY . /var/www/html
COPY --from=composer /var/www/html/vendor /var/www/html/vendor
COPY --from=node /var/www/html/public/css /var/www/html/public/css
COPY --from=node /var/www/html/public/js /var/www/html/public/js
COPY --from=node /var/www/html/public/fonts /var/www/html/public/fonts
# copy env file for our Docker image
COPY env.docker /var/www/html/.env
# create sqlite db structure (in case we will use sqlite)
RUN mkdir -p storage/app
&& touch storage/app/db.sqlite
&& php artisan migrate
# clear config cache
RUN php artisan cache:clear
# these directories need to be writable by Apache
RUN chown -R www-data:www-data /var/www/html/storage
/var/www/html/bootstrap/cache
### Docker image metadata
VOLUME ["/var/www/html/storage", "/var/www/html/bootstrap/cache"]
Each step can use its own base docker image (with the FROM
keyword). In the final step (to build our real image), we can copy any file from the previous steps.
We can now build the image as usual:
docker build -t <myapp> ./
and test the resulting image:
docker run -rm -p 8080:80 <myapp>
As usual, we can use .dockerignore
to exclude some file from the image:
.git
node_modules
vendor
bootstrap/cache/*
storage/app
storage/logs
storage/framework/cache/data
storage/framework/sessions/*
storage/framework/testing
## if you are using debugbar
storage/debugbar
Once your Dockerfile is working, you can add a job to your .gitlab-ci.yml to automatically build your docker images
This blog post is licensed under CC BY-SA 4.0