Mounts and Chroot

The simplest container is the chroot container. Combined with adjusting the mounts, quite a bit can be done with it. This is used as part of most container ecosystems. We will start with doing it with root for simplicity. In the next lesson, you will learn about namespaces and how to do it rootless.

Warning

Be very careful when doing operations with root, whether directory or via sudo, so as to not break you system. A careless bind mount can cause serious problems.

Objectives

  • Basic understanding of the Unix/Linux Virtual Filesystem (VFS)

  • Learn how to do bind and tmpfs mounts

  • Learn how to do simple rootful containers with chroot and pivot_root

Instructor note

  • XX min teaching

  • XX min exercises

Unix/Linux Virtual Filesystem

Unix-like OSes including Linux use a hierarchical virtual filesystem to which other filesystems, directories, files, etc. are mounted to to make a single tree. Unlike DOS/Windows, this is a single unified virtual filesystem tree. There are no “drives” like C:\. Each location in the tree translates to a location on some device, interface, pseudo-filesystem, etc. What can be mounted:

  • Disk partitions

  • Remote filesystems

  • Virtual devices like kernel interfaces (e.g. proc)

  • RAM disks/filesystems (e.g. tmpfs and devtmpfs)

  • Bind Mounts

    • Mount file/directory in VFS tree to another location in the VFS tree

    • Can even change mount options (e.g. mount it read-only)

A common Linux layout is

  • / : Root of the VFS (usually a disk or a tmpfs)

  • /boot : Mount location of boot partition (sometimes)

  • /dev : devtmpfs filesystem for device files

  • /dev/shm : tmpfs for shared memory files

  • /etc : Directory for system-wide configuration

  • /home : User home directories (sometimes another partition/filesystem)

  • /media/USER/MOUNTID : Common mount location of removable media

  • /mnt/MOUNT : Common place to mount extra disks, remote filesystems, etc.

  • /proc : proc filesystem from kernel for info

  • /usr : Location for most software, configuration, data, etc. (sometimes another partition/filesystem)

  • /sys : sysfs filesystem from kernel for info and control

Seeing The Current Mounts

There are several ways to see the current mounts in the VFS with different things that are easy to see and others that are hard.

Authoritative List: /proc/PID/mountinfo

