Step by step guide to my Inception project done on Debian VM. Based on codeshaman's work and bitnami/wordpress-nginx.
- login as root
apt install -y neofetch curl sudo ufw docker docker-compose make openbox xinit kitty firefox-esr lsof wget libnss3-tools
- neofetch: a command-line utility that displays system information and distribution logos. It's useful for getting a quick overview of the system's configuration and can be used for diagnostic purposes. It also makes your machine look cool ;)
- sudo: Sudo is a program that allows users to run programs with the security privileges of a root. It is essential for executing administrative tasks on the system.
- ufw (Uncomplicated Firewall): UFW is a user-friendly command-line interface for managing iptables, which is a firewall management tool. It can be used to configure firewall rules to control incoming and outgoing network traffic, helping secure the server and Docker containers.
- docker: Docker is a platform for developing, shipping, and running applications in containers. It's commonly used for containerization, making it easier to deploy and manage applications, especially in server environments.
- docker-compose: Docker Compose is a tool for defining and running multi-container Docker applications. It allows you to define the services, networks, and volumes required for your applications in a single
docker-compose.yml
file. - make: Make is a build automation tool that can be used to simplify and automate the building and deployment of software. It can be useful for creating scripts and automating tasks related to Docker container management.
- openbox: Openbox is a lightweight, highly configurable window manager. Just so firefox can show the website.
- xinit: Xinit is a script that starts the X Window System server. It's used to launch X sessions and window managers, including Openbox.
- kitty: Kitty is a fast, feature-rich terminal emulator, so we're not bounded to tty only
- firefox-esr: to show websites on machine
- curl: Curl is a command-line tool for transferring data with URLs. It's commonly used to make HTTP requests to APIs or web services, which can be be useful when interacting with external resources or Docker-related web services.
- lsof: to check ports, and what apps use them
- wget: a command-line utility for downloading files from the internet. It can be used to retrieve files from web servers, FTP servers, and various other protocols. Will be used to install
mkcert
- libnss3-tools: a collection of command-line tools related to the Network Security Services (NSS) library. This library provides support for SSL/TLS, and it's used for secure network communications. Is a dependency of
mkcert
let non-root account to use sudo
and docker
- add non-root user to
/etc/sudoers
file - add non-root to docker group
sudo usermod -aG docker
non-root-username, to let the use of docker withoutsudo
by a non-root user
allow ports that will be used: 22
- default for ssh, 80
- http and 443
- https
Set some security settings. In /etc/ssh/sshd-config
PermitEmptyPasswords no
PermitRootLogin no
AllowUsers login
PasswordAuthentication no
PublicKeyAuthentication yes
Inside VirtualBox: Current machine > settings > network > Advanced > Port forwarding. Without port forwarding there is no way to access VM via network connection.
| Name | Host Port | Guest Port |
|----------|-----------|------------|
| SSH | 4222 | 22 |
| HTTP | 4280 | 80 |
| HTTPS | 42443 | 443 |
The ports with number less than 1024 are reserved by system
Install mkcert
- Run
url -s https://api.github.com/repos/FiloSottile/mkcert/releases/latest| grep browser_download_url | grep linux-amd64 | cut -d '"' -f 4 | wget -qi -
- rename executable to
mkcert
and move to/usr/local/bin
Subject requirement is that our domain name should be login.42.fr
In order to achieve this we need to modify /etc/hosts
file - add desired hostname in line with localhost
. After this operation we'll be able to access our web service via specified address while inside VM's terminal.
All certificates should be placed inside ~/project/requirements/nginx/tools
. Run mkcert login.42.fr
in this directory. Then, rename certificate files in a way nginx will understand:
mv login.42.fr-key.pem login.42.fr.key
mv login.42.fr.pem login.42.fr.crt
In ~/project/srcs
DOMAIN_NAME=login.42.fr
CERT_=./requirements/tools/login.42.fr.crt
KEY_=./requirements/tools/login.42.fr.key
DB_NAME=wordpress
DB_ROOT=rootpass
DB_USER=wpuser
DB_PASS=wppass
WP_ADMIN=wproot
WP_ADMIN_PASS=wprootpass
[email protected]
WP_USER=login
WP_USER_PASS=loginpass
[email protected]
Create a little shell library that will help automatically edit config files at the container's build time with sed confedit.sh
In ~/project/srcs/requirements/tools/
#!/bin/sh
# Check the there are 4 args
if [ "$#" -ne 4 ]; then
echo "Usage: $0 key value file assignation_operator"
exit 1
fi
# Give them meaningful names
key="$1"
value="$2"
file="$3"
assignation="$4"
if [ ! -f "$file" ]; then
echo "Error: File '$file' does not exist."
exit 1
fi
if [ ! -w "$file" ]; then
echo "Error: File '$file' is not writable."
exit 1
fi
# Find a line that tsarts with spaces, comments preceeding key in file
if grep -q "^[[:space:];#]*$key" "$file"; then
# Replace that line with concatenated key, assignation and value
sed -i "s|^[[:space:];#]*$key.*|$key$assignation$value|" "$file"
echo "Updated key '$key' with value '$value' in '$file'."
else
# If key not present - append concatenated string to the end of file
echo "$key$assignation$value" >> "$file"
echo "Appended key '$key' with value '$value' to '$file'."
fi
Create Dockerfile
In ~/project/srcs/requirements/mariadb
FROM alpine:3.18.4
Define build-time arguments. The ARG
instruction defines a variable that users can pass at build-time to the builder with the docker build
command.
ARG DB_NAME \
DB_USER \
DB_PASS \
DB_ROOT
Install MariaDB
RUN apk update && apk add --no-cache mariadb mariadb-client
COPY
confedit.sh to edit configs on buildtime and docker.cnf - config for mysql server's daemon process from host to container's root
COPY requirements/tools/confedit.sh .
COPY requirements/mariadb/conf/docker.cnf /etc/my.cnf.d/
Create a directory for MariaDB's runtime data and give full permissions. This is done to allow MariaDB to create its runtime socket file here.
RUN mkdir /var/run/mysqld; chmod 777 /var/run/mysqld
Set skip-networking
to 0
inside /etc/my.cnf.d/mariadb-server.cnf
to enable network connections.
RUN sh confedit.sh skip-networking 0 /etc/my.cnf.d/mariadb-server.cnf "="
Initialize the MariaDB data directory /var/lib/mysql
, then copy create_db.sh to init database, run it and remove - along with other scripts
RUN mysql_install_db --user=mysql --datadir=/var/lib/mysql
COPY requirements/mariadb/conf/create_db.sh .
RUN sh create_db.sh && rm -f /*.sh
The EXPOSE
instruction informs Docker that the container listens on the specified network ports at runtime. Through this interface the database will interact with the wordpress.
EXPOSE 3306
The USER
instruction sets the user name to use as the default user for the remainder of the current stage. The specified user is used for RUN
instructions and at runtime, runs the relevant and CMD
commands.
USER mysql
this is default command to be executed when the container is started. Launch the MariaDB server mysqld
.
CMD ["/usr/bin/mysqld"]
in ~/project/srcs/requirements/mariadb/conf
The MySQL server, or mysqld
, has a built-in mechanism to read configuration files when it launches. The docker.cnf
file will be picked up and applied by the MySQL server.
skip-host-cache
: This directive is used to skip the DNS host cache, which can help with performance.skip-name-resolve
: This directive disables hostname resolution, which means that the MySQL server won't perform DNS lookups to resolve hostnames. It can improve performance by avoiding the overhead of DNS resolution.bind-address=0.0.0.0
: This directive specifies the IP address to which the MySQL server will bind. In this case, it's set to0.0.0.0
, which means the MySQL server will listen on all available network interfaces. This allows the server to accept connections from any IP address. It's commonly used in Docker containers to make the MySQL server accessible from outside the container.
[mysqld]
skip-host-cache
skip-name-resolve
bind-address=0.0.0.0
In ~/project/srcs/requirements/mariadb/tools
SQL queries are passed through shell, because the access to env variables is required to resolve the name of databese, user, etc. But SQL engine can't resolve them, so at the first step sh
substitutes values of the variables in place of their names, and only then the script is passed to mysqld
engine
#!bin/sh
# Check if database already set up (normally, not yet)
if [ ! -d "/var/lib/mysql/wordpress" ]; then
/usr/bin/mysqld --user=mysql --bootstrap << EOF
USE mysql;
FLUSH PRIVILEGES;
DELETE FROM mysql.user WHERE User='';
DROP DATABASE test;
DELETE FROM mysql.db WHERE Db='test';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
ALTER USER 'root'@'localhost' IDENTIFIED BY '${DB_ROOT}';
CREATE DATABASE ${DB_NAME} CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE USER '${DB_USER}'@'%' IDENTIFIED by '${DB_PASS}';
GRANT ALL PRIVILEGES ON wordpress.* TO '${DB_USER}'@'%';
FLUSH PRIVILEGES;
EOF
fi
Shell-processed script looks like this:
USE mysql;
FLUSH PRIVILEGES;
DELETE FROM mysql.user WHERE User='';
DROP DATABASE test;
DELETE FROM mysql.db WHERE Db='test';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
ALTER USER 'root'@'localhost' IDENTIFIED BY 'rootpass';
CREATE DATABASE wordpress CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE USER 'wpuser'@'%' IDENTIFIED by 'wppass';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'%';
FLUSH PRIVILEGES;
Switch the active database to mysql. The mysql database contains system-related tables, including user and privilege information.
USE mysql;
Reload the user and privilege information. This is typically used after making changes to user accounts or privileges to ensure that the changes take effect immediately.
FLUSH PRIVILEGES;
Removes anonymous users that might exist in the MySQL user table.
DELETE FROM mysql.user WHERE User='';
Delete (drop) test database, needed for security. The test database is commonly present in MySQL installations for testing purposes.
DROP DATABASE test;
Remove any records related to the test database.
DELETE FROM mysql.db WHERE Db='test';
Ensure that the root user can only connect from the local machine and not from remote hosts, needed for security.
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
Set password for root user to the content of ${DB_ROOT} variable. Changing root password is also needed for security (COME ON).
ALTER USER 'root'@'localhost' IDENTIFIED BY '${DB_ROOT}';
Create a database for the app
CREATE DATABASE ${DB_NAME} CHARACTER SET utf8 COLLATE utf8_general_ci;
Create a database user for the app. In SQL scripts, the '%' character is used as a wildcard to represent zero or more unspecified characters
CREATE USER '${DB_USER}'@'%' IDENTIFIED by '${DB_PASS}';
Grant all privileges on the wordpress database to the user created in the previous step.
GRANT ALL PRIVILEGES ON wordpress.* TO '${DB_USER}'@'%';
FLUSH PRIVILEGES;
In ~/project/srcs/requirements/wordpress
FROM alpine:3.18.4
# latest stable php for now 8.2, but it is written 82
ARG PHP_VERSION=82 \
# Neded to setup connection to the database
DB_ROOT \
DB_USER \
DB_PASS \
# Wordpress users data
WP_ADMIN \
WP_ADMIN_PASS \
WP_ADMIN_MAIL \
WP_USER \
WP_USER_PASS \
WP_USER_MAIL
# required modules for wordpress
RUN apk update && apk upgrade && apk add --no-cache \
# The PHP interpreter itself
php${PHP_VERSION} \
# FastCGI process manager for PHP. Needed for nginx
php${PHP_VERSION}-fpm \
# Used for interacting with MySQL databases using the improved MySQLi extension.
# Essential for WordPress to communicate with the MySQL
php${PHP_VERSION}-mysqli \
# Provides functions for encoding and decoding JSON data, which is used by WP
php${PHP_VERSION}-json \
# Allows PHP to make HTTP requests using the cURL library.
php${PHP_VERSION}-curl \
# Provides functions for working with the Document Object Model (DOM).
# Essential for manipulating XML and HTML documents
php${PHP_VERSION}-dom \
# Allows reading and manipulating metadata from image files.
php${PHP_VERSION}-exif \
# Provides functions for determining the file type of a file or a stream.
# Useful for identifying the MIME type and handle file-related operations.
php${PHP_VERSION}-fileinfo \
# Supports handling multibyte charars (because internet is not ascii-only thing)
php${PHP_VERSION}-mbstring \
# Allows PHP to interact with the OpenSSL library (https, certificates ...)
php${PHP_VERSION}-openssl \
# Provides functions for parsing and manipulating XML documents.
php${PHP_VERSION}-xml \
# Allows PHP to work with ZIP archives.
php${PHP_VERSION}-zip \
# PHP Archive (phar) manager. Needed to install wp-cli
php${PHP_VERSION}-phar \
# Interface to communicate with Redis, an in-memory data structure store (bonus)
php${PHP_VERSION}-redis \
wget \
unzip
COPY requirements/tools/confedit.sh .
fix php-fpm config in-place and create symlinks for php82->php, because wp-cli is version-agnostic, and refers to php binary as "php" and not php82, as it is called by default
# The address on which to accept FastCGI requests
RUN sh confedit.sh "listen =" 9000 /etc/php82/php-fpm.d/www.conf " " && \
# create links
ln -s /usr/bin/php${PHP_VERSION} /usr/bin/php
The WORKDIR
instruction sets the working directory for any RUN
, CMD
, ENTRYPOINT
, COPY
and ADD
instructions that follow it in the Dockerfile
.
/var/www - is a default location where php-fpm will look for php files
WORKDIR /var/www
Install wordpress and remove archive
RUN wget https://wordpress.org/latest.zip && \
unzip latest.zip && \
cp -rf wordpress/* . && \
rm -rf wordpress latest.zip
Install WP-CLI, to be able to initialize website automatically, move to /bin
and make accessible as wp
RUN wget https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
chmod +x wp-cli.phar && \
mv wp-cli.phar /usr/local/bin/wp
Cleanup
RUN apk del wget && \
apk del unzip && \
apk cache clean
Define WP config with database connection details, etc
COPY ./requirements/wordpress/conf/wp-config-create.sh .
RUN sh wp-config-create.sh && rm wp-config-create.sh && chmod -R 0777 wp-content/
Create wp-cli core initializing script and entrypoint script
COPY ./requirements/wordpress/tools/make_wp_core_install_script.sh .
RUN sh make_wp_core_install_script.sh && rm make_wp_core_install_script.sh
Execute preconfiguration script and start php-fpm. CMD
gets actually appended to ENTRYPOINT
, so in fact, container starts with sh entrypoint.sh /usr/sbin/php-fpm82 -F
command
ENTRYPOINT ["sh", "entrypoint.sh"]
CMD ["/usr/sbin/php-fpm82", "-F"]
Create config wp-config-create.sh
Here, the idea is similar to [[README#Script to create DB create_db.sh
]] - but the output with expanded variables redirected to the file wp-config.php
, that will be used to connect to the database, etc..
In ~/project/srcs/requirements/wordpress/conf
#!bin/sh
if [ ! -f "/var/www/wp-config.php" ]; then
cat << EOF > /var/www/wp-config.php
<?php
define( 'DB_NAME', '${DB_NAME}' );
define( 'DB_USER', '${DB_USER}' );
define( 'DB_PASSWORD', '${DB_PASS}' );
define( 'DB_HOST', 'mariadb' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
define('FS_METHOD','direct');
\$table_prefix = 'wp_';
define( 'WP_DEBUG', false );
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );}
define( 'WP_REDIS_HOST', 'redis' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
define( 'WP_REDIS_DATABASE', 0 );
require_once ABSPATH . 'wp-settings.php';
EOF
fi
And here is how wp-config.php
looks after variable expansion:
<?php
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', 'wppass' );
define( 'DB_HOST', 'mariadb' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
define('FS_METHOD','direct');
$table_prefix = 'wp_';
define( 'WP_DEBUG', false );
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );}
define( 'WP_REDIS_HOST', 'redis' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
define( 'WP_REDIS_DATABASE', 0 );
require_once ABSPATH . 'wp-settings.php';
Here's almost the same, but this file is used to generate other two scripts:
wp_core_install
to actually perform creation of 2 users with credentialsentrypoint
to run the first script, then delete it (because it contains user credentials including passwords in unencrypted form) and execute the main service, which will be/usr/sbin/php-fpm82
In ``~/project/srcs/requirements/wordpress/tools`
#!/bin/sh
cat >> wp_core_install.sh << EOF
#!/bin/sh
dom_name='${DOMAIN_NAME}'
localhost='https://localhost:42443'
wp_admin='${WP_ADMIN}'
wp_admin_pass='${WP_ADMIN_PASS}'
wp_admin_mail='${WP_ADMIN_MAIL}'
wp_user='${WP_USER}'
wp_user_pass='${WP_USER_PASS}'
wp_user_mail='${WP_USER_MAIL}'
if ! wp core is-installed; then
wp core install \\
--url="\$localhost" \\ # if you want to access from host machine, or --url="\$dom_name" to access from inside VM
--title="Inception" \\
--admin_user="\$wp_admin" \\
--admin_password="\$wp_admin_pass" \\
--admin_email="\$wp_admin_mail"
wp user create \\
"\$wp_user" \\
"\$wp_user_mail" \\
--user_pass="\$wp_user_pass"
fi
EOF
cat >> entrypoint.sh << EOF
#!/bin/sh
sh wp_core_install.sh
rm wp_core_install.sh
exec "\$@"
EOF
chmod +x wp_core_install.sh entrypoint.sh
The multi-step expansion is needed, because all the sensitive data are defined in the dockerfile as ARG
- which means that it will exist only in the temporary build-time images, and will not be present in the runtime.
However, we need some of this data in the runtime, because user creation operations involve modifying the database, which in the build time won't be up.
So in order to sneak needed date into run-time-init operations the temporary script file wp_core_install.sh
is made. The variables are expanded in the build time into wp_core_install.sh
file, which should be executed right after container's startup and removed before the start of container's main service.
#!/bin/sh
dom_name='https://login.42.fr'
localhost='https://localhost:42443'
wp_admin='wproot'
wp_admin_pass='wprootpass'
wp_admin_mail='[email protected]'
wp_user='login'
wp_user_pass='loginpass'
wp_user_mail='[email protected]'
if ! wp core is-installed; then
wp core install \
--url="$localhost" \
--title="Inception" \
--admin_user="$wp_admin" \
--admin_password="$wp_admin_pass" \
--admin_email="$wp_admin_mail"
wp user create \
"$wp_user" \
"$wp_user_mail" \
--user_pass="$wp_user_pass"
fi
And that's what entrypoint.sh
file is made for. At the container's ENTRYPOINT
, the following script get's executed.
- executes
wp_core_install
- immediately removes it, because data in
wp_core_install
is a security hole - starts container's
CMD
- that get's appended as argument
#!/bin/sh
sh wp_core_install.sh
rm wp_core_install.sh
exec "$@"
In ~/project/srcs/requirements/nginx/
The FROM
instruction specifies the Parent Image from which you are building.
# Normally alpine:latest, but "latest" is forbidden in subject
FROM alpine:3.18.4
The RUN
instruction will execute any commands in a new layer on top of the current image and commit the results. The resulting committed image will be used for the next step in the Dockerfile
.
# Install nginx, "--no-cache" to reduce size
RUN apk update && apk upgrade && apk add --no-cache nginx
EXPOSE 443
The main purpose of a CMD
is to provide defaults for an executing container
# Starting nginx in non-daemon mode, so we can see logs directly in container's tty
CMD ["nginx", "-g", "daemon off;"]
Inside container - config will be mounted to /etc/nginx/http.d/
- the reserved directory that in turn gets included in the main config, created during instalation /etc/nginx/nginx.conf
...
# Includes virtual hosts configs.
include /etc/nginx/http.d/*.conf;
In ~/project/srcs/requirements/nginx/conf
server {
# Listen on port 443 (HTTPS) with SSL enabled
listen 443 ssl;
# Define the server names (domain) for this configuration
server_name login.42.fr www.login.42.fr;
# Set the root directory for the website
root /var/www/;
index index.php;
SSL sessions are used to store the state of a client-server interaction securely.
- After 10 minutes of inactivity, the
ssl_session_timeout
will expire, requiring a new SSL handshake. This helps manage server resources and enhances security. keepalive_timeout
sets the maximum time a connection is kept open between the client and the server. In this case, it's set to 60 seconds. Keeping connections alive reduces the overhead of establishing new connections for subsequent requests from the same client
# Set the SSL certificate file
ssl_certificate /etc/nginx/ssl/login.42.fr.crt;
# Set the SSL certificate key file
ssl_certificate_key /etc/nginx/ssl/login.42.fr.key;
# Define supported SSL/TLS protocols (subj requirement)
ssl_protocols TLSv1.2 TLSv1.3;
# Set the SSL session timeout to 10 minutes
ssl_session_timeout 10m;
# Set the keep-alive timeout for connections
keepalive_timeout 60;
Handles requests when the URI matches the root path - requests made to the main domain or the default path of the website. Such as:
- Direct Access to the Root Path: like https://login.42.fr/
- Requests for Static Files in root directory: like https://login.42.fr/logo.jpg
- Fallback for PHP Processing: If the attempt to serve a static file directly fails, the
try_files $uri /index.php?$args;
line rewrites the request to "/index.php" with any query parameters appended. This directive is essential for WordPress ensuring that pretty permalinks and other features work as expected.
location / {
# Attempt to serve the requested URI directly if fails rewrite the request to "/index.php" with any query parameters ($args) appended.
try_files $uri /index.php?$args;
Instructions needed to prevent users of seeing outdated versions of pages, especially when content changes frequently. These directives are common in configurations for dynamic sites like those powered by WordPress, to ensure that users always receive the latest content.
# Add a Last-Modified header to the response using the $date_gmt variable.
add_header Last-Modified $date_gmt;
# Set the Cache-Control header to 'no-store, no-cache' instructing clients not to store or cache the response.
add_header Cache-Control 'no-store, no-cache';
# Disable the If-Modified-Since header, preventing conditional requests based on modification time.
if_modified_since off;
# Disable the Expires header, indicating that the response should not be cached based on time.
expires off;
# Disables the ETag header, which is another mechanism for cache validation.
etag off;
}
Handles requests ending with .php with forwarding to FastCGI server.
location ~ \.php$ {
# Split the path info into script filename, and the path info.
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Forward the request to a FastCGI server (wordpress port 9000)
fastcgi_pass wordpress:9000;
# Default index file for FastCGI requests.
fastcgi_index index.php;
# Set params to tweak nginx-fastcgi communication
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
In ~/project/srcs
version: '3'
services:
nginx:
build:
context: .
dockerfile: requirements/nginx/Dockerfile
container_name: nginx
depends_on:
- wordpress
ports:
- "443:443"
networks:
- inception
volumes:
- ./requirements/nginx/conf/:/etc/nginx/http.d/
- ./requirements/nginx/tools:/etc/nginx/ssl/
- wp-volume:/var/www/
restart: always
mariadb:
build:
context: .
dockerfile: requirements/mariadb/Dockerfile
args:
DB_NAME: ${DB_NAME}
DB_USER: ${DB_USER}
DB_PASS: ${DB_PASS}
DB_ROOT: ${DB_ROOT}
container_name: mariadb
ports:
- "3306:3306"
networks:
- inception
volumes:
- db-volume:/var/lib/mysql
restart: always
wordpress:
build:
context: .
dockerfile: requirements/wordpress/Dockerfile
args:
DB_NAME: ${DB_NAME}
DB_ROOT: ${DB_ROOT}
DB_USER: ${DB_USER}
DB_PASS: ${DB_PASS}
WP_ADMIN: ${WP_ADMIN}
WP_ADMIN_PASS: ${WP_ADMIN_PASS}
WP_ADMIN_MAIL: ${WP_ADMIN_MAIL}
WP_USER: ${WP_USER}
WP_USER_PASS: ${WP_USER_PASS}
WP_USER_MAIL: ${WP_USER_MAIL}
container_name: wordpress
depends_on:
- mariadb
networks:
- inception
volumes:
- wp-volume:/var/www/
restart: always
volumes:
wp-volume:
driver_opts:
o: bind
type: none
device: /home/${USER}/data/wordpress
db-volume:
driver_opts:
o: bind
type: none
device: /home/${USER}/data/mariadb
networks:
inception:
driver: bridge
In ~/project/srcs/requirements/tools
#!/bin/bash
if [ ! -d "/home/${USER}/data" ]; then
mkdir ~/data
mkdir ~/data/mariadb
mkdir ~/data/wordpress
fi
In ~/project
Container's name
name = inception
all:
Run script to make directories if they don't exist
@bash srcs/requirements/tools/make_dir.sh
Start container that was already built
- -f ./docker-compose.yml: Docker-compose with specified config
- --env-file srcs/.env: Specify an environment file from which Docker Compose will load environment variables by address relative to the current working directory.
- up: start the services defined in the Compose file.
- -d: This flag tells Docker Compose to run the services in detached mode, meaning they run in the background, and you get your terminal prompt back. This is useful for running services without locking up the terminal.
@docker-compose -f ./srcs/docker-compose.yml --env-file srcs/.env up -d
--build: This flag tells Docker Compose to rebuild the images for the services defined in the Compose file, even if they already exist. It ensures that the images are latest based on any changes in project.
build:
@bash srcs/requirements/wordpress/tools/make_dir.sh
@docker-compose -f ./srcs/docker-compose.yml --env-file srcs/.env up -d --build
down: stop the services defined in the Compose file.
down:
@docker-compose -f ./srcs/docker-compose.yml --env-file srcs/.env down
Stop, rebuild and launch
re: down
@docker-compose -f ./srcs/docker-compose.yml --env-file srcs/.env up -d --build
prune -a: is used to remove all stopped containers, all networks not used by at least one container, and all images without at least one container associated with them
clean: down
@docker system prune -a
Remove everything from WP and DB data folders
@sudo rm -rf ~/data/wordpress/*
@sudo rm -rf ~/data/mariadb/*
The command docker stop $$(docker ps -qa)
is a shell command that stops all running Docker containers.
1. docker ps -qa
: This part of the command lists all containers, whether they are running or stopped, and provides their container IDs. The -q
option stands for "quiet," which only displays the numeric IDs of the containers, and the -a
option includes all containers.
2. $$(...)
: The $(...)
syntax is used for command substitution in shell scripts. It allows you to capture the output of the enclosed command and use it as a value. In this case, the command substitution captures the container IDs of all containers, whether running or stopped.
3. docker stop $(...)
: This part of the command uses the output of the command substitution to stop the containers. It essentially runs the docker stop
command with the list of container IDs obtained from the docker ps -qa
command.
Then cleanup all data of Docker, WP and DB
fclean:
@docker stop $$(docker ps -qa)
@docker system prune --all --force --volumes
@docker network prune --force
@docker volume prune --force
@sudo rm -rf ~/data/wordpress/*
@sudo rm -rf ~/data/mariadb/*
Or what will happen when I'll type make
?
- Make runs
docker-compose up
and docker-compose executes instructions - docker-compose loads Dockerfiles of the described services, in order of their interdependency and asks docker to execute them, as well as mount volumes and creating networks
- docker starts with creating described networks, and then goes by all services's Dockerfiles
- docker starts from the Dockerfile for
mariadb
, because nginx depends on wordpress, which in turn depends onmariadb
- docker looks for
FROM
instruction, which tells us what distro image do we need for building up this container - in our case it isalpine:3.18.4
- docker downloads distro image if needed and proceeds with it's configuration as described
- docker loads environement variables via
ENV
orARG
- docker executes Dockerfile
CMD
instruction, that starts service which is a purpose of a particular container - when all containers are up and configured propperly the 443 port of nginx's container get's exposed from docker network to hosting machine (VM) which then forwards it to PC's 42443 port and becomes accessible through https://localhost:42443 via browser
- Nginx loads
index.php
- entry point to the wordpress - nginx receives the HTTP Request initiated by the user's interaction with the webpage. It looks up
nginx.conf
and directs incoming requests to the appropriate location block and sends FastCGI request to PHP-FPM - The
location ~ \.php$
block in nginx config forwards PHP requests to the PHP-FPM service running in the WordPress/PHP container and listens for FastCGI requests on port 9000, as configured innginx.conf
on nginx side andphp-fpm.d/www.conf
on WP/PHP side. - WordPress interprets the PHP scripts associated with the requested action querying the database, generating dynamic content, and preparing the HTTP response.
- WordPress, using the database abstraction layer, performs SQL queries to read or write data in the MariaDB database.
- MariaDB executes the SQL queries received from WordPress, updating the database records accordingly. For instance, when creating a new post, records are inserted into the
wp_posts
table, capturing post content, titles, timestamps, and other metadata. - WordPress generates an HTTP response, which is sent back through the PHP-FPM service and Nginx to the user's browser.
- browser receives the HTTP response and renders the updated webpage based on the changes made through the WordPress interface.