Are you still doing all your Linux container management using an insecure, bloated daemon? Well, don’t feel bad. I was too until recently. Now I’m finding myself saying goodbye to my beloved Docker daemon, and saying hello to Buildah, Podman, and Skopeo. In this article, we’ll explore the exciting new world of rootless and daemon-less Linux container tools.

 

Some History

If you are like me, Docker and containers may as well be the same word. Dockerfiles and build, pull, run, tag, and push commands of the Docker CLI have become part of my daily life as a software developer for the last few years. However, days of the monolith daemon approach to container management are pretty much coming to an end. There have been two significant factors leading to this. First, Docker and CoreOS, along with few other organizations, started Open Container Initiative (OCI) as an attempt to standardize container runtime and image format specifications in 2015. OCI efforts have since matured quite a bit. The OCI image format specification is now supported by most, if not all, mainstream image registries (e.g. Docker Hub, Amazon’s ECR, etc). The OCI runtime specification has a reference implementation in runC, and most existing container runtimes are either OCI compliant already or have OCI compliance in their roadmap.

Second, Kubernetes created CRI to start supporting vendors other than Docker for their container runtime implementation. The story behind the creation of CRI is actually pretty amusing. Essentially, in the beginning, Docker was the only supported runtime in Kubernetes. Naturally, CoreOS wanted rkt support in Kubernetes, and therefore submitted a pull request that basically had a bunch of “if some-flag do rkt else do docker“. Kubernetes saw this approach breaking the open-closed principle and decided to do the right thing by creating CRI as a high-level abstraction to be implemented by vendors like Docker, CoreOS, and whoever else. 

Meanwhile, the landscape of the container ecosystem changed in one significant way. Docker had enjoyed its status as the de facto runtime and image format standard in Linux containers from 2013 until about 2017. Their image format and runtime were constants in an otherwise heated war in the container orchestration space. However, since 2017 or so, Kubernetes has decidedly won that war and become the de facto standard in container orchestration.

All of these developments mean Docker is suddenly finding itself drifting away from the center of the Linux container universe. Instead of enjoying other projects orbiting its enormous gravity well, Docker is slowly finding itself becoming one of the many others orbiting the gravity well of Kubernetes. This excites some people who always saw the monolith daemon that required root access for everything as a problem. This brings us to the heart of this article – the daemon-less and largely rootless suite of container management tools.

New Generation of Container Management Tools

Buildah builds, Podman runs, and Skopeo transfers container images. These open-source tools are maintained by the containers organization on Github. There is some overlap in functionality between Buildah and Podman, but the separation of core responsibilities is clear. None of these tools require a running daemon, and for the most part, don’t need root access either, with a few exceptions. So, having set the stage, I’ll spend the rest of the article sharing how I quickly ramped up on the three tools using my recent dummy application, Task Master, from my Gophin Off with Linked Lists series. You don’t need to go through that series for this article. Task Master is a dumb little web app written in Go that I will containerize and play with using these tools. Grab the source code here.

Before we get started, one caveat with these tools is that they are very much Linux tools. This should be obvious given they are dealing with Linux containers. After all, Docker itself is specific to Linux. However, Docker does a good job of hiding behind a VM in MacOS and Windows to give those user bases a seamless experience. These tools, being relatively new on the block, don’t yet have those conveniences. You will need to grab a Linux VM if you want to play with them on your Windows or MacOS machines. OK, let’s get started.

Containerizing using Docker

I wrote the Task Master web app trying to learn Golang. So, I haven’t yet bothered containerizing it yet. Before jumping in to the new tools, let’s containerize the app using good ol’ Docker. Dockerfile for a simple Go app is pretty straightforward

1

2

3

4

5

6

7

8
FROM scratch

 

LABEL maintainer="Saharsh Singh"

 

COPY taskmaster /

 

EXPOSE 8080

ENTRYPOINT [ "/taskmaster" ]

The Dockerfile assumes there is a standalone executable already present in the top level of the Dockerfile context. It copies it to the root directory of the container file system. It advertises 8080 as a port to expose since that’s the port where Task Master listens for incoming HTTP requests. Finally, the copied over Task Master executable is set as the entry point. So now I need to build the executable for Task Master so that the Docker build can run. To keep things seamless, I’ll go ahead and write a build script so that I can just run one command to build both the Go executable as well as the Docker image.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18
#!/bin/bash

 

