You can have LXD containers on your home computer, you can also have them on your Virtual-Private Server (VPS). If you have any further questions on LXD, see https://www.stgraber.org/2016/03/11/lxd-2-0-blog-post-series-012/
Here we see how to configure on a VPS at DigitalOcean (yeah, referral). We go cheap and select the 512MB RAM and 20GB disk VPS for $5/month. Containers are quite lightweight, so it’s interesting to see how many we can squeeze. We are going to use ZFS for the storage of the containers, stored on a file and not a block device. Here is what we are doing today,
- Set up LXD on a 512MB RAM/20GB diskspace VPS
- Create a container with a web server
- Expose the container service to the Internet
- Visit the webserver from our browser
Set up LXD on DigitalOcean
When creating the VPS, it is important to change these two options; we need 16.04 (default is 14.04) so that it has ZFS pre-installed as a kernel module, and we try out the cheapest VPS offering with 512MB RAM.
Once we create the VPS, we connect with
$ ssh root@128.199.41.205 # change with the IP address you get from the DigitalOcean panel The authenticity of host '128.199.41.205 (128.199.41.205)' can't be established. ECDSA key fingerprint is SHA256:7I094lF8aeLFQ4WPLr/iIX4bMs91jNiKhlIJw3wuMd4. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '128.199.41.205' (ECDSA) to the list of known hosts. Welcome to Ubuntu 16.04 LTS (GNU/Linux 4.4.0-24-generic x86_64) * Documentation: https://help.ubuntu.com/ 0 packages can be updated. 0 updates are security updates. root@ubuntu-512mb-ams3-01:~# apt update Get:1 http://security.ubuntu.com/ubuntu xenial-security InRelease [94.5 kB] Hit:2 http://ams2.mirrors.digitalocean.com/ubuntu xenial InRelease Get:3 http://security.ubuntu.com/ubuntu xenial-security/main Sources [24.9 kB] ... Fetched 10.2 MB in 4s (2,492 kB/s) Reading package lists... Done Building dependency tree Reading state information... Done 13 packages can be upgraded. Run 'apt list --upgradable' to see them. root@ubuntu-512mb-ams3-01:~# apt upgrade Reading package lists... Done Building dependency tree Reading state information... Done Calculating upgrade... Done The following packages will be upgraded: dnsmasq-base initramfs-tools initramfs-tools-bin initramfs-tools-core libexpat1 libglib2.0-0 libglib2.0-data lshw python3-software-properties shared-mime-info snapd software-properties-common wget 13 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Need to get 6,979 kB of archives. After this operation, 78.8 kB of additional disk space will be used. Do you want to continue? [Y/n] Y ... Processing triggers for initramfs-tools (0.122ubuntu8.1) ... update-initramfs: Generating /boot/initrd.img-4.4.0-24-generic W: mdadm: /etc/mdadm/mdadm.conf defines no arrays. Processing triggers for libc-bin (2.23-0ubuntu3) ...
We update the package list and then upgrade any packages that need upgrading.
root@ubuntu-512mb-ams3-01:~# apt policy lxd lxd: Installed: 2.0.2-0ubuntu1~16.04.1 Candidate: 2.0.2-0ubuntu1~16.04.1 Version table: *** 2.0.2-0ubuntu1~16.04.1 500 500 http://mirrors.digitalocean.com/ubuntu xenial-updates/main amd64 Packages 500 http://security.ubuntu.com/ubuntu xenial-security/main amd64 Packages 100 /var/lib/dpkg/status 2.0.0-0ubuntu4 500 500 http://mirrors.digitalocean.com/ubuntu xenial/main amd64 Packages
The lxd package is already installed, all the better. Nice touch 🙂
root@ubuntu-512mb-ams3-01:~# apt install zfsutils-linux Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: libnvpair1linux libuutil1linux libzfs2linux libzpool2linux zfs-doc zfs-zed Suggested packages: default-mta | mail-transport-agent samba-common-bin nfs-kernel-server zfs-initramfs The following NEW packages will be installed: libnvpair1linux libuutil1linux libzfs2linux libzpool2linux zfs-doc zfs-zed zfsutils-linux 0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded. Need to get 881 kB of archives. After this operation, 2,820 kB of additional disk space will be used. Do you want to continue? [Y/n] Y ... zed.service is a disabled or a static unit, not starting it. Processing triggers for libc-bin (2.23-0ubuntu3) ... Processing triggers for systemd (229-4ubuntu6) ... Processing triggers for ureadahead (0.100.0-19) ... root@ubuntu-512mb-ams3-01:~# _
We installed zfsutils-linux in order to be able to use ZFS as storage for our containers. In this tutorial we are going to use a file as storage (still, ZFS filesystem) instead of a block device. If you subscribe to the DO Beta for block storage volumes, you can get a proper block device for the storage of the containers. Currently free to beta members, available only on the NYC1 datacenter.
root@ubuntu-512mb-ams3-01:~# df -h / Filesystem Size Used Avail Use% Mounted on /dev/vda1 20G 1.1G 18G 6% / root@ubuntu-512mb-ams3-01:~# _
We got 18GB free diskspace, so let’s allocate 15GB for LXD.
root@ubuntu-512mb-ams3-01:~# lxd init Name of the storage backend to use (dir or zfs): zfs Create a new ZFS pool (yes/no)? yes Name of the new ZFS pool: lxd-pool Would you like to use an existing block device (yes/no)? no Size in GB of the new loop device (1GB minimum): 15 Would you like LXD to be available over the network (yes/no)? no Do you want to configure the LXD bridge (yes/no)? yes we accept the default settings for the bridge configuration Warning: Stopping lxd.service, but it can still be activated by: lxd.socket LXD has been successfully configured. root@ubuntu-512mb-ams3-01:~# _
What we did,
- we initialized LXD with the ZFS storage backend,
- we created a new pool and gave a name (here, lxd-pool),
- we do not have a block device, so we get a (sparse) image file that contains the ZFS filesystem
- we do not want now to make LXD available over the network
- we want to configure the LXD bridge for the inter-networking of the containters
Let’s create a new user and add them to the lxd group,
root@ubuntu-512mb-ams3-01:~# adduser ubuntu Adding user `ubuntu' ... Adding new group `ubuntu' (1000) ... Adding new user `ubuntu' (1000) with group `ubuntu' ... Creating home directory `/home/ubuntu' ... Copying files from `/etc/skel' ... Enter new UNIX password: ******** Retype new UNIX password: ******** passwd: password updated successfully Changing the user information for ubuntu Enter the new value, or press ENTER for the default Full Name []: <ENTER> Room Number []: <ENTER> Work Phone []: <ENTER> Home Phone []: <ENTER> Other []: <ENTER> Is the information correct? [Y/n] Y root@ubuntu-512mb-ams3-01:~# _
The username is ubuntu. Make sure you add a good password, since we do not deal in this tutorial with best security practices. Many people use scripts on these VPSs that try common usernames and passwords. When you create a VPS, it is nice to have a look at /var/log/auth.log for those failed attempts to get into your VPS. Here are a few lines from this VPS,
Jun 26 18:36:15 digitalocean sshd[16318]: Failed password for root from 121.18.238.29 port 45863 ssh2 Jun 26 18:36:15 digitalocean sshd[16320]: Connection closed by 123.59.134.76 port 49378 [preauth] Jun 26 18:36:17 digitalocean sshd[16318]: Failed password for root from 121.18.238.29 port 45863 ssh2 Jun 26 18:36:20 digitalocean sshd[16318]: Failed password for root from 121.18.238.29 port 45863 ssh2
We add the ubuntu user into the lxd group in order to be able to run commands as a non-root user.
root@ubuntu-512mb-ams3-01:~# adduser ubuntu lxd Adding user `ubuntu' to group `lxd' ... Adding user ubuntu to group lxd Done. root@ubuntu-512mb-ams3-01:~# _
We are now good to go. Log in as user ubuntu and run an LXD command to list images.
Create a Web server in a container
We launch (init and start) a container named c1.
The ubuntu:x in the screenshot is an alias for Ubuntu 16.04 (Xenial), that resides in the ubuntu: repository of images. You can find other distributions in the images: repository.
As soon as the launch action was completed, I run the list action. Then, after a few seconds, I run it again. You can notice that it took a few seconds before the container actually booted and got an IP address.
Let’s enter into the container by executing a shell. We update and then upgrade the container.
ubuntu@ubuntu-512mb-ams3-01:~$ lxc exec c1 -- /bin/bash root@c1:~# apt update Get:1 http://security.ubuntu.com/ubuntu xenial-security InRelease [94.5 kB] Hit:2 http://archive.ubuntu.com/ubuntu xenial InRelease Get:3 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [94.5 kB] ... Fetched 9819 kB in 2s (3645 kB/s) Reading package lists... Done Building dependency tree Reading state information... Done 13 packages can be upgraded. Run 'apt list --upgradable' to see them. root@c1:~# apt upgrade Reading package lists... Done Building dependency tree Reading state information... Done Calculating upgrade... Done The following packages will be upgraded: dnsmasq-base initramfs-tools initramfs-tools-bin initramfs-tools-core libexpat1 libglib2.0-0 libglib2.0-data lshw python3-software-properties shared-mime-info snapd software-properties-common wget 13 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Need to get 6979 kB of archives. After this operation, 3339 kB of additional disk space will be used. Do you want to continue? [Y/n] Y Get:1 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 initramfs-tools all 0.122ubuntu8.1 [8602 B] ... Processing triggers for initramfs-tools (0.122ubuntu8.1) ... Processing triggers for libc-bin (2.23-0ubuntu3) ... root@c1:~#
Let’s install nginx, our Web server.
root@c1:~# apt install nginx Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: fontconfig-config fonts-dejavu-core libfontconfig1 libfreetype6 libgd3 libjbig0 libjpeg-turbo8 libjpeg8 libtiff5 libvpx3 libxpm4 libxslt1.1 nginx-common nginx-core Suggested packages: libgd-tools fcgiwrap nginx-doc ssl-cert The following NEW packages will be installed: fontconfig-config fonts-dejavu-core libfontconfig1 libfreetype6 libgd3 libjbig0 libjpeg-turbo8 libjpeg8 libtiff5 libvpx3 libxpm4 libxslt1.1 nginx nginx-common nginx-core 0 upgraded, 15 newly installed, 0 to remove and 0 not upgraded. Need to get 3309 kB of archives. After this operation, 10.7 MB of additional disk space will be used. Do you want to continue? [Y/n] Y Get:1 http://archive.ubuntu.com/ubuntu xenial/main amd64 libjpeg-turbo8 amd64 1.4.2-0ubuntu3 [111 kB] ... Processing triggers for ufw (0.35-0ubuntu2) ... root@c1:~#
Is the Web server running? Let’s check with the ss command (preinstalled, from package iproute2)
root@c1:~# ss -tula Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 *:bootpc *:* tcp LISTEN 0 128 *:http *:* tcp LISTEN 0 128 *:ssh *:* tcp LISTEN 0 128 :::http :::* tcp LISTEN 0 128 :::ssh :::* root@c1:~#
The parameters mean
- -t: Show only TCP sockets
- -u: Show only UDP sockets
- -l: Show listening sockets
- -a: Show all sockets (makes no difference because of previous options; it’s just makes an easier word to remember, tula)
Of course, there is also lsof with the parameter -i (IPv4/IPv6).
root@c1:~# lsof -i COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME dhclient 240 root 6u IPv4 45606 0t0 UDP *:bootpc sshd 306 root 3u IPv4 47073 0t0 TCP *:ssh (LISTEN) sshd 306 root 4u IPv6 47081 0t0 TCP *:ssh (LISTEN) nginx 2034 root 6u IPv4 51636 0t0 TCP *:http (LISTEN) nginx 2034 root 7u IPv6 51637 0t0 TCP *:http (LISTEN) nginx 2035 www-data 6u IPv4 51636 0t0 TCP *:http (LISTEN) nginx 2035 www-data 7u IPv6 51637 0t0 TCP *:http (LISTEN) root@c1:~#
From both commands we verify that the Web server is indeed running inside the VPS, along with a SSHD server.
Let’s change a bit the default Web page,
root@c1:~# nano /var/www/html/index.nginx-debian.html
Expose the container service to the Internet
Now, if we try to visit the public IP of our VPS at http://128.199.41.205/ we obviously notice that there is no Web server there. We need to expose the container to the world, since the container only has a private IP address.
The following iptables line exposes the container service at port 80. Note that we run this as root on the VPS (root@ubuntu-512mb-ams3-01:~#), NOT inside the container (root@c1:~#).
iptables -t nat -I PREROUTING -i eth0 -p TCP -d 128.199.41.205/32 --dport 80 -j DNAT --to-destination 10.160.152.184:80
Adapt accordingly the public IP of your VPS and the private IP of your container (10.x.x.x). Since we have a web server, this is port 80.
We have not made this firewall rule persistent as it is outside of our scope; see iptables-persistent on how to make it persistent.
Visit our Web server
Here is the URL, http://128.199.41.205/ so let’s visit it.
That’s it! We created an LXD container with the nginx Web server, then exposed it to the Internet.
9 comments
5 pings
Skip to comment form
Thank you so much for this guide!
I couldn’t figure out how to do this, and it really helped, many thanks!
you are a life saver
Do you know how to properly point domain directly to the containers ip address on a server with multiple containers ?
Right on!
Glad you wrote this.
Author
@michael:
If you have several containers with, for example, Web servers, then you would use a “reverse proxy” to direct each connection to the correct container.
One such software for “reverse proxy” is HAProxy. See https://simos.info/blog/how-to-set-up-multiple-secure-ssltls-qualys-ssl-labs-a-websites-using-lxd-containers/ on how to set it up.
In most situations, beside HAProxy is already used, iptables would be more than enough
Author
If it is a single website, then it’s fine to use iptables.
If you wanted several websites, with each to have SSL/TLS support, then you would need an SNI proxy (such as HAProxy).
There is an updated guide on LXD on Digitalocean at https://www.digitalocean.com/community/tutorials/how-to-set-up-and-use-lxd-on-ubuntu-16-04
Thank you for this. I have been looking for something like this without success and I am happy I found it.
I’ve been following your LXD/haproxy stuff lately and think it’s fantastic! I like the recent Hetzner Cloud guides as well. Was wondering if you have heard of Turnkey LXC? I’m sure you know of Turnkey Linux and their appliances, but I just stumbled upon their LXC model for their Turnkey appliances, which is kind of similar to what you are doing with haproxy/lxd:
https://www.turnkeylinux.org/lxc
Anyway, I love the lxc/lxd model, with haproxy or some other reverse proxy container (nginx whatever) handling the domain based load balancing and SSL. All very interesting stuff!!
I like the idea though of sticking with haproxy/lxd, but perhaps utilize the Turnkey LXC images for some services/applications/domains. There is probably an easy way to convert LXC images to LXD, or can you just use any LXC image out of the box with LXD? Thanks for any feedback!
[…] have seen how to try out LXD containers on Ubuntu on DigitalOcean. In this post, we will see how to use the new DigitalOcean block storage support (just out of […]
[…] That’s it! LXD is up and running, and we successfully created a container. See these instructions on how to test the container with a Web server. […]
[…] previous posts we saw how to set up LXD on a DigitalOcean VPS, how to set up LXD on a Scaleway VPS, and how the lifecycle of an LXD container looks […]
[…] previous posts, we saw how to configure LXD/LXC containers on a VPS on DigitalOcean and Scaleway. There are many more VPS […]
[…] have set up LXD on either our personal computer or on the cloud (like DigitalOcean and Scaleway). Actually, we can even try LXD online for free at […]