Demystifying Containers – Part I: Kernel Space

This series of blog posts and corresponding talks aims to provide you with a pragmatic view on containers from a historic perspective. Together we will discover modern cloud architectures layer by layer, which means we will start at the Linux Kernel level and end up at writing our own secure cloud native applications.

Simple examples paired with the historic background will guide you from the beginning with a minimal Linux environment up to crafting secure containers, which fit perfectly into todays’ and futures’ orchestration world. In the end it should be much easier to understand how features within the Linux kernel, container tools, runtimes, software defined networks and orchestration software like Kubernetes are designed and how they work under the hood.


Part I: Kernel Space

This first blog post (and talk) is scoped to Linux kernel related topics, which will provide you with the necessary foundation to build up a deep understanding about containers. We will gain an insight about the history of UNIX, Linux and talk about solutions like chroot, namespaces and cgroups combined with hacking our own examples. Besides this we will peel some containers to get a feeling about future topics we will talk about.

Introduction

If we are talking about containers nowadays, most people tend to think of the big blue whale or the white steering wheel on the blue background.

Let’s put these thoughts aside and ask ourselves: What are containers in detail? If we look at the corresponding documentation of Kubernetes we only find explanations about “Why to use containers?“ and lots of references to Docker. Docker itself explains containers as “a standard unit of software“. Their explanations provide a general overview but do not reveal much of the underlying “magic“. Eventually, people tend to imagine containers as cheap virtual machines (VMs), which technically does not come close to the real world. This could be reasoned since the word “container” does not mean anything precisely at all. The same applies to the word “pod” in the container orchestration ecosystem.

If we strip it down then containers are only isolated groups of processes running on a single host, which fulfill a set of “common” features. Some of these fancy features are built directly into the Linux kernel and mostly all of them have different historical origins.

So containers have to fulfill four major requirements to be acceptable as such:

  1. Not negotiable: They have to run on a single host. Okay, so two computers cannot run a single container.
  2. Clearly: They are groups of processes. You might know that Linux processes live inside a tree structure, so we can say containers must have a root process.
  3. Okay: They need to be isolated, whatever this means in detail.
  4. Not so clear: They have to fulfill common features. Features in general seem to change over time, so we have to point out what the most common features are.

These requirements alone can lead into confusion and the picture is not clear yet. So let’s start from the historical beginning to keep things simple.

chroot

Mostly every UNIX operating system has the possibility to change the root directory of the current running process (and its children). This originates from the first occurrence of chroot in UNIX Version 7 (released 1979), from where it continued the journey into the awesome Berkeley Software Distribution (BSD). In Linux you can nowadays chroot(2) as system call (a kernel API function call) or the corresponding standalone wrapper program. Chroot is also referenced as “jail“, because some person used it as a honeypot to monitor a security hacker back in 1991. So chroot is much older than Linux and it has been (mis)used in the early 2000s for the first approaches in running applications as what we would call today “microservices”. Chroot is currently used by a wide range of applications, for example within build services for different distributions. Nowadays the BSD implementation differs a lots from the Linux one, where we will focus on the latter part for now.

What is needed to run an own chroot environment? Not that much, since something like this already works:

> mkdir -p new-root/{bin,lib64}
> cp /bin/bash new-root/bin
> cp /lib64/{ld-linux-x86-64.so*,libc.so*,libdl.so.2,libreadline.so*,libtinfo.so*} new-root/lib64
> sudo chroot new-root

We create a new root directory, copy a bash shell and its dependencies in and run chroot. This jail is pretty useless: All we have at hand is bash and its builtin functions like cd and pwd.

One might think it could be worth running a statically linked binary in a jail and that would be the same as running a container image. It’s absolutely not, and a jail is not really a standalone security feature but more a good addition to our container world.

The current working directory is left unchanged when calling chroot via a syscall, whereas relative paths can still refer to files outside of the new root. This call changes only the root path and nothing else. Beside this, further calls to chroot do not stack and they will override the current jail. Only privileged processes with the capability CAP_SYS_CHROOT are able to call chroot. At the end of the day the root user can easily escape from a jail by running a program like this:

#include <sys/stat.h>
#include <unistd.h>

int main(void)
{
    mkdir(".out", 0755);
    chroot(".out");
    chdir("../../../../../");
    chroot(".");
    return execl("/bin/bash", "-i", NULL);
}

We create a new jail by overwriting the current one and change the working directly to some relative path outside of the chroot environment. Another call to chroot might bring us outside of the jail which can be verified by spawning a new interactive bash shell.

Nowadays chroot is not used by container runtimes any more and was replaced by pivot_root(2), which has the benefit of putting the old mounts into a separate directory on calling. These old mounts could be unmounted afterwards to make the filesystem completely invisible to broken out processes.

To continue with a more useful jail we need an appropriate root filesystem (rootfs). This contains all binaries, libraries and the necessary file structure. But where to get one? What about peeling it from an already existing Open Container Initiative (OCI) container, which can be easily done with the two tools skopeo and umoci:

> skopeo copy docker://opensuse/tumbleweed:latest oci:tumbleweed:latest

[output removed]

> sudo umoci unpack –image tumbleweed:latest bundle

[output removed]

Now with our freshly downloaded and extracted rootfs we can chroot into the jail via:

> sudo chroot bundle/rootfs
#

It looks like we’re running inside a fully working environment, right? But what did we achieve? We can see that we may sneak-peak outside the jail from a process perspective:

> mkdir /proc
> mount -t proc proc /proc
> ps aux

[output removed]

There is no process isolation available at all. We can even kill programs running outside of the jail, what a metaphor! Let’s peek into the network devices:

> mkdir /sys
> mount -t sysfs sys /sys
> ls /sys/class/net
eth0 lo

There is no network isolation, too. This missing isolation paired with the ability to leave the jail leads into lots of security related concerns, because jails are sometimes used for wrong (security related) purposes. How to solve this? This is where the Linux namespaces join the party.

Linux Namespaces

Namespaces are a Linux kernel feature which were introduced back in 2002 with Linux 2.4.19. The idea behind a namespace is to wrap certain global system resources in an abstraction layer. This makes it appear like the processes within a namespace have their own isolated instance of the resource. The kernels namespace abstraction allows different groups of processes to have different views of the system.

Not all available namespaces were implemented from the beginning. A full support for what we now understand as “container ready” was finished in kernel version 3.8 back in 2013 with the introduction of the user namespace. We end up having currently seven distinct namespaces implemented: mnt, pid, net, ipc, uts, user and cgroup. No worries, we will discuss them in detail. In September 2016 two additional namespaces were proposed (time and syslog) which are not fully implemented yet. Let’s have a look into the namespace API before digging into certain namespaces.

