Skip to content

Podman for RISC-V

Abstract

This guide will show you the steps of building the requirements for podman, the daemonless container engine for developing, managing, and running OCI Containers on RISC-V.

Introduction

In the guide Linux on RISC-V using QEMU and BUSYBOX from scratch, we have been able to create a minimal, bootable, Linux. However, it lacks a lot a feature because of it is inner simplicity. In order to run Kubernetes on RISC-V, we will use the Fedora Core OS Linux distribution, a minimal operating system for running containerized workloads securely and at scale.

The Fedora Core OS can be build using COSA (CoreOS Assembler). It is a collection of various tools used to build Fedora CoreOS style systems, including RHEL CoreOS. The goal is that everything needed to build and test the OS comes encapsulated in one (admittedly large) container.

COSA relies on other packages : ostree git rclone podman. Through this guide, we will build those dependencies for RISC-V on Fedora 33 over Qemu.

Fedora 33 QEMU Setup

First, we will prepare a Fedora 33 virtual machine that will run over QEMU on the RISCV64 architecture. This virtual machine will be our host for building Fedora Core OS and its dependencies.

Cross-compile golang 1.16.x from source targeting RISC-V from X86

From your X86 host, install golang (revision > 1.14) and build the last revision for RISC-V

dnf install golang

# Switch to root
sudo su
cd /root

# Override go path
export GOPATH=~/go
export PATH=$GOPATH/bin:$PATH

git clone https://go.googlesource.com/go

cd go
git checkout tags/go1.16.2
# Note: switching to 'tags/go1.16.2'.

cd src/
export GOOS=linux
export GOARCH=riscv64
bash -x ./make.bash -v

More information about building golang from source here : * Install GO from source * GO 1.16 RISC-V

Make a simple package

From the go directory, make an archive of the compilation artifacts :

cd ..
tar -czvf golang-16_2-bin-riscv64.tar.gz ./bin/linux_riscv64 ./pkg/tool/linux_riscv64 ./pkg/linux_riscv64 ./pkg/include ./src

Install Requirements

WARNING : the QEMU VM requires 100GB of free space on your filesystem.

Install virt-builder by issuing :

dnf install libguestfs-tools-c

Append to virt-builder known repositories this new entry for RISC-V images :

mkdir -p ~/.config/virt-builder/repos.d/
cat <<EOF >~/.config/virt-builder/repos.d/fedora-riscv.conf
[fedora-riscv]
uri=https://dl.fedoraproject.org/pub/alt/risc-v/repo/virt-builder-images/images/index
EOF

You can list available templates with :

virt-builder --list | grep riscv64

fedora-rawhide-developer-20190703n0 riscv64 Fedora® Rawhide Developer 20190703.n.0 fedora-rawhide-developer-20191030n0 riscv64 Fedora® Rawhide Developer 20191030.n.0 fedora-rawhide-developer-20191123.n.0 riscv64 Fedora® Rawhide Developer 20191123.n.0 fedora-rawhide-minimal-20191123.n.1 riscv64 Fedora® Rawhide Minimal 20191123.n.1 fedora-rawhide-developer-20200108.n.0 riscv64 Fedora® Rawhide Developer 20200108.n.0 fedora-rawhide-minimal-20200108.n.0 riscv64 Fedora® Rawhide Minimal 20200108.n.0

virt-builder --arch riscv64 --notes fedora-rawhide-developer-20200108.n.0 | grep fw_payload

https://dl.fedoraproject.org/pub/alt/risc-v/repo/virt-builder-images/images/Fedora-Developer-Rawhide-20200108.n.0-fw_payload-uboot-qemu-virt-smode.elf https://dl.fedoraproject.org/pub/alt/risc-v/repo/virt-builder-images/images/Fedora-Developer-Rawhide-20200108.n.0-fw_payload-uboot-qemu-virt-smode.elf.CHECKSUM

We will now fetch the last available pre-built image, with the boot payload :

wget http://fedora.riscv.rocks/kojifiles/work/tasks/959/910959/Fedora-Developer-Rawhide-20210113.n.0-sda.raw.zst
wget https://dl.fedoraproject.org/pub/alt/risc-v/repo/virt-builder-images/images/Fedora-Developer-Rawhide-20200108.n.0-fw_payload-uboot-qemu-virt-smode.elf

This image comes from an appliance image build produced by KOJI, the Fedora build system. You can see other artifacts here : f33, Fedora-Developer-Rawhide-20210113.n.0, fedora-riscv64-developer-rawhide.ks, riscv64

Install ZSTD archive manager :

dnf install zstd

Unpack the appliance disk image and rename it :

unzstd Fedora-Developer-Rawhide-20210113.n.0-sda.raw.zst
mv Fedora-Developer-Rawhide-20210113.n.0-sda.raw Fedora.raw

