DEV Community

Daniel Favour
Daniel Favour

Posted on

Setting up a LAMP Stack application

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

image edit

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:

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • Give the script executable permission and run it with the following command:
chmod +x install.sh
./install.sh
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • It should look like this:
vagrant scp /Users/favour/Desktop/Modules/module-2/ dockertask:/home/vagrant
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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>
Enter fullscreen mode Exit fullscreen mode
  • In the same directory, create a form_submit.php file:
touch form_submit.php
Enter fullscreen mode Exit fullscreen mode
  • 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>';
}
?>
Enter fullscreen mode Exit fullscreen mode
  • Create a .env file which will contain environment variables for the PHP script to use in connecting to the database:
touch .env
Enter fullscreen mode Exit fullscreen mode
  • Paste the below content into the file:
DB_HOST=mysql
MYSQL_DATABASE=dev_to
DB_USER=root
MYSQL_PASSWORD=dockertask
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
  • Set the ServerName directive globally, add the ServerName at the top or bottom of the file
sudo nano /etc/apache2/apache2.conf
Enter fullscreen mode Exit fullscreen mode
  • On the first line of the apache2.conf file, add the below:
ServerName 192.168.56.2
Enter fullscreen mode Exit fullscreen mode

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 the dockertask.conf file exists in apache's sites-available directory
sudo ls /etc/apache2/sites-available
Enter fullscreen mode Exit fullscreen mode
  • The output should resemble the below:
000-default.conf  default-ssl.conf  dockertask.conf
Enter fullscreen mode Exit fullscreen mode
  • Enable the newly created virtual host:
sudo a2ensite dockertask
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • To make sure the configuration file doesn’t contain syntax errors, run:
sudo apache2ctl configtest
Enter fullscreen mode Exit fullscreen mode
  • The output should resemble the below:
Syntax OK
Enter fullscreen mode Exit fullscreen mode
  • Finally, reload Apache so the changes take effect:
sudo systemctl reload apache2
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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>
Enter fullscreen mode Exit fullscreen mode
  • After saving and closing the file, reload Apache for the changes to take effect:
sudo systemctl reload apache2
Enter fullscreen mode Exit fullscreen mode

Setup Mysql database

  • Log into mysql to set up a database
sudo mysql -u root -p
Enter fullscreen mode Exit fullscreen mode
  • 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';
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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)
); 
Enter fullscreen mode Exit fullscreen mode
  • Confirm the database was created;
describe test;
Enter fullscreen mode Exit fullscreen mode

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    |                |
+---------+--------------+------+-----+---------+----------------+
Enter fullscreen mode Exit fullscreen mode
  • Exit the MySQL shell:
exit
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • Search for bind-address and change it from 0.0.0.0 to 192.168.56.2, save and exit.

  • Restart the mysql server

sudo service mysql restart
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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 the test table.
SHOW DATABASES;
USE dev_to;
SHOW TABLES;
describe test;
SELECT * FROM test;
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)