this post was submitted on 13 Dec 2023
233 points (97.9% liked)

Selfhosted

40487 readers
195 users here now

A place to share alternatives to popular online services that can be self-hosted without giving up privacy or locking you into a service you don't control.

Rules:

  1. Be civil: we're here to support and learn from one another. Insults won't be tolerated. Flame wars are frowned upon.

  2. No spam posting.

  3. Posts have to be centered around self-hosting. There are other communities for discussing hardware or home computing. If it's not obvious why your post topic revolves around selfhosting, please include details to make it clear.

  4. Don't duplicate the full text of your blog or github here. Just post the link for folks to click.

  5. Submission headline should match the article title (don’t cherry-pick information from the title to fit your agenda).

  6. No trolling.

Resources:

Any issues on the community? Report it using the report flag.

Questions? DM the mods!

founded 2 years ago
MODERATORS
 

I'm a retired Unix admin. It was my job from the early '90s until the mid '10s. I've kept somewhat current ever since by running various machines at home. So far I've managed to avoid using Docker at home even though I have a decent understanding of how it works - I stopped being a sysadmin in the mid '10s, I still worked for a technology company and did plenty of "interesting" reading and training.

It seems that more and more stuff that I want to run at home is being delivered as Docker-first and I have to really go out of my way to find a non-Docker install.

I'm thinking it's no longer a fad and I should invest some time getting comfortable with it?

you are viewing a single comment's thread
view the rest of the comments
[–] [email protected] 2 points 11 months ago (2 children)

Yeah, it also has the effect that when starting up say a new postgres or web server is one simple command, a few seconds and a few mb of disk and ram, you do it more for smaller stuff.

Instead of setting up one nginx for multiple sites you run one nginx per site and have the settings for that as part of the site repository. Or when a service needs a DB, just start a new one just for that. And if that file analyzer ran in it's own image instead of being part of the web service, you could scale that separately.. oh, and it needs a redis instance and a rabbitmq server, that's two more containers, that serves just that web service. And so on..

Things that were a huge hassle before, like separate mini vm's for each sub-service, and unique sub-services for each service doesn't just become practical but easy. You can define all the services and their relations in one file and docker will recreate the whole stack with all services with one command.

And then it also gets super easy to start more than one of them, for example for testing or if you have a different client. .. which is how you easily reach a hundred instances running.

So instead of a service you have a service blueprint, which can be used in service stack blueprints, which allows you to set up complex systems relatively easily. With a granularity that would traditionally be insanity for anything other than huge, serious big-company deployments.

[–] [email protected] 2 points 11 months ago (1 children)

Well congrats, you are the first person who has finally convinced me that it might actually be worth looking at even for my small setup. Nobody else has been able to even provide a convincing argument that docker might improve on my VM setup, and I've been asking about it for a few years now.

[–] [email protected] 1 points 11 months ago* (last edited 11 months ago) (1 children)

It's a great tool to have in the toolbox. Might take some time to wrap your head around, but coming from vm's you already have most of the base understanding.

From a VM user's perspective, some translations:

  • Dockerfile = script to set up a VM from a base distro, and create a checkpoint that is used as a base image for starting up vm's
  • A container is roughly similar to a running VM. It runs inside the host os, jailed, which account for it's low overhead.
  • When a container is killed, every file system change gets thrown out. Certain paths and files can be mapped to host folders / storage to keep data between restarts.
  • Containers run on their own internal network. You can specify ports to nat in from host interface to containers.
  • Most service setup is done by specifying environment variables for the container, or mapping in a config file or folder.
  • Since the base image is static, and config is per container, one image can be used to run multiple containers. So if you have a postgres image, you can run many containers on that image. And specify different config for each instance.
  • Docker compose is used for multiple containers, and their relationship. For example a web service with a DB, static file server, and redis cache. Docker compose also handles things like setting up a unique network for the containers, storage volumes, logs, internal name resolution, unique names for the containers and so on.

A small tip: you can "exec" into a running container, which will run a command inside that container. Combined with interactive (-i) and terminal (-t) flags, it's a good way to get a shell into a running container and have a look around or poke things. Sort of like getting a shell on a VM.

One thing that's often confusing for new people are image tags. Partially because it can mean two things. For example "postgres" is a tag. That is attached to an image. The actual "name" of an image is it's Sha sum. An image can have multiple tags attached. So far so good, right?

Now, let's get complicated. The actual tag, the full tag for "postgres" is actually "docker.io/postgres:latest". You see, every tag is a URL, and if it doesn't have a domain name, docker uses it's own. And then we get to the ": latest" part. Which is called a tag. Yup. All tags have a tag. If one isn't given, it's automatically set to "latest". This is used for versioning and different builds.

For example postgres have tags like "16.1" which points to latest 16.1.x version image, built on postgres maintainers' preferred distro. "16.1-alpine" that point to latest Alpine based 16.1.x version. "16" for latest 16.x.x version, "alpine" for latest alpine based version, be it 16 or 17 or 18.. and so on. You can find more details here.