# Define script constants

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

 

# Change directory to code location

set -e

pushd $SCRIPT_DIR

trap popd EXIT INT TERM

 

# Test code

go test ./...

 

# Build Go binary

time GOOS=linux CGO_ENABLED=0 go build

 

# Build container

docker build -t saharshsingh/taskmaster .

I still haven’t figured out how to run the Go test and build commands safely while being in another directory. The compiler seems to have weird issues with fully qualified file paths. So that’s why you see the pushd, trap, and popd business here. Otherwise the steps are straightforward.

  • Run unit tests.
  • Build the standalone executable targeting Linux.
  • Build the container image.

Alright, it’s time to run this bad boy.

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

32

33

34
[sahsingh@sahsingh gophinoff]$ ./taskmaster/build.sh

~/go/src/github.com/saharshsingh/gophinoff/taskmaster ~/go/src/github.com/saharshsingh/gophinoff

?    github.com/saharshsingh/gophinoff/taskmaster    [no test files]

?    github.com/saharshsingh/gophinoff/taskmaster/http    [no test files]

ok      github.com/saharshsingh/gophinoff/taskmaster/impl    (cached)

 

real    0m0.640s

user    0m0.655s

sys  0m0.216s

Sending build context to Docker daemon  6.901MB

Step 1/5 : FROM scratch

--->

Step 2/5 : LABEL maintainer="Saharsh Singh"

---> Running in 565c237d6254

Removing intermediate container 565c237d6254

---> c21fc573607e

Step 3/5 : COPY taskmaster /

---> ee354893b8d4

Step 4/5 : EXPOSE 8080

---> Running in dce9a0919d54

Removing intermediate container dce9a0919d54

---> 586259b53a99

Step 5/5 : ENTRYPOINT [ "/taskmaster" ]

---> Running in 05f3b3249c44

Removing intermediate container 05f3b3249c44

---> 58e6754cfcb0

Successfully built 58e6754cfcb0

Successfully tagged saharshsingh/taskmaster:latest

~/go/src/github.com/saharshsingh/gophinoff

Saharshs-MBP:gophinoff ssingh$

Saharshs-MBP:gophinoff ssingh$

Saharshs-MBP:gophinoff ssingh$ docker images

REPOSITORY                                            TAG              IMAGE ID            CREATED SIZE

saharshsingh/taskmaster                                  latest 58e6754cfcb0 About a minute ago   6.87MB

Great! Do note the awesomely minimal container image size of just 6M. Go Go! Alright, let’s do some light testing.

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

32
[sahsingh@sahsingh gophinoff]$ docker run -d -p 8080:8080 --name taskmaster saharshsingh/taskmaster

5f38d3f13314717a283f60485edcb15a225818e8391c3fff7e9856620c225d29

Saharshs-MBP:gophinoff ssingh$

Saharshs-MBP:gophinoff ssingh$

Saharshs-MBP:gophinoff ssingh$ curl -i localhost:8080/task && echo

HTTP/1.1 200 OK

Content-Type: application/json

Date: Fri, 18 Jan 2019 18:47:22 GMT

Content-Length: 4

 

null

[sahsingh@sahsingh gophinoff]$ curl -i localhost:8080/task -XPOST -H "Content-Type: application/json" -d '{"Name":"Some Task"}' && echo

HTTP/1.1 201 Created

Date: Fri, 18 Jan 2019 18:48:04 GMT

Content-Length: 0

 

 

[sahsingh@sahsingh gophinoff]$ curl -i localhost:8080/task && echo

HTTP/1.1 200 OK

Content-Type: application/json

Date: Fri, 18 Jan 2019 18:48:07 GMT

Content-Length: 50

 

{"Name":"Some Task","Description":"","Priority":0}

[sahsingh@sahsingh gophinoff]$ docker stop taskmaster && docker rm taskmaster && docker rmi saharshsingh/taskmaster

taskmaster

taskmaster

Untagged: saharshsingh/taskmaster:latest

Deleted: sha256:58e6754cfcb0124a7475b10986cac9600eb5eee0308adf7ff011be7eb0fe31e8