API

The namespace API of the Linux kernel consists of three main system calls:

clone

The clone(2) API function creates a new child process, in a manner similar to fork(2). Unlike fork(2), the clone(2) API allows the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers. You can pass different namespace flags to clone(2)to create new namespaces for the child process.

unshare

The function unshare(2) allows a process to disassociate parts of the execution context which are currently being shared with others.

setns

The function setns(2) reassociates the calling thread with the provided namespace file descriptor. This function can be used to join an existing namespace.

proc

Besides the available syscalls, the proc filesystem populates additional namespace related files. Since Linux 3.8, each file in /proc/$PID/ns is a “magic“ link which can be used as a handle for performing operations (like setns(2)) to the referenced namespace.

> ls -Gg /proc/self/ns/
total 0
lrwxrwxrwx 1 0 Feb  6 18:32 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 0 Feb  6 18:32 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 0 Feb  6 18:32 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 0 Feb  6 18:32 net -> 'net:[4026532008]'
lrwxrwxrwx 1 0 Feb  6 18:32 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 0 Feb  6 18:32 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 0 Feb  6 18:32 user -> 'user:[4026531837]'
lrwxrwxrwx 1 0 Feb  6 18:32 uts -> 'uts:[4026531838]'

This allows us for example to track in which namespaces certain processes reside. Another way to play around with namespaces apart from the programmatic approach is using tools from the util-linux package. This contains dedicated wrapper programs for the mentioned syscalls. One handy tool related to namespaces within this package is lsns. It lists useful information about all currently accessible namespaces or about a single given one. But now let’s finally get our hands dirty.

Available Namespaces

Mount (mnt)

The first namespace we want to try out is the mnt namespace, which was the first implemented one back in 2002. During that time (mostly) no one thought that multiple namespaces would ever be needed, so they decided to call the namespace clone flag CLONE_NEWNS. This leads into a small inconsistency with other namespace clone flags (I see you suffering!). With the mnt namespace Linux is able to isolate a set of mount points by a group of processes.

A great use case of the mnt namespace is to create environments similar to jails, but in a more secure fashion. How to create such a namespace? This can be easily done via an API function call or the unshare command line tool. So we can do this:

> sudo unshare -m
# mkdir mount-dir
# mount -n -o size=10m -t tmpfs tmpfs mount-dir
# df mount-dir
Filesystem     1K-blocks  Used Available Use% Mounted on
tmpfs              10240     0     10240   0% <PATH>/mount-dir
# touch mount-dir/{0,1,2}

Looks like we have a successfully mounted tmpfs, which is not available on the host system level:

> ls mount-dir
> grep mount-dir /proc/mounts
>

The actual memory being used for the mount point is laying in an abstraction layer called Virtual File System (VFS), which is part of the kernel and where every other filesystem is based on. If the namespace gets destroyed, the mount memory is unrecoverably lost. The mount namespace abstraction gives us the possibility to create entire virtual environments in which we are the root user even without root permissions.

On the host system we are able to see the mount point via the mountinfo file inside of the proc filesystem:

> grep mount-dir /proc/$(pgrep -u root bash)/mountinfo
349 399 0:84 / /mount-dir rw,relatime - tmpfs tmpfs rw,size=1024k

How to work with these mount points on a source code level? Well, programs tend to keep a file handle on the corresponding /proc/$PID/ns/mnt file, which refers to the used namespace. In the end mount namespace related implementation scenarios can be really complex, but they give us the power to create flexible container filesystem trees. The last thing I want to mention is that mounts can have different flavors (shared, slave, private, unbindable), which is best explained within the shared subtree documentation of the Linux kernel.

UNIX Time-sharing System (uts)

The UTS namespace was introduced in Linux 2.6.19 (2006) and allows us to unshare the domain- and hostname from the current host system. Let’s give it a try:

> sudo unshare -u
# hostname
nb
# hostname new-hostname
# hostname
new-hostname

And if we look at the system level nothing has changed, hooray:

> hostname
nb

The UTS namespace is yet another nice addition in containerization, especially when it comes to container networking related topics.

Interprocess Communication (ipc)

IPC namespaces came with Linux 2.6.19 (2006) too and isolate interprocess communication (IPC) resources. In special these are System V IPC objects and POSIX message queues. One use case of this namespace would be to separate the shared memory (SHM) between two processes to avoid misusage. Instead, each process will be able to use the same identifiers for a shared memory segment and produce two distinct regions. When an IPC namespace is destroyed, then all IPC objects in the namespace are automatically destroyed, too.

Process ID (pid)

The PID namespace was introduced in Linux 2.6.24 (2008) and gives processes an independent set of process identifiers (PIDs). This means that processes which reside in different namespaces can own the same PID. In the end a process has two PIDs: the PID inside the namespace, and the PID outside the namespace on the host system. The PID namespaces can be nested, so if a new process is created it will have a PID for each namespace from its current namespace up to the initial PID namespace.

The first process created in a PID namespace gets the number 1 and gains all the same special treatment as the usual init process. For example, all processes within the namespace will be re-parented to the namespace’s PID 1 rather than the host PID 1. In addition the termination of this process will immediately terminate all processes in its PID namespace and any descendants. Let’s create a new PID namespace:

> sudo unshare -fp --mount-proc
# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.4  0.6  18688  6608 pts/0    S    23:15   0:00 -bash
root        39  0.0  0.1  35480  1768 pts/0    R+   23:15   0:00 ps aux

Looks isolated, doesn’t it? The --mount-proc flag is needed to re-mount the proc filesystem from the new namespace. Otherwise we would not see the PID subtree corresponding with the namespace. Another option would be to manually mount the proc filesystem via mount -t proc proc /proc, but this also overrides the mount from the host where it has to be remounted afterwards.

Network (net)

Network namespaces were completed in Linux 2.6.29 (2009) and can be used to virtualize the network stack. Each network namespace contains its own resource properties within /proc/net. Furthermore, a network namespace contains only a loopback interface on initial creation. Let’s create one:

> sudo unshare -n
# ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

Every network interface (physical or virtual) is present exactly once per namespace. It is possible that an interface will be moved between namespaces. Each namespace contains a private set of IP addresses, its own routing table, socket listing, connection tracking table, firewall, and other network-related resources.

Destroying a network namespace destroys any virtual and moves any physical interfaces within it back to the initial network namespace.

A possible use case for the network namespace is creating Software Defined Networks (SDN) via virtual Ethernet (veth) interface pairs. One end of the network pair will be plugged into a bridged interface whereas the other end will be assigned to the target container. This is how pod networks like flannel work in general.

