Skip to main content

Varnish: Loadbalancer Example with Nginx TLS Termination for Apache2 Webservers; Systemd Service Unit Configuration, Manage VCL Configurations with Varnishadm

1119 words·
Varnish Varnishadm Nginx Apache Loadbalancer

Overview
#

In this tutorial I’m using Varnish as loadbalancer for 3 Apache webservers. Nginx is used for the TLS termination in front of Varnish.

The following VMs are based on Debian 12 bookworm:

192.168.30.20 # Loadbalancer VM: Nginx TLS termination, Varnish loadbalancer
192.168.30.21 # Apache webserver VM 1
192.168.30.22 # Apache webserver VM 2
192.168.30.23 # Apache webserver VM 3 

Prerequisites
#

Install & Configure Apache
#

Install the Apache webserver on the Apache VMs:

# Install Apache
sudo apt install apache2 -y

# Verify the Apache status
sudo systemctl status apache2

Change the default “index.html” file on the Apache VMs and define a different VM number for each VM:

# Adopt the default HTML file
sudo vi /var/www/html/index.html
<!DOCTYPE html>
<html>

<head>
	<title>jklug.work</title>
</head>

<body>
	<h1>Apache VM1,2,3</h1>
</body>

</html>

Varnish Loadbalancer
#

Install Varnish
#

Install Varnish on the loadbalancer VM:

# Install Varnish
sudo apt install varnish -y

# Verify Varnish status
sudo systemctl status varnish

Varnish VCL Configuration
#

# Create a backup of the default configuration
sudo cp /etc/varnish/default.vcl /etc/varnish/default.vcl.bak

# Edit the default configuration
sudo vi /etc/varnish/default.vcl
vcl 4.1;

import directors;

backend server1 {
    .host = "192.168.30.21";
    .port = "80";
}

backend server2 {
    .host = "192.168.30.22";
    .port = "80";
}

backend server3 {
    .host = "192.168.30.23";
    .port = "80";
}

sub vcl_init {
    new my_backends = directors.round_robin();
    my_backends.add_backend(server1);
    my_backends.add_backend(server2);
    my_backends.add_backend(server3);
}

sub vcl_recv {
    set req.backend_hint = my_backends.backend();
    return (pass);
}

Restart Varnish
#

# Restart Varnish
sudo systemctl restart varnish

# Verify Varnish status
sudo systemctl status varnish

Verify Varnish Output
#

Every time Varnish is curled, it should output another Apache VM:

# Curl the Varnish output
curl localhost:6081
curl localhost:6081
curl localhost:6081

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

<body>
        <h1>Apache VM1</h1>
</body>

</html>

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

<body>
        <h1>Apache VM2</h1>
</body>

</html>

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

<body>
        <h1>Apache VM3</h1>
</body>

</html>

Nginx TLS Termination
#

Install Nginx
#

Install Nginx on the same loadbalancer VM where Varnish is installed:

# Install Nginx
sudo apt install nginx -y

# Verify the Nginx status
sudo systemctl status nginx

# Install Certbot
sudo apt install certbot -y

Nginx Configuration
#

# Copy the default configuration
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/varnish-lb-example.jklug.work

# Edit the new configuration
sudo vi /etc/nginx/sites-available/varnish-lb-example.jklug.work
server {
    listen 443 ssl;
    server_name varnish-lb-example.jklug.work;

    ssl_certificate         /etc/letsencrypt/live/varnish-lb-example.jklug.work/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/varnish-lb-example.jklug.work/privkey.pem;

    # TLS configurations
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://127.0.0.1:6081; # Varnish port 6081
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }
}
# Disable the default configuration
sudo rm /etc/nginx/sites-enabled/default

# Enable the new configuration
sudo ln -s /etc/nginx/sites-available/varnish-lb-example.jklug.work /etc/nginx/sites-enabled/

Restart Nginx
#

# Restart Nginx
sudo systemctl restart nginx

# Verify the Nginx status
sudo systemctl status nginx

DNS Entry
#

Since I’m using a Let’s Encrypt wildcard certificate it’s necessary to create a local hosts entry:

# Create a DNS entry for Nginx
192.168.30.20 varnish-lb-example.jklug.work

Verify Nginx Output
#

# Curl Nginx
curl https://varnish-lb-example.jklug.work
curl https://varnish-lb-example.jklug.work
curl https://varnish-lb-example.jklug.work

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

<body>
        <h1>Apache VM3</h1>
</body>

</html>

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

