Skip to content

Commit 578e6e7

Browse files
committed
Containerize semonitor
To be able to track dependencies better, make development easier and have a more reliable means of distributing and managing semonitor, it is good to put it isolate it within a container. Everything should still be usable as before, but care needs to be taken when running pipelines, as if these are not properly passed into the container, one is piping the outside of the container. The result would be the same, but either two docker instances would need to be run or the second command needs to be available on the host. Both are not ideal solutions. Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
1 parent b53f02a commit 578e6e7

9 files changed

Lines changed: 323 additions & 0 deletions

File tree

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.circleci/
2+
.git/
3+
test/
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: Create and publish Container image
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
tags:
8+
- 'v*'
9+
pull_request:
10+
branches:
11+
- master
12+
13+
env:
14+
REGISTRY: ghcr.io
15+
IMAGE_NAME: ${{ github.repository }}
16+
TEST_TAG: ${{ github.repository }}:test_tag
17+
18+
jobs:
19+
build-and-push-image:
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: read
23+
packages: write
24+
strategy:
25+
matrix:
26+
include:
27+
- container: Containerfile.debian
28+
autotag: false
29+
suffix: -debian
30+
- container: Containerfile.alpine
31+
autotag: auto
32+
runuser: apk add --no-cache bash runuser &&
33+
34+
steps:
35+
- name: Checkout repository
36+
uses: actions/checkout@v3
37+
with:
38+
lfs: true
39+
40+
- name: Set up QEMU
41+
uses: docker/setup-qemu-action@v2
42+
43+
- name: Set up Docker Buildx
44+
uses: docker/setup-buildx-action@v2
45+
46+
- name: Login to Container registry
47+
uses: docker/login-action@v2
48+
with:
49+
registry: ${{ env.REGISTRY }}
50+
username: ${{ github.actor }}
51+
password: ${{ secrets.GITHUB_TOKEN }}
52+
53+
- name: Docker meta
54+
id: meta
55+
uses: docker/metadata-action@v4
56+
with:
57+
images: |
58+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
59+
tags: |
60+
type=ref,event=branch
61+
type=ref,event=pr
62+
type=edge
63+
type=semver,pattern={{version}}
64+
type=semver,pattern={{major}}.{{minor}}
65+
type=semver,pattern={{major}}
66+
flavor: |
67+
latest=${{ matrix.autotag }}
68+
suffix=${{ matrix.suffix }}
69+
70+
- name: Build and export
71+
uses: docker/build-push-action@v4
72+
with:
73+
context: .
74+
file: Containerfile
75+
load: true
76+
tags: ${{ env.TEST_TAG }}
77+
78+
- name: Test
79+
run: >
80+
docker container run \
81+
--rm \
82+
--user 'root:root' \
83+
--volume "$(pwd)/test:/usr/local/src/semonitor/test" \
84+
${{ env.TEST_TAG }} \
85+
'/bin/sh' -c '${{ matrix.runuser }} runuser --user semonitor -- ./test/test.sh'
86+
87+
- name: Build and push
88+
uses: docker/build-push-action@v4
89+
with:
90+
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6
91+
context: .
92+
file: Containerfile
93+
push: ${{ github.event_name != 'pull_request' }}
94+
tags: ${{ steps.meta.outputs.tags }}
95+
labels: ${{ steps.meta.outputs.labels }}

Containerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Containerfile.alpine