Let’s see how it works. First, we need to create a new network namespace, which can be done via ip, too:

> sudo ip netns add mynet
> sudo ip netns list
mynet

So we created a new network namespace called mynet. When ip creates a network namespace, it will create a bind mount for it under /var/run/netns too. This allows the namespace to persist even when no processes are running within it.

With ip netns exec we can inspect and manipulate our network namespace even further:

> sudo ip netns exec mynet ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
> sudo ip netns exec mynet ping 127.0.0.1
connect: Network is unreachable

The network seems down, let’s bring it up:

> sudo ip netns exec mynet ip link set dev lo up
> sudo ip netns exec mynet ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.016 ms

Hooray! Now let’s create a veth pair which should allow communication later on:

> sudo ip link add veth0 type veth peer name veth1
> sudo ip link show type veth
11: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether b2:d1:fc:31:9c:d3 brd ff:ff:ff:ff:ff:ff
12: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ca:0f:37:18:76:52 brd ff:ff:ff:ff:ff:ff

Both interfaces are automatically connected, which means that packets sent to veth0 will be received by veth1 and vice versa. Now we associate one end of the veth pair to our network namespace:

> sudo ip link set veth1 netns mynet
> ip link show type veth
12: veth0@if11: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ca:0f:37:18:76:52 brd ff:ff:ff:ff:ff:ff link-netns mynet

Our network interfaces need some addresses for sure:

> sudo ip netns exec mynet ip addr add 172.2.0.1/24 dev veth1
> sudo ip netns exec mynet ip link set dev veth1 up
> sudo ip addr add 172.2.0.2/24 dev veth0
> sudo ip link set dev veth0 up

Communicating in both directions should now be possible:

> ping -c1 172.2.0.1
PING 172.2.0.1 (172.2.0.1) 56(84) bytes of data.
64 bytes from 172.2.0.1: icmp_seq=1 ttl=64 time=0.036 ms

--- 172.2.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.036/0.036/0.036/0.000 ms
> sudo ip netns exec mynet ping -c1 172.2.0.2
PING 172.2.0.2 (172.2.0.2) 56(84) bytes of data.
64 bytes from 172.2.0.2: icmp_seq=1 ttl=64 time=0.020 ms

--- 172.2.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.020/0.020/0.020/0.000 ms

It works, but we wouldn’t have any internet access from the network namespace. We would need a network bridge or something similar for that and a default route from the namespace. I leave this task up to you, for now let’s go on to the next namespace.

User ID (user)

With Linux 3.5 (2012) the isolation of user and group IDs was finally possible via namespaces. Linux 3.8 (2013) made it possible to create user namespaces even without being actually privileged. The user namespace enables that a user and group IDs of a process can be different inside and outside of the namespace. An interesting use-case is that a process can have a normal unprivileged user ID outside a user namespace while being fully privileged inside.

Let’s give it a try:

> id -u
1000
> unshare -U
> whoami
nobody

After the namespace creation, the files /proc/$PID/{u,g}id_map expose the mappings for user and group IDs for the PID. These files can be written only once to define the mappings.

In general each line within these files contain a one to one mapping of a range of contiguous user IDs between two user namespaces and could look like this:

> cat /proc/$PID/uid_map
0 1000 1

The example above translates to: With the starting user ID 0 the namespace maps to a range starting at ID 1000. This applies only to the user with the ID 1000, since the defined length is 1.

If now a process tries to access a file, its user and group IDs are mapped into the initial user namespace for the purpose of permission checking. When a process retrieves file user and group IDs (via stat(2)), the IDs are mapped in the opposite direction.

In the unshare example (we did above) we implicitly call getuid(2) before writing an appropriate user mapping, which will result in an unmapped ID. This unmapped ID is automatically converted to the overflow user ID (65534 or the value in /proc/sys/kernel/overflow{g,u}id).

The file /proc/$PID/setgroups contains either allow or deny to enable or disable the permission to call thesetgroups(2) syscall within the user namespace. The file was added to address an added security issue introduced with the user namespace: It would be possible to an unprivileged process to create a new namespace in which the user had all privileges. This formerly unprivileged user would be able to drop groups via setgroups(2) to gain access to files he previously not had.

In the end the user namespace enables great security additions to the container world, which are essential for running rootless containers.

Control Group (cgroup)

Cgroups started their journey 2008 with Linux 2.6.24 as dedicated Linux kernel feature. The main goal of cgroups is to support resource limiting, prioritization, accounting and controlling. A major redesign started with version 2 in 2013, whereas the cgroup namespace was added with Linux 4.6 (2016) to prevent leaking host information into a namespace. The second version of cgroups were released there too and major features were added since then. One latest example is an Out-of-Memory (OOM) killer which adds an ability to kill a cgroup as a single unit to guarantee the overall integrity of the workload.

Let’s play around with cgroups and create a new one. By default, the kernel exposes cgroups in /sys/fs/cgroup. To create a new cgroup, we simply create a new sub-directory on that location:

> sudo mkdir /sys/fs/cgroup/memory/demo
> ls /sys/fs/cgroup/memory/demo
cgroup.clone_children
cgroup.event_control
cgroup.procs
memory.failcnt
memory.force_empty
memory.kmem.failcnt
memory.kmem.limit_in_bytes
memory.kmem.max_usage_in_bytes
memory.kmem.slabinfo
memory.kmem.tcp.failcnt
memory.kmem.tcp.limit_in_bytes
memory.kmem.tcp.max_usage_in_bytes
memory.kmem.tcp.usage_in_bytes
memory.kmem.usage_in_bytes
memory.limit_in_bytes
memory.max_usage_in_bytes
memory.move_charge_at_immigrate
memory.numa_stat
memory.oom_control
memory.pressure_level
memory.soft_limit_in_bytes
memory.stat
memory.swappiness
memory.usage_in_bytes
memory.use_hierarchy
notify_on_release
tasks

You can see that there are already some default values exposed there. Now, we are able to set the memory limits for that cgroup. We are also turning off swap to make our example implementation work.

> sudo su
# echo 100000000 > /sys/fs/cgroup/memory/demo/memory.limit_in_bytes
# echo 0 > /sys/fs/cgroup/memory/demo/memory.swappiness

To assign a process to a cgroup we can write the corresponding PID to the cgroup.procs file:

# echo $$ > /sys/fs/cgroup/memory/demo/cgroup.procs

Now we can execute a sample application to consume more than the allowed 100 megabytes of memory. The application I used is written in Rust and looks like this:

pub fn main() {
    let mut vec = vec![];
    loop {
        vec.extend_from_slice(&[1u8; 10_000_000]);
        println!("{}0 MB", vec.len() / 10_000_000);
    }
}

If we run the program, we see that the PID will be killed because of the set memory constraints. So our host system is still usable.

# rustc memory.rs
# ./memory
10 MB
20 MB
30 MB
40 MB
50 MB
60 MB
70 MB
80 MB
90 MB
Killed

Composing Namespaces

Namespaces are composable, too! This reveals their true power and makes it possible to have isolated pid namespaces which share the same network interface, like it is done in Kubernetes Pods.

To demonstrate this, let’s create a new namespace with an isolated PID:

> sudo unshare -fp --mount-proc
# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.6  18688  6904 pts/0    S    23:36   0:00 -bash
root        39  0.0  0.1  35480  1836 pts/0    R+   23:36   0:00 ps aux

The setns(2) syscall with its appropriate wrapper program nsenter can now be used to join the namespace. For this we have to find out which namespace we want to join:

> export PID=$(pgrep -u root bash)
> sudo ls -l /proc/$PID/ns

Now, it is easily possible to join the namespace via nsenter:

> sudo nsenter --pid=/proc/$PID/ns/pid unshare --mount-proc
# ps aux
root         1  0.1  0.0  10804  8840 pts/1    S+   14:25   0:00 -bash
root        48  3.9  0.0  10804  8796 pts/3    S    14:26   0:00 -bash
root        88  0.0  0.0   7700  3760 pts/3    R+   14:26   0:00 ps aux

We can now see that we are member of the same PID namespace! It is also possible to enter already running containers via nsenter, but this topic will be covered later on.

Demo Application

A small demo application can be used to create a simple isolated environment via the namespace API:

#define _GNU_SOURCE
#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define STACKSIZE (1024 * 1024)
static char stack[STACKSIZE];

void print_err(char const * const reason)
{
    fprintf(stderr, "Error %s: %s\n", reason, strerror(errno));
}

int exec(void * args)
{
    // Remount proc
    if (mount("proc", "/proc", "proc", 0, "") != 0) {
        print_err("mounting proc");
        return 1;
    }

    // Set a new hostname
    char const * const hostname = "new-hostname";
    if (sethostname(hostname, strlen(hostname)) != 0) {
        print_err("setting hostname");
        return 1;
    }

    // Create a message queue
    key_t key = {0};
    if (msgget(key, IPC_CREAT) == -1) {
        print_err("creating message queue");
        return 1;
    }

    // Execute the given command
    char ** const argv = args;
    if (execvp(argv[0], argv) != 0) {
        print_err("executing command");
        return 1;
    }

    return 0;
}

int main(int argc, char ** argv)
{
    // Provide some feedback about the usage
    if (argc < 2) {
        fprintf(stderr, "No command specified\n");
        return 1;
    }

    // Namespace flags
    const int flags = CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWNS | CLONE_NEWIPC |
                      CLONE_NEWPID | CLONE_NEWUSER | SIGCHLD;

    // Create a new child process
    pid_t pid = clone(exec, stack + STACKSIZE, flags, &argv[1]);

    if (pid < 0) {
        print_err("calling clone");
        return 1;
    }

    // Wait for the process to finish
    int status = 0;
    if (waitpid(pid, &status, 0) == -1) {
        print_err("waiting for pid");
        return 1;
    }

    // Return the exit code
    return WEXITSTATUS(status);
}

Purpose of the application is to spawn a new child process in different namespaces. Every command provided to the executable will be forwarded to the new child process. The application terminates, when the command execution is done. You can test and verify the implementation via:

> gcc -o namespaces namespaces.c
> ./namespaces ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
> ./namespaces ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
nobody       1  0.0  0.1  36524  1828 pts/0    R+   23:46   0:00 ps aux
> ./namespaces whoami
nobody

This is truly not a working container, but it should give you a slight feeling about how container runtimes leverage namespaces to manage containers. Feel free to use this example as a starting point for your own little experiments with the namespace API.

Putting it all Together

Do you remember the rootfs we extracted from the image within the chroot section? We can use a low level container runtime like runc to easily run a container from the rootfs:

> sudo runc run -b bundle container

If we now inspect the system namespaces, we see that runc already created mnt, uts, ipc, pid and net for us:

> sudo lsns | grep bash
4026532499 mnt         1  6409 root   /bin/bash
4026532500 uts         1  6409 root   /bin/bash
4026532504 ipc         1  6409 root   /bin/bash
4026532505 pid         1  6409 root   /bin/bash
4026532511 net         1  6409 root   /bin/bash

I will stop here and we will learn more about container runtimes and what they do, in upcoming blog posts and talks.

Conclusion

I really hope you enjoyed the read and that the mysteries about containers are now a little bit more fathomable. If you run Linux it is easy to play around with different isolation techniques from scratch. In the end a container runtime nicely uses all these isolation features on different abstraction levels to provide a stable and robust development and production platform for containers.

There are lots of topics which were not covered here because I wanted to stay at a stable level of detail. For sure, a great resource for digging deeper into the topic of Linux namespaces is the Linux programmers manual: NAMESPACES(7).

Feel free to drop me a line or get in contact with me for any questions or feedback. The next blog posts will cover container runtimes, security and the overall ecosystem around latest container technologies. Stay tuned!

You can find all necessary resources about this series on GitHub.

Due

Dicono che durante la nostra vita abbiamo due grandi amori. Uno con il quale ti sposerai o vivrai per sempre, può essere il padre o la madre dei tuoi figli: con questa persona otterrai la massima comprensione per stare il resto della tua vita insieme.E dicono che c’è un secondo grande amore, una persona che perderai per sempre. Qualcuno con cui sei nato collegato, così collegato, che le forze della chimica scappano dalla ragione e ti impediranno sempre di raggiungere un finale felice. Fino a che un giorno smetterai di provarci, ti arrenderai e cercherai un’altra persona che finirai per incontrare. Però ti assicuro che non passerà una sola notte senza aver bisogno di un altro suo bacio, o anche di discutere una volta in più. Tutti sanno di chi sto parlando, perché mentre stai leggendo queste righe, il suo nome ti è venuto in mente. Ti libererai di lui o di lei e smetterai di soffrire, finirai per incontrare la pace, però ti assicuro che non passerà un giorno in cui non desidererai che sia qui per disturbarti. Perché a volte si libera più energia discutendo con chi ami, che facendo l’amore con qualcuno che apprezzi.
(Paulo Coelho)

La felicità …

