Containers 101, Part 1: Containers Defined and How to Run Them

I have published no less than two Kubernetes Series posts and not once did I dive into Containers. I linked to the great work of Nigel Poulton, but I’ve never talked about it myself.

BAD BLOGGER!

So here, I am going to:

  1. Dive somewhat into describing what containers are.
  2. Show you the basics of running a container.

In Part 2, we’ll dive deeper into using Docker files to build containers and use container registries.

My Usual Biographical Aside: The Year Bryan Descended Even Further into Existential Angst

The Year was 2015. Bryan was a hot-shot VMware Engineer without a care in the world. Then, BAM! He created his first container.

Imagine what goes through the mind of a VMware engineer who has gone all in on Hypervisor-based virtualization when he or she builds a container for the very first time:

“You mean to tell me that containers can run multiple applications all on the same machine, standing them up is trivial, and you don’t need a Hypervisor to do it? And this all runs on a singular operating system and . . . here’s the kicker . . . that operating system could be . . .

. . . . . .

BARE METAL?”

Would you be concerned about the future of what you do?

I was.

Still am.

If you have read my blog long enough, you know I believe that a little paranoia about one’s job security can go a long way. But, we’ve already talked about that.

At the risk of poking at the, “Well, acshually . . .” crowd, my point is that containers accomplish at the application layer the ability to silo resources without the need for a hypervisor. So if you’ve gone all in on virtualization and have never run or built a container, this post is for you.

What is a Container?

The ability to “isolate processes” has actually been around for decades. chroot is a form of isolating processes, as it were, which has been around since at least 1979.

But if we fast-forward to the modern implementation of containers as we leverage them now in the context of Kubernetes, we’re really talking about cgroups.

