Add Alpine Linux and Docker Infrastructure Three Years Later

This commit is contained in:
Wesley Moore 2022-02-28 14:50:24 +10:00
parent f0eed29ba7
commit 902b75077e
No known key found for this signature in database
GPG key ID: BF67766C0BC2D0EE
3 changed files with 321 additions and 0 deletions

View file

@ -0,0 +1,106 @@
+++
title = "Alpine Linux and Docker Infrastructure Three Years Later"
date = 2022-02-28T09:52:10+10:00
[extra]
#updated = 2022-01-27T21:07:32+10:00
+++
Three years ago I published,
[Rebuilding My Personal Infrastructure With Alpine Linux and Docker][original-post],
in which I described how I was hosting various applications using an [Alpine
Linux][alpine] host and [Docker] on a virtual machine at [Vultr]. I thought it
would be good to write a follow-up on how this worked out.
<!-- more -->
The server I set up in 2019 is still running, although it has been through a
few Alpine upgrades in that time. Since there are very few packages installed
on the host, upgrades are painless. When I originally configured the
server Docker Compose was not in the stable Alpine package repos. It was added
in the mid-2019 Alpine 3.10 release. I was glad to be able to remove `pip` and
not have to manually manage compose updates after migrating to the package from
that point on.
In 2019 the services I was hosting looked like this:
![Before](https://www.wezm.net/images/2019/services.svg)
In 2022 the situation is now:
![After](services.svg)
As you can see the server is now running more containers. It now hosts a number
of new services including a [Pleroma] instance and several web-applications I
built in Rust ([leaf], [quotes], [dewpoint]). I also retired the [Binary
Trance] Rails instance and migrated from [acme.sh] to [lego]. I ran a [Nitter]
instance for a while but removed it after it bots made it consume a huge amount
of bandwidth. See my [Burning 2.5Tb of Bandwidth Hosting a Nitter
Instance](@/posts/2021/nitter-bandwidth/index.md) post for more details on
that.
The [Read Rust] application is implemented in [Crystal]. This one posed a bit
of a challenge. It is the only container I run that is not derived from a
minimal Alpine base image that I build myself. Instead, it uses `debian-slim`. I
need to use a specific version of the Crystal compiler to build my application,
which means I can't use the version in the Alpine package repos. Additionally
the Linux binaries that the project published for this version are not
dynamically linked but the bundled `libgc.a` assumes a `glibc` based system:
$ crystal build src/asdf.cr
/usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../x86_64-alpine-linux-musl/bin/ld: /crystal-0.34.0-1/bin/../lib/crystal/lib/libgc.a(pthread_support.o): in function `GC_thr_init':
pthread_support.c:(.text+0x1137): undefined reference to `gnu_get_libc_version'
For this reason I opted to use a Debian base image for this container and that's
worked fine.
Another notable change is the move from [acme.sh] to [lego] for managing TLS
certificates. In my original post I noted the following regarding how the
certificates were renewed:
> Docker and cron is also a challenge. I ended up solving that with a simple
> solution: use the host cron to `docker exec` acme.sh in the `hitch` container.
> Perhaps not “pure” Docker but a lot simpler than some of the options I saw.
It turned out that this never worked. I could see that cron was running the
script but the certs would not get renewed. For a while I ran the
script manually to renew them, which did work. Eventually I got sick of this, and
thinking `acme.sh` was to blame I searched for an alternative.
I settled on [lego], an ACME client implemented in Go. I [discovered and
suggested a fix for a bug][lego-bug] and [contributed LuaDNS
support][lego-luadns] in May 2020 and then migrated over to using it instead of
`acme.sh`. It was in the final stages of test this that I discovered the cron
bug was in my script all along. Adding `-T` to inhibit `docker-compose exec`
from allocating a TTY fixed the issue. It's likely this would have fixed the
issue for `acme.sh` as well. Ultimately lego felt like the better option as the
code in `acme.sh` for constructing API requests and [parsing their
results][acme-parse] seemed quite fragile
[Jokes aside][it-works-on-my-machine], I still find the Docker workflow of
iterating on an image locally then shipping it when it's working to be quite
pleasant. In summary this server has served me well over the last three years
and I have no immediate plans to rebuild again. It's been reliable and mostly
hassle free. Hosting on [Vultr] has also been reliable and stable over that
whole time with only the odd interruption for network maintenance or network
issues.
[original-post]: https://www.wezm.net/technical/2019/02/alpine-linux-docker-infrastructure/
[alpine]: https://alpinelinux.org/
[Docker]: https://www.docker.com/
[Vultr]: https://www.vultr.com/?ref=7903263
[lego-luadns]: https://github.com/go-acme/lego/pull/1135
[lego-bug]: https://github.com/go-acme/lego/issues/1150
[acme-parse]: https://github.com/acmesh-official/acme.sh/blob/2a2d556551e9266a3924da205c1ede55d89a689d/dnsapi/dns_lua.sh#L117-L126
[Pleroma]: https://pleroma.social/
[lego]: https://go-acme.github.io/lego/
[acme.sh]: https://github.com/acmesh-official/acme.sh
[Read Rust]: https://github.com/wezm/read-rust/
[Crystal]: https://crystal-lang.org/
[Binary Trance]: https://binarytrance.com/
[leaf]: https://github.com/wezm/leaf
[quotes]: https://github.com/wezm/Quotes
[dewpoint]: https://github.com/wezm/dewpoint.7bit.org
[Nitter]: https://github.com/zedeus/nitter
[it-works-on-my-machine]: https://www.reddit.com/r/ProgrammerHumor/comments/cw58z7/it_works_on_my_machine/

View file

@ -0,0 +1,24 @@
digraph G {
node[shape=box];
edge[headport=n];
subgraph cluster {
hitch
lego
}
hitch -> varnish;
varnish -> nginx;
varnish -> pkb;
varnish -> wizards;
varnish -> rust_melbourne;
varnish -> pleroma;
varnish -> read_rust;
varnish -> read_rust_leaf;
varnish -> quotes;
varnish -> dewpoint;
pkb -> syncthing;
read_rust -> postgresql:ne;
wizards -> postgresql;
rust_melbourne -> postgresql:ne;
pleroma -> postgresql:ne;
}

View file

@ -0,0 +1,191 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.50.0 (0)
-->
<!-- Title: G Pages: 1 -->
<svg width="891pt" height="276pt"
viewBox="0.00 0.00 890.50 276.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 272)">
<title>G</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-272 886.5,-272 886.5,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster</title>
<polygon fill="none" stroke="black" points="314,-208 314,-260 456,-260 456,-208 314,-208"/>
</g>
<!-- hitch -->
<g id="node1" class="node">
<title>hitch</title>
<polygon fill="none" stroke="black" points="448,-252 394,-252 394,-216 448,-216 448,-252"/>
<text text-anchor="middle" x="421" y="-230.3" font-family="Times,serif" font-size="14.00">hitch</text>
</g>
<!-- varnish -->
<g id="node3" class="node">
<title>varnish</title>
<polygon fill="none" stroke="black" points="456,-180 386,-180 386,-144 456,-144 456,-180"/>
<text text-anchor="middle" x="421" y="-158.3" font-family="Times,serif" font-size="14.00">varnish</text>
</g>
<!-- hitch&#45;&gt;varnish -->
<g id="edge1" class="edge">
<title>hitch&#45;&gt;varnish:n</title>
<path fill="none" stroke="black" d="M421,-215.81C421,-208.28 421,-199.1 421,-190.06"/>
<polygon fill="black" stroke="black" points="424.5,-190 421,-180 417.5,-190 424.5,-190"/>
</g>
<!-- lego -->
<g id="node2" class="node">
<title>lego</title>
<polygon fill="none" stroke="black" points="376,-252 322,-252 322,-216 376,-216 376,-252"/>
<text text-anchor="middle" x="349" y="-230.3" font-family="Times,serif" font-size="14.00">lego</text>
</g>
<!-- nginx -->
<g id="node4" class="node">
<title>nginx</title>
<polygon fill="none" stroke="black" points="56,-108 0,-108 0,-72 56,-72 56,-108"/>
<text text-anchor="middle" x="28" y="-86.3" font-family="Times,serif" font-size="14.00">nginx</text>
</g>
<!-- varnish&#45;&gt;nginx -->
<g id="edge2" class="edge">
<title>varnish&#45;&gt;nginx:n</title>
<path fill="none" stroke="black" d="M385.78,-159.92C297.61,-156.8 71.7,-146.02 33.52,-116.75"/>
<polygon fill="black" stroke="black" points="36.29,-114.59 28,-108 30.37,-118.33 36.29,-114.59"/>
</g>
<!-- pkb -->
<g id="node5" class="node">
<title>pkb</title>
<polygon fill="none" stroke="black" points="128,-108 74,-108 74,-72 128,-72 128,-108"/>
<text text-anchor="middle" x="101" y="-86.3" font-family="Times,serif" font-size="14.00">pkb</text>
</g>
<!-- varnish&#45;&gt;pkb -->
<g id="edge3" class="edge">
<title>varnish&#45;&gt;pkb:n</title>
<path fill="none" stroke="black" d="M385.84,-158.82C311.15,-153.77 140.75,-139.87 106.91,-116.35"/>
<polygon fill="black" stroke="black" points="109.63,-114.14 101,-108 103.92,-118.18 109.63,-114.14"/>
</g>
<!-- wizards -->
<g id="node6" class="node">
<title>wizards</title>
<polygon fill="none" stroke="black" points="218,-108 146,-108 146,-72 218,-72 218,-108"/>
<text text-anchor="middle" x="182" y="-86.3" font-family="Times,serif" font-size="14.00">wizards</text>
</g>
<!-- varnish&#45;&gt;wizards -->
<g id="edge4" class="edge">
<title>varnish&#45;&gt;wizards:n</title>
<path fill="none" stroke="black" d="M385.99,-160.44C325.31,-158.6 205.13,-150.8 184.92,-117.95"/>
<polygon fill="black" stroke="black" points="188.17,-116.61 182,-108 181.46,-118.58 188.17,-116.61"/>
</g>
<!-- rust_melbourne -->
<g id="node7" class="node">
<title>rust_melbourne</title>
<polygon fill="none" stroke="black" points="365.5,-108 236.5,-108 236.5,-72 365.5,-72 365.5,-108"/>
<text text-anchor="middle" x="301" y="-86.3" font-family="Times,serif" font-size="14.00">rust_melbourne</text>
</g>
<!-- varnish&#45;&gt;rust_melbourne -->
<g id="edge5" class="edge">
<title>varnish&#45;&gt;rust_melbourne:n</title>
<path fill="none" stroke="black" d="M385.97,-159.89C354.01,-156.98 310.54,-147.58 302.36,-118.24"/>
<polygon fill="black" stroke="black" points="305.78,-117.45 301,-108 298.85,-118.37 305.78,-117.45"/>
</g>
<!-- pleroma -->
<g id="node8" class="node">
<title>pleroma</title>
<polygon fill="none" stroke="black" points="458.5,-108 383.5,-108 383.5,-72 458.5,-72 458.5,-108"/>
<text text-anchor="middle" x="421" y="-86.3" font-family="Times,serif" font-size="14.00">pleroma</text>
</g>
<!-- varnish&#45;&gt;pleroma -->
<g id="edge6" class="edge">
<title>varnish&#45;&gt;pleroma:n</title>
<path fill="none" stroke="black" d="M421,-143.81C421,-136.28 421,-127.1 421,-118.06"/>
<polygon fill="black" stroke="black" points="424.5,-118 421,-108 417.5,-118 424.5,-118"/>
</g>
<!-- read_rust -->
<g id="node9" class="node">
<title>read_rust</title>
<polygon fill="none" stroke="black" points="561.5,-108 476.5,-108 476.5,-72 561.5,-72 561.5,-108"/>
<text text-anchor="middle" x="519" y="-86.3" font-family="Times,serif" font-size="14.00">read_rust</text>
</g>
<!-- varnish&#45;&gt;read_rust -->
<g id="edge7" class="edge">
<title>varnish&#45;&gt;read_rust:n</title>
<path fill="none" stroke="black" d="M456.1,-156.48C480.82,-151.36 510.68,-140.58 517.54,-118.01"/>
<polygon fill="black" stroke="black" points="521.02,-118.4 519,-108 514.1,-117.39 521.02,-118.4"/>
</g>
<!-- read_rust_leaf -->
<g id="node10" class="node">
<title>read_rust_leaf</title>
<polygon fill="none" stroke="black" points="698,-108 580,-108 580,-72 698,-72 698,-108"/>
<text text-anchor="middle" x="639" y="-86.3" font-family="Times,serif" font-size="14.00">read_rust_leaf</text>
</g>
<!-- varnish&#45;&gt;read_rust_leaf -->
<g id="edge8" class="edge">
<title>varnish&#45;&gt;read_rust_leaf:n</title>
<path fill="none" stroke="black" d="M456.03,-159.68C511.87,-156.7 616.77,-147.23 635.95,-117.79"/>
<polygon fill="black" stroke="black" points="639.37,-118.59 639,-108 632.68,-116.51 639.37,-118.59"/>
</g>
<!-- quotes -->
<g id="node11" class="node">
<title>quotes</title>
<polygon fill="none" stroke="black" points="781.5,-108 716.5,-108 716.5,-72 781.5,-72 781.5,-108"/>
<text text-anchor="middle" x="749" y="-86.3" font-family="Times,serif" font-size="14.00">quotes</text>
</g>
<!-- varnish&#45;&gt;quotes -->
<g id="edge9" class="edge">
<title>varnish&#45;&gt;quotes:n</title>
<path fill="none" stroke="black" d="M456.1,-158.97C532.12,-154.15 707.97,-140.59 742.9,-116.56"/>
<polygon fill="black" stroke="black" points="746.05,-118.17 749,-108 740.35,-114.11 746.05,-118.17"/>
</g>
<!-- dewpoint -->
<g id="node12" class="node">
<title>dewpoint</title>
<polygon fill="none" stroke="black" points="882.5,-108 799.5,-108 799.5,-72 882.5,-72 882.5,-108"/>
<text text-anchor="middle" x="841" y="-86.3" font-family="Times,serif" font-size="14.00">dewpoint</text>
</g>
<!-- varnish&#45;&gt;dewpoint -->
<g id="edge10" class="edge">
<title>varnish&#45;&gt;dewpoint:n</title>
<path fill="none" stroke="black" d="M456.09,-160.24C548.95,-157.8 796.69,-148.37 835.78,-116.77"/>
<polygon fill="black" stroke="black" points="838.89,-118.38 841,-108 832.88,-114.8 838.89,-118.38"/>
</g>
<!-- syncthing -->
<g id="node13" class="node">
<title>syncthing</title>
<polygon fill="none" stroke="black" points="144,-36 58,-36 58,0 144,0 144,-36"/>
<text text-anchor="middle" x="101" y="-14.3" font-family="Times,serif" font-size="14.00">syncthing</text>
</g>
<!-- pkb&#45;&gt;syncthing -->
<g id="edge11" class="edge">
<title>pkb&#45;&gt;syncthing:n</title>
<path fill="none" stroke="black" d="M101,-71.81C101,-64.28 101,-55.1 101,-46.06"/>
<polygon fill="black" stroke="black" points="104.5,-46 101,-36 97.5,-46 104.5,-46"/>
</g>
<!-- postgresql -->
<g id="node14" class="node">
<title>postgresql</title>
<polygon fill="none" stroke="black" points="361,-36 269,-36 269,0 361,0 361,-36"/>
<text text-anchor="middle" x="315" y="-14.3" font-family="Times,serif" font-size="14.00">postgresql</text>
</g>
<!-- wizards&#45;&gt;postgresql -->
<g id="edge13" class="edge">
<title>wizards&#45;&gt;postgresql:n</title>
<path fill="none" stroke="black" d="M218.34,-74.98C248.03,-63.48 287.89,-47.9 305.71,-40.38"/>
<polygon fill="black" stroke="black" points="307.45,-43.43 315,-36 304.46,-37.1 307.45,-43.43"/>
</g>
<!-- rust_melbourne&#45;&gt;postgresql -->
<g id="edge14" class="edge">
<title>rust_melbourne&#45;&gt;postgresql:ne</title>
<path fill="none" stroke="black" d="M340.43,-71.93C354.33,-64.2 366.38,-54.62 365.61,-44.91"/>
<polygon fill="black" stroke="black" points="368.71,-43.27 361,-36 362.49,-46.49 368.71,-43.27"/>
</g>
<!-- pleroma&#45;&gt;postgresql -->
<g id="edge15" class="edge">
<title>pleroma&#45;&gt;postgresql:ne</title>
<path fill="none" stroke="black" d="M400.43,-71.93C390.8,-63.72 379.09,-53.44 368.29,-43.12"/>
<polygon fill="black" stroke="black" points="370.6,-40.48 361,-36 365.71,-45.49 370.6,-40.48"/>
</g>
<!-- read_rust&#45;&gt;postgresql -->
<g id="edge12" class="edge">
<title>read_rust&#45;&gt;postgresql:ne</title>
<path fill="none" stroke="black" d="M476.21,-74.9C440.12,-62.98 391.17,-46.73 370.66,-39.59"/>
<polygon fill="black" stroke="black" points="371.59,-36.2 361,-36 369.16,-42.76 371.59,-36.2"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.9 KiB