What does HackerNews think of tini?

A tiny but valid `init` for containers

Language: C

#8 in C
#16 in Docker
#18 in Linux
To be fair, even for running a single process the pitfalls are real. I've been seeing Tini[1] a lot for these situations.

I just read in the README that Tini is included by Docker since 1.13 if using --init flag.

[1] https://github.com/krallin/tini

"For a modern personal computer", systemd is pretty much spot-on what you'd want.

On the other hand, if you're running a bunch of fast-scaling containers/VMs, and just want init to (re)spawn one (or a handful) of processes, then step aside as quickly as possible, sitting there quietly, reaping zombies - TFA has this page:

https://without-systemd.org/wiki/index_php/Init/#standalone_...

And there are other alternatives as well, such as:

https://github.com/Yelp/dumb-init

https://github.com/krallin/tini

This is a great use case for tini[0]. Try this, after installing the tini binary to /sbin:

  ENTRYPOINT ["/sbin/tini", "--"]
  CMD ["node", "/path/to/main/process.js"]
[0]: https://github.com/krallin/tini

edit: formatting, sorry.

Pretty sure this is what tini[1] is for, and there's supervisord[2] for more complex use cases, e.g. running multiple processes. Neither are a full replacement for traditional systemd, which I heard you can technically run in a container these days, but I've never seen anyone try.

[1] https://github.com/krallin/tini

[2] https://github.com/Supervisor/supervisor

Sigh No, it really does, and it has nothing to do with init systems.