Cgroups (aka “Control Groups”) is a Linux Kernel feature released in 2008 that provides resource and process isolation where applications can run siloed from each other on the same OS. Here’s a graphic that might help that you’ve probably seen before (source: https://www.docker.com/blog/containers-replacing-virtual-machines/):

Source: docker.com

You should begin to see what made me nervous all those years ago. The “Containerized Applications” part of that graphic could run on a Bare Metal Linux OS, and in my defense, running containers/K8s on bare metal is a thing.

We are pondering bare metal for worker nodes where I work.

Anyway, in order to appreciate containers, you really have to understand application dependencies. You have to put your dev hat on for a moment.

Set aside the need for redundancy for a second, and forget about the fact that building a database in a container is not usually the greatest of ideas, but humor me on this one:

You are running a multi-tier application that needs a small, simple, and static read-only database and an nginx web server. Maybe you can run them on the same OS, maybe not. But, the bottom line is, with containers I don’t have to worry about it. I can build my (small!) database container with its packages and libraries and I can build my web server with its packages and libraries as two separate containers. The containers run decoupled from each other, and the underlying OS. This is more highly scalable. This is easier to develop over time. The containers can be more easily made redundant across OSes, not unlike with hypervisor-based virtualization.

Add all of this to a container orchestration tool, like, oh I don’t know, Kubernetes and you have what might arguably be described as the best of all possible worlds.

A Pet Peave of Mine About Running Containers on a VM for Us Non-Dev Types: Are We Still Doing Phrasing?

You’re not getting out of here without at least one rant.

One of my pet peaves is when people say that, “Containers on a VM is virtualization on top of virtualization.” I am paraphrasing here, but I have read some form of this in blogs or heard it said out loud.

It bothers me because a VM that runs on a hypervisor (KVM, ESXi, etc.) is not the same thing as cgroups. They are apples and oranges.

It is more accurate to say that they are two, “layers of abstraction.”

Furthermore, if you are unfamiliar with software engineering development (I am still learning myself), you should get used to the idea that multiple layers of abstraction and process separation is very common: chroot, venv (Python), nginx server blocks, JVM, and so on.

I say this because there was a time where I thought that too many layers of abstraction meant poor performance, and everyone should be concerned with performance, but containerization is yet another method for abstraction among many; and that multiple layers of abstraction is perfectly normal.

What’s the Story with Kubernetes and Supporting the Docker Runtime?

You may have read that Kubernetes has deprecated support for the Docker Runtime. It does not mean you can’t use docker images/containers now or in the future, it just means that the devs of Kubernetes have opted to use runtimes that leverage the Container Runtime Interface (CRI).

You may already know this, but Docker isn’t the only gig in town. The bottom line is that CRI runs the containers, no matter what is providing the container, whether it’s a docker container, LXC, or whatever.

In fact, everyone talks about Docker, but the market leader actually is LXC. For our examples here, we’ll be using docker, but feel free to explore others.

More information about Kubernetes’ announcement about the Docker Runtime is here. It should make you feel better.

The Most Important Thing You’ll Read During This Post

A super-important thing about Containers is that they are built to have the bare minimum of packages installed to do what they need to do. The VMware PowerCLI Core docker image is 673MB. That’s not small, but it isn’t huge either when you think about the size of a true-blue VM.

The Alpine Linux docker image is 5.61MB.

This minimalist approach is also an advantage with running containers. It’s more secure and better performing. It helps to avoid bloat.

On the other hand, the container(s) may not have commonly used commands like yum or vim. You will need to experiment with various images to get used to this idea (or build your own!).

These are things to remember when we get to part 2 where we will be building our own container. Think about an application you are running and think about the bare minimum it needs to run. That’s what you are shooting for.

And Finally, Let’s Run a Container!

I am going to do something super easy here. We are going to take a look at what a docker image is, use the docker command to inspect it, run it, and run commands in it.

  1. First, you will need an account on Docker Hub. Get signed up for an account. It’s free! We’ll need this for Part 2.
  2. Next, download and install Docker Desktop, or you can install docker on Linux if you like. I have never done this on a Windows machine, so if you’re running windows, YMMV. I am going to use my handy-dandy Macbook Pro here, but Linux will work too.
  3. Once Docker Desktop is installed, you should now have the docker command available:
> docker version                                                                                                                                                               
Client: Docker Engine - Community
 Cloud integration: 1.0.9
 Version:           20.10.5
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        55c4c88
 Built:             Tue Mar  2 20:13:00 2021
 OS/Arch:           darwin/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.5
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       363e9a8
  Built:            Tue Mar  2 20:15:47 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.3
  GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939b
 runc:
  Version:          1.0.0-rc92
  GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7aff
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Let’s run a container that is VMware-appropriate: the VMware PowerCLI Core container. Feel free to browse Docker Hub for others, but this is one that is relevant for me because it’s one I will literally be using in prod this week.

  1. Next you will need to pull the image from Docker Hub with the docker pull command:
> docker pull vmware/powerclicore                                                                                                                                                
Using default tag: latest
latest: Pulling from vmware/powerclicore
b982b6cdcb40: Pull complete
2fbc21f9f390: Pull complete
Digest: sha256:ca387df91f4848b3954ab5146e543a4cbcd0c6637ac7912e14bcb6683744631b
Status: Downloaded newer image for vmware/powerclicore:latest
docker.io/vmware/powerclicore:latest

By default, Docker Desktop will pull these images without any configuration. You can now take a look at the docker images with docker image list:

> docker image list                                                                                                                                                              
REPOSITORY            TAG       IMAGE ID       CREATED        SIZE
vmware/powerclicore   latest    dc9464c88b3b   2 weeks ago    673MB

You can also inspect the container with docker inspect. You can use either the REPOSITORY, or the IMAGE ID. I recommend the IMAGE ID:

> docker inspect dc9464c88b3b                                                                                                                                                    
[
    {
        "Id": "sha256:dc9464c88b3bda4a449391aa8674cfc4f4dacfc17d3c282aa0ee76c0068f9d6a",
        "RepoTags": [
            "vmware/powerclicore:latest"
        ],
        "RepoDigests": [
            "vmware/powerclicore@sha256:ca387df91f4848b3954ab5146e543a4cbcd0c6637ac7912e14bcb6683744631b"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2021-03-25T07:21:36.756183559Z",
        "Container": "7ee9c275c71eb6456959b4fc3be92a3ce11fb5441c649005f5684c0a8e820521",
        "ContainerConfig": {
            "Hostname": "7ee9c275c71e",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "TERM=linux"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "CMD [\"/bin/pwsh\"]"
            ],
            "Image": "sha256:22e0ae3662e56bb41fd9d4af1025685ee139d9c2295f58778fa0febd6c5043b1",
            "Volumes": null,
            "WorkingDir": "/root",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "authors": "renoufa@vmware.com,jaker@vmware.com,dmilov@vmware.com",
                "build-date": "20200911",
                "name": "Photon OS x86_64/3.0 Base Image",
                "vendor": "VMware"
            }
        },
        "DockerVersion": "19.03.12",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "TERM=linux"
            ],
            "Cmd": [
                "/bin/pwsh"
            ],
            "Image": "sha256:22e0ae3662e56bb41fd9d4af1025685ee139d9c2295f58778fa0febd6c5043b1",
            "Volumes": null,
            "WorkingDir": "/root",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "authors": "renoufa@vmware.com,jaker@vmware.com,dmilov@vmware.com",
                "build-date": "20200911",
                "name": "Photon OS x86_64/3.0 Base Image",
                "vendor": "VMware"
            }
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 673476365,
        "VirtualSize": 673476365,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/a9ab3ccdaaecb6b1c542f6e10e0a4684f99d3841f5547ef31f5e6f8d71301110/diff",
                "MergedDir": "/var/lib/docker/overlay2/103ff6c54419c6f6283d9bf16f239becbbc37e17bc862175be6359a1ea36dbb2/merged",
                "UpperDir": "/var/lib/docker/overlay2/103ff6c54419c6f6283d9bf16f239becbbc37e17bc862175be6359a1ea36dbb2/diff",
                "WorkDir": "/var/lib/docker/overlay2/103ff6c54419c6f6283d9bf16f239becbbc37e17bc862175be6359a1ea36dbb2/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:f64c6a3b5be3aefd6d534b3aead1b6e9c365216dabd4b0a184bd938208f0b50c",
                "sha256:7824b0cf9b247071aac14c6f3dbfaa95c63cc636113d719c078288bddd3c2b21"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

