Create a Multi-VM LAMP Stack on Oracle Linux KVM

Introduction

At the core of the Oracle Virtualization solution is the Oracle Linux KVM host. On top of this hypervisor you can run many virtual machines, each running one of the supported operating systems including Oracle Linux, RHEL, Windows, Ubuntu, Solaris and others. This virtualization solution is managed through the Oracle Linux Virtualization Manager which offers a single interface to keep things organized. In addition to multi-host/multi-VM management, Oracle Linux Virtualization Manager enables key features of the Oracle Virtualization solution. For example, Oracle Linux Virtualization Manager provides automatic high availability with VM failover, live migration of VMs between hosts with zero downtime, role-based access control and audit logging, and simplified backup and disaster recovery workflows. These enterprise features transform Oracle Linux KVM from individual hosts into a true enterprise virtualization platform.

In this tutorial, we’ll explore the core hypervisor technology that powers Oracle Virtualization. Kernel-based Virtual Machine (KVM) is an open-source type-1 (bare-metal) hypervisor. This functionality permits a host system, such as Oracle Linux, to host multiple virtual machines (VMs) or guests when running on supported hardware.

This tutorial deploys Oracle Linux KVM to create virtual machines configured with LAMP stack applications, enabling you to host complete web development environments with Linux, Apache, MySQL, and PHP components across multiple VMs.

Important: The two application codes in this lab are for educational purposes only. They are designed to help developers learn and practice application development skills with the LAMP stack. These codes are not intended for production environments and should not be used as-is in a live setting.

Objectives

Prerequisites

Any Oracle Linux system with a non-root user with sudo permissions.

Step 1: Deploy Oracle Linux

Note: If running in your own tenancy, read the linux-virt-labs GitHub project README.md and complete the prerequisites before deploying the lab environment.

  1. Open a terminal on the Luna Desktop.

  2. Clone the linux-virt-labs GitHub project.

    git clone https://github.com/oracle-devrel/linux-virt-labs.git
    
  3. Change into the working directory.

    cd linux-virt-labs/ol
    
  4. Install the required collections.

    ansible-galaxy collection install -r requirements.yml
    
  5. Deploy the lab environment.

    ansible-playbook create_instance.yml -e localhost_python_interpreter="/usr/bin/python3.6" -e instance_ocpus="4" -e instance_memory="64"
    

    The free lab environment requires the extra variable local_python_interpreter, which sets ansible_python_interpreter for plays running on localhost. This variable is needed because the environment installs the RPM package for the Oracle Cloud Infrastructure SDK for Python, located under the python3.6 modules.

    The default deployment shape uses the AMD CPU and Oracle Linux 8. To use an Intel CPU add -e instance_shape="VM.Standard3.Flex"

    Important: Wait for the playbook to run successfully and reach the pause task. At this stage of the playbook, the installation of Oracle Linux is complete, and the instances are ready. Take note of the previous play, which prints the public and private IP addresses of the nodes it deploys and any other deployment information needed while running the lab.

Step 2: Validate Environment Supports Virtualization

  1. Open a terminal and connect by using SSH to the ol-node-01 instance.

    ssh opc@<ip_address_of_instance>
    
  2. Run the following command to determine your CPU type.

    grep -e 'vendor_id' /proc/cpuinfo | uniq
    

    The vendor_id reports either AuthenticAMD for an AMD CPU or GenuinIntel for an Intel CPU.

  3. Check that the hardware supports virtualization.

    Run the command that matches your CPU type.

    1. Verify the AMD V CPU extensions exist.
    grep -w -o 'svm' /proc/cpuinfo | uniq
    
    1. Verify that the Intel VT CPU extensions exist.
    grep -w -o 'vmx' /proc/cpuinfo | uniq
    

    The existence of one of these flags in the command output indicates this system supports virtualization. You can also use the lscpu command and look for the Virtualization entry in the output.

  4. Check for the loaded KVM modules.

    lsmod | grep kvm
    

    The output displays the KVM kernel modules after installing virtualization packages and starting the libvirtd service. On cloud systems like OCI, these modules load automatically when libvirtd starts, not during package installation. The loading sequence is: install packages → start libvirtd service → modules appear in lsmod output.

