The LAMP stack is a powerful combination of open-source technologies, enabling developers to build dynamic and interactive websites and web applications.
This article is a comprehensive guide to containerizing a LAMP (Linux, Apache, MySQL, PHP) stack application. It covers everything from setting up the application to running the containers using Docker commands and Docker Compose, along with monitoring the containers. To improve readability, it has been split up into different parts to form a series. This first part focuses on setting up the application on your local machine.
Application Architecture
Please check the below embed to view the image in higher resolution:
In the above architecture diagram, users initiate an HTTP request by accessing the application through the browser using either "localhost" or the server's IP address. The server, with Apache installed, responds by serving the "form.html" file to users, prompting them to fill in their details, including their name, email, and description.
Upon completing the form, users submit the data back to the server. Apache then forwards the submitted data to a PHP script responsible for storing this information in the MySQL database. If the data is successfully stored, MySQL communicates this success to the PHP script, which responds with an HTML message displayed in the user's browser. On the other hand, if there is an issue while saving the data, the PHP script returns an error message to the user's browser, notifying them of the encountered problem.
This robust architecture ensures a seamless flow of data between users, Apache, PHP, and MySQL, providing a smooth user experience and reliable data management.
Prerequisites
Before we get started, ensure that you have the following in place:
- Docker or Docker Desktop installed
- Docker Compose (If you are using Docker Desktop, it comes with it already)
- An IDE, VSCode recommended
Application Setup
For setup and testing purposes, the application will be deployed and tested on a Linux Ubuntu OS, utilizing VirtualBox and Vagrant within a macOS environment.
Project Structure
At the beginning of the build, the environment will resemble the structure below but overtime as we create more folders and files, the structure will change.
.
├── form_submit.php
├── form.html
├── install.sh
├── setup.sh
└── vagrantfile
Setup the Linux Environment
To setup the Linux Environment on mac, we will be utilizing Vagrant and Virtual Box. A script has been provided to automatically provision them.
NB: This step is applicable to mac users only. Linux users can proceed to the installation section.
- Create an
install.sh
file in the root of the project and copy the below contents into it.
#/bin/sh
# Check if VirtualBox is installed
if ! command -v VBoxManage &> /dev/null; then
echo "VirtualBox not found. Installing VirtualBox..."
brew install --cask virtualbox
else
echo "VirtualBox is already installed."
fi
# Check if Vagrant is installed
if ! command -v vagrant &> /dev/null; then
echo "Vagrant not found. Installing Vagrant..."
brew install --cask vagrant
brew install --cask vagrant-manager
else
echo "Vagrant is already installed."
fi
- Give the script executable permission and run it with the following command:
chmod +x install.sh
./install.sh
It will automatically check if Vagrant and Virtual Box are already installed on your system, if they are installed outputs a message to let you know. However, if they are not found, the script will proceed to install both Vagrant and VirtualBox for you seamlessly.
Also in the root of the project folder, create a vagrantfile
file and paste the below contents into it:
NUM_CONTROLLER_NODE = 1
IP_NTW = "192.168.56."
CONTROLLER_IP_START = 2
NODE_IP_START = 3
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/bionic64"
i = 0
(1..NUM_CONTROLLER_NODE).each do [i]
config.vm.define "dockertask" do |node|
node.vm.provider "virtualbox" do |vb|
vb.name = "dockertask"
vb.memory = 2048
vb.cpus = 2
end
node.vm.hostname = "dockertask"
node.vm.network "private_network", ip: IP_NTW + "#{CONTROLLER_IP_START + i}"
node.vm.network "forwarded_port", guest: 22, host: "#{2710 + i}"
end
end
end
This vagrantfile configuration helps in automatically setting up the Linux VM, utilizing an ubuntu/bionic64
image for the setup. The VM has been given a name dockertask
which you are free to modify. To provision the VM, run the following command
vagrant up --provision
Open the Virtual Box application to see the setup VM
NB: To seamlessly copy files from your local (macOs) to the Linux VM, you can use the below command:
vagrant scp </absolute-path-of-the-project-directory-to-the project-folder/> <name-of-vm>:/home/vagrant
- It should look like this:
vagrant scp /Users/favour/Desktop/Modules/module-2/ dockertask:/home/vagrant
Note that the command should always be ran every time a change has been made on your local so that the Linux VM has the updated version.
Installation
With the VM now provisioned, the next step involves installing Apache, MySQL, and PHP, creating the essential LAMP stack foundation for the application. A script has also been provided to automate the installation process.
- Create a
setup.sh
file at the root of the project folder, copy and paste the below contents into it:
#/bin/sh
# update the VM
sudo apt-get update -y
# Install apache web server and start it
sudo apt-get install apache2 -y
sudo service apache2 start
# Install mysql and go through the setup. Please remember your root password
sudo apt-get install mysql-server -y
sudo /usr/bin/mysql_secure_installation
# Install PHP
sudo apt-get install php -y
sudo apt-get install php-mysql -y
sudo apt-get install libapache2-mod-php -y
sudo apache2ctl -M
sudo a2dismod mpm_event
sudo a2enmod mpm_prefork
sudo a2enmod php7.2
sudo /etc/init.d/apache2 restart
What each command does:
sudo apt-get update -y
: This updates the package lists and ensures that the package information is up-to-date before proceeding with any installations.sudo apt-get install apache2 -y
: This installs the Apache web server on the system. The -y flag allows the installation to proceed automatically without asking for user confirmation.sudo service apache2 start
: This command starts the Apache web server, so it becomes active and can serve web pages.sudo apt-get install mysql-server -y
: This installs the MySQL database server. The -y flag allows the installation to proceed automatically without asking for user confirmation.sudo /usr/bin/mysql_secure_installation
: This script guides you through a series of steps to set up MySQL securely. It prompts you to configure the root password, remove anonymous users, disable remote root login, and more.sudo apt-get install php -y
: This installs PHP, a server-side scripting language, on the system.sudo apt-get install php-mysql -y
: This installs the PHP MySQL extension, which allows PHP to communicate with a MySQL database. This extension is required if your PHP application needs to interact with a MySQL database.sudo apt-get install libapache2-mod-php -y
: This installs the PHP module for Apache web server (libapache2-mod-php) along with its dependencies. The -y flag allows the installation to proceed automatically without asking for user confirmation.sudo apache2ctl -M
: This command lists all the loaded Apache modules. It is used to check if the PHP module (mod_php) is successfully loaded after installation.sudo a2dismod mpm_event
: This disables the Apache event module (mpm_event) to switch to the prefork module, which is required for running PHP with Apache.sudo a2enmod mpm_prefork
: This enables the Apache prefork module (mpm_prefork), which is necessary to work with PHP.sudo a2enmod php7.2
: This enables the PHP module (mod_php) in Apache for PHP version 7.2. Replace 7.2 with the appropriate version if you are using a different PHP version.sudo /etc/init.d/apache2 restart
: This restarts the Apache web server to apply the changes made by enabling the PHP module and switching to the prefork module.
To execute the script, run the below command:
chmod +x setup.sh
./setup.sh
A prompt will come up asking you to pick a password level for mysql, it should look like this :
Would you like to setup VALIDATE PASSWORD plugin?
Press y|Y for Yes, any other key for No: y
It is best to give a password for security purposes, so input "y", then you will get the following prompt asking you to pick a password level:
LOW Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary
Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
For securing the MySQL server, you have the option to select the password complexity level of your preference. If you choose the "LOW" length, which is set to "0", you could use a password like dockertask
to meet the requirements.
It's important to note that selecting a more complex password, especially for production environments, is strongly recommended to enhance security. However, for local development or testing purposes, a simple password like dockertask
can be used.
Enabling password validation during the MySQL setup will prompt the system to evaluate the strength of the root password you provided. The server will then present you with the password strength assessment. If you are satisfied with the current password, you can proceed by entering "Y" for "yes" at the prompt. This step ensures that you have reviewed the password strength and are content with the chosen password before proceeding with the setup.
It should resemble the below output:
Estimated strength of the password: 100
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y
After completing the MySQL setup, the script will proceed with the remaining instructions. To ensure that PHP has been successfully installed, you can confirm it by running the following command:
php -v
Executing this command will display the installed PHP version along with relevant details, affirming that PHP is now operational and ready for use in your environment.
Now, open your web browser and enter the IP address of the server, which should be 192.168.56.2
or localhost
depending on the environment you are working from. If everything is set up correctly, you should see the Apache web server running, and it will display the default Apache landing page or any other content that you might have configured. This confirms that Apache is up and running and successfully serving web pages.
- To verify that you can access the MySQL console with the updated root user password, execute the following command:
sudo mysql -p
NB: The -p
flag prompts you to enter the password you set during the MySQL setup. After providing the correct password, you should be able to log in to the MySQL console, where you can manage and interact with the MySQL database.
To exit the MySQL console, simply type:
exit
This will take you out of the MySQL console and return you to the regular command prompt.
Setup the Application
In the root of the project folder, create a form.html
file:
touch form.html
- Paste the below contents into it
<head>
<title>
Test Page
</title>
</head>
<body>
<form action="http://localhost/form_submit.php" class="alt" method="POST">
<div class="row uniform">
<div class="name">
<input name="name" id="" placeholder="Name" type="text">
</div>
<div class="email">
<input name="email" placeholder="Email" type="email">
</div>
<div class="message">
<textarea name="message" placeholder="Message" rows="4"></textarea>
</div>
</div>
<br/>
<input class="alt" value="Submit" name="submit" type="submit">
</form>
</body>
- In the same directory, create a
form_submit.php
file:
touch form_submit.php
- Paste the below contents into it:'
<?php
$host = getenv('DB_HOST');
$db_name = getenv('MYSQL_DATABASE');
$username = getenv('DB_USER');
$password = getenv('MYSQL_PASSWORD');
$option = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
# Catch errors
try{
$connection = new PDO("mysql:host=" . $host . ";dbname=" . $db_name, $username, $password, $option);
$connection->exec("set names utf8");
} catch(PDOException $exception){
echo "Connection error: " . $exception->getMessage();
}
function saveData($name, $email, $message){
global $connection;
$query = "INSERT INTO test(name, email, message) VALUES( :name, :email, :message)";
try {
$callToDb = $connection->prepare( $query );
$name=htmlspecialchars(strip_tags($name));
$email=htmlspecialchars(strip_tags($email));
$message=htmlspecialchars(strip_tags($message));
$callToDb->bindParam(":name",$name);
$callToDb->bindParam(":email",$email);
$callToDb->bindParam(":message",$message);
if($callToDb->execute()){
return '<h3 style="text-align:center;">Your information has been submitted to the database successfully!</h3>';
} else {
return '<h3 style="text-align:center;">Failed to save data.</h3>';
}
} catch (PDOException $exception) {
return '<h3 style="text-align:center;">Error: ' . $exception->getMessage() . '</h3>';
}
}
if( isset($_POST['submit'])){
$name = htmlentities($_POST['name']);
$email = htmlentities($_POST['email']);
$message = htmlentities($_POST['message']);
//then you can use them in a PHP function.
$result = saveData($name, $email, $message);
echo $result;
} else{
echo '<h3 style="text-align:center;">A very detailed error message ( ͡° ͜ʖ ͡°)</h3>';
}
?>
- Create a
.env
file which will contain environment variables for the PHP script to use in connecting to the database:
touch .env
- Paste the below content into the file:
DB_HOST=mysql
MYSQL_DATABASE=dev_to
DB_USER=root
MYSQL_PASSWORD=dockertask
Creating a Virtual Host
Creating a Virtual Host in Apache provides numerous benefits, such as hosting multiple websites on a single server while maintaining separate configurations for each site. It conserves IP addresses, streamlines website management, and enhances security by isolating websites from one another.
While it is not mandatory to create a virtual host, doing so is highly beneficial, especially when dealing with multiple websites that need to be served using Apache. We will be creating a virtual host for learning purposes as this process helps you gain familiarity with Apache's directory configurations, enabling you to manage websites more effectively in the future.
In case you choose not to create a virtual host, you can proceed to work with the default directory located at /var/www/html
. This directory serves as the default location for hosting websites on the Apache web server.
For better organization, we will create a directory called dockertask
to house the project files, making it easier to manage and serve them.
- To create the directory for the
dockertask
, run the following command:
sudo mkdir /var/www/dockertask
- To ensure proper ownership and permissions, assign the directory dockertask to your current system user by running the below command:
sudo chown -R $USER:$USER /var/www/projectlamp
sudo chmod -R 755 /var/www/dockertask
This will allow you to work with the project files without encountering any permission issues.
- Create and open a new configuration file,
dockertask.conf
, in Apache’s sites-available directory
sudo nano /etc/apache2/sites-available/dockertask.conf
This will create a new blank file. Paste in the following bare-bones configuration, then do a ctrl+x
and type y for yes to save it:
<VirtualHost 192.168.56.2:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/dockertask
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
If you are working directly from a Linux OS, use the belo configuration:
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/dockertask
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
- Set the ServerName directive globally, add the ServerName at the top or bottom of the file
sudo nano /etc/apache2/apache2.conf
- On the first line of the
apache2.conf
file, add the below:
ServerName 192.168.56.2
Leave the ServerName as localhost
if you are working directly with a Linux OS.
Setting ServerName
globally in Apache means specifying a default server name that will be used for any virtual host that does not explicitly define its own ServerName.
In Apache HTTP Server, the ServerName directive is used to define the hostname and port number of the virtual host. A virtual host allows you to run multiple websites on the same physical server, and each virtual host can have its own ServerName that corresponds to a unique domain or hostname.
- Use the
ls
command to confirm thedockertask.conf
file exists in apache's sites-available directory
sudo ls /etc/apache2/sites-available
- The output should resemble the below:
000-default.conf default-ssl.conf dockertask.conf
- Enable the newly created virtual host:
sudo a2ensite dockertask
NB: We need to disable the default website that comes installed with Apache. This is required if you’re not using a custom domain name, because in this case Apache’s default configuration would overwrite your virtual host.
- To disable Apache’s default website use
a2dissite
command , run the following command:
sudo a2dissite 000-default
- To make sure the configuration file doesn’t contain syntax errors, run:
sudo apache2ctl configtest
- The output should resemble the below:
Syntax OK
- Finally, reload Apache so the changes take effect:
sudo systemctl reload apache2
The new website is now active, but the web root /var/www/dockertask
is still empty.
Now we move the required folders, form.html
and form_submit.php
, to this directory from the home directory where the files exist
- To move the files, run the below command:
sudo mv form.html /var/www/dockertask
sudo mv form_submit.php /var/www/dockertask
By default, Apache prioritises the index.html
file over index.php
, making it the landing page for the application. After maintenance, simply renaming or removing the index.html from the document root restores the regular application page.
To change this behavior, we need to edit the /etc/apache2/mods-enabled/dir.conf
file and change the order in which the index.php file is listed within the DirectoryIndex directive:
sudo nano /etc/apache2/mods-enabled/dir.conf
- Change the existing configuration to look like the below:
<IfModule mod_dir.c>
#Change this:
#DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm
#To this:
DirectoryIndex form.html form_submit.php index.cgi index.pl index.xhtml index.htm
</IfModule>
- After saving and closing the file, reload Apache for the changes to take effect:
sudo systemctl reload apache2
Setup Mysql database
- Log into mysql to set up a database
sudo mysql -u root -p
- Create a new MySQL user:
In the MySQL shell, run the following query to create a new user:
CREATE USER 'vagrant'@'192.168.56.2' IDENTIFIED BY 'Strongpassword@123';
NB: You can choose a different user and password for the above. Also remember to change the server address depending on the environment.
- Grant privileges to the new user:
Next, grant the necessary privileges to the new user and flush the privileges to ensure the changes take effect immediately:
GRANT ALL PRIVILEGES ON dev_to.* TO 'vagrant'@'192.168.56.2';
FLUSH PRIVILEGES;
NB: Troubleshooting
If you run into access denied error for root user after providing your password, troubleshoot with this. It is a common issue with ubuntu/linux systems
- Create a database using the below SQL commands:
create database dev_to;
use dev_to;
create table test(
id int NOT NULL AUTO_INCREMENT,
name varchar(255),
email varchar(255),
message text,
PRIMARY KEY (id)
);
- Confirm the database was created;
describe test;
You should see an output resembling the below
mysql> describe test;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| email | varchar(255) | YES | | NULL | |
| message | text | YES | | NULL | |
+---------+--------------+------+-----+---------+----------------+
- Exit the MySQL shell:
exit
Fix binding settings for mysql
Skip this step if you are working from a Linux OS with your ServerName as localhost
.
By default, MySQL is bound to the local host (127.0.0.1). However, since the Vagrant server has its own unique IP address setup, we must configure MySQL to bind to this specific IP address.
- Open the file for mysql conf
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
Search for
bind-address
and change it from0.0.0.0
to192.168.56.2
, save and exit.Restart the mysql server
sudo service mysql restart
Now, any information filled into the html form will automatically be saved in this database.
- To run checks, log into the mysql database
sudo mysql -u root -p
- Check for existing databases and identify the one we created earlier,
dev_to
. Once located, we can proceed to examine the tables residing under it, including thetest
table.
SHOW DATABASES;
USE dev_to;
SHOW TABLES;
describe test;
SELECT * FROM test;
Conclusion
In this article, we explored the process of setting up a fully functional LAMP stack application and learned how the components communicate with each other. In the next article, we will explore how to containerize the application using Docker.
- Credits to Adnan Alam
Top comments (0)