Create small Laravel docker image - php

I have a project with separate frontend and backend (ReactJS + Laravel) and now I need the docker image of them also separate, the problem is that I can't create a docker image for Laravel smaller than 100mb.
My React directory is 700mb (with node_modules), but for production I do the "build" and just use the build on a Nginx docker image, generating a .tar of 50mb (the docker .tar is bigger than the normal, but 50mb is fine).
My Laravel directory has 240mb (with Vendor) and all the ways I used to create an image for production generate a .tar (final image) between 600mb and 1gb. How to make a "build" of laravel and use only it in Nginx like I did with Reactjs?
Important: If it's possible to use only one file (Dockerfile) as I do it will be PERFECT, I don't want to involve external configuration files or docker_composer, but if it's not possible I accept these alternatives.
My Nginx image using only reactjs build (52mb):
FROM node:16-alpine as build
ENV PATH /app/node_modules/.bin:$PATH
WORKDIR /app
COPY . /app
RUN yarn
RUN yarn add react-scripts#5.0 -g
RUN yarn build
# -----------------------------------------------------------------------------
FROM fitiavana07/nginx-react
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD nginx -g 'daemon off;'
My Laravel image (1gb):
FROM webdevops/php-nginx:8.0
# Get latest Composer
# COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN curl -sS https://getcomposer.org/installer | \
php -- --install-dir=/usr/local/bin --filename=composer
RUN apt-get update
RUN apt-get -y install git libicu-dev libonig-dev libzip-dev \
unzip locales libpng-dev libonig-dev libxml2-dev
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*
RUN locale-gen en_US.UTF-8
RUN localedef -f UTF-8 -i en_US en_US.UTF-8
RUN mkdir /var/run/php-fpm
RUN docker-php-ext-install intl pdo_mysql zip bcmath mbstring \
exif pcntl bcmath gd
ENV WEB_DOCUMENT_ROOT /app/public
ENV APP_ENV production
WORKDIR /app
COPY . .
RUN composer install --no-interaction --optimize-autoloader --no-dev
# Optimizing Configuration loading
RUN php artisan config:cache
# Optimizing Route loading
RUN php artisan route:cache
# Optimizing View loading
# RUN php artisan view:cache
RUN chown -R application:application .

Related

Container cannot see the prebuilt vendor folder in it

My project is throwing the next error after restarting the docker container:
Warning: require(/var/www/ /var/www/
/vendor/composer/./symfony/polyfill-php80/bootstrap.php): Failed to
open stream: No such file or directory in
'vendor/composer/autoload_real.php line 71
My Dockerfile:
FROM php:8.0-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/project/
# Set working directory
WORKDIR /var/www/project
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl \
libicu-dev \
libonig-dev \
libzip-dev
# install node
RUN curl -sL https://deb.nodesource.com/setup_current.x | bash -
RUN apt-get install -y nodejs
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-install pdo_mysql zip exif pcntl
RUN docker-php-ext-configure gd --enable-gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/
RUN docker-php-ext-install gd
RUN docker-php-ext-configure intl
RUN docker-php-ext-install intl
RUN pecl install xdebug && docker-php-ext-enable xdebug
RUN echo 'xdebug.client_port=9000' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.mode=debug' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.discover_client_host=true' >> /usr/local/etc/php/php.ini
RUN echo 'memory_limit = 4G' >> /usr/local/etc/php/conf.d/docker-php-memlimit.ini;
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
# Copy existing application directory permissions
COPY --chown=www:www . /var/www/project
RUN composer install
RUN npm install
RUN npm run dev
RUN php artisan storage:link
ENV PATH="vendor/bin:${PATH}"
# Change current user to www
USER www
RUN composer global require tightenco/tlint
ENV PATH="${PATH}:/home/www/.composer/vendor/bin"
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
My docker-compose:
...
project:
build:
context: ./project/.
dockerfile: Dockerfile
image: project:v0
restart: unless-stopped
tty: true
environment:
PHP_IDE_CONFIG: serverName=docker_project
XDEBUG_CONFIG: remote_host=172.17.0.1
SERVICE_NAME: project
SERVICE_TAGS: dev
volumes:
- /var/www/project/vendor/
- ./project:/var/www/project
- ./project/.docker/php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- project-network
...
Inside container run ls vendor/symfony before restarting the container:
And after:
The error is solving if remove the vendor directory and run composer install.
I'm not a Jedi of the Docker.
Thanks for any help/suggestion!
In your volume section, you are mounting the host machine folder ./project to your container's /var/www/project:
volumes:
- /var/www/project/vendor/
- ./project:/var/www/project
- ./project/.docker/php/local.ini:/usr/local/etc/php/conf.d/local.ini
The vendor folder is built inside of you container by the RUN command. It's probably still there. But when you run docker-compose up, you instructed it to mask whatever inside with your host's ./project folder when running it. So the container don't see anything that you built before hand.
My advice is to only mount the folder that you need to change from time to time. For example:
volumes:
- /var/www/project/vendor/
# You're probably uploading files here
- ./project/public/assets:/var/www/project/public/assets
# Note: You might need to mount some other files / folders that changes
- ./project/.docker/php/local.ini:/usr/local/etc/php/conf.d/local.ini
Then the container will properly use the prebuilt /vendor while storing the thing that you need to keep in host.
For who are facing the same issue, just map the project folder except vendor folder:
services:
app:
volumes:
.:/var/www/html
/var/www/html/vendor
...
If you want to have all vendor files in the host machine, just copy them from the container to your host machine, so IDE (Like PHPSTORM) can identify composer packages:
$ cd /your/project/host/path
$ docker cp $(docker-compose ps -q app):/var/www/html/vendor .

