Hello, people. Long time, no tutorial…
I was thinking the other day about this thing called docker containers, and after all this time I still f***ing hate the idea of having this new stack over our main interest, the programming part. Why do we have to bother ourselves with these kind of problems, when we don’t even know how to program properly. Anyways…
Every time I want to start working with the “new” CodeIgniter 4 (it’s not so new, as it was launched quite some time ago), I have this big wall called “Docker containers”, even though it is not mentioned in the documentation, and it is not really necessary.
But, even though it is not necessary, let us be prepared for the deployment part when the project is done. So in order to be prepared for production let’s start by launching the project the right way.
So, let’s dig into starting our docker stack by requiring Apache, PHP and MySQL. Before everything, I must warn you that you first need to install Docker and start it. I will not explain you how to install Docker.
I’ve started by making the following directory structure
project_folder
- db_data
- docker
- apache
- mysql
- src
- public
OK. So, we have a “docker” directory in which we put (almost) everything related to docker. Also, we have a “db_data” directory in which we will keep everything that is stored into our database. In our “src” directory we will keep our CodeIgniter core files, and in “public” will reside the public assets of our application.
Now, we have to create “docker-compose.yml”, which we will put in our project folder (in our case “project_folder”). This file should contain, for starters, the following:
version: '3' services:
In our docker-compose.yml we will tell docker that we need two, or three, containers – depends on your needs. The first one is the Apache server with PHP, the second will be the database container, and the third, if you want, can be a database client, namely “Adminer”.
The “db” container
Considering that we will need a MySQL database, let’s start by defining the “db” container in our “services”. So far, the docker-compose.yml file will look like this:
version: '3'
services:
db:
build:
context: .
dockerfile: docker/mysql/Dockerfile
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
command: --default-authentication-plugin=mysql_native_password
restart: unless-stopped
volumes:
- ./db_data:/usr/data
ports:
- 3306:3306
volumes:
db_data:
What did we do here? We’ve told Docker that, in order to create a db container it will need to use the “Dockerfile” file that can be found in “docker/mysql”. We also told it to restart until is manually stopped or until there is a problem with the service. We’ve also declared the local volume that the container should use in order to save the data, and we’ve opened the 3306 port for communication between containers.
I’ve said earlier something about a “Dockerfile”. So, let’s create one in “docker/mysql”. Don’t worry. This will be fast, as it looks like this:
FROM mysql
ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
ENV MYSQL_DATABASE=${MYSQL_DATABASE}
#COPY ./docker/mysql/codeigniter_database.sql /docker-entrypoint-initdb.d/init.sql
EXPOSE 3306
And, that’s it. We simply told docker to get the latest version of MySQL and expose 3306 port to the “world”. If we want, we can also choose the MySQL version by doing something like: FROM mysql:5.7 . See more here: https://hub.docker.com/_/mysql
We also told it to create a root password that can be retrieved from an environment variable name MYSQL_ROOT_PASSWORD and to create a database whose name is also mentioned in an environment variable named MYSQL_DATABASE.
You may also see a commented line that starts with COPY . If you want to start a project with some default tables, you can at any tyme save the tables in a file (maybe “codeigniter_database.sql“?) and serve it to our container as starting point for the MySQL database.
I mentioned something about environment variables. This environment variables can be saved in an “.env” file that can be saved in our project folder (in my case in the “project_folder”).
So, let’s create one that will contain our first variables:
MYSQL_ROOT_PASSWORD=cod31gn1t3
MYSQL_DATABASE=codeigniter_db
Nice. Now, going further, let’s install our Apache server container.
The “web” container
We create the server container by adding the “web” service to our docker-compose.yml file, which now should look like this:
version: '3'
services:
db:
build:
context: .
dockerfile: docker/mysql/Dockerfile
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
command: --default-authentication-plugin=mysql_native_password
restart: unless-stopped
volumes:
- ./db_data:/usr/data
ports:
- 3306:3306
web:
build:
context: .
dockerfile: docker/apache/Dockerfile
args:
uid: ${UID}
environment:
- APACHE_RUN_USER=#${UID}
- APACHE_RUN_GROUP=#${UID}
restart: unless-stopped
volumes:
- ./public:/var/www/html/public
- ./apache_log:/var/log/apache2
ports:
- 80:80
depends_on:
- db
links:
- db
volumes:
db_data:
src:
What did we say in here? We told docker that we have a Dockerfile that we want to be used in order to set up the Apache server container. We want to pass some arguments (args) to the Dockerfile, arguments that will also be taken from our environment variables. We want the container to restarted unless manually stopped and also we want it to use the “src” local directory for its “var/www/html” directory. We will use the 80 port for communicating between our computer and the server and we’ve let the Apache to depend and to be linked to the “db” service.
Now, let’s also look to our other “Dockerfile”, the one that we’ve told Docker to use for Apache and PHP. Let’s create it in “docker/apache” directory and put the following in:
FROM php:7.4-apache
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
RUN apt-get update
RUN apt-get install -y \
git \
zip \
curl \
sudo \
unzip \
libicu-dev \
libbz2-dev \
libpng-dev \
libjpeg-dev \
libmcrypt-dev \
libreadline-dev \
libfreetype6-dev \
g++
RUN docker-php-ext-install \
bz2 \
intl \
bcmath \
opcache \
calendar \
pdo_mysql \
mysqli
# 2. set up document root for apache
COPY docker/apache/000-default.conf /etc/apache2/sites-available/000-default.conf
# 3. mod_rewrite for URL rewrite and mod_headers for .htaccess extra headers like Access-Control-Allow-Origin-
RUN a2enmod rewrite headers
# 4. start with base php config, then add extensions
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
# 5. Composer
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
RUN chmod +x /usr/local/bin/composer
RUN composer self-update
COPY src/ /var/www/html/
# 6. we need a user with the same UID/GID with host user
# so when we execute CLI commands, all the host file's ownership remains intact
# otherwise command from inside container will create root-owned files and directories
ARG uid
RUN useradd -G www-data,root -u $uid -d /home/devuser devuser
RUN mkdir -p /home/devuser/.composer && \
chown -R devuser:devuser /home/devuser
EXPOSE 80
So… we will use Apache with PHP 7.4, we will name our server localhost, and we start by updating and installing some needed PHP extensions. After that, we establish a new apache config file which we create in “docker/apache” directory.
After this, we install Composer, we copy everything we find from our local “src” directory into the containers “/var/www/html” directory.
And, at the end we set up some read/write rights for our root user that is nemd in our UID environment variable.
Now let’s update the “.env” file with the following variable:
UID=1000
I said something earlier about the apache config file. So, let’s create a file “000-default.conf” into our “docker/apache” directory and put the following into it:
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/public
<Directory /var/www/html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
All done… Unless you also want a MySQL visual client, like Adminer.
Set up “Adminer” container? Simple…
We just need to add the service to our “docker-compose.yml”. So the file should now look like this:
version: '3'
services:
db:
build:
context: .
dockerfile: docker/mysql/Dockerfile
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
command: --default-authentication-plugin=mysql_native_password
restart: unless-stopped
volumes:
- ./db_data:/usr/data
ports:
- 3306:3306
web:
build:
context: .
dockerfile: docker/apache/Dockerfile
args:
uid: ${UID}
environment:
- APACHE_RUN_USER=#${UID}
- APACHE_RUN_GROUP=#${UID}
restart: unless-stopped
volumes:
- ./src:/var/www/html
- ./apache_log:/var/log/apache2
ports:
- 80:80
depends_on:
- db
links:
- db
adminer:
image: adminer
restart: unless-stopped
ports:
- 8080:8080
volumes:
db_data:
src:
Now, if we go to http://localhost:8080, we will have access to Adminer.
OK. Done. Now, if we go to our “project_folder” and do a “docker-compose up” in our terminal, all our stack should be built, and we can go to work.
We can test that everything is alright by creating an index.php file in our “public” directory, and we put the following code in it:
<?php
$host = 'db';
$user = 'root';
$pass = 'cod31gn1t3';
$conn = new mysqli($host, $user, $pass);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "Connected to MySQL successfully!";
If everything went alright, if we go to http://localhost, we should see that we are connected to MySQL using the mysqli driver. Don’t worry, we’ve also installed the PDO driver, just in case you like it better.
Note: This tutorial is actually a compiling of tutorials found on the internet, while also removing the frustrations gathered from them, and benefiting from the great help coming from my colleagues: Cristian Obreja and Vlad Stoica. I also want to mention that I F***ING HATE CONTAINERS AND EVERYTHING RELATED TO THEM!
How about reading about how to install CodeIgniter4 in Docker containers in my next tutorial?
tl;dr
- Clone my Github repository: https://github.com/avenirer/CodeIgniter4-Docker-Apache-PHP-MySQL
- go to terminal and write: ‘docker-compose up’
- go in container’s terminal and install CodeIgniter 4 using composer’s ‘create-project’
- enjoy
Read more from here:
https://hub.docker.com/_/php
https://medium.com/better-programming/setting-up-mysql-database-in-a-docker-d6c69a3e9afe
https://dev.to/veevidify/docker-compose-up-your-entire-laravel-apache-mysql-development-environment-45ea
https://medium.com/@juangsalazprabowo/docker-setup-for-laravel-development-in-local-environment-21b1df760cfd
https://dev.to/truthseekers/setup-a-basic-local-php-development-environment-in-docker-kod
https://stackify.com/docker-for-php-a-start-to-finish-guide/
https://www.cloudreach.com/en/resources/blog/ct-apache-docker-containers/
https://semaphoreci.com/community/tutorials/dockerizing-a-php-application
thank you bro….