Skip to content

davidbetz/recursivecall-go

master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 

RecursiveCall (Go)

Description

Simple API that calls another instance of itself. Used to demonstrate Docker and Kubernetes orchestration.

Related: NodeJS version

Details

This Go version creates a Docker image with a size of 4.26MB.

Docker Background

Docker "containers" aren't like virtual machines; you're not creating an general purpose environment with its own kernel. What you're doing is giving your project the minimum is needs to run.

Namespaces

(see https://netfxharmonics.com/2018/02/advintrodocker for an advanced, practical introduction to Docker for much more details)

Docker uses an existing Linux concept called namespaces. In fact, Docker is largely a tool that abstracts already existing Linux cleverness, including namespaces (and cgroups).

Namespaces in Linux serve a similar purpose as namespaces in C++/C#: they create a separate "area" where your names won't conflict. Types X.Y.Person and A.B.Person won't conflict. However, they aren't in different runtimes. They're isolated, but not entirely separate.

Linux processes and mounts (and other types of entities) can have different namespaces. Processes in one namespace don't conflict with processes in another. You can have a PID 1 in one namespace and PID 1 in a different namespace. The same goes for mounts. You can have /etc/hosts in one namespace and /etc/hosts in a different namespace. There's an isolation, but not an entire separate (e.g. they're using the same Kernel).

This is what Docker does with containers. There's just a lot of namespace magic to give the illusion of various "micro-machines". In reality, there are no "micro-machines"; everything is running in the same space, but with a simple label separating them. In fact, when you run ps in Linux, you see all the processes "in" each "container" (more accurately, you see all the process trees under the parent process tree).

The term "container" and the preposition "in" lead to extreme confusion, but the terminology is pretty much baked into the industry at this point. There's nothing "in" a container, but something can be "in" an image.

Namespaces are clever and very helpful. If I were to write a plug-in model for an application, I'd create each plug-in in a different namespace then share an IPC namespace for communication. Supposedly, Google Chrome on Linux does something similar with namespaces for various add-ons. Namespaces give you an easy, built-in way to do jailing/sandboxing.

Because a mount namespace is isolated from another mount namespace, when files are required, they need to be in the correct namespace. In this case, isolation implies redundancy. Having /etc/hosts, for example, on the host doesn't help you, you need it in your namespace too.

Images

Let's apply this information to Docker image creation.

When you're creating an image, you're mainly making careful decisions about what minimum files are required.

If you're trying to run a custom NodeJS API, you need to ask yourself: what's the the bare minimum required? The bare minimum is Node. Node, however, can't run by itself; it requires various shared libraries.

You could copy each of these libraries into the image or you could use something designed to satisfy most general share library requirements. The most common way to do this is to start your image with the content from Alpine.

You could use Alpine, but I said content from Alpine. You want what it provides, if not Alpine as a whole. You could literally take the xz file and run with it. That said, there's little reason not to base your work on Alpine.

We care less about a "base image" as we do the shared libraries within. Theoretically, if you're careful, you can create an image from scratch, dump in the Node binaries, then map each shared library from the host to the container.

There is, however, a simpler out-of-the-box model: Go.

With Go, you statically link your binary. No external libraries are required. No runtime is required. Your entire image is a single file: your binary.

In fact, your entire Docker image will have two layers: 1) copy the file, and 2) run the file.

That's it.

Usage

To run directly:

docker run -e GOPATH=/usr/src/app -v $PWD:/usr/src/app -w /usr/src/app golang go run app.go

To build:

docker build . -t local/recursivecall-go

To get the full effect of this sample, run in a Swarm environment:

docker stack deploy --compose-file docker-compose.yml --with-registry-auth rcgo

You need to add g to some form of name resolution (DNS or /etc/hosts). Once the call is inside Swarm, it will handle the rest of the name resolution itself.

Kubernetes Deploy

See https://github.com/davidbetz/recursivecall-k8s for the Kubernetes deployment files.

About

Simple API that calls another instance of itself. Used to demonstrate Docker orchestration.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published