I want to run my Symfony3 app out of Docker container. As I understand I can install/create multiple images with docker and spin them up into a container that will hold my app.
I been going through some docks on Docker on how I may do this, I seen that I can have a docker images such as:
Ubuntu
PHP
Nginx -> As I understand it is like an Apache server, so in theory it will handle all the requests and responses.
But I still find it hard to understand the concept of spinning multiple images into one container.
Also I have seen something called DockerFile which apparently can can build a my dev environment into one container that I can also work with.
Question 1:
Can someone please clarify the whole process I still find it hard to wrap my head around it.
Question 2:
How can I build DockerFile and what is it?
You don't want to spin multiple images into one container. It's possible that you don't even need a docker file (but for PHP, you probably do).
The usual mantra concerning docker is "one process per container", and after working with it for several months, I find this to be great advice, even if it's not always achievable. For a PHP app, whether it's symphony, Cake, Laravel, Wordpress, whatever, this is how I do it. I use apache, and it sounds like you might be more familiar with apache as well. You can easily substitute the official nginx container with minor changes to my example if desired.
One container runs PHP-FPM
One container runs Apache (httpd)
If you need a database, one container for mysql
Optionally, a container for composer.
docker-compose to orchestrate all of these containers
I typically use the official httpd container, the official mysql container, and I extent the offical php fpm container as described to include the mods that I need. Here is an example of a PHP-FPM dockerfile that adds in some external libs that might be needed for your app:
FROM php:5.5-fpm
RUN apt-get update && apt-get install -y \
php5-mysql \
php5-curl \
php5-common \
php5-gd \
php5-imagick \
php5-intl \
php5-dev \
php5-sqlite \
php5-xdebug \
php5-memcached \
\
libmemcached-dev \
libmcrypt-dev \
libfreetype6-dev \
libxml2-dev \
libmagickwand-dev \
libjpeg62-turbo-dev \
libpng-dev && \
\
docker-php-ext-install pdo pdo_mysql && \
docker-php-ext-install soap && \
docker-php-ext-configure gd --with-jpeg-dir=/usr/include/ && \
docker-php-ext-install gd && \
docker-php-ext-install iconv mcrypt && \
\
pecl install imagick && \
docker-php-ext-enable imagick && \
pecl install memcached && \
docker-php-ext-enable memcached && \
\
pecl install xdebug && \
docker-php-ext-enable xdebug && \
\
mkdir -p /app/content && \
mkdir -p /app/usr/local/apache2 && \
cd /app/usr/local/apache2 && \
ln -s ../../../content htdocs
COPY copy/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
This builds an image that I actually use for development. In addition to installing dependencies, it copies a config for xdebug, and sets up the folder structure to hold my app.
You would build this container like this:
docker build -f nameoffile.Dockerfile -t myhubaccount/myphpcontainer \
./path/to/folder/where/dockerfile/is
This builds an image on your machine tagged as myhubaccount/myphpcontainer and you can refer to it in your compose file.
A basic compose file that tells these containers how to talk to each other might look something like this:
docker-compose.yml
version: '2'
services:
httpd:
image: httpd:latest
volumes:
- ./docker_conf/httpd.conf:/usr/local/apache2/conf/httpd.conf
- ./webroot:/usr/local/apache2/htdocs
ports:
- "80:80"
links:
- fpm
logging:
options:
max-size: "0"
database:
image: mysql
ports:
- "3306:3306"
volumes:
- ./schema.sql:/docker-entrypoint-initdb.d/schema.sql
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_DATABASE: development
logging:
options:
max-size: "5k"
fpm:
image: myhubaccount/myphpcontainer
volumes:
- ./webroot:/app/content
links:
- database
logging:
options:
max-size: "50k"
I think it's beneficial to highlight several parts of this file. First, for php-fpm you need to set up apache to talk to the fpm server. The links object under httpd tells the container that there is another container with a domain name of "fpm", and docker knows how to resolve that name, so any communication with the fpm server can use that name. We have to mount (under volumes) the apache config in the httpd container. It looks like the default config, but has this part added to accommodate php-fpm:
ProxyTimeout 30
<FilesMatch ".*\.php$">
SetHandler "proxy:fcgi://fpm:9000"
</FilesMatch>
This tells apache to forward requests for php files to the fpm server and serve the result.
The ports entry causes port 80 of the container to be forwarded to port 80 of the docker machine. This is localhost on linux, or the docker-machine ip on Mac and Windows. You can find this ip with the console command docker-machine ip.
We do the same thing on the mysql container so that we access mysql directly with a tool like Mysql Workbench. You can read about the environment variables that the official mysql container allows and what they do.
We have links for fpm, if it needs to talk to the database. The hostname for your database in this case is just "database".
The logging items are not necessary, just personal preference to keep the log output from becoming excessive.
Once you have all this in place, you bring up the environment with docker-compose up. If you want to take a look at what a container looks like you can get a shell on a running container with docker-compose exec fpm bash, substituting "fpm" with the name of the container you want to look at. The caveat here is that the container must actually include the bash binary. All of these here do, but some containers do not.
I hope that this gives enough php-specific example to help you wrap your head around how docker sort of works. I would suggest re-reading the docs for bother Docker and Docker Compose. And I would also suggest reading the Dockerfiles for the official images if you are interested in building your own containers. The docs have links to Dockerfiles that the Docker team considers to be exemplary.
Related
I think I'm going to go crazy. I've searched all over and can't seem to find a working solution both here on Stack, GitHub and other far reaches of the interwebs.
On this particular project, running docker-compose build is taking FOREVER. It didn't use to be this way, and on other projects that use Docker, it's not an issue at all. And by forever... I'm talking around 10-15 minute build times when it used to only take around 2 minutes tops. I had two separate coworkers DL the same repo (one on Ubuntu 18, and the other on macOS 14.x). When they ran the build command, the entire process took ~2 minutes. Both of these people had never built this project before, so they were starting from complete scratch.
I've uninstalled/reinstalled Docker, ran a complete docker system prune -a, connected via wifi, connected via Ethernet, tried a different wifi network, tweaked my compose file, tweaked my docker file -- nothing.
My machine is a 2018 MacBook Pro with a quad-core 2.7GHz i7, running macOS 10.14.6 with 16gb of installed RAM with Docker Desktop 2.1.0.5.
I've allowed Docker Desktop to have up to 12gb or RAM. During the build process, my machine cpu usage spikes on average from 110% up to 270% running the com.docker.hyperkit process.
To be clear, it's hanging on the "Building php" (or "Building web") status message(s) before anything really even starts. After that, the actual build process runs smoothly and quick.
Here is my docker-compose.yaml file:
version: '3.1'
services:
db:
container_name: clientsname.db
hostname: db
image: mariadb:10.4.1-bionic
volumes:
- ./db-data:/var/lib/mysql:delegated
ports:
- 3307:3306
environment:
MYSQL_DATABASE: my_database
MYSQL_USER: my_user
MYSQL_PASSWORD: my_pass
MYSQL_ROOT_PASSWORD: my_pass
php:
container_name: clientsname.php
hostname: php
build:
dockerfile: php/php.dockerfile
context: ./
environment:
XDEBUG_CONFIG: remote_host=${REMOTE_HOST}
volumes:
- ../web:/var/www/web
- ../moodle:/var/www/moodle
- ../moodledata:/var/www/moodledata
- ./php/custom.ini:/usr/local/etc/php/conf.d/zzz-custom.ini
- ./php/z-errors.ini:/usr/local/etc/php/conf.d/z-errors.ini:delegated
- ./php/z-upload.ini:/usr/local/etc/php/conf.d/z-upload.ini:delegated
- ./php/z-xdebug.ini:/usr/local/etc/php/conf.d/z-xdebug.ini:delegated
depends_on:
- db
web:
container_name: clientsname.web
hostname: web
build:
dockerfile: nginx/nginx.dockerfile
context: ./
volumes:
- ../web:/var/www/web
- ../moodle:/var/www/moodle
- ../moodledata:/var/www/moodledata
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./nginx/ssl:/etc/nginx/ssl
- ./logs:/var/log/nginx
ports:
- 80:80
- 443:443
depends_on:
- php
- db
Here is the referenced php.dockerfile file:
FROM php:7.2.26-fpm
LABEL maintainer="My Clients Name"
# Environment variables
ENV DEBIAN_FRONTEND=noninteractive
ENV COMPOSER_ALLOW_SUPERUSER=1
ENV COMPOSER_NO_INTERACTION=1
ENV COMPOSER_HOME=/usr/local/share/composer
# Working Directory
WORKDIR /var/www/web
WORKDIR /var/www/moodle
WORKDIR /var/www/moodledata
RUN rm /etc/apt/preferences.d/no-debian-php && apt-get update && apt-get install -y --no-install-recommends apt-utils \
build-essential \
php-soap \
libzip-dev \
libmagickcore-dev \
libmagickwand-dev \
libmagic-dev \
libpng-dev \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libmemcached-dev \
zlib1g-dev \
nano \
sudo \
gnupg \
curl \
unzip && \
docker-php-ext-install soap pdo_mysql mysqli && \
docker-php-ext-install -j$(nproc) gd iconv && \
docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ && \
pecl install zip-1.15.2 imagick memcached-3.0.4 xdebug && \
docker-php-ext-enable memcached imagick zip xdebug
# Install Composer, Node, Gulp, and SASS
RUN curl -s https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
RUN curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - && apt-get install -y nodejs && npm install npm#latest -g && npm install --global gulp-cli && npm config set unsafe-perm=true
# Export composer vendor path
RUN echo "" >> ~/.bashrc && echo 'export PATH="$HOME/.composer/vendor/bin:$PATH"' >> ~/.bashrc
And the referenced nginx.dockerfile
FROM nginx:stable-alpine
RUN apk add --update bash && rm -rf /var/cache/apk/*
WORKDIR /var/www/web
It's driving me batty... what in the bloody hell could I be doing wrong? If there is anything I've left out that you'd all like to know, please let me know and I'll update the post.
UPDATE
Thanks to #BMitch and you all who have commented thus far. I took my entire /docker build directory and moved it into a test folder, and then created empty /web, /moodle, and /moodledata directories before running the build command. It started to compile immediately.
What's curious to me is that the other coworkers DL'd the same Git repo that I have and did not have any of the same issues. Oooh... come to think of it... I bet I know what the issue is.
This is from your build context (that's often the directory where you run your build, but can be overridden as you've done in the compose file). You have a large number of files, or large files, in the context directory that is sent before performing the build.
You can use .dockerignore which has a nearly identical format to .gitignore to exclude files from being sent on build. And with BuildKit (enabled if you export DOCKER_BUILDKIT=1 in recent versions of docker) it will only send context when you explicitly copy files and then only when those files have changed from what is available in the cache.
For more on the build context, see: https://docs.docker.com/engine/reference/commandline/build/
There's also the best practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
Working with a Docker file which includes PHP, Apache, and MySQL. I was able to get the page to pull up in localhost. However, I am unable to get the MySQL running.
# Use an official PHP Apache runtime as a parent image
FROM php:7.0-apache
# Set the working directory to /var/www/html/
WORKDIR /var/www/html/
# Install mysqli extensions
RUN docker-php-ext-install mysqli && \
apt-get update && \
apt-get install -y zlib1g-dev && \
apt-get install -y libxml2-dev && \
docker-php-ext-install zip && \
docker-php-ext-install xml
# Make port 80 available to the world outside this container
EXPOSE 80
Based on the above, when I attempt to run the following command:
docker run --name some-mysql -e MYSQL_abcd_123456=my-secret-pw -d mysql:tag
I receive the following error in the terminal:
Unable to find image 'mysql:tag' locally
docker: Error response from daemon: manifest for mysql:tag not found.
What am I missing?
docker run command requires the image name parameter with optional version of the image (recommended).
Use:
docker run --name some-mysql -e MYSQL_abcd_123456=my-secret-pw -d mysql:latest
to pull the latest mysql image or choose the exact version listed by supported tags
for example:
5.7.25
or
8.0.15
In majority cases you should not use the tag with the version latest (or skip the version) because that is ambiguous and can give you different versions of the image on two different machines even they used for the build the same Dockerfile FROM statement or you used the same docker run (docker pull) command. Read more
I would recommend always stick to the explicit version number if possible, for example:
docker run --name some-mysql -e MYSQL_abcd_123456=my-secret-pw -d mysql:8.0.15
I have set up a Symfony4 project with in a Docker container.
I followed the Jobeet-Tutorial where they use the phpdocker.io - generator.
All works perfect but very slow. So I want to speed up and enable the opcache and configure it.
I found helpful links in the net. So I added to my Dockerfile this:
RUN docker-php-ext-configure opcache --enable-opcache \
&& docker-php-ext-install opcache
# Copy configuration
COPY config/opcache.ini $PHP_INI_DIR/conf.d/
The problem is that I don't have this helperscripts:
docker-php-ext-configure
docker-php-ext-install
docker-php-ext-enable
So I decided to search it in the internet and copy it into my project.
Now I have it in the php-fpm folder of my docker directory.
My directory looks like this now - the scripts are in the beneath the Dockerfile:
Is there any other step I forgot to do, like registering these scripts somewhere?
The most immediate answer to your question is that you need to copy those scripts into the Docker image you are building. To do that, you should create a subdirectory within the php-fpm directory named bin and put all of those scripts in that directory. Then, in your Dockerfile:
COPY bin /usr/local/bin
Now when you try to use that image, the scripts will be within your executable PATH.
However
Those docker-php-ext-* scripts you found are from the PHP project's official Docker images and are intended to be used with those images.
You are using the phpdockerio/php73-fpm:latest image, which seems to use ubuntu:bionic as a base image. These scripts depend heavily on the PHP Dockerfiles, which do a bunch of preparatory steps, such as downloading the source code for the PHP interpreter itself to /usr/src. Making these scripts run directly in a phpdockerio container would be a very involved process.
That leaves you with 2 options:
Forgo the scripts and install Ubuntu's prebuilt packages. You seem to already have the apcu, apcu-bc, cli, curl, json, mbstring, opcache, readline, xml, and zip PHP extensions installed. You can see the full list of packages that are available from the default repos this way by running
docker run --rm -it phpdockerio/php73-fpm:latest bash -c 'apt-get update && apt search ^php7.3-';
When you know which packages you want, you can add them to your Dockerfile.
Switch to using an official PHP image instead so you can use the docker-php-ext-* scripts. The phpdocker-io image you are using is essentially PHP7.3-FPM on Ubuntu, and the closest official PHP image to that is php:7.3-fpm-stretch (Debian 9). You can build and install the extensions listed in Option 1 by changing your PHP-FPM Dockerfile to:
FROM php:7.3-fpm-stretch
# Run in Bash instead of Bourne shell to get lists
RUN ["bash", "-c", " \
#Exits on error or unbound variable. Now we can use semicolons instead of
#ampersands
set -eu; \
\
ext_build_dependencies=( \
#Needed to build php-curl
libcurl4-gnutls-dev \
\
#Needed to build php-mbstring
libedit-dev \
\
#Needed to build php-xml \
libxml2-dev \
\
#Needed to build php-zip
zlib1g-dev libzip-dev \
); \
\
apt-get update; \
apt-get install -y ${ext_build_dependencies[#]}; \
\
#Build the extensions
docker-php-ext-install curl json mbstring readline xml zip ; \
pecl install apcu apcu_bc; \
\
apt-get purge -y ${ext_build_dependencies[#]}; \
apt-get autoremove -y; \
apt-get clean -y; \
"]
If Ubuntu 18 and Debian were binary-compatible (they're not), you could try a third option, which would be building the extensions using a PHP image, then copying over the built extensions as the second stage of a multi-stage build. This would be possible if your image uses the same Linux flavor as as the PHP image does. For example, if your image were based on alpine:3.8, you could use php:7.3-fpm-alpine3.8 to build your extensions and copy them over.
I have two Docker images created on different days. One is able to connect with a separate mysql 8.0 image, the other is not.
The history of the images is that I made one that works, tweaked the Dockerfile for portability, then when that image didn't work, I rolled the Dockerfile back to the original - but it still didn't work. I added the --no-cache option to the build but that didn't help.
The details:
Here is the Dockerfile:
FROM php:7.2-apache
RUN apt-get update && apt-get install -y \
apt-utils \
zlib1g-dev \
libmemcached-dev \
memcached \
libevent-dev \
libc-client-dev \
curl \
libxml2-dev \
zip \
unzip \
ldap-utils \
&& docker-php-ext-install mysqli pdo_mysql \
&& pecl install memcached xdebug \
&& docker-php-ext-enable memcached xdebug
RUN service memcached restart
Here's the line in the php that works for one image and not the other:
mysqli_connect('mysql80', 'my_username', 'my_password', 'my_database');
The error is:
Message: mysqli_connect(): php_network_getaddresses: getaddrinfo failed: Name or service not known
However, when I run the following from the command line of EITHER running container, it works just fine:
mysql --host=mysql80 -umy_username -pmy_password
I run the containers with identical instructions except the port to listen on, and I connect them to the network with scripts that are identical except for the image name.
Other things I've checked in running instances of the two images:
php -i yields identical results except of course HOSTNAME
likewise, phpinfo() in the code produces the same results except ports and HOSTNAME.
apache2ctl -M is identical between the two
apt list --installed lists identical packages
I currently have them attached to the same bridge network, communicating with the same mysql instance.
They are running php code from the same mounted source directory (confirmed by altering the code).
For completeness, here's how I run the two images (Usually of course I would only run one at a time, but having both running does not change the behavior.)
# newer image that doesn't work
docker run -d -p 8787:80 --name awesomeApp \
-v /path/to/php/src:/var/www/html \
-e DB_HOST='mysql80' \
awesomeImage:latest
docker network connect awesome-net awesomeApp
# old image that does work
docker run -d -p 8788:80 --name oldAwesome \
-v /path/to/php/src:/var/www/html \
-e DB_HOST='mysql80' \
awesomeImage:oldOneThatWorks
docker network connect awesome-net oldAwesome
I've completely run out of things to try to find a meaningful difference between the images, and need to be able to modify the image and have it work. Any suggestions welcome.
I'll preface this by saying I am very new to the docker world and despite reading documentation I am still a little confused about a few things.
I want to build a container with centos7 apache and php. I don't want to use an already existing image, want to build a custom container. I have the following folder structure
My rw/docker/webserver/Dockerfile:
FROM centos:7
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]
RUN yum -y install httpd
RUN systemctl start httpd
RUN systemctl enable httpd
RUN yum update -y && yum install -y libpng-dev curl libcurl4-openssl-dev
RUN docker-php-ext-install pdo pdo_mysql gd curl
RUN a2enmod rewrite
MY docker-compose.yml
version: '2'
services:
webserver:
build: ./docker/webserver
ports:
- "80:80"
- "443:443"
volumes:
- /**PATH**/rw/services:/var/www/html
links:
- db
db:
image: mysql:5.7
ports:
- "3306:3306"
volumes:
- ./db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=****
- MYSQL_DATABASE=****
This fails when docker tries to start httpd with the error
ERROR: Service 'webserver' failed to build: The command '/bin/sh -c systemctl start httpd' returned a non-zero code: 1
Q1. Why is the install failing?
Q2. Is the the proper way to do this? Should my dockerfile for centos and apache+php be separate. If yes, how does that work?
Q1. I think systemctl may not be provided with CentOS docker image.
Indeed, docker services are not meant to be run as daemons, but in the foreground. Take a look at apache's original http-foreground shell script for a better understanding of the concept.
Q2. Not that's not the right way IMHO.
Running apache is the job of an entrypoint or command script.
So instead of RUN your-command-to-run-apache, it would rather be CMD your-command-to-run-apache.
Once again, Apache official repository can give you some clue about this.
To my eyes these kinds of Dockerfiles look too old as they try to map the external docker daemon inside the container. That's a workaround as a systemd daemon cannot be run separately in a container.
Instead I am using the docker-systemctl-replacement script. The docker systemctl.py can parse the normal *.service files to know how to start and stop services. You can register it as the CMD of an image in which case it will look for all the systemctl-enabled services - those will be started and stopped in the correct order.
There are even some testcases for the LAMP stack available, so it should work in your case quite smoothly. The systemctl.py script is compatible with the systemd systemctl as much as that one simply overwrite the /usr/bin/systemctl inside the image - and all the non-docker installation instructions will work for docker builds.