Skip to main content

EDU Container

note

The memory of the VPS is almost full now, after installing several applications. Let's rescale it from 2 VCPUs and 4 GB RAM to 4 VCPU and 8 GB RAM. We can keep the disk size the same (40 GB).

Rescaling can be done from web interface of Hetzner. First we turn the power off, then select the new size, then click the button Rescale. It will take a few minutes. We can check with free -h the new size of the memory.

1. Introduction

We have installed already many docker-scripts apps, and the directory /var/ds/ has started to look a bit crowded. We can create an Incus container, install Docker and docker-scripts in it, and migrate some of these apps from the host to this container. For example let's move all the applications related to education (Moodle, VCLab, LinuxMint, RaspberryPi, etc.).

After this reorganization, the deployment diagram should look like this:


So, we are going to:

  1. Create an Incus container named edu.

  2. Give it a fixed IP and make some improvements (set a better prompt, etc.)

  3. Install Docker and docker-scripts. Install revproxy and mariadb.

  4. Migrate to it containers for Moodle, Guacamole, LinuxMint, RaspberryPi, etc.

  5. Change the configuration of sniproxy to forward to the IP of the Incus container the domains edu.user1.fs.al (Moodle) and vclab.user1.fs.al (Guacamole).

2. Create an Incus container

2.1 Create the container

Let's create a Debian container, named edu:

incus launch images:debian/12 edu \
-c security.nesting=true \
-c security.syscalls.intercept.mknod=true \
-c security.syscalls.intercept.setxattr=true

incus list
incus info edu
incus config show edu

The configuration -c security.nesting=true is needed in order to run docker inside the container. However, since the container is unprivileged, it does not really have any security implications.

Similarly, the other two configuration options are needed so that docker can handle its images efficiently.

2.2 Setup the container

Let's make a few small improvements inside the container:

# get a shell in the container
incus shell edu

# install some tools
apt install --yes nano curl wget

# uncomment some aliases
sed -i ~/.bashrc \
-e 's/# export LS_OPTIONS=/export LS_OPTIONS=/' \
-e 's/# alias ls=/alias ls=/' \
-e 's/# alias ll=/alias ll=/' \
-e 's/# alias l=/alias l=/'
cat ~/.bashrc

# set a better prompt
echo 'source ~/.bashrc_custom' >> ~/.bashrc

