89luca89 / lilipod

Lilipod is a simple container manager, able to download, unpack and use OCI images from various container registries.
GNU General Public License v3.0
240 stars 11 forks source link
chroot containers development docker go golang linux nerdctl oci podman registry tools

Lilipod

Lilipod aims to be a very simple (as in few features) container and image manager.

Lilipod?

Sounds like little-pod, and I like it.

So what does this manage?

Lilipod is a very simple container manager with minimal features to:

It tries to keep a somewhat compatible CLI interface with Podman/Docker/Nerdctl

~$ lilipod help
Manage containers and images

Usage:
  lilipod [command]

Available Commands:
  completion      Generate the autocompletion script for the specified shell
  cp              Copy files/folders between a container and the local filesystem
  create          Create but do not start a container
  exec            Exec but do not start a container
  help            Help about any command
  images          List images in local storage
  inspect         Inspect a container or image
  logs            Fetch the logs of one or more 
  ps              List containers
  pull            Pull an image from a registry
  rename          Rename a container
  rm              Remove one or more containers
  rmi             Removes one or more images from local storage
  run             Run but do not start a container
  start           Start one or more containers
  stop            Remove one or more containers
  update          Update but do not start a container
  version         Show lilipod version

Flags:
  -h, --help               help for lilipod
      --log-level string   log messages above specified level (debug, warn, warning, error)
  -v, --version            version for lilipod

Use "lilipod [command] --help" for more information about a command.

Warning This is beta quality software, it's not heavily used and tested like other alternatives. Be aware.

So are these containers like in Podman or Docker right?

image

Well...superficially yes; Sure you have a separate user, mount (and optionally network, pid, ipc) namespaces, and the processes are in a pivotroot jail, but this does not manage anything else, so:

If you need full blown containers, look no further than Podman or Nerdctl for your needs.

Goals

Lilipod wants to be:

This tool does not aim to be a full replacement for Podman, Docker, Nerdctl or similar tools

Ok but why was it created?

Well, I felt the need to go deeper in how to download a container image from a registry... then one thing lead to another... and here we are. 👀

Also this is a nice fallback for Distrobox when no container-manager is found, or when it's not possible to install one.
Being a fully self-contained binary that lives in one directory (LILIPOD_HOME) it makes it easy to install and remove without package managers.

Getting started

Install

Download the binary from the release page, and use it.

Compile

make

This will create a statically compiled binary.

Dependencies

By itself Lilipod depends only on some Linux utilities (nsenter, tar, cp, ps etc etc), those will be sourced from a bundled busybox static binary. This ensures working dependencies even on atypical systems.

But be aware that to work in a rootless manner, you need to have a working installation of the uidmap package.

Citing their README:

subuid

See also https://rootlesscontaine.rs/getting-started/common/subuid/

Usage

Which commands are available:

~$ lilipod
Manage containers and images

Usage:
  lilipod [command]

Available Commands:
  completion      Generate the autocompletion script for the specified shell
  cp              Copy files/folders between a container and the local filesystem
  create          Create but do not start a container
  exec            Exec but do not start a container
  help            Help about any command
  images          List images in local storage
  inspect         Inspect a container or image
  logs            Fetch the logs of one or more 
  ps              List containers
  pull            Pull an image from a registry
  rename          Rename a container
  rm              Remove one or more containers
  rmi             Removes one or more images from local storage
  run             Run but do not start a container
  start           Start one or more containers
  stop            Remove one or more containers
  update          Update but do not start a container
  version         Show lilipod version

Flags:
  -h, --help               help for lilipod
      --log-level string   log messages above specified level (debug, warn, warning, error)
  -v, --version            version for lilipod

Use "lilipod [command] --help" for more information about a command.

Pull an image:

:~$ lilipod pull registry.opensuse.org/opensuse/tumbleweed:latest
pulling image manifest: registry.opensuse.org/opensuse/tumbleweed:latest
pulling layer db709715e7606a81da9764311fad42de7cebf7cedc14656853797864c5fc5aae.tar.gz
Copying blob sha256:db709715e7606a81da9764311fad42de7cebf7cedc14656853797864c5fc5aae 100% |██████████████████████████████| (4.9 MB/s)         
saving layer sha256:db709715e7606a81da9764311fad42de7cebf7cedc14656853797864c5fc5aae done
saving manifest for registry.opensuse.org/opensuse/tumbleweed:latest
saving config for registry.opensuse.org/opensuse/tumbleweed:latest
saving metadata for registry.opensuse.org/opensuse/tumbleweed:latest
done
84cfef9d6263a008a2d77f4a0863660f

Run a container and remove it afterwards:

:~$ lilipod run --rm -ti alpine cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.18.3
PRETTY_NAME="Alpine Linux v3.18"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"

Create the first container:

:~$ lilipod create --name first-lilipod docker.io/alpine:latest /bin/sh -l
f1c35f7b7de161116abb3157bd125f06

Start the container:

:~$ lilipod start -ti first-lilipod
first-lilipod:/# 

Exec a command in an existing container:

:~$ lilipod exec -ti first-lilipod cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.18.3
PRETTY_NAME="Alpine Linux v3.18"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"

Stop the container:

:~$ lilipod stop first-lilipod
first-lilipod

Inspect the container:

:~$ lilipod inspect --type container first-lilipod
{
  "env": [
   "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
   "HOSTNAME=first-lilipod",
   "TERM=xterm"
  ],
  "cgroup": "private",
  "created": "2023.09.07 10:17:04",
  "gidmap": "1000:100000:65536",
  "hostname": "first-lilipod",
  "id": "f1c35f7b7de161116abb3157bd125f06",
  "image": "docker.io/alpine:latest",
  "ipc": "private",
  "names": "first-lilipod",
  "network": "private",
  "pid": "private",
  "privileged": false,
  "size": "",
  "status": "stopped",
  "time": "private",
  "uidmap": "1000:100000:65536",
  "user": "root:root",
  "userns": "keep-id",
  "workdir": "/",
  "stopsignal": "SIGTERM",
  "mounts": [],
  "labels": [],
  "entrypoint": [
   "/bin/sh",
   "-l"
  ]
 }

Inspect the image:

:-$ lilipod inspect --type image alpine:latest
{
  "architecture": "amd64",
  "container": "ba09fe2c8f99faad95871d467a22c96f4bc8166bd01ce0a7c28dd5472697bfd1",
  "created": "2023-08-07T19:20:20.894140623Z",
  "docker_version": "20.10.23",
  "history": [
   {
    "created": "2023-08-07T19:20:20.71894984Z",
    "created_by": "/bin/sh -c #(nop) ADD file:32ff5e7a78b890996ee4681cc0a26185d3e9acdb4eb1e2aaccb2411f922fed6b in / "
   },
   {
    "created": "2023-08-07T19:20:20.894140623Z",
    "created_by": "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
    "empty_layer": true
   }
  ],
  "os": "linux",
  "rootfs": {
   "type": "layers",
   "diff_ids": [
    "sha256:4693057ce2364720d39e57e85a5b8e0bd9ac3573716237736d6470ec5b7b7230"
   ]
  },
  "config": {
   "Cmd": [
    "/bin/sh"
   ],
   "Env": [
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
   ],
   "Image": "sha256:39dfd593e04b939e16d3a426af525cad29b8fc7410b06f4dbad8528b45e1e5a9"
  },
  "container_config": {
   "Cmd": [
    "/bin/sh",
    "-c",
    "#(nop) ",
    "CMD [\"/bin/sh\"]"
   ],
   "Env": [
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
   ],
   "Hostname": "ba09fe2c8f99",
   "Image": "sha256:39dfd593e04b939e16d3a426af525cad29b8fc7410b06f4dbad8528b45e1e5a9"
  }
} 

Delete the container:

:-$ lilipod rm first-lilipod
first-lilipod

For more advanced use, you can always use --help to have information about the commands to launch.

You can always set the log level to warn error or debug by using the --log-level flag:

:-$ lilipod --log-level debug pull alpine:latest
pulling image manifest: index.docker.io/library/alpine:latest
pulling layer 7264a8db6415046d36d16ba98b79778e18accee6ffa71850405994cffa9be7de.tar.gz
Copying blob sha256:7264a8db6415046d36d16ba98b79778e18accee6ffa71850405994cffa9be7de 100% |██████████████████████████████| (4.9 MB/s)        
saving layer sha256:7264a8db6415046d36d16ba98b79778e18accee6ffa71850405994cffa9be7de done
file_utils.go:119 [debug] input checksum is: sha256:7264a8db6415046d36d16ba98b79778e18accee6ffa71850405994cffa9be7de
file_utils.go:120 [debug] expected checksum is: sha256:7264a8db6415046d36d16ba98b79778e18accee6ffa71850405994cffa9be7de
image_utils.go:354 [debug] successfully checked layer: 7264a8db6415046d36d16ba98b79778e18accee6ffa71850405994cffa9be7de.tar.gz
image_utils.go:116 [debug] 1 layers successfully saved
image_utils.go:117 [debug] cleaning up unwanded files
saving manifest for index.docker.io/library/alpine:latest
saving config for index.docker.io/library/alpine:latest
saving metadata for index.docker.io/library/alpine:latest
done
ff727edbcbe60df2bd6a89cf65d6db2b

Performance

Doing like 1/20th of what Podman or Nerdctl do, at least it tries to be fast...

There are some basic entering speed for an execution:

:~$ time (for i in {1..20}; do podman exec -ti --user luca-linux fedora-rawhide whoami >/dev/null 2>/dev/null; done)

real    0m4.690s
user    0m2.178s
sys     0m0.829s
:~$ time (for i in {1..20}; do ./lilipod exec -i --user luca-linux fedora-rawhide whoami >/dev/null 2>/dev/null; done)

real    0m0.741s
user    0m0.458s
sys     0m0.450s
:~$ time (for i in {1..20}; do podman run --rm -ti alpine:latest whoami >/dev/null 2>/dev/null; done)

real    0m10.125s
user    0m2.606s
sys     0m1.744s
:~$ time (for i in {1..20}; do ./lilipod run --rm -ti alpine:latest whoami >/dev/null 2>/dev/null; done)

real    0m6.157s
user    0m3.545s
sys     0m2.613s

It takes about 5~8ms to enter a container and execute stuff

This obviously is a completely useless and arbitrary metric compared to the difference of utility of the two tools.

Configuration

You can set LILIPOD_HOME to force lilipod to create images/containers/volumes in a specific directory.

Else lilipod will use XDG_DATA_HOME or fallback to $HOME/.local/share/lilipod

Limitations

TO DO