]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
container: Add container support
authorOlliver Schinagl <oliver@schinagl.nl>
Wed, 1 Mar 2023 18:08:01 +0000 (19:08 +0100)
committerFlole998 <Flole998@users.noreply.github.com>
Sat, 10 Feb 2024 17:52:05 +0000 (18:52 +0100)
This commit adds support for containizersation of TVHeadend. It adds the
actual technology agnostic container file, an entry point and
healthcheck for it and a github workflow component to publish it.

TODO: Healthcheck script is not yet working.
TODO: Add decent documetnation

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
.dockerignore [new file with mode: 0644]
.github/workflows/container-build.yaml [new file with mode: 0644]
Containerfile [new symlink]
Containerfile.alpine [new file with mode: 0644]
Containerfile.debian [new file with mode: 0644]
Dockerfile [new symlink]
README.Docker.md [new file with mode: 0644]
README.md
configure
support/container-entrypoint.sh [new file with mode: 0755]

diff --git a/.dockerignore b/.dockerignore
new file mode 100644 (file)
index 0000000..3baed2f
--- /dev/null
@@ -0,0 +1,2 @@
+.config.mk
+build.*/
diff --git a/.github/workflows/container-build.yaml b/.github/workflows/container-build.yaml
new file mode 100644 (file)
index 0000000..a32220d
--- /dev/null
@@ -0,0 +1,74 @@
+name: Create and publish Container image
+
+on:
+  push:
+    branches:
+      - master
+    tags:
+      - 'v*'
+  pull_request:
+    branches:
+      - master
+
+env:
+  REGISTRY: ghcr.io
+  IMAGE_NAME: ${{ github.repository }}
+
+jobs:
+  build-and-push-image:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+      packages: write
+    strategy:
+      matrix:
+        include:
+          - container: Containerfile.debian
+            autotag: false
+            suffix: -debian
+          - container: Containerfile.alpine
+            autotag: auto
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v3
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v2
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v2
+
+      - name: Login to Container registry
+        uses: docker/login-action@v2
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Docker meta
+        id: meta
+        uses: docker/metadata-action@v4
+        with:
+          images: |
+            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            type=ref,event=pr
+            type=edge
+            type=semver,pattern={{version}}
+            type=semver,pattern={{major}}.{{minor}}
+            type=semver,pattern={{major}}
+          flavor: |
+            latest=${{ matrix.autotag }}
+            suffix=${{ matrix.suffix }}
+
+      - name: Build and push
+        uses: docker/build-push-action@v4
+        with:
+          platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6
+          context: .
+          file: ${{ matrix.container }}
+          push: ${{ github.event_name != 'pull_request' }}
+          tags: ${{ steps.meta.outputs.tags }}
+          labels: ${{ steps.meta.outputs.labels }}
diff --git a/Containerfile b/Containerfile
new file mode 120000 (symlink)
index 0000000..83ac7f4
--- /dev/null
@@ -0,0 +1 @@
+Containerfile.alpine
\ No newline at end of file
diff --git a/Containerfile.alpine b/Containerfile.alpine
new file mode 100644 (file)
index 0000000..0d501df
--- /dev/null
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# Copyright (C) 2023 Olliver Schinagl <oliver@schinagl.nl>
+
+ARG ALPINE_VERSION="latest"
+ARG TARGET_ARCH="library"
+
+FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} AS builder
+
+WORKDIR /src
+
+COPY . /src/
+
+RUN apk add --no-cache \
+        'avahi-dev' \
+        'bash' \
+        'bsd-compat-headers' \
+        'build-base' \
+        'cmake' \
+        'coreutils' \
+        'dbus-dev' \
+        'ffmpeg4-dev' \
+        'findutils' \
+        'gettext-dev' \
+        'git' \
+        'gnu-libiconv-dev' \
+        'libdvbcsa-dev' \
+        'libhdhomerun-dev' \
+        'libva-dev' \
+        'libvpx-dev' \
+        'linux-headers' \
+        'musl-dev' \
+        'openssl-dev>3' \
+        'opus-dev' \
+        'pngquant' \
+        'python3' \
+        'uriparser-dev' \
+        'wget' \
+        'x264-dev' \
+        'x265-dev' \
+        'zlib-dev' \
+    && \
+    git config --global --add safe.directory '/src/data/dvb-scan' && \
+    ./configure \
+                --prefix='/usr/local' \
+                --disable-doc \
+                --disable-execinfo \
+                --disable-ffmpeg_static \
+                --disable-hdhomerun_static \
+                --disable-libfdkaac_static \
+                --disable-libmfx_static \
+                --disable-libopus_static \
+                --disable-libtheora_static \
+                --disable-libvorbis_static \
+                --disable-libvpx_static \
+                --disable-libx264_static \
+                --disable-libx265_static \
+                --enable-bundle \
+                --enable-dvbcsa \
+                --enable-hdhomerun_client \
+                --enable-kqueue \
+                --enable-libav \
+                --enable-nvenc \
+                --enable-pngquant \
+                --enable-qsv \
+                --python=python3 \
+        && \
+        make DESTDIR='/tvheadend' -j$(($(nproc) - 1)) install
+
+FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION}
+
+LABEL maintainer="Olliver Schinagl <oliver@schinagl.nl>"
+
+EXPOSE 9981 \
+       9982 \
+       9983
+
+RUN apk add --no-cache \
+        'avahi' \
+        'dbus-libs' \
+        'ffmpeg4' \
+        'ffmpeg4-libavcodec' \
+        'ffmpeg4-libavdevice' \
+        'ffmpeg4-libavfilter' \
+        'ffmpeg4-libavfilter' \
+        'ffmpeg4-libavformat' \
+        'ffmpeg4-libavutil' \
+        'ffmpeg4-libpostproc' \
+        'ffmpeg4-libswresample' \
+        'ffmpeg4-libswscale' \
+        'gnu-libiconv-libs' \
+        'libcrypto3' \
+        'libdvbcsa' \
+        'libhdhomerun-libs' \
+        'libssl3' \
+        'liburiparser' \
+        'libva' \
+        'libvpx' \
+        'mesa' \
+        'opus' \
+        'perl-http-entity-parser' \
+        'pngquant' \
+        'python3' \
+        'tini' \
+        'x264-libs' \
+        'x265-libs' \
+        'xmltv' \
+        'zlib' \
+    && \
+    [ "$(uname -m)" = 'x86'* ] && apk add --no-cache \
+        'libva-intel-driver' \
+        ; \
+    [ "$(uname -m)" = 'x86_64' ] && apk add --no-cache \
+        'intel-media-driver' \
+        ; \
+    addgroup -S 'tvheadend' && \
+    adduser -D -G 'tvheadend' -h '/var/lib/tvheadend' -s '/bin/nologin' -S 'tvheadend' && \
+    adduser 'tvheadend' 'audio' && \
+    adduser 'tvheadend' 'usb' && \
+    adduser 'tvheadend' 'video' && \
+    install -d -m 775 -g 'tvheadend' -o 'tvheadend' '/var/lib/tvheadend/recordings' && \
+    install -d -m 775 -g 'tvheadend' -o 'tvheadend' '/var/log/tvheadend'
+
+COPY --from=builder "/tvheadend" "/"
+COPY "./support/container-entrypoint.sh" "/init"
+
+VOLUME /var/lib/tvheadend
+VOLUME /var/lib/tvheadend/recordings
+WORKDIR /var/lib/tvheadend/
+USER tvheadend
+
+ENTRYPOINT [ "/sbin/tini", "--", "/init" ]
diff --git a/Containerfile.debian b/Containerfile.debian
new file mode 100644 (file)
index 0000000..dadbedd
--- /dev/null
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# Copyright (C) 2023 Olliver Schinagl <oliver@schinagl.nl>
+
+ARG DEBIAN_VERSION="stable-slim"
+ARG TARGET_ARCH="library"
+
+FROM docker.io/${TARGET_ARCH}/debian:${DEBIAN_VERSION} AS builder
+
+ENV DEBIAN_FRONTEND noninteractive
+
+WORKDIR /src
+
+COPY . /src/
+
+RUN apt-get update --yes && apt-get install --yes \
+        'build-essential' \
+        'bzip2' \
+        'cmake' \
+        'debhelper' \
+        'gettext' \
+        'git-core' \
+        'libavahi-client-dev' \
+        'libavdevice-dev' \
+        'libdbus-1-dev' \
+        'libdvbcsa-dev' \
+        'libhdhomerun-dev' \
+        'libopus-dev' \
+        'libpcre2-dev' \
+        'libssl-dev' \
+        'liburiparser-dev' \
+        'libva-dev' \
+        'libvpx-dev' \
+        'libx264-dev' \
+        'libx265-dev' \
+        'lsb-release' \
+        'pkg-config' \
+        'pngquant' \
+        'python3' \
+        'python3-distutils-extra' \
+        'python3-requests' \
+        'wget' \
+        'zlib1g-dev' \
+    && \
+    git config --global --add safe.directory '/src/data/dvb-scan' && \
+    ./configure \
+                --prefix='/usr/local' \
+                --disable-doc \
+                --disable-execinfo \
+                --disable-ffmpeg_static \
+                --disable-hdhomerun_static \
+                --disable-libfdkaac_static \
+                --disable-libmfx_static \
+                --disable-libopus_static \
+                --disable-libtheora_static \
+                --disable-libvorbis_static \
+                --disable-libvpx_static \
+                --disable-libx264_static \
+                --disable-libx265_static \
+                --enable-bundle \
+                --enable-dvbcsa \
+                --enable-hdhomerun_client \
+                --enable-kqueue \
+                --enable-libav \
+                --enable-nvenc \
+                --enable-pngquant \
+                --enable-qsv \
+                --python=python3 \
+        && \
+        make DESTDIR='/tvheadend' -j$(($(nproc) - 1)) install
+
+FROM docker.io/${TARGET_ARCH}/debian:${DEBIAN_VERSION}
+
+ENV DEBIAN_FRONTEND noninteractive
+
+LABEL maintainer="Olliver Schinagl <oliver@schinagl.nl>"
+
+EXPOSE 9981 \
+       9982 \
+       9983
+
+RUN apt-get update --yes && apt-get install --yes \
+        'libavahi-client3' \
+        'libavcodec59' \
+        'libavdevice59' \
+        'libavfilter8' \
+        'libavfilter8' \
+        'libavutil57' \
+        'libdbus-1-3' \
+        'libdvbcsa1' \
+        'libhdhomerun4' \
+        'libpostproc56' \
+        'libswscale6' \
+        'libopus0' \
+        'libpcre2-8-0' \
+        'libssl3' \
+        'liburiparser1' \
+        'libva2' \
+        'libvpx7' \
+        'libx264-164' \
+        'libx265-199' \
+        'pngquant' \
+        'python3' \
+        'mesa-va-drivers' \
+        'mesa-vdpau-drivers' \
+        'zlib1g' \
+        'xmltv' \
+        'tini' \
+    && \
+    useradd -c 'TVHeadend' -d '/var/lib/tvheadend' -G 'audio','video' -m -r -s '/bin/false' -U 'tvheadend' && \
+    install -d -m 775 -g 'tvheadend' -o 'tvheadend' '/var/lib/tvheadend/recordings' && \
+    install -d -m 775 -g 'tvheadend' -o 'tvheadend' '/var/log/tvheadend'
+
+COPY --from=builder "/tvheadend" "/"
+COPY "./support/container-entrypoint.sh" "/init"
+
+VOLUME /var/lib/tvheadend
+VOLUME /var/lib/tvheadend/recordings
+WORKDIR /var/lib/tvheadend/
+USER tvheadend
+
+ENTRYPOINT [ "/usr/bin/tini", "--", "/init" ]
diff --git a/Dockerfile b/Dockerfile
new file mode 120000 (symlink)
index 0000000..5240dc0
--- /dev/null
@@ -0,0 +1 @@
+Containerfile
\ No newline at end of file
diff --git a/README.Docker.md b/README.Docker.md
new file mode 100644 (file)
index 0000000..28bc20d
--- /dev/null
@@ -0,0 +1,490 @@
+# TVHeadend in Docker
+TVHeadend can be run within a Docker container. This provides isolation from
+other processes by running it in a containerized environment. As this is not
+and in-depth tutorial on docker, those with Docker, containers or cgroups see
+[docker.com][docker].
+
+
+## Building the TVHeadend image
+While it is recommended to pull the image from the [GHCR][ghcr] (GitHub
+Container Registry), it is certainly possible to build the image locally.
+
+To do so, clone this repository:
+
+$ git clone https://github.com/tvheadend/tvheadend
+
+Then, from within the repository
+
+```sh
+docker image build
+    --rm \
+    --tag 'tvheadend:issue-123' \
+    './'
+```
+
+The tag 'issue-123' in the example, is just that, an example, anything can be
+used for the tag.
+
+> _Note_: Omitting the tag, will use `latest` by default.
+
+
+## Running the TVHeadend image
+If the container wasn't built with the above instructions, it can be optionally
+be pulled from the [GHCR][ghcr] first instead.
+
+```sh
+docker image pull ghcr.io/tvheadend/tvheadend:latest
+```
+
+> _Note_: The `latest` tag can be replaced with any desired tag, including
+> `master` for the git development version.
+
+Running the TVHeadend container is then done as follows.
+
+```sh
+docker container run \
+    --interactive \
+    --name 'TVHeadend_container_01' \
+    --rm \
+    --tty \
+    'ghcr.io/tvheadend/tvheadend:issue-123' \
+    --firstrun
+```
+
+> _Note_: Docker will try to pull a container it doesn't know about yet. So if
+> the container was not previously built or pulled, the `run` sub command
+> will try to pull it instead. Likewise, `--pull always` can be used to force
+> a pull. See the docker documentation for more details.
+
+The above will now run TVHeadend and the log output should be visible.
+
+In the snippet above, the `--firstrun` flag was used. This flag is of course
+optional. Please do read the remainder of the next chapter
+[Persistently storing configuration](#Persistently-storing-configuration)
+to learn more about where configuration is stored.
+
+
+## Persistently storing configuration
+Containers do not store files persistently, they are ephemeral by design.
+Obviously, storing of configuration (or video) data is desirable and of course
+is there a solution for this. There are three (probably more) potential options.
+
+It is also important be aware that the configuration directory can be different
+depending on how TVHeadend is started. If tvheadend is started, and a directory
+exists in `/var/lib/tvheadend` or `/etc/tvheadend` which matches the UID of the
+user executing TVHeadend, those locations will be preferred. Since the container
+will always run as the user `tvheadend` and these locations are always created,
+TVHeadend in the container will always use `/var/lib/tvheadend` as its
+configuration directory.
+
+Before diving more deeply into things, the current container has two default
+volumes defined, `/var/lib/tvheadend` and `/var/lib/tvheadend/recordings`. These
+can also be defined as named/external volumes as well as more that can be added.
+
+
+### Secrets
+Docker secrets are files that can be mounted in a container. They are not
+ever written to disk (they live in `/run/secrets/`). While useful, not
+applicable, as they can't be controlled runtime, and store one file per secret.
+
+
+### Config
+Very similar to Secrets, but stored on disk within the container.
+
+
+### Volumes
+Volumes come in a few flavors actually, but the basics are the same. A local
+directory is mounted inside the container. The flavors come in the form of
+where to get the source directory from.
+
+
+#### Local directory
+Lets say the docker container should share the configuration files with the
+local user. In such case, the local `.config/hts` directory is needed within
+the container, can be easily mounted with the following example. Note that
+the `--volume` flag requires a absolute path.
+
+```sh
+docker container run \
+    --interactive \
+    --name 'TVHeadend_container_01' \
+    --rm \
+    --tty \
+    --user "$(id -u):$(id -g)" \
+    --volume '/home/user/.config/hts:/var/lib/tvheadend:rw' \
+    --volume '/home/user/Videos:/var/lib/tvheadend/recordings:rw' \
+    'ghcr.io/tvheadend/tvheadend:issue-123'
+```
+
+> _Note_: The `--user` flag is also used here, to ensure file-ownership does
+> not change. By default the TVHeadend container runs as user `tvheadend`
+> which may not have the same UID or GID as the local user. If the `id` command
+> is not available, `"1000:1000"` could be used instead, where `1000` would be
+> the actual UID and GID for the current user. The `rw` (read-write) or `ro`
+> (read-only) flags can set the access mode, but are optional.
+
+
+#### Anonymous Volumes
+Docker manage volumes also internally. This is actually very common when using
+docker containers, as the volumes are fully docker managed. It is even possible
+to share volumes between containers, e.g. have multiple TVHeadend instances
+running, with their own configuration, but sharing the recordings volume.
+
+The TVHeadend container already creates an anonymous volume by default, so that
+configuration is stored/re-used. Anonymous volumes are not 'forever persistent'
+and are removed by regular cleanup actions (`docker system prune` for example).
+
+
+#### Named Volumes
+Docker named volumes are manually created and persistently stored. For long
+term use (using a server for example), they are the preferred way of handling
+data. Docker compose can create them automatically (more on that later) but
+generally, a volume is created beforehand as such.
+
+$ docker volume create 'hts_config'
+
+and mounted as:
+
+```sh
+docker container run \
+    --interactive \
+    --name 'TVHeadend_container_01' \
+    --rm \
+    --tty \
+    --user "$(id -u):$(id -g)" \
+    --volume 'hts_config:/var/lib/tvheadend:rw' \
+    --volume '/home/user/Videos:/var/lib/tvheadend/recordings:rw' \
+    'ghcr.io/tvheadend/tvheadend:issue-123'
+```
+
+
+### Additional files
+It is possible to use the volumes concept to add additional files to an
+dockerized setup. For example if there is a volume holding various picons,
+which are created and managed/updated through a different container. The volume
+can be simply added in the same way.
+
+
+```sh
+docker container run \
+    --interactive \
+    --name 'TVHeadend_container_01' \
+    --rm \
+    --tty \
+    --user "$(id -u):$(id -g)" \
+    --volume 'hts_config:/var/lib/tvheadend:rw' \
+    --volume '/home/user/Videos:/var/lib/tvheadend/recordings:rw' \
+    --volume 'picons_volume:/usr/share/picons' \
+    'ghcr.io/tvheadend/tvheadend:issue-123'
+```
+
+> _Note_: The same can be done for directories holding binaries or scripts
+> but beware that this may not always work as expected. For example if a
+> python script is added, and requires the python interpreter, which is not
+> available in the TVHeadend container, the binary will be there, but it cannot
+> run. The same is true for an executable, if the libraries it depends on are
+> not there, it will fail to run. If in doubt, entering the container by
+> appending `/bin/sh` and running the binary or `ldd`ing may give a clue.
+
+
+### Environment variables
+TVHeadend can also consume environment variables for additional configuration.
+Most notably being the timezone. Environment variables can be easily added from
+the commandline.
+
+```sh
+docker container run \
+    --env 'TZ="Etc/UTC"' \
+    --interactive \
+    --name 'TVHeadend_container_01' \
+    --rm \
+    --tty \
+    --user "$(id -u):$(id -g)" \
+    --volume 'hts_config:/var/lib/tvheadend:rw' \
+    --volume '/home/user/Videos:/var/lib/tvheadend/recordings:rw' \
+    --volume 'picons_volume:/usr/share/picons' \
+    'ghcr.io/tvheadend/tvheadend:issue-123'
+```
+
+
+## Configuring devices
+TVHeadend gets most of its use via external devices, DVB tuners and the like.
+The following section is completely optional however if no devices need to be
+mapped to the container however.
+
+There are a some caveats depending on static or dynamic devices.
+
+In both cases, the device needs to exist however when starting docker and
+the permissions to access the device need to be correct. The TVHeadend
+container is by default part of the `video`, `audio` and `usb` groups,
+which seems to use the same UID on at least Gentoo and Alpine, but Arch has
+different GID's.
+
+> _Note_: If there is no shared GID, and no desire to change the GID of the
+> host, it is also possible to give **HIGHLY UNRECOMMENDED** 666 permissions to
+> the device.
+
+
+### Static devices
+For static devices, that are not added or removed while the container is
+running, this is easy enough with the add the `--device` flag. Assuming
+TVHeadend is to take care of all devices, the entire dvb directory can be
+shared.
+
+```sh
+docker container run \
+    --env 'TZ="Etc/UTC"' \
+    --device '/dev/dvb' \
+    --interactive \
+    --name 'TVHeadend_container_01' \
+    --rm \
+    --tty \
+    --user "$(id -u):$(id -g)" \
+    --volume 'hts_config:/var/lib/tvheadend:rw' \
+    --volume '/home/user/Videos:/var/lib/tvheadend/recordings:rw' \
+    --volume 'picons_volume:/var/lib/picons' \
+    'ghcr.io/tvheadend/tvheadend:issue-123'
+```
+
+
+### Dynamic devices
+Dynamic devices are a lot harder to deal with, due to their dynamic nature.
+For DVB devices, that only create and remove themselves from `/dev/dvb` this
+should work. Devices however that get exposed by their exact path, things
+are less easy.
+
+One thing that seems to work quite well, is to create a specific udev rule,
+that creates a symlink the device seems to work well. The following example
+exposes a USB serial converter as `/dev/myserialdevice`.
+
+```
+ACTION=="add",
+SUBSYSTEM=="tty",
+ATTRS{idVendor}=="1236",
+ATTRS{idProduct}=="5678",
+ATTRS{serial}=="12345678",
+MODE="660",
+TAG+="uaccess",
+SYMLINK+="myserialdevice"
+```
+
+> _Note_: Worse comes to worst, the entire `/dev` directory could be device
+> mounted or the `--privileged` flag, but are **HIGHLY UNRECOMMENDED** from
+> a security (and isolation) perspective.
+> Also `udevadm info -q all -n '/dev/mydevice'` can be used to inspect a device
+> and also show any currently installed symlinks.
+> Finally, if permission access is not working, the `MODE` could be set to an
+> insecure `666`.
+
+### Other devices
+The above section spoke mostly of adding DVB devices to the container, but
+other devices can be added as well, for example `/dev/dri` to map a GPU for
+encoding acceleration, assuming the needed tools are available to do so.
+
+> _Tip_: It is actually better to have a dedicated container running conversion
+> and share storage locations via volumes instead.
+
+## Network access
+TVHeadend is a network connected device, but by default, docker will not map
+any ports that a service listen to the host. Full network isolation. As such
+network ports need to be published to the host. The `--publish` flag does so.
+In the example below, a range of ports gets published to all devices via the
+IP address `0.0.0.0`. This can be restricted to a specific interface via its
+specific IP address.
+
+```sh
+docker container run \
+    --device '/dev/dvb' \
+    --env 'TZ="Etc/UTC"' \
+    --interactive \
+    --name 'TVHeadend_container_01' \
+    --publish "0.0.0.0:9981-9982:9981-9982/tcp" \
+    --rm \
+    --tty \
+    --user "$(id -u):$(id -g)" \
+    --volume 'hts_config:/var/lib/tvheadend:rw' \
+    --volume '/home/user/Videos:/var/lib/tvheadend/recordings:rw' \
+    --volume 'picons_volume:/var/lib/picons' \
+    'ghcr.io/tvheadend/tvheadend:issue-123'
+```
+
+
+> _Note_: A proper docker setup isolates container's networking completely with
+> unique interfaces per container. The default `run` command as used throughout
+> this guide however, uses the default docker network, which means that
+> individual containers are actually on the same network internally. Think of
+> it as a networking router controlled by docker, where all containers are
+> plugged into. A proper setup can be done with the normal `docker run`, but
+> is out of scope, as it is better to use `docker compose` for that. More on
+> this later.
+
+
+### CAClient access
+Using a CAClient over the network, requires that both containers are on the
+same network, as they have to be to reach each other. With the default
+network setup of `docker run`, this shouldn't be an issue generally.
+
+
+### Firewall
+There currently exists a bug with docker, in that a default firewall policy of
+`DROP` on the `INPUT` chain breaks certain container to container traffic.
+
+While it is possible to craft `iptables` rules to fix this, the dynamic nature
+of everything makes this very tricky in general. The only work around is
+to set the default policy to `ACCEPT` and ensure the chain `DROPS` all packets.
+
+Assuming `ppp0` is the internet connection that is a monitored interface and
+everything else is `okay`. On an empty/firewall;
+
+$ iptables -A -i ppp0 -j DROP
+$ iptables -P INPUT DROP
+
+
+## Troubleshooting
+To trouble shoot device pass through, networking, or other permission related
+ales, it is possible to start the container in privileged mode, connect directly
+to the host network and run TVHeadend as root.
+
+All are **NOT** recommended for normal use, but can help isolate issues.
+
+$ docker container run --privileged --user 'root:root' --network 'host' ...
+
+> _Tip_: After running the container as root, make sure to change all
+> ownership back to `tvheadend:tvheadend`, most likely the configuration files.
+> This is best done from within the container using
+>
+> $ chmod -R 'tvheadend:tvheadend' '/home/tvheadend'
+>
+
+
+## Compose
+Docker compose can be used to define whole cluster of services. While this entry
+will not be an expert piece on how to properly define compose services and setup
+whole service clusters, here is an example that puts all of the above in a
+single file. It is here for reference only and users are expected to know what
+they are doing when using this.
+
+```yaml
+networks:
+  tvheadend: {}
+  oscam: {}
+
+volumes:
+  tvheadend:
+  oscam:
+
+services:
+  oscam:
+    image: docker.io/olliver/oscam:latest # TODO
+    cap_drop:
+      - all
+    ulimits:
+      nproc: 64
+      nofile:
+        soft: 1024
+        hard: 65535
+    devices:
+      - /dev/cardreader
+    env_file:
+      - common.env
+    volumes:
+      - oscam:/etc/oscam:rw
+    networks:
+      - oscam
+    expose:
+      - "1988/udp"
+      - "15000/tcp"
+    ports:
+      - "8888:8888/tcp"
+    command: -d 64
+    restart: unless-stopped
+    healthcheck:
+      test: wget localhost:8888 2>&1 | grep -q 'connected'
+
+
+services:
+  tvheadend:
+    image: ghcr.io/tvheadend/tvheadend:latest
+    cap_drop:
+      - all
+    ulimits:
+      nproc: 256
+      nofile:
+        soft: 8192
+        hard: 65535
+    devices:
+      - /dev/dvb/
+    environment:
+      - TZ: 'Europe/Amsterdam'
+    volumes:
+      - tvheadend:/var/lib/tvheadend:rw
+      - /export/recordings:/var/lib/tvheadend/recordings:rw
+    networks:
+      - tvheadend
+      - oscam
+    ports:
+      - "9981-9982:9981-9982/tcp"
+    command: --config '/var/lib/tvheadend' --nosatip
+    restart: unless-stopped
+    healthcheck:
+      test: wget -O - -q 'http://localhost:9981/ping' | grep -q 'PONG'
+```
+
+> _Tip_: The `ulimits` are optional but recommended. Oscam uses a `common.env`
+> file holding the Timezone, TVHeadend uses a variable, both achieve the same.
+
+> _Note_: TVHeadend automatically detects the location for storage, the above
+> is just an example, if the location is changed here however, make sure to
+> update the configuration in the UI to match.
+
+
+## Developing using a container
+While it is certainly easy and possible to develop using `docker image build`,
+it is quite slow, as some setup and teardown work needs to be done before
+compilation can be done, also already compiled objects get discarded.
+
+Instead, the `builder` container, an intermediate step, can be used instead
+and the source directory volume mounted herein.
+
+```sh
+docker image build
+    --rm \
+    --tag 'tvheadend:builder' \
+    --target 'builder' \
+    './'
+```
+
+> _Note_: Because the way the builder is set up, it will build everything
+> once regardless. This is to keep the current `Containerfile` simple.
+> Other then the cost of some time, it is harmless.
+
+With the built `builder` image, the container can be entered, and make can be
+issued as normal.
+
+```sh
+docker container run \
+    --interactive \
+    --rm \
+    --tty \
+    --user "$(id -u):$(id -g)" \
+    --volume "$(pwd):/workdir" \
+    --workdir '/workdir' \
+    'tvheadend:builder' '/bin/sh'
+./configure
+make
+```
+
+> _Tip_: It might be tempting to short-circuit the command `/bin/sh` by
+> replacing it with `make`. However it will still be slower due to the
+> container creation/teardown, but otherwise functionally identical.
+
+> _Note_: As the current working dir is volume mounted into the container,
+> all changes done by the container will be made on the regular source code
+> directory. As such, exiting and re-starting the container won't remove any
+> intermediate object files etc. As the build-container runs normally runs as
+> root, the `--user` flag has to be set as otherwise files created will be
+> owned by root when exiting the container.
+
+
+[docker]: https://www.docker.com
+[ghcr]: https://github.com/tvheadend/tvheadend/pkgs/container/tvheadend
index f54059bbad4d2b327c7d599b0340c86a2e4c928d..de75d35607478eab7665116e2f43dbf04135584b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -40,6 +40,14 @@ It supports the following outputs:
   * HTSP (own protocol)
   * SAT>IP
 
+Running in docker
+-----------------
+Running in docker can be as simple as
+
+       $ docker run --rm ghcr.io/tvheadend/tvheadend:latest
+
+See [README.Docker.md](README.Docker.md) in this repository for more details.
+
 How to build for Linux
 ----------------------
 
index 0d729023cdae564e430bbc218a9d677912ab7e27..970404d2ee5830ec49112632b03d61ee9d653dd8 100755 (executable)
--- a/configure
+++ b/configure
@@ -283,7 +283,6 @@ int test(void)
 check_cc_snippet time_ld '
 #define _FILE_OFFSET_BITS 64
 #define _TIME_BITS 64
-#include <assert.h>
 #include <stdio.h>
 #include <time.h>
 #define TEST test
@@ -297,7 +296,6 @@ int test(void)
 check_cc_snippet time_lld '
 #define _FILE_OFFSET_BITS 64
 #define _TIME_BITS 64
-#include <assert.h>
 #include <stdio.h>
 #include <time.h>
 #define TEST test
diff --git a/support/container-entrypoint.sh b/support/container-entrypoint.sh
new file mode 100755 (executable)
index 0000000..7a506b1
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2023 Olliver Schinagl <oliver@schinagl.nl>
+#
+# A beginning user should be able to docker run image bash (or sh) without
+# needing to learn about --entrypoint
+# https://github.com/docker-library/official-images#consistency
+
+set -eu
+
+bin='tvheadend'
+
+# run command if it is not starting with a "-" and is an executable in PATH
+if [ "${#}" -le 0 ] || \
+   [ "${1#-}" != "${1}" ] || \
+   [ -d "${1}" ] || \
+   ! command -v "${1}" > '/dev/null' 2>&1; then
+       entrypoint='true'
+fi
+
+exec ${entrypoint:+${bin:?}} "${@}"
+
+exit 0