Deleted: sha256:586259b53a994ff7d9b6af31d6fcf9016ab9a8ab0ca54ba56652dcb26b8baeac

Deleted: sha256:ee354893b8d47c592551a80dccc8254d816452d9750b6415c1b444a9029aae27

Deleted: sha256:223399315075be73ac5e761f349e7d223859ee35ff4c0c08658af236bdbb1c1d

OK, so that’s just plain old Docker. Knowing that things work, let’s convert this over to use our new tools. 

Installing the tools

The installation of these tools is very straightforward. Just grab your Linux package manager and install the buildah, podman, and skopeo packages. Being on Fedora, I use dnf.

1
[sahsingh@sahsingh gophinoff]$ sudo dnf -y install buildah podman skopeo

Buildah Using Dockerfiles

So the cool thing about migrating to Buildah is that it supports Dockerfiles. So the easiest thing to do here is to use Buildah’s bud (short for build-using-dockerfile) command.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20
[sahsingh@sahsingh gophinoff]$ buildah bud -t saharshsingh/taskmaster taskmaster

STEP 1: FROM scratch

STEP 2: LABEL maintainer="Saharsh Singh"

STEP 3: COPY taskmaster /

STEP 4: EXPOSE 8080

STEP 5: ENTRYPOINT [ "/taskmaster" ]

STEP 6: COMMIT containers-storage:[vfs@/home/sahsingh/.local/share/containers/storage+/run/user/1000:overlay.mount_program=/usr/bin/fuse-overlayfs]localhost/saharshsingh/taskmaster:latest

Getting image source signatures

Copying blob sha256:258da81a6b2aba1003628a44cec752cf44067f74a299abe1c636acd25d8654c5

3.30 MiB / 3.30 MiB [======================================================] 0s

Copying config sha256:9d82e14463d8eec0c408a5245435f3fd74ca0055243a96c2cb76d49d49ff93ce

438 B / 438 B [============================================================] 0s

Writing manifest to image destination

Storing signatures

--> 9d82e14463d8eec0c408a5245435f3fd74ca0055243a96c2cb76d49d49ff93ce

[sahsingh@sahsingh gophinoff]$

[sahsingh@sahsingh gophinoff]$

[sahsingh@sahsingh gophinoff]$ buildah images

IMAGE NAME IMAGE TAG IMAGE ID SIZE

localhost/saharshsingh/taskmaster latest 9d82e14463d8 6.87 MB

Note how only one image layer, as opposed to one layer per step in Docker, is created. And just like that, we have our image. Do note that I am using the buildah images command to see my image. At this point I have, in fact, gone ahead and uninstalled Docker on my machine completely. This image is also saved underneath my home directory instead of /var/lib. What this means is this image is local to my user on this machine. If I run buildah images as any other user on this machine, I won’t see that image. A quick way to illustrate this is using sudo to build the image as root.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22
[sahsingh@sahsingh gophinoff]$ buildah rmi 9d8

9d82e14463d8eec0c408a5245435f3fd74ca0055243a96c2cb76d49d49ff93ce

[sahsingh@sahsingh gophinoff]$ sudo buildah bud -t saharshsingh/taskmaster taskmaster

[sudo] password for sahsingh:

STEP 1: FROM scratch

STEP 2: LABEL maintainer="Saharsh Singh"

STEP 3: COPY taskmaster /

STEP 4: EXPOSE 8080

STEP 5: ENTRYPOINT [ "/taskmaster" ]

STEP 6: COMMIT containers-storage:[overlay@/var/lib/containers/storage+/var/run/containers/storage:overlay.mountopt=nodev,overlay.override_kernel_check=true]localhost/saharshsingh/taskmaster:latest

Getting image source signatures

Copying blob sha256:258da81a6b2aba1003628a44cec752cf44067f74a299abe1c636acd25d8654c5

3.30 MiB / 3.30 MiB [======================================================] 0s

Copying config sha256:207306a078e79c60650e68acc10ac7ea2944e2d5e79a1bdddbed9c50937499c1

438 B / 438 B [============================================================] 0s

Writing manifest to image destination

Storing signatures

--> 207306a078e79c60650e68acc10ac7ea2944e2d5e79a1bdddbed9c50937499c1

[sahsingh@sahsingh gophinoff]$ buildah images