Step 3: Install and Start KVM

  1. Check the running version of Oracle Linux.

    hostnamectl | grep 'Operating System'
    
  2. Install the associated software packages for Oracle Linux virtualization.

    This commands installs the virtualization packages, which include libvirt and other dependencies on Oracle Linux. The libvirt package provides a software library and API to manage KVM virtual machines and includes the libvirtd daemon that runs in the background to handle the actual VM management.

    sudo dnf module install -y virt
    
  3. Install virt-install, a tool for creating and configuring virtual machines (VMs) using KVM (Kernel-based Virtual Machine) hypervisor.

    sudo dnf install -y virt-install
    
  4. Validate that the host machine is ready and set up to run libvirt VMs.

    virt-host-validate
    

    If all checks pass, the system is prepared for creating VMs. If any checks fail, follow the instructions to correct the problem. If any check returns the value of WARN, consider following the instructions to improve the virtualization capabilities.

  5. Start the Systemd services, enabling them to start automatically on each boot.

    sudo systemctl enable --now libvirtd.service
    
  6. Check the services status to confirm that they are up and running.

    sudo systemctl status libvirtd.service
    

    Theutput shows the service as enabled and running. If needed enter the letter ‘q’ to exit the output mode.

Step 4: Create two Virtual Machines using Oracle Cloud Images

  1. Change to the KVM image storage location.

    cd /var/lib/libvirt/images
    
  2. Download the Oracle Linux VM template.

    sudo curl -O https://yum.oracle.com/templates/OracleLinux/OL8/u10/x86_64/OL8U10_x86_64-kvm-b237.qcow2
    

    Create VM-01 (Database)

  3. Create a meta-data file.

    cat << 'EOF' | sudo tee ~/meta-data > /dev/null
    instance-id: iid-local01
    local-hostname: vm-01
    EOF
    
  4. Create a user-data file.

    cat << 'EOF' | sudo tee ~/user-data > /dev/null
    #cloud-config
    
    system_info:
      default_user:
        name: opc
    
    ssh_authorized_keys:
      - <paste_public_key_here>
    EOF
    
  5. Generate an SSH key pair for secure connection to the VM.

    ssh-keygen -t rsa -b 4096
    

    Press Enter to accept each of the defaults. The command writes the key pair to the .ssh directory in the user’s home.

  6. Extract the generated public SSH key.
    SSHKEY=$(cat ~/.ssh/id_rsa.pub)
    
  7. Update the user-data file with the generated public SSH key.
    sed -i "s|<paste_public_key_here>|${SSHKEY}|g" ~/user-data
    
  8. Generate an ISO image containing the user-data and meta-data files.
    sudo genisoimage -output /var/lib/libvirt/images/vm-01.iso -volid cidata -joliet -rock ~/user-data ~/meta-data
    
  9. Find the OS variant that matches the downloaded image.
    osinfo-query os | grep ol8
    
  10. Copy the Oracle Linux image to a new disk image for vm-01.
    sudo cp /var/lib/libvirt/images/OL8U10_x86_64-kvm-b237.qcow2 /var/lib/libvirt/images/vm-01.qcow
    
  11. Create a new virtual machine named vm-01 with specified resources and configuration.
    sudo virt-install --name vm-01 \
    --memory 2048 \
    --vcpus 2 \
    --disk /var/lib/libvirt/images/vm-01.qcow,device=disk,bus=virtio \
    --disk /var/lib/libvirt/images/vm-01.iso,device=cdrom \
    --os-type linux --os-variant ol8.10 \
    --virt-type kvm --graphics none \
    --network network=default,model=virtio \
    --noautoconsole \
    --import
    
  12. List all running virtual machines.
    sudo virsh list
    
  13. Retrieve the IP address of the vm-01 virtual machine.

    sudo virsh net-dhcp-leases --network default
    
  14. Verify the virtual machine works by connecting with ssh.

    ssh opc@<ip_address_of_vm-01>
    
  15. Exit the vm-01 server to continue to the next step.

    exit
    

    You can verify the version and get additional details about the OS within the virtual machine by running hostnamectl.

    Create VM-02 (Web Server)

  16. Create a meta-data file for vm-02.

    cat << 'EOF' | sudo tee ~/meta-data > /dev/null
    instance-id: iid-local02
    local-hostname: vm-02
    EOF
    
  17. Generate an ISO image for vm-02.
    sudo genisoimage -output /var/lib/libvirt/images/vm-02.iso -volid cidata -joliet -rock ~/user-data ~/meta-data
    
  18. Copy the Oracle Linux image to a new disk image for vm-02.
    sudo cp /var/lib/libvirt/images/OL8U10_x86_64-kvm-b237.qcow2 /var/lib/libvirt/images/vm-02.qcow
    
  19. Create a new virtual machine named vm-02 with specified resources and configuration.
    sudo virt-install --name vm-02 \
    --memory 2048 \
    --vcpus 2 \
    --disk /var/lib/libvirt/images/vm-02.qcow,device=disk,bus=virtio \
    --disk /var/lib/libvirt/images/vm-02.iso,device=cdrom \
    --os-type linux --os-variant ol8.10 \
    --virt-type kvm --graphics none \
    --network network=default,model=virtio \
    --noautoconsole \
    --import
    
  20. List all running virtual machines.
    sudo virsh list
    
  21. Retrieve the IP address of the vm-02 virtual machine.

    sudo virsh net-dhcp-leases --network default
    
  22. Verify the virtual machine works by connecting with ssh.

    ssh opc@<ip_address_of_vm-02>
    
  23. Exit the vm-01 server to continue to the next step.

    exit
    
  24. You can verify the version and get additional details about the OS within the virtual machine by running hostnamectl.

    hostnamectl
    

    Example Output:

    [oracle@ol-node01 images]$ ssh opc@192.168.122.46
    The authenticity of host '[host]' can't be established.
    ECDSA key fingerprint is [fingerprint].
    Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
    Warning: Permanently added '[IP]' (ECDSA) to the list of known hosts.
    
    [opc@vm-01 ~]$ hostnamectl
       Static hostname: vm-01
             Icon name: computer-vm
               Chassis: vm
            Machine ID: [MachineID]
               Boot ID: [BootID]
        Virtualization: kvm
      Operating System: Oracle Linux Server 8.10
           CPE OS Name: cpe:/o:oracle:linux:8:10:server
                Kernel: Linux 5.15.0-206.153.7.1.el8uek.x86_64
          Architecture: x86-64
    
  25. Extract the IP address of vm-01.
     VM01_IP=$(sudo virsh net-dhcp-leases --network default | grep vm-01 | tail -1 | awk '{print $5}' | cut -d'/' -f1)
    
  26. Extract the IP address of vm-02.
     VM02_IP=$(sudo virsh net-dhcp-leases --network default | grep vm-02 | tail -1 | awk '{print $5}' | cut -d'/' -f1)
    
  27. Save the VMs IP address for later use:
     echo "VM-01 (Web): $VM01_IP"
    
     echo "VM-02 (Web): $VM02_IP"
    
  28. Test the SSH command for the VMs.
     ssh -o ConnectTimeout=10 opc@$VM01_IP "echo 'VM-01 OK'"
    
     ssh -o ConnectTimeout=10 opc@$VM02_IP "echo 'VM-02 OK'"
    

