Logo

Noion Labs

Alpine Linux

June 2019

Alpine Linux is an awesome Linux distro. Minimal, simple, secure.

I have often longed for a simpler, more secure, more understandable, more correct operating system. That search led me to the various BSD variants, to illumos, SmartOS and while they had awesome features: Dtrace, ZFS, containers that are more secure than VMs etc using them didn’t make my life simpler.

Most of my clients, and therefore most of my work, was (and is) on Linux, so I had to know and stay up to date on multiple OSes. I had to resolve odd (and often harry) bugs that only showed up in non-Linux environments. Then too Linux has been gaining ground on those awesome features: Linux now has eBPF which is a reasonable replacement for DTrace, the ZFS on Linux project is now the de facto “Master” for ZFS development, and Docker is the container run time that is eating the world and to be fair, Docker’s APIs are a lot nicer than SmartOS’s where, and for running trusted code alongside trusted code we don’t need the additional isolation.

I’ve also found that a worse is better approach in regards file systems is better. ZFS really is an awesome file system, perhaps the last word on file systems, and maybe in another 10 years we’ll all be running it. But until then ext4 and traditional snapshoting and backup tools, along with some Docker volume management is just a more practical solution for the small to medium (ie 10-100 million/year in revenue) companies I like to work with.

It’s been sad to see Joyent’s public cloud’s end of life and I’ll miss ZFS, and in particular Manta but truth be told I’d already mostly stopped using them earlier this year.

So what have I turned to as a simple, better solution than a BSD Unix, or SmartOS, or Debian/Ubuntu? Alpine.

As a server solution Alpine let’s you keep the — the truly awesome SmartOS practice — of complete separation of OS and application. If you haven’t used SmartOS it might come as a surprise that you can’t modify the OS. You don’t have to worry about someone installing malware or messing with the OS configuration, it is immutable. You also don’t have to provision a partition on disk, it will just boot from a USB thumb drive, load itself into RAM, and then you have the full machine to run your application on.

On the desktop Alpine has all of Linux’s advantages — namely drivers for laptop and desktop grade hardware and a plethora of window managers / desktop environments.

As a container OS, the docker community has already embraced it for it’s small size, fast package manager etc. So many official images have a Alpine option.

Lastly while the hardened kernel that used to ship as the default for versions prior to 3.8 didn’t play nicely with Docker as a host, Alpine 3.8 and onward work excellently out-of-the-box as docker hosts.

The only thing that is left is how to run it everywhere. Many cloud providers don’t (yet?) offer it as an official image. But where you can bring your own image (ie AWS, Google, Azure) the Alpine community has some packer scripts to get you started. In places where that won’t work, you can do the more esoteric (& fun) option of installing Alpine from inside another OS. And no that doesn’t require a dual boot, after rebooting you will have a clean Alpine install.

Here is a script that I use to install Alpine on a bare metal host at OVH:

#!/bin/sh

set -ex

PATH=/bin:/sbin:/usr/bin:/usr/sbin
HOST=alpine
USER=pioneer
ROOT_FS=ext4
BOOT_FS=ext4
FEATURES="ata base ide scsi usb virtio $ROOT_FS"
MODULES="sd-mod,usb-storage,$ROOT_FS"
REL=3.8 # from https://alpinelinux.org don't use third dot
MIRROR=http://dl-cdn.alpinelinux.org/alpine
REPO=$MIRROR/v$REL/main
# from https://pkgs.alpinelinux.org/packages?name=apk-tools-static&branch=v3.8&arch=x86_64
APKV=2.10.1-r0
DEV=/dev/sda
BOOT_DEV=${DEV}1
ROOT_DEV=${DEV}2
ROOT=/mnt
BOOT=/mnt/boot
ARCH=$(uname -m)

sgdisk --zap-all $DEV
sgdisk --new 1:0:+512M $DEV
# the type is from `sgdisk --list-types` 8300 is Linux filesystem
sgdisk --typecode 1:8300 $DEV
sgdisk --change-name 1:boot $DEV
sgdisk --new 2:0:0 $DEV # zero means max here
sgdisk --typecode 2:8300 $DEV
sgdisk --change-name 2:root $DEV
sgdisk --attributes 1:set:2 $DEV

# -m reserved-blocks-percentage
# -q Quiet execution.
# -L new-volume-label
mkfs.$BOOT_FS -m 0 -q -L boot $BOOT_DEV
mkfs.$ROOT_FS -q -L root $ROOT_DEV
mount $ROOT_DEV $ROOT
mkdir $BOOT
mount $BOOT_DEV $BOOT

curl -s $MIRROR/v$REL/main/$ARCH/apk-tools-static-${APKV}.apk | tar xz

./sbin/apk.static \
    --repository $REPO \
    --update-cache \
    --allow-untrusted \
    --root $ROOT \
    --initdb add alpine-base syslinux dhcpcd linux-vanilla

cat << EOF > $ROOT/etc/fstab
$ROOT_DEV / $ROOT_FS defaults,noatime 0 0
$BOOT_DEV /boot $BOOT_FS defaults 0 2
EOF

echo $REPO > $ROOT/etc/apk/repositories

cat /etc/resolv.conf > $ROOT/etc/resolv.conf

cat << EOF > $ROOT/etc/update-extlinux.conf
overwrite=1
vesa_menu=0
default_kernel_opts="quiet"
modules=$MODULES
root=$ROOT_DEV
verbose=0
hidden=1
timeout=1
serial_port=
serial_baud=115200
password=''
EOF

cat << EOF > $ROOT/etc/network/interfaces
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
  hostname $HOST
EOF

mount --bind /proc $ROOT/proc
mount --bind /dev $ROOT/dev
mount --bind /sys $ROOT/sys

chroot $ROOT /bin/sh -x << CHROOT
apk update
apk add openssh

setup-hostname -n $HOST

rc-update -q add devfs sysinit
rc-update -q add dmesg sysinit
rc-update -q add mdev sysinit
rc-update -q add hwdrivers sysinit

rc-update -q add hwclock boot
rc-update -q add modules boot
rc-update -q add sysctl boot
rc-update -q add hostname boot
rc-update -q add bootmisc boot
rc-update -q add syslog boot
rc-update -q add networking boot
rc-update -q add urandom boot
rc-update -q add dhcpcd boot

rc-update -q add mount-ro shutdown
rc-update -q add killprocs shutdown
rc-update -q add savecache shutdown

rc-update -q add acpid default
rc-update -q add crond default
rc-update -q add sshd default

echo features=\""$FEATURES"\" > /etc/mkinitfs/mkinitfs.conf

extlinux -i /boot
dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/gptmbr.bin of=$DEV
update-extlinux
CHROOT

chroot $ROOT passwd
chroot $ROOT adduser -s /bin/ash -D $USER
chroot $ROOT passwd $USER

umount $ROOT/proc
umount $ROOT/dev
umount $ROOT/sys
umount $BOOT
umount $ROOT