Docker containers are a form of operating system-level virtualization that allow developers to package an application with all of its dependencies into a standardized unit for software development. How Docker Containers Work:
Containers are built from Docker images, which are read-only templates with instructions for creating a Docker container. When a container is run from an image, it adds a read-write layer on top of the image, which is where the application and its dependencies are stored. Containers are isolated from each other and from the host system, ensuring that they do not interfere with each other or with the host system. Docker uses the host system's kernel, which means containers can start much faster than VMs and use fewer resources.
In summary, Docker containers are best for deploying lightweight, scalable applications and microservices, while VMs are better suited for scenarios requiring strong isolation, security, and compatibility with different operating systems. The choice between Docker containers and VMs depends on the specific needs of the application and the environment in which it will be deployed.
MariaDB Server is one of the most popular database servers in the world, known for its performance, stability, and openness. It is part of most cloud offerings and the default in most Linux distributions. It was created by some of the original developers of MySQL, who forked it due to concerns over its acquisition by Oracle Corporation in 2009. MariaDB aims to maintain high compatibility with MySQL, including exact matching with MySQL APIs and commands, allowing it to function as a drop-in replacement for MySQL in many cases. MariaDB is developed as open-source software and provides an SQL interface for accessing data. It is used by notable organizations such as Wikipedia, WordPress.com, and Google, and is included by default in several Linux distributions and BSD operating systems.
FROM alpine:3.18
specifies the base image for the Docker container.RUN apk update && apk upgrade &&\
updates the package index and upgrades all installed packages to their latest versions.apk add mariadb mariadb-client
installs MariaDB and its client.COPY ./conf/configure-mariadb.sh /tmp/configure-mariadb.sh
copies a shell script namedconfigure-mariadb.sh
from theconf
directory in the host machine to the /tmp directory inside the Docker container.RUN chmod +x /tmp/configure-mariadb.sh
makes the script executable.ENTRYPOINT [ "sh", "/tmp/configure-mariadb.sh" ]
sets the entry point of the Docker container to theconfigure-mariadb.sh
script.
This script is designed to automate the process of starting a MariaDB service, creating a database and users with specific privileges, and then stopping the service. Let's break down each line:
#!/bin/sh
specifies the interpreter for the script.echo "[DB config] Configuring MariaDB..."
prints a message indicating the start of the MariaDB configuration process.if [ ! -d "/run/mysqld" ]; then
checks if the directory/run/mysqld
does not exist.echo "[DB config] Granting MariaDB daemon run permissions..."
prints a message indicating that the script is granting permissions to the MariaDB daemon.mkdir -p /run/mysqld
Creates the directory/run/mysqld
if it doesn't already exist.chown -R mysql:mysql /run/mysqld
changes the ownership of the /run/mysqld directory.fi
ends the if statement.if [ -d "/var/lib/mysql/mysql" ]
checks if the directory/var/lib/mysql/mysql
exists, indicating that MariaDB has already been configured.echo "[DB config] MariaDB already configured."
prints a message indicating that MariaDB is already configured.else
if the directory does not exist, the script proceeds with the MariaDB configuration.echo "[DB config] Installing MySQL Data Directory..."
prints a message indicating the start of the MySQL data directory installation.chown -R mysql:mysql /var/lib/mysql
changes the ownership of the/var/lib/mysql
directory and its contents to the mysql user and group.mysql_install_db --basedir=/usr --datadir=/var/lib/mysql --user=mysql --rpm > /dev/null
installs the MySQL data directory. The--basedir
option specifies the base directory of the MariaDB installation,--datadir
specifies the data directory,--user
specifies the user to run the command as, and--rpm
is used for compatibility with RPM-based systems. The output is redirected to/dev/null
to suppress it.echo "[DB config] MySQL Data Directory done."
prints a message indicating that the MySQL data directory installation is complete.echo "[DB config] Configuring MySQL..."
prints a message indicating the start of the MySQL configuration.TMP=/tmp/.tmpfile
sets a temporary file path for the MySQL configuration commands./usr/bin/mysqld --user=mysql --bootstrap < ${TMP}
starts the MariaDB server in bootstrap mode, executing the SQL commands from the temporary file.rm -f ${TMP}
removes the temporary file after it has been used.echo "[DB config] MySQL configuration done."
prints a message indicating that the MySQL configuration is complete.
fi
ends the if statement.echo "[DB config] Allowing remote connections to MariaDB"
prints a message indicating that the script is configuring MariaDB to allow remote connections.echo "[DB config] Starting MariaDB daemon on port 3306."
prints a message indicating that the MariaDB daemon is being started.exec /usr/bin/mysqld --user=mysql --console
starts the MariaDB server in the foreground, allowing it to receive signals from Docker.
Now, you can build your container and tests it. Inside the folder mariadb, run the following command. build is the command to build the image, and -t is the tag name and mariadb is the name and . indicates that the Dockerfile is in the current folder.
docker build -t mariadb .
Then, run the container with the following command. run is the command to run the container, -d is the flag to run the container in background, and mariadb is the name of the image that we want to run.
docker run -d mariadb
docker ps -a
With the ID copied, run the next command to get inside the container. exec is the command to execute a command inside the container, -it is the flag to run the command in interactive mode, and ID is the ID of the container and /bin/bash is the command that we want to execute, in this case we want to use its terminal.
docker exec -it $ID /bin/bash
mysql -u $DB_USER -p $DB_NAME
if you see the the prompt MariaDB [$DB_NAME]>
it means that all is ok. Too see the tables, run the following command. For now, we don't have any table, so it'll return an empty set, But at the end of the project, it'll have some tables created by wordpress.
SHOW TABLES;
Now, to exit mysql, run exit then run exit again to exit the container. So it's all working, then we'll clean our container test. To stop the container, remove it and the image run the following commands.
docker rm -f $(docker ps -aq) && docker rmi -f $(docker images -aq)
WordPress is a free, open-source content management system (CMS) that allows users to create and manage websites and blogs without needing to know how to code. It powers over 42.7% of all websites on the internet, making it the most popular CMS available. The platform is highly customizable, with thousands of themes and plugins available to extend its functionality. WordPress is known for its simplicity, making it accessible even to beginners. It allows for quick publishing and building of website content, and it's extendable with plugins for added features. The platform is also highly secure, with a vigilant security team and a community that continuously works on improving its security.
FROM alpine:3.18
specifies the base image for the Docker container.RUN apk update && apk upgrade &&\
updates the package index and upgrades all installed packages to their latest versions.apk add php81 php81-fpm php81-bcmath php81-bz2 php81-calendar php81-cli php81-ctype \ apk add mariadb-client
installs the PHP8.1 and a variety of extension necessary for Wordpress.RUN sed -i 's/listen = 127.0.0.1:9000/listen = 9000/g' /etc/php81/php-fpm.d/www.conf
modifies the PHP-FPM configuration to listen on all interfaces.RUN apk add curl && curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && mv wp-cli.phar /usr/bin/wp-cli.phar
installcurl
and downloads and move the WP-CLI tools.COPY ./conf/configure-wordpress.sh /tmp/configure-wordpress.sh
copies a shell script namedconfigure-wordpress.sh
from the conf directory in the host machine to the/tmp
directory inside the Docker container.RUN chmod +x /tmp/configure-wordpress.sh
makes the script executable.WORKDIR /var/www/html/wordpress
sets the working directory inside the container.ENTRYPOINT [ "sh", "/tmp/configure-wordpress.sh" ]
sets the entry point of the Docker container to theconfigure-wordpress.sh
script.
This script is designed to automate the setup of a WordPress site using WP-CLI, a command-line interface for WordPress.
#!/bin/sh
cspecifies the interpreter for the script.echo "[WP config] Configuring WordPress..."
prints a message indicating the start of the WordPress configuration process.echo "[WP config] Waiting for MariaDB..."
prints a message indicating that the script is waiting for MariaDB to become accessible.while ! mariadb -h${DB_HOST} -u${WP_DB_USER} -p${WP_DB_PASS} ${WP_DB_NAME} &>/dev/null; do
starts a loop that attempts to connect to the MariaDB database using the provided host, user, password, and database name. The loop continues until a successful connection is made. The output of the command is redirected to/dev/null
to suppress it.sleep 3
pauses the script for 3 seconds before attempting to connect to MariaDB again.done
ends the loop.echo "[WP config] MariaDB accessible."
prints a message indicating that MariaDB is accessible.WP_PATH=/var/www/html/wordpress
sets the path where WordPress files will be located.if [ -f ${WP_PATH}/wp-config.php ]
checks if the wp-config.php file exists in the specified WordPress path..echo "[WP config] WordPress already configured."
prints a message indicating that WordPress is already configured.echo "[WP config] Setting up WordPress..."
prints a message indicating the start of the WordPress setup process.wp-cli.phar cli update --yes --allow-root
updates WP-CLI to the latest version. The--yes
flag automatically confirms the update, and--allow-root
allows the command to be run as the root user.wp-cli.phar core download --allow-root
downloads the latest version of WordPress.echo "[WP config] Creating wp-config.php..."
prints a message indicating that thewp-config.php
file is being created.wp-cli.phar config create --dbname=${WP_DB_NAME} --dbuser=${WP_DB_USER} --dbpass=${WP_DB_PASS} --dbhost=${DB_HOST} --path=${WP_PATH} --allow-root
creates thewp-config.php
file with the specified database name, user, password, host, and path.wp-cli.phar core install --url=${NGINX_HOST}/wordpress --title=${WP_TITLE} --admin_user=${WP_ADMIN_USER} --admin_password=${WP_ADMIN_PASS} --admin_email=${WP_ADMIN_EMAIL} --path=${WP_PATH} --allow-root
installs the WordPress core with the specified URL, title, admin user, password, email, and path.p-cli.phar theme install blocksy --path=${WP_PATH} --activate --allow-root
installs and activates the Blocksy theme for WordPress.wp-cli.phar theme status blocksy --allow-root
checks the status of the Blocksy theme.fi
ends the if statement.echo "[WP config] Starting WordPress fastCGI on port 9000."
prints a message indicating that the WordPress FastCGI process is being started.exec /usr/sbin/php-fpm81 -F -R
starts the PHP FastCGI Process Manager (FPM) for PHP 8.1 in the foreground.
Go to the wordpress folder and run the following command.
docker build -t wordpress .
docker run -d wordpress
docker ps -a
docker exec -it copiedID /bin/bash
Now, you are inside the container. Run the following command to check if the wordpress files are there. The sleep here is used to give time to the container to download the files.
sleep 30 && ls /var/www/inception/
If you see the wordpress files, it means that all is ok. Exits the container and let's clean our container test.
docker rm -f $(docker ps -aq) && docker rmi -f $(docker images -aq)
FROM alpine:3.18
specifies the base image for the Docker container.RUN apk update && apk upgrade && apk add nginx &&
updates and upgrade the package index to their latest versions, then install Nginx a popular web server.mkdir -p /var/www/html/
creates the directory/var/www/html/
COPY ./conf/nginx.conf /etc/nginx/nginx.conf
copies a custom NGINX configuration file from theconf
directory in the host machine to the/etc/nginx
directory inside the Docker container. This allows for customizing the NGINX configuration.COPY ./conf/default.conf /etc/nginx/http.d/default.conf
copies a custom default server block configuration file from theconf
directory in the host machine to the/etc/nginx/http.d
directory inside the Docker container. This allows for customizing the default server block.RUN apk add openssl && openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt -subj "/C=FR/ST=Normandie/L=LeHavre/O=42Network/OU=42LeHavre/CN=inception"
installs OpenSSL and generate a self-signed certificate.RUN adduser -D -g 'www' www && chown -R www:www /run/nginx/ && chown -R www:www /var/www/html/
creates a new user named www with the group www and changes the ownership of the directories/run/nginx/
&&/var/www/html/
EXPOSE 443/tcp
sets the entry point of the Docker container to the nginx command.CMD ["-g", "daemon off;"]
sets the default command to be executed when the container starts.
This Nginx configuration document is designed to set up a secure and efficient web server for a specific website, ensuring that it only accepts HTTPS connections and uses TLSv1.2 for encryption. Here's a line-by-line explanation of what each part of the document does:
listen 443 ssl;
&&listen [::]:443 ssl
are telling Nginx to listen on port 443 (the standard port for HTTPS traffic) for both IPV4 and IPV6 addresses.server_name
routes request to this server block based on theHost
header of the incoming request.root /var/www/inception/;
&&index index.php index.html;
sets the root directory for the website and specifies the default files to serve when a directory is requested.ssl_protocols TLSv1.2
specifies that only TSLv1.2 should be used for the SSL connection. TLS 1.2, or Transport Layer Security version 1.2, is a cryptographic protocol designed to provide secure communication over a network. It is the successor to SSL (Secure Sockets Layer) and its use in securing web traffic, email, and other online services helps protect sensitive information from eavesdropping and unauthorized access.We both the 1.2 and 1.3 versions due to compatibility reasons, since not all servers and browsers support TSLv1.3location /
block defines how to handle requests for the root directory and all subdirectories.fastcgi_split_path_info ^(.+\.php)(/.+)$;
splits the request URI into the script name and the path info.fastcgi_pass wordpress:9000;
passes PHP requests to a FastCGI server listening on wordpress:9000, which is typically a PHP-FPM service.fastcgi_index index.php;
specifies the default file to serve when a directory is requested.include fastcgi_params;
includes the FastCGI parameters file, which contains common FastCGI parameters.fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
sets theSCRIPT_FILENAME
parameter to the full path of the script to be executed.fastcgi_param PATH_INFO $fastcgi_path_info;
sets thePATH_INFO
parameter to the path info extracted from the request URI.fastcgi_intercept_errors off;
disables intercepting of FastCGI errors by NGINX.fastcgi_buffer_size 16k;
sets the buffer size for reading the response from the FastCGI server.fastcgi_buffers 16 32k;
sets the number and size of the buffers for reading the response from the FastCGI server.
fastcgi_connect_timeout 120;
sets the timeout for establishing a connection with the FastCGI server.fastcgi_send_timeout 120;
sets the timeout for sending a request to the FastCGI server.fastcgi_read_timeout 120;
sets the timeout for reading a response from the FastCGI server.}
ends the location block for PHP files.location / {
starts a location block that matches all requests.autoindex on;
enables directory listing.try_files $uri $uri/ =404;
tries to serve the requested URI as a file, then as a directory, and finally returns a 404 error if neither is found.}
ends the location block for all requests and the server block.
This Nginx configuration file is designed to set up a web server with specific settings for handling requests, logging, and including additional configuration files. Here's a line-by-line explanation:
user www;
sets the user that NGINX will run as to www. This is a security measure to limit the permissions of the NGINX process.worker_processes auto;
automatically sets the number of worker processes to the number of CPU cores available. This is important for handling multiple connections efficiently.pcre_jit on;
enables the Just-In-Time (JIT) compilation for Perl Compatible Regular Expressions (PCRE), which can improve performance for regular expression matching.error_log /var/log/nginx/error.log warn;
configures the error log file and sets the log level to warn. This means that only warnings and errors will be logged.include /etc/nginx/modules/*.conf;
includes additional configuration files for NGINX modules. This allows for modular configuration and easier management of different settings.events {
starts the events block, which contains directives related to handling connections.worker_connections 1024;
sets the maximum number of simultaneous connections that each worker process can handle.}
ends the events block.http {
starts the http block, which contains directives related to HTTP and HTTPS traffic.include /etc/nginx/mime.types;
includes the MIME types file, which maps file extensions to their MIME types.default_type application/octet-stream;
sets the default MIME type for responses that do not match any file extension in the mime.types file.server_tokens off;
disables the display of the NGINX version number in error messages. This is a security measure to hide version information.client_max_body_size 1m;
sets the maximum allowed size of the client request body to 1 megabyte.sendfile on;
enables the use ofsendfile()
for sending files to the client.tcp_nopush on;
enables the TCP_NOPUSH optionssl_protocols TLSv1.2 TLSv1.3;
specifies the SSL protocols to be used.ssl_prefer_server_ciphers on;
configures NGINX to prefer the server's cipher suite over the client's when negotiating an SSL connection.ssl_session_cache shared:SSL:2m;
configures the SSL session cache. This can improve performance by reusing SSL sessions.ssl_session_timeout 1h;
sets the SSL session timeout to 1 hour. This determines how long an SSL session can be reused.
ssl_session_tickets off;
disables the use of SSL session tickets. This can improve security by preventing session resumption attacks.gzip_vary on;
enables theVary: Accept-Encoding
response header when the gzip module is enabled.map $http_upgrade $connection_upgrade {
starts a map block that sets the$connection_upgrade
variable based on the $http_upgrade variable. This is used for handling WebSocket connections.default upgrade;
the default action is to upgrade the connection.'' close;
if the$http_upgrade
variable is empty, the connection is closed.}
ends the map block.log_format main '$remote_addr - $remote_user [$time_local] "$request" '
defines a custom log format named main.'$status $body_bytes_sent "$http_referer" '
continues the log format to include the response status, the number of bytes sent, and the referrer.'"$http_user_agent" "$http_x_forwarded_for"';
completes the log format to include the user agent and the X-Forwarded-For header.access_log /var/log/nginx/access.log main;
configures the access log file and uses the main log format.include /etc/nginx/http.d/*.conf;
includes additional configuration files for HTTP server blocks. This allows for modular configuration and easier management of different server blocks.}
ends the http block.
Go to the nginx folder and run the following command. We don't run the container because it need to connect with the wordpress container. And we'll do it with the compose file. We'll the built command only to check if the image is ok then we'll remove it.
docker build -t nginx .
docker images
docker rmi -f nginx
Docker Compose is a tool designed to simplify the definition and management of multi-container Docker applications. It allows developers to define their application's services, networks, and volumes in a single YAML file, making it easier to manage and replicate the application environment.
The Docker Compose configuration provided outlines a multi-container application setup that includes MariaDB, WordPress, and Nginx containers, along with volume and network configurations.
- MariaDB Container This is the only container that no depend on the others, so its the first to be created. Its fields are self-explanatory. Build is where the Dockerfile is, volumes is where the database files will be saved in the container, networks is the network that the container will use, <init is used to run the setup.sh script, restart is used to restart the container if it fails, and env_file is the file that contains the variables that will be used in the container.
- The wordpress service is similar to the mariadb service, but it has a depends_on field that indicates that the wordpress container will only start after the mariadb container is running and volume and the build path are different.
- The NGINX service depends on the wordpress service and has a ports field that indicates that the container will be listening on port
443
. The build field beyond the path, it has some arguments that will be used in the Dockerfile given by the .env file. - The volumes define the local host folder that will be used to save the database and the wordpress files. This volumes will work like a shared folder between the host and the containers.
- The networks define the network that the containers will use to communicate with each other. This is like a virtual switch that will connect the containers.
This file will hold every variables we'll use in the docker-compose file as credential.
Simply run the makefile.
The script
rules is a shell script snippet that checks if a domain (represented by the variable $DOMAIN
) is already present in the /etc/hosts
file. If the domain is not found, it appends the domain with the IP address 127.0.0.1
to the /etc/hosts
file.
This script is useful for accessing a local web server using a domain name instead of an IP address.
- Download debian image. Try this link
- Open the VirtualBox and create a new VM as Linux Debian 64 bits.
- Set the RAM to 4096 MB
- Create a dynamic VDI with at least 30 GB
- Go to the VM settings > System > Motherboard and set the boot order to Optical, Hard Disk, Network.
- Then at processor tab, set the number of processors to 4.
- In the display menu, set the video memory to 128 MB.
- In the audio menu, disable the audio.
- In the network menu, set the network to NAT.
- In the storage, select the CD icon and select the debian image that you downloaded.
- Now start your VM.
- Select install
- then follow the normal installation steps, choosing region, user, password, etc. Nothing special here.
- In the partition menu, select the guided - use entire disk - LVM
- After that, select separate var/ tmp/ home/ partitions and Confirm it.
- In the software selection, select only XFCE, Webserver, SSH server and standard system utilities.
- In the GRUB menu, select yes and select the disk that you created.
- At the end, your VM will reboot with the debian installed.
Log in as root. Access the sudoers file via nano /etc/sudoers
, then add the user after the root. Now, reboot the VM.
- In your main PC, create a folder in your home directory called shared . This folder will be used to share files between your main PC and the VM.
- In the VirtualBox settings > Shared Folders, add a new shared folder with the name shared and the path to the folder that you created in your main PC and check the auto-mount and make permanent options.
- Now, in the VM, at the VirtualBox menu > Devices > select insert Guest Additions CD image.
- Open the terminal in the CD folder and run the following command.
sudo sh VBoxLinuxAdditions.run \sudo reboot
- Add your user to the vboxsf group and define your user as owner of the shared folder.
sudo usermod -a -G vboxsf your_user \sudo chown -R your_user:users /media/
- Logout and login again to apply the changes. Now, you can see the shared folder in the
/media
folder as a external device.
Prepare the docker repository installation
//Add Docker's official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg
//Add the repository to Apt sources: echo
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian
"$(. /etc/os-release && echo "bullseye")" stable" |
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update
Then install docker and plugins
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Now add your user to the docker group. It's important use the docker commands without sudo.
sudo usermod -aG docker your_user su - your_user sudo reboot
Now check if the docker is working well with the following command:
docker run hello-world
sudo apt-get install -y make hostsed
Et voila! Everything should be just fine.
Differences between a VM and a docker containers
Configure MariaDB with Option files
A guide to Create an User in MariaDB
How to Grant Privileges to an User
Hostsed and the 3 hours quests to find out what this fucking does