Extend the storage available on the raw disk :

qemu-img resize -f raw Fedora.raw +80G

Boot

Boot the virtual machine with :

qemu-system-riscv64 \
   -nographic \
   -machine virt \
   -smp 4 \
   -m 8G \
   -bios none \
   -kernel Fedora-Developer-Rawhide-20200108.n.0-fw_payload-uboot-qemu-virt-smode.elf \
   -object rng-random,filename=/dev/urandom,id=rng0 \
   -device virtio-rng-device,rng=rng0 \
   -device virtio-blk-device,drive=hd0 \
   -drive file=Fedora.raw,format=raw,id=hd0 \
   -device virtio-net-device,netdev=usernet \
   -netdev user,id=usernet,hostfwd=tcp::10000-:22

Increase partition size

sudo fdisk /dev/vda
Command (m for help): p
Disk /dev/vda: 88.98 GiB, 95537856512 bytes, 186597376 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x5e96b6d1

Device     Boot   Start      End  Sectors  Size Id Type
/dev/vda1  *       8192  1007615   999424  488M 83 Linux
/dev/vda2       1007616 17960959 16953344  8.1G 83 Linux

delete partition...

Command (m for help): d

... number 2

Partition number (1,2, default 2): 2

Partition 2 has been deleted.

new partition...

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)

... of type primary ...

Select (default p): p

... number 2...

Partition number (2-4, default 2): 2

... First sector at 1007616...

First sector (2048-186597375, default 2048): 1007616

... Last sector at 186597375 ...

Last sector, +/-sectors or +/-size{K,M,G,T,P} (1007616-186597375, default 186597375): 186597375

Created a new partition 2 of type 'Linux' and of size 88.5 GiB.
Partition #2 contains a ext4 signature.

Keep signature with N

Do you want to remove the signature? [Y]es/[N]o: N

Verify that the /dev/vda2 has been extended

Command (m for help): p

Disk /dev/vda: 88.98 GiB, 95537856512 bytes, 186597376 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x5e96b6d1

Device     Boot   Start       End   Sectors  Size Id Type
/dev/vda1  *       8192   1007615    999424  488M 83 Linux
/dev/vda2       1007616 186597375 185589760 88.5G 83 Linux

Write the changes to the partition table and reboot

Command (m for help): w
The partition table has been altered.
Syncing disks.

Restart the instance

reboot

Verify that the partition is now larger

[root@fedora-riscv ~]# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda    252:0    0   89G  0 disk 
├─vda1 252:1    0  488M  0 part /boot
└─vda2 252:2    0 88.5G  0 part /

Resize the filesystem :

resize2fs /dev/vda2

resize2fs 1.45.6 (20-Mar-2020) Filesystem at /dev/vda2 is mounted on /; on-line resizing required old_desc_blocks = 2, new_desc_blocks = 12 The filesystem on /dev/vda2 is now 23198720 (4k) blocks long.

You should now be able to enjoy the extra space :