Systemd was never necessary. An init system just has to run a couple of steps once a system starts or stops. Bash is fine for that, or a tiny C wrapper. Look at every freaking Docker container in the world - they all use tini (https://github.com/krallin/tini), the tiniest, most dinky init tool ever made, because it's totally fine to use something small and stupid to initialize some system calls and execute a program. Once you've "started up", you can choose to use a dedicated service manager to manage various applications.

There's a reason we use K8s, Nomad, or Docker for container scheduling even on a single host: Systemd is not good at it, and we'd have to replace it to scale up to over 1 host anyway. We use distributed decentralized services on modern systems. Systemd isn't intended to work for modern services; it's intended to just be a "faster desktop system", iterating on what we had before, rather than being redesigned for modern workloads and applications.

Anyone who used Systemd purely because they didn't like Bash scripts had no idea how to manage a system. There were already replacements that managed services well, and you'd just install those and use them, with very minimal change to the rest of your system. I mean, if developers need to write a new microservice, they don't say, "I know, I'll use Systemd!" They say, "I know, I'll use minikube!" Because it actually gives them everything they'll need to run their application locally, and on a globally distributed decentralized fault-tolerant service coordinator. Their application can then opt-in to all of K8s' myriad customizations and complexities, but the rest of their operating system doesn't have to!!

But furthermore, as a system itself, Systemd actually just sucks. The user interfaces are totally clunky and not standard to anything else we have in Linux. The binary format makes it much more annoying to use with other tools without having to learn how the wrappers work, and managing the binary files from a systems perspective is annoying. The filesystem structure is fucking atrocious; who the fuck puts config files in /lib ??? Why do I have to re-run 3 commands just to reload a daemon after I've edited its service file?? Why do I need to now learn an entirely new DNS resolver setup that I never asked for?? Then there's the bugs and security issues and generally arrogant, dickish way Systemd works with other open source projects. It sucks balls. You can't tell me it doesn't, because I have to use the damn thing every day, and work around its dumb issues.

To people who manage systems for a living, and regular users just trying to get on the 'Net, Systemd was not some revelation from the gods because "oh no, bash scripts". Bash scripts may have been annoying, but Systemd is annoying in a whole new, more complicated way.

Correct. Docker containers typically only contain one process, and that process runs as PID 1. If you need an init system, tini is very popular, and is now built in to docker itself[1]. Systemd is way heavy and overkill inside docker.

[1] https://github.com/krallin/tini

Not OP, but I’ve been running a kubernetes cluster for a few months myself, so I’ll try to give context.

> Don’t trust arbitrary base images.

Kinda obvious, the equivalent of "don’t trust any random binary from the web", as it can contain malware at worst.

> Use small base image.

Generally, you should look at using Alpine as base image, to reduce storage size, and memory consumption of the overlay filesystem that docker uses. But be aware, Alpine uses musl and busybox instead of GNU libc and utils, so some software might not work there. Generally, this is also common sense. See more at https://alpinelinux.org/about/ and https://hub.docker.com/_/alpine/ – but be aware, often the projects you want to depend on already have an alpine image (e.g., openjdk, nodejs or postgres images are all available in an alpine version, reducing their size from 500M+ to around 10-20M)

> Use the builder pattern.

With containers, many people include all build dependencies in the final container. Docker has a new syntax to avoid this, by declaring first a builder container, with its dependencies, then building, and then declaring the final container, and COPY'ing the build artifacts from the build container. This, too, keeps container size a lot smaller.

You can find more here: https://docs.docker.com/engine/userguide/eng-image/multistag...

> Use non-root user inside container.

This is basically common sense with regards to docker as runtime, and is bad security practice (especially when combined with problematic filesystem mounts). Also is recommended by the Docker team: https://docs.docker.com/develop/dev-best-practices/

> Make the file system read only.

I’m not sure what OP is referring to here, but if it refers to the container filesystem, that is because writing to AuFS or OverlayFS is significantly slower (and more memory intensive) than writing to a PersistentVolumeClaim or EmptyDir volume in Kubernetes, so you should always mount an EmptyDir volume for all log folders, temporary data, etc, and a PersistentVolumeClaim for all persistent data.

Also is recommended by the Docker team: https://docs.docker.com/develop/dev-best-practices/

> One process per container, Don’t restart on failure, crash cleanly instead, Log to stdout & stdderr

This is related to the logging system (which mostly looks at stdout and stderr), which is a result of the fact that Kubernetes itself was mostly designed to work with a single process per container. Yes, you can spawn multiple processes, from a single shell, and print their combined stdout, but then you also need to ensure that if one crashes, everything restarts properly.

If you use a single process per container, logging to stdout/stderr, then scaling is a lot simpler, and restarts are handled automatically (and this is required for staged rollout).

> Add dumb-init to prevent zombie processes.

If you need multiple processes, and one that isn’t PID1 crashes, you’ll end up with zombie processes. A single process per container obviously avoids this, but if you have to use multiple, at least add an init system to reap dead child processes, potentially restart crashed dependent processes, etc.

Normally, docker supports the --init parameter to do this, but the version recommended for use with Kubernetes does not support this yet (EDIT: apparently, since 1.7.0, Kubernetes actually does automatically do this for you), so you could add e.g. https://github.com/Yelp/dumb-init or https://github.com/krallin/tini (both officially recommended by the Docker team)

It has been a reasonably big issue. E.g. I kept seeing zombies with Consul for a while until we realised that every single Consul Docker container on Dockerhub just had Consul run as pid 1 in the container (this is a while ago, no idea if that's still the case), without realising that Consul health checks then could end up as zombies if you weren't very careful about how you wrote them (e.g. typical example: Spawning curl from a shell script, with a timeout on the health check that was shorter than any timeouts on the curl requests).

It's usually fairly simple to fix (e.g. for Consul above, I raised it with the Consul guys and they said they'd look at adding waiting on children to it as a precaution - it's just a couple of lines -, but people building containers could also introduce a minimal init, or you can write your health checks to guard against it), but it happens all over the place, and people are often unaware and so not on the lookout for it and it may not be immediately obvious.

The reason I raised it as an issue for Consul, for example, even though it wasn't really their fault, but an issue with the containers, is that people need to be aware of the problem when packaging the containers, need to be aware that a given application may spawn children, and that they may not wait for them. Even a lot of people aware of the zombie issue end up packaging software that they didn't realise where spawning child processes that could end up as zombies (in this case, it took running it in a container without a proper pid 1, using health checks which not everyone will do, and writing the health checks in a particular way in order to notice the effects).

Thankfully there are a number of tiny little inits. E.g. there's suckless sinit [1], Tini[2] , and here's a tiny little proof of concept Go init [3] I wrote (though frankly, suckless or Tini compiled with musl will give you a much smaller binary) as what little you actually need to do is very trivial.

[1] http://git.suckless.org/sinit

[2] https://github.com/krallin/tini

[3] https://gist.github.com/vidarh/91a110792c86d6c3bb41