How to create a docker-based LAMP stack using docker-compose on Ubuntu 18.04 Bionic Beaver Linux
- https://linuxconfig.org/how-to-create-a-docker-based-lamp-stack-using-docker-compose-on-ubuntu-18-04-bionic-beaver-linux
Contents
Objective
Following this tutorial you will be able to create a LAMP environment using the Docker technology.Requirements
- Root permissions
- Basic knowledge of Docker
Difficulty
MEDIUMConventions
- # - requires given linux commands to be executed with root privileges either directly as a root user or by use of
sudo
command - $ - requires given linux commands to be executed as a regular non-privileged user
Introduction
Docker is an open source project aimed at providing software insidecontainers
. You can think of a container as a sort of "package", an isolated environment which shares the kernel with the host machine and contains everything the application needs. All containers are built using images
(the central images repository for them being Dockerhub).In this tutorial, we will see how to create a LAMP stack based on dockerized components: following the "one service per container" philosophy, we will assemble the environment using
docker-compose
, a tool to orchestrate container compositions.One service vs multiple service for container
There are several advantages in using one service per container, instead of running multiple services in the same one. Modularity, for example, (we can reuse a container for different purposes), or a better maintainability: it's easier to focus on a specific piece of an environment instead of considering all of them at once. If we want to respect this philosophy, we must create a container for each component of our LAMP stack: one for apache-php and one for the database. The different containers must be able to speak to each other: to easily orchestrate linked containers we will usedocker-compose
.Preliminary steps
Before proceeding we need to installdocker
and docker-compose
on our system:# apt-get install docker docker-composeThe packages will be installed in few seconds, and the
docker
service will be automatically started. We can now proceed into creating a directory for our project and inside of it, another one to hold the pages that will be served by Apache. DocumentRoot would be a meaningful name for it; in this case the only page that will be served it's index.php
:$ mkdir -p dockerized-lamp/DocumentRoot
$ echo "<?php phpinfo(); ?>" > dockerized-lamp/DocumentRoot/index.php
Here our code consists simply in the phpinfo
function: it's output (a page showing information about php, in case you don't know) will be what our server will display by default. Now let's use our favorite editor to create the docker-compose.yml
file for our project.Subscribe to Linux Career NEWSLETTER and receive latest Linux news, jobs, career advice and tutorials.
Php-apache
We can now start providing instruction about building and connecting our containers into the docker-compose file. This is a file which uses theyaml
syntax. All definitions must be provided into the services
section.version: '3'
services:
php-apache:
image: php:7.2.1-apache
ports:
- 80:80
volumes:
- ./DocumentRoot:/var/www/html
links:
- 'mariadb'
Let's take a look at what we just done here. The first line we inserted into the file, version
, specifies what docker-compose syntax version we are going to use, in this case the version 3
, the latest main version available. Inside the services
section, we started describing our service by specifying its name, php-apache
(an arbitrary name, you can use whatever you want), then the instructions for building it.The
image
keyword lets docker know what image we want to use to build our container: in this case I used 7.2.1-apache
which will provide us php 7.2.1 together with the apache web server. Need another php version? you just need to choose from the many provided in the image page on dockerhub.The second instruction we provided is
ports
: we are telling docker to map the port 80
on our host, to the port 80
on the container: this way will appear as we were running the web server directly on our system.We then used the
volumes
instruction to specify a bind mount
. Since during development the code changes a lot and fast, there would be no sense in putting the code directly inside a container: this way we should rebuild it every time we make some modifications. Instead, what we are going to do is to tell docker to bind-mount the DocumentRoot
directory, at /var/www/html
inside the container. This directory represents the main apache VirtualHost
document root, therefore the code we put inside it, will be immediately available.Finally we used the
link
keyword specifying mariadb
as its argument. This keyword it's not needed, as it may seem, to create a connection between the two containers: even without specifying it, the mariadb
service would be reachable from inside the container built for the apache-php
service, by using its name as an hostname. The keyword does two things: first let us optionally specify an alias
we can use to reference a service in addition to its name. So, for example, by writing:link:
mariadb:database-service
the service could also be reached using database-service
. The second thing link
does, is specify a dependency: in this case the php-apache
service will be considered as dependent from the mariadb
one, so the latter will be started before the former when building or starting the environment.Install php extensions
The default php-apache dockerfile does not include some php extensions, like mysqli or pdo. To install them we have to build our own dockerfile, based on it. To do that, we create a directory inside of our project named php-apache (this will be ourbuild context
) and inside of it, our dockerfile. Paste and save the code below as php-apache/Dockerfile:
FROM php:7.2.1-apache
MAINTAINER egidio docile
RUN docker-php-ext-install pdo pdo_mysql mysqli
As you can see, with the FROM
instruction, we specified that this dockerfile should be based on the default one. Then we included a RUN
instruction: using the script provided in the image itself, docker-php-ext-install
, we include the extensions needed to use pdo and mysqli. At this point, if we want to use our custom dockerfile, we have to slightly change the php-apache section in our docker-compose.yml, this way:version: '3'
services:
php-apache:
build:
context: ./php-apache
ports:
- 80:80
volumes:
- ./DocumentRoot:/var/www/html
links:
- 'mariadb'
What has changed? Instead of directly specifying the remote image to use, we provided the context
instruction, inside the build
section, so that the dockerfile contained in the directory we created and here provided as the argument, will be automatically used. The context directory is imported by the docker daemon when building the image, so if we want to add additional files we have to put them also there.The database service
A database in an essential part of a LAMP environment, it is used for persistence. In this case we are going to usemariadb
:mariadb:
image: mariadb:10.1
volumes:
- mariadb:/var/lib/mysql
environment:
TZ: "Europe/Rome"
MYSQL_ALLOW_EMPTY_PASSWORD: "no"
MYSQL_ROOT_PASSWORD: "rootpwd"
MYSQL_USER: 'testuser'
MYSQL_PASSWORD: 'testpassword'
MYSQL_DATABASE: 'testdb'
We already know what the image
keyword is for. The same goes for the volumes
instruction, except for the fact that this time we didn't declared a bind mount
, instead, we referenced a named volume
, for persistence. It's important to focus on the difference between the two for a moment.As said before, a
bind mount
is a quick way to mount an host directory inside a container, so that the files contained in said directory become accessible from inside the restricted environment: to specify a bind mount, the short syntax
is:<host_path>:<mountpoint_inside_the_container>The host path can be a relative (to the docker-compose file) or an absolute path, while the mountpoint inside the container must be specified in absolute form.
A
named volume
is something different: it is a proper docker volume
used for persistence, and it is generally to be preferred over a bind mount, because it doesn't depend on the host file structure (one of the many advantages of containers it's their portability). The syntax to use to reference a named volume
inside a service definition is:<volume_name>:<mountpoint_inside_container>A
named volume
life cycle is independent from that of a container which uses it, and must be declared in the volumes
section of the docker-compose file, as we will see in a moment.Back to the definition of the service now. The last keyword we used is
environment
: it lets us set some environment variables which will influence the behavior of the service. First we used TZ
to specify our database timezone: in this case I used "Europe/Rome". The names of the other variables say everything about their purpose: by using them we set important details as the name of the default database to be created (testdb), the user to be created and its password. We also set the root user password and decided to don't allow empty passwords.The volumes section
In this section we must declare thenamed volume
we referenced from the mariadb
server definition:volumes:
mariadb:
At the end, this is how our file will look in its entirety:version: '2'
services:
php-apache:
image: php:7.2.1-apache
ports:
- 80:80
volumes:
- ./DocumentRoot:/var/www/html:z
links:
- 'mariadb'
mariadb:
image: mariadb:10.1
volumes:
- mariadb:/var/lib/mysql
environment:
TZ: "Europe/Rome"
MYSQL_ALLOW_EMPTY_PASSWORD: "no"
MYSQL_ROOT_PASSWORD: "rootpwd"
MYSQL_USER: 'testuser'
MYSQL_PASSWORD: 'testpassword'
MYSQL_DATABASE: 'testdb'
volumes:
mariadb:
It's really important to respect indentation for the file to be interpreted correctly.Let's build our environment
Once we specified all instructions for our services, we can use thedocker-compose up
command to build them. The command must be executed inside the same directory where the docker-compose.yml
file is located:# docker-compose upFew minutes and we will be ready to go. At the end if everything went well, by navigating to
localhost
on our host, we shall see the output of the php script we placed inside DocumentRoot
:Our lamp environment is now ready to be used.
Closing thoughts
We have seen how to create a basicLAMP
environment, using docker and orchestrating containers and services with docker-compose
. The setup we used it's focused on development, and can be further expanded and tweaked to match different needs: Docker documentation it's a very well written source you can consult to expand your docker knowledge. Don't hesitate to leave a comment for whatever doubts or questions you have.
Tidak ada komentar:
Posting Komentar