Powershell module import not persisting in docker container image

I installed powershell in the php debian docker image and now I am installing PowerCLI module in the docker image to access the vsphere info and display using laravel. The issue is with the PowerCLI installation. Powershell doesn't seem to persist the modules installed and imported. When I import module and use RUN pwsh -c connect-viserver, it seems working in the docker-image. But when I call the cmdlet in laravel container as $process = new Process(['pwsh', '-c','Connect-VIServer', 'SERVERNAME']); it fails. I check to see if it is imported iin the powershell by accessing container docker exec -it app bash But the module is not installed. I am manually keeping the modules in a folder powershell/Modules and adding it to $env:$PSModulePath
I do not understand what I'm missing.
Here's is my docker file.
FROM php:7.4-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
RUN pwd
# Set working directory
WORKDIR /var/www
RUN pwd
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
libonig-dev \
locales \
libzip-dev \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl \
wget \
apt-utils
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl mysqli
RUN docker-php-ext-configure gd --enable-gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/
RUN docker-php-ext-install gd
RUN docker-php-ext-enable mysqli
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
#########################
RUN pwd
# Download the Microsoft repository GPG keys
RUN wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb
# Register the Microsoft repository GPG keys
RUN dpkg -i packages-microsoft-prod.deb
# Update the list of products
RUN apt-get update
# Install PowerShell
RUN apt-get install -y powershell
# Start PowerShell
#RUN pwsh
# Allow installation from PSGallery
SHELL ["pwsh", "-command"]
RUN Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
#RUN Install-Module VMware.VimAutomation.Core -Confirm:$false
#RUN Import-Module VMware.VimAutomation.Core; Get-Module
#RUN Set-PowerCLIConfiguration -ParticipateInCEIP $false -Confirm:$false
#RUN Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false
##RUN connect-viserver 10.21.24.9
RUN Get-Module -ListAvailable VMware.VimAutomation.Core | Import-Module
RUN $env:PSModulePath = $env:PSModulePath + ":/var/www/powershell/Modules"
RUN mkdir -p /root/.config/powershell
RUN touch /root/.config/powershell/Microsoft.PowerShell_profile.ps1
RUN echo "Get-Module -ListAvailable PowerCLI* | Import-Module" >> /root/.config/powershell/Microsoft.PowerShell_profile.ps1
RUN $PSHome
RUN Get-Module
SHELL ["/bin/sh", "-c"]
# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
# Copy existing application directory contents
COPY . /var/www
# Copy existing application directory permissions
COPY --chown=www:www . /var/www
# Change current user to www
USER www
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
RUN pwd
RUN ls -la && ls -la /var/www/powershell/Modules/

How to run cron and web application in same container?