cat <<'EOF' > ~/.bashrc_custom
# set a better prompt
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;36m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\$ \[\033[00m\]'
EOF
source ~/.bashrc_custom

# install and enable bash-completion
apt install --yes bash-completion

cat <<'EOF' >> ~/.bashrc_custom
# enable programmable completion features
if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
source /etc/bash_completion
fi
EOF
nano ~/.bashrc_custom
source ~/.bashrc_custom

# enable automatic security updates
apt install --yes unattended-upgrades

# in case of using vim, uncomment its dark background setting
sed -i /etc/vim/vimrc \
-e 's/^"set background=dark/set background=dark/'

2.3 Set a fixed IP

We have to set a fixed IP to this container, so that sniproxy can forward HTTPS connections to it. If the IP is a dynamic random one, then the configuration of sniproxy has to be changed whenever the IP of the container changes, which is not convenient.

Remember that when we installed Incus, we limited the DHCP range of the containers to 10.31.96.2-10.31.96.200. So, we can used fixed IPs from 10.31.96.201 and above. Let's use 10.31.96.201 for this container.

ls /etc/systemd/network/
nano /etc/systemd/network/eth0.network

ip addr
IP=10.31.96.201/24
GW=10.31.96.1
cat <<EOF > /etc/systemd/network/eth0.network
[Match]
Name=eth0

[Address]
Address=$IP

[Route]
Gateway=$GW

[Network]
DHCP=no
DNS=8.8.8.8
EOF
nano /etc/systemd/network/eth0.network
systemctl restart systemd-networkd

ip addr
ip ro
ping google.com

exit
incus ls
incus restart edu
incus ls

3. Install Docker etc

3.1 Install Docker

  1. Get Docker’s official GPG key:

    wget https://download.docker.com/linux/ubuntu/gpg \
    -O /etc/apt/keyrings/docker.asc
    ls -l /etc/apt/keyrings/docker.asc
  2. Add the Docker sources to the apt list:

    cat <<EOF > /etc/apt/sources.list.d/docker.sources
    Types: deb
    URIs: https://download.docker.com/linux/debian
    Suites: bookworm
    Components: stable
    Signed-By: /etc/apt/keyrings/docker.asc
    EOF

    cat /etc/apt/sources.list.d/docker.sources
  3. Install it:

    apt update
    apt install --yes \
    docker-ce \
    docker-ce-cli \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin

3.2 Install docker-scripts

apt install git make m4 highlight tree
git clone \
https://gitlab.com/docker-scripts/ds \
/opt/docker-scripts/ds
cd /opt/docker-scripts/ds/
make install

3.3 Install revproxy

ds pull revproxy
ds init revproxy @revproxy
cd /var/ds/revproxy/
nano settings.sh
ds make

On settings.sh, before running ds make:

  • Set your email address to SSL_CERT_EMAIL.
  • Uncomment ENABLE_PROXY_PROTOCOL=true.

3.4 Install mariadb

ds pull mariadb
ds init mariadb @mariadb
cd /var/ds/mariadb/
nano settings.sh
ds make

3.5 Logwatch and notifications

Let's make sure that we get logwatch notifications from the edu container:

  1. Install and setup msmtp:

    apt install \
    msmtp msmtp-mta bsd-mailx
    cat << _EOF_ > /etc/msmtprc
    account smtp
    maildomain user1.fs.al
    host smtp.user1.fs.al
    from edu@user1.fs.al
    tls_starttls on
    set_from_header on

    account default : smtp
    aliases /etc/aliases.msmtp
    syslog LOG_MAIL
    _EOF_

    nano /etc/msmtprc
    cat << _EOF_ > /etc/aliases.msmtp
    default: dashohoxha@gmail.com
    _EOF_

    nano /etc/aliases.msmtp
    mail --help
    mail -s Test1 -- root <<< "Test 1"
  2. Install and setup logwatch:

    apt install --yes logwatch libdate-manip-perl
    cat << _EOF_ > /etc/logwatch/conf/logwatch.conf
    Range = between -7 days and -1 days
    _EOF_
    mkdir -p /etc/cron.weekly/
    mv /etc/cron.daily/00logwatch /etc/cron.weekly/
    nano /etc/cron.weekly/00logwatch
    /etc/cron.weekly/00logwatch

Now let's enable logwatch for the containers that we will install with docker-scripts inside edu:

cat << _EOF_ >> ~/.ds/global_settings.sh
SMTP_SERVER="smtp.user1.fs.al"
SMTP_DOMAIN="user1.fs.al"
#SMTP_PORT=25
LOGWATCH_EMAIL=dashohoxha@gmail.com
_EOF_

nano ~/.ds/global_settings.sh

4. Migrate containers

4.1 Setup sniproxy

Go to /var/ds/sniproxy/, edit etc/sniproxy.conf and modify the table so that it looks like this:

table {
# incus container: edu
edu.user1.fs.al 10.31.96.201 proxy_protocol
vclab.user1.fs.al 10.31.96.201 proxy_protocol

# docker container: revproxy
.* revproxy proxy_protocol
}
cd /var/ds/sniproxy/
nano etc/sniproxy.conf
ds restart

The requests for the domains edu.user1.fs.al and vclab.user1.fs.al are being forwarded to the IP of the incus container edu. The rest are forwarded to the docker container revproxy, as usual.

4.2 Migrate Moodle

  1. On the host, make a backup and stop the container:

    cd /var/ds/edu.user1.fs.al/
    ds backup
    ds stop
  2. Transfer the scripts and the directory of the container:

    incus file --help
    incus file push --help

    incus file push -p -r \
    /opt/docker-scripts/moodle/ \
    edu/opt/docker-scripts/
    incus file push -p -r \
    /var/ds/edu.user1.fs.al \
    edu/var/ds/
  3. Inside the incus container, build the Moodle container and restore the backup:

    incus shell edu

    cd /var/ds/edu.user1.fs.al/
    ds make
    ds restore backup-*.tgz
  4. Open https://edu.user1.fs.al in browser and login as admin. Go to the Notifications page and make sure that the database is updated.

  5. On the host, clean up the Moodle container etc.

    ds /var/ds/edu.user1.fs.al/
    ds stop
    ds remove
    ds mariadb drop
    ds revproxy rm

    cd ..
    rm -rf edu.user1.fs.al/
    rm -rf /opt/docker-scripts/moodle/

    cd revproxy/
    ls domains/
    ls letsencrypt/live/
    ds del-ssl-cert edu.user1.fs.al

4.3 Migrate Guacamole

  1. On the host, make a backup and stop the container:

    cd /var/ds/vclab.user1.fs.al/
    ds backup
    ds stop
  2. Transfer the scripts and the directory of the container:

    incus file push -pr \
    /opt/docker-scripts/guacamole/ \
    edu/opt/docker-scripts/
    incus file push -pr \
    /var/ds/vclab.user1.fs.al \
    edu/var/ds/
  3. Inside the incus container, build the Guacamole container and restore the backup:

    incus shell edu

    cd /var/ds/vclab.user1.fs.al/
    ds make
    ds restore backup-*.tgz
  4. Open https://vclab.user1.fs.al in browser.

  5. On the host, clean up the Guacamole container etc.

    ds /var/ds/vclab.user1.fs.al/
    ds stop
    ds remove
    ds mariadb drop
    ds revproxy rm

    cd ..
    rm -rf vclab.user1.fs.al/
    rm -rf /opt/docker-scripts/guacamole/

    cd revproxy/
    ls domains/
    ls letsencrypt/live/
    ds del-ssl-cert vclab.user1.fs.al

4.4 LinuxMint and RaspberryPi

  1. On the host, make a backup:

    cd /var/ds/mate1/
    ds users backup
    ds stop

    cd /var/ds/raspi1/
    ds users backup
    ds stop
  2. Transfer the scripts and the directory of the containers:

    incus file push -pr \
    /opt/docker-scripts/linuxmint/ \
    edu/opt/docker-scripts/
    incus file push -pr \
    /var/ds/mate1 \
    edu/var/ds/

    incus file push -pr \
    /opt/docker-scripts/raspberrypi/ \
    edu/opt/docker-scripts/
    incus file push -pr \
    /var/ds/raspi1 \
    edu/var/ds/
  3. Inside edu, build the containers and restore the backups:

    incus shell edu

    cd /var/ds/mate1/
    ds make
    ds users restore backup/users-*.tgz

    cd /var/ds/raspi1/
    ds make
    ds users restore backup/users-*.tgz
  4. Clean up:

    ds @mate1 remove
    ds @raspi1 remove

    rm -rf /var/ds/mate1/
    rm -rf /var/ds/raspi1/

    rm -rf /opt/docker-scripts/linuxmint/
    rm -rf /opt/docker-scripts/raspberrypi/

    docker system prune