How do I build dockerized laravel app in azure pipeline? - php

I am trying to build and use a dockerized laravel app in an azure pipeline, but I get the error
Verifying lock file contents can be installed on current platform.
Package operations: 163 installs, 0 updates, 0 removals
In Filesystem.php line 268:
/var/www/app/vendor does not exist and could not be created.
when trying to install composer dependencies after building the image. The setup is pretty standard, I've tried so many different things but to no avail.
Dockerfile
FROM php:8.1-apache
RUN apt update
RUN apt-get install -y --no-install-recommends \
g++ \
libicu-dev \
libpq-dev \
libzip-dev \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
git \
lsb-release \
gnupg \
zip
RUN docker-php-ext-install \
intl \
opcache \
pdo \
pdo_mysql \
zip \
exif \
gd
RUN pecl install \
pcov \
xdebug
WORKDIR /var/www/app
RUN usermod -a -G www-data www-data
RUN chown root:root /var/www
RUN chown -R www-data:www-data /var/www/app
RUN chmod -R 755 /var/www/app
RUN mkdir /var/www/app/vendor
RUN chmod -R 775 /var/www/app/vendor
RUN chown -R www-data:www-data /var/www/app/vendor
RUN a2enmod rewrite
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
USER www-data
docker-compose
version: '3.8'
services:
database:
image: mysql:8.0
container_name: database
environment:
- MYSQL_DATABASE=app
- MYSQL_ROOT_PASSWORD=root
volumes:
- ./infrastructure/mysql-data:/var/lib/mysql
ports:
- '3306:3306'
api:
container_name: api
build:
context: ./infrastructure/php
ports:
- '8080:80'
volumes:
- ./server:/var/www/app
- ./infrastructure/apache/default.conf:/etc/apache2/sites-enabled/000-default.conf
depends_on:
- database
mailhog:
image: mailhog/mailhog:latest
container_name: mailhog
ports:
- '1025:1025'
- '8025:8025'
azure-pipelines.yml
trigger:
- develop
pool:
vmImage: ubuntu-latest
steps:
- script: make up
displayName: 'Build'
- script: |
docker-compose exec -T api composer install
make db_reset
displayName: 'Install'
- script: |
make analyse
displayName: 'Analyse backend'
Makefile
DOCKER_COMPOSE = docker-compose
COMPOSER ?= composer
PHP_CMD = php
PHP_SERVICE = api
up:
#echo "\n==> Docker container building and starting ..."
$(DOCKER_COMPOSE) up --build -d
db_reset:
$(DOCKER_COMPOSE) exec -T api php artisan migrate:fresh
$(DOCKER_COMPOSE) exec -T api php artisan passport:install
$(DOCKER_COMPOSE) exec -T api php artisan db:seed
analyse:
$(DOCKER_COMPOSE) exec -T -u root api php artisan ide-helper:models -W
$(DOCKER_COMPOSE) exec -T -u root api ./vendor/bin/php-cs-fixer fix app
$(DOCKER_COMPOSE) exec -T -u root api ./vendor/bin/phpstan analyse --memory-limit=2G -c phpstan.neon
I really don't know what I am doing wrong. Any help is appreciated

Related

Database migration fails in entrypoint.sh during docker-compose up

I am new to Docker and trying to dockerize the project I am working on, which is a simple Symfony API with a MySql database.
I want docker to execute the DB migration php bin/console doctrine:migrations:migrate when it builds the container, so I added an entrypoint to do so:
entrypoint.sh
#create or update db
php bin/console doctrine:migrations:migrate --no-interaction
# start apache
apache2-foreground
Then when I run docker-compose up -d everything works fine but database is not migrated. When I enter the container I can manually execute the doc:mig:mig command but how do I get this done automatically? Am I not understanding the usage of entrypoint properly?
Here is my docker-compose.yaml file (substituted sensitive data)
version: '3.7'
services:
api:
build: .
ports:
- 8000:80
links:
- db
volumes:
- C:/Repos/myProjectName/src:/var/www/myProjectName_api/src
db:
image: 'mysql:8.0'
container_name: mysql
environment:
MYSQL_USER: myUserName
MYSQL_PASSWORD: myPwd
MYSQL_ROOT_PASSWORD: myRootPwd
MYSQL_DATABASE: dev_myProjectName
volumes:
- ./mysql:/var/lib/mysql
command: ["--default-authentication-plugin=mysql_native_password"]
ports:
- "3307:3306"
and my Dockerfile
FROM php:8.1-apache
RUN a2enmod rewrite
RUN apt-get update \
&& apt-get install -y libzip-dev git wget nano --no-install-recommends \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN docker-php-ext-install pdo
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install zip
RUN wget https://getcomposer.org/download/2.4.2/composer.phar \
&& mv composer.phar /usr/bin/composer && chmod +x /usr/bin/composer
COPY docker/apache.conf /etc/apache2/sites-enabled/000-default.conf
COPY . /var/www/myProjectName_api
RUN chown -R www-data:www-data /var/www/myProjectName_api/var
# Allow Authorization header
RUN echo 'SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1' >> /etc/apache2/apache2.conf
COPY environments/.env.dev /var/www/myProjectName_api/.env.local
WORKDIR /var/www/myProjectName_api
COPY docker/entrypoint.sh /tmp
RUN chmod +x /tmp/entrypoint.sh
ENTRYPOINT /tmp/entrypoint.sh

docker entrypoint sh file restarting