I am new to docker. Hardly I have containerized my php application to run it in the web interface. But I have some cron to run with it. I learnt how to create separate cron image and run it from How to run a cron job inside a docker container?. But my use case is different. I need to use the php files from my php application container which seems not possible from my way. I tried creating the docker-compose.yml as follow to see if it would work
docker-compose.yml:
version: "3"
services:
app:
build:
context: ./docker/php
container_name: 'app'
restart: 'always'
ports:
- "80:80"
- "443:443"
links:
- db
volumes:
- ${DOCUMENT_ROOT-./src}:/var/www/html
- ${PHP_INI-./docker/php/php.ini}:/usr/local/etc/php/php.ini
- ${VHOSTS_DIR-./docker/apache2/vhosts}:/etc/apache2/sites-enabled
- ${LOG_DIR-./docker/logs/apache2}:/var/log/apache2
extra_hosts:
- "test.local:127.0.0.1"
hostname: cloudservice.local
domainname: local
#entrypoint: sh /var/www/html/cron.sh
As I have commented entry point here and if I do docker-compose up, everything works perfectly fine, My Dockerfile is as under
Dockerfile:
FROM php:7.2.27-apache
RUN apt-get update
RUN apt-get install -y cron
RUN apt-get -y update --fix-missing
RUN apt-get upgrade -y
# Install useful tools
RUN apt-get -y install apt-utils nano wget dialog
# Install important libraries
RUN apt-get -y install --fix-missing apt-utils build-essential git curl libcurl4 libcurl4-openssl-dev zip
# Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Install xdebug
#RUN pecl install xdebug-2.5.0
#RUN docker-php-ext-enable xdebug
# Other PHP7 Extensions
RUN apt-get -y install libsqlite3-dev libsqlite3-0 default-mysql-client
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install pdo_sqlite
RUN docker-php-ext-install mysqli
RUN docker-php-ext-install curl
RUN docker-php-ext-install tokenizer
RUN docker-php-ext-install json
RUN apt-get -y install zlib1g-dev
RUN docker-php-ext-install zip
RUN apt-get -y install libicu-dev
RUN docker-php-ext-install -j$(nproc) intl
RUN docker-php-ext-install mbstring
RUN apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev
RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/
RUN docker-php-ext-install -j$(nproc) gd
RUN pecl install redis-5.1.1 \
&& docker-php-ext-enable redis
# Enable apache modules
RUN a2enmod rewrite headers
My cron.sh file is as under
#!/usr/bin/env bash
# Ensure the log file exists
touch /var/www/html/logs/crontab.log
# Ensure permission on the command
chmod a+x /var/www/html/cron-local/hn-shc-rapid-daily.sh
# Added a cronjob in a new crontab
echo "* * * * * bash /var/www/html/cron-local/hn-shc-rapid-daily.sh >> /var/www/html/logs/crontab.log 2>&1" > /etc/crontab
# Registering the new crontab
crontab /etc/crontab
# Starting the cron
/usr/sbin/service cron start
# Displaying logs
# Useful when executing docker-compose logs mycron
tail -f /var/www/html/logs/crontab.log
But with the entry point commented, i cannot run cron. If i don't comment entrypoint, then cron runs, my web application doesn't. Is there any possibility to fix this?
Thanks
At the end of the docker file i added the following code after removing
RUN a2enmod rewrite headers
# Copy hello-cron file to the cron.d directory
COPY hello-cron /etc/cron.d/hello-cron #just any name
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron
# Apply cron job
RUN crontab /etc/cron.d/hello-cron
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
# Enable apache modules
RUN a2enmod rewrite headers
CMD cron && /usr/sbin/apache2ctl -D FOREGROUND
and cron.sh was changed to
* * * * * echo "hello world" >> /var/www/html/logs/crontab.log 2>&1
This worked for me. I need not have to add entry neither on docker-compose nor on docker file, but I guess entry also would work.
I don't know if this is the right way to do. If anyone would give me more advance idea, I would be happy to give it a try.
Thanks
I think it's better if you specify the entry point in the docker-compose file without "sh" in front of it. Remember that declaring a new entrypoint in the docker-compose file overwrites the entrypoint in the dockerfile. Link
I would advise you to create your own Entrypoint Script which will execute your crons in the container CMD ["/entrypoint.sh"]
Example:
Create an file and named "entrypoint.sh" or whatever and save it in the same folder where your Dockerfile is located. In this file push your Content from your cron.sh.
RUN apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev
RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/
RUN docker-php-ext-install -j$(nproc) gd
RUN pecl install redis-5.1.1 \
&& docker-php-ext-enable redis
# Enable apache modules
RUN a2enmod rewrite headers
COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
But you could also just add your cron directly in the docker file.
...
# Enable apache modules
RUN a2enmod rewrite headers
COPY your-cron.cron /
RUN crontab /your-cron.cron
...

Docker with laravel fails because of php extension

Running Laravel on an appache server.
Upon building the image with docker-compose up --build with the following Dockerfile
FROM php:7.3-apache-stretch
RUN apt-get update -y && apt-get install -y libpng-dev
RUN docker-php-ext-install pdo pdo_mysql gd
FROM composer:1.9.0 as build
WORKDIR /app
COPY . /app
RUN composer global require hirak/prestissimo && composer install
I am getting the error message:
phpoffice/phpspreadsheet 1.13.0 requires ext-gd * -> the requested PHP extension gd is missing from your system.
This happens when the composer install command runs.
As you can see up, I am actually installing gd from php, so it should not give me this error message.
Do you have any idea how I can solve it?
Thanks!
It's happen, because you are using multistage building and your composer second stage have nothing to do with previous build using PHP container. Primary use case with multistaging is to produce some useful artefacts which can be used later.
So what I suggest is to copy composer file from composer image, then place it somewhere in your php container.
I will give you my solution which is working perfectly for me with laravel/symfony etc.
FROM php:7.4.4-fpm
# We copy composer from it's original image to our php container to use it later.
COPY --from=composer:1.9 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
ARG USER_ID
RUN useradd -s /bin/bash -d /home/user/ -m -G sudo,www-data user -u $USER_ID
RUN apt update && apt install -y zip unzip wget zlib1g-dev libicu-dev
RUN docker-php-ext-install pdo_mysql intl opcache gd
USER user
RUN wget https://get.symfony.com/cli/installer -O - | bash
ENV PATH="/home/user/.symfony/bin:${PATH}"
COPY php.ini /usr/local/etc/php
# You can also run here composer install, depends on your use case
You can change your docker image. For example try this:
FROM richarvey/nginx-php-fpm
WORKDIR /app
RUN php ./artisan config:cache && composer install