Crescendo impari che la felicità non e’ quella delle grandi cose. Non e’ quella che si insegue a vent’anni, quando, come gladiatori si combatte il mondo per uscirne vittoriosi.
La felicità non e’ quella che affannosamente si insegue credendo che l’amore sia tutto o niente; non e’ quella delle emozioni forti che fanno il “botto” e che esplodono fuori con tuoni spettacolari.
La felicità non e’ quella di grattacieli da scalare, di sfide da vincere mettendosi continuamente alla prova.
Crescendo impari che la felicità e’ fatta di cose piccole ma preziose.
E impari che il profumo del caffe’ al mattino e’ un piccolo rituale di felicità, che bastano le note di una canzone, le sensazioni di un libro dai colori che scaldano il cuore, che bastano gli aromi di una cucina, la poesia dei pittori della felicità, che basta il muso del tuo gatto o del tuo cane per sentire una felicità lieve. E impari che la felicità e’ fatta di emozioni in punta di piedi, di piccole esplosioni che in sordina allargano il cuore, che le stelle ti possono commuovere e il sole far brillare gli occhi, e impari che un campo di girasoli sa illuminarti il volto, che il profumo della primavera ti sveglia dall’inverno, e che sederti a leggere all’ombra di un albero rilassa e libera i pensieri.
E impari che l’amore e’ fatto di sensazioni delicate, di piccole scintille allo stomaco, di presenze vicine anche se lontane, e impari che il tempo si dilata e che quei 5 minuti sono preziosi e lunghi più di tante ore, e impari che basta chiudere gli occhi, accendere i sensi, sfornellare in cucina, leggere una poesia, scrivere su un libro o guardare una foto per annullare il tempo e le distanze ed essere con chi ami.
E impari che sentire una voce al telefono, ricevere un messaggio inaspettato, sono piccoli attimi felici.
E impari ad avere, nel cassetto e nel cuore, sogni piccoli ma preziosi.
E impari che tenere in braccio un bimbo e’ una deliziosa felicità.
E impari che i regali più grandi sono quelli che parlano delle persone che ami.
E impari che c’e’ felicità anche in quella urgenza di scrivere su un foglio i tuoi pensieri, che c’e’ qualcosa di amaramente felice anche nella malinconia.
E impari che nonostante le tue difese, nonostante il tuo volere o il tuo destino, in ogni gabbiano che vola c’e’ nel cuore un piccolo-grande Jonathan Livingston.
E impari quanto sia bella e grandiosa la semplicità.

 

Perché Simone ?

Coloro che riescono meglio nella vita, spesso non sono i più capaci (a meno di avere delle doti naturali fuori dal comune, cosa che non mi ispira più di tanto) quanto piuttosto i più determinati.

E’ sopratutto la determinazione a permettere il raggiungimento di un risultato desiderato, motivo per il quale ammiro tantissimo, ancora di più chi parte da una situazione sfavorevole e raggiunge risultati inimmaginabili solo perchè ci ha creduto, ha combattuto per un sogno, non lo ha mai perso di vista anche nelle difficoltà.

Intorno al concetto di motivazione esiste molto disorientamento, sopratutto oggi. Vedo tanti ragazzi che hanno gettato la spugna ancora prima di cominciare, non vedo il fuoco nei loro occhi, non vedo il famoso occhio della tigre, credo perchè la nostra cultura ha smarrito il senso dell’impegno e delle volontà individuali, siamo portati a pensare la motivazione come qualcosa che dipende esclusivamente dalle condizioni esterne. Ci motivano sempre gli altri o le situazioni fuori da noi. Alibi.

La differenza  sta nella capacità di mantenere il “sogno” e farlo durare a lungo nonostante ostacoli, difficoltà e problemi. La capacità di perseverare, di fare durare a lungo la motivazione, viene definita RESILIENZA, ma è l’unica spinta nella quale credo per raggiungere il risultato.

La resilienza non è un dono magico o sovranaturale: è una capacità cognitiva, cioè legata al modo con cui elaboriamo le informazioni e ci rapportiamo con la realtà. Può essere allenata ed accresciuta da tutti, in qualsiasi momento della vita. Ma richiede impegno e disciplina.

Faccio analogie con lo sport perchè è forse l’unica disciplina che ti mette alla prova subito e senze troppa dolcezza, vinci o perdi.

Rocky, il mio personaggio preferito, è un uomo che ha fatto della resilenza la sua arma migliore perchè senza quella sarebbe stato un uomo normale, non era sicuramente naturalmente dotato per essere un campione di boxe.
20090901191813!Rocky1
Ci sono delle frasi, passaggi indelebili nella mia testa :
“Nessuno può colpire duro come fa la vita, perciò andando avanti non è importante come colpisci, l’importante è come sai resistere ai colpi, come incassi e se finisci al tappeto hai la forza di rialzarti. Così sei un vincente!”
Vorrei poter trasmettere questo concetto a mio figlio.
Certo Rocky è un personaggio inventato ma io ne conosco uno reale, una persona che ci è riuscito, un caro amico di infanzia, una persona con la quale ho avuto l’onore di giocare e condividere una passione da piccolo : Simone Di Tommaso
.
u16
.
Nonostante non sia alto : 1.73 ci ha sempre messo il cuore, ci ha sempre creduto e ci è riuscito, ha coronato un sogno per il quale chissà quante volte è stato preso in giro, chissà quante volte è stato osservato dalla squdra avversaria sotto rete pensando ad un muro “facile” e chissà quante volte sono stati stampati 🙂
Simone di Tommaso ha giocato in a2 ed uno dei giocatori più forti della B1 maschile.
Molto probabilmente giocherà ancora in a2.
Cosa gli ha permesso di raggiungere questo grande obiettivo ? La resilenza. La motivazione.
Io non ci ho mai creduto, ho sempre pensato che la mia altezza mi avrebbe precluso qualiasi strada pallavolistica, infatti ho fallito.
.
Rivivo in Simone il sogno che non sono mai riuscito ed un’attegiamento che ho imparato troppo tardi e che vorrei donare a mio figlio regalandogli il nome Simone !
 .
Forza Simone, non avere paura di fallire, non si può sempre vincere, ma non si deve avere paura di prendere decisioni e di tentare sempre. Devi sempre credere che qualcosa di eccezionale possa accadere se crederai sempre in te stesso.
.
Cerca di capire prima quello che io ho capito solo dopo.
.
.

Apache server-status

To the uninitiated, the mod_status output can look like so much gobbledegook, but it’s really quite straightforward. Let’s take a look at some sample output.

Apache Server Status for somedomain.com
Server Version: Apache/1.3.9 (Unix) PHP/4.0b3 
Server Built: Mar 4 2000 17:01:01

