What is a container

Linux container technology

A Linux container is a process with specific isolation provided by the Linux Kernel. Linux container technology allows you to package or isolate your application and its entire runtime environment (including all required files) together. This allows users to easily migrate applications between environments while retaining the full functionality of the application.

Docker’s problem

Docker is a famous open source container engine, which has become almost synonymous with containers now that container technology is gradually gaining popularity.

Docker itself is also a packaging of Linux container technology, through and provide users with a simple interface, so that users are very convenient to package and use containers.

As the mainstream container engine, Docker has a wealth of scenarios and solutions, but also has some problems.

  1. Docker needs to run a daemon, and all containers are children of the daemon
  2. Docker needs root identity to run the daemon

This may seem like a no-brainer, but if you try to use Docker at scale you will find that

  1. daemons are not as stable as you might think
  2. the OOM of one container is likely to drag down the parent process and affect neighboring containers
  3. The Docker process tree has some oddities, and you can’t be sure if it’s a Docker bug or something the Kernel has done
  4. The Docker container is the process started by root.

If we go in a different direction, is the daemon really necessary?

What is Podman

Podman was once part of the CRI-O project, but was later spun off as a separate project: libpod. Podman’s goal is to provide a Container CLI similar to Docker (even the official direct It is recommended to use: alias docker=podman).

Installation

Installing Podman is very easy, the installation documentation: https://github.com/containers/libpod/blob/master/install.md

MacOS

Using Homebrew:

1
brew cask install podman

Fedora, CentOS

1
sudo yum -y install podman

Ubuntu(development versions)

1
2
3
4
5
sudo apt-get update -qq
sudo apt-get install -qq -y software-properties-common uidmap
sudo add-apt-repository -y ppa:projectatomic/ppa
sudo apt-get update -qq
sudo apt-get -qq -y install podman

Use

The following experiments are based on Podman V1.4.4.

1
2
3
4
5
# podman version
Version:            1.4.4
RemoteAPI Version:  1
Go Version:         go1.10.3
OS/Arch:            linux/amd64

Pulling images

Podman will pull the registry.access.redhat.com image first by default. If the pull fails, Podman will try the docker.io image again.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# podman pull nginx
Trying to pull registry.access.redhat.com/nginx...ERRO[0001] Error pulling image ref //registry.access.redhat.com/nginx:latest: Error initializing source docker://registry.access.redhat.com/nginx:latest: Error reading manifest latest in registry.access.redhat.com/nginx: name unknown: Repo not found
Failed
Trying to pull docker.io/library/nginx...Getting image source signatures
Copying blob 7acba7289aa3 done
Copying blob b8f262c62ec6 done
Copying blob e9218e8f93b1 done
Copying config f949e7d76d done
Writing manifest to image destination
Storing signatures
f949e7d76d63befffc8eec2cbf8a6f509780f96fb3bacbdc24068d594a77f043

Podman’s data path is under /var/lib/containers, similar to Docker, which holds data such as layers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[root@podman-test-vm lib]# tree /var/lib/containers/ -L 2
/var/lib/containers/
├── cache
│   └── blob-info-cache-v1.boltdb
└── storage
    ├── libpod
    ├── mounts
    ├── overlay
    ├── overlay-containers
    ├── overlay-images
    ├── overlay-layers
    ├── storage.lock
    └── tmp

You can see the information of the images you just pulled.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@podman-test-vm lib]# cat /var/lib/containers/storage/overlay-images/images.json | python -m json.tool
[
    {
        "big-data-digests": {
            "manifest": "sha256:066edc156bcada86155fd80ae03667cf3811c499df73815a2b76e43755ebbc76",
            "manifest-sha256:066edc156bcada86155fd80ae03667cf3811c499df73815a2b76e43755ebbc76": "sha256:066edc156bcada86155fd80ae03667cf3811c499df73815a2b76e43755ebbc76",
            "sha256:f949e7d76d63befffc8eec2cbf8a6f509780f96fb3bacbdc24068d594a77f043": "sha256:f949e7d76d63befffc8eec2cbf8a6f509780f96fb3bacbdc24068d594a77f043"
        },
        "big-data-names": [
            "sha256:f949e7d76d63befffc8eec2cbf8a6f509780f96fb3bacbdc24068d594a77f043",
            "manifest-sha256:066edc156bcada86155fd80ae03667cf3811c499df73815a2b76e43755ebbc76",
            "manifest"
        ],
        "big-data-sizes": {
            "manifest": 948,
            "manifest-sha256:066edc156bcada86155fd80ae03667cf3811c499df73815a2b76e43755ebbc76": 948,
            "sha256:f949e7d76d63befffc8eec2cbf8a6f509780f96fb3bacbdc24068d594a77f043": 6669
        },
        "created": "2019-09-24T23:33:17.034191345Z",
        "digest": "sha256:066edc156bcada86155fd80ae03667cf3811c499df73815a2b76e43755ebbc76",
        "id": "f949e7d76d63befffc8eec2cbf8a6f509780f96fb3bacbdc24068d594a77f043",
        "layer": "ea345052c98934e4e4673b2d359b5000a9ff1cc7f0332df0d406980f172deea6",
        "metadata": "{}",
        "names": [
            "docker.io/library/nginx:latest"
        ]
    }
]

