Flatcar Container Linux is an open source Linux distribution designed to be immutable and suitable for running containers. It is a friendly fork of CoreOS Container Linux and as such, compatible with it. By being a minimal operating system it reduces the dependencies required and attack surface area in your infrastructure. With its automated atomic updates, you can easily apply all latest security patches and rollback partition in case of issues.
Kinvolk, the company behind Flatcar Container Linux was acquired by Microsoft in 2021. The initial engineers of Flatcar Linux were also critical in early work with CoreOS (the company acquired by Red Hat) on the rkt container runtime. The positive side is Microsoft’s commitment to keeping Flatcar Container Linux project under community development, while intending to bring expertise of the Kinvolk team to Azure and AKS success.
OpenStack gives us a powerful infrastructure-as-a-service (IaaS) where we can create and run Flatcar Container Linux machines. In this tutorial we’ll share a step-by-step process you’ll follow to have Flatcar Container Linux running in OpenStack. We presume that you have a working OpenStack installation with administrative access to it.
In our recently did an article using Flatcar Container Linux on KVM, see link:
Setup pre-prequisites
- Deployed OpenStack Platform with
openstack
command line tool configured - Internet access for downloading Flatcar Container Linux OpenStack image
- Workstation machine where operations are to be done, possibly where openstack CLI is configured.
Step 1: Download Flatcar image for OpenStack
Flatcar Container Linux is distributed in different channels – Stable, Beta, and Alpha Channel. The Stable channel should be used when setting up production clusters.
In your machine with openstack
command line tool configured, download the latest stable release of Flatcar Container Linux OpenStack image.
wget https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_openstack_image.img.bz2
Uncompress the file once it’s downloaded.
bunzip2 flatcar_production_openstack_image.img.bz2
A new file called flatcar_production_openstack_image.img will be created in current directory.
$ file flatcar_production_openstack_image.img
flatcar_production_openstack_image.img: QEMU QCOW Image (v2), 9116319744 bytes
Step 2: Upload Flatcar image to Glance
Once the image has been downloaded, we can begin to add Flatcar Container Linux image into Glance using openstack
command.
openstack image create --shared \
--container-format bare \
--disk-format qcow2 \
--file flatcar_production_openstack_image.img \
Flatcar
Successful upload sample output:
+------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| container_format | bare |
| created_at | 2022-12-11T20:01:31Z |
| disk_format | qcow2 |
| file | /v2/images/f164cb8c-3adf-4c7b-9527-44910dac1e63/file |
| id | f164cb8c-3adf-4c7b-9527-44910dac1e63 |
| min_disk | 0 |
| min_ram | 0 |
| name | Flatcar |
| owner | cbb75bc759d94e4e86a2b218a3187d0c |
| properties | os_hidden='False', owner_specified.openstack.md5='', owner_specified.openstack.object='images/Flatcar', owner_specified.openstack.sha256='' |
| protected | False |
| schema | /v2/schemas/image |
| status | queued |
| tags | |
| updated_at | 2022-12-11T20:01:31Z |
| visibility | shared |
+------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
List available images in Glance storage.
$ openstack image list
+--------------------------------------+-----------------+--------+
| ID | Name | Status |
+--------------------------------------+-----------------+--------+
| 4d40c585-5fc3-4885-b2e6-b5594a1e00a6 | Cirros | active |
| 420929c2-e295-41b6-974a-71ec9be18673 | Fedora-CoreOS | active |
| f164cb8c-3adf-4c7b-9527-44910dac1e63 | Flatcar | active |
+--------------------------------------+-----------------+--------+
Step 3: Create Flatcar Container Linux Configuration
For simplicity we shall create only one instance of Flatcar Container Linux on OpenStack. But the same procedure can me modified to replicate as required. The Flatcar Container Linux machine parameters and systemd units can be configured via Container Linux Configs (CLC). CLC format is YAML. Once the configurations are defined, they are then transpiled into Ignition JSON configs and provided to the instance when booting up. For more reading on supported features check official documentation.
The default tool recommended generating Ignition JSON configs from CLC is the Config Transpiler (ct for short). The Config Transpiler will validate and transform a Container Linux Config into the format that Flatcar Container Linux can consume – Ignition Config.
Let’s create Container Linux Config for machine called flatcar-vm-1-config.yaml
vim flatcar-vm-1-config.yaml
Paste and modify below contents accordingly.
passwd:
users:
- name: core
ssh_authorized_keys:
- ssh-rsa ABCD...
storage:
links:
- path: /etc/localtime
target: ../usr/share/zoneinfo/Africa/Nairobi
files:
- path: /etc/systemd/timesyncd.conf
filesystem: root
mode: 0644
contents:
inline: |
[Time]
NTP=192.168.1.1
- path: /etc/hostname
mode: 0644
filesystem: "root"
contents:
inline: server1.example.com
Where:
- core is the name of the user to be created
- ssh-rsa ABCD… is the valid SSH key that will be used to connect to the instance. You can add multiple keys.
- Africa/Nairobi is the timezone to be set in your instance.
- 192.168.1.1 is the NTP server IP address to be configured in the machine
- server1.example.com is the hostname to be set on the instance one running
You can add other configurations such as systemd units in the file for services to be started. Example systemd unit to start Nginx container using systemd.
systemd:
units:
- name: nginx.service
enabled: true
contents: |
[Unit]
Description=NGINX example
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm --force nginx1
ExecStart=/usr/bin/docker run --name nginx1 --pull always --log-driver=journald --net host docker.io/nginx:1
ExecStop=/usr/bin/docker stop nginx1
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
Step 4: Generate Ignition Config using Config Transpiler
The Config Transpiler is responsible for transforming a Container Linux Config written by a user into an Ignition Config to be consumed by instances of Flatcar Container Linux. The Container Linux Config Transpiler command line interface, ct
for short, can be downloaded from its GitHub Releases page or used via Docker / Podman.
For container method ensure Docker or Podman is installed. ct
requires information about the target environment before it can transform configs which use templating. See example:
### Using Docker ###
cat flatcar-vm-1-config.yaml | docker run --rm -i ghcr.io/flatcar/ct:latest -platform openstack-metadata > flatcar-vm-1-ignition.json
### Using Podman ###
cat flatcar-vm-1-config.yaml | podman run --rm -i ghcr.io/flatcar/ct:latest -platform openstack-metadata > flatcar-vm-1-ignition.json
Sample command execution output:
Trying to pull ghcr.io/flatcar/ct:latest...
Getting image source signatures
Copying blob fa667c24ea49 done
Copying config 1b42533095 done
Writing manifest to image destination
Storing signatures
You can see the contents generated using:
cat flatcar-vm-1-ignition.json
Step 5: Boot Flatcar instance on OpenStack using Ignition Config
With the configuration we can boot the the machine with the openstack
CLI, referencing our JSON file from ct. But first get required information to create an instance.
### OpenStack Network to use ###
$ openstack network list
+--------------------------------------+---------+--------------------------------------+
| ID | Name | Subnets |
+--------------------------------------+---------+--------------------------------------+
| 60ba5cbb-dc2d-439f-93e2-4f0ba92279e5 | public | ef63e577-7201-4924-b203-daccb21a16ec |
| b6452743-3910-47dd-b50d-ec8d985fb807 | private | f268d15c-ec47-4c69-ad55-9dc894ce0bc7 |
+--------------------------------------+---------+--------------------------------------+
### OpenStack flavor to use ###
$ openstack flavor list
+----+-----------+------+------+-----------+-------+-----------+
| ID | Name | RAM | Disk | Ephemeral | VCPUs | Is Public |
+----+-----------+------+------+-----------+-------+-----------+
| 0 | m1.tiny | 512 | 5 | 0 | 1 | True |
| 1 | m1.small | 1024 | 10 | 0 | 1 | True |
| 2 | m1.medium | 2048 | 20 | 0 | 2 | True |
| 3 | m1.large | 4096 | 30 | 0 | 2 | True |
| 4 | m1.xlarge | 8192 | 40 | 0 | 4 | True |
+----+-----------+------+------+-----------+-------+-----------+
### OpenStack security group to use ###
$ openstack security group list
+--------------------------------------+------------+------------------------+----------------------------------+------+
| ID | Name | Description | Project | Tags |
+--------------------------------------+------------+------------------------+----------------------------------+------+
| 60c38659-0594-45ed-b662-be22ccc2e63a | permit_all | Allow all ports | cbb75bc759d94e4e86a2b218a3187d0c | [] |
| d2d7ffe3-5bd5-4703-8b02-b9c59df254ed | default | Default security group | cbb75bc759d94e4e86a2b218a3187d0c | [] |
| fdae38aa-916a-444c-8b0c-769625717730 | default | Default security group | 8fa3ebba82c44b21878c7740fdc9f4a5 | [] |
+--------------------------------------+------------+------------------------+----------------------------------+------+
### OpenStack keypair to use ###
$ openstack keypair list
+---------+-------------------------------------------------+------+
| Name | Fingerprint | Type |
+---------+-------------------------------------------------+------+
| admin | 19:7b:5c:14:a2:21:7a:a3:dd:56:c6:e4:3a:22:e8:3f | ssh |
+---------+-------------------------------------------------+------+
We’re going to provide our Container Linux Config to OpenStack via the user-data
flag. See example:
openstack server create --image Flatcar \
--flavor m1.k8s \
--network public \
--security-group permit_all \
--key-name admin \
--user-data ./ flatcar-vm-1-ignition.json \
server01
Replace provided parameters with correct values in your environment. An instance boot process begins immediately.
+-------------------------------------+------------------------------------------------+
| Field | Value |
+-------------------------------------+------------------------------------------------+
| OS-DCF:diskConfig | MANUAL |
| OS-EXT-AZ:availability_zone | |
| OS-EXT-SRV-ATTR:host | None |
| OS-EXT-SRV-ATTR:hypervisor_hostname | None |
| OS-EXT-SRV-ATTR:instance_name | |
| OS-EXT-STS:power_state | NOSTATE |
| OS-EXT-STS:task_state | scheduling |
| OS-EXT-STS:vm_state | building |
| OS-SRV-USG:launched_at | None |
| OS-SRV-USG:terminated_at | None |
| accessIPv4 | |
| accessIPv6 | |
| addresses | |
| adminPass | c9aZW7Jn7WxY |
| config_drive | |
| created | 2022-12-11T22:19:11Z |
| flavor | m1.k8s (5) |
| hostId | |
| id | 43770e4c-d1fa-406e-9e5a-300d10e10809 |
| image | Flatcar (f164cb8c-3adf-4c7b-9527-44910dac1e63) |
| key_name | jkmutai |
| name | server01 |
| progress | 0 |
| project_id | cbb75bc759d94e4e86a2b218a3187d0c |
| properties | |
| security_groups | name='60c38659-0594-45ed-b662-be22ccc2e63a' |
| status | BUILD |
| updated | 2022-12-11T22:19:11Z |
| user_id | ff9beb7f5dca4a0887fda897da86f12d |
| volumes_attached | |
+-------------------------------------+------------------------------------------------+
Listing running server instances on OpenStack:
$ openstack server list
+--------------------------------------+----------+--------+---------------------+--------------------------+-----------+
| ID | Name | Status | Networks | Image | Flavor |
+--------------------------------------+----------+--------+---------------------+--------------------------+-----------+
| 43770e4c-d1fa-406e-9e5a-300d10e10809 | server01 | ACTIVE | public=192.168.1.12 | Flatcar | m1.k8s |
| ec90bfd2-e163-47a1-9826-46c4130eae6b | test | ACTIVE | public=192.168.1.84 | N/A (booted from volume) | m1.medium |
+--------------------------------------+----------+--------+---------------------+--------------------------+-----------+
Test it reachability
$ ping -c 2 192.168.1.12
PING 192.168.1.12 (192.168.1.12): 56 data bytes
64 bytes from 192.168.1.12: icmp_seq=0 ttl=63 time=58.931 ms
64 bytes from 192.168.1.12: icmp_seq=1 ttl=63 time=42.578 ms
--- 192.168.1.12 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 42.578/50.755/58.931/8.177 ms
Once it’s running you should be able to login with core
user:
$ ssh [email protected]
Warning: Permanently added '192.168.1.12' (ED25519) to the list of known hosts.
Last login: Mon Dec 12 01:21:46 EAT 2022 from 10.10.10.3 on pts/0
Flatcar Container Linux by Kinvolk stable 3374.2.1 for Openstack
Confirm if timezone was configured as set in config
core@server01 ~ $ timedatectl
Local time: Mon 2022-12-12 01:25:13 EAT
Universal time: Sun 2022-12-11 22:25:13 UTC
RTC time: Sun 2022-12-11 22:25:13
Time zone: Africa/Nairobi (EAT, +0300)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Other examples:
Booting an instance using Cinder Volume
openstack server create --image Flatcar \
--flavor m1.k8s \
--security-group permit_all \
--key-name jkmutai \
--user-data ./node04-ignition.json \
--boot-from-volume 40 \
k8snode04
Booting an instance using Static IP address
openstack server create --image Flatcar \
--flavor m1.k8s \
--security-group permit_all \
--key-name jkmutai \
--user-data ./node04-ignition.json \
--nic net-id=60ba5cbb-dc2d-439f-93e2-4f0ba92279e5,v4-fixed-ip=192.168.1.16 \
k8snode04
Conclusion
Flatcar Container Linux’s built-in security features, minimal design and automated updates provide a strong foundation for your infrastructure’s security strategy. By utilizing OpenStack’s powerful orchestration system it becomes easy to deploy your instances that can power other projects such as Kubernetes cluster deployment.