<body>
        <h1>Apache VM1</h1>
</body>

</html>

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

<body>
        <h1>Apache VM2</h1>
</body>

</html>

More
#

Varinish Systemd Service Unit
#

# Edit the default systemd service unit file
sudo vi /lib/systemd/system/varnish.service
[Unit]
Description=Varnish Cache, a high-performance HTTP accelerator
Documentation=https://www.varnish-cache.org/docs/ man:varnishd

[Service]
Type=simple

# Maximum number of open files (for ulimit -n)
LimitNOFILE=131072

# Locked shared memory - should suffice to lock the shared memory log
# (varnishd -l argument)
# Default log size is 80MB vsl + 1M vsm + header -> 82MB
# unit is bytes
LimitMEMLOCK=85983232
ExecStart=/usr/sbin/varnishd \
          -j unix,user=vcache \
          -F \
          -a :6081 \
          -T localhost:6082 \
          -f /etc/varnish/default.vcl \
          -S /etc/varnish/secret \
          -s malloc,256m
ExecReload=/usr/share/varnish/varnishreload
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
PrivateDevices=true

[Install]
WantedBy=multi-user.target
  • -a :6081 Varnish is listening on all available interfaces (0.0.0.0) on port 6081

  • localhost:6082 This interface allows to issue management commands to Varnish, such as purging the cache & loading new VCL configurations

  • -f /etc/varnish/default.vcl Default VCL configuration file


Change Varnish Port
#

For example set the default listening port to “8080”:

[Unit]
Description=Varnish Cache, a high-performance HTTP accelerator
Documentation=https://www.varnish-cache.org/docs/ man:varnishd

[Service]
Type=simple

# Maximum number of open files (for ulimit -n)
LimitNOFILE=131072

# Locked shared memory - should suffice to lock the shared memory log
# (varnishd -l argument)
# Default log size is 80MB vsl + 1M vsm + header -> 82MB
# unit is bytes
LimitMEMLOCK=85983232
ExecStart=/usr/sbin/varnishd \
          -j unix,user=vcache \
          -F \
          -a :8080 \
          -T localhost:6082 \
          -f /etc/varnish/default.vcl \
          -S /etc/varnish/secret \
          -s malloc,256m
ExecReload=/usr/share/varnish/varnishreload
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
PrivateDevices=true

[Install]
WantedBy=multi-user.target
# Reload the systemd configuration
sudo systemctl daemon-reload

# Restart Varnish
sudo systemctl restart varnish

Load new VCL Configuration
#

Create New VCL Configuration
#

# Create a new VCL configuration
sudo vi /etc/varnish/lb-example.vcl
vcl 4.1;

import directors;

backend server1 {
    .host = "192.168.30.21";
    .port = "80";
}

backend server2 {
    .host = "192.168.30.22";
    .port = "80";
}

sub vcl_init {
    new my_backends = directors.round_robin();
    my_backends.add_backend(server1);
    my_backends.add_backend(server2);
}

sub vcl_recv {
    set req.backend_hint = my_backends.backend();
    return (pass);
}

Load & Activate new VCL Configuration
#

Verify the Varnish secret:

# This file is used for authenticating administrative commands
 sudo cat /etc/varnish/secret
# Load new VCL configuration (Define path to Varnish secret)
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.load lb-example /etc/varnish/lb-example.vcl

# Activate the new configuration
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.use lb-example

# Shell output:
VCL 'lb-example' now active

List Active & Available VCL Configuartions
#

# List active & available configurations
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.list

# Shell output:
available   auto    warm         0    boot
active      auto    warm         0    lb-example

Verify Varnish Output:
#

Verify the new Varnish configuration, it now only outputs the Apache VM1 & VM2 webservers:

# Curl the Varnish Output
curl localhost:8080
curl localhost:8080
curl localhost:8080
...

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

<body>
        <h1>Apache VM2</h1>
</body>

</html>

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

<body>
        <h1>Apache VM1</h1>
</body>

</html>

Switch to Default VCL Configuration
#

# Switch to default configuration
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.use boot

# Shell output:
VCL 'boot' now active
# List active & available configurations
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.list

# Shell output:
active      auto    warm         0    boot
available   auto    warm         0    lb-example

Delete the new VCL Configuration
#

# Delete the "lb-example" configuration
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.discard lb-example
# List active & available configurations
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.list

# Shell output:
active   auto    warm         0    boot

Links #

# Varnish Official Documentation
https://varnish-cache.org/docs/index.html