Starting containers

Most of Podman’s commands are compatible with Docker, so you can start containers in a similar way.

1
2
3
4
5
6
[root@podman-test-vm ~]# podman run -p 80:80 --name=web -d nginx
9d284597eeedbbdfb4df933e063fe1035cbd39f1e712173f7a8a3652773eac02

[root@podman-test-vm ~]# podman ps
CONTAINER ID  IMAGE                           COMMAND               CREATED         STATUS             PORTS               NAMES
9d284597eeed  docker.io/library/nginx:latest  nginx -g daemon o...  48 seconds ago  Up 48 seconds ago  0.0.0.0:80->80/tcp  web

Try checking the processes of nginx.

1
2
3
4
[root@podman-test-vm ~]# ps -ef | grep nginx
root      2518  2508  0 12:01 ?        00:00:00 nginx: master process nginx -g daemon off;
101       2529  2518  0 12:01 ?        00:00:00 nginx: worker process
root      2637  1259  0 12:10 pts/0    00:00:00 grep --color=auto nginx

Then view the process tree based on the pid.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
[root@podman-test-vm ~]# pstree -H 2518
systemd─┬─NetworkManager─┬─2*[dhclient]
        │                └─2*[{NetworkManager}]
        ├─anacron
        ├─auditd───{auditd}
        ├─conmon─┬─nginx───nginx
        │        └─{conmon}
        ├─crond
        ├─dbus-daemon───{dbus-daemon}
        ├─firewalld───{firewalld}
        ├─login───bash
        ├─lvmetad
        ├─master─┬─pickup
        │        └─qmgr
        ├─polkitd───5*[{polkitd}]
        ├─rsyslogd───2*[{rsyslogd}]
        ├─sshd───sshd───bash───pstree
        ├─systemd-journal
        ├─systemd-logind
        ├─systemd-udevd
        └─tuned───4*[{tuned}]

View the parent process based on ppid.

1
2
3
4
[root@podman-test-vm ~]# ps -ef | grep 2508
root      2508     1  0 12:01 ?        00:00:00 /usr/libexec/podman/conmon -s -c 9d284597eeedbbdfb4df933e063fe1035cbd39f1e712173f7a8a3652773eac02 -u 9d284597eeedbbdfb4df933e063fe1035cbd39f1e712173f7a8a3652773eac02 -n web -r /usr/bin/runc -b /var/lib/containers/storage/overlay-containers/9d284597eeedbbdfb4df933e063fe1035cbd39f1e712173f7a8a3652773eac02/userdata -p /var/run/containers/storage/overlay-containers/9d284597eeedbbdfb4df933e063fe1035cbd39f1e712173f7a8a3652773eac02/userdata/pidfile --exit-dir /var/run/libpod/exits --exit-command /usr/bin/podman --exit-command-arg --root --exit-command-arg /var/lib/containers/storage --exit-command-arg --runroot --exit-command-arg /var/run/containers/storage --exit-command-arg --log-level --exit-command-arg error --exit-command-arg --cgroup-manager --exit-command-arg systemd --exit-command-arg --tmpdir --exit-command-arg /var/run/libpod --exit-command-arg --runtime --exit-command-arg runc --exit-command-arg --storage-driver --exit-command-arg overlay --exit-command-arg container --exit-command-arg cleanup --exit-command-arg 9d284597eeedbbdfb4df933e063fe1035cbd39f1e712173f7a8a3652773eac02 --socket-dir-path /var/run/libpod/socket -l k8s-file:/var/lib/containers/storage/overlay-containers/9d284597eeedbbdfb4df933e063fe1035cbd39f1e712173f7a8a3652773eac02/userdata/ctr.log --log-level error
root      2518  2508  0 12:01 ?        00:00:00 nginx: master process nginx -g daemon off;
root      2639  1259  0 12:10 pts/0    00:00:00 grep --color=auto 2508