Step 5: Setup MySQL on VM-01

Install MySQL

  1. From the ol-node-01 instance SSH establish an SSH connection to VM-01.
     ssh opc@$VM01_IP
    
  2. Download MySQL repository configuration.
     sudo yum -y install https://dev.mysql.com/get/mysql84-community-release-el8-1.noarch.rpm
    
  3. Disable default MySQL module to avoid conflicts.
     sudo yum -y module disable mysql
    
  4. Install MySQL server and client.
     sudo yum install -y mysql mysql-server
    
  5. Start MySQL service.
     sudo systemctl start mysqld
    
  6. Enable MySQL service to start at boot.
     sudo systemctl enable mysqld
    
  7. Allow incoming MySQL traffic through the firewall.
     sudo firewall-cmd --permanent --add-service=mysql
    
  8. Reload firewall configuration to apply changes.
     sudo firewall-cmd --reload
    

    Configure MySQL

  9. Extract temporary root password from MySQL log.
     TEMP_PASS=$(sudo grep 'temporary password' /var/log/mysqld.log | awk '{print $NF}')
    
  10. Login to MySQL with temporary root password.
     mysql -uroot -p$TEMP_PASS --connect-expired-password
    
  11. Change root password to a secure value.
     ALTER USER 'root'@'localhost' IDENTIFIED BY '[password]';
    
  12. Create ‘admin’ user with a secure password.
     CREATE USER 'admin'@'%' IDENTIFIED BY '[password]';
    
  13. Grant all privileges to ‘admin’ user.
     GRANT ALL PRIVILEGES ON *.* TO 'admin'@'%' WITH GRANT OPTION;
    
  14. Create ‘empuser’ user with a secure password.
     CREATE USER 'empuser'@'%' IDENTIFIED BY '[password]';
    
  15. Grant all privileges to ‘empuser’.
     GRANT ALL PRIVILEGES ON *.* TO 'empuser'@'%' WITH GRANT OPTION;
    
  16. Reload privilege tables to apply changes.
     FLUSH PRIVILEGES;
    
  17. Quit MySQL shell.
     \q
    
  18. Exit SSH session from VM-01.
     exit
    