The images on docker hub are made by .. well, other people. Often the developers of that software themselves, sometimes by docker, sometimes by random people. You can make your own account there, it's free. If you do, make an image and pushes it, it will be available as shdwdrgn/name - if it doesn't have a user component it's maintained / sanctioned by docker.

You can also run your own image repository service, as long as it has https with valid cert. Then it will be yourdomain.tld/something

So that was a brief introduction to the strange World of docker. Docker is a for profit company, btw. But the image format is standardized, and there's fully open source ways to make and run images too. At the top of my head, podman and Kubernetes.

[–] [email protected] 1 points 11 months ago (1 children)

One thing I'm not following in all the discussions about how self-contained docker is... nearly all of my images make use of NFS shares and common databases. For example, I have three separate smtp servers which need to put incoming emails into the proper home folders, but also database connections to track detected spam and other things. So how would all these processes talk to each other if they're all locked within their container?

The other thing I keep coming back to, again using my smtp servers as an example... It is highly unlikely that anyone else has exactly the same setup that I do, let alone that they've taken the time to build a docker image for it. So would I essentially have to rebuild the entire system from scratch, then learn how to create a docker script to launch it, just to get the service back online again?

[–] [email protected] 3 points 11 months ago (1 children)

For the nfs shares, there's generally two approaches to that. First is to mount it on host OS, then map it in to the container. Let's say the host has the nfs share at /nfs, and the folders you need are at /nfs/homes. You could do "docker run -v /nfs/homes:/homes smtpserverimage" and then those would be available from /homes inside the image.

The second approach is to set up NFS inside the image, and have that connect directly to the nfs server. This is generally seen as a bad idea since it complicates the image and tightly couples the image to a specific configuration. But there are of course exceptions to each rule, so it's good to keep in mind.

With database servers, you'd have that set up for accepting network connections, and then just give the address and login details in config.

And having a special setup.. How special are we talking? If it's configuration, then that's handled by env vars and mapping in config files. If it's specific plugins or compile options.. Most built images tend to cast a wide net, and usually have a very "everything included" approach, and instructions / mechanics for adding plugins to the image.

If you can't find what you're looking for, you can build your own image. Generally that's done by basing your Dockerfile on an official image for that software, then do your changes. We can again take the "postgres" image since that's a fairly well made one that has exactly the easy function for this we're looking for.

If you would like to do additional initialization in an image derived from this one, add one or more *.sql, *.sql.gz, or *.sh scripts under /docker-entrypoint-initdb.d (creating the directory if necessary). After the entrypoint calls initdb to create the default postgres user and database, it will run any *.sql files, run any executable *.sh scripts, and source any non-executable *.sh scripts found in that directory to do further initialization before starting the service.

So if you have a .sh script that does some extra stuff before the DB starts up, let's say "mymagicpostgresthings.sh" and you want an image that includes that, based on Postgresql 16, you could make this Dockerfile in the same folder as that file:

FROM postgres:16
RUN mkdir /docker-entrypoint-initdb.d
COPY mymagicpostgresthings.sh /docker-entrypoint-initdb.d/mymagicpostgresthings.sh
RUN chmod a+x /docker-entrypoint-initdb.d/mymagicpostgresthings.sh

and when you run "docker build . -t mymagicpostgres" in that folder, it will build that image with your file included, and call it "mymagicpostgres" - which you can run by doing "docker run -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 mymagicpostgres"

In some cases you need a more complex approach. For example I have an nginx streaming server - which needs extra patches. I found this repository for just that, and if you look at it's Dockerfile you can see each step it's doing. I needed a bit of modifications to that, so I have my own copy with different nginx.conf, an extra patch it downloads and applies to the src code, and a startup script that changes some settings from env vars, but that had 90% of the work done.

So depending on how big changes you need, you might have to recreate from scratch or you can piggyback on what's already made. And for "docker script to launch it" that's usually a docker-compose.yml file. Here's a postgres example:

version: '3.1'

services:
  db:
    image: postgres
    restart: always
    environment:
      POSTGRES_PASSWORD: example

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

If you run "docker compose up -d" in that file's folder it will cause docker to download and start up the images for postgres and adminer, and port forward in 8080 to adminer. From adminer's point of view, the postgres server is available as "db". And since both have "restart: always" if one of them crashes or the machine reboots, docker will start them up again. So that will continue running until you run "docker compose down" or something catastrophic happens.

[–] [email protected] 3 points 11 months ago

Hey I wanted to say thanks for all the info and I've saved this aside. Had something come up that is requiring all my attention so I just got around to reading your message but it looks like my foray into docker will have to wait a bit longer.

[–] [email protected] 1 points 8 months ago

Instead of setting up one nginx for multiple sites you run one nginx per site and have the settings for that as part of the site repository.

Doesn't that require a lot of resources since you're running (mysql, nginx, etc.) numerous times (once for each container), instead of once globally?

Or, per your comment below:

Since the base image is static, and config is per container, one image can be used to run multiple containers. So if you have a postgres image, you can run many containers on that image. And specify different config for each instance.

You'd only have two instances of postgres, for example, one for all docker containers and one global/server-wide? Still, that doubles the resources used no?