The first few lines identify and provide a brief description of your server. The server version information includes an incomplete list of some of the modules compiled into your server. Our example server is running on a Unix system and has been compiled with support for the PHP scripting language. (The level of detail provided by the server version line may be limited by the ServerTokens configuration directive.)

Current Time: Thursday, 13-Apr-2000 17:22:36 PDT
Restart Time: Thursday, 13-Apr-2000 17:15:26 PDT
Parent Server Generation: 14
Server uptime: 7 minutes 10 seconds
Total accesses: 42 - Total Traffic: 187 kB
CPU Usage: u.1 s.1 cu0 cs0 - .0465% CPU load
.0977 requests/sec - 445 B/second - 4559 B/request
3 requests currently being processed, 5 idle servers

The next block represents the server’s current state. Our example server has only been up for a few minutes and hasn’t yet seen much activity. It is currently dealing with three requests, one of which is my request for the server status itself. The message that five servers are idle servers is a clue that this server is configured to maintain a pool of at least five spare child processes ready to spring into action should the need arise.

K___K_W_........................................................
................................................................
................................................................
................................................................

Scoreboard Key:
   "_" Waiting for Connection, "S" Starting up, "R" Reading Request,
   "W" Sending Reply, "K" KeepAlive (read), "D" DNS Lookup, "L" Logging,
   "G" Gracefully finishing, "." Open slot with no current process

No, that’s not boring morse-code; it’s the “scoreboard,” a pseudo-graphical representation of the state of the server’s child processes. According to the included Scoreboard Key, our server is replying to one request, maintaining two KeepAlive connections, and is maintaining five idle processes. A busier server’s scoreboard would look more like:


WWKW__WW_KKKWK__KKKKWKKKKK_WKKK_KK__KRWKKK__KK___K____WKK__KWWKK
_K___K___WWKWWW_W_W_WWWK_WW_WWWLWWW_KWWKKWKWWKWWKKWW_KWKKKKW__WK
WKWWW_KKWKKKWK_KW_KKKK__KK_KKKWWK_KW__K_KKK_K..........W........
................................................................

For more on pool regulation and KeepAlive, see my earlier HTTP Wrangler column, “An Amble Through Apache Configuration.”

Srv  PID   Acc     M CPU  SS Req Conn Child Slot
0-14 29987 0/24/24 W 0.09 2  0   0.0  0.16  0.16

Client     VHost Request     Request
127.0.0.1  www.mydomain.net  GET /server-status HTTP/1.0

In addition to a more general overview of your server’s activity, mod_status gets down to the nitty-gritty, displaying a snapshot of the individual requests it is currently handling. Let’s take a gander at a fairly representative request. Please note that the output above has been split in half for display purposes.

0-14 Srv
The ID of the child process and its generation. The generation increases each time a child process is restarted, whether due to a server-restart or a limit placed on the number of processes a child is allowed to handle. See the MaxRequestsPerChild directive.
29987 PID
The child’s process ID.
0/24/24 Acc
The first number in this trio is the number of accesses or requests using this connection. For non-KeepAlive connections, this will be 0 since each request makes its own connection and so is always the first (and last). The second is the number of requests handled thus far by this child. The third is the number of requests handled by this slot; the child may have come and gone, its slot taken by another.
W Mode
The child’s mode of operation; one of the following possibilities:

"_" Waiting for Connection, "S" Starting up, 
"R" Reading Request, "W" Sending Reply,
 "K" KeepAlive (read), 
"D" DNS Lookup, "L" Logging, "G" Gracefully finishing, 
"." Open slot with no current process
0.09
2
0
0.0
0.16
0.16
CPU SS Req Conn Child Slot
Some of the less useful bits and pieces…

CPU: The child’s CPU usage in number of seconds.
SS: Seconds elapsed since the beginning of the request.
Req: Milliseconds taken to process the request.
Conn: Kilobytes transferred across this connection.
Child: Megabytes transferred by this child process.
Slot: Megabytes transferred by this slot, across children.

www.mydomain.net VHost
Perhaps your server hosts multiple virtual domains; how would you determine which page is being requested by GET /index.html?. The VHost column helps you sort out which request is coming to which virtual host — in this example, www.mydomain.net.
GET /server-status HTTP/1.0 Request
This particular hit is my request for server-status. The GET bit indicates a simple request for a document (as opposed to sending data to the server using POST). The browser (in this case the Unix command-line wget program) is using HTTP version 1.0.

For more on HTTP, see my earlier HTTP Wrangler column, “Introducing Apache.”

Installation

So how do you install and configure mod_status? I make the assumption here that you built and installed Apache from source. If you’re not familiar with building Apache, may I suggest you read my earlier HTTP Wrangler column, Getting, Installing, and Running Apache.

First, move into your Apache source directory.

% cd /usr/local/src/apache_1.3.x

Thankfully Apache’s configure script creates a cache file, config.status, saving us the bother of completely reconfiguring our Apache build from scratch. All we need to do is run config.status, supplying the one argument necessary to add mod_status.

If you’ve not already done so, now would be the time to become root.

# ./config.status --enable-module=status
Configuring for Apache, Version 1.3.11
...
Creating Makefile
Creating Configuration.apaci in src
Creating Makefile in src
 + configured for Linux platform
 + setting C compiler to gcc
 + setting C pre-processor to gcc -E
 + checking for system header files
 + adding selected modules
 + checking sizeof various data types
 + doing sanity check on compiler and options
...
Creating Makefile in src/modules/standard

Note: Apache’s configure script automagically updates config.status to include mod_status; next time you configure you will not need to enable mod_status again.

Now that we’ve reconfigured Apache, let’s rebuild.

# make

Your screen should look something like:

# make
===&amp;gt; src
make[1]: Entering directory `src/httpd/apache_1.3.11'
make[2]: Entering directory `src/httpd/apache_1.3.11/src'
===&amp;gt; src/regex
...
[several unsightly lines later]
...
gcc  -DLINUX=2 -DUSE_HSREGEX -DUSE_EXPAT -I../lib/expat-lite 
-DNO_DL_NEEDED `../apaci` -o ab   -L../os/unix -L../ap ab.o 
-lap -los  -lm -lcrypt
make[2]: Leaving directory `src/httpd/apache_1.3.11/src/support'
&amp;lt;=== src/support
make[1]: Leaving directory `src/httpd/apache_1.3.11'
&amp;lt;=== src
#

Finally, you’re ready to install your newly freshly built Apache.

# make install

(While not strictly necessary — reinstalling should only overwrite files that probably haven’t changed since your last install — I always advise backing up your Apache directory.)

Configuration

