Container (Docker/Podman)

Install

If not on a base Ubuntu image, cat /etc/upstream-release/lsb-release may give you a result for the release you are on.

sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
apt-cache policy docker-ce
sudo apt install -y docker-ce
sudo systemctl status docker

You may want to add your user to the dockre group: sudo usermod -aG docker $USER followed by exec bash -l and/or newgrp docker to take immediate effect.

Portainer

Update

docker stop portainer
docker rm portainer
docker image rm portainer/portainer-ce
docker run -d -p 8000:8000 -p 9000:9000 -p 9443:9443 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest

Run Container without immediate exiting

On container Setup go to Advanced container settings and select Interactive & TTY (or just TTY) under Console. Next check the Command, it should read something similar to /bin/sh or /bin/bash

View Network

podman network ls --format "{{.Name}}: {{range .Subnets}}{{.Subnet}} {{end}}"

Set CPU and Memory Limit

podman machine stop
podman machine set --cpus 2 --memory 2048
podman machine start

podman machine set --cpus 12 --memory 36864 --disk-size 500

If Images are reported as in use, but no container is running

podman ps --all --storage

podman rmi --force _image_id_

View Usage

podman system df

https://docs.podman.io/en/latest/markdown/podman-system-df.1.html

Maintenance

podman image prune -a --filter "until=168h"  # One Week
podman system prune --all --force  # Whipe everything (fresh start, this make take some time, rebuilding the images)
podman volume prune --filter "label!=keep"  # Remove (currently) unused volumes
podman system df -v
podman ps --size

Copy files to/from container from/to host

podman cp <containerId>:/file/path/within/container /host/path/target

Private-Hub

Setup

podman run registry

To access the Private-Hub without a secure connection (HTTP instead of HTTPS) you need to add an exception for it. To do this, you need to add the ip to the insecure-registries. You can do this by editing the file /etc/docker/daemon.json

{
   "insecure-registries" : ["<ip_of_your_registry>:5000"]
}

Alternatively you can make the registry accessable via a reverse proxy with TLS. If you are using you own certificates, you need to add the certs for docker to accept the certificate. Setup with TLS/Certificates: https://docs.docker.com/engine/security/certificates/#troubleshooting-tips

/etc/docker/certs.d/
   └── my-https.registry.example.com          <-- Hostname without port
      ├── client.cert
      ├── client.key
      └── ca.crt

Docker-Desktop

Settings → Docker Engine → Add "insecure-registries" : ["<ip_of_registry>:5000"] on same level as builder.

Linux

Add "insecure-registries" : ["<ip_of_registry>:5000"] to daemon.json. Can be located using locate.

Publish image on Private-Registry

podman tag <image-name> <registry-ip>:5000/<image-name>
podman push <registry-ip>:5000/<image-name>

Action to build and push automatically

View Registry Content

  • http://<ip_address>:5000/v2/_catalog

  • http://<ip_address>:5000/v2/<image_name>/tags/list

Dockerfile

Example

FROM python:latest

# set the working directory
WORKDIR /app

# install dependencies
COPY ./requirements.txt /app
RUN pip install --no-cache-dir --upgrade -r requirements.txt

# copy the scripts to the folder
COPY . /app

# start the server
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

Comments

RUN echo 'This one gets printed'
# RUN echo 'This one does not'

Build and Run in seperat containers

FROM golang:1-alpine as build

WORKDIR /app
COPY cmd cmd
RUN go build cmd/hello/hello.go

FROM alpine:latest

WORKDIR /app
COPY --from=build /app/hello /app/hello

EXPOSE 8180
ENTRYPOINT ["./hello"]

Basic Commands

Build image from current folder and tag with given name.

podman build -t <tag_name> .
  • --no-cache build the image completetly new, takes a lot longer, then building with cache enabled

  • --platform build the image for the specified plattform (linux/amd64, linux/i386, linux/arm/v5, linux/arm/v6, linux/arm/v7, linux/arm64, linux/ppc64le, linux/s390x) plattform must be available for/in the base image.

Run a already build image (my tag name -t) in a container in interactive (-i) mode.

podman run -i -t <image_name>
  • -d (Detached mode)

  • -p (port mapping, could be 8080:80, which would map the port 80 of the container to the port 8080 of the host).

  • -rm (remove conatiner after exit)

If you need to enter a already running container, check the Container ID with podman ps and than run the following command.

podman exec -it <mycontainer> bash

as root

podman exec -u 0 -it <mycontainer> bash

Get logs from (stopped/exited) container

podman logs -t <container-name>

Use podman-compose to run app with “shared filesystem” (use volumes in docker-compose.yaml file to map a path on the host system with a path in the container)

podman-compose run app # or
podman-compose up --build

Example docker-compose.yaml:

services:
   app:
      build: .
      container_name: <just_give_it_a_name>
      command: /bin/sh
      ports:
         - 8080:80
      volumes:
         - .:/app

Command to list all containers, including failed / crashed containers:

podman ps -a
podman logs <container-id>

podman exec -it <container-id> bash

Run Cron in Container

Install

If not on a base Ubuntu image, cat /etc/upstream-release/lsb-release may give you a result for the release you are on.

sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
apt-cache policy docker-ce
sudo apt install -y docker-ce
sudo systemctl status docker

You may want to add your user to the dockre group: sudo usermod -aG docker $USER followed by exec bash -l and/or newgrp docker to take immediate effect.

Portainer

Update

docker stop portainer
docker rm portainer
docker image rm portainer/portainer-ce
docker run -d -p 8000:8000 -p 9000:9000 -p 9443:9443 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest

Run Container without immediate exiting

On container Setup go to Advanced container settings and select Interactive & TTY (or just TTY) under Console. Next check the Command, it should read something similar to /bin/sh or /bin/bash

Maintenance

podman image prune -a --filter "until=168h"  # One Week
podman system prune --all --force  # Whipe everything (fresh start, this make take some time, rebuilding the images)
podman volume prune --filter "label!=keep"  # Remove (currently) unused volumes
podman system df -v
podman ps --size

Copy files to/from container from/to host

podman cp <containerId>:/file/path/within/container /host/path/target

Private-Hub

Setup

docker run registry

To access the Private-Hub without a secure connection (HTTP instead of HTTPS) you need to add an exception for it. To do this, you need to add the ip to the insecure-registries. You can do this by editing the file /etc/docker/daemon.json

{
   "insecure-registries" : ["<ip_of_your_registry>:5000"]
}

Alternatively you can make the registry accessable via a reverse proxy with TLS. If you are using you own certificates, you need to add the certs for docker to accept the certificate. Setup with TLS/Certificates: https://docs.docker.com/engine/security/certificates/#troubleshooting-tips

/etc/docker/certs.d/
   └── my-https.registry.example.com          <-- Hostname without port
      ├── client.cert
      ├── client.key
      └── ca.crt

Docker-Desktop

Settings → Docker Engine → Add "insecure-registries" : ["<ip_of_registry>:5000"] on same level as builder.

Linux

Add "insecure-registries" : ["<ip_of_registry>:5000"] to daemon.json. Can be located using locate.

Publish image on Private-Registry

docker tag <image-name> <registry-ip>:5000/<image-name>
docker push <registry-ip>:5000/<image-name>

Action to build and push automatically

View Registry Content

  • http://<ip_address>:5000/v2/_catalog

  • http://<ip_address>:5000/v2/<image_name>/tags/list

Dockerfile

Example

FROM python:latest

# set the working directory
WORKDIR /app

# install dependencies
COPY ./requirements.txt /app
RUN pip install --no-cache-dir --upgrade -r requirements.txt

# copy the scripts to the folder
COPY . /app

# start the server
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

Comments

RUN echo 'This one gets printed'
# RUN echo 'This one does not'

Build and Run in seperat containers

FROM golang:1-alpine as build

WORKDIR /app
COPY cmd cmd
RUN go build cmd/hello/hello.go

FROM alpine:latest

WORKDIR /app
COPY --from=build /app/hello /app/hello

EXPOSE 8180
ENTRYPOINT ["./hello"]

Basic Commands

Build image from current folder and tag with given name.

docker build -t <tag_name> .
  • --no-cache build the image completetly new, takes a lot longer, then building with cache enabled

  • --platform build the image for the specified plattform (linux/amd64, linux/i386, linux/arm/v5, linux/arm/v6, linux/arm/v7, linux/arm64, linux/ppc64le, linux/s390x) plattform must be available for/in the base image.

Run a already build image (my tag name -t) in a container in interactive (-i) mode.

docker run -i -t <image_name>
  • -d (Detached mode)

  • -p (port mapping, could be 8080:80, which would map the port 80 of the container to the port 8080 of the host).

  • -rm (remove conatiner after exit)

If you need to enter a already running container, check the Container ID with docker ps and than run the following command.

docker exec -it <mycontainer> bash

as root

docker exec -u 0 -it <mycontainer> bash

Get logs from (stopped/exited) container

docker logs -t <container-name>

Use docker-compose to run app with “shared filesystem” (use volumes in docker-compose.yaml file to map a path on the host system with a path in the container)

docker-compose run app # or
docker-compose up --build

Example docker-compose.yaml:

services:
   app:
      build: .
      container_name: <just_give_it_a_name>
      command: /bin/sh
      ports:
         - 8080:80
      volumes:
         - .:/app

Command to list all containers, including failed / crashed containers:

docker ps -a
docker logs <container-id>

docker exec -it <container-id> bash

Run Cron in Container

Create a file with the cron infos:

# must be ended with a new line "LF" (Unix) and not "CRLF" (Windows)
* * * * * echo "Hello world" >> /var/log/cron.log 2>&1
# An empty line is required at the end of this file for a valid cron file.
*/5 * * * * /usr/bin/sh /example-scheduled-task.sh

Next, change your Dockerfile to install cron and register your crontab (example for Debian-based image):

RUN apt-get update && apt-get install -y cron
COPY example-crontab /etc/cron.d/example-crontab
RUN chmod 0644 /etc/cron.d/example-crontab
RUN crontab /etc/cron.d/example-crontab

If you can not run ENTRYPOINT ["cron", "-f"], because there are other processes that need to be startet, create an init.sh and run that instead on startup ENTRYPOINT ["bash", "init.sh"].

In the init.sh insert the start command for crontab and any furter commands you need.

service cron start
<further commands>

Dump MySQL Database from Container

If you have the root-password set in an environmental variable (MYSQL_ROOT_PASSWORD) you can simply run the following command.

docker exec <your-mysql-container> sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > <path-on-your-host-system>/all-databases.sql

Mount CIFS within a container

docker run …

–cap-add SYS_ADMIN –cap-add DAC_READ_SEARCH

Common Hickups

  • If you get the Error init.sh not found, make sure to use LF instead of CRLF (Linux vs Windows)

NGINX Reverse-Proxy

Usefull Containers

LF” (Unix) and not “CRLF” (Windows)
          • echo “Hello world” >> /var/log/cron.log 2>&1

# An empty line is required at the end of this file for a valid cron file. */5 * * * * /usr/bin/sh /example-scheduled-task.sh

Next, change your Dockerfile to install cron and register your crontab (example for Debian-based image):

RUN apt-get update && apt-get install -y cron
COPY example-crontab /etc/cron.d/example-crontab
RUN chmod 0644 /etc/cron.d/example-crontab
RUN crontab /etc/cron.d/example-crontab

If you can not run ENTRYPOINT ["cron", "-f"], because there are other processes that need to be startet, create an init.sh and run that instead on startup ENTRYPOINT ["bash", "init.sh"].

In the init.sh insert the start command for crontab and any furter commands you need.

service cron start
<further commands>

Dump MySQL Database from Container

If you have the root-password set in an environmental variable (MYSQL_ROOT_PASSWORD) you can simply run the following command.

podman exec <your-mysql-container> sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > <path-on-your-host-system>/all-databases.sql

Create Multi Architecture Image/Manifest

podman build --arch=linux/amd64 -t <user>/<image_name>:v0.1-linux_amd64 .
podman build --arch=linux/arm64 -t <user>/<image_name>:v0.1-linux_arm64 .

podman manifest create <user>/<image_name>:v0.1 <user>/<image_name>:v0.1-linux_arm64 <user>/<image_name>:v0.1-linux_amd64
podman manifest push <user>/<image_name>:v0.1

Mount CIFS within a container

docker run ... \
   --cap-add SYS_ADMIN \
   --cap-add DAC_READ_SEARCH \

Common Hickups

  • If you get the Error init.sh not found, make sure to use LF instead of CRLF (Linux vs Windows)

NGINX Reverse-Proxy

Usefull Containers