Containerfile.alpine

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
#
3+
# Copyright (C) 2023 Olliver Schinagl <oliver@schinagl.nl>
4+
5+
ARG PYTHON_VERSION="3-alpine"
6+
7+
FROM index.docker.io/library/python:${PYTHON_VERSION}
8+
9+
COPY requirements.txt /tmp/
10+
11+
RUN apk add --no-cache --virtual .build-deps \
12+
gcc \
13+
musl-dev \
14+
linux-headers \
15+
&& \
16+
pip --no-cache-dir install --requirement '/tmp/requirements.txt' && \
17+
rm '/tmp/requirements.txt' && \
18+
apk del .build-deps && \
19+
install -d -m 0775 '/usr/local/src' && \
20+
addgroup -S 'semonitor' && \
21+
adduser -D -G 'semonitor' -h '/usr/local/src/semonitor' -s '/bin/nologin' -S 'semonitor' && \
22+
adduser 'semonitor' 'usb' && \
23+
install -d -m 0775 -g 'semonitor' -o 'semonitor' '/var/lib/semonitor'
24+
25+
VOLUME /var/lib/semonitor
26+
27+
WORKDIR /usr/local/src/semonitor/
28+
29+
COPY conversion /usr/local/src/semonitor/conversion
30+
COPY scripts/semonitor.sh /usr/local/bin/semonitor.sh
31+
COPY se /usr/local/src/semonitor/se
32+
COPY semonitor.py /usr/local/src/semonitor/semonitor.py
33+
COPY services/container-entrypoint.sh /init
34+
COPY utilities /usr/local/src/semonitor/utilities
35+
36+
USER semonitor
37+
38+
ENTRYPOINT [ "/init" ]

Containerfile.debian

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
ARG PYTHON_VERSION="3-slim"
2+
3+
FROM index.docker.io/library/python:${PYTHON_VERSION}
4+
5+
COPY requirements.txt /tmp/
6+
7+
RUN ARCH="$(dpkg --print-architecture | \
8+
sed -e 's|armel|rpi|g' -e 's|armhf|armmp|g' -e 's|i386|686|g')" && \
9+
BUILD_DEPS=" \
10+
gcc \
11+
linux-headers-${ARCH} \
12+
" && \
13+
apt-get update && apt-get install --yes ${BUILD_DEPS:+${BUILD_DEPS}} && \
14+
pip --no-cache-dir install --requirement '/tmp/requirements.txt' && \
15+
rm '/tmp/requirements.txt' && \
16+
apt-get purge --yes ${BUILD_DEPS:+${BUILD_DEPS}} && \
17+
apt-get autoremove --yes && \
18+
apt-get clean && \
19+
rm -f -r '/var/lib/apt' && \
20+
install -d -m 0775 '/usr/local/src' && \
21+
useradd -d '/usr/local/src/semonitor' -m -r -s '/usr/sbin/nologin' 'semonitor' && \
22+
install -d -m 0775 -g 'semonitor' -o 'semonitor' '/var/lib/semonitor'
23+
24+
VOLUME /var/lib/semonitor
25+
26+
WORKDIR /usr/local/src/semonitor/
27+
28+
COPY conversion /usr/local/src/semonitor/conversion
29+
COPY scripts/semonitor.sh /usr/local/bin/semonitor.sh
30+
COPY se /usr/local/src/semonitor/se
31+
COPY semonitor.py /usr/local/src/semonitor/semonitor.py
32+
COPY services/container-entrypoint.sh /init
33+
COPY utilities /usr/local/src/semonitor/utilities
34+
35+
USER semonitor
36+
37+
ENTRYPOINT [ "/init" ]

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Containerfile.debian