Step 6: Setup Apache/PHP on VM-02

  1. From the ol-node-01 instance SSH establish an SSH connection to VM-02.
     ssh opc@$VM02_IP
    
  2. Install Apache HTTP server.
     sudo yum install -y httpd
    
  3. Install PHP 8.2 and its dependencies.
     sudo dnf install -y @php:8.2
    
  4. Install PHP MySQL and JSON extensions.
     sudo yum install -y php-mysqlnd php-json
    
  5. Enable and start Apache HTTP server.
     sudo systemctl enable --now httpd
    
  6. Allow incoming HTTP traffic on port 80.
     sudo firewall-cmd --permanent --add-port=80/tcp
    
  7. Reload firewall configuration to apply changes.
     sudo firewall-cmd --reload
    
  8. Allow Apache to connect to network resources.
     sudo setsebool -P httpd_can_network_connect 1
    
  9. Exit SSH session from VM-02.
     exit
    

Step 7: Test Apache

  1. Display public IP address of VM-02 YOUR_VM_02_PUBLIC_IP:
     echo "VM-02 (Web): $VM02_IP"
    
  2. Open a new terminal Luna or on-prem environment and create SSH tunnel to access VM-02 web server from local machine.

    … be sure to first replace **** and ****

     ssh -L 8081:<YOUR_VM_02_PUBLIC_IP>:80 opc@<YOUR_OCI_PUBLIC_IP>
    
  3. Use a browser from Luna or on-prem environment to test Apache by accessing the web server through the SSH tunnel.
     http://localhost:8081
    

    Important: Leave the SSH tunnel and Browser open for later use

