The quest to launch an ETCD cluster on Firecracker starts here.
In this post, I’m describing how I’ve built my initial Alpine 3.13 VMM with OpenSSH and a dedicated sudoer
user. In AWS, when one launches a Ubuntu instance, one can access it via ssh ubuntu@<address>
, a CentOS VM is ssh centos@<address>
. At the end of this write up, I’ll have ssh alpine@<address>
. This VMM will have access to the outside world so I can install additional software and even ping the BBC! For the networking, I’ll use the Docker docker0
bridge; inspired again by Julia Evans, the Day 41: Trying to understand what a bridge is[1] was very helpful. I will look at my own networking setup in future write ups.
The result is a refinement of the process from my previous Firecracker articles.
§Dockerfile
The root file system is built from an Alpine 3.13 Docker image.
|
|
Plenty but rather straightforward, let’s break it down:
- update the source packages and install required packages:
openrc
because an init system in requiredopenssh
and other other packages so there is a minimalistic system that can be accessed and used after launch
|
|
- generate host keys:
|
|
- create the home directory structure for the
alpine
user:
|
|
- create the
alpine
group and the user, assign home directory, init shell and a random password; without the password the user account stays disabled and it’s not possible to SSH as that user:
|
|
- make the user a password-less
sudoer
:
|
|
- mount special file systems on boot and enable local services:
|
|
- copy the generated public key to authorized keys, there’s a single key so add directly to
authorized_keys
:
|
|
- finally, apply settings required to access the system via SSH:
- OpenSSH is picky about home and
.ssh
directory permissions so I make sure these are correct:0740
for home,0700
for$HOME/.ssh
and0400
for the keys file - enable OpenSSH and make sure it starts when the system starts
|
|
I have a /firecracker
directory structure which I described in Taking Firecracker for a spin[2]. The Dockerfile
is saved in /firecracker/docker/alpine-3.13/Dockerfile
.
§File system
Now I put together the program to start the container and extract the file system. The program is saved as /firecracker/docker/create-alpine-3.13.sh
and goes like this:
|
|
First, I’m setting up the build context and generating a key pair. ssh-keygen
is smart to check if the key pair already exists and answering no
will prevent it from overwriting on every run.
In the Dockerfile
, I was using a build local key.pub
for the image (step 8). Here’s how I make sure it exists:
|
|
Next, bring the Dockerfile
to the build directory and build the Docker image. Tag the image with a known name. If the docker build
fails, the program will report that fact and exit.
|
|
The next step is to prepare the root file system:
|
|
and start the container:
|
|
followed by copying everything out of the container to the file system file. I do it the same way as with the Vault VMM root file system in my previous articles.
I could combine the first two commands together but I decided to keep them separate to distinguish what belongs to the file system and what’s mine, in this case that’s the /home
directory alone:
|
|
When everything is copied, unmount the file system, stop the container and clean up:
|
|
To run it simply execute /firecracker/docker/create-alpine-3.13.sh
. On my machine, assuming that I already have alpine:3.13
Docker image, the process takes about 20 seconds.
§Networking
The resulting VMM would be useless without access to the outside world. My previous write ups didn’t discuss any of that, none of those VMMs were able reach the internet.
Here, I’m using the method from Julia’s article - use the docker0
bridge. This is really straightforward. I have the following /firecracker/docker/tap-alpine-3.13.sh
program:
|
|
The gateway IP and mask come from the docker0
bridge:
|
|
The IP address of the VMM is an arbitrary selection.
Run it with /firecracker/docker/tap-alpine-3.13.sh
, the outcome will be similar to:
|
|
Time to configure the VMM.
§VMM configuration file
A couple of things to take a note of:
ip=172.17.0.42::172.17.0.1:255.255.255.0::eth0:off
is of the formatip=${VMM_IP}::${GATEWAY_IP}:{DOCKER_MASK_LONG}::${VMM_INTERFACE_ID}:off
network-interfaces[0].host_dev_name
matches the value of$TAP_DEV
|
|
§Run the VMM
To start the VMM, simply execute:
|
|
About two seconds later:
* Mounting misc binary format filesystem ... [ ok ]
* Mounting /sys ... [ ok ]
* Mounting security filesystem ... [ ok ]
* Mounting debug filesystem ... [ ok ]
* Mounting SELinux filesystem ... [ ok ]
* Mounting persistent storage (pstore) filesystem ... [ ok ]
* Starting local ... [ ok ]
Welcome to Alpine Linux 3.13
Kernel 5.8.0 on an x86_64 (ttyS0)
172 login:
§SSH into the VMM
In another terminal:
|
|
The authenticity of host '172.17.0.42 (172.17.0.42)' can't be established.
ECDSA key fingerprint is SHA256:gYxEJdQIXM3242/yV/RV9qVQBaGSdLoUtpFSmBKEyHE.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.17.0.42' (ECDSA) to the list of known hosts.
Enter passphrase for key '/home/radek/.ssh/alpine':
Welcome to Alpine!
The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <http://wiki.alpinelinux.org/>.
You can setup the system with the command: setup-alpine
You may change this message by editing /etc/motd.
|
|
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
v3.13.1-115-gf65775dfbc [https://dl-cdn.alpinelinux.org/alpine/v3.13/main]
v3.13.1-117-g6a5e33f63c [https://dl-cdn.alpinelinux.org/alpine/v3.13/community]
OK: 13880 distinct packages available
|
|
PING 1.1.1.1 (1.1.1.1): 56 data bytes
64 bytes from 1.1.1.1: seq=0 ttl=58 time=18.995 ms
64 bytes from 1.1.1.1: seq=1 ttl=58 time=15.660 ms
64 bytes from 1.1.1.1: seq=2 ttl=58 time=16.246 ms
64 bytes from 1.1.1.1: seq=3 ttl=58 time=17.889 ms
^C
--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 15.660/17.197/18.995 ms
|
|
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 02:fc:00:00:00:05 brd ff:ff:ff:ff:ff:ff
|
|
Nice. Everything is working as expected. The UX is not fully complete, to ping
stuff I do have to sudo
. Whatever, if I can ping
the BBC, I’m good:
|
|
PING bbc.co.uk (151.101.64.81): 56 data bytes
64 bytes from 151.101.64.81: seq=0 ttl=58 time=23.371 ms
64 bytes from 151.101.64.81: seq=1 ttl=58 time=20.238 ms
64 bytes from 151.101.64.81: seq=2 ttl=58 time=24.788 ms
64 bytes from 151.101.64.81: seq=3 ttl=58 time=24.047 ms
^C
--- bbc.co.uk ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 20.238/23.111/24.788 ms
§Next steps
Next time I am going to look at setting up the network with IPAM so the IP addresses are assigned from a given range.
That’s it for today:
|
|
172:/home/alpine# Connection to 172.17.0.42 closed by remote host.
Connection to 172.17.0.42 closed.