Setup a Docker container with only the docker-compose up command

I have an assessment in which I have to create a Docker container with CakePHP. I already have a working Docker container with CakePHP and I run the following commands for my container:
docker-compose build
docker-compose run cakephp composer install --no-interaction
docker-compose run cakephp bin/cake migrations migrate
docker-compose run cakephp bin/cake migrations seed
docker-compose up
The goal is to reduce the process down to only running the single command docker-compose up to be able to start testing the container. I'm very new to both Docker and CakePHP so I'm not sure how to do this.
Any help is greatly appreciated!
Dockerfile
#start with our base image (the foundation) - version 7.1.29
FROM php:7.1.29-apache
#install all the system dependencies and enable PHP modules
RUN apt-get update && apt-get install -y \
gcc \
make \
autoconf \
libc-dev \
pkg-config \
libicu-dev \
libpq-dev \
libmcrypt-dev \
mysql-client \
git \
zip \
unzip \
&& rm -r /var/lib/apt/lists/* \
&& docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd \
&& docker-php-ext-install \
intl \
mbstring \
mcrypt \
pcntl \
pdo_mysql \
pdo_pgsql \
pgsql \
opcache
RUN set -eux; apt-get update; apt-get install -y libzip-dev zlib1g-dev; docker-php-ext-install zip
#install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer
ENV COMPOSER_ALLOW_SUPERUSER=1
#set our application folder as an environment variable
ENV APP_HOME /var/www/html
#change uid and gid of apache to docker user uid/gid
RUN usermod -u 1000 www-data && groupmod -g 1000 www-data
#change the web_root to laravel /var/www/html/public folder
RUN sed -i -e "s/html/html\/webroot/g" /etc/apache2/sites-enabled/000-default.conf
# enable apache module rewrite
RUN a2enmod rewrite && \
echo "ServerName localhost" >> /etc/apache2/apache2.conf
#copy source files and run composer
COPY . $APP_HOME
# install all PHP dependencies
RUN composer install --no-interaction
#change ownership of our applications
RUN chown -R www-data:www-data $APP_HOME
docker-compose.yml
version: '2'
services:
cakephp:
build: .
depends_on:
- mysql
links:
- "mysql"
ports:
- "4000:80"
volumes:
- .:/var/www/html/
environment:
- SECURITY_SALT= *some salt here*
- MYSQL_URL=mysql
- MYSQL_USERNAME=root
- MYSQL_PASSWORD=root
mysql:
image: mysql:5.6
volumes:
- mysql-data:/var/lib/mysql
environment:
- MYSQL_DATABASE=cakephp
- MYSQL_ROOT_PASSWORD=root
volumes:
mysql-data:
Option 1
What I usually do to reduce my commands when handling Docker and Docker Compose in general is using a Makefile.
So, in your case, you could write something like:
Makefile
SUDO := $(shell groups | grep -q docker || echo sudo)
.PHONY: start
start:
$(SUDO) docker-compose build \
&& $(SUDO) docker-compose run cakephp composer install --no-interaction \
&& $(SUDO) docker-compose run cakephp bin/cake migrations migrate \
&& $(SUDO) docker-compose run cakephp bin/cake migrations seed \
&& $(SUDO) docker-compose up
All you would have to do then is putting this file in your project folder and run make start.
(The $(SUDO) part ensures that you can run this comfortably even with a user that is not in the docker group.)
Option 2
To really just run docker-compose up (perhaps with --build flag), you would have to write a little script that you COPY into the Docker image (you're already doing that with COPY . $APP_HOME – provided that you put this script at where your Docker build context points to) and then use it as ENTRYPOINT.
Something like this should work for you.
entrypoint.sh:
#!/bin/sh
set -e
cakephp composer install --no-interaction
cakephp bin/cake migrations migrate
cakephp bin/cake migrations seed
exec "$#"
In your Dockerfile, you would then have to put ENTRYPOINT ["/bin/sh", "entrypoint.sh"]

Categories