README.Docker.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Running semonitor in docker
2+
SEMonitor can be run within a Docker container. This provides isolation from
3+
other processes by running it in a containerized environment. As this is not
4+
and in-depth tutorial on docker, those with Docker, containers or cgroups see
5+
[docker.com][docker].
6+
7+
This guide is not a comprehensive guide, but just lists the most basic things!
8+
9+
10+
## Building the container
11+
Building the container is only needed if the official one is not sufficient,
12+
or when developing on semonitor.
13+
14+
To build the image, the following can be used, where `ISSUE-123` is just used
15+
as an example. It is important in that it will be re-used later.
16+
17+
```sh
18+
docker image build \
19+
--file 'Containerfile' \
20+
--rm \
21+
--tag 'semonitor:ISSUE-123' \
22+
'./'
23+
```
24+
25+
26+
## Running tests
27+
The tests are not included in the container image, so we volume mount them to
28+
make them available to the installed semonitor application.
29+
30+
```sh
31+
docker container run \
32+
--interactive \
33+
--rm \
34+
--tty \
35+
--user 'root:root' \
36+
--volume "$(pwd)/test:/usr/local/src/semonitor/test" \
37+
'semonitor:ISSUE-123' \
38+
'/bin/sh' -c 'runuser --user semonitor -- ./test/test.sh'
39+
```
40+
41+
42+
## Running serial device
43+
Running the semonitor on a serial device using the official latest image can
44+
be done as follows.
45+
46+
```sh
47+
docker container run \
48+
--device '/dev/solaredge0:/dev/ttyUSB0' \
49+
--interactive \
50+
--rm \
51+
--tty \
52+
--volume "$(pwd)/semonitor_logs/:/semonitor/" \
53+
'ghcr.io/jbuehl/solaredge:latest' \
54+
semonitor.sh \
55+
-a \
56+
-b 115200 \
57+
-m \
58+
-o "/semonitor/json/$(date +%Y%m%d).json" \
59+
-r "/semonitor/rec/$(date +%Y%m%d).rec" \
60+
-s '1234567' \
61+
-t 4 \
62+
'/dev/ttyUSB0'
63+
```
64+
65+
## Using compose
66+
It is also possible to run the container using `docker compose`. Here an
67+
example. The device used is a udev symlinked serial to USB adapter, whith
68+
the appropriate permissions.
69+
70+
```yaml
71+
networks:
72+
semonitor: {}
73+
74+
volumes:
75+
semonitor:
76+
77+
services:
78+
sslh:
79+
image: ghcr.io/jbuehl/solaredge:master
80+
cap_drop:
81+
- all
82+
ulimits:
83+
nproc: 64
84+
nofile:
85+
soft: 4194304
86+
hard: 16777216
87+
devices:
88+
- /dev/solaredge:/dev/ttyUSB0
89+
env_file:
90+
- common.env
91+
volumes:
92+
- semonitor:/var/lib/semonitor:rw
93+
networks:
94+
- semonitor
95+
expose:
96+
- "80/tcp"
97+
- "22221-22222/tcp"
98+
command: -a -b 115200 -m -o "/var/lib/semonitor/json/__date__.json" -r "/var/lib/semonitor/rec/__date__.rec" -s '1234567' -t 4 '/dev/ttyUSB0'
99+
restart: unless-stopped
100+
```

scripts/semonitor.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/sh
2+
3+
set -eu
4+
if [ -n "${DEBUG_TRACE_SH:-}" ] && \
5+
[ "${DEBUG_TRACE_SH:-}" != "${DEBUG_TRACE_SH#*"$(basename "${0}")"*}" ] || \
6+
[ "${DEBUG_TRACE_SH:-}" = 'all' ]; then
7+
set -x
8+
fi
9+
10+
se_date_fmt="${SE_DATE_FMT:-%Y%m%d}"
11+
se_start_year="${SE_START_YEAR:-2023}"
12+
13+
# Wait for a reasonable date to be set
14+
while [ "$(date '+%Y')" -lt "${se_start_year}" ]; do
15+
echo "Date is to far in the past ($(date '+%Y') < ${se_start_year})."
16+
echo 'Override by setting "SE_START_YEAR" environment variable.'
17+
sleep 1
18+
done
19+
20+
args="$(echo "${@}" | sed "s|__date__|$(date "+${se_date_fmt}")|g")"
21+
22+
exec '/usr/local/src/semonitor/semonitor.py' ${args:+${args}}
23+
24+
exit 0

services/container-entrypoint.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/sh
2+
# SPDX-License-Identifier: GPL-2.0-or-later
3+
#
4+
# Copyright (C) 2023 Olliver Schinagl <oliver@schinagl.nl>
5+
#
6+
# A beginning user should be able to docker run image bash (or sh) without
7+
# needing to learn about --entrypoint
8+
# https://github.com/docker-library/official-images#consistency
9+
10+
set -eu
11+
12+
bin='semonitor.sh'
13+
14+
# run command if it is not starting with a "-" and is an executable in PATH
15+
if [ "${#}" -le 0 ] || \
16+
[ "${1#-}" != "${1}" ] || \
17+
[ -d "${1}" ] || \
18+
! command -v "${1}" > '/dev/null' 2>&1; then
19+
entrypoint='true'
20+
fi
21+
22+
exec ${entrypoint:+${bin:?}} "${@}"
23+
24+
exit 0

0 commit comments

Comments
 (0)