As you can see, podman starts the container through podman/conmon, and this process is hooked under pid 1, which is systemd.

podman/conmon is Podman’s initiator and is responsible for two main functions: monitoring runc and managing containers with runc’s capabilities, and establishing communication with Podman and passing instructions for container operations.

Podman does not communicate with using the CRI protocol. Instead, Podman creates containers using runc, and manages storage using containers/storage. Technically, Podman launches conmon which launches and monitors the OCI Runtime (runc). Podman can exit and later reconnect to conmon to talk to the container. Runc stops running once the container starts.

《Crictl Vs Podman》:https://blog.openshift.com/crictl-vs-podman/

Build images

Podman can be built directly using Dockerfile.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@podman-test-vm ~]# git clone https://github.com/DaoCloud/dao-2048.git
正克隆到 'dao-2048'...
remote: Enumerating objects: 116, done.
remote: Total 116 (delta 0), reused 0 (delta 0), pack-reused 116
接收对象中: 100% (116/116), 304.76 KiB | 60.00 KiB/s, done.
处理 delta 中: 100% (40/40), done.
[root@podman-test-vm ~]# podman build dao-2048/
STEP 1: FROM daocloud.io/nginx:1.11-alpine
Getting image source signatures
Copying blob ed383a1b82df done
Copying blob c92260fe6357 done
Copying blob 4b21d71b440a done
Copying blob 709515475419 done
Copying config bedece1f06 done
Writing manifest to image destination
Storing signatures
STEP 2: MAINTAINER Golfen Guo <golfen.guo@daocloud.io>
711d32f788782528ad36a0c12ae895993474b168f7f2d65158e531a924b3dd55
STEP 3: COPY . /usr/share/nginx/html
b859763deeb7eaab39fd8c34a8c8af18e8de74c80e98f9fcb1e0c694881c5e9c
STEP 4: EXPOSE 80
eedb1a66c8316a309546b21a5c687f3e3611927b54a7fa0f4dbd3f175eeb253c
STEP 5: CMD sed -i "s/ContainerID: /ContainerID: "$(hostname)"/g" /usr/share/nginx/html/index.html && nginx -g "daemon off;"
STEP 6: COMMIT
fda5a2d14a918c3eb088c28dc0d8e89e66b061923a82e8722d9e2a62c994422d

[root@podman-test-vm ~]# podman images
REPOSITORY                TAG           IMAGE ID       CREATED          SIZE
<none>                    <none>        fda5a2d14a91   22 seconds ago   56.9 MB
docker.io/library/nginx   latest        f949e7d76d63   4 days ago       130 MB
daocloud.io/nginx         1.11-alpine   bedece1f06cc   2 years ago      55.9 MB

Dockerfile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[root@podman-test-vm ~]# cat dao-2048/Dockerfile
# Using a compact OS
FROM daocloud.io/nginx:1.11-alpine

MAINTAINER Golfen Guo <golfen.guo@daocloud.io>

# Add 2048 stuff into Nginx server
COPY . /usr/share/nginx/html

EXPOSE 80

# Start Nginx and keep it running background and start php
CMD sed -i "s/ContainerID: /ContainerID: "$(hostname)"/g" /usr/share/nginx/html/index.html

Problem

Podman is committed to removing the daemon, which means that Podman cannot do the tasks that need to be done by the daemon.

Restart issues

In Docker, you can specify a restart policy with the -restart command, so that when a node restarts, the container with the restart policy will recover itself as long as dockerd is up.

Since Podman entrusts the management of containers to systemd, the official advice is to use systemd to solve this problem. describe the boot method and restart policy.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ vim /etc/systemd/system/nginx_container.service 
 
[Unit] 
Description=Podman Nginx Service 
After=network.target 
After=network-online.target 
 
[Service] 
Type=simple 
ExecStart=/usr/bin/podman start -a nginx 
ExecStop=/usr/bin/podman stop -t 10 nginx 
Restart=always 
 
[Install] 
WantedBy=multi-user.target 

Although some trouble (and feel a bit against the trend), but if you think about it, this is not the more recommended way to manage services on Linux. Secondly, although Docker supports container self-starting, it does not support sequential startup according to dependencies, but using systemd’s ability to make startup dependencies through After, you can better manage the startup order instead.