Latest commit

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Inception

This project aims to deepen the knowledge of system administration.

Use of dockerfile for the creation and management of custom images, micro services.

Use of docker-compose for the deployment of containers, the creation and management of the network, storage space, etc ...

Table of contents

Local Domains in Linux

Structure of the project with the bonuses

DOCKER

BASIC DOCKER COMMANDS

  • docker ps -a : List active containers (-a is for showing all containers, running and stopped)
  • docker stop <id>/<name> : Stop running containers
  • docker start <id>/<name> : Start stopped containers
  • docker rm -f <id>/<name> : Remove containers (-f is for force the removal of a running container)
  • docker exec -it <name> bash : Execute a command in a running container

Tips to delete all containers, use: docker rm -f $(docker ps -qa)

DOCKER RUN

$ docker run [OPTIONS] IMAGE[:TAG]
ParametersDescription
-dRun container in background (daemon mode)
-itcreating an interactive container
-pPublish a container port(s) to the host
--rmAutomatically remove the container when it exits
--hostnameContainer host name
--nameAssign a name to the container

Exemple

$ docker run -d -ti -p 80:80 --rm --name web-ngnix --hostname nginx-container nginx:latest

use docker ps to list running containers

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86335dfeaa0b nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web-ngnix

We can see that the container is running in daemon mode. That the exposure of the ports is well done and that the name of the container is the one that we specified in parameter

$ docker exec -it web-ngnix bash

The docker exec command runs a new command in a running container.

$ root@nginx-container:/#

We can now see that the name specified in --hostname is applied

DOCKER VOLUMES

The advantages of volumes :

  • Easy to persist data.
  • Convenient for making backups
  • Share data between multiple containers
  • Multi-containers and permissions

Basic command for managed volumes :

  • docker volume ls : list volumes

  • docker volume create <name> : creating a new volume

  • docker volume rm <name> : delete a volume

  • docker volume inspect <name> : inspection of a volume

The different types of volumes :

  • Bind Mount : Bind mounts are dependent on the directory structure and OS of the host machine
  • Volumes Docker : volumes are completely managed by Docker
  • TMPFS : As opposed to volumes and bind mounts, a tmpfs mount is temporary, and only persisted in the host memory. When the container stops, the tmpfs mount is removed, and files written there won’t be persisted.

DOCKER RUN WITH VOLUMES

1. Bind Mount :

sudo mkdir /data (creation of mount folder is necessary otherwise error will appear when using docker run)

docker run -d --name TestBindMount --mount type=bind,source=/data/,target=/usr/share/nginx/html -p 80:80 nginx:latest

docker exec -ti TestBindMount bash

2. Volumes Docker :

docker volume create mynginx (optional because if the volume is not created, docker will do it)

docker run -d --name TestVolume --mount type=volume,src=mynginx,destination=/usr/share/nginx/html -p 81:80 nginx:latest

docker exec -ti TestVolume bash

3. Tmpfs:

docker run -d --name TestTmpfs --mount type=tmpfs,destination=/usr/share/nginx/html -p 82:80 nginx:latest

docker exec -ti TestTmpfs bash

To check data persistence you can delete all containers and recreate them !! (do not recreate the volumes)

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0096643b045 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->80/tcp, :::82->80/tcp TestTmpfs
92260c1f5880 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:81->80/tcp, :::81->80/tcp TestVolume
dcad272f7531 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp TestBindMount

In each container modify/create the /usr/share/nginx/html/index.html, Remove containers and recreate. Now check if the changes have been saved.

If you are running docker on your OS. You can admire the changes from your websites.

ENVIRONEMENT VARIABLE (ENV, ENVFILE...)

$ docker run -tid --name testenv --env MYVAR="123" debian:latest

Add to the docker environment the variable MYVAR=123

$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@cb9e44034297:/# env
HOSTNAME=cb9e44034297
MYVAR=123
PWD=/
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

This method works but is not secure for example for passwords. To do this we will be able to add an env file ".ENV"

To do this, we will create a ".ENV" file in which we will put our environment variables. "vim .ENV"

MYPASSWORD="safepassword"
MYUSER="secretuser"
MYDB="BDD1"
$ docker run -tid --name testenv --env-file .ENV debian:latest
$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@553c2ac8a657:/# env
HOSTNAME=553c2ac8a657
PWD=/
HOME=/root
MYPASSWORD="safepassword"
TERM=xterm
SHLVL=1
MYUSER="secretuser"
MYDB="BDD1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

DOCKER NETWORK

  • Communication between containers or outside
  • Different types : bridge, host, none, overlay
  • Be careful, a container does not have a fixed IP address (stop / start)

Basic command for managed network :

  • docker network ls : List networks

  • docker network create <name> : Create a network

  • docker network rm <name> : Remove one or more networks

  • docker network inspect <name> : Display detailed information on one or more networks

IPs are not static

In general, IPs in a network are not static.

The addressing of the Ips depends on the starting order of the containers.

Exemple

Create bridge network with name, mynetwork :

$ docker network create --driver=bridge mynetwork

Start two container connect to network "mynetwork"

$ docker run -d --name c1 --network mynetwork nginx:latest
$ docker run -d --name c2 --network mynetwork nginx:latest

Container 1 will have as ip address : 172.26.0.2

$ docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2 

Container 2 will have as ip address : 172.26.0.3

$ docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3

We will now reverse the boot order

sudo docker stop c1
sudo docker stop c2
### reverse containers start order ###
sudo docker start c2
sudo docker start c1

We can see that the ip addresses are no longer the same

docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3
docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2

If the ips change, how do the containers communicate ?

The containers will have to communicate with their name which redirects to the ip.

sudo docker exec -ti c1 bash root@54bb6caca8fb:/# apt update && apt install iputils-ping -y
### ping install ###
root@54bb6caca8fb:/# ping c2
PING c2 (172.26.0.2) 56(84) bytes of data.
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=2 ttl=64 time=0.204 ms

It will therefore be necessary to use the name of the containers, in our different configurations, applications, programs to communicate. Container names are used as domain names.

DOCKERFILE

Dockerfile is a configuration file for the purpose of creating an image

Dockerfile benefit

  • Restart an image creation at any time
  • Better configuration visibility
  • Dockerfile editing script
  • Image creation, production or development

Instructions Dockerfile

β–‘InstructionsDescription
1FROMNew build stage and sets the Base Image for subsequent instructions.
2MAINTAINERauthor
3ARGDefines a variable that users can pass when building the image
4ENVEnvironment variable
4LABELAdding metadata
5VOLUMECreate a mount point
6RUNExecute a command when creating the image
6COPY // ADDAdd a file and directory in the image
6WORKDIRAllows you to change the current path
7EXPOSEPort listened by the container (metadata)
9CMD // ENTRYPOINTExecute a command when the container starts

BUILD A IMAGE

We will now create a mariadb image

Here are the different files we need to build the image

$ tree
.
β”œβ”€β”€ 50-server.cnf # Mariadb configuration file
β”œβ”€β”€ Dockerfile # The dockerfile to build the image
└── script.sh # Database configuration script

Dockerfile

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update
RUN apt install -y mariadb-server # COPY THE CONF FOR THE BIND AND THE SQL SCRIPT FOR THE PRIVILEGECOPY 50-server.cnf /etc/mysql/mariadb.conf.d/
# COPY THE SCRIPT IN THE IMAGES AND MODIFY THE EXECUTION RIGHTS OF ITCOPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT [ "/script.sh" ]

By default, the server does not accept external connections, or rather, it only accepts local connections (from the LoopBack address: localhost = 127.0.0.1). We need change that !

50-server.cnf

[server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
# bind-address = 127.0.0.1 # You need to change this line to allow external connectionsbind-address = 0.0.0.0 # Now it's better :-)expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb][mariadb-10.5]

Script.sh will be executed at entrypoint at runtime. this allow us to initialize the environment variables with an .envfile

script.sh

#!/bin/sh
service mysql start # CREATE USER #echo"CREATE USER '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
# PRIVILGES FOR ROOT AND USER FOR ALL IP ADRESS #echo"GRANT ALL PRIVILEGES ON *.* TO '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
echo"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '$BDD_ROOT_PASSWORD';"| mysql
echo"FLUSH PRIVILEGES;"| mysql
# CREAT WORDPRESS DATABASE #echo"CREATE DATABASE $BDD_NAME;"| mysql
kill$(cat /var/run/mysqld/mysqld.pid)
mysqld

DOCKER BUILD :

$ docker build -t my-mariadb . ......
......
Successfully built 6ad0c955aa67
Successfully tagged my-mariadb:latest πŸ‘

For this example, we'll change to \home and run my-mariadb image with an environment file.

$ cd /home

Create .env file in which username, user, password, database name, root password.

This information will be embedded in the container at runtime.

$ vim .env
BDD_USER=user
BDD_USER_PASSWORD=safepwd
BDD_NAME=wordpress
BDD_ROOT_PASSWORD=safepwdroot

To run the image you will need a specific env file and image name

$ docker run -tid --name testmariadb --env-file .env my-mariadb

The container is well executed, we can check with a docker ps

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e058b2f18f my-mariadb "/script.sh" 22 seconds ago Up 22 seconds testmariadb

Enter the container to check if our variables have integrated

$ docker exec -ti testmariadb bash root@34e058b2f18f:/# 

Everything is good 🀩

root@34e058b2f18f:/# env
HOSTNAME=34e058b2f18f
PWD=/
BDD_NAME=wordpress
HOME=/root
BDD_USER_PASSWORD=safepwd
TERM=xterm
SHLVL=1
BDD_ROOT_PASSWORD=safepwdroot
BDD_USER=user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

Check if the conf file has been copied

root@34e058b2f18f:/# cat /etc/mysql/mariadb.conf.d/50-server.cnf [server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
bind-address = 0.0.0.0
expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb]

Let's start mysql to check users and database

root@34e058b2f18f:/# mysql Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.38-MariaDB-0+deb10u1 Debian 10
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Check if our user and root is enabled for any host

MariaDB [(none)]>SELECT user,host,password FROMmysql.user;
+------+-----------+-------------------------------------------+
| user | host | password |
+------+-----------+-------------------------------------------+
| root | localhost | |
| user | % | *1C848575FF465642717BE88F2015E168769A62F3 |
| root | % | *FDB22E6F75BD75009DEE947AFD0BD73CB7EB88DA |
+------+-----------+-------------------------------------------+3 rows inset (0.005 sec)

Check if the "wordpress" database has been created

MariaDB [(none)]> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| wordpress |
+--------------------+4 rows inset (0.005 sec)

Starter Pack [ MariaDB - Adminer ]

In the previous part we saw how to write a dockerfile and build the image using docker build

In this part we will see how to use docker compose and write a docker-compose.yml

But first, we will see the configuration and the creation of the dockerfile for Adminer.

Adminer is a tool for managing content in databases. It natively supports MySQL, MariaDB, PostgreSQL, SQLite,

Once installed, we will be able to connect to our database from the Web Adminer interface 😎

Dockerfile (Adminer)

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update RUN apt install -y adminer # COPY THE CONF FILE COPY 000-default.conf /etc/apache2/sites-available/
RUN echo 'ServerName adminer' >> /etc/apache2/apache2.conf
# START AND CONF RUN service apache2 start && a2enconf adminer.conf ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

000-default.conf (Adminer)

<VirtualHost *:80>
DocumentRoot /etc/adminer
Alias /adminer /etc/adminer
<Directory /etc/adminer>
Require all granted
DirectoryIndex conf.php
</Directory> ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

DOCKER-COMPOSE

What is Docker Compose?

Docker Compose is a tool that was developed to help define and share multi-container applications.

With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

BASIC DOCKER COMMANDS

  • docker-compose build : To build the images
  • docker-compose up -d : To run containers in daemon mode
  • docker-compose up --build -d : To build images and run containers in daemon mode {my favorite :-)}
  • docker-compose start/stop : To start and stop services
  • docker-compose down : To stop and delete containers

It is important that the project structure is consistent with the dockerfiles and docker-compose.yml

$ tree .
β”œβ”€β”€ adminer_directory
β”‚ β”œβ”€β”€ 000-default.conf β”‚ └── Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ .env # same .env as before 
β”œβ”€β”€ mariadb_directory
β”‚ β”œβ”€β”€ 50-server.cnf # Same file seen above
β”‚ β”œβ”€β”€ Dockerfile # Same file seen above
β”‚ └── script.sh # Same file seen above
└── my_volume. # Persistent volume

docker-compose.yml

version: '3.5'services:adminer:container_name: Adminer # Name redirect to IP -> 172.X.X.Zbuild: adminer_directory/. # Build the dockerfile in ./adminer_directory/Dockerfile restart: always # Restart the container if it has stoppedports:- "80:80" # Redirect port 80 of Adminer on the hostnetworks:- mynetwork # Use mynetwork for communicate with mariadbmariadb:container_name: Mariadbbuild: mariadb_directory/.restart: alwaysnetworks:- mynetworkvolumes:- db:/var/lib/mysqlenv_file: .env# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge # Remember the different types of Networks, I showed you before ???# VOLUMEvolumes:db:driver: localdriver_opts: # Options specific to the drivertype: 'none'o: 'bind'device: ./my_volume # Persistent volume

The docker-compose.yml is edited.

The various essential elements of the infrastructure being positioned in the right place.

We will be able to launch our infrastructure using the command : docker-compose up --build -d .

This will build and then launch the images.

$ docker-compose up --build -d
....
....
Creating Mariadb ... done
Creating Adminer ... done
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b1e14853a6e mdb-adm_adminer "/usr/sbin/apache2ct…" 1 minutes ago Up 1 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp Adminer
4cb7c3cb88f8 mdb-adm_mariadb "/script.sh" 1 minutes ago Up 1 minutes Mariadb

The launch of our containers went well.

We will be able to connect to our database through the Adminer web interface using the host address.

For my part, the address of my host is 192.168.64.13, because i work remotely on a vm.

Most likely your host address is localhost or 127.0.0.1.

Adminer will ask us for the connection information.

This information corresponds to the information present in the ".env" file

The server address to enter is Mariadb

USERNAME = user
PASSWORD = safepwd
DATABASE = wordpress

Great the connection works πŸ‘πŸΌ

You can also log in as root. You just have to put in "root" in user and the password present in the env file.

PHP-FPM & NGNIX

In this part we will create a simple infrastructure allowing to separate nginx and php.

We will then use this same infrastructure to implement the SSL certificate and communicate only on port 443 to connect to our web server.

$ tree
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ nginx
β”‚ β”œβ”€β”€ conf
β”‚ β”‚ └── default
β”‚ └── Dockerfile
└── wordpress
β”œβ”€β”€ conf
β”‚ β”œβ”€β”€ index.php
β”‚ └── www.conf
└── Dockerfile

To work, nginx and php need to have access to the same file.

This is why our "wordress" volume is common to both containers.

Both will share the folder /var/www/html

docker-compose.yml

version: '3.5'services:ngnix:container_name: ngnixbuild: ./nginx/restart: alwaysvolumes:- WordPress:/var/www/htmldepends_on:- wordpressports:- "80:80"networks:- mynetworkwordpress:container_name: wordpressbuild: ./wordpress/restart: alwaysvolumes:- WordPress:/var/www/htmlnetworks:- mynetwork# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge# VOLUMEvolumes:WordPress:driver: localdriver_opts:type: 'none'o: 'bind'device: /home/tliot/data/website

Installing NGINX

Dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# NGINX INSTALLATIONRUN apt-get install -y nginx
# Copy of default web page configurationCOPY ./conf/default /etc/nginx/sites-available/default
ENTRYPOINT ["nginx", "-g", "daemon off;"]

default

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # <------------ Redirect to wordpress container
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Installing PHP-FPM

dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# UDPATE & INSTALLATIONRUN apt install php-fpm -y
# To create the PID file (/run/php/php7.3-fpm.pid)RUN mkdir /run/php
# To allow external connectionsCOPY ./conf/www.conf /etc/php/7.3/fpm/pool.d/
# To create index.php COPY ./conf/index.php /var/www/html/wordpress/index.php
# Is optional, just a metadataEXPOSE 9000 ENTRYPOINT ["/usr/sbin/php-fpm7.3","-F" ]

index.php

<? php echo phpinfo(); ?>

www.conf

[www]
user = www-data
group = www-data
# listen = 127.0.0.1:9000 # Change this line
listen = 9000 # Now it's better
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

Connecting NGINX

Local Domains in Linux

Configure DNS Locally Using /etc/hosts File in Linux

Now open the /etc/hosts file using your editor of choice as follows

sudo vi /etc/hosts

Then add the lines below to the end of the file as shown in the screen shot below.

127.0.0.1 localhost
255.255.255.255	broadcasthost
::1 localhost
192.168.64.13	tliot.42.fr # <--- Principal Domains
192.168.64.13	adminer.tliot.42.fr # <--- adminer subdomain (optional)
192.168.64.13	*.tliot.42.fr # <--- all subdomain (optional)

Next, test if everything is working well as expected, using the ping command.

$ ping tliot.42.fr
PING tliot.42.fr (192.168.64.13): 56 data bytes
64 bytes from 192.168.64.13: icmp_seq=0 ttl=64 time=1.919 ms
64 bytes from 192.168.64.13: icmp_seq=1 ttl=64 time=2.046 ms
64 bytes from 192.168.64.13: icmp_seq=2 ttl=64 time=2.391 ms
64 bytes from 192.168.64.13: icmp_seq=3 ttl=64 time=2.017 ms
64 bytes from 192.168.64.13: icmp_seq=4 ttl=64 time=2.481 ms
^C
--- tliot.42.fr ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss

Setup a self-signed SSL certificate

Create the self-signed SSL certificate:

RUN 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=Ile-de-France/L=Paris/O=42/OU=42Paris/CN=TLIOT/UID=TTT'

Create a new configuration snippet file for Nginx:

RUN echo "ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;\nssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;" > /etc/nginx/snippets/self-signed.conf

Create a strong Diffie-Hellman group:

RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create a configuration snippet with strong encryption settings:

COPY ./conf/ssl-params.conf /etc/nginx/snippets/

ssl-params.conf

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Configure Nginx site to use certificate:

server {
listen 443 ssl default_server; <--- 80 to 443
listen [::]:443 ssl default_server; <--- 80 to 443
server_name tliot.42.fr; <--- _ to tliot.42.fr
# ssl include snippets/self-signed.conf; <--- self-signed SSL
include snippets/ssl-params.conf; <--- strong encryption
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Configure docker-compose.yml site to use 443:

docker-compose.yml

 ngnix:
container_name: ngnix
build: ./nginx/
restart: always
volumes:
- WordPress:/var/www/html
depends_on:
- wordpress
ports:
- "443:443" <--- 80:80 to 443:443
networks:
- mynetwork

Testing the SSL Server

Next, test whether the SSL encryption is working.

On your browser, type the prefix http:// then your domain name:

https://server_domain

Since the certificate is not already signed by a trusted certificate authority, you will most likely get a warning like the one below:

You will see a warning that may pop-up because the SSL certificate created earlier isn’t signed by a trusted certificate authority:

It's goood πŸ‘πŸΌ

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Inception

This project aims to deepen the knowledge of system administration.

Use of dockerfile for the creation and management of custom images, micro services.

Use of docker-compose for the deployment of containers, the creation and management of the network, storage space, etc ...

Table of contents

Local Domains in Linux

Structure of the project with the bonuses

DOCKER

BASIC DOCKER COMMANDS

  • docker ps -a : List active containers (-a is for showing all containers, running and stopped)
  • docker stop <id>/<name> : Stop running containers
  • docker start <id>/<name> : Start stopped containers
  • docker rm -f <id>/<name> : Remove containers (-f is for force the removal of a running container)
  • docker exec -it <name> bash : Execute a command in a running container

Tips to delete all containers, use: docker rm -f $(docker ps -qa)

DOCKER RUN

$ docker run [OPTIONS] IMAGE[:TAG]
ParametersDescription
-dRun container in background (daemon mode)
-itcreating an interactive container
-pPublish a container port(s) to the host
--rmAutomatically remove the container when it exits
--hostnameContainer host name
--nameAssign a name to the container

Exemple

$ docker run -d -ti -p 80:80 --rm --name web-ngnix --hostname nginx-container nginx:latest

use docker ps to list running containers

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86335dfeaa0b nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web-ngnix

We can see that the container is running in daemon mode. That the exposure of the ports is well done and that the name of the container is the one that we specified in parameter

$ docker exec -it web-ngnix bash

The docker exec command runs a new command in a running container.

$ root@nginx-container:/#

We can now see that the name specified in --hostname is applied

DOCKER VOLUMES

The advantages of volumes :

  • Easy to persist data.
  • Convenient for making backups
  • Share data between multiple containers
  • Multi-containers and permissions

Basic command for managed volumes :

  • docker volume ls : list volumes

  • docker volume create <name> : creating a new volume

  • docker volume rm <name> : delete a volume

  • docker volume inspect <name> : inspection of a volume

The different types of volumes :

  • Bind Mount : Bind mounts are dependent on the directory structure and OS of the host machine
  • Volumes Docker : volumes are completely managed by Docker
  • TMPFS : As opposed to volumes and bind mounts, a tmpfs mount is temporary, and only persisted in the host memory. When the container stops, the tmpfs mount is removed, and files written there won’t be persisted.

DOCKER RUN WITH VOLUMES

1. Bind Mount :

sudo mkdir /data (creation of mount folder is necessary otherwise error will appear when using docker run)

docker run -d --name TestBindMount --mount type=bind,source=/data/,target=/usr/share/nginx/html -p 80:80 nginx:latest

docker exec -ti TestBindMount bash

2. Volumes Docker :

docker volume create mynginx (optional because if the volume is not created, docker will do it)

docker run -d --name TestVolume --mount type=volume,src=mynginx,destination=/usr/share/nginx/html -p 81:80 nginx:latest

docker exec -ti TestVolume bash

3. Tmpfs:

docker run -d --name TestTmpfs --mount type=tmpfs,destination=/usr/share/nginx/html -p 82:80 nginx:latest

docker exec -ti TestTmpfs bash

To check data persistence you can delete all containers and recreate them !! (do not recreate the volumes)

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0096643b045 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->80/tcp, :::82->80/tcp TestTmpfs
92260c1f5880 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:81->80/tcp, :::81->80/tcp TestVolume
dcad272f7531 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp TestBindMount

In each container modify/create the /usr/share/nginx/html/index.html, Remove containers and recreate. Now check if the changes have been saved.

If you are running docker on your OS. You can admire the changes from your websites.

ENVIRONEMENT VARIABLE (ENV, ENVFILE...)

$ docker run -tid --name testenv --env MYVAR="123" debian:latest

Add to the docker environment the variable MYVAR=123

$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@cb9e44034297:/# env
HOSTNAME=cb9e44034297
MYVAR=123
PWD=/
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

This method works but is not secure for example for passwords. To do this we will be able to add an env file ".ENV"

To do this, we will create a ".ENV" file in which we will put our environment variables. "vim .ENV"

MYPASSWORD="safepassword"
MYUSER="secretuser"
MYDB="BDD1"
$ docker run -tid --name testenv --env-file .ENV debian:latest
$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@553c2ac8a657:/# env
HOSTNAME=553c2ac8a657
PWD=/
HOME=/root
MYPASSWORD="safepassword"
TERM=xterm
SHLVL=1
MYUSER="secretuser"
MYDB="BDD1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

DOCKER NETWORK

  • Communication between containers or outside
  • Different types : bridge, host, none, overlay
  • Be careful, a container does not have a fixed IP address (stop / start)

Basic command for managed network :

  • docker network ls : List networks

  • docker network create <name> : Create a network

  • docker network rm <name> : Remove one or more networks

  • docker network inspect <name> : Display detailed information on one or more networks

IPs are not static

In general, IPs in a network are not static.

The addressing of the Ips depends on the starting order of the containers.

Exemple

Create bridge network with name, mynetwork :

$ docker network create --driver=bridge mynetwork

Start two container connect to network "mynetwork"

$ docker run -d --name c1 --network mynetwork nginx:latest
$ docker run -d --name c2 --network mynetwork nginx:latest

Container 1 will have as ip address : 172.26.0.2

$ docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2 

Container 2 will have as ip address : 172.26.0.3

$ docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3

We will now reverse the boot order

sudo docker stop c1
sudo docker stop c2
### reverse containers start order ###
sudo docker start c2
sudo docker start c1

We can see that the ip addresses are no longer the same

docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3
docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2

If the ips change, how do the containers communicate ?

The containers will have to communicate with their name which redirects to the ip.

sudo docker exec -ti c1 bash root@54bb6caca8fb:/# apt update && apt install iputils-ping -y
### ping install ###
root@54bb6caca8fb:/# ping c2
PING c2 (172.26.0.2) 56(84) bytes of data.
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=2 ttl=64 time=0.204 ms

It will therefore be necessary to use the name of the containers, in our different configurations, applications, programs to communicate. Container names are used as domain names.

DOCKERFILE

Dockerfile is a configuration file for the purpose of creating an image

Dockerfile benefit

  • Restart an image creation at any time
  • Better configuration visibility
  • Dockerfile editing script
  • Image creation, production or development

Instructions Dockerfile

β–‘InstructionsDescription
1FROMNew build stage and sets the Base Image for subsequent instructions.
2MAINTAINERauthor
3ARGDefines a variable that users can pass when building the image
4ENVEnvironment variable
4LABELAdding metadata
5VOLUMECreate a mount point
6RUNExecute a command when creating the image
6COPY // ADDAdd a file and directory in the image
6WORKDIRAllows you to change the current path
7EXPOSEPort listened by the container (metadata)
9CMD // ENTRYPOINTExecute a command when the container starts

BUILD A IMAGE

We will now create a mariadb image

Here are the different files we need to build the image

$ tree
.
β”œβ”€β”€ 50-server.cnf # Mariadb configuration file
β”œβ”€β”€ Dockerfile # The dockerfile to build the image
└── script.sh # Database configuration script

Dockerfile

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update
RUN apt install -y mariadb-server # COPY THE CONF FOR THE BIND AND THE SQL SCRIPT FOR THE PRIVILEGECOPY 50-server.cnf /etc/mysql/mariadb.conf.d/
# COPY THE SCRIPT IN THE IMAGES AND MODIFY THE EXECUTION RIGHTS OF ITCOPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT [ "/script.sh" ]

By default, the server does not accept external connections, or rather, it only accepts local connections (from the LoopBack address: localhost = 127.0.0.1). We need change that !

50-server.cnf

[server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
# bind-address = 127.0.0.1 # You need to change this line to allow external connectionsbind-address = 0.0.0.0 # Now it's better :-)expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb][mariadb-10.5]

Script.sh will be executed at entrypoint at runtime. this allow us to initialize the environment variables with an .envfile

script.sh

#!/bin/sh
service mysql start # CREATE USER #echo"CREATE USER '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
# PRIVILGES FOR ROOT AND USER FOR ALL IP ADRESS #echo"GRANT ALL PRIVILEGES ON *.* TO '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
echo"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '$BDD_ROOT_PASSWORD';"| mysql
echo"FLUSH PRIVILEGES;"| mysql
# CREAT WORDPRESS DATABASE #echo"CREATE DATABASE $BDD_NAME;"| mysql
kill$(cat /var/run/mysqld/mysqld.pid)
mysqld

DOCKER BUILD :

$ docker build -t my-mariadb . ......
......
Successfully built 6ad0c955aa67
Successfully tagged my-mariadb:latest πŸ‘

For this example, we'll change to \home and run my-mariadb image with an environment file.

$ cd /home

Create .env file in which username, user, password, database name, root password.

This information will be embedded in the container at runtime.

$ vim .env
BDD_USER=user
BDD_USER_PASSWORD=safepwd
BDD_NAME=wordpress
BDD_ROOT_PASSWORD=safepwdroot

To run the image you will need a specific env file and image name

$ docker run -tid --name testmariadb --env-file .env my-mariadb

The container is well executed, we can check with a docker ps

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e058b2f18f my-mariadb "/script.sh" 22 seconds ago Up 22 seconds testmariadb

Enter the container to check if our variables have integrated

$ docker exec -ti testmariadb bash root@34e058b2f18f:/# 

Everything is good 🀩

root@34e058b2f18f:/# env
HOSTNAME=34e058b2f18f
PWD=/
BDD_NAME=wordpress
HOME=/root
BDD_USER_PASSWORD=safepwd
TERM=xterm
SHLVL=1
BDD_ROOT_PASSWORD=safepwdroot
BDD_USER=user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

Check if the conf file has been copied

root@34e058b2f18f:/# cat /etc/mysql/mariadb.conf.d/50-server.cnf [server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
bind-address = 0.0.0.0
expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb]

Let's start mysql to check users and database

root@34e058b2f18f:/# mysql Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.38-MariaDB-0+deb10u1 Debian 10
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Check if our user and root is enabled for any host

MariaDB [(none)]>SELECT user,host,password FROMmysql.user;
+------+-----------+-------------------------------------------+
| user | host | password |
+------+-----------+-------------------------------------------+
| root | localhost | |
| user | % | *1C848575FF465642717BE88F2015E168769A62F3 |
| root | % | *FDB22E6F75BD75009DEE947AFD0BD73CB7EB88DA |
+------+-----------+-------------------------------------------+3 rows inset (0.005 sec)

Check if the "wordpress" database has been created

MariaDB [(none)]> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| wordpress |
+--------------------+4 rows inset (0.005 sec)

Starter Pack [ MariaDB - Adminer ]

In the previous part we saw how to write a dockerfile and build the image using docker build

In this part we will see how to use docker compose and write a docker-compose.yml

But first, we will see the configuration and the creation of the dockerfile for Adminer.

Adminer is a tool for managing content in databases. It natively supports MySQL, MariaDB, PostgreSQL, SQLite,

Once installed, we will be able to connect to our database from the Web Adminer interface 😎

Dockerfile (Adminer)

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update RUN apt install -y adminer # COPY THE CONF FILE COPY 000-default.conf /etc/apache2/sites-available/
RUN echo 'ServerName adminer' >> /etc/apache2/apache2.conf
# START AND CONF RUN service apache2 start && a2enconf adminer.conf ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

000-default.conf (Adminer)

<VirtualHost *:80>
DocumentRoot /etc/adminer
Alias /adminer /etc/adminer
<Directory /etc/adminer>
Require all granted
DirectoryIndex conf.php
</Directory> ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

DOCKER-COMPOSE

What is Docker Compose?

Docker Compose is a tool that was developed to help define and share multi-container applications.

With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

BASIC DOCKER COMMANDS

  • docker-compose build : To build the images
  • docker-compose up -d : To run containers in daemon mode
  • docker-compose up --build -d : To build images and run containers in daemon mode {my favorite :-)}
  • docker-compose start/stop : To start and stop services
  • docker-compose down : To stop and delete containers

It is important that the project structure is consistent with the dockerfiles and docker-compose.yml

$ tree .
β”œβ”€β”€ adminer_directory
β”‚ β”œβ”€β”€ 000-default.conf β”‚ └── Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ .env # same .env as before 
β”œβ”€β”€ mariadb_directory
β”‚ β”œβ”€β”€ 50-server.cnf # Same file seen above
β”‚ β”œβ”€β”€ Dockerfile # Same file seen above
β”‚ └── script.sh # Same file seen above
└── my_volume. # Persistent volume

docker-compose.yml

version: '3.5'services:adminer:container_name: Adminer # Name redirect to IP -> 172.X.X.Zbuild: adminer_directory/. # Build the dockerfile in ./adminer_directory/Dockerfile restart: always # Restart the container if it has stoppedports:- "80:80" # Redirect port 80 of Adminer on the hostnetworks:- mynetwork # Use mynetwork for communicate with mariadbmariadb:container_name: Mariadbbuild: mariadb_directory/.restart: alwaysnetworks:- mynetworkvolumes:- db:/var/lib/mysqlenv_file: .env# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge # Remember the different types of Networks, I showed you before ???# VOLUMEvolumes:db:driver: localdriver_opts: # Options specific to the drivertype: 'none'o: 'bind'device: ./my_volume # Persistent volume

The docker-compose.yml is edited.

The various essential elements of the infrastructure being positioned in the right place.

We will be able to launch our infrastructure using the command : docker-compose up --build -d .

This will build and then launch the images.

$ docker-compose up --build -d
....
....
Creating Mariadb ... done
Creating Adminer ... done
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b1e14853a6e mdb-adm_adminer "/usr/sbin/apache2ct…" 1 minutes ago Up 1 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp Adminer
4cb7c3cb88f8 mdb-adm_mariadb "/script.sh" 1 minutes ago Up 1 minutes Mariadb

The launch of our containers went well.

We will be able to connect to our database through the Adminer web interface using the host address.

For my part, the address of my host is 192.168.64.13, because i work remotely on a vm.

Most likely your host address is localhost or 127.0.0.1.

Adminer will ask us for the connection information.

This information corresponds to the information present in the ".env" file

The server address to enter is Mariadb

USERNAME = user
PASSWORD = safepwd
DATABASE = wordpress

Great the connection works πŸ‘πŸΌ

You can also log in as root. You just have to put in "root" in user and the password present in the env file.

PHP-FPM & NGNIX

In this part we will create a simple infrastructure allowing to separate nginx and php.

We will then use this same infrastructure to implement the SSL certificate and communicate only on port 443 to connect to our web server.

$ tree
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ nginx
β”‚ β”œβ”€β”€ conf
β”‚ β”‚ └── default
β”‚ └── Dockerfile
└── wordpress
β”œβ”€β”€ conf
β”‚ β”œβ”€β”€ index.php
β”‚ └── www.conf
└── Dockerfile

To work, nginx and php need to have access to the same file.

This is why our "wordress" volume is common to both containers.

Both will share the folder /var/www/html

docker-compose.yml

version: '3.5'services:ngnix:container_name: ngnixbuild: ./nginx/restart: alwaysvolumes:- WordPress:/var/www/htmldepends_on:- wordpressports:- "80:80"networks:- mynetworkwordpress:container_name: wordpressbuild: ./wordpress/restart: alwaysvolumes:- WordPress:/var/www/htmlnetworks:- mynetwork# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge# VOLUMEvolumes:WordPress:driver: localdriver_opts:type: 'none'o: 'bind'device: /home/tliot/data/website

Installing NGINX

Dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# NGINX INSTALLATIONRUN apt-get install -y nginx
# Copy of default web page configurationCOPY ./conf/default /etc/nginx/sites-available/default
ENTRYPOINT ["nginx", "-g", "daemon off;"]

default

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # <------------ Redirect to wordpress container
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Installing PHP-FPM

dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# UDPATE & INSTALLATIONRUN apt install php-fpm -y
# To create the PID file (/run/php/php7.3-fpm.pid)RUN mkdir /run/php
# To allow external connectionsCOPY ./conf/www.conf /etc/php/7.3/fpm/pool.d/
# To create index.php COPY ./conf/index.php /var/www/html/wordpress/index.php
# Is optional, just a metadataEXPOSE 9000 ENTRYPOINT ["/usr/sbin/php-fpm7.3","-F" ]

index.php

<? php echo phpinfo(); ?>

www.conf

[www]
user = www-data
group = www-data
# listen = 127.0.0.1:9000 # Change this line
listen = 9000 # Now it's better
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

Connecting NGINX

Local Domains in Linux

Configure DNS Locally Using /etc/hosts File in Linux

Now open the /etc/hosts file using your editor of choice as follows

sudo vi /etc/hosts

Then add the lines below to the end of the file as shown in the screen shot below.

127.0.0.1 localhost
255.255.255.255	broadcasthost
::1 localhost
192.168.64.13	tliot.42.fr # <--- Principal Domains
192.168.64.13	adminer.tliot.42.fr # <--- adminer subdomain (optional)
192.168.64.13	*.tliot.42.fr # <--- all subdomain (optional)

Next, test if everything is working well as expected, using the ping command.

$ ping tliot.42.fr
PING tliot.42.fr (192.168.64.13): 56 data bytes
64 bytes from 192.168.64.13: icmp_seq=0 ttl=64 time=1.919 ms
64 bytes from 192.168.64.13: icmp_seq=1 ttl=64 time=2.046 ms
64 bytes from 192.168.64.13: icmp_seq=2 ttl=64 time=2.391 ms
64 bytes from 192.168.64.13: icmp_seq=3 ttl=64 time=2.017 ms
64 bytes from 192.168.64.13: icmp_seq=4 ttl=64 time=2.481 ms
^C
--- tliot.42.fr ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss

Setup a self-signed SSL certificate

Create the self-signed SSL certificate:

RUN 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=Ile-de-France/L=Paris/O=42/OU=42Paris/CN=TLIOT/UID=TTT'

Create a new configuration snippet file for Nginx:

RUN echo "ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;\nssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;" > /etc/nginx/snippets/self-signed.conf

Create a strong Diffie-Hellman group:

RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create a configuration snippet with strong encryption settings:

COPY ./conf/ssl-params.conf /etc/nginx/snippets/

ssl-params.conf

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Configure Nginx site to use certificate:

server {
listen 443 ssl default_server; <--- 80 to 443
listen [::]:443 ssl default_server; <--- 80 to 443
server_name tliot.42.fr; <--- _ to tliot.42.fr
# ssl include snippets/self-signed.conf; <--- self-signed SSL
include snippets/ssl-params.conf; <--- strong encryption
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Configure docker-compose.yml site to use 443:

docker-compose.yml

 ngnix:
container_name: ngnix
build: ./nginx/
restart: always
volumes:
- WordPress:/var/www/html
depends_on:
- wordpress
ports:
- "443:443" <--- 80:80 to 443:443
networks:
- mynetwork

Testing the SSL Server

Next, test whether the SSL encryption is working.

On your browser, type the prefix http:// then your domain name:

https://server_domain

Since the certificate is not already signed by a trusted certificate authority, you will most likely get a warning like the one below:

You will see a warning that may pop-up because the SSL certificate created earlier isn’t signed by a trusted certificate authority:

It's goood πŸ‘πŸΌ

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Inception

This project aims to deepen the knowledge of system administration.

Use of dockerfile for the creation and management of custom images, micro services.

Use of docker-compose for the deployment of containers, the creation and management of the network, storage space, etc ...

Table of contents

Local Domains in Linux

Structure of the project with the bonuses

DOCKER

BASIC DOCKER COMMANDS

  • docker ps -a : List active containers (-a is for showing all containers, running and stopped)
  • docker stop <id>/<name> : Stop running containers
  • docker start <id>/<name> : Start stopped containers
  • docker rm -f <id>/<name> : Remove containers (-f is for force the removal of a running container)
  • docker exec -it <name> bash : Execute a command in a running container

Tips to delete all containers, use: docker rm -f $(docker ps -qa)

DOCKER RUN

$ docker run [OPTIONS] IMAGE[:TAG]
ParametersDescription
-dRun container in background (daemon mode)
-itcreating an interactive container
-pPublish a container port(s) to the host
--rmAutomatically remove the container when it exits
--hostnameContainer host name
--nameAssign a name to the container

Exemple

$ docker run -d -ti -p 80:80 --rm --name web-ngnix --hostname nginx-container nginx:latest

use docker ps to list running containers

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86335dfeaa0b nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web-ngnix

We can see that the container is running in daemon mode. That the exposure of the ports is well done and that the name of the container is the one that we specified in parameter

$ docker exec -it web-ngnix bash

The docker exec command runs a new command in a running container.

$ root@nginx-container:/#

We can now see that the name specified in --hostname is applied

DOCKER VOLUMES

The advantages of volumes :

  • Easy to persist data.
  • Convenient for making backups
  • Share data between multiple containers
  • Multi-containers and permissions

Basic command for managed volumes :

  • docker volume ls : list volumes

  • docker volume create <name> : creating a new volume

  • docker volume rm <name> : delete a volume

  • docker volume inspect <name> : inspection of a volume

The different types of volumes :

  • Bind Mount : Bind mounts are dependent on the directory structure and OS of the host machine
  • Volumes Docker : volumes are completely managed by Docker
  • TMPFS : As opposed to volumes and bind mounts, a tmpfs mount is temporary, and only persisted in the host memory. When the container stops, the tmpfs mount is removed, and files written there won’t be persisted.

DOCKER RUN WITH VOLUMES

1. Bind Mount :

sudo mkdir /data (creation of mount folder is necessary otherwise error will appear when using docker run)

docker run -d --name TestBindMount --mount type=bind,source=/data/,target=/usr/share/nginx/html -p 80:80 nginx:latest

docker exec -ti TestBindMount bash

2. Volumes Docker :

docker volume create mynginx (optional because if the volume is not created, docker will do it)

docker run -d --name TestVolume --mount type=volume,src=mynginx,destination=/usr/share/nginx/html -p 81:80 nginx:latest

docker exec -ti TestVolume bash

3. Tmpfs:

docker run -d --name TestTmpfs --mount type=tmpfs,destination=/usr/share/nginx/html -p 82:80 nginx:latest

docker exec -ti TestTmpfs bash

To check data persistence you can delete all containers and recreate them !! (do not recreate the volumes)

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0096643b045 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->80/tcp, :::82->80/tcp TestTmpfs
92260c1f5880 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:81->80/tcp, :::81->80/tcp TestVolume
dcad272f7531 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp TestBindMount

In each container modify/create the /usr/share/nginx/html/index.html, Remove containers and recreate. Now check if the changes have been saved.

If you are running docker on your OS. You can admire the changes from your websites.

ENVIRONEMENT VARIABLE (ENV, ENVFILE...)

$ docker run -tid --name testenv --env MYVAR="123" debian:latest

Add to the docker environment the variable MYVAR=123

$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@cb9e44034297:/# env
HOSTNAME=cb9e44034297
MYVAR=123
PWD=/
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

This method works but is not secure for example for passwords. To do this we will be able to add an env file ".ENV"

To do this, we will create a ".ENV" file in which we will put our environment variables. "vim .ENV"

MYPASSWORD="safepassword"
MYUSER="secretuser"
MYDB="BDD1"
$ docker run -tid --name testenv --env-file .ENV debian:latest
$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@553c2ac8a657:/# env
HOSTNAME=553c2ac8a657
PWD=/
HOME=/root
MYPASSWORD="safepassword"
TERM=xterm
SHLVL=1
MYUSER="secretuser"
MYDB="BDD1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

DOCKER NETWORK

  • Communication between containers or outside
  • Different types : bridge, host, none, overlay
  • Be careful, a container does not have a fixed IP address (stop / start)

Basic command for managed network :

  • docker network ls : List networks

  • docker network create <name> : Create a network

  • docker network rm <name> : Remove one or more networks

  • docker network inspect <name> : Display detailed information on one or more networks

IPs are not static

In general, IPs in a network are not static.

The addressing of the Ips depends on the starting order of the containers.

Exemple

Create bridge network with name, mynetwork :

$ docker network create --driver=bridge mynetwork

Start two container connect to network "mynetwork"

$ docker run -d --name c1 --network mynetwork nginx:latest
$ docker run -d --name c2 --network mynetwork nginx:latest

Container 1 will have as ip address : 172.26.0.2

$ docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2 

Container 2 will have as ip address : 172.26.0.3

$ docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3

We will now reverse the boot order

sudo docker stop c1
sudo docker stop c2
### reverse containers start order ###
sudo docker start c2
sudo docker start c1

We can see that the ip addresses are no longer the same

docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3
docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2

If the ips change, how do the containers communicate ?

The containers will have to communicate with their name which redirects to the ip.

sudo docker exec -ti c1 bash root@54bb6caca8fb:/# apt update && apt install iputils-ping -y
### ping install ###
root@54bb6caca8fb:/# ping c2
PING c2 (172.26.0.2) 56(84) bytes of data.
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=2 ttl=64 time=0.204 ms

It will therefore be necessary to use the name of the containers, in our different configurations, applications, programs to communicate. Container names are used as domain names.

DOCKERFILE

Dockerfile is a configuration file for the purpose of creating an image

Dockerfile benefit

  • Restart an image creation at any time
  • Better configuration visibility
  • Dockerfile editing script
  • Image creation, production or development

Instructions Dockerfile

β–‘InstructionsDescription
1FROMNew build stage and sets the Base Image for subsequent instructions.
2MAINTAINERauthor
3ARGDefines a variable that users can pass when building the image
4ENVEnvironment variable
4LABELAdding metadata
5VOLUMECreate a mount point
6RUNExecute a command when creating the image
6COPY // ADDAdd a file and directory in the image
6WORKDIRAllows you to change the current path
7EXPOSEPort listened by the container (metadata)
9CMD // ENTRYPOINTExecute a command when the container starts

BUILD A IMAGE

We will now create a mariadb image

Here are the different files we need to build the image

$ tree
.
β”œβ”€β”€ 50-server.cnf # Mariadb configuration file
β”œβ”€β”€ Dockerfile # The dockerfile to build the image
└── script.sh # Database configuration script

Dockerfile

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update
RUN apt install -y mariadb-server # COPY THE CONF FOR THE BIND AND THE SQL SCRIPT FOR THE PRIVILEGECOPY 50-server.cnf /etc/mysql/mariadb.conf.d/
# COPY THE SCRIPT IN THE IMAGES AND MODIFY THE EXECUTION RIGHTS OF ITCOPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT [ "/script.sh" ]

By default, the server does not accept external connections, or rather, it only accepts local connections (from the LoopBack address: localhost = 127.0.0.1). We need change that !

50-server.cnf

[server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
# bind-address = 127.0.0.1 # You need to change this line to allow external connectionsbind-address = 0.0.0.0 # Now it's better :-)expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb][mariadb-10.5]

Script.sh will be executed at entrypoint at runtime. this allow us to initialize the environment variables with an .envfile

script.sh

#!/bin/sh
service mysql start # CREATE USER #echo"CREATE USER '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
# PRIVILGES FOR ROOT AND USER FOR ALL IP ADRESS #echo"GRANT ALL PRIVILEGES ON *.* TO '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
echo"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '$BDD_ROOT_PASSWORD';"| mysql
echo"FLUSH PRIVILEGES;"| mysql
# CREAT WORDPRESS DATABASE #echo"CREATE DATABASE $BDD_NAME;"| mysql
kill$(cat /var/run/mysqld/mysqld.pid)
mysqld

DOCKER BUILD :

$ docker build -t my-mariadb . ......
......
Successfully built 6ad0c955aa67
Successfully tagged my-mariadb:latest πŸ‘

For this example, we'll change to \home and run my-mariadb image with an environment file.

$ cd /home

Create .env file in which username, user, password, database name, root password.

This information will be embedded in the container at runtime.

$ vim .env
BDD_USER=user
BDD_USER_PASSWORD=safepwd
BDD_NAME=wordpress
BDD_ROOT_PASSWORD=safepwdroot

To run the image you will need a specific env file and image name

$ docker run -tid --name testmariadb --env-file .env my-mariadb

The container is well executed, we can check with a docker ps

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e058b2f18f my-mariadb "/script.sh" 22 seconds ago Up 22 seconds testmariadb

Enter the container to check if our variables have integrated

$ docker exec -ti testmariadb bash root@34e058b2f18f:/# 

Everything is good 🀩

root@34e058b2f18f:/# env
HOSTNAME=34e058b2f18f
PWD=/
BDD_NAME=wordpress
HOME=/root
BDD_USER_PASSWORD=safepwd
TERM=xterm
SHLVL=1
BDD_ROOT_PASSWORD=safepwdroot
BDD_USER=user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

Check if the conf file has been copied

root@34e058b2f18f:/# cat /etc/mysql/mariadb.conf.d/50-server.cnf [server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
bind-address = 0.0.0.0
expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb]

Let's start mysql to check users and database

root@34e058b2f18f:/# mysql Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.38-MariaDB-0+deb10u1 Debian 10
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Check if our user and root is enabled for any host

MariaDB [(none)]>SELECT user,host,password FROMmysql.user;
+------+-----------+-------------------------------------------+
| user | host | password |
+------+-----------+-------------------------------------------+
| root | localhost | |
| user | % | *1C848575FF465642717BE88F2015E168769A62F3 |
| root | % | *FDB22E6F75BD75009DEE947AFD0BD73CB7EB88DA |
+------+-----------+-------------------------------------------+3 rows inset (0.005 sec)

Check if the "wordpress" database has been created

MariaDB [(none)]> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| wordpress |
+--------------------+4 rows inset (0.005 sec)

Starter Pack [ MariaDB - Adminer ]

In the previous part we saw how to write a dockerfile and build the image using docker build

In this part we will see how to use docker compose and write a docker-compose.yml

But first, we will see the configuration and the creation of the dockerfile for Adminer.

Adminer is a tool for managing content in databases. It natively supports MySQL, MariaDB, PostgreSQL, SQLite,

Once installed, we will be able to connect to our database from the Web Adminer interface 😎

Dockerfile (Adminer)

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update RUN apt install -y adminer # COPY THE CONF FILE COPY 000-default.conf /etc/apache2/sites-available/
RUN echo 'ServerName adminer' >> /etc/apache2/apache2.conf
# START AND CONF RUN service apache2 start && a2enconf adminer.conf ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

000-default.conf (Adminer)

<VirtualHost *:80>
DocumentRoot /etc/adminer
Alias /adminer /etc/adminer
<Directory /etc/adminer>
Require all granted
DirectoryIndex conf.php
</Directory> ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

DOCKER-COMPOSE

What is Docker Compose?

Docker Compose is a tool that was developed to help define and share multi-container applications.

With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

BASIC DOCKER COMMANDS

  • docker-compose build : To build the images
  • docker-compose up -d : To run containers in daemon mode
  • docker-compose up --build -d : To build images and run containers in daemon mode {my favorite :-)}
  • docker-compose start/stop : To start and stop services
  • docker-compose down : To stop and delete containers

It is important that the project structure is consistent with the dockerfiles and docker-compose.yml

$ tree .
β”œβ”€β”€ adminer_directory
β”‚ β”œβ”€β”€ 000-default.conf β”‚ └── Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ .env # same .env as before 
β”œβ”€β”€ mariadb_directory
β”‚ β”œβ”€β”€ 50-server.cnf # Same file seen above
β”‚ β”œβ”€β”€ Dockerfile # Same file seen above
β”‚ └── script.sh # Same file seen above
└── my_volume. # Persistent volume

docker-compose.yml

version: '3.5'services:adminer:container_name: Adminer # Name redirect to IP -> 172.X.X.Zbuild: adminer_directory/. # Build the dockerfile in ./adminer_directory/Dockerfile restart: always # Restart the container if it has stoppedports:- "80:80" # Redirect port 80 of Adminer on the hostnetworks:- mynetwork # Use mynetwork for communicate with mariadbmariadb:container_name: Mariadbbuild: mariadb_directory/.restart: alwaysnetworks:- mynetworkvolumes:- db:/var/lib/mysqlenv_file: .env# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge # Remember the different types of Networks, I showed you before ???# VOLUMEvolumes:db:driver: localdriver_opts: # Options specific to the drivertype: 'none'o: 'bind'device: ./my_volume # Persistent volume

The docker-compose.yml is edited.

The various essential elements of the infrastructure being positioned in the right place.

We will be able to launch our infrastructure using the command : docker-compose up --build -d .

This will build and then launch the images.

$ docker-compose up --build -d
....
....
Creating Mariadb ... done
Creating Adminer ... done
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b1e14853a6e mdb-adm_adminer "/usr/sbin/apache2ct…" 1 minutes ago Up 1 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp Adminer
4cb7c3cb88f8 mdb-adm_mariadb "/script.sh" 1 minutes ago Up 1 minutes Mariadb

The launch of our containers went well.

We will be able to connect to our database through the Adminer web interface using the host address.

For my part, the address of my host is 192.168.64.13, because i work remotely on a vm.

Most likely your host address is localhost or 127.0.0.1.

Adminer will ask us for the connection information.

This information corresponds to the information present in the ".env" file

The server address to enter is Mariadb

USERNAME = user
PASSWORD = safepwd
DATABASE = wordpress

Great the connection works πŸ‘πŸΌ

You can also log in as root. You just have to put in "root" in user and the password present in the env file.

PHP-FPM & NGNIX

In this part we will create a simple infrastructure allowing to separate nginx and php.

We will then use this same infrastructure to implement the SSL certificate and communicate only on port 443 to connect to our web server.

$ tree
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ nginx
β”‚ β”œβ”€β”€ conf
β”‚ β”‚ └── default
β”‚ └── Dockerfile
└── wordpress
β”œβ”€β”€ conf
β”‚ β”œβ”€β”€ index.php
β”‚ └── www.conf
└── Dockerfile

To work, nginx and php need to have access to the same file.

This is why our "wordress" volume is common to both containers.

Both will share the folder /var/www/html

docker-compose.yml

version: '3.5'services:ngnix:container_name: ngnixbuild: ./nginx/restart: alwaysvolumes:- WordPress:/var/www/htmldepends_on:- wordpressports:- "80:80"networks:- mynetworkwordpress:container_name: wordpressbuild: ./wordpress/restart: alwaysvolumes:- WordPress:/var/www/htmlnetworks:- mynetwork# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge# VOLUMEvolumes:WordPress:driver: localdriver_opts:type: 'none'o: 'bind'device: /home/tliot/data/website

Installing NGINX

Dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# NGINX INSTALLATIONRUN apt-get install -y nginx
# Copy of default web page configurationCOPY ./conf/default /etc/nginx/sites-available/default
ENTRYPOINT ["nginx", "-g", "daemon off;"]

default

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # <------------ Redirect to wordpress container
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Installing PHP-FPM

dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# UDPATE & INSTALLATIONRUN apt install php-fpm -y
# To create the PID file (/run/php/php7.3-fpm.pid)RUN mkdir /run/php
# To allow external connectionsCOPY ./conf/www.conf /etc/php/7.3/fpm/pool.d/
# To create index.php COPY ./conf/index.php /var/www/html/wordpress/index.php
# Is optional, just a metadataEXPOSE 9000 ENTRYPOINT ["/usr/sbin/php-fpm7.3","-F" ]

index.php

<? php echo phpinfo(); ?>

www.conf

[www]
user = www-data
group = www-data
# listen = 127.0.0.1:9000 # Change this line
listen = 9000 # Now it's better
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

Connecting NGINX

Local Domains in Linux

Configure DNS Locally Using /etc/hosts File in Linux

Now open the /etc/hosts file using your editor of choice as follows

sudo vi /etc/hosts

Then add the lines below to the end of the file as shown in the screen shot below.

127.0.0.1 localhost
255.255.255.255	broadcasthost
::1 localhost
192.168.64.13	tliot.42.fr # <--- Principal Domains
192.168.64.13	adminer.tliot.42.fr # <--- adminer subdomain (optional)
192.168.64.13	*.tliot.42.fr # <--- all subdomain (optional)

Next, test if everything is working well as expected, using the ping command.

$ ping tliot.42.fr
PING tliot.42.fr (192.168.64.13): 56 data bytes
64 bytes from 192.168.64.13: icmp_seq=0 ttl=64 time=1.919 ms
64 bytes from 192.168.64.13: icmp_seq=1 ttl=64 time=2.046 ms
64 bytes from 192.168.64.13: icmp_seq=2 ttl=64 time=2.391 ms
64 bytes from 192.168.64.13: icmp_seq=3 ttl=64 time=2.017 ms
64 bytes from 192.168.64.13: icmp_seq=4 ttl=64 time=2.481 ms
^C
--- tliot.42.fr ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss

Setup a self-signed SSL certificate

Create the self-signed SSL certificate:

RUN 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=Ile-de-France/L=Paris/O=42/OU=42Paris/CN=TLIOT/UID=TTT'

Create a new configuration snippet file for Nginx:

RUN echo "ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;\nssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;" > /etc/nginx/snippets/self-signed.conf

Create a strong Diffie-Hellman group:

RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create a configuration snippet with strong encryption settings:

COPY ./conf/ssl-params.conf /etc/nginx/snippets/

ssl-params.conf

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Configure Nginx site to use certificate:

server {
listen 443 ssl default_server; <--- 80 to 443
listen [::]:443 ssl default_server; <--- 80 to 443
server_name tliot.42.fr; <--- _ to tliot.42.fr
# ssl include snippets/self-signed.conf; <--- self-signed SSL
include snippets/ssl-params.conf; <--- strong encryption
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Configure docker-compose.yml site to use 443:

docker-compose.yml

 ngnix:
container_name: ngnix
build: ./nginx/
restart: always
volumes:
- WordPress:/var/www/html
depends_on:
- wordpress
ports:
- "443:443" <--- 80:80 to 443:443
networks:
- mynetwork

Testing the SSL Server

Next, test whether the SSL encryption is working.

On your browser, type the prefix http:// then your domain name:

https://server_domain

Since the certificate is not already signed by a trusted certificate authority, you will most likely get a warning like the one below:

You will see a warning that may pop-up because the SSL certificate created earlier isn’t signed by a trusted certificate authority:

It's goood πŸ‘πŸΌ

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Inception

This project aims to deepen the knowledge of system administration.

Use of dockerfile for the creation and management of custom images, micro services.

Use of docker-compose for the deployment of containers, the creation and management of the network, storage space, etc ...

Table of contents

Local Domains in Linux

Structure of the project with the bonuses

DOCKER

BASIC DOCKER COMMANDS

  • docker ps -a : List active containers (-a is for showing all containers, running and stopped)
  • docker stop <id>/<name> : Stop running containers
  • docker start <id>/<name> : Start stopped containers
  • docker rm -f <id>/<name> : Remove containers (-f is for force the removal of a running container)
  • docker exec -it <name> bash : Execute a command in a running container

Tips to delete all containers, use: docker rm -f $(docker ps -qa)

DOCKER RUN

$ docker run [OPTIONS] IMAGE[:TAG]
ParametersDescription
-dRun container in background (daemon mode)
-itcreating an interactive container
-pPublish a container port(s) to the host
--rmAutomatically remove the container when it exits
--hostnameContainer host name
--nameAssign a name to the container

Exemple

$ docker run -d -ti -p 80:80 --rm --name web-ngnix --hostname nginx-container nginx:latest

use docker ps to list running containers

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86335dfeaa0b nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web-ngnix

We can see that the container is running in daemon mode. That the exposure of the ports is well done and that the name of the container is the one that we specified in parameter

$ docker exec -it web-ngnix bash

The docker exec command runs a new command in a running container.

$ root@nginx-container:/#

We can now see that the name specified in --hostname is applied

DOCKER VOLUMES

The advantages of volumes :

  • Easy to persist data.
  • Convenient for making backups
  • Share data between multiple containers
  • Multi-containers and permissions

Basic command for managed volumes :

  • docker volume ls : list volumes

  • docker volume create <name> : creating a new volume

  • docker volume rm <name> : delete a volume

  • docker volume inspect <name> : inspection of a volume

The different types of volumes :

  • Bind Mount : Bind mounts are dependent on the directory structure and OS of the host machine
  • Volumes Docker : volumes are completely managed by Docker
  • TMPFS : As opposed to volumes and bind mounts, a tmpfs mount is temporary, and only persisted in the host memory. When the container stops, the tmpfs mount is removed, and files written there won’t be persisted.

DOCKER RUN WITH VOLUMES

1. Bind Mount :

sudo mkdir /data (creation of mount folder is necessary otherwise error will appear when using docker run)

docker run -d --name TestBindMount --mount type=bind,source=/data/,target=/usr/share/nginx/html -p 80:80 nginx:latest

docker exec -ti TestBindMount bash

2. Volumes Docker :

docker volume create mynginx (optional because if the volume is not created, docker will do it)

docker run -d --name TestVolume --mount type=volume,src=mynginx,destination=/usr/share/nginx/html -p 81:80 nginx:latest

docker exec -ti TestVolume bash

3. Tmpfs:

docker run -d --name TestTmpfs --mount type=tmpfs,destination=/usr/share/nginx/html -p 82:80 nginx:latest

docker exec -ti TestTmpfs bash

To check data persistence you can delete all containers and recreate them !! (do not recreate the volumes)

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0096643b045 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->80/tcp, :::82->80/tcp TestTmpfs
92260c1f5880 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:81->80/tcp, :::81->80/tcp TestVolume
dcad272f7531 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp TestBindMount

In each container modify/create the /usr/share/nginx/html/index.html, Remove containers and recreate. Now check if the changes have been saved.

If you are running docker on your OS. You can admire the changes from your websites.

ENVIRONEMENT VARIABLE (ENV, ENVFILE...)

$ docker run -tid --name testenv --env MYVAR="123" debian:latest

Add to the docker environment the variable MYVAR=123

$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@cb9e44034297:/# env
HOSTNAME=cb9e44034297
MYVAR=123
PWD=/
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

This method works but is not secure for example for passwords. To do this we will be able to add an env file ".ENV"

To do this, we will create a ".ENV" file in which we will put our environment variables. "vim .ENV"

MYPASSWORD="safepassword"
MYUSER="secretuser"
MYDB="BDD1"
$ docker run -tid --name testenv --env-file .ENV debian:latest
$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@553c2ac8a657:/# env
HOSTNAME=553c2ac8a657
PWD=/
HOME=/root
MYPASSWORD="safepassword"
TERM=xterm
SHLVL=1
MYUSER="secretuser"
MYDB="BDD1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

DOCKER NETWORK

  • Communication between containers or outside
  • Different types : bridge, host, none, overlay
  • Be careful, a container does not have a fixed IP address (stop / start)

Basic command for managed network :

  • docker network ls : List networks

  • docker network create <name> : Create a network

  • docker network rm <name> : Remove one or more networks

  • docker network inspect <name> : Display detailed information on one or more networks

IPs are not static

In general, IPs in a network are not static.

The addressing of the Ips depends on the starting order of the containers.

Exemple

Create bridge network with name, mynetwork :

$ docker network create --driver=bridge mynetwork

Start two container connect to network "mynetwork"

$ docker run -d --name c1 --network mynetwork nginx:latest
$ docker run -d --name c2 --network mynetwork nginx:latest

Container 1 will have as ip address : 172.26.0.2

$ docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2 

Container 2 will have as ip address : 172.26.0.3

$ docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3

We will now reverse the boot order

sudo docker stop c1
sudo docker stop c2
### reverse containers start order ###
sudo docker start c2
sudo docker start c1

We can see that the ip addresses are no longer the same

docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3
docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2

If the ips change, how do the containers communicate ?

The containers will have to communicate with their name which redirects to the ip.

sudo docker exec -ti c1 bash root@54bb6caca8fb:/# apt update && apt install iputils-ping -y
### ping install ###
root@54bb6caca8fb:/# ping c2
PING c2 (172.26.0.2) 56(84) bytes of data.
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=2 ttl=64 time=0.204 ms

It will therefore be necessary to use the name of the containers, in our different configurations, applications, programs to communicate. Container names are used as domain names.

DOCKERFILE

Dockerfile is a configuration file for the purpose of creating an image

Dockerfile benefit

  • Restart an image creation at any time
  • Better configuration visibility
  • Dockerfile editing script
  • Image creation, production or development

Instructions Dockerfile

β–‘InstructionsDescription
1FROMNew build stage and sets the Base Image for subsequent instructions.
2MAINTAINERauthor
3ARGDefines a variable that users can pass when building the image
4ENVEnvironment variable
4LABELAdding metadata
5VOLUMECreate a mount point
6RUNExecute a command when creating the image
6COPY // ADDAdd a file and directory in the image
6WORKDIRAllows you to change the current path
7EXPOSEPort listened by the container (metadata)
9CMD // ENTRYPOINTExecute a command when the container starts

BUILD A IMAGE

We will now create a mariadb image

Here are the different files we need to build the image

$ tree
.
β”œβ”€β”€ 50-server.cnf # Mariadb configuration file
β”œβ”€β”€ Dockerfile # The dockerfile to build the image
└── script.sh # Database configuration script

Dockerfile

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update
RUN apt install -y mariadb-server # COPY THE CONF FOR THE BIND AND THE SQL SCRIPT FOR THE PRIVILEGECOPY 50-server.cnf /etc/mysql/mariadb.conf.d/
# COPY THE SCRIPT IN THE IMAGES AND MODIFY THE EXECUTION RIGHTS OF ITCOPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT [ "/script.sh" ]

By default, the server does not accept external connections, or rather, it only accepts local connections (from the LoopBack address: localhost = 127.0.0.1). We need change that !

50-server.cnf

[server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
# bind-address = 127.0.0.1 # You need to change this line to allow external connectionsbind-address = 0.0.0.0 # Now it's better :-)expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb][mariadb-10.5]

Script.sh will be executed at entrypoint at runtime. this allow us to initialize the environment variables with an .envfile

script.sh

#!/bin/sh
service mysql start # CREATE USER #echo"CREATE USER '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
# PRIVILGES FOR ROOT AND USER FOR ALL IP ADRESS #echo"GRANT ALL PRIVILEGES ON *.* TO '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
echo"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '$BDD_ROOT_PASSWORD';"| mysql
echo"FLUSH PRIVILEGES;"| mysql
# CREAT WORDPRESS DATABASE #echo"CREATE DATABASE $BDD_NAME;"| mysql
kill$(cat /var/run/mysqld/mysqld.pid)
mysqld

DOCKER BUILD :

$ docker build -t my-mariadb . ......
......
Successfully built 6ad0c955aa67
Successfully tagged my-mariadb:latest πŸ‘

For this example, we'll change to \home and run my-mariadb image with an environment file.

$ cd /home

Create .env file in which username, user, password, database name, root password.

This information will be embedded in the container at runtime.

$ vim .env
BDD_USER=user
BDD_USER_PASSWORD=safepwd
BDD_NAME=wordpress
BDD_ROOT_PASSWORD=safepwdroot

To run the image you will need a specific env file and image name

$ docker run -tid --name testmariadb --env-file .env my-mariadb

The container is well executed, we can check with a docker ps

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e058b2f18f my-mariadb "/script.sh" 22 seconds ago Up 22 seconds testmariadb

Enter the container to check if our variables have integrated

$ docker exec -ti testmariadb bash root@34e058b2f18f:/# 

Everything is good 🀩

root@34e058b2f18f:/# env
HOSTNAME=34e058b2f18f
PWD=/
BDD_NAME=wordpress
HOME=/root
BDD_USER_PASSWORD=safepwd
TERM=xterm
SHLVL=1
BDD_ROOT_PASSWORD=safepwdroot
BDD_USER=user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

Check if the conf file has been copied

root@34e058b2f18f:/# cat /etc/mysql/mariadb.conf.d/50-server.cnf [server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
bind-address = 0.0.0.0
expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb]

Let's start mysql to check users and database

root@34e058b2f18f:/# mysql Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.38-MariaDB-0+deb10u1 Debian 10
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Check if our user and root is enabled for any host

MariaDB [(none)]>SELECT user,host,password FROMmysql.user;
+------+-----------+-------------------------------------------+
| user | host | password |
+------+-----------+-------------------------------------------+
| root | localhost | |
| user | % | *1C848575FF465642717BE88F2015E168769A62F3 |
| root | % | *FDB22E6F75BD75009DEE947AFD0BD73CB7EB88DA |
+------+-----------+-------------------------------------------+3 rows inset (0.005 sec)

Check if the "wordpress" database has been created

MariaDB [(none)]> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| wordpress |
+--------------------+4 rows inset (0.005 sec)

Starter Pack [ MariaDB - Adminer ]

In the previous part we saw how to write a dockerfile and build the image using docker build

In this part we will see how to use docker compose and write a docker-compose.yml

But first, we will see the configuration and the creation of the dockerfile for Adminer.

Adminer is a tool for managing content in databases. It natively supports MySQL, MariaDB, PostgreSQL, SQLite,

Once installed, we will be able to connect to our database from the Web Adminer interface 😎

Dockerfile (Adminer)

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update RUN apt install -y adminer # COPY THE CONF FILE COPY 000-default.conf /etc/apache2/sites-available/
RUN echo 'ServerName adminer' >> /etc/apache2/apache2.conf
# START AND CONF RUN service apache2 start && a2enconf adminer.conf ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

000-default.conf (Adminer)

<VirtualHost *:80>
DocumentRoot /etc/adminer
Alias /adminer /etc/adminer
<Directory /etc/adminer>
Require all granted
DirectoryIndex conf.php
</Directory> ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

DOCKER-COMPOSE

What is Docker Compose?

Docker Compose is a tool that was developed to help define and share multi-container applications.

With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

BASIC DOCKER COMMANDS

  • docker-compose build : To build the images
  • docker-compose up -d : To run containers in daemon mode
  • docker-compose up --build -d : To build images and run containers in daemon mode {my favorite :-)}
  • docker-compose start/stop : To start and stop services
  • docker-compose down : To stop and delete containers

It is important that the project structure is consistent with the dockerfiles and docker-compose.yml

$ tree .
β”œβ”€β”€ adminer_directory
β”‚ β”œβ”€β”€ 000-default.conf β”‚ └── Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ .env # same .env as before 
β”œβ”€β”€ mariadb_directory
β”‚ β”œβ”€β”€ 50-server.cnf # Same file seen above
β”‚ β”œβ”€β”€ Dockerfile # Same file seen above
β”‚ └── script.sh # Same file seen above
└── my_volume. # Persistent volume

docker-compose.yml

version: '3.5'services:adminer:container_name: Adminer # Name redirect to IP -> 172.X.X.Zbuild: adminer_directory/. # Build the dockerfile in ./adminer_directory/Dockerfile restart: always # Restart the container if it has stoppedports:- "80:80" # Redirect port 80 of Adminer on the hostnetworks:- mynetwork # Use mynetwork for communicate with mariadbmariadb:container_name: Mariadbbuild: mariadb_directory/.restart: alwaysnetworks:- mynetworkvolumes:- db:/var/lib/mysqlenv_file: .env# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge # Remember the different types of Networks, I showed you before ???# VOLUMEvolumes:db:driver: localdriver_opts: # Options specific to the drivertype: 'none'o: 'bind'device: ./my_volume # Persistent volume

The docker-compose.yml is edited.

The various essential elements of the infrastructure being positioned in the right place.

We will be able to launch our infrastructure using the command : docker-compose up --build -d .

This will build and then launch the images.

$ docker-compose up --build -d
....
....
Creating Mariadb ... done
Creating Adminer ... done
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b1e14853a6e mdb-adm_adminer "/usr/sbin/apache2ct…" 1 minutes ago Up 1 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp Adminer
4cb7c3cb88f8 mdb-adm_mariadb "/script.sh" 1 minutes ago Up 1 minutes Mariadb

The launch of our containers went well.

We will be able to connect to our database through the Adminer web interface using the host address.

For my part, the address of my host is 192.168.64.13, because i work remotely on a vm.

Most likely your host address is localhost or 127.0.0.1.

Adminer will ask us for the connection information.

This information corresponds to the information present in the ".env" file

The server address to enter is Mariadb

USERNAME = user
PASSWORD = safepwd
DATABASE = wordpress

Great the connection works πŸ‘πŸΌ

You can also log in as root. You just have to put in "root" in user and the password present in the env file.

PHP-FPM & NGNIX

In this part we will create a simple infrastructure allowing to separate nginx and php.

We will then use this same infrastructure to implement the SSL certificate and communicate only on port 443 to connect to our web server.

$ tree
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ nginx
β”‚ β”œβ”€β”€ conf
β”‚ β”‚ └── default
β”‚ └── Dockerfile
└── wordpress
β”œβ”€β”€ conf
β”‚ β”œβ”€β”€ index.php
β”‚ └── www.conf
└── Dockerfile

To work, nginx and php need to have access to the same file.

This is why our "wordress" volume is common to both containers.

Both will share the folder /var/www/html

docker-compose.yml

version: '3.5'services:ngnix:container_name: ngnixbuild: ./nginx/restart: alwaysvolumes:- WordPress:/var/www/htmldepends_on:- wordpressports:- "80:80"networks:- mynetworkwordpress:container_name: wordpressbuild: ./wordpress/restart: alwaysvolumes:- WordPress:/var/www/htmlnetworks:- mynetwork# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge# VOLUMEvolumes:WordPress:driver: localdriver_opts:type: 'none'o: 'bind'device: /home/tliot/data/website

Installing NGINX

Dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# NGINX INSTALLATIONRUN apt-get install -y nginx
# Copy of default web page configurationCOPY ./conf/default /etc/nginx/sites-available/default
ENTRYPOINT ["nginx", "-g", "daemon off;"]

default

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # <------------ Redirect to wordpress container
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Installing PHP-FPM

dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# UDPATE & INSTALLATIONRUN apt install php-fpm -y
# To create the PID file (/run/php/php7.3-fpm.pid)RUN mkdir /run/php
# To allow external connectionsCOPY ./conf/www.conf /etc/php/7.3/fpm/pool.d/
# To create index.php COPY ./conf/index.php /var/www/html/wordpress/index.php
# Is optional, just a metadataEXPOSE 9000 ENTRYPOINT ["/usr/sbin/php-fpm7.3","-F" ]

index.php

<? php echo phpinfo(); ?>

www.conf

[www]
user = www-data
group = www-data
# listen = 127.0.0.1:9000 # Change this line
listen = 9000 # Now it's better
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

Connecting NGINX

Local Domains in Linux

Configure DNS Locally Using /etc/hosts File in Linux

Now open the /etc/hosts file using your editor of choice as follows

sudo vi /etc/hosts

Then add the lines below to the end of the file as shown in the screen shot below.

127.0.0.1 localhost
255.255.255.255	broadcasthost
::1 localhost
192.168.64.13	tliot.42.fr # <--- Principal Domains
192.168.64.13	adminer.tliot.42.fr # <--- adminer subdomain (optional)
192.168.64.13	*.tliot.42.fr # <--- all subdomain (optional)

Next, test if everything is working well as expected, using the ping command.

$ ping tliot.42.fr
PING tliot.42.fr (192.168.64.13): 56 data bytes
64 bytes from 192.168.64.13: icmp_seq=0 ttl=64 time=1.919 ms
64 bytes from 192.168.64.13: icmp_seq=1 ttl=64 time=2.046 ms
64 bytes from 192.168.64.13: icmp_seq=2 ttl=64 time=2.391 ms
64 bytes from 192.168.64.13: icmp_seq=3 ttl=64 time=2.017 ms
64 bytes from 192.168.64.13: icmp_seq=4 ttl=64 time=2.481 ms
^C
--- tliot.42.fr ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss

Setup a self-signed SSL certificate

Create the self-signed SSL certificate:

RUN 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=Ile-de-France/L=Paris/O=42/OU=42Paris/CN=TLIOT/UID=TTT'

Create a new configuration snippet file for Nginx:

RUN echo "ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;\nssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;" > /etc/nginx/snippets/self-signed.conf

Create a strong Diffie-Hellman group:

RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create a configuration snippet with strong encryption settings:

COPY ./conf/ssl-params.conf /etc/nginx/snippets/

ssl-params.conf

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Configure Nginx site to use certificate:

server {
listen 443 ssl default_server; <--- 80 to 443
listen [::]:443 ssl default_server; <--- 80 to 443
server_name tliot.42.fr; <--- _ to tliot.42.fr
# ssl include snippets/self-signed.conf; <--- self-signed SSL
include snippets/ssl-params.conf; <--- strong encryption
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Configure docker-compose.yml site to use 443:

docker-compose.yml

 ngnix:
container_name: ngnix
build: ./nginx/
restart: always
volumes:
- WordPress:/var/www/html
depends_on:
- wordpress
ports:
- "443:443" <--- 80:80 to 443:443
networks:
- mynetwork

Testing the SSL Server

Next, test whether the SSL encryption is working.

On your browser, type the prefix http:// then your domain name:

https://server_domain

Since the certificate is not already signed by a trusted certificate authority, you will most likely get a warning like the one below:

You will see a warning that may pop-up because the SSL certificate created earlier isn’t signed by a trusted certificate authority:

It's goood πŸ‘πŸΌ

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Inception

This project aims to deepen the knowledge of system administration.

Use of dockerfile for the creation and management of custom images, micro services.

Use of docker-compose for the deployment of containers, the creation and management of the network, storage space, etc ...

Table of contents

Local Domains in Linux

Structure of the project with the bonuses

DOCKER

BASIC DOCKER COMMANDS

  • docker ps -a : List active containers (-a is for showing all containers, running and stopped)
  • docker stop <id>/<name> : Stop running containers
  • docker start <id>/<name> : Start stopped containers
  • docker rm -f <id>/<name> : Remove containers (-f is for force the removal of a running container)
  • docker exec -it <name> bash : Execute a command in a running container

Tips to delete all containers, use: docker rm -f $(docker ps -qa)

DOCKER RUN

$ docker run [OPTIONS] IMAGE[:TAG]
ParametersDescription
-dRun container in background (daemon mode)
-itcreating an interactive container
-pPublish a container port(s) to the host
--rmAutomatically remove the container when it exits
--hostnameContainer host name
--nameAssign a name to the container

Exemple

$ docker run -d -ti -p 80:80 --rm --name web-ngnix --hostname nginx-container nginx:latest

use docker ps to list running containers

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86335dfeaa0b nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web-ngnix

We can see that the container is running in daemon mode. That the exposure of the ports is well done and that the name of the container is the one that we specified in parameter

$ docker exec -it web-ngnix bash

The docker exec command runs a new command in a running container.

$ root@nginx-container:/#

We can now see that the name specified in --hostname is applied

DOCKER VOLUMES

The advantages of volumes :

  • Easy to persist data.
  • Convenient for making backups
  • Share data between multiple containers
  • Multi-containers and permissions

Basic command for managed volumes :

  • docker volume ls : list volumes

  • docker volume create <name> : creating a new volume

  • docker volume rm <name> : delete a volume

  • docker volume inspect <name> : inspection of a volume

The different types of volumes :

  • Bind Mount : Bind mounts are dependent on the directory structure and OS of the host machine
  • Volumes Docker : volumes are completely managed by Docker
  • TMPFS : As opposed to volumes and bind mounts, a tmpfs mount is temporary, and only persisted in the host memory. When the container stops, the tmpfs mount is removed, and files written there won’t be persisted.

DOCKER RUN WITH VOLUMES

1. Bind Mount :

sudo mkdir /data (creation of mount folder is necessary otherwise error will appear when using docker run)

docker run -d --name TestBindMount --mount type=bind,source=/data/,target=/usr/share/nginx/html -p 80:80 nginx:latest

docker exec -ti TestBindMount bash

2. Volumes Docker :

docker volume create mynginx (optional because if the volume is not created, docker will do it)

docker run -d --name TestVolume --mount type=volume,src=mynginx,destination=/usr/share/nginx/html -p 81:80 nginx:latest

docker exec -ti TestVolume bash

3. Tmpfs:

docker run -d --name TestTmpfs --mount type=tmpfs,destination=/usr/share/nginx/html -p 82:80 nginx:latest

docker exec -ti TestTmpfs bash

To check data persistence you can delete all containers and recreate them !! (do not recreate the volumes)

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0096643b045 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->80/tcp, :::82->80/tcp TestTmpfs
92260c1f5880 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:81->80/tcp, :::81->80/tcp TestVolume
dcad272f7531 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp TestBindMount

In each container modify/create the /usr/share/nginx/html/index.html, Remove containers and recreate. Now check if the changes have been saved.

If you are running docker on your OS. You can admire the changes from your websites.

ENVIRONEMENT VARIABLE (ENV, ENVFILE...)

$ docker run -tid --name testenv --env MYVAR="123" debian:latest

Add to the docker environment the variable MYVAR=123

$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@cb9e44034297:/# env
HOSTNAME=cb9e44034297
MYVAR=123
PWD=/
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

This method works but is not secure for example for passwords. To do this we will be able to add an env file ".ENV"

To do this, we will create a ".ENV" file in which we will put our environment variables. "vim .ENV"

MYPASSWORD="safepassword"
MYUSER="secretuser"
MYDB="BDD1"
$ docker run -tid --name testenv --env-file .ENV debian:latest
$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@553c2ac8a657:/# env
HOSTNAME=553c2ac8a657
PWD=/
HOME=/root
MYPASSWORD="safepassword"
TERM=xterm
SHLVL=1
MYUSER="secretuser"
MYDB="BDD1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

DOCKER NETWORK

  • Communication between containers or outside
  • Different types : bridge, host, none, overlay
  • Be careful, a container does not have a fixed IP address (stop / start)

Basic command for managed network :

  • docker network ls : List networks

  • docker network create <name> : Create a network

  • docker network rm <name> : Remove one or more networks

  • docker network inspect <name> : Display detailed information on one or more networks

IPs are not static

In general, IPs in a network are not static.

The addressing of the Ips depends on the starting order of the containers.

Exemple

Create bridge network with name, mynetwork :

$ docker network create --driver=bridge mynetwork

Start two container connect to network "mynetwork"

$ docker run -d --name c1 --network mynetwork nginx:latest
$ docker run -d --name c2 --network mynetwork nginx:latest

Container 1 will have as ip address : 172.26.0.2

$ docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2 

Container 2 will have as ip address : 172.26.0.3

$ docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3

We will now reverse the boot order

sudo docker stop c1
sudo docker stop c2
### reverse containers start order ###
sudo docker start c2
sudo docker start c1

We can see that the ip addresses are no longer the same

docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3
docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2

If the ips change, how do the containers communicate ?

The containers will have to communicate with their name which redirects to the ip.

sudo docker exec -ti c1 bash root@54bb6caca8fb:/# apt update && apt install iputils-ping -y
### ping install ###
root@54bb6caca8fb:/# ping c2
PING c2 (172.26.0.2) 56(84) bytes of data.
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=2 ttl=64 time=0.204 ms

It will therefore be necessary to use the name of the containers, in our different configurations, applications, programs to communicate. Container names are used as domain names.

DOCKERFILE

Dockerfile is a configuration file for the purpose of creating an image

Dockerfile benefit

  • Restart an image creation at any time
  • Better configuration visibility
  • Dockerfile editing script
  • Image creation, production or development

Instructions Dockerfile

β–‘InstructionsDescription
1FROMNew build stage and sets the Base Image for subsequent instructions.
2MAINTAINERauthor
3ARGDefines a variable that users can pass when building the image
4ENVEnvironment variable
4LABELAdding metadata
5VOLUMECreate a mount point
6RUNExecute a command when creating the image
6COPY // ADDAdd a file and directory in the image
6WORKDIRAllows you to change the current path
7EXPOSEPort listened by the container (metadata)
9CMD // ENTRYPOINTExecute a command when the container starts

BUILD A IMAGE

We will now create a mariadb image

Here are the different files we need to build the image

$ tree
.
β”œβ”€β”€ 50-server.cnf # Mariadb configuration file
β”œβ”€β”€ Dockerfile # The dockerfile to build the image
└── script.sh # Database configuration script

Dockerfile

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update
RUN apt install -y mariadb-server # COPY THE CONF FOR THE BIND AND THE SQL SCRIPT FOR THE PRIVILEGECOPY 50-server.cnf /etc/mysql/mariadb.conf.d/
# COPY THE SCRIPT IN THE IMAGES AND MODIFY THE EXECUTION RIGHTS OF ITCOPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT [ "/script.sh" ]

By default, the server does not accept external connections, or rather, it only accepts local connections (from the LoopBack address: localhost = 127.0.0.1). We need change that !

50-server.cnf

[server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
# bind-address = 127.0.0.1 # You need to change this line to allow external connectionsbind-address = 0.0.0.0 # Now it's better :-)expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb][mariadb-10.5]

Script.sh will be executed at entrypoint at runtime. this allow us to initialize the environment variables with an .envfile

script.sh

#!/bin/sh
service mysql start # CREATE USER #echo"CREATE USER '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
# PRIVILGES FOR ROOT AND USER FOR ALL IP ADRESS #echo"GRANT ALL PRIVILEGES ON *.* TO '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
echo"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '$BDD_ROOT_PASSWORD';"| mysql
echo"FLUSH PRIVILEGES;"| mysql
# CREAT WORDPRESS DATABASE #echo"CREATE DATABASE $BDD_NAME;"| mysql
kill$(cat /var/run/mysqld/mysqld.pid)
mysqld

DOCKER BUILD :

$ docker build -t my-mariadb . ......
......
Successfully built 6ad0c955aa67
Successfully tagged my-mariadb:latest πŸ‘

For this example, we'll change to \home and run my-mariadb image with an environment file.

$ cd /home

Create .env file in which username, user, password, database name, root password.

This information will be embedded in the container at runtime.

$ vim .env
BDD_USER=user
BDD_USER_PASSWORD=safepwd
BDD_NAME=wordpress
BDD_ROOT_PASSWORD=safepwdroot

To run the image you will need a specific env file and image name

$ docker run -tid --name testmariadb --env-file .env my-mariadb

The container is well executed, we can check with a docker ps

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e058b2f18f my-mariadb "/script.sh" 22 seconds ago Up 22 seconds testmariadb

Enter the container to check if our variables have integrated

$ docker exec -ti testmariadb bash root@34e058b2f18f:/# 

Everything is good 🀩

root@34e058b2f18f:/# env
HOSTNAME=34e058b2f18f
PWD=/
BDD_NAME=wordpress
HOME=/root
BDD_USER_PASSWORD=safepwd
TERM=xterm
SHLVL=1
BDD_ROOT_PASSWORD=safepwdroot
BDD_USER=user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

Check if the conf file has been copied

root@34e058b2f18f:/# cat /etc/mysql/mariadb.conf.d/50-server.cnf [server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
bind-address = 0.0.0.0
expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb]

Let's start mysql to check users and database

root@34e058b2f18f:/# mysql Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.38-MariaDB-0+deb10u1 Debian 10
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Check if our user and root is enabled for any host

MariaDB [(none)]>SELECT user,host,password FROMmysql.user;
+------+-----------+-------------------------------------------+
| user | host | password |
+------+-----------+-------------------------------------------+
| root | localhost | |
| user | % | *1C848575FF465642717BE88F2015E168769A62F3 |
| root | % | *FDB22E6F75BD75009DEE947AFD0BD73CB7EB88DA |
+------+-----------+-------------------------------------------+3 rows inset (0.005 sec)

Check if the "wordpress" database has been created

MariaDB [(none)]> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| wordpress |
+--------------------+4 rows inset (0.005 sec)

Starter Pack [ MariaDB - Adminer ]

In the previous part we saw how to write a dockerfile and build the image using docker build

In this part we will see how to use docker compose and write a docker-compose.yml

But first, we will see the configuration and the creation of the dockerfile for Adminer.

Adminer is a tool for managing content in databases. It natively supports MySQL, MariaDB, PostgreSQL, SQLite,

Once installed, we will be able to connect to our database from the Web Adminer interface 😎

Dockerfile (Adminer)

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update RUN apt install -y adminer # COPY THE CONF FILE COPY 000-default.conf /etc/apache2/sites-available/
RUN echo 'ServerName adminer' >> /etc/apache2/apache2.conf
# START AND CONF RUN service apache2 start && a2enconf adminer.conf ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

000-default.conf (Adminer)

<VirtualHost *:80>
DocumentRoot /etc/adminer
Alias /adminer /etc/adminer
<Directory /etc/adminer>
Require all granted
DirectoryIndex conf.php
</Directory> ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

DOCKER-COMPOSE

What is Docker Compose?

Docker Compose is a tool that was developed to help define and share multi-container applications.

With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

BASIC DOCKER COMMANDS

  • docker-compose build : To build the images
  • docker-compose up -d : To run containers in daemon mode
  • docker-compose up --build -d : To build images and run containers in daemon mode {my favorite :-)}
  • docker-compose start/stop : To start and stop services
  • docker-compose down : To stop and delete containers

It is important that the project structure is consistent with the dockerfiles and docker-compose.yml

$ tree .
β”œβ”€β”€ adminer_directory
β”‚ β”œβ”€β”€ 000-default.conf β”‚ └── Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ .env # same .env as before 
β”œβ”€β”€ mariadb_directory
β”‚ β”œβ”€β”€ 50-server.cnf # Same file seen above
β”‚ β”œβ”€β”€ Dockerfile # Same file seen above
β”‚ └── script.sh # Same file seen above
└── my_volume. # Persistent volume

docker-compose.yml

version: '3.5'services:adminer:container_name: Adminer # Name redirect to IP -> 172.X.X.Zbuild: adminer_directory/. # Build the dockerfile in ./adminer_directory/Dockerfile restart: always # Restart the container if it has stoppedports:- "80:80" # Redirect port 80 of Adminer on the hostnetworks:- mynetwork # Use mynetwork for communicate with mariadbmariadb:container_name: Mariadbbuild: mariadb_directory/.restart: alwaysnetworks:- mynetworkvolumes:- db:/var/lib/mysqlenv_file: .env# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge # Remember the different types of Networks, I showed you before ???# VOLUMEvolumes:db:driver: localdriver_opts: # Options specific to the drivertype: 'none'o: 'bind'device: ./my_volume # Persistent volume

The docker-compose.yml is edited.

The various essential elements of the infrastructure being positioned in the right place.

We will be able to launch our infrastructure using the command : docker-compose up --build -d .

This will build and then launch the images.

$ docker-compose up --build -d
....
....
Creating Mariadb ... done
Creating Adminer ... done
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b1e14853a6e mdb-adm_adminer "/usr/sbin/apache2ct…" 1 minutes ago Up 1 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp Adminer
4cb7c3cb88f8 mdb-adm_mariadb "/script.sh" 1 minutes ago Up 1 minutes Mariadb

The launch of our containers went well.

We will be able to connect to our database through the Adminer web interface using the host address.

For my part, the address of my host is 192.168.64.13, because i work remotely on a vm.

Most likely your host address is localhost or 127.0.0.1.

Adminer will ask us for the connection information.

This information corresponds to the information present in the ".env" file

The server address to enter is Mariadb

USERNAME = user
PASSWORD = safepwd
DATABASE = wordpress

Great the connection works πŸ‘πŸΌ

You can also log in as root. You just have to put in "root" in user and the password present in the env file.

PHP-FPM & NGNIX

In this part we will create a simple infrastructure allowing to separate nginx and php.

We will then use this same infrastructure to implement the SSL certificate and communicate only on port 443 to connect to our web server.

$ tree
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ nginx
β”‚ β”œβ”€β”€ conf
β”‚ β”‚ └── default
β”‚ └── Dockerfile
└── wordpress
β”œβ”€β”€ conf
β”‚ β”œβ”€β”€ index.php
β”‚ └── www.conf
└── Dockerfile

To work, nginx and php need to have access to the same file.

This is why our "wordress" volume is common to both containers.

Both will share the folder /var/www/html

docker-compose.yml

version: '3.5'services:ngnix:container_name: ngnixbuild: ./nginx/restart: alwaysvolumes:- WordPress:/var/www/htmldepends_on:- wordpressports:- "80:80"networks:- mynetworkwordpress:container_name: wordpressbuild: ./wordpress/restart: alwaysvolumes:- WordPress:/var/www/htmlnetworks:- mynetwork# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge# VOLUMEvolumes:WordPress:driver: localdriver_opts:type: 'none'o: 'bind'device: /home/tliot/data/website

Installing NGINX

Dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# NGINX INSTALLATIONRUN apt-get install -y nginx
# Copy of default web page configurationCOPY ./conf/default /etc/nginx/sites-available/default
ENTRYPOINT ["nginx", "-g", "daemon off;"]

default

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # <------------ Redirect to wordpress container
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Installing PHP-FPM

dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# UDPATE & INSTALLATIONRUN apt install php-fpm -y
# To create the PID file (/run/php/php7.3-fpm.pid)RUN mkdir /run/php
# To allow external connectionsCOPY ./conf/www.conf /etc/php/7.3/fpm/pool.d/
# To create index.php COPY ./conf/index.php /var/www/html/wordpress/index.php
# Is optional, just a metadataEXPOSE 9000 ENTRYPOINT ["/usr/sbin/php-fpm7.3","-F" ]

index.php

<? php echo phpinfo(); ?>

www.conf

[www]
user = www-data
group = www-data
# listen = 127.0.0.1:9000 # Change this line
listen = 9000 # Now it's better
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

Connecting NGINX

Local Domains in Linux

Configure DNS Locally Using /etc/hosts File in Linux

Now open the /etc/hosts file using your editor of choice as follows

sudo vi /etc/hosts

Then add the lines below to the end of the file as shown in the screen shot below.

127.0.0.1 localhost
255.255.255.255	broadcasthost
::1 localhost
192.168.64.13	tliot.42.fr # <--- Principal Domains
192.168.64.13	adminer.tliot.42.fr # <--- adminer subdomain (optional)
192.168.64.13	*.tliot.42.fr # <--- all subdomain (optional)

Next, test if everything is working well as expected, using the ping command.

$ ping tliot.42.fr
PING tliot.42.fr (192.168.64.13): 56 data bytes
64 bytes from 192.168.64.13: icmp_seq=0 ttl=64 time=1.919 ms
64 bytes from 192.168.64.13: icmp_seq=1 ttl=64 time=2.046 ms
64 bytes from 192.168.64.13: icmp_seq=2 ttl=64 time=2.391 ms
64 bytes from 192.168.64.13: icmp_seq=3 ttl=64 time=2.017 ms
64 bytes from 192.168.64.13: icmp_seq=4 ttl=64 time=2.481 ms
^C
--- tliot.42.fr ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss

Setup a self-signed SSL certificate

Create the self-signed SSL certificate:

RUN 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=Ile-de-France/L=Paris/O=42/OU=42Paris/CN=TLIOT/UID=TTT'

Create a new configuration snippet file for Nginx:

RUN echo "ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;\nssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;" > /etc/nginx/snippets/self-signed.conf

Create a strong Diffie-Hellman group:

RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create a configuration snippet with strong encryption settings:

COPY ./conf/ssl-params.conf /etc/nginx/snippets/

ssl-params.conf

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Configure Nginx site to use certificate:

server {
listen 443 ssl default_server; <--- 80 to 443
listen [::]:443 ssl default_server; <--- 80 to 443
server_name tliot.42.fr; <--- _ to tliot.42.fr
# ssl include snippets/self-signed.conf; <--- self-signed SSL
include snippets/ssl-params.conf; <--- strong encryption
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Configure docker-compose.yml site to use 443:

docker-compose.yml

 ngnix:
container_name: ngnix
build: ./nginx/
restart: always
volumes:
- WordPress:/var/www/html
depends_on:
- wordpress
ports:
- "443:443" <--- 80:80 to 443:443
networks:
- mynetwork

Testing the SSL Server

Next, test whether the SSL encryption is working.

On your browser, type the prefix http:// then your domain name:

https://server_domain

Since the certificate is not already signed by a trusted certificate authority, you will most likely get a warning like the one below:

You will see a warning that may pop-up because the SSL certificate created earlier isn’t signed by a trusted certificate authority:

It's goood πŸ‘πŸΌ

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Inception

This project aims to deepen the knowledge of system administration.

Use of dockerfile for the creation and management of custom images, micro services.

Use of docker-compose for the deployment of containers, the creation and management of the network, storage space, etc ...

Table of contents

Local Domains in Linux

Structure of the project with the bonuses

DOCKER

BASIC DOCKER COMMANDS

  • docker ps -a : List active containers (-a is for showing all containers, running and stopped)
  • docker stop <id>/<name> : Stop running containers
  • docker start <id>/<name> : Start stopped containers
  • docker rm -f <id>/<name> : Remove containers (-f is for force the removal of a running container)
  • docker exec -it <name> bash : Execute a command in a running container

Tips to delete all containers, use: docker rm -f $(docker ps -qa)

DOCKER RUN

$ docker run [OPTIONS] IMAGE[:TAG]
ParametersDescription
-dRun container in background (daemon mode)
-itcreating an interactive container
-pPublish a container port(s) to the host
--rmAutomatically remove the container when it exits
--hostnameContainer host name
--nameAssign a name to the container

Exemple

$ docker run -d -ti -p 80:80 --rm --name web-ngnix --hostname nginx-container nginx:latest

use docker ps to list running containers

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86335dfeaa0b nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web-ngnix

We can see that the container is running in daemon mode. That the exposure of the ports is well done and that the name of the container is the one that we specified in parameter

$ docker exec -it web-ngnix bash

The docker exec command runs a new command in a running container.

$ root@nginx-container:/#

We can now see that the name specified in --hostname is applied

DOCKER VOLUMES

The advantages of volumes :

  • Easy to persist data.
  • Convenient for making backups
  • Share data between multiple containers
  • Multi-containers and permissions

Basic command for managed volumes :

  • docker volume ls : list volumes

  • docker volume create <name> : creating a new volume

  • docker volume rm <name> : delete a volume

  • docker volume inspect <name> : inspection of a volume

The different types of volumes :

  • Bind Mount : Bind mounts are dependent on the directory structure and OS of the host machine
  • Volumes Docker : volumes are completely managed by Docker
  • TMPFS : As opposed to volumes and bind mounts, a tmpfs mount is temporary, and only persisted in the host memory. When the container stops, the tmpfs mount is removed, and files written there won’t be persisted.

DOCKER RUN WITH VOLUMES

1. Bind Mount :

sudo mkdir /data (creation of mount folder is necessary otherwise error will appear when using docker run)

docker run -d --name TestBindMount --mount type=bind,source=/data/,target=/usr/share/nginx/html -p 80:80 nginx:latest

docker exec -ti TestBindMount bash

2. Volumes Docker :

docker volume create mynginx (optional because if the volume is not created, docker will do it)

docker run -d --name TestVolume --mount type=volume,src=mynginx,destination=/usr/share/nginx/html -p 81:80 nginx:latest

docker exec -ti TestVolume bash

3. Tmpfs:

docker run -d --name TestTmpfs --mount type=tmpfs,destination=/usr/share/nginx/html -p 82:80 nginx:latest

docker exec -ti TestTmpfs bash

To check data persistence you can delete all containers and recreate them !! (do not recreate the volumes)

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0096643b045 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->80/tcp, :::82->80/tcp TestTmpfs
92260c1f5880 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:81->80/tcp, :::81->80/tcp TestVolume
dcad272f7531 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp TestBindMount

In each container modify/create the /usr/share/nginx/html/index.html, Remove containers and recreate. Now check if the changes have been saved.

If you are running docker on your OS. You can admire the changes from your websites.

ENVIRONEMENT VARIABLE (ENV, ENVFILE...)

$ docker run -tid --name testenv --env MYVAR="123" debian:latest

Add to the docker environment the variable MYVAR=123

$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@cb9e44034297:/# env
HOSTNAME=cb9e44034297
MYVAR=123
PWD=/
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

This method works but is not secure for example for passwords. To do this we will be able to add an env file ".ENV"

To do this, we will create a ".ENV" file in which we will put our environment variables. "vim .ENV"

MYPASSWORD="safepassword"
MYUSER="secretuser"
MYDB="BDD1"
$ docker run -tid --name testenv --env-file .ENV debian:latest
$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@553c2ac8a657:/# env
HOSTNAME=553c2ac8a657
PWD=/
HOME=/root
MYPASSWORD="safepassword"
TERM=xterm
SHLVL=1
MYUSER="secretuser"
MYDB="BDD1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

DOCKER NETWORK

  • Communication between containers or outside
  • Different types : bridge, host, none, overlay
  • Be careful, a container does not have a fixed IP address (stop / start)

Basic command for managed network :

  • docker network ls : List networks

  • docker network create <name> : Create a network

  • docker network rm <name> : Remove one or more networks

  • docker network inspect <name> : Display detailed information on one or more networks

IPs are not static

In general, IPs in a network are not static.

The addressing of the Ips depends on the starting order of the containers.

Exemple

Create bridge network with name, mynetwork :

$ docker network create --driver=bridge mynetwork

Start two container connect to network "mynetwork"

$ docker run -d --name c1 --network mynetwork nginx:latest
$ docker run -d --name c2 --network mynetwork nginx:latest

Container 1 will have as ip address : 172.26.0.2

$ docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2 

Container 2 will have as ip address : 172.26.0.3

$ docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3

We will now reverse the boot order

sudo docker stop c1
sudo docker stop c2
### reverse containers start order ###
sudo docker start c2
sudo docker start c1

We can see that the ip addresses are no longer the same

docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3
docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2

If the ips change, how do the containers communicate ?

The containers will have to communicate with their name which redirects to the ip.

sudo docker exec -ti c1 bash root@54bb6caca8fb:/# apt update && apt install iputils-ping -y
### ping install ###
root@54bb6caca8fb:/# ping c2
PING c2 (172.26.0.2) 56(84) bytes of data.
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=2 ttl=64 time=0.204 ms

It will therefore be necessary to use the name of the containers, in our different configurations, applications, programs to communicate. Container names are used as domain names.

DOCKERFILE

Dockerfile is a configuration file for the purpose of creating an image

Dockerfile benefit

  • Restart an image creation at any time
  • Better configuration visibility
  • Dockerfile editing script
  • Image creation, production or development

Instructions Dockerfile

β–‘InstructionsDescription
1FROMNew build stage and sets the Base Image for subsequent instructions.
2MAINTAINERauthor
3ARGDefines a variable that users can pass when building the image
4ENVEnvironment variable
4LABELAdding metadata
5VOLUMECreate a mount point
6RUNExecute a command when creating the image
6COPY // ADDAdd a file and directory in the image
6WORKDIRAllows you to change the current path
7EXPOSEPort listened by the container (metadata)
9CMD // ENTRYPOINTExecute a command when the container starts

BUILD A IMAGE

We will now create a mariadb image

Here are the different files we need to build the image

$ tree
.
β”œβ”€β”€ 50-server.cnf # Mariadb configuration file
β”œβ”€β”€ Dockerfile # The dockerfile to build the image
└── script.sh # Database configuration script

Dockerfile

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update
RUN apt install -y mariadb-server # COPY THE CONF FOR THE BIND AND THE SQL SCRIPT FOR THE PRIVILEGECOPY 50-server.cnf /etc/mysql/mariadb.conf.d/
# COPY THE SCRIPT IN THE IMAGES AND MODIFY THE EXECUTION RIGHTS OF ITCOPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT [ "/script.sh" ]

By default, the server does not accept external connections, or rather, it only accepts local connections (from the LoopBack address: localhost = 127.0.0.1). We need change that !

50-server.cnf

[server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
# bind-address = 127.0.0.1 # You need to change this line to allow external connectionsbind-address = 0.0.0.0 # Now it's better :-)expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb][mariadb-10.5]

Script.sh will be executed at entrypoint at runtime. this allow us to initialize the environment variables with an .envfile

script.sh

#!/bin/sh
service mysql start # CREATE USER #echo"CREATE USER '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
# PRIVILGES FOR ROOT AND USER FOR ALL IP ADRESS #echo"GRANT ALL PRIVILEGES ON *.* TO '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
echo"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '$BDD_ROOT_PASSWORD';"| mysql
echo"FLUSH PRIVILEGES;"| mysql
# CREAT WORDPRESS DATABASE #echo"CREATE DATABASE $BDD_NAME;"| mysql
kill$(cat /var/run/mysqld/mysqld.pid)
mysqld

DOCKER BUILD :

$ docker build -t my-mariadb . ......
......
Successfully built 6ad0c955aa67
Successfully tagged my-mariadb:latest πŸ‘

For this example, we'll change to \home and run my-mariadb image with an environment file.

$ cd /home

Create .env file in which username, user, password, database name, root password.

This information will be embedded in the container at runtime.

$ vim .env
BDD_USER=user
BDD_USER_PASSWORD=safepwd
BDD_NAME=wordpress
BDD_ROOT_PASSWORD=safepwdroot

To run the image you will need a specific env file and image name

$ docker run -tid --name testmariadb --env-file .env my-mariadb

The container is well executed, we can check with a docker ps

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e058b2f18f my-mariadb "/script.sh" 22 seconds ago Up 22 seconds testmariadb

Enter the container to check if our variables have integrated

$ docker exec -ti testmariadb bash root@34e058b2f18f:/# 

Everything is good 🀩

root@34e058b2f18f:/# env
HOSTNAME=34e058b2f18f
PWD=/
BDD_NAME=wordpress
HOME=/root
BDD_USER_PASSWORD=safepwd
TERM=xterm
SHLVL=1
BDD_ROOT_PASSWORD=safepwdroot
BDD_USER=user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

Check if the conf file has been copied

root@34e058b2f18f:/# cat /etc/mysql/mariadb.conf.d/50-server.cnf [server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
bind-address = 0.0.0.0
expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb]

Let's start mysql to check users and database

root@34e058b2f18f:/# mysql Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.38-MariaDB-0+deb10u1 Debian 10
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Check if our user and root is enabled for any host

MariaDB [(none)]>SELECT user,host,password FROMmysql.user;
+------+-----------+-------------------------------------------+
| user | host | password |
+------+-----------+-------------------------------------------+
| root | localhost | |
| user | % | *1C848575FF465642717BE88F2015E168769A62F3 |
| root | % | *FDB22E6F75BD75009DEE947AFD0BD73CB7EB88DA |
+------+-----------+-------------------------------------------+3 rows inset (0.005 sec)

Check if the "wordpress" database has been created

MariaDB [(none)]> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| wordpress |
+--------------------+4 rows inset (0.005 sec)

Starter Pack [ MariaDB - Adminer ]

In the previous part we saw how to write a dockerfile and build the image using docker build

In this part we will see how to use docker compose and write a docker-compose.yml

But first, we will see the configuration and the creation of the dockerfile for Adminer.

Adminer is a tool for managing content in databases. It natively supports MySQL, MariaDB, PostgreSQL, SQLite,

Once installed, we will be able to connect to our database from the Web Adminer interface 😎

Dockerfile (Adminer)

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update RUN apt install -y adminer # COPY THE CONF FILE COPY 000-default.conf /etc/apache2/sites-available/
RUN echo 'ServerName adminer' >> /etc/apache2/apache2.conf
# START AND CONF RUN service apache2 start && a2enconf adminer.conf ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

000-default.conf (Adminer)

<VirtualHost *:80>
DocumentRoot /etc/adminer
Alias /adminer /etc/adminer
<Directory /etc/adminer>
Require all granted
DirectoryIndex conf.php
</Directory> ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

DOCKER-COMPOSE

What is Docker Compose?

Docker Compose is a tool that was developed to help define and share multi-container applications.

With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

BASIC DOCKER COMMANDS

  • docker-compose build : To build the images
  • docker-compose up -d : To run containers in daemon mode
  • docker-compose up --build -d : To build images and run containers in daemon mode {my favorite :-)}
  • docker-compose start/stop : To start and stop services
  • docker-compose down : To stop and delete containers

It is important that the project structure is consistent with the dockerfiles and docker-compose.yml

$ tree .
β”œβ”€β”€ adminer_directory
β”‚ β”œβ”€β”€ 000-default.conf β”‚ └── Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ .env # same .env as before 
β”œβ”€β”€ mariadb_directory
β”‚ β”œβ”€β”€ 50-server.cnf # Same file seen above
β”‚ β”œβ”€β”€ Dockerfile # Same file seen above
β”‚ └── script.sh # Same file seen above
└── my_volume. # Persistent volume

docker-compose.yml

version: '3.5'services:adminer:container_name: Adminer # Name redirect to IP -> 172.X.X.Zbuild: adminer_directory/. # Build the dockerfile in ./adminer_directory/Dockerfile restart: always # Restart the container if it has stoppedports:- "80:80" # Redirect port 80 of Adminer on the hostnetworks:- mynetwork # Use mynetwork for communicate with mariadbmariadb:container_name: Mariadbbuild: mariadb_directory/.restart: alwaysnetworks:- mynetworkvolumes:- db:/var/lib/mysqlenv_file: .env# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge # Remember the different types of Networks, I showed you before ???# VOLUMEvolumes:db:driver: localdriver_opts: # Options specific to the drivertype: 'none'o: 'bind'device: ./my_volume # Persistent volume

The docker-compose.yml is edited.

The various essential elements of the infrastructure being positioned in the right place.

We will be able to launch our infrastructure using the command : docker-compose up --build -d .

This will build and then launch the images.

$ docker-compose up --build -d
....
....
Creating Mariadb ... done
Creating Adminer ... done
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b1e14853a6e mdb-adm_adminer "/usr/sbin/apache2ct…" 1 minutes ago Up 1 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp Adminer
4cb7c3cb88f8 mdb-adm_mariadb "/script.sh" 1 minutes ago Up 1 minutes Mariadb

The launch of our containers went well.

We will be able to connect to our database through the Adminer web interface using the host address.

For my part, the address of my host is 192.168.64.13, because i work remotely on a vm.

Most likely your host address is localhost or 127.0.0.1.

Adminer will ask us for the connection information.

This information corresponds to the information present in the ".env" file

The server address to enter is Mariadb

USERNAME = user
PASSWORD = safepwd
DATABASE = wordpress

Great the connection works πŸ‘πŸΌ

You can also log in as root. You just have to put in "root" in user and the password present in the env file.

PHP-FPM & NGNIX

In this part we will create a simple infrastructure allowing to separate nginx and php.

We will then use this same infrastructure to implement the SSL certificate and communicate only on port 443 to connect to our web server.

$ tree
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ nginx
β”‚ β”œβ”€β”€ conf
β”‚ β”‚ └── default
β”‚ └── Dockerfile
└── wordpress
β”œβ”€β”€ conf
β”‚ β”œβ”€β”€ index.php
β”‚ └── www.conf
└── Dockerfile

To work, nginx and php need to have access to the same file.

This is why our "wordress" volume is common to both containers.

Both will share the folder /var/www/html

docker-compose.yml

version: '3.5'services:ngnix:container_name: ngnixbuild: ./nginx/restart: alwaysvolumes:- WordPress:/var/www/htmldepends_on:- wordpressports:- "80:80"networks:- mynetworkwordpress:container_name: wordpressbuild: ./wordpress/restart: alwaysvolumes:- WordPress:/var/www/htmlnetworks:- mynetwork# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge# VOLUMEvolumes:WordPress:driver: localdriver_opts:type: 'none'o: 'bind'device: /home/tliot/data/website

Installing NGINX

Dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# NGINX INSTALLATIONRUN apt-get install -y nginx
# Copy of default web page configurationCOPY ./conf/default /etc/nginx/sites-available/default
ENTRYPOINT ["nginx", "-g", "daemon off;"]

default

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # <------------ Redirect to wordpress container
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Installing PHP-FPM

dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# UDPATE & INSTALLATIONRUN apt install php-fpm -y
# To create the PID file (/run/php/php7.3-fpm.pid)RUN mkdir /run/php
# To allow external connectionsCOPY ./conf/www.conf /etc/php/7.3/fpm/pool.d/
# To create index.php COPY ./conf/index.php /var/www/html/wordpress/index.php
# Is optional, just a metadataEXPOSE 9000 ENTRYPOINT ["/usr/sbin/php-fpm7.3","-F" ]

index.php

<? php echo phpinfo(); ?>

www.conf

[www]
user = www-data
group = www-data
# listen = 127.0.0.1:9000 # Change this line
listen = 9000 # Now it's better
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

Connecting NGINX

Local Domains in Linux

Configure DNS Locally Using /etc/hosts File in Linux

Now open the /etc/hosts file using your editor of choice as follows

sudo vi /etc/hosts

Then add the lines below to the end of the file as shown in the screen shot below.

127.0.0.1 localhost
255.255.255.255	broadcasthost
::1 localhost
192.168.64.13	tliot.42.fr # <--- Principal Domains
192.168.64.13	adminer.tliot.42.fr # <--- adminer subdomain (optional)
192.168.64.13	*.tliot.42.fr # <--- all subdomain (optional)

Next, test if everything is working well as expected, using the ping command.

$ ping tliot.42.fr
PING tliot.42.fr (192.168.64.13): 56 data bytes
64 bytes from 192.168.64.13: icmp_seq=0 ttl=64 time=1.919 ms
64 bytes from 192.168.64.13: icmp_seq=1 ttl=64 time=2.046 ms
64 bytes from 192.168.64.13: icmp_seq=2 ttl=64 time=2.391 ms
64 bytes from 192.168.64.13: icmp_seq=3 ttl=64 time=2.017 ms
64 bytes from 192.168.64.13: icmp_seq=4 ttl=64 time=2.481 ms
^C
--- tliot.42.fr ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss

Setup a self-signed SSL certificate

Create the self-signed SSL certificate:

RUN 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=Ile-de-France/L=Paris/O=42/OU=42Paris/CN=TLIOT/UID=TTT'

Create a new configuration snippet file for Nginx:

RUN echo "ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;\nssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;" > /etc/nginx/snippets/self-signed.conf

Create a strong Diffie-Hellman group:

RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create a configuration snippet with strong encryption settings:

COPY ./conf/ssl-params.conf /etc/nginx/snippets/

ssl-params.conf

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Configure Nginx site to use certificate:

server {
listen 443 ssl default_server; <--- 80 to 443
listen [::]:443 ssl default_server; <--- 80 to 443
server_name tliot.42.fr; <--- _ to tliot.42.fr
# ssl include snippets/self-signed.conf; <--- self-signed SSL
include snippets/ssl-params.conf; <--- strong encryption
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Configure docker-compose.yml site to use 443:

docker-compose.yml

 ngnix:
container_name: ngnix
build: ./nginx/
restart: always
volumes:
- WordPress:/var/www/html
depends_on:
- wordpress
ports:
- "443:443" <--- 80:80 to 443:443
networks:
- mynetwork

Testing the SSL Server

Next, test whether the SSL encryption is working.

On your browser, type the prefix http:// then your domain name:

https://server_domain

Since the certificate is not already signed by a trusted certificate authority, you will most likely get a warning like the one below:

You will see a warning that may pop-up because the SSL certificate created earlier isn’t signed by a trusted certificate authority:

It's goood πŸ‘πŸΌ

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Inception

This project aims to deepen the knowledge of system administration.

Use of dockerfile for the creation and management of custom images, micro services.

Use of docker-compose for the deployment of containers, the creation and management of the network, storage space, etc ...

Table of contents

Local Domains in Linux

Structure of the project with the bonuses

DOCKER

BASIC DOCKER COMMANDS

  • docker ps -a : List active containers (-a is for showing all containers, running and stopped)
  • docker stop <id>/<name> : Stop running containers
  • docker start <id>/<name> : Start stopped containers
  • docker rm -f <id>/<name> : Remove containers (-f is for force the removal of a running container)
  • docker exec -it <name> bash : Execute a command in a running container

Tips to delete all containers, use: docker rm -f $(docker ps -qa)

DOCKER RUN

$ docker run [OPTIONS] IMAGE[:TAG]
ParametersDescription
-dRun container in background (daemon mode)
-itcreating an interactive container
-pPublish a container port(s) to the host
--rmAutomatically remove the container when it exits
--hostnameContainer host name
--nameAssign a name to the container

Exemple

$ docker run -d -ti -p 80:80 --rm --name web-ngnix --hostname nginx-container nginx:latest

use docker ps to list running containers

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86335dfeaa0b nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web-ngnix

We can see that the container is running in daemon mode. That the exposure of the ports is well done and that the name of the container is the one that we specified in parameter

$ docker exec -it web-ngnix bash

The docker exec command runs a new command in a running container.

$ root@nginx-container:/#

We can now see that the name specified in --hostname is applied

DOCKER VOLUMES

The advantages of volumes :

  • Easy to persist data.
  • Convenient for making backups
  • Share data between multiple containers
  • Multi-containers and permissions

Basic command for managed volumes :

  • docker volume ls : list volumes

  • docker volume create <name> : creating a new volume

  • docker volume rm <name> : delete a volume

  • docker volume inspect <name> : inspection of a volume

The different types of volumes :

  • Bind Mount : Bind mounts are dependent on the directory structure and OS of the host machine
  • Volumes Docker : volumes are completely managed by Docker
  • TMPFS : As opposed to volumes and bind mounts, a tmpfs mount is temporary, and only persisted in the host memory. When the container stops, the tmpfs mount is removed, and files written there won’t be persisted.

DOCKER RUN WITH VOLUMES

1. Bind Mount :

sudo mkdir /data (creation of mount folder is necessary otherwise error will appear when using docker run)

docker run -d --name TestBindMount --mount type=bind,source=/data/,target=/usr/share/nginx/html -p 80:80 nginx:latest

docker exec -ti TestBindMount bash

2. Volumes Docker :

docker volume create mynginx (optional because if the volume is not created, docker will do it)

docker run -d --name TestVolume --mount type=volume,src=mynginx,destination=/usr/share/nginx/html -p 81:80 nginx:latest

docker exec -ti TestVolume bash

3. Tmpfs:

docker run -d --name TestTmpfs --mount type=tmpfs,destination=/usr/share/nginx/html -p 82:80 nginx:latest

docker exec -ti TestTmpfs bash

To check data persistence you can delete all containers and recreate them !! (do not recreate the volumes)

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0096643b045 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->80/tcp, :::82->80/tcp TestTmpfs
92260c1f5880 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:81->80/tcp, :::81->80/tcp TestVolume
dcad272f7531 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp TestBindMount

In each container modify/create the /usr/share/nginx/html/index.html, Remove containers and recreate. Now check if the changes have been saved.

If you are running docker on your OS. You can admire the changes from your websites.

ENVIRONEMENT VARIABLE (ENV, ENVFILE...)

$ docker run -tid --name testenv --env MYVAR="123" debian:latest

Add to the docker environment the variable MYVAR=123

$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@cb9e44034297:/# env
HOSTNAME=cb9e44034297
MYVAR=123
PWD=/
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

This method works but is not secure for example for passwords. To do this we will be able to add an env file ".ENV"

To do this, we will create a ".ENV" file in which we will put our environment variables. "vim .ENV"

MYPASSWORD="safepassword"
MYUSER="secretuser"
MYDB="BDD1"
$ docker run -tid --name testenv --env-file .ENV debian:latest
$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@553c2ac8a657:/# env
HOSTNAME=553c2ac8a657
PWD=/
HOME=/root
MYPASSWORD="safepassword"
TERM=xterm
SHLVL=1
MYUSER="secretuser"
MYDB="BDD1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

DOCKER NETWORK

  • Communication between containers or outside
  • Different types : bridge, host, none, overlay
  • Be careful, a container does not have a fixed IP address (stop / start)

Basic command for managed network :

  • docker network ls : List networks

  • docker network create <name> : Create a network

  • docker network rm <name> : Remove one or more networks

  • docker network inspect <name> : Display detailed information on one or more networks

IPs are not static

In general, IPs in a network are not static.

The addressing of the Ips depends on the starting order of the containers.

Exemple

Create bridge network with name, mynetwork :

$ docker network create --driver=bridge mynetwork

Start two container connect to network "mynetwork"

$ docker run -d --name c1 --network mynetwork nginx:latest
$ docker run -d --name c2 --network mynetwork nginx:latest

Container 1 will have as ip address : 172.26.0.2

$ docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2 

Container 2 will have as ip address : 172.26.0.3

$ docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3

We will now reverse the boot order

sudo docker stop c1
sudo docker stop c2
### reverse containers start order ###
sudo docker start c2
sudo docker start c1

We can see that the ip addresses are no longer the same

docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3
docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2

If the ips change, how do the containers communicate ?

The containers will have to communicate with their name which redirects to the ip.

sudo docker exec -ti c1 bash root@54bb6caca8fb:/# apt update && apt install iputils-ping -y
### ping install ###
root@54bb6caca8fb:/# ping c2
PING c2 (172.26.0.2) 56(84) bytes of data.
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=2 ttl=64 time=0.204 ms

It will therefore be necessary to use the name of the containers, in our different configurations, applications, programs to communicate. Container names are used as domain names.

DOCKERFILE

Dockerfile is a configuration file for the purpose of creating an image

Dockerfile benefit

  • Restart an image creation at any time
  • Better configuration visibility
  • Dockerfile editing script
  • Image creation, production or development

Instructions Dockerfile

β–‘InstructionsDescription
1FROMNew build stage and sets the Base Image for subsequent instructions.
2MAINTAINERauthor
3ARGDefines a variable that users can pass when building the image
4ENVEnvironment variable
4LABELAdding metadata
5VOLUMECreate a mount point
6RUNExecute a command when creating the image
6COPY // ADDAdd a file and directory in the image
6WORKDIRAllows you to change the current path
7EXPOSEPort listened by the container (metadata)
9CMD // ENTRYPOINTExecute a command when the container starts

BUILD A IMAGE

We will now create a mariadb image

Here are the different files we need to build the image

$ tree
.
β”œβ”€β”€ 50-server.cnf # Mariadb configuration file
β”œβ”€β”€ Dockerfile # The dockerfile to build the image
└── script.sh # Database configuration script

Dockerfile

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update
RUN apt install -y mariadb-server # COPY THE CONF FOR THE BIND AND THE SQL SCRIPT FOR THE PRIVILEGECOPY 50-server.cnf /etc/mysql/mariadb.conf.d/
# COPY THE SCRIPT IN THE IMAGES AND MODIFY THE EXECUTION RIGHTS OF ITCOPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT [ "/script.sh" ]

By default, the server does not accept external connections, or rather, it only accepts local connections (from the LoopBack address: localhost = 127.0.0.1). We need change that !

50-server.cnf

[server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
# bind-address = 127.0.0.1 # You need to change this line to allow external connectionsbind-address = 0.0.0.0 # Now it's better :-)expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb][mariadb-10.5]

Script.sh will be executed at entrypoint at runtime. this allow us to initialize the environment variables with an .envfile

script.sh

#!/bin/sh
service mysql start # CREATE USER #echo"CREATE USER '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
# PRIVILGES FOR ROOT AND USER FOR ALL IP ADRESS #echo"GRANT ALL PRIVILEGES ON *.* TO '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
echo"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '$BDD_ROOT_PASSWORD';"| mysql
echo"FLUSH PRIVILEGES;"| mysql
# CREAT WORDPRESS DATABASE #echo"CREATE DATABASE $BDD_NAME;"| mysql
kill$(cat /var/run/mysqld/mysqld.pid)
mysqld

DOCKER BUILD :

$ docker build -t my-mariadb . ......
......
Successfully built 6ad0c955aa67
Successfully tagged my-mariadb:latest πŸ‘

For this example, we'll change to \home and run my-mariadb image with an environment file.

$ cd /home

Create .env file in which username, user, password, database name, root password.

This information will be embedded in the container at runtime.

$ vim .env
BDD_USER=user
BDD_USER_PASSWORD=safepwd
BDD_NAME=wordpress
BDD_ROOT_PASSWORD=safepwdroot

To run the image you will need a specific env file and image name

$ docker run -tid --name testmariadb --env-file .env my-mariadb

The container is well executed, we can check with a docker ps

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e058b2f18f my-mariadb "/script.sh" 22 seconds ago Up 22 seconds testmariadb

Enter the container to check if our variables have integrated

$ docker exec -ti testmariadb bash root@34e058b2f18f:/# 

Everything is good 🀩

root@34e058b2f18f:/# env
HOSTNAME=34e058b2f18f
PWD=/
BDD_NAME=wordpress
HOME=/root
BDD_USER_PASSWORD=safepwd
TERM=xterm
SHLVL=1
BDD_ROOT_PASSWORD=safepwdroot
BDD_USER=user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

Check if the conf file has been copied

root@34e058b2f18f:/# cat /etc/mysql/mariadb.conf.d/50-server.cnf [server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
bind-address = 0.0.0.0
expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb]

Let's start mysql to check users and database

root@34e058b2f18f:/# mysql Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.38-MariaDB-0+deb10u1 Debian 10
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Check if our user and root is enabled for any host

MariaDB [(none)]>SELECT user,host,password FROMmysql.user;
+------+-----------+-------------------------------------------+
| user | host | password |
+------+-----------+-------------------------------------------+
| root | localhost | |
| user | % | *1C848575FF465642717BE88F2015E168769A62F3 |
| root | % | *FDB22E6F75BD75009DEE947AFD0BD73CB7EB88DA |
+------+-----------+-------------------------------------------+3 rows inset (0.005 sec)

Check if the "wordpress" database has been created

MariaDB [(none)]> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| wordpress |
+--------------------+4 rows inset (0.005 sec)

Starter Pack [ MariaDB - Adminer ]

In the previous part we saw how to write a dockerfile and build the image using docker build

In this part we will see how to use docker compose and write a docker-compose.yml

But first, we will see the configuration and the creation of the dockerfile for Adminer.

Adminer is a tool for managing content in databases. It natively supports MySQL, MariaDB, PostgreSQL, SQLite,

Once installed, we will be able to connect to our database from the Web Adminer interface 😎

Dockerfile (Adminer)

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update RUN apt install -y adminer # COPY THE CONF FILE COPY 000-default.conf /etc/apache2/sites-available/
RUN echo 'ServerName adminer' >> /etc/apache2/apache2.conf
# START AND CONF RUN service apache2 start && a2enconf adminer.conf ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

000-default.conf (Adminer)

<VirtualHost *:80>
DocumentRoot /etc/adminer
Alias /adminer /etc/adminer
<Directory /etc/adminer>
Require all granted
DirectoryIndex conf.php
</Directory> ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

DOCKER-COMPOSE

What is Docker Compose?

Docker Compose is a tool that was developed to help define and share multi-container applications.

With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

BASIC DOCKER COMMANDS

  • docker-compose build : To build the images
  • docker-compose up -d : To run containers in daemon mode
  • docker-compose up --build -d : To build images and run containers in daemon mode {my favorite :-)}
  • docker-compose start/stop : To start and stop services
  • docker-compose down : To stop and delete containers

It is important that the project structure is consistent with the dockerfiles and docker-compose.yml

$ tree .
β”œβ”€β”€ adminer_directory
β”‚ β”œβ”€β”€ 000-default.conf β”‚ └── Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ .env # same .env as before 
β”œβ”€β”€ mariadb_directory
β”‚ β”œβ”€β”€ 50-server.cnf # Same file seen above
β”‚ β”œβ”€β”€ Dockerfile # Same file seen above
β”‚ └── script.sh # Same file seen above
└── my_volume. # Persistent volume

docker-compose.yml

version: '3.5'services:adminer:container_name: Adminer # Name redirect to IP -> 172.X.X.Zbuild: adminer_directory/. # Build the dockerfile in ./adminer_directory/Dockerfile restart: always # Restart the container if it has stoppedports:- "80:80" # Redirect port 80 of Adminer on the hostnetworks:- mynetwork # Use mynetwork for communicate with mariadbmariadb:container_name: Mariadbbuild: mariadb_directory/.restart: alwaysnetworks:- mynetworkvolumes:- db:/var/lib/mysqlenv_file: .env# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge # Remember the different types of Networks, I showed you before ???# VOLUMEvolumes:db:driver: localdriver_opts: # Options specific to the drivertype: 'none'o: 'bind'device: ./my_volume # Persistent volume

The docker-compose.yml is edited.

The various essential elements of the infrastructure being positioned in the right place.

We will be able to launch our infrastructure using the command : docker-compose up --build -d .

This will build and then launch the images.

$ docker-compose up --build -d
....
....
Creating Mariadb ... done
Creating Adminer ... done
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b1e14853a6e mdb-adm_adminer "/usr/sbin/apache2ct…" 1 minutes ago Up 1 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp Adminer
4cb7c3cb88f8 mdb-adm_mariadb "/script.sh" 1 minutes ago Up 1 minutes Mariadb

The launch of our containers went well.

We will be able to connect to our database through the Adminer web interface using the host address.

For my part, the address of my host is 192.168.64.13, because i work remotely on a vm.

Most likely your host address is localhost or 127.0.0.1.

Adminer will ask us for the connection information.

This information corresponds to the information present in the ".env" file

The server address to enter is Mariadb

USERNAME = user
PASSWORD = safepwd
DATABASE = wordpress

Great the connection works πŸ‘πŸΌ

You can also log in as root. You just have to put in "root" in user and the password present in the env file.

PHP-FPM & NGNIX

In this part we will create a simple infrastructure allowing to separate nginx and php.

We will then use this same infrastructure to implement the SSL certificate and communicate only on port 443 to connect to our web server.

$ tree
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ nginx
β”‚ β”œβ”€β”€ conf
β”‚ β”‚ └── default
β”‚ └── Dockerfile
└── wordpress
β”œβ”€β”€ conf
β”‚ β”œβ”€β”€ index.php
β”‚ └── www.conf
└── Dockerfile

To work, nginx and php need to have access to the same file.

This is why our "wordress" volume is common to both containers.

Both will share the folder /var/www/html

docker-compose.yml

version: '3.5'services:ngnix:container_name: ngnixbuild: ./nginx/restart: alwaysvolumes:- WordPress:/var/www/htmldepends_on:- wordpressports:- "80:80"networks:- mynetworkwordpress:container_name: wordpressbuild: ./wordpress/restart: alwaysvolumes:- WordPress:/var/www/htmlnetworks:- mynetwork# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge# VOLUMEvolumes:WordPress:driver: localdriver_opts:type: 'none'o: 'bind'device: /home/tliot/data/website

Installing NGINX

Dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# NGINX INSTALLATIONRUN apt-get install -y nginx
# Copy of default web page configurationCOPY ./conf/default /etc/nginx/sites-available/default
ENTRYPOINT ["nginx", "-g", "daemon off;"]

default

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # <------------ Redirect to wordpress container
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Installing PHP-FPM

dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# UDPATE & INSTALLATIONRUN apt install php-fpm -y
# To create the PID file (/run/php/php7.3-fpm.pid)RUN mkdir /run/php
# To allow external connectionsCOPY ./conf/www.conf /etc/php/7.3/fpm/pool.d/
# To create index.php COPY ./conf/index.php /var/www/html/wordpress/index.php
# Is optional, just a metadataEXPOSE 9000 ENTRYPOINT ["/usr/sbin/php-fpm7.3","-F" ]

index.php

<? php echo phpinfo(); ?>

www.conf

[www]
user = www-data
group = www-data
# listen = 127.0.0.1:9000 # Change this line
listen = 9000 # Now it's better
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

Connecting NGINX

Local Domains in Linux

Configure DNS Locally Using /etc/hosts File in Linux

Now open the /etc/hosts file using your editor of choice as follows

sudo vi /etc/hosts

Then add the lines below to the end of the file as shown in the screen shot below.

127.0.0.1 localhost
255.255.255.255	broadcasthost
::1 localhost
192.168.64.13	tliot.42.fr # <--- Principal Domains
192.168.64.13	adminer.tliot.42.fr # <--- adminer subdomain (optional)
192.168.64.13	*.tliot.42.fr # <--- all subdomain (optional)

Next, test if everything is working well as expected, using the ping command.

$ ping tliot.42.fr
PING tliot.42.fr (192.168.64.13): 56 data bytes
64 bytes from 192.168.64.13: icmp_seq=0 ttl=64 time=1.919 ms
64 bytes from 192.168.64.13: icmp_seq=1 ttl=64 time=2.046 ms
64 bytes from 192.168.64.13: icmp_seq=2 ttl=64 time=2.391 ms
64 bytes from 192.168.64.13: icmp_seq=3 ttl=64 time=2.017 ms
64 bytes from 192.168.64.13: icmp_seq=4 ttl=64 time=2.481 ms
^C
--- tliot.42.fr ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss

Setup a self-signed SSL certificate

Create the self-signed SSL certificate:

RUN 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=Ile-de-France/L=Paris/O=42/OU=42Paris/CN=TLIOT/UID=TTT'

Create a new configuration snippet file for Nginx:

RUN echo "ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;\nssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;" > /etc/nginx/snippets/self-signed.conf

Create a strong Diffie-Hellman group:

RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create a configuration snippet with strong encryption settings:

COPY ./conf/ssl-params.conf /etc/nginx/snippets/

ssl-params.conf

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Configure Nginx site to use certificate:

server {
listen 443 ssl default_server; <--- 80 to 443
listen [::]:443 ssl default_server; <--- 80 to 443
server_name tliot.42.fr; <--- _ to tliot.42.fr
# ssl include snippets/self-signed.conf; <--- self-signed SSL
include snippets/ssl-params.conf; <--- strong encryption
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Configure docker-compose.yml site to use 443:

docker-compose.yml

 ngnix:
container_name: ngnix
build: ./nginx/
restart: always
volumes:
- WordPress:/var/www/html
depends_on:
- wordpress
ports:
- "443:443" <--- 80:80 to 443:443
networks:
- mynetwork

Testing the SSL Server

Next, test whether the SSL encryption is working.

On your browser, type the prefix http:// then your domain name:

https://server_domain

Since the certificate is not already signed by a trusted certificate authority, you will most likely get a warning like the one below:

You will see a warning that may pop-up because the SSL certificate created earlier isn’t signed by a trusted certificate authority:

It's goood πŸ‘πŸΌ

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Inception

This project aims to deepen the knowledge of system administration.

Use of dockerfile for the creation and management of custom images, micro services.

Use of docker-compose for the deployment of containers, the creation and management of the network, storage space, etc ...

Table of contents

Local Domains in Linux

Structure of the project with the bonuses

DOCKER

BASIC DOCKER COMMANDS

  • docker ps -a : List active containers (-a is for showing all containers, running and stopped)
  • docker stop <id>/<name> : Stop running containers
  • docker start <id>/<name> : Start stopped containers
  • docker rm -f <id>/<name> : Remove containers (-f is for force the removal of a running container)
  • docker exec -it <name> bash : Execute a command in a running container

Tips to delete all containers, use: docker rm -f $(docker ps -qa)

DOCKER RUN

$ docker run [OPTIONS] IMAGE[:TAG]
ParametersDescription
-dRun container in background (daemon mode)
-itcreating an interactive container
-pPublish a container port(s) to the host
--rmAutomatically remove the container when it exits
--hostnameContainer host name
--nameAssign a name to the container

Exemple

$ docker run -d -ti -p 80:80 --rm --name web-ngnix --hostname nginx-container nginx:latest

use docker ps to list running containers

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86335dfeaa0b nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web-ngnix

We can see that the container is running in daemon mode. That the exposure of the ports is well done and that the name of the container is the one that we specified in parameter

$ docker exec -it web-ngnix bash

The docker exec command runs a new command in a running container.

$ root@nginx-container:/#

We can now see that the name specified in --hostname is applied

DOCKER VOLUMES

The advantages of volumes :

  • Easy to persist data.
  • Convenient for making backups
  • Share data between multiple containers
  • Multi-containers and permissions

Basic command for managed volumes :

  • docker volume ls : list volumes

  • docker volume create <name> : creating a new volume

  • docker volume rm <name> : delete a volume

  • docker volume inspect <name> : inspection of a volume

The different types of volumes :

  • Bind Mount : Bind mounts are dependent on the directory structure and OS of the host machine
  • Volumes Docker : volumes are completely managed by Docker
  • TMPFS : As opposed to volumes and bind mounts, a tmpfs mount is temporary, and only persisted in the host memory. When the container stops, the tmpfs mount is removed, and files written there won’t be persisted.

DOCKER RUN WITH VOLUMES

1. Bind Mount :

sudo mkdir /data (creation of mount folder is necessary otherwise error will appear when using docker run)

docker run -d --name TestBindMount --mount type=bind,source=/data/,target=/usr/share/nginx/html -p 80:80 nginx:latest

docker exec -ti TestBindMount bash

2. Volumes Docker :

docker volume create mynginx (optional because if the volume is not created, docker will do it)

docker run -d --name TestVolume --mount type=volume,src=mynginx,destination=/usr/share/nginx/html -p 81:80 nginx:latest

docker exec -ti TestVolume bash

3. Tmpfs:

docker run -d --name TestTmpfs --mount type=tmpfs,destination=/usr/share/nginx/html -p 82:80 nginx:latest

docker exec -ti TestTmpfs bash

To check data persistence you can delete all containers and recreate them !! (do not recreate the volumes)

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0096643b045 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:82->80/tcp, :::82->80/tcp TestTmpfs
92260c1f5880 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:81->80/tcp, :::81->80/tcp TestVolume
dcad272f7531 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp TestBindMount

In each container modify/create the /usr/share/nginx/html/index.html, Remove containers and recreate. Now check if the changes have been saved.

If you are running docker on your OS. You can admire the changes from your websites.

ENVIRONEMENT VARIABLE (ENV, ENVFILE...)

$ docker run -tid --name testenv --env MYVAR="123" debian:latest

Add to the docker environment the variable MYVAR=123

$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@cb9e44034297:/# env
HOSTNAME=cb9e44034297
MYVAR=123
PWD=/
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

This method works but is not secure for example for passwords. To do this we will be able to add an env file ".ENV"

To do this, we will create a ".ENV" file in which we will put our environment variables. "vim .ENV"

MYPASSWORD="safepassword"
MYUSER="secretuser"
MYDB="BDD1"
$ docker run -tid --name testenv --env-file .ENV debian:latest
$ docker exec -ti testenv bash

Look in the container for the environment variables with the "env" command.

root@553c2ac8a657:/# env
HOSTNAME=553c2ac8a657
PWD=/
HOME=/root
MYPASSWORD="safepassword"
TERM=xterm
SHLVL=1
MYUSER="secretuser"
MYDB="BDD1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

DOCKER NETWORK

  • Communication between containers or outside
  • Different types : bridge, host, none, overlay
  • Be careful, a container does not have a fixed IP address (stop / start)

Basic command for managed network :

  • docker network ls : List networks

  • docker network create <name> : Create a network

  • docker network rm <name> : Remove one or more networks

  • docker network inspect <name> : Display detailed information on one or more networks

IPs are not static

In general, IPs in a network are not static.

The addressing of the Ips depends on the starting order of the containers.

Exemple

Create bridge network with name, mynetwork :

$ docker network create --driver=bridge mynetwork

Start two container connect to network "mynetwork"

$ docker run -d --name c1 --network mynetwork nginx:latest
$ docker run -d --name c2 --network mynetwork nginx:latest

Container 1 will have as ip address : 172.26.0.2

$ docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2 

Container 2 will have as ip address : 172.26.0.3

$ docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3

We will now reverse the boot order

sudo docker stop c1
sudo docker stop c2
### reverse containers start order ###
sudo docker start c2
sudo docker start c1

We can see that the ip addresses are no longer the same

docker inspect c1 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.3
docker inspect c2 --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.26.0.2

If the ips change, how do the containers communicate ?

The containers will have to communicate with their name which redirects to the ip.

sudo docker exec -ti c1 bash root@54bb6caca8fb:/# apt update && apt install iputils-ping -y
### ping install ###
root@54bb6caca8fb:/# ping c2
PING c2 (172.26.0.2) 56(84) bytes of data.
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=1 ttl=64 time=0.099 ms
64 bytes from c2.mynetwork (172.26.0.2): icmp_seq=2 ttl=64 time=0.204 ms

It will therefore be necessary to use the name of the containers, in our different configurations, applications, programs to communicate. Container names are used as domain names.

DOCKERFILE

Dockerfile is a configuration file for the purpose of creating an image

Dockerfile benefit

  • Restart an image creation at any time
  • Better configuration visibility
  • Dockerfile editing script
  • Image creation, production or development

Instructions Dockerfile

β–‘InstructionsDescription
1FROMNew build stage and sets the Base Image for subsequent instructions.
2MAINTAINERauthor
3ARGDefines a variable that users can pass when building the image
4ENVEnvironment variable
4LABELAdding metadata
5VOLUMECreate a mount point
6RUNExecute a command when creating the image
6COPY // ADDAdd a file and directory in the image
6WORKDIRAllows you to change the current path
7EXPOSEPort listened by the container (metadata)
9CMD // ENTRYPOINTExecute a command when the container starts

BUILD A IMAGE

We will now create a mariadb image

Here are the different files we need to build the image

$ tree
.
β”œβ”€β”€ 50-server.cnf # Mariadb configuration file
β”œβ”€β”€ Dockerfile # The dockerfile to build the image
└── script.sh # Database configuration script

Dockerfile

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update
RUN apt install -y mariadb-server # COPY THE CONF FOR THE BIND AND THE SQL SCRIPT FOR THE PRIVILEGECOPY 50-server.cnf /etc/mysql/mariadb.conf.d/
# COPY THE SCRIPT IN THE IMAGES AND MODIFY THE EXECUTION RIGHTS OF ITCOPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT [ "/script.sh" ]

By default, the server does not accept external connections, or rather, it only accepts local connections (from the LoopBack address: localhost = 127.0.0.1). We need change that !

50-server.cnf

[server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
# bind-address = 127.0.0.1 # You need to change this line to allow external connectionsbind-address = 0.0.0.0 # Now it's better :-)expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb][mariadb-10.5]

Script.sh will be executed at entrypoint at runtime. this allow us to initialize the environment variables with an .envfile

script.sh

#!/bin/sh
service mysql start # CREATE USER #echo"CREATE USER '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
# PRIVILGES FOR ROOT AND USER FOR ALL IP ADRESS #echo"GRANT ALL PRIVILEGES ON *.* TO '$BDD_USER'@'%' IDENTIFIED BY '$BDD_USER_PASSWORD';"| mysql
echo"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '$BDD_ROOT_PASSWORD';"| mysql
echo"FLUSH PRIVILEGES;"| mysql
# CREAT WORDPRESS DATABASE #echo"CREATE DATABASE $BDD_NAME;"| mysql
kill$(cat /var/run/mysqld/mysqld.pid)
mysqld

DOCKER BUILD :

$ docker build -t my-mariadb . ......
......
Successfully built 6ad0c955aa67
Successfully tagged my-mariadb:latest πŸ‘

For this example, we'll change to \home and run my-mariadb image with an environment file.

$ cd /home

Create .env file in which username, user, password, database name, root password.

This information will be embedded in the container at runtime.

$ vim .env
BDD_USER=user
BDD_USER_PASSWORD=safepwd
BDD_NAME=wordpress
BDD_ROOT_PASSWORD=safepwdroot

To run the image you will need a specific env file and image name

$ docker run -tid --name testmariadb --env-file .env my-mariadb

The container is well executed, we can check with a docker ps

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
34e058b2f18f my-mariadb "/script.sh" 22 seconds ago Up 22 seconds testmariadb

Enter the container to check if our variables have integrated

$ docker exec -ti testmariadb bash root@34e058b2f18f:/# 

Everything is good 🀩

root@34e058b2f18f:/# env
HOSTNAME=34e058b2f18f
PWD=/
BDD_NAME=wordpress
HOME=/root
BDD_USER_PASSWORD=safepwd
TERM=xterm
SHLVL=1
BDD_ROOT_PASSWORD=safepwdroot
BDD_USER=user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

Check if the conf file has been copied

root@34e058b2f18f:/# cat /etc/mysql/mariadb.conf.d/50-server.cnf [server][mysqld]user = mysql
pid-file = /run/mysqld/mysqld.pid
socket = /run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
lc-messages = en_US
skip-external-locking
bind-address = 0.0.0.0
expire_logs_days = 10
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
[embedded][mariadb]

Let's start mysql to check users and database

root@34e058b2f18f:/# mysql Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.38-MariaDB-0+deb10u1 Debian 10
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Check if our user and root is enabled for any host

MariaDB [(none)]>SELECT user,host,password FROMmysql.user;
+------+-----------+-------------------------------------------+
| user | host | password |
+------+-----------+-------------------------------------------+
| root | localhost | |
| user | % | *1C848575FF465642717BE88F2015E168769A62F3 |
| root | % | *FDB22E6F75BD75009DEE947AFD0BD73CB7EB88DA |
+------+-----------+-------------------------------------------+3 rows inset (0.005 sec)

Check if the "wordpress" database has been created

MariaDB [(none)]> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| wordpress |
+--------------------+4 rows inset (0.005 sec)

Starter Pack [ MariaDB - Adminer ]

In the previous part we saw how to write a dockerfile and build the image using docker build

In this part we will see how to use docker compose and write a docker-compose.yml

But first, we will see the configuration and the creation of the dockerfile for Adminer.

Adminer is a tool for managing content in databases. It natively supports MySQL, MariaDB, PostgreSQL, SQLite,

Once installed, we will be able to connect to our database from the Web Adminer interface 😎

Dockerfile (Adminer)

# SPECIFIES DISTRIBUTIONFROM debian:buster
# UPDATE AND INSTALLATIONRUN apt-get update RUN apt install -y adminer # COPY THE CONF FILE COPY 000-default.conf /etc/apache2/sites-available/
RUN echo 'ServerName adminer' >> /etc/apache2/apache2.conf
# START AND CONF RUN service apache2 start && a2enconf adminer.conf ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

000-default.conf (Adminer)

<VirtualHost *:80>
DocumentRoot /etc/adminer
Alias /adminer /etc/adminer
<Directory /etc/adminer>
Require all granted
DirectoryIndex conf.php
</Directory> ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

DOCKER-COMPOSE

What is Docker Compose?

Docker Compose is a tool that was developed to help define and share multi-container applications.

With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

BASIC DOCKER COMMANDS

  • docker-compose build : To build the images
  • docker-compose up -d : To run containers in daemon mode
  • docker-compose up --build -d : To build images and run containers in daemon mode {my favorite :-)}
  • docker-compose start/stop : To start and stop services
  • docker-compose down : To stop and delete containers

It is important that the project structure is consistent with the dockerfiles and docker-compose.yml

$ tree .
β”œβ”€β”€ adminer_directory
β”‚ β”œβ”€β”€ 000-default.conf β”‚ └── Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ .env # same .env as before 
β”œβ”€β”€ mariadb_directory
β”‚ β”œβ”€β”€ 50-server.cnf # Same file seen above
β”‚ β”œβ”€β”€ Dockerfile # Same file seen above
β”‚ └── script.sh # Same file seen above
└── my_volume. # Persistent volume

docker-compose.yml

version: '3.5'services:adminer:container_name: Adminer # Name redirect to IP -> 172.X.X.Zbuild: adminer_directory/. # Build the dockerfile in ./adminer_directory/Dockerfile restart: always # Restart the container if it has stoppedports:- "80:80" # Redirect port 80 of Adminer on the hostnetworks:- mynetwork # Use mynetwork for communicate with mariadbmariadb:container_name: Mariadbbuild: mariadb_directory/.restart: alwaysnetworks:- mynetworkvolumes:- db:/var/lib/mysqlenv_file: .env# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge # Remember the different types of Networks, I showed you before ???# VOLUMEvolumes:db:driver: localdriver_opts: # Options specific to the drivertype: 'none'o: 'bind'device: ./my_volume # Persistent volume

The docker-compose.yml is edited.

The various essential elements of the infrastructure being positioned in the right place.

We will be able to launch our infrastructure using the command : docker-compose up --build -d .

This will build and then launch the images.

$ docker-compose up --build -d
....
....
Creating Mariadb ... done
Creating Adminer ... done
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b1e14853a6e mdb-adm_adminer "/usr/sbin/apache2ct…" 1 minutes ago Up 1 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp Adminer
4cb7c3cb88f8 mdb-adm_mariadb "/script.sh" 1 minutes ago Up 1 minutes Mariadb

The launch of our containers went well.

We will be able to connect to our database through the Adminer web interface using the host address.

For my part, the address of my host is 192.168.64.13, because i work remotely on a vm.

Most likely your host address is localhost or 127.0.0.1.

Adminer will ask us for the connection information.

This information corresponds to the information present in the ".env" file

The server address to enter is Mariadb

USERNAME = user
PASSWORD = safepwd
DATABASE = wordpress

Great the connection works πŸ‘πŸΌ

You can also log in as root. You just have to put in "root" in user and the password present in the env file.

PHP-FPM & NGNIX

In this part we will create a simple infrastructure allowing to separate nginx and php.

We will then use this same infrastructure to implement the SSL certificate and communicate only on port 443 to connect to our web server.

$ tree
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ nginx
β”‚ β”œβ”€β”€ conf
β”‚ β”‚ └── default
β”‚ └── Dockerfile
└── wordpress
β”œβ”€β”€ conf
β”‚ β”œβ”€β”€ index.php
β”‚ └── www.conf
└── Dockerfile

To work, nginx and php need to have access to the same file.

This is why our "wordress" volume is common to both containers.

Both will share the folder /var/www/html

docker-compose.yml

version: '3.5'services:ngnix:container_name: ngnixbuild: ./nginx/restart: alwaysvolumes:- WordPress:/var/www/htmldepends_on:- wordpressports:- "80:80"networks:- mynetworkwordpress:container_name: wordpressbuild: ./wordpress/restart: alwaysvolumes:- WordPress:/var/www/htmlnetworks:- mynetwork# NETWORKnetworks:mynetwork:name : mynetworkdriver : bridge# VOLUMEvolumes:WordPress:driver: localdriver_opts:type: 'none'o: 'bind'device: /home/tliot/data/website

Installing NGINX

Dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# NGINX INSTALLATIONRUN apt-get install -y nginx
# Copy of default web page configurationCOPY ./conf/default /etc/nginx/sites-available/default
ENTRYPOINT ["nginx", "-g", "daemon off;"]

default

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000; # <------------ Redirect to wordpress container
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Installing PHP-FPM

dockerfile

# SPECIFIE LA DISTRIBUTIONFROM debian:buster
RUN apt-get update
# UDPATE & INSTALLATIONRUN apt install php-fpm -y
# To create the PID file (/run/php/php7.3-fpm.pid)RUN mkdir /run/php
# To allow external connectionsCOPY ./conf/www.conf /etc/php/7.3/fpm/pool.d/
# To create index.php COPY ./conf/index.php /var/www/html/wordpress/index.php
# Is optional, just a metadataEXPOSE 9000 ENTRYPOINT ["/usr/sbin/php-fpm7.3","-F" ]

index.php

<? php echo phpinfo(); ?>

www.conf

[www]
user = www-data
group = www-data
# listen = 127.0.0.1:9000 # Change this line
listen = 9000 # Now it's better
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

Connecting NGINX

Local Domains in Linux

Configure DNS Locally Using /etc/hosts File in Linux

Now open the /etc/hosts file using your editor of choice as follows

sudo vi /etc/hosts

Then add the lines below to the end of the file as shown in the screen shot below.

127.0.0.1 localhost
255.255.255.255	broadcasthost
::1 localhost
192.168.64.13	tliot.42.fr # <--- Principal Domains
192.168.64.13	adminer.tliot.42.fr # <--- adminer subdomain (optional)
192.168.64.13	*.tliot.42.fr # <--- all subdomain (optional)

Next, test if everything is working well as expected, using the ping command.

$ ping tliot.42.fr
PING tliot.42.fr (192.168.64.13): 56 data bytes
64 bytes from 192.168.64.13: icmp_seq=0 ttl=64 time=1.919 ms
64 bytes from 192.168.64.13: icmp_seq=1 ttl=64 time=2.046 ms
64 bytes from 192.168.64.13: icmp_seq=2 ttl=64 time=2.391 ms
64 bytes from 192.168.64.13: icmp_seq=3 ttl=64 time=2.017 ms
64 bytes from 192.168.64.13: icmp_seq=4 ttl=64 time=2.481 ms
^C
--- tliot.42.fr ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss

Setup a self-signed SSL certificate

Create the self-signed SSL certificate:

RUN 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=Ile-de-France/L=Paris/O=42/OU=42Paris/CN=TLIOT/UID=TTT'

Create a new configuration snippet file for Nginx:

RUN echo "ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;\nssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;" > /etc/nginx/snippets/self-signed.conf

Create a strong Diffie-Hellman group:

RUN openssl dhparam -out /etc/nginx/dhparam.pem 2048

Create a configuration snippet with strong encryption settings:

COPY ./conf/ssl-params.conf /etc/nginx/snippets/

ssl-params.conf

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Configure Nginx site to use certificate:

server {
listen 443 ssl default_server; <--- 80 to 443
listen [::]:443 ssl default_server; <--- 80 to 443
server_name tliot.42.fr; <--- _ to tliot.42.fr
# ssl include snippets/self-signed.conf; <--- self-signed SSL
include snippets/ssl-params.conf; <--- strong encryption
root /var/www/html/wordpress;
index index.php ;
# logging
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

Configure docker-compose.yml site to use 443:

docker-compose.yml

 ngnix:
container_name: ngnix
build: ./nginx/
restart: always
volumes:
- WordPress:/var/www/html
depends_on:
- wordpress
ports:
- "443:443" <--- 80:80 to 443:443
networks:
- mynetwork

Testing the SSL Server

Next, test whether the SSL encryption is working.

On your browser, type the prefix http:// then your domain name:

https://server_domain

Since the certificate is not already signed by a trusted certificate authority, you will most likely get a warning like the one below:

You will see a warning that may pop-up because the SSL certificate created earlier isn’t signed by a trusted certificate authority:

It's goood πŸ‘πŸΌ

Releases

Packages

Used by

Contributors

Languages