Step 8: Create Test Application


  1. Return to your opc@ol-node-01 ssh instance.

  2. Retrieve IP address of VM-01 from DHCP leases.
     VM01_IP=$(sudo virsh net-dhcp-leases --network default | grep vm-01 | tail -1 | awk '{print $5}' | cut -d'/' -f1)
    
  3. Retrieve IP address of VM-02 from DHCP leases.
     VM02_IP=$(sudo virsh net-dhcp-leases --network default | grep vm-02 | tail -1 | awk '{print $5}' | cut -d'/' -f1)
    
  4. From the ol-node-01 instance SSH establish an SSH connection to VM-02.
     ssh opc@$VM02_IP
    
  5. Create a PHP info page to display PHP configuration.
     sudo tee /var/www/html/info.php > /dev/null << 'EOF'
     <?php phpinfo(); ?>
     EOF
    
  6. Create database connection test application.

     sudo tee /var/www/html/dbtest.php > /dev/null << 'EOF'
     <?php
     echo "<h1>Multi-VM LAMP Stack Test</h1>";
    
     // Database connection details
     define('DB_SERVER', '$VM01_IP');
     define('DB_USERNAME', 'admin');
     define('DB_PASSWORD', '[password]');
     define('DB_NAME', 'mysql');
    
     echo "<p>Testing connection to MySQL at: " . DB_SERVER . "</p>";
    
     // Test network connectivity
     $fp = @fsockopen(DB_SERVER, 3306, $errno, $errstr, 5);
     if (!$fp) {
         echo "<p style='color: red;'>ERROR: Cannot reach MySQL server</p>";
         echo "<p>Error: $errstr ($errno)</p>";
     } else {
         echo "<p style='color: green;'>✓ Network connection successful</p>";
         fclose($fp);
            
         // Test MySQL connection
         $link = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
         if($link === false){
             echo "<p style='color: red;'>ERROR: Could not connect to MySQL</p>";
             echo "<p>Error: " . mysqli_connect_error() . "</p>";
         } else {
             echo "<p style='color: green;'>✓ Successfully Connected to MySQL!</p>";
             echo "<p>MySQL Version: " . mysqli_get_server_info($link) . "</p>";
             echo "<p>Host Info: " . mysqli_get_host_info($link) . "</p>";
             mysqli_close($link);
         }
     }
     ?>
     EOF
    
  7. Set application file permissions.
     sudo chown apache:apache /var/www/html/*.php
    
  8. Update line define(‘DB_SERVER’, ‘$VM01_IP’); by changing the $VM01_IP value with the saved IP address value.
     sudo vi /var/www/html/dbtest.php
    

    Example Output:

    ```text … Code before define(‘DB_SERVER’, ‘192.168.122.???’); … Code after

  9. Exit VM-02.

     exit
    

Step 9: Access Your Webserver and Database Test application


  1. If SSH tunnel is closed then create for basic test.
     ssh -L 8081:$VM02_IP:80 opc@<YOUR_OCI_PUBLIC_IP>
    
  2. Basic LAMP test Browse to:
     http://localhost:8081/info.php
    
  3. Basic Database test Demo Browse to:
    http://localhost:8081/dbtest.php
    

Expected Results:

Basic test: Green “Successfully Connected to MySQL!” message


Step 10: Create and Load Employee Database

  1. You should be in your opc@ol-node-01 ssh instance.

  2. Retrieve IP address of VM-01 from DHCP leases.
     VM01_IP=$(sudo virsh net-dhcp-leases --network default | grep vm-01 | tail -1 | awk '{print $5}' | cut -d'/' -f1)
    
  3. Retrieve IP address of VM-02 from DHCP leases.
     VM02_IP=$(sudo virsh net-dhcp-leases --network default | grep vm-02 | tail -1 | awk '{print $5}' | cut -d'/' -f1)
    
  4. From the opc@ol-node-01 ssh instance, establish an SSH connection to VM-01.
     ssh opc@$VM01_IP
    
  5. Change to /tmp folder.
     cd /tmp
    
  6. Download MySQL employee sample database.
     curl -L -o master.zip https://github.com/datacharmer/test_db/zipball/master/
    
  7. Install compression tools.
     sudo dnf install -y unzip
    
  8. Extract the database.
     sudo unzip master.zip
    
     sudo mv datacharmer-test_db-* employees_db
    
     cd employees_db
    
  9. Load the database.
     mysql -u admin -p[password] < employees.sql
    
  10. Verify database loaded.
     mysql -u admin -p[password] -e "USE employees; SHOW TABLES; SELECT COUNT(*) FROM employees;"
    
  11. Exit database VM.
     exit
    

Step 11: Create Employee Database Web Application

  1. From the ol-node-01 SSH instance, establish an SSH connection to VM-02.
     ssh opc@$VM02_IP
    
  2. Create professional employee database application.
     sudo tee /var/www/html/employees.php > /dev/null << 'EOF'
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <title>Employee Database - Multi-VM LAMP Demo</title>
         <style>
             body { 
                 font-family: Arial, sans-serif; 
                 max-width: 1200px; 
                 margin: 0 auto; 
                 padding: 20px;
                 background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
             }
             .header { 
                 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                 color: white; 
                 text-align: center; 
                 padding: 30px;
                 border-radius: 15px;
                 margin-bottom: 20px;
                 box-shadow: 0 8px 16px rgba(0,0,0,0.1);
             }
             .info-box { 
                 background: rgba(255,255,255,0.9);
                 padding: 25px; 
                 border-radius: 12px;
                 box-shadow: 0 4px 8px rgba(0,0,0,0.1);
                 margin-bottom: 20px;
                 backdrop-filter: blur(10px);
             }
             .success { color: #28a745; font-weight: bold; }
             .error { color: #dc3545; font-weight: bold; }
             table { 
                 width: 100%; 
                 border-collapse: collapse; 
                 margin: 20px 0;
                 background: rgba(255,255,255,0.9);
                 border-radius: 12px;
                 overflow: hidden;
                 box-shadow: 0 4px 8px rgba(0,0,0,0.1);
             }
             th, td { 
                 padding: 15px; 
                 text-align: left; 
                 border-bottom: 1px solid #ddd;
             }
             th { 
                 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                 color: white;
                 font-weight: bold;
             }
             tr:hover { background-color: rgba(102, 126, 234, 0.1); }
             .stats { 
                 display: grid; 
                 grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); 
                 gap: 20px; 
                 margin: 20px 0; 
             }
             .stat-card { 
                 background: white; 
                 padding: 20px; 
                 border-radius: 10px; 
                 text-align: center; 
                 box-shadow: 0 4px 8px rgba(0,0,0,0.1);
             }
             .stat-number { font-size: 2em; font-weight: bold; color: #667eea; }
         </style>
     </head>
     <body>
         <div class="header">
             <h1>🏢 Employee Database Demo</h1>
             <p>MySQL Employee Sample Database on Multi-VM Architecture</p>
         </div>
    
         <?php
         // Database connection details
         define('DB_SERVER', '$VM01_IP');
         define('DB_USERNAME', 'empuser');
         define('DB_PASSWORD', '[password]');
         define('DB_NAME', 'employees');
    
         try {
             // Connect to MySQL database on separate VM
             $pdo = new PDO("mysql:host=" . DB_SERVER . ";dbname=" . DB_NAME, DB_USERNAME, DB_PASSWORD);
             $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                
             echo '<div class="info-box success">';
             echo '<h2>✅ Connected to Employee Database</h2>';
             echo '<p><strong>Database Server:</strong> ' . DB_SERVER . ' (vm-01)</p>';
             echo '<p><strong>Web Server:</strong> ' . gethostname() . ' (vm-02)</p>';
             echo '</div>';
                
             // Get database statistics
             $stats = [];
                
             $stmt = $pdo->query("SELECT COUNT(*) as count FROM employees");
             $stats['employees'] = $stmt->fetch()['count'];
                
             $stmt = $pdo->query("SELECT COUNT(*) as count FROM departments");
             $stats['departments'] = $stmt->fetch()['count'];
                
             $stmt = $pdo->query("SELECT COUNT(*) as count FROM salaries");
             $stats['salaries'] = $stmt->fetch()['count'];
                
             $stmt = $pdo->query("SELECT COUNT(*) as count FROM titles");
             $stats['titles'] = $stmt->fetch()['count'];
                
             echo '<div class="info-box">';
             echo '<h2>📊 Database Statistics</h2>';
             echo '<div class="stats">';
             echo '<div class="stat-card"><div class="stat-number">' . number_format($stats['employees']) . '</div><div>Employees</div></div>';
             echo '<div class="stat-card"><div class="stat-number">' . number_format($stats['departments']) . '</div><div>Departments</div></div>';
             echo '<div class="stat-card"><div class="stat-number">' . number_format($stats['salaries']) . '</div><div>Salary Records</div></div>';
             echo '<div class="stat-card"><div class="stat-number">' . number_format($stats['titles']) . '</div><div>Job Titles</div></div>';
             echo '</div>';
             echo '</div>';
                
             // Show recent employees
             echo '<div class="info-box">';
             echo '<h2>👥 Sample Employee Data</h2>';
             $stmt = $pdo->query("SELECT emp_no, first_name, last_name, gender, hire_date FROM employees ORDER BY hire_date DESC LIMIT 20");
             $employees = $stmt->fetchAll();
                
             echo '<table>';
             echo '<thead><tr><th>Employee #</th><th>First Name</th><th>Last Name</th><th>Gender</th><th>Hire Date</th></tr></thead>';
             echo '<tbody>';
                
             foreach ($employees as $emp) {
                 echo '<tr>';
                 echo '<td>' . htmlspecialchars($emp['emp_no']) . '</td>';
                 echo '<td>' . htmlspecialchars($emp['first_name']) . '</td>';
                 echo '<td>' . htmlspecialchars($emp['last_name']) . '</td>';
                 echo '<td>' . htmlspecialchars($emp['gender']) . '</td>';
                 echo '<td>' . htmlspecialchars($emp['hire_date']) . '</td>';
                 echo '</tr>';
             }
             echo '</tbody></table>';
             echo '</div>';
                
             // Show departments
             echo '<div class="info-box">';
             echo '<h2>🏬 Departments</h2>';
             $stmt = $pdo->query("SELECT dept_no, dept_name FROM departments ORDER BY dept_name");
             $departments = $stmt->fetchAll();
                
             echo '<table>';
             echo '<thead><tr><th>Department Code</th><th>Department Name</th></tr></thead>';
             echo '<tbody>';
                
             foreach ($departments as $dept) {
                 echo '<tr>';
                 echo '<td>' . htmlspecialchars($dept['dept_no']) . '</td>';
                 echo '<td>' . htmlspecialchars($dept['dept_name']) . '</td>';
                 echo '</tr>';
             }
             echo '</tbody></table>';
             echo '</div>';
                
         } catch (PDOException $e) {
             echo '<div class="info-box error">';
             echo '<h2>❌ Database Connection Error</h2>';
             echo '<p>Error: ' . htmlspecialchars($e->getMessage()) . '</p>';
             echo '</div>';
         }
         ?>
    
         <div class="info-box">
             <h2>🏗️ Multi-VM Architecture</h2>
             <p><strong>Database VM (vm-01):</strong> MySQL Server with Employee Database</p>
             <p><strong>Web VM (vm-02):</strong> Apache + PHP Web Application</p>
             <p><strong>Data Source:</strong> MySQL Sample Employee Database</p>
             <p><strong>Records:</strong> 300,000+ employees, 400,000+ salary records</p>
         </div>
     </body>
     </html>
     EOF
    
  3. Set proper permissions.
     sudo chown apache:apache /var/www/html/employees.php
    
  4. Update line define(‘DB_SERVER’, ‘$VM01_IP’); by changing the $VM01_IP value with the saved IP address value.
     sudo vi /var/www/html/employees.php
    

    Example Output:

    ```text … Code before define(‘DB_SERVER’, ‘192.168.122.???’); … Code after

  5. Exit web server VM.
     exit
    

Step 12: Access Employee Application

  1. If SSH tunnel is closed then create for basic test.
     ssh -L 8081:$VM02_IP:80 opc@<YOUR_OCI_PUBLIC_IP>
    
  2. Employee Database Demo Browse to:
     http://localhost:8081/employees.php
    

Expected Results:


Applications Available:

🔧 Basic LAMP Test (http://localhost:8081/dbtest.php)

🏢 Employee Database Demo (http://localhost:8081/employees.php)


Essential Management Commands

VM Management

  1. List running VMs and their status.
     sudo virsh list
    
  2. Start VM-01.
     sudo virsh start vm-01
    
  3. Shutdown VM-01.
     sudo virsh shutdown vm-01
    
  4. Display DHCP leases for VMs on the default network.
     sudo virsh net-dhcp-leases --network default
    

Setup For running this lab on Oracle Cloud Infrastructure

  1. Virtual Cloud Network Setup: ``` Navigate: Networking → Virtual Cloud Networks → Start VCN Wizard
    • Name: ol-vcn-01
    • Create VCN Navigate: Networking → Virtual Cloud Networks → kvm-network → Security Lists Add these ingress rules:
    • SSH: Source 0.0.0.0/0, TCP Port 22
    • HTTP: Source 0.0.0.0/0, TCP Port 80 ```
  2. Compute Instance Setup:
         Navigation: Hamburger Menu → Compute → Instances → Create Instance
    
         Configuration:
         - Name: ol-node-01
         - Image: Oracle Linux 8 (Latest)
         - Shape: VM.Standard.E4.Flex (4 OCPUs, 16GB RAM)
         - Boot Volume: 100 GB
         - VCN: ol-vcn-01 
         - Subnet: Public subnet ol-vcn-01
         - Assign Public IP: Yes
         - Add SSH Key: Upload your public key
    
  3. Test Compute public IP with SSH.
     ssh opc@<ol-vcn-01_PUBLIC_IP>
    
  4. Continue to Step 2: Validate Environment Supports Virtualization.

Next Steps

Learn to manage hosts and vms with Oracle Linux Virtual Manager (OLVM)

Acknowledgments

More Learning Resources

Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.

For product documentation, visit Oracle Help Center.