[sahsingh@sahsingh gophinoff]$ sudo buildah images

IMAGE NAME IMAGE TAG IMAGE ID SIZE

localhost/saharshsingh/taskmaster latest 207306a078e7 6.87 MB

Note how buildah images show no images while sudo buildah images shows the image we just built.

Buildah beyond Dockerfiles

Buildah is much more than just a third party tool to process Dockerfiles. Buildah allows you to build container images one step at a time interactively. It does this by spawning an instance of the container from some base image. You can then use this container to execute all the necessary steps to get to your final image or some intermediate layer. Once you are done with a layer of the build, you can commit the container up to that point as an image tag to Buildah, and restart the process from that tag as the base image. Once you are completely done, commit the final tag and remove the working containers.

To see this in practice, let’s recreate the way Docker built our image using Buildah. First, we spawn a working container using the scratch image.

1

2

3

4

5

6
[sahsingh@sahsingh gophinoff]$ buildah from scratch

working-container

[sahsingh@sahsingh gophinoff]$ buildah containers

CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME

41b2e348f98a * scratch working-container

[sahsingh@sahsingh gophinoff]$

Alright, so we have our container instance named working-container. Next, let’s create the maintainer label, commit the change, and spawn another intermediate container.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
[sahsingh@sahsingh gophinoff]$ buildah config --label "maintainer=Saharsh Singh" working-container

[sahsingh@sahsingh gophinoff]$ buildah commit working-container tm-intermediate-1

Getting image source signatures

Copying blob sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1

32 B / 32 B [==============================================================] 0s

Copying config sha256:4786863027c87a85710f432101cc04157fccbe51aee9bf9de3695bf87623f7c3

302 B / 302 B [============================================================] 0s

Writing manifest to image destination

Storing signatures

4786863027c87a85710f432101cc04157fccbe51aee9bf9de3695bf87623f7c3

[sahsingh@sahsingh gophinoff]$ buildah from tm-intermediate-1

tm-intermediate-1-working-container

[sahsingh@sahsingh gophinoff]$ buildah containers

CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME

41b2e348f98a * scratch working-container

fb6c8f1f72c7 * 4786863027c8 localhost/tm-intermediate-1:latest tm-intermediate-1-working-container

[sahsingh@sahsingh gophinoff]$ buildah rm working-container

41b2e348f98a859821095787804d97b1d80332e7af36717e92fe7b44b4041bd5

[sahsingh@sahsingh gophinoff]$

So in true docker build fashion, we execute a step, capture that step in an intermediate layer, spawn a new working container from the intermediate layer to execute the next step, and finally remove the first working container. We can repeat the process to execute the rest of the steps.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16
# Dockerfile step 2: COPY taskmaster /

[sahsingh@sahsingh gophinoff]$ buildah copy tm-intermediate-1-working-container taskmaster/taskmaster /

[sahsingh@sahsingh gophinoff]$ buildah commit tm-intermediate-1-working-container tm-intermediate-2

[sahsingh@sahsingh gophinoff]$ buildah rm tm-intermediate-1-working-container

 

# Dockerfile step 3: EXPOSE 8080

[sahsingh@sahsingh gophinoff]$ buildah from tm-intermediate-2

[sahsingh@sahsingh gophinoff]$ buildah config --port 8080 tm-intermediate-2-working-container

[sahsingh@sahsingh gophinoff]$ buildah commit tm-intermediate-2-working-container tm-intermediate-3

[sahsingh@sahsingh gophinoff]$ buildah rm tm-intermediate-2-working-container

 

# Dockerfile step 4: ENTRYPOINT [ "/taskmaster" ]

[sahsingh@sahsingh gophinoff]$ buildah from tm-intermediate-3

[sahsingh@sahsingh gophinoff]$ buildah config --entrypoint '["/taskmaster"]' tm-intermediate-3-working-container

[sahsingh@sahsingh gophinoff]$ buildah commit tm-intermediate-3-working-container saharshsingh/taskmaster

[sahsingh@sahsingh gophinoff]$ buildah rm tm-intermediate-3-working-container

I removed the output of the commands above for brevity. Instead, we can look at the output of the commands below for the big picture.

1

2

3

4

5

6

7

8
[sahsingh@sahsingh gophinoff]$ buildah containers

CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME

[sahsingh@sahsingh gophinoff]$ buildah images

IMAGE NAME IMAGE TAG IMAGE ID CREATED AT SIZE

localhost/tm-intermediate-1 latest 4786863027c8 Jan 18, 2019 17:14 1.67 KB

localhost/tm-intermediate-2 latest 26ac01201862 Jan 18, 2019 17:22 6.88 MB

localhost/tm-intermediate-3 latest 574d0f313b8d Jan 18, 2019 17:25 6.88 MB

localhost/saharshsingh/taskmaster latest ff407f6aaa8e Jan 18, 2019 17:28 6.88 MB

Now, this is overkill. I do not need to keep each step separated in its own image layer. This is to illustrate the flexibility allowed by buildah. In practice, I would likely want to put all these steps in the same layer. I also would like to script these steps so that they become part of my build file.

1

2

3

4

5
tm_container=$(buildah from scratch)

buildah copy $tm_container taskmaster /

buildah config --port 8080 --entrypoint '["/taskmaster"]' --label "maintainer=Saharsh Singh" $tm_container

buildah commit $tm_container saharshsingh/taskmaster

buildah rm $tm_container

That does the job. Do also note how I can combine all my config steps in one command. Now to incorporate this into my build file, I will create a flag called –using-buildah that the caller can supply to build using buildah. By default, I can have my script default to Docker.

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

32

33

34

35

36

37

38

39

40
#!/bin/bash

 

cleanup() {

 

    # Remove working container from buildah

    if [ "$tm_container" != "" ]; then buildah rm $tm_container; fi

 

    # Go back to user directory

    popd

}

 

# Define script constants

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

 

# Process CLI args

USE_BUILDAH=0

if [ "$1" == "--using-buildah" ]; then

    USE_BUILDAH=1

fi

 

# Change directory to code location

set -e

pushd $SCRIPT_DIR

trap cleanup EXIT INT TERM

 

# Test code

go test ./...

 

# Build Go binary

time GOOS=linux CGO_ENABLED=0 go build

 

# Build container

if [ $USE_BUILDAH -eq 1 ]; then

    tm_container=$(buildah from scratch)

    buildah copy $tm_container taskmaster /

    buildah config --port 8080 --entrypoint '["/taskmaster"]' --label "maintainer=Saharsh Singh" $tm_container

    buildah commit $tm_container saharshsingh/taskmaster

else

    docker build -t saharshsingh/taskmaster $SCRIPT_DIR

fi

Note how the last line that removes the working container has been moved to a cleanup function called by the trap. This allows the working container to be removed in case one of the intermediate buildah steps fails. Now we can run the buildah flow in the build script using the –using-buildah flag.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21
[sahsingh@sahsingh gophinoff]$ ./taskmaster/build.sh --using-buildah

~/go/src/github.com/saharshsingh/gophinoff/taskmaster ~/go/src/github.com/saharshsingh/gophinoff

?    github.com/saharshsingh/gophinoff/taskmaster    [no test files]

?    github.com/saharshsingh/gophinoff/taskmaster/http    [no test files]

ok      github.com/saharshsingh/gophinoff/taskmaster/impl    (cached)

 

real    0m0.067s

user    0m0.091s

sys  0m0.025s

0a54a535c63f8e3f9bc5cde935f23fc9eaca8c6149d49332b8fff770732a1dad

Getting image source signatures

Copying blob sha256:fd2ce6fd51eb18f8842e7ae325b4db767dd69d97d19a107f649ab9edafe86c79

3.30 MiB / 3.30 MiB [======================================================] 0s

Copying config sha256:5d410baead92137dcd770421d28ad66325c4680a28f169f4a06eb43219de05c7

356 B / 356 B [============================================================] 0s

Writing manifest to image destination

Storing signatures

5d410baead92137dcd770421d28ad66325c4680a28f169f4a06eb43219de05c7

18848d23f1fc500e84563d2139bc6a5c7a4628cd9f3c5cde1005a045723c1426

~/go/src/github.com/saharshsingh/gophinoff

[sahsingh@sahsingh gophinoff]$

Awesome! So now that we can build them, it’s time to run our container using Podman.

Running Containers with Podman

Podman is pretty straightforward. For all your docker run needs, replace docker with podman. So the container I just built can easily be run using podman.