Mod_status is easy to configure; in fact the directives already exist in your httpd.conf file and simply need to be uncommented and edited slightly. If you’re not familiar with Apache configuration, may I suggest you read my earlier HTTP Wrangler column, An Amble Through Apache Configuration.

# cd /usr/local/apache/conf

(or wherever your Apache installation’s configuration files are located)

Open your httpd.conf file in the text editor of your choice and search for the following set of configuration directives:

# Allow server status reports, with the URL of http://servername/server-status
# Change the ".your_domain.com" to match your domain to enable.
#
#<Location /server-status>
#    SetHandler server-status
#    Order deny,allow
#    Deny from all
#    Allow from .your_domain.com
#</Location>

Uncomment everything from &amp;lt;Location /server-status&amp;gt; to &amp;lt;/Location&amp;gt; by removing the # characters from the beginning of each line.

It’s wise to protect your server-status output from prying eyes. The easiest way to do this is to restrict its access to one computer or domain. Change the .your_domain.com to the name of a computer or domain you wish to allow a peek at server-status. For example, if you’re the webmaven for your server, you may want to allow only your computer,mycomputer.mydomain.org access, your server-status configuration would look something like:

# Allow server status reports, with the URL of http://servername/server-status
# Change the ".your_domain.com" to match your domain to enable.
#
<Location /server-status>
    SetHandler server-status
    Order deny,allow
    Deny from all
    Allow from mycomputer.mydomain.org
</Location>

Only one tiny piece left. The default status display isn’t as detailed as what you I showed you above. The more abbreviated version looks something like:

PID Key:
   29955 in state: _ ,   29956 in state: _ ,   29957 in state: _
   29958 in state: _ ,   29959 in state: W ,   29978 in state: _

