Apache on a MicroVM
Run the Apache HTTP Server in a MicroVM to serve web applications. Apache is heavier than Nginx but more flexible thanks to its modular design.
Preparing the Image
sudo mount -o loop rootfs.img /mnt/microvm
# Debian/Ubuntu
sudo chroot /mnt/microvm bash -c '
apt update && apt install -y apache2
a2enmod rewrite
a2enmod proxy
a2enmod proxy_http
'
# Alpine
sudo chroot /mnt/microvm bash -c '
apk add apache2 apache2-proxy
'
sudo umount /mnt/microvm
Optimal Configuration
sudo mount -o loop rootfs.img /mnt/microvm
sudo tee /mnt/microvm/etc/apache2/conf-available/microvm.conf << 'APACHECONF'
# Performance tuning for MicroVM
ServerRoot "/etc/apache2"
PidFile ${APACHE_PID_FILE}
Timeout 30
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
# MPM Event (most efficient)
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 100
MaxConnectionsPerChild 1000
</IfModule>
# Logging
ErrorLog /var/log/apache2/error.log
LogLevel warn
APACHECONF
sudo chroot /mnt/microvm a2enconf microvm
sudo umount /mnt/microvm
Running the MicroVM
qemu-system-x86_64 \
-M microvm -enable-kvm -cpu host -m 512m -smp 2 \
-kernel vmlinuz \
-append "console=ttyS0 root=/dev/vda rw" \
-drive id=rootfs,file=rootfs.img,format=raw,if=none,cache=none \
-device virtio-blk-device,drive=rootfs \
-netdev user,id=net0,hostfwd=tcp::8080-:80 \
-device virtio-net-device,netdev=net0 \
-nographic -serial stdio
Setting Up the Virtual Host
# Create the document root
mkdir -p /var/www/html
# Create an index page
cat > /var/www/html/index.html << 'HTML'
<!DOCTYPE html>
<html>
<head><title>Apache on MicroVM</title></head>
<body>
<h1>Apache HTTP Server on QEMU MicroVM</h1>
<p>Server is running successfully!</p>
</body>
</html>
HTML
# Virtual host config
cat > /etc/apache2/sites-available/000-default.conf << 'VHOST'
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/html
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
VHOST
Reverse Proxy Setup
# Enable modules
a2enmod proxy
a2enmod proxy_http
# Create the proxy config
cat > /etc/apache2/sites-available/proxy.conf << 'PROXY'
<VirtualHost *:80>
ServerName proxy.local
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>
PROXY
a2ensite proxy
systemctl restart apache2
Auto Start
sudo tee /mnt/microvm/etc/init.d/apache2 << 'APACHEINIT'
#!/sbin/openrc-run
APACHE2=/usr/sbin/apache2
PIDFILE=/run/apache2.pid
depend() {
need net
keyword timeout 60
}
start() {
ebegin "Starting Apache2"
mkdir -p /run/apache2
$APACHE2 -f /etc/apache2/apache2.conf -DFOREGROUND &
echo $! > $PIDFILE
eend $?
}
stop() {
ebegin "Stopping Apache2"
kill $(cat $PIDFILE) 2>/dev/null
eend $?
}
APACHEINIT
sudo chmod +x /mnt/microvm/etc/init.d/apache2
sudo chroot /mnt/microvm rc-update add apache2 default
SSL/TLS (Let's Encrypt)
# Install certbot
apt install -y certbot python3-certbot-apache
# Obtain certificate
certbot --apache -d example.com -d www.example.com
# Auto renew
echo "0 12 * * * /usr/bin/certbot renew --quiet" | crontab -
Performance Tuning
# Enable compression
a2enmod deflate
# Check the modules
apachectl -M
# Test config
apachectl configtest
# Benchmark
ab -n 1000 -c 10 http://localhost/
Troubleshooting
Port 80 is already in use
# Check what is using port 80
ss -tlnp | grep :80
# Stop the other service or change the port
Listen 8080
Out of memory
# Reduce MaxRequestWorkers
MaxRequestWorkers 50
# Use mpm_event instead of mpm_prefork
a2dismod mpm_prefork
a2enmod mpm_event
# Disable unused modules
a2dismod autoindex
a2dismod negotiation
Permission error
# Set permissions
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html