I am testing docker with my php project. Everything is ok in testing but if I add ENTRYPOINT, docker is restarting.
Here is my docker compose file
version: "3.7"
services:
#Laravel App
app:
build:
args:
user: maruan
uid: 1000
context: ./docker/7.4
dockerfile: Dockerfile
# command: sh -c "start-container.sh"
image: laravel-app
restart: unless-stopped
working_dir: /var/www/
volumes:
- ./:/var/www
networks:
- app-network
#Nginx Service
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- 8000:80
volumes:
- ./:/var/www
- ./docker/7.4/nginx/conf.d:/etc/nginx/conf.d/default.conf
networks:
- app-network
#Mysl Service
db:
image: mysql:8
restart: unless-stopped
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
networks:
- app-network
networks:
app-network:
driver: bridge
Dockerfile
FROM php:7.4-fpm
# Arguments defined in docker-compose.yml
ARG user
ARG uid
WORKDIR /var/www
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# Install system dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential mariadb-client libfreetype6-dev libjpeg-dev libpng-dev libwebp-dev zlib1g-dev libzip-dev gcc g++ make vim unzip git jpegoptim optipng pngquant gifsicle locales libonig-dev \
&& docker-php-ext-configure gd \
&& docker-php-ext-install gd \
&& apt-get install -y --no-install-recommends libgmp-dev \
&& docker-php-ext-install gmp \
&& docker-php-ext-install mysqli pdo_mysql zip \
&& docker-php-ext-enable opcache \
&& apt-get autoclean -y \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /tmp/pear/
COPY . /var/www
# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
chown -R $user:$user /home/$user
COPY start-container.sh /usr/local/bin/start-container.sh
RUN chmod +x /usr/local/bin/start-container.sh
ENTRYPOINT ["start-container.sh"]
start-container.sh file
#!/usr/bin/env bash
set -e
cd /var/www
php artisan optimize
php artisan view:cache
#composer install && composer dump-autoload
exec "$#"
I also print log for that docker image.
Configuration cached successfully!
Route cache cleared!
Routes cached successfully!
Files cached successfully!
Compiled views cleared!
Blade templates cached successfully!
I think my error is docker container is restarting after running start-container.sh file. When I google, some people use PHP artisan script with ENTRYPOINT sh file.
What should I do not to restart again and again with ENTRYPOINT sh file?
Your entrypoint script ends with the line exec "$#". This runs the image's CMD, and is generally a best practice. However, your image doesn't have a CMD, so that command just expands to a bare exec, which causes the main container process to exit.
An image built FROM php:fpm often won't have a CMD line since the base image's Dockerfile specifies CMD ["php-fpm"]; it is enough to COPY your application code into a derived image, and the base image's CMD knows how to run it. However, setting ENTRYPOINT in a derived image resets the CMD from the base image (see the note in the Dockerfile documentation discussing CMD and ENTRYPOINT together). This means you need to repeat the base image's CMD:
ENTRYPOINT ["start-container.sh"]
CMD ["php-fpm"] # duplicated from base image, because you reset ENTRYPOINT

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 .

Need help in executing powershell commands in dockerized laravel

I want to access vsphere config info from powercli script to laravel. But I do not know how to make them work together in docker. Whatever I do, the error is similar to this - The command "'pwsh' '-v'" failed. Exit Code: 127(Command not found) Working directory: /var/www/public Output: ================ Error Output: ================ sh: 1: exec: pwsh: not found
As a last resort, I am here.
docker-compose.yml:
version: '3'
services:
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: vapp
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: vapp
MYSQL_ROOT_PASSWORD: vapp
SERVICE_TAGS: dev
SERVICE_NAME: mysql
TZ: Asia/Kolkata
volumes:
- dbdata:/var/lib/mysql/
- ./mysql/my.cnf:/etc/mysql/my.cnf
- ./mysql:/var/lib/mysql-files/
networks:
- app-network
phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: phpMyAdmin
restart: always
ports:
- "8080:80"
environment:
MYSQL_ROOT_PASSWORD: vapp
PMA_HOST: db
external_links:
- mariadb:mariadb
volumes:
- "./phpmyadmin/sessions:/sessions"
networks:
- app-network
#Docker Networks
networks:
app-network:
driver: bridge
#Volumes
volumes:
dbdata:
driver: local
Dockerfile
FROM mcr.microsoft.com/powershell:latest
WORKDIR ./
FROM php:7.4-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
# Set working directory
WORKDIR /var/www
# 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
RUN snap install powershell --classic
# 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
# 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"]
Controller:
//$process = new Process(['ls', '-lsa']); #This works but next one do not
$process = new Process(['pwsh', '-v']);
$process->run();
// executes after the command finishes
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
echo $process->getOutput();
I know how the Process() method works. Above code fails.
I need help on making powershell and laravel work together in docker.
Is there anything wrong with docker configuration or the controller code in accessing powershell.
You are using multistage build in Dockerfile. It can copy artifacts, but you don't copy anything. So pwsh app doesn't copy to PHP image (to the second stage).
You could remove first stage (FROM mcr.microsoft.com/powershell:latest) and install properly Powershell inside PHP image.
For example:
RUN apt-get update && apt-get install -y \
wget
RUN wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb && \
dpkg -i packages-microsoft-prod.deb
RUN apt-get update && apt-get install -y \
powershell
PHP image use Debian 10, so here is the instruction: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-7.1#debian-10
Check pwsh inside container first:
docker exec -it app bash
pwsh
Thanks to #konstantin-bogomolov
The reason was the incorrect docker file. Powershell was not properly installed in container.
For those who stop by, Working dockerfile is below.
FROM php:7.4-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
# Set working directory
WORKDIR /var/www
# 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
# 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
# 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
# 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"]

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