In order to see all the gory details, you need to enable “full” status. Find the following lines and uncomment (remove the initial #) the ExtendedStatus directive; the result should look like:

# ExtendedStatus controls whether Apache will generate "full" status
# information (ExtendedStatus On) or just basic information (ExtendedStatus
# Off) when the "server-status" handler is called. The default is Off.
#
ExtendedStatus On

That’s all there is to mod_status configuration. Save your httpd.conf file, shut down and start Apache.

# /usr/local/apache/sbin/apachectl stop
/usr/local/apache/sbin/apachectl stop: httpd stopped
# /usr/local/apache/sbin/apachectl start
/usr/local/apache/sbin/apachectl start: httpd started
#

Fire up your Web browser on a machine allowed access to your server’s server-status and point it at the URL:

http://servername/server-status

Happy reading! For more information on mod_status and other aspects of Apache we touched on along the way, visit the Resources section below.

Logging the php mail function

From php version 5.3.0 we can use the directive mail.log to log who’s calling the function mail(). When someone calls the function mail() from a php script we can get some info about the sender in our log.

I will enable logging globally. You can choose yourself where to activate it, editing your php.ini for cli, cgi, apache2, fpm…

To enable it globally:

sudo echo “mail.log = /var/log/phpmail.log” > /etc/php5/conf.d/mail.ini

phpmail.log is the log filename used in my example. Then create the file:

touch /var/log/phpmail.log

chmod 777 /var/log/phpmail.log

…and restart apache or process manager you are using:

/etc/init.d/apache2 restart

or

/etc/init.d/php5-fpm restart

Troubleshooting High I/O Wait in Linux

Linux has many tools available for troubleshooting some are easy to use, some are more advanced.

I/O Wait is an issue that requires use of some of the more advanced tools as well as an advanced usage of some of the basic tools. The reason I/O Wait is difficult to troubleshoot is due to the fact that by default there are plenty of tools to tell you that your system is I/O bound, but not as many that can narrow the problem to a specific process or processes.

Answering whether or not I/O is causing system slowness

To identify whether I/O is causing system slowness you can use several commands but the easiest is the unix command top.

 # top
 top - 14:31:20 up 35 min, 4 users, load average: 2.25, 1.74, 1.68
 Tasks: 71 total, 1 running, 70 sleeping, 0 stopped, 0 zombie
 Cpu(s): 2.3%us, 1.7%sy, 0.0%ni, 0.0%id, 96.0%wa, 0.0%hi, 0.0%si, 0.0%st
 Mem: 245440k total, 241004k used, 4436k free, 496k buffers
 Swap: 409596k total, 5436k used, 404160k free, 182812k cached

From the CPU(s) line you can see the current percentage of CPU in I/O Wait; The higher the number the more cpu resources are waiting for I/O access.

wa -- iowait
 Amount of time the CPU has been waiting for I/O to complete.

Finding which disk is being written to

The above top command shows I/O Wait from the system as a whole but it does not tell you what disk is being affected; for this we will use the iostat command.

 $ iostat -x 2 5
 avg-cpu: %user %nice %system %iowait %steal %idle
  3.66 0.00 47.64 48.69 0.00 0.00

 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
 sda 44.50 39.27 117.28 29.32 11220.94 13126.70 332.17 65.77 462.79 9.80 2274.71 7.60 111.41
 dm-0 0.00 0.00 83.25 9.95 10515.18 4295.29 317.84 57.01 648.54 16.73 5935.79 11.48 107.02
 dm-1 0.00 0.00 57.07 40.84 228.27 163.35 8.00 93.84 979.61 13.94 2329.08 10.93 107.02

The iostat command in the example will print a report every 2 seconds for 5 intervals; the -x tells iostat to print out an extended report.

The 1st report from iostat will print statistics based on the last time the system was booted; for this reason in most circumstances the first report from iostat should be ignored. Every sub-sequential report printed will be based on the time since the previous interval. For example in our command we will print a report 5 times, the 2nd report are disk statistics gathered since the 1st run of the report, the 3rd is based from the 2nd and so on.

In the above example the %utilized for sda is 111.41% this is a good indicator that our problem lies with processes writing to sda. While the test system in my example only has 1 disk this type of information is extremely helpful when the server has multiple disks as this can narrow down the search for which process is utilizing I/O.

Aside from %utilized there is a wealth of information in the output of iostat; items such as read and write requests per millisecond(rrqm/s & wrqm/s), reads and writes per second (r/s & w/s) and plenty more. In our example our program seems to be read and write heavy this information will be helpful when trying to identify the offending process.

Finding the processes that are causing high I/O

iotop

 # iotop
 Total DISK READ: 8.00 M/s | Total DISK WRITE: 20.36 M/s
  TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
 15758 be/4 root 7.99 M/s 8.01 M/s 0.00 % 61.97 % bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp

The simplest method of finding which process is utilizing storage the most is to use the command iotop. After looking at the statistics it is easy to identify bonnie++ as the process causing the most I/O utilization on this machine.

While iotop is a great command and easy to use, it is not installed on all (or the main) Linux distributions by default; and I personally prefer not to rely on commands that are not installed by default. A systems administrator may find themselves on a system where they simply cannot install the non-defualt packages until a scheduled time which may be far too late depending on the issue.

If iotop is not available the below steps will also allow you to narrow down the offending process/processes.

Process list “state”

The ps command has statistics for memory and cpu but it does not have a statistic for disk I/O. While it may not have a statistic for I/O it does show the processes state which can be used to indicate whether or not a process is waiting for I/O.

The ps state field provides the processes current state; below is a list of states from the man page.

PROCESS STATE CODES
 D uninterruptible sleep (usually IO)
 R running or runnable (on run queue)
 S interruptible sleep (waiting for an event to complete)
 T stopped, either by a job control signal or because it is being traced.
 W paging (not valid since the 2.6.xx kernel)
 X dead (should never be seen)
 Z defunct ("zombie") process, terminated but not reaped by its parent.

Processes that are waiting for I/O are commonly in an “uninterruptible sleep” state or “D”; given this information we can simply find the processes that are constantly in a wait state.

Example:

 # for x in `seq 1 1 10`; do ps -eo state,pid,cmd | grep "^D"; echo "----"; sleep 5; done
 D 248 [jbd2/dm-0-8]
 D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
 ----
 D 22 [kswapd0]
 D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
 ----
 D 22 [kswapd0]
 D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
 ----
 D 22 [kswapd0]
 D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
 ----
 D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
 ----

The above for loop will print the processes in a “D” state every 5 seconds for 10 intervals.

From the output above the bonnie++ process with a pid of 16528 is waiting for I/O more often than any other process. At this point the bonnie++ seems likely to be causing the I/O Wait, but just because the process is in an uninterruptible sleep state does not necessarily prove that it is the cause of I/O wait.

To help confirm our suspicions we can use the /proc file system. Within each processes directory there is a file called “io” which holds the same I/O statistics that iotop is utilizing.

 # cat /proc/16528/io
 rchar: 48752567
 wchar: 549961789
 syscr: 5967
 syscw: 67138
 read_bytes: 49020928
 write_bytes: 549961728
 cancelled_write_bytes: 0

The read_bytes and write_bytes are the number of bytes that this specific process has written and read from the storage layer. In this case the bonnie++ process has read 46 MB and written 524 MB to disk. While for some processes this may not be a lot, in our example this is enough write and reads to cause the high i/o wait that this system is seeing.

Finding what files are being written too heavily

The lsof command will show you all of the files open by a specific process or all processes depending on the options provided. From this list one can make an educated guess as to what files are likely being written to often based on the size of the file and the amounts present in the “io” file within /proc.

To narrow down the output we will use the -p <pid> options to print only files open by the specific process id.

 # lsof -p 16528
 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
 bonnie++ 16528 root cwd DIR 252,0 4096 130597 /tmp
 <truncated>
 bonnie++ 16528 root 8u REG 252,0 501219328 131869 /tmp/Bonnie.16528
 bonnie++ 16528 root 9u REG 252,0 501219328 131869 /tmp/Bonnie.16528
 bonnie++ 16528 root 10u REG 252,0 501219328 131869 /tmp/Bonnie.16528
 bonnie++ 16528 root 11u REG 252,0 501219328 131869 /tmp/Bonnie.16528
 bonnie++ 16528 root 12u REG 252,0 501219328 131869 <strong>/tmp/Bonnie.16528</strong>

To even further confirm that these files are being written to the heavily we can see if the /tmp filesystem is part of sda.

 # df /tmp
 Filesystem 1K-blocks Used Available Use% Mounted on
 /dev/mapper/workstation-root 7667140 2628608 4653920 37% /

From the output of df we can determine that /tmp is part of the root logical volume in the workstation volume group.

 # pvdisplay
  --- Physical volume ---
  PV Name /dev/sda5
  VG Name workstation
  PV Size 7.76 GiB / not usable 2.00 MiB
  Allocatable yes
  PE Size 4.00 MiB
  Total PE 1986
  Free PE 8
  Allocated PE 1978
  PV UUID CLbABb-GcLB-l5z3-TCj3-IOK3-SQ2p-RDPW5S

Using pvdisplay we can see that the /dev/sda5 partition part of the sda disk is the partition that the workstation volume group is using and in turn is where /tmp exists. Given this information it is safe to say that the large files listed in the lsof above are likely the files being read & written to frequently.

Deframmenta per ottimizzare le tabelle di MySQL

In MySQL, quando si eliminano record da una tabella, lo spazio è riassegnato automaticamente. E ‘come uno spazio vuoto e formando gli allegati nuovi vantaggio.

Il problema è che se una tabella di eseguire molte operazioni di DELETE, lo spazio fisico del tavolo sarà sempre più frammentato e il rendimento è ridotto.

Nel MyISAM InnoDBOPTIMIZE TABLE comando disponibile ad effettuare l’ottimizzazione su qualsiasi tabella che, tra l’altro, esegue una deframmentazione automatica del tavolo.

Si consiglia vivamente di utilizzare questo comando regolarmente in particolare su tabelle che sono più le dichiarazioni di smaltimento dei record.

Per precauzione, di tenere presente che durante l’esecuzione, naturalmente, la tabella è bloccato. Si deve ricordare quando si sta per l’utilizzo con tabelle di grandi dimensioni e occupato.

La sintassi è la seguente:

OPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE mi_tabla1 [, mi_tabla2] ...;

Per fare un tavolo di ottimizzazione frammentati possono essere selezionati per avere spazio libero, probabilmente a causa di DELETE:

SELECT TABLE_SCHEMA,TABLE_NAME
FROM TABLES WHERE TABLE_SCHEMA NOT IN ("information_schema","mysql") AND
Data_free > 0

Un semplice script per deframmentare in automatico : 

#!/bin/bash

# Get a list of all fragmented tables
FRAGMENTED_TABLES="$( mysql -e 'use information_schema; SELECT TABLE_SCHEMA,TABLE_NAME \
FROM TABLES WHERE TABLE_SCHEMA NOT IN ("information_schema","mysql") AND \
Data_free > 0' | grep -v "^+" | sed "s,\t,.," )"

for fragment in $FRAGMENTED_TABLES; do
   database="$( echo $fragment | cut -d. -f1 )"
   table="$( echo $fragment | cut -d. -f2 )"
   [ $fragment != "TABLE_SCHEMA.TABLE_NAME" ] && mysql -e "USE $database;\
   OPTIMIZE TABLE $table;" > /dev/null 2>&1
done