Each Linux process potentially sees a different set of mounts in its VFS (will go into more next lesson). All mount information can be gotten from the file /proc/PID/mountinfo where PID is the process ID of the of the process. In a POSIX shell, the special shell variable $$ contains the PID. Additionally, for every process, the path /proc/self/* is the proc directory for that process.

cat /proc/self/mountinfo

What you will get will depend on the linux system you are on and any mounts you have setup. An example could be

[root@656ee6c0d367 /]# cat /proc/self/mountinfo
1253 1259 0:68 /containers/overlay-containers/656ee6c0d3679de6b3900028afe42ad4f34eebc7598003242f38cda848b8bd4e/userdata/resolv.conf /etc/resolv.conf rw,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1254 1259 0:68 /containers/overlay-containers/656ee6c0d3679de6b3900028afe42ad4f34eebc7598003242f38cda848b8bd4e/userdata/hosts /etc/hosts rw,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1255 1261 0:104 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,context="system_u:object_r:container_file_t:s0:c287,c963",size=64000k,uid=1000,gid=1000,inode64
1256 1259 0:68 /containers/overlay-containers/656ee6c0d3679de6b3900028afe42ad4f34eebc7598003242f38cda848b8bd4e/userdata/.containerenv /run/.containerenv rw,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1257 1259 0:68 /containers/overlay-containers/656ee6c0d3679de6b3900028afe42ad4f34eebc7598003242f38cda848b8bd4e/userdata/run/secrets /run/secrets rw,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1258 1259 0:68 /containers/overlay-containers/656ee6c0d3679de6b3900028afe42ad4f34eebc7598003242f38cda848b8bd4e/userdata/hostname /etc/hostname rw,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1259 1212 0:108 / / rw,relatime - overlay overlay rw,context="system_u:object_r:container_file_t:s0:c287,c963",lowerdir=/home/myusername/.local/share/containers/storage/overlay/l/KD7XJA4NIIYJNLFFUYOLD2LB2T,upperdir=/home/myusername/.local/share/containers/storage/overlay/b1e31318e18758b0ce7e4c3bbfbffd4d41cce105cfd29f3007469c4c011eaf28/diff,workdir=/home/myusername/.local/share/containers/storage/overlay/b1e31318e18758b0ce7e4c3bbfbffd4d41cce105cfd29f3007469c4c011eaf28/work,redirect_dir=nofollow,uuid=on,volatile,userxattr
1260 1259 0:133 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
1261 1259 0:134 / /dev rw,nosuid - tmpfs tmpfs rw,context="system_u:object_r:container_file_t:s0:c287,c963",size=65536k,mode=755,uid=1000,gid=1000,inode64
1262 1259 0:135 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs rw,seclabel
1263 1261 0:136 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,context="system_u:object_r:container_file_t:s0:c287,c963",gid=100004,mode=620,ptmxmode=666
1264 1261 0:114 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw,seclabel
1265 1262 0:29 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup2 rw,seclabel,nsdelegate,memory_recursiveprot
1266 1261 0:6 /null /dev/null rw,nosuid,noexec - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1267 1261 0:6 /zero /dev/zero rw,nosuid,noexec - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1268 1261 0:6 /full /dev/full rw,nosuid,noexec - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1269 1261 0:6 /tty /dev/tty rw,nosuid,noexec - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1270 1261 0:6 /random /dev/random rw,nosuid,noexec - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1271 1261 0:6 /urandom /dev/urandom rw,nosuid,noexec - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1272 1260 0:68 /crun/.empty-directory /proc/acpi ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1273 1260 0:6 /null /proc/kcore ro,nosuid - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1274 1260 0:6 /null /proc/keys ro,nosuid - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1275 1260 0:6 /null /proc/latency_stats ro,nosuid - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1276 1260 0:68 /crun/.empty-directory /proc/scsi ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1277 1260 0:6 /null /proc/timer_list ro,nosuid - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1325 1262 0:68 /crun/.empty-directory /sys/devices/virtual/powercap ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1425 1262 0:68 /crun/.empty-directory /sys/firmware ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1507 1262 0:68 /crun/.empty-directory /sys/fs/selinux ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1508 1260 0:6 /null /proc/interrupts ro,nosuid - devtmpfs devtmpfs rw,seclabel,size=7783956k,nr_inodes=1945989,mode=755,inode64
1509 1262 0:68 /crun/.empty-directory /sys/devices/system/cpu/cpu0/thermal_throttle ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1510 1262 0:68 /crun/.empty-directory /sys/devices/system/cpu/cpu1/thermal_throttle ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1511 1262 0:68 /crun/.empty-directory /sys/devices/system/cpu/cpu10/thermal_throttle ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1512 1262 0:68 /crun/.empty-directory /sys/devices/system/cpu/cpu11/thermal_throttle ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1513 1262 0:68 /crun/.empty-directory /sys/devices/system/cpu/cpu6/thermal_throttle ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1514 1262 0:68 /crun/.empty-directory /sys/devices/system/cpu/cpu7/thermal_throttle ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1515 1262 0:68 /crun/.empty-directory /sys/devices/system/cpu/cpu8/thermal_throttle ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1516 1262 0:68 /crun/.empty-directory /sys/devices/system/cpu/cpu9/thermal_throttle ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1517 1325 0:68 /crun/.empty-directory /sys/devices/virtual/powercap ro,nosuid,nodev,relatime - tmpfs tmpfs rw,seclabel,size=1578076k,nr_inodes=394519,mode=700,uid=1000,gid=1000,inode64
1518 1260 0:133 /asound /proc/asound ro,nosuid,nodev,noexec,relatime - proc proc rw
1519 1260 0:133 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw
1520 1260 0:133 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw
1521 1260 0:133 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw
1522 1260 0:133 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw
1523 1260 0:133 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw

Each line is a separate mount with the full mount information (unlike what some other methods will give you). Each column is well defined and can be looked up in the manual page proc_pid_mountinfo

Exercise

Look for the line in your /proc/PID/mountinfo for /dev/shm. Then read man proc_pid_mountinfo to try to understand the columns.

To which mount is it mounted on top of, what filesystem is mounted, and what are its mount options?

More Readable List: findmnt

While /proc/PID/mountinfo has all the information, it is a bit unwieldy for a person to use and reason about. It is more suited for consumption by programs than for a person. Luckily, there is a convenient tool, findmnt, that renders most of the information in a fashion that is easier to understand.

Type-Along

To see the information in a more human readable format, run

findmnt

What you will get will depend on the linux system you are on and any mounts you have setup. An example could be

[foo@bar ~]$ findmnt
TARGET                         SOURCE      FSTYPE     OPTIONS
/                              /dev/vda1   ext4       rw,relatime,seclabel
├─/proc                        proc        proc       rw,nosuid,nodev,noexec,relatime
│ └─/proc/sys/fs/binfmt_misc   systemd-1   autofs     rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5
│   └─/proc/sys/fs/binfmt_misc binfmt_misc binfmt_mis rw,nosuid,nodev,noexec,relatime
├─/sys                         sysfs       sysfs      rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/kernel/security       securityfs  securityfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup             cgroup2     cgroup2    rw,nosuid,nodev,noexec,relatime,seclabel,nsdelegate,memo
│ ├─/sys/fs/pstore             pstore      pstore     rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/fs/bpf                bpf         bpf        rw,nosuid,nodev,noexec,relatime,mode=700
│ ├─/sys/fs/selinux            selinuxfs   selinuxfs  rw,nosuid,noexec,relatime
│ ├─/sys/kernel/debug          debugfs     debugfs    rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/kernel/tracing        tracefs     tracefs    rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/kernel/config         configfs    configfs   rw,nosuid,nodev,noexec,relatime
│ └─/sys/fs/fuse/connections   fusectl     fusectl    rw,nosuid,nodev,noexec,relatime
├─/dev                         devtmpfs    devtmpfs   rw,nosuid,seclabel,size=4096k,nr_inodes=2008483,mode=755
│ ├─/dev/shm                   tmpfs       tmpfs      rw,nosuid,nodev,seclabel,inode64
│ ├─/dev/pts                   devpts      devpts     rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmo
│ ├─/dev/mqueue                mqueue      mqueue     rw,nosuid,nodev,noexec,relatime,seclabel
│ └─/dev/hugepages             hugetlbfs   hugetlbfs  rw,relatime,seclabel,pagesize=2M
├─/run                         tmpfs       tmpfs      rw,nosuid,nodev,seclabel,size=3221940k,nr_inodes=819200,
│ ├─/run/credentials/systemd-sysctl.service
│ │                            none        ramfs      ro,nosuid,nodev,noexec,relatime,seclabel,mode=700
│ ├─/run/credentials/systemd-tmpfiles-setup-dev.service
│ │                            none        ramfs      ro,nosuid,nodev,noexec,relatime,seclabel,mode=700
│ ├─/run/credentials/systemd-tmpfiles-setup.service
│ │                            none        ramfs      ro,nosuid,nodev,noexec,relatime,seclabel,mode=700
│ └─/run/user/1002             tmpfs       tmpfs      rw,nosuid,nodev,relatime,seclabel,size=1610968k,nr_inode
└─/var/lib/nfs/rpc_pipefs      sunrpc      rpc_pipefs rw,relatime

findmnt arranges the mounts into a tree and aligns some of the columns you saw before to make them more readalbe.

Exercise

In the example output above, onto which mountpoint is /dev/shm mounted to?

A Shorter More Minimal List: /proc/PID/mounts

Every process also has a more minimal list that has all the mounts but in a more simplified format. It is easier to read, but is incomplete. The incompleteness is most glaring for bind mounts, but it is still useful for quick checks with other mounts, particularly to quickly read the mount options. The more minimal list for a process is at /proc/PID/mounts, which can also be accessed by the convenient short cut path /proc/mounts.

Type-Along

To see the more minimal information, run

cat /proc/self/mountinfo

What you will get will depend on the linux system you are on and any mounts you have setup. An example could be

[foo@bar ~]$ cat /proc/mounts
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=4096k,nr_inodes=2008483,mode=755,inode64 0 0
securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /dev/shm tmpfs rw,seclabel,nosuid,nodev,inode64 0 0
devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /run tmpfs rw,seclabel,nosuid,nodev,size=3221940k,nr_inodes=819200,mode=755,inode64 0 0
cgroup2 /sys/fs/cgroup cgroup2 rw,seclabel,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot 0 0
pstore /sys/fs/pstore pstore rw,seclabel,nosuid,nodev,noexec,relatime 0 0
bpf /sys/fs/bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700 0 0
/dev/vda1 / ext4 rw,seclabel,relatime 0 0
selinuxfs /sys/fs/selinux selinuxfs rw,nosuid,noexec,relatime 0 0
systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=1207 0 0
mqueue /dev/mqueue mqueue rw,seclabel,nosuid,nodev,noexec,relatime 0 0
hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime,pagesize=2M 0 0
debugfs /sys/kernel/debug debugfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
tracefs /sys/kernel/tracing tracefs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
none /run/credentials/systemd-sysctl.service ramfs ro,seclabel,nosuid,nodev,noexec,relatime,mode=700 0 0
configfs /sys/kernel/config configfs rw,nosuid,nodev,noexec,relatime 0 0
none /run/credentials/systemd-tmpfiles-setup-dev.service ramfs ro,seclabel,nosuid,nodev,noexec,relatime,mode=700 0 0
fusectl /sys/fs/fuse/connections fusectl rw,nosuid,nodev,noexec,relatime 0 0
none /run/credentials/systemd-tmpfiles-setup.service ramfs ro,seclabel,nosuid,nodev,noexec,relatime,mode=700 0 0
sunrpc /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /run/user/1002 tmpfs rw,seclabel,nosuid,nodev,relatime,size=1610968k,nr_inodes=402742,mode=700,uid=1002,gid=1002,inode64 0 0

Now the columns are what is mounted (disk, special filesystem, etc.), the mount point, the filesystem, the mount options, the dump option, and the filesystem check order. Note that bind mounts show up exactly like the disk, special filesystem, etc. they are mounted from but instead have a different mount point; which is why bind mounts are rather incomplete in this format.

Seeing the Utilization of Each Mountpoint: df

The program df is meant to see the capacity and utilization of the mounted filesystems that have capacities (not all do). While not intended for this purpose, it can be used as a quick way to see a subset of the filesystems you have mounted and where they are mounted.

Type-Along

Run the following to see the filesystems that have capacities and how much is used.

df

What you will get will depend on the linux system you are on and any mounts you have setup. An example could be

[foo@bar ~]$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        4,0M     0  4,0M   0% /dev
tmpfs           7,7G   84K  7,7G   1% /dev/shm
tmpfs           3,1G   17M  3,1G   1% /run
/dev/vda1       158G  2,4G  149G   2% /
tmpfs           1,6G     0  1,6G   0% /run/user/1002

Notice how few of the mounts are shown. Most of the mounts don’t have a capacity per-se.

tmpfs Mounts

tmpfs filesystems are special RAM disks whose free space does not actually consume RAM and which can go to swap. They have almost entirely replaced traditional ramdisks that were locked in RAM and consumed the same amount whether empty or full. To mount a tmpfs filesystem, you would run

mount -t tmpfs [OPTIONS] tmpfs TARGET

where TARGET is where you want to mount it to (must be a directory that exists) You will notice that the “device” for a tmpfs is always tmpfs. In addition to the standard mount options with -o, there are additional ones to control how large the tmpfs can be (see man tmpfs for all extra options). They are unmounted just like any other mountpoint with umount TARGET. Since you aren’t in suitably prepared namespaces (see next lesson), you will have to do the mount and umount commands as root.

Warning

When a tmpfs is no longer mounted anywhere, all its contents are lost forever. So, backup anything you want to keep in them somewhere else. Also, never assume the space in a tmpfs is reclaimed securely. It is just freed, not overwritten first.

Exercise

Do the following:

  1. Create a directory

  2. Create a file in that directory

  3. Mount a tmpfs to the directory that has a capacity of 4 GiB.

  4. Look at the mount with findmnt.

  5. Create a file in the mount

  6. Run ls to look at the files in the mount

  7. Unmount it

  8. Run ls to look at the files in the directory again

Bind Mounts

Bind mounts let you attach one part of the VFS tree to another location. It then exists at both, so if you edit under one part it will show up on the the other too. This can mean that more than one path can refer to the same file or directory just like symlinks and hardlinks. With bind mounts, you bind a file or directory to one in another part of the tree. Note that the target must exist and must be of the same kind (file or directory) as the source. Since you aren’t in suitably prepared namespaces (see next lesson), you will have to do the mount and umount commands as root. Bind mounts are created using mount in the following way:

mount --bind [OPTIONS] SOURCE TARGET

where SOURCE and TARGET are the paths to the source object and the target where you want to bind it to. A common option to add is -o ro to make the binding be read-only at the target even if it is read-write at the source. You can then unmount the bind mount just like you would any other mountpoint with umount TARGET

Exercise

Do the following:

  1. Create two directories

  2. Create a file with a different name in each directory

  3. Bind mount the first directory to the second read-only

  4. Run ls to look at the files in each directory

  5. Try to add text to the file you see in the second directory

  6. Edit the file you see in the first directory and save some content

  7. Check the content of the file you see in the second directory

  8. Unmount the bind mount

  9. Run ls to look at the files in both directories again

  10. Check the content of the file you see in the first directory

  11. Check the content of the file you see in the second directory

Change Root

The chroot command (and syscall) is used to change where in the VFS / is for a process and its children. Its syntax is

chroot [OPTIONS] NEWROOT [COMMAND [ARG ...]]

and runs the specified COMMAND and arguments with the new /. If you don’t specify COMMAND, it will be the username’s default shell. If you do this on the terminal, the command runs in that environment until it terminates and then you are back at the terminal where you previously were. Since you aren’t in suitably prepared namespaces (see next lesson), you will have to do the chroot command as root.

Make a Fresh Container Image

You will be using the image you made in the previous lesson in Section Making A Simple Container Image and Image. It is time to make a fresh container image from it.

Type-Along

Make a directory owned by root, mount a small tmpfs to it, and unpack the tarball image into it.

~/containers> sudo mkdir con
~/containers> sudo mount -t tmpfs -o size=1g tmpfs con
~/containers> sudo tar -C con -xvzf img.tgz
./
./bin
./dev/
./dev/null
./etc/
./proc/
./root/
./usr/
./usr/bin/
./usr/bin/ash
./usr/bin/busybox
./usr/bin/cd
./usr/bin/cp
./usr/bin/ln
./usr/bin/ls
./usr/bin/mkdir
./usr/bin/mount
./usr/bin/mv
./usr/bin/ps
./usr/bin/rm
./usr/bin/sh
./usr/bin/touch
./usr/bin/umount

Now check that it unpacked OK.

~/containers> ls -lh con/usr/bin
total 1.5M
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 ash -> busybox
-rwxr-xr-x. 1 root root 1.4M Mar 31 16:33 busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 cd -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:36 cp -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 ln -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 ls -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 mkdir -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 mount -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:36 mv -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:36 ps -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 rm -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 sh -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:34 touch -> busybox
lrwxrwxrwx. 1 root root    7 Mar 31 16:35 umount -> busybox

You now have a fresh container image in its own tmpfs that can be cleaned up merely by unmounting the tmpfs.

chroot Time

With the container image prepared, you can chroot into it to have a running container.

Type-Along

Chroot into it using ash as your shell and see what files you see.

~/containers> sudo chroot con /usr/bin/ash
/ # ls -lh
total 0
lrwxrwxrwx    1 0        0              7 Mar 31 14:32 bin -> usr/bin
drwxr-xr-x    2 0        0             60 Mar 31 14:33 dev
drwxr-xr-x    2 0        0             40 Mar 31 14:32 etc
drwxr-xr-x    2 0        0             40 Mar 31 14:32 proc
drwxr-xr-x    2 0        0             60 Mar 31 15:25 root
drwxr-xr-x    3 0        0             60 Mar 31 14:32 usr

Exercise

Try some of the shell commands you made available to yourself for the next few minutes. If you realize you are missing something useful, you can just run it with busybox COMMAND or make a new symlink with ln -s busybox /usr/bin/COMMAND. You can exit the chroot with Ctrl+D, and re-enter if you want.

Note that because this tmpfs you unpacked the image into, any changes you did in the exercise do not change the actual image. If you wanted to persist your changes, you would need to make a new image from it.

Congratulation, you have made and run your first container made from scratch!

Pivot Root