1

2

3

4

5
[sahsingh@sahsingh gophinoff]$ podman run -d --name taskmaster saharshsingh/taskmaster

e10b1bc3c6529d2dfee6529458551cb27911e0964ccea5fdd04c13c0d2da509e

[sahsingh@sahsingh gophinoff]$ podman ps

CONTAINER ID  IMAGE                          COMMAND      CREATED  STATUS          PORTS  NAMES

e10b1bc3c652  localhost/saharshsingh/taskmaster:latest  /taskmaster 22 seconds ago Up 22 seconds ago      taskmaster

Just like buildah, podman also keeps containers user-specific.

1

2

3

4
[sahsingh@sahsingh gophinoff]$ sudo podman ps

[sudo] password for sahsingh:

CONTAINER ID  IMAGE COMMAND  CREATED STATUS PORTS  NAMES

[sahsingh@localhost gophinoff]$

Great! Next, we would ideally want to test to make sure podman is actually running the container instance. However, there is one minor issue. Unfortunately, you can’t do port bindings as a non-root user. This means I can’t expose the port on which Task Master listens for requests outside the container.

1

2
[sahsingh@sahsingh gophinoff]$ podman run -d -p 8080:8080 --name taskmaster saharshsingh/taskmaster

port bindings are not yet supported by rootless containers

So to run my app so that it can be of use, I need to run it via podman as a root user. Now I have two options; I can either rebuild the image as root using buildah, or I can do something unnecessarily complicated. Let’s do the latter. I will run a Docker registry locally exposed on port 5000. Then I can push my image using buildah as sahsingh user to this registry, so that podman, run as root, can pull it.

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
[sahsingh@sahsingh gophinoff]$ sudo podman run -d -p 5000:5000 --name registry registry

Trying to pull docker.io/registry:latest...Getting image source signatures

Copying blob cd784148e348: 2.10 MiB / 2.10 MiB [============================] 1s

Copying blob 0ecb9b11388e: 611.83 KiB / 611.83 KiB [========================] 1s

Copying blob 45793cf0ff93: 6.51 MiB / 6.51 MiB [============================] 1s

Copying blob d7eadb9e7675: 370 B / 370 B [==================================] 1s

Copying blob 4b2356bbbed3: 213 B / 213 B [==================================] 1s

Copying config 116995fd6624: 3.09 KiB / 3.09 KiB [==========================] 0s

Writing manifest to image destination

Storing signatures

de85373e682a139aba705450a68a6d0d4fd1273d74bf228493a1d4a510e23a16

[sahsingh@sahsingh gophinoff]$ buildah tag saharshsingh/taskmaster localhost:5000/saharshsingh/taskmaster

[sahsingh@sahsingh gophinoff]$ buildah push localhost:5000/saharshsingh/taskmaster

Getting image source signatures

Copying blob sha256:2f9f39ea93e54364337f35def7b0f04e39b2c1590a988adaae5ec739841c15f6

6.56 MiB / 6.56 MiB [======================================================] 0s

Copying config sha256:5d410baead92137dcd770421d28ad66325c4680a28f169f4a06eb43219de05c7

356 B / 356 B [============================================================] 0s

Writing manifest to image destination

Storing signatures

Successfully pushed //localhost:5000/saharshsingh/taskmaster:latest@sha256:9a61d636eebd230812d916dc849bc79e86b51fd8ea71a5c6fca9c897ce9a953c

[sahsingh@sahsingh gophinoff]$ sudo podman pull localhost:5000/saharshsingh/taskmaster

Trying to pull localhost:5000/saharshsingh/taskmaster...Getting image source signatures

Copying blob fd2ce6fd51eb: 3.30 MiB / 3.30 MiB [============================] 0s

Copying config 5d410baead92: 356 B / 356 B [================================] 0s

Writing manifest to image destination

Storing signatures

5d410baead92137dcd770421d28ad66325c4680a28f169f4a06eb43219de05c7

Great! Now we can run and test the image.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22
[sahsingh@sahsingh gophinoff]$ sudo podman run -d -p 8080:8080 --name taskmaster saharshsingh/taskmaster

c98ee6cccef9a9103afad7d3958efe93a4ad085ae424e6e98e59d44901dd9488

[sahsingh@localhost gophinoff]$ curl -i localhost:8080/task && echo

HTTP/1.1 200 OK

Content-Type: application/json

Date: Mon, 21 Jan 2019 06:10:52 GMT

Content-Length: 4

 

null

[sahsingh@localhost gophinoff]$ curl -i localhost:8080/task -XPOST -H "Content-Type: application/json" -d '{"Name":"Some Task"}' && echo

HTTP/1.1 201 Created

Date: Mon, 21 Jan 2019 06:11:06 GMT

Content-Length: 0

 

 

[sahsingh@sahsingh gophinoff]$ curl -i localhost:8080/task && echo

HTTP/1.1 200 OK

Content-Type: application/json

Date: Mon, 21 Jan 2019 06:11:30 GMT

Content-Length: 50

 

{"Name":"Some Task","Description":"","Priority":0}

Runs just like before and all without a daemon!

Skopeo

Finally, let’s look at skopeo. Skopeo is all about working with images in remote repositories – transferring them, inspecting them, and even deleting them. So, let’s use skopeo to transfer the Task Master image from the local Docker registry I am running using podman to my official Docker Hub account. First, let’s inspect

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20
[sahsingh@sahsingh gophinoff]$ skopeo inspect docker://localhost:5000/saharshsingh/taskmaster

{

    "Name": "localhost:5000/saharshsingh/taskmaster",

    "Digest": "sha256:9a61d636eebd230812d916dc849bc79e86b51fd8ea71a5c6fca9c897ce9a953c",

    "RepoTags": [

        "latest"

    ],

    "Created": "2019-01-21T05:31:53.86215492Z",

    "DockerVersion": "",

    "Labels": {

        "maintainer": "Saharsh Singh"

    },

    "Architecture": "amd64",

    "Os": "linux",

    "Layers": [

        "sha256:fd2ce6fd51eb18f8842e7ae325b4db767dd69d97d19a107f649ab9edafe86c79"

    ]

}

[sahsingh@sahsingh gophinoff]$ skopeo inspect docker://saharshsingh/taskmaster

FATA[0000] Error reading manifest latest in docker.io/saharshsingh/taskmaster: manifest unknown: manifest unknown

Seeing how I have the image at localhost:5000 but not at Docker Hub, let’s do a copy. I have replaced my actual username and password below with a stub user:pass.

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
[sahsingh@sahsingh gophinoff]$ skopeo copy --dest-creds=user:pass docker://localhost:5000/saharshsingh/taskmaster docker://saharshsingh/taskmaster

Getting image source signatures

Copying blob fd2ce6fd51eb: 3.30 MiB / 3.30 MiB [============================] 2s

Copying config 5d410baead92: 356 B / 356 B [================================] 1s

Writing manifest to image destination

Copying config 5d410baead92: 0 B / 356 B [----------------------------------] 0s

Writing manifest to image destination

Storing signatures

[sahsingh@sahsingh gophinoff]$ skopeo inspect docker://saharshsingh/taskmaster

{

    "Name": "docker.io/saharshsingh/taskmaster",

    "Digest": "sha256:f4044c55bfda0ac2c74547f6fe8d7b5bbf4520974f6f342ca62cce379c1ce16f",

    "RepoTags": [

        "latest"

    ],

    "Created": "2019-01-21T05:31:53.86215492Z",

    "DockerVersion": "",

    "Labels": {

        "maintainer": "Saharsh Singh"

    },

    "Architecture": "amd64",

    "Os": "linux",

    "Layers": [

        "sha256:fd2ce6fd51eb18f8842e7ae325b4db767dd69d97d19a107f649ab9edafe86c79"

    ]

}

Boom!

End

So with that, this is it for this blog entry. I hope you enjoyed learning about the new set of daemonless and rootless container tooling. Till next time…

Connect with Red Hat Services
Learn more about Red Hat Consulting
Learn more about Red Hat Training
Join the Red Hat Learning Community
Learn more about Red Hat Certification
Subscribe to the Training Newsletter
Follow Red Hat Services on Twitter
Follow Red Hat Open Innovation Labs on Twitter
Like Red Hat Services on Facebook
Watch Red Hat Training videos on YouTube
Follow Red Hat Certified Professionals on LinkedIn