And finally, to run it, type docker run -ti vmware/powerclicore:

> docker run -ti vmware/powerclicore                                                                                                                                             
PowerShell 7.1.1
Copyright (c) Microsoft Corporation.

https://aka.ms/powershell
Type 'help' to get help.

PS /root> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.1.1
PSEdition                      Core
GitCommitId                    7.1.1
OS                             Linux 4.19.121-linuxkit #1 SMP Thu Jan 21 15:36:34 UTC 2021
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

PS /root> Get-PowerCLIVersion
WARNING: Please consider joining the VMware Customer Experience Improvement Program, so you can help us make PowerCLI a better product. You can join using the following command:

Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $true

VMware's Customer Experience Improvement Program ("CEIP") provides VMware with information that enables VMware to improve its products and services, to fix problems, and to advise you on how best to deploy and use our products.  As part of the CEIP, VMware collects technical information about your organization’s use of VMware products and services on a regular basis in association with your organization’s VMware license key(s).  This information does not personally identify any individual.

For more details: type "help about_ceip" to see the related help article.

To disable this warning and set your preference use the following command and restart PowerShell:
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $true or $false.
WARNING: The cmdlet "Get-PowerCLIVersion" is deprecated. Please use the 'Get-Module' cmdlet instead.

PowerCLI Version
----------------
   VMware PowerCLI 12.2.0 build 17538434
---------------
Component Versions
---------------
   VMware Common PowerCLI Component 12.2 build 17531244
   VMware Cis Core PowerCLI Component PowerCLI Component 12.2 build 17531611
   VMware VimAutomation VICore Commands PowerCLI Component PowerCLI Component 12.2 build 17531987


PS /root>

NEATO, GANG!

Running Containers Without Orchestration

The details I am showing here would be in situations where you are developing a container. In the year 2021, you would not usually run the container in production without orchestrating it (that’s the vendor-neutral way of saying you’d use Kubernetes). The reason for this, is that the true nature of containers is that they are ephemeral. You are treating your application as cattle, or on-demand.

However, you should be aware that running containers full time on a standalone Linux machine is commonplace, but I have really seen this mostly in situations where the containers are made redundant through more traditional clustering technologies.

While you have the current container running, open a new terminal and type docker ps:

> docker ps                                                                                                                                                                       
CONTAINER ID   IMAGE                 COMMAND       CREATED              STATUS              PORTS     NAMES
ece279668f6b   vmware/powerclicore   "/bin/pwsh"   About a minute ago   Up About a minute             exciting_lewin

Notice that there’s no pet hostname. Each docker container gets a “randomly generated” name (in this case “exciting_lewin” and ID.

Go back to your powercli core container and type “exit“. Now type docker ps.

ZOMG! IT GONE!

This is by design. There are ways to get containers to run more permanently, but that goes beyond this post!

Your homework is to see if you can get this running as a Pod in Kubernetes. I think you can do it!

Hit me up on twitter @RussianLitGuy or email me at bryansullins@thinkingoutcloud.org. I would love to hear from you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s