[root@fedora-riscv ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           1.6G  8.5M  1.6G   1% /run
/dev/vda2        88G  5.9G   82G   7% /

Install dependencies (golang ostree rclone podman)

Update the system

dnf update && dnf upgrade --best

golang 1.16.x

From the X86 host, from the folder containing the archive golang-16_2-bin-riscv64.tar.gz previously created :

python3 -m http.server

Download to your QEMU riscv64 powered host the file with wget, and unpack it to out directory :

sudo su
cd /root

wget http://X86_HOST:8000/golang-16_2-bin-riscv64.tar.gz -O golang-16_2-bin-riscv64.tar.gz
mkdir out
tar -zxvf golang-16_2-bin-riscv64.tar.gz -C out

cd out
mv ./bin/linux_riscv64/* ./bin/

Add go sources next to the compiled binaries

# Fetch golang sources
git clone https://go.googlesource.com/go
cd go
git checkout tags/go1.16.2

# Append compiled artifacts to sources
cd ..
cp -rn out/* go/

# Custom binary location
export PATH=~/go/bin:$PATH

Prepare golang workspace

mkdir -p ~/golang
export GOPATH=~/golang # WORKSPACE

Verify that it works !

[root@fedora-riscv ~]# go version
go version go1.16.2 linux/riscv64

ostree

dnf install ostree

rclone

Fetch rclone with the last stable version :

git clone https://github.com/rclone/rclone.git
cd rclone
git checkout tags/v1.54.0

Patch dependency error RISC-V support

go mod download github.com/prometheus/procfs
go mod edit -replace=github.com/prometheus/procfs=github.com/prometheus/procfs@910e685

Tidy deps, build, check version and copy artifact to /usr/bin

go mod tidy
go build
./rclone version
cp rclone /usr/bin/

podman

Build and Run Dependencies

podman Dependencies

dnf install -y \
  btrfs-progs-devel \
  conmon \
  device-mapper-devel \
  git \
  glib2-devel \
  glibc-devel \
  glibc-static \
  golang-github-cpuguy83-md2man \
  gpgme-devel \
  iptables \
  libassuan-devel \
  libgpg-error-devel \
  libseccomp-devel \
  libselinux-devel \
  make \
  pkgconfig
runc
git clone https://github.com/opencontainers/runc.git
cd runc
git checkout tags/v1.0.0-rc93
make vendor
make BUILDTAGS="seccomp"

Verify that it works

[root@fedora-riscv runc]# ./runc --version
runc version 1.0.0-rc93
commit: 12644e614e25b05da6fd08a38ffa0cfe1903fdec
spec: 1.0.2-dev
go: go1.16.2
libseccomp: 2.4.1

Install the binary

make install
container networking

https://github.com/containernetworking/plugins/

git clone https://github.com/containernetworking/plugins.git
cd plugins
GOOS=linux GOARCH=riscv64 ./build_linux.sh

export CNI_PATH=$PWD/bin

https://github.com/containernetworking/cni#running-the-plugins

mkdir -p /etc/cni/net.d

cat >/etc/cni/net.d/10-mynet.conf <<EOF
{
    "cniVersion": "0.2.0",
    "name": "mynet",
    "type": "bridge",
    "bridge": "cni0",
    "isGateway": true,
    "ipMasq": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.22.0.0/16",
        "routes": [
            { "dst": "0.0.0.0/0" }
        ]
    }
}
EOF

cat >/etc/cni/net.d/99-loopback.conf <<EOF
{
    "cniVersion": "0.2.0",
    "name": "lo",
    "type": "loopback"
}
EOF

https://github.com/containernetworking/cni

git clone https://github.com/containernetworking/cni.git
cd cni
cd scripts
./priv-net-run.sh ifconfig

Verify that you have a similar output :

root@fedora-riscv scripts]# ./priv-net-run.sh ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.22.0.2  netmask 255.255.0.0  broadcast 10.22.255.255
        inet6 fe80::d02e:f7ff:fec2:3ce9  prefixlen 64  scopeid 0x20<link>
        ether d2:2e:f7:c2:3c:e9  txqueuelen 0  (Ethernet)
        RX packets 11  bytes 1026 (1.0 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7  bytes 558 (558.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Configure CNI networking for podman

mkdir -p /etc/containers
curl -L -o /etc/containers/registries.conf https://raw.githubusercontent.com/projectatomic/registries/master/registries.fedora
curl -L -o /etc/containers/policy.json https://raw.githubusercontent.com/containers/skopeo/master/default-policy.json

mkdir -p /opt/cni/bin
mv plugins/bin/* /opt/cni/bin/
containers-common
git clone https://github.com/containers/common.git
cd common
make vendor
GOOS=linux GOARCH=riscv64 go build -tags "seccomp" ./...  # make BUILDTAGS="seccomp"
make install
Podman
git clone https://github.com/containers/podman.git
cd podman
git checkout tags/v3.0.1
make BUILDTAGS="selinux seccomp systemd"
make install

Verify that podman is correctly set-up

podman --version

podman version 3.0.1

Build and run a simple image

https://mschirbel.medium.com/introduction-to-podman-from-scratch-to-hello-world-b26b126d18d4

mkdir helloworld

Create a simple C application :

nano hello.c

#include <stdio.h>

int main()
{
     printf("\nHello from Docker!\nThis message shows that your installation appears to be working correctly.\n\n");
     return 0;
}

Compile :

gcc -static hello.c -o hello

Make a Dockerfile :

FROM scratch
COPY hello /
ENTRYPOINT ["/hello"]

Build and run the docker image with podman :

podman build . -t helloworld
podman run helloworld

Build and run a simple webapp

podman run -d -p 8080:8080 carlosedp/echo_on_riscv

✔ docker.io/carlosedp/echo_on_riscv:latest Trying to pull docker.io/carlosedp/echo_on_riscv:latest... Getting image source signatures …

Writing manifest to image destination Storing signatures 3091688cc4a5dbf3e7a5ce07d93ce519bb306a7136eaedd5978b5445a12c6dd4

podman ps
CONTAINER ID  IMAGE                                     COMMAND  CREATED         STATUS             PORTS                   NAMES
3091688cc4a5  docker.io/carlosedp/echo_on_riscv:latest           32 seconds ago  Up 17 seconds ago  0.0.0.0:8080->8080/tcp  intelligent_fermat

Query the HTTP server :

[root@fedora-riscv helloworld]# curl http://localhost:8080
Hello, World! I'm running on linux/riscv64 inside a container!