Files
opsi/README.md

168 lines
6.9 KiB
Markdown

# opsi Docker Compose files
Docker Compose files to spin up an instance of [opsi](https://opsi.org) open source device management system. This specifically launches a so-called opsi Config Server. In opsi lingo Config Server is the central hub that includes among other components an opsi Depot Server. See also [opsi 4.3 English docs section "opsi Server"](https://docs.opsi.org/opsi-docs-en/4.3/server/overview.html) for reference.
# How to run
Add a `COMPOSE_ENV` file and save its location as a shell variable along with the location where this repo lives, here for example `/opt/containers/opsi` plus all other variables. At [env/fqdn_context.env.example](env/fqdn_context.env.example) you'll find an example environment file.
When everything's ready start opsi with Docker Compose, otherwise head down to [Initial setup](#initial-setup) first.
## Environment
Make sure the upstream [github.com/opsi-org/opsi-docker](https://github.com/opsi-org/opsi-docker) repository is checked out locally. We're going with example dir `/opt/git/github.com/opsi-org/opsi-docker/branch/main`. We're also assuming that this repo exists at `/opt/containers/opsi`.
```
export UPSTREAM_REPO_DIR='/opt/git/github.com/opsi-org/opsi-docker/branch/main'
export UPSTREAM_COMPOSE_FILE="${UPSTREAM_REPO_DIR%/}"'/opsi-server/docker-compose.yml'
export UPSTREAM_ENV_FILE="${UPSTREAM_REPO_DIR%/}"'/opsi-server/opsi-server.env'
export CONTEXT='ux_vilnius'
export COMPOSE_PROJECT_NAME='opsi-'"${CONTEXT}"
export COMPOSE_ENV=<add accordingly>
export COMPOSE_OVERRIDE='/opt/containers/opsi/compose.override.yaml'
```
In opsi's Git repo check out newest commit:
```
git -C "${UPSTREAM_REPO_DIR:?}" reset --hard origin/main
git -C "${UPSTREAM_REPO_DIR:?}" checkout main
git -C "${UPSTREAM_REPO_DIR:?}" pull
```
## Context
On your deployment machine create the necessary Docker context to connect to and control the Docker daemon on whatever target host you'll be using, for example:
```
docker context create fully.qualified.domain.name --docker 'host=ssh://root@fully.qualified.domain.name'
```
## Pull
Pull images from Docker Hub verbatim.
```
docker compose \
--project-name "${COMPOSE_PROJECT_NAME}" \
--file "${UPSTREAM_COMPOSE_FILE}" \
--file "${COMPOSE_OVERRIDE}" \
--env-file "${UPSTREAM_ENV_FILE}" \
--env-file "${COMPOSE_ENV}" \
pull
```
## Copy to target
Copy images to target Docker host, that is assuming you deploy to a machine that itself has no network route to reach Docker Hub or your private registry of choice. Copying in its simplest form involves a local `docker save` and a remote `docker load`. Consider the helper mini-project [quico.space/Quico/copy-docker](https://quico.space/Quico/copy-docker) where [copy-docker.sh](https://quico.space/Quico/copy-docker/src/branch/main/copy-docker.sh) allows the following workflow:
```
images="$(docker compose --project-name "${COMPOSE_PROJECT_NAME}" --file "${UPSTREAM_COMPOSE_FILE}" --file "${COMPOSE_OVERRIDE}" --env-file "${UPSTREAM_ENV_FILE}" --env-file "${COMPOSE_ENV}" config | grep -Pi -- 'image:' | awk '{print $2}' | sort | uniq)"
while IFS= read -u 10 -r image; do
copy-docker "${image}" fully.qualified.domain.name
done 10<<<"${images}"
```
This will for example copy over:
```
# docker image ls -a
REPOSITORY TAG IMAGE ID CREATED SIZE
grafana/grafana latest 0a7de979b313 2 weeks ago 723MB
redis/redis-stack-server latest 1ebedd176a23 7 weeks ago 513MB
uibmz/opsi-server 4.3 f07683f1828b 2 months ago 996MB
mariadb 10.7 895b6c8829c3 2 years ago 396MB
```
## Start
```
docker \
--context 'fully.qualified.domain.name' \
compose \
--project-name "${COMPOSE_PROJECT_NAME}" \
--file "${UPSTREAM_COMPOSE_FILE}" \
--file "${COMPOSE_OVERRIDE}" \
--env-file "${UPSTREAM_ENV_FILE}" \
--env-file "${COMPOSE_ENV}" \
up --detach
```
## Clean up
```
docker --context 'fully.qualified.domain.name' system prune -af
docker system prune -af
```
# Initial setup
We're assuming you run Docker Compose workloads with ZFS-based bind mounts. ZFS management, creating a zpool and setting adequate properties for its datasets is out of scope of this document.
## Datasets
Create ZFS datasets and set permissions as needed.
* Parent dateset
```
export "$(grep -Pi -- '^CONTEXT=' "${COMPOSE_ENV}" | xargs --max-lines 1)"
zfs create -o canmount=off zpool/data/opt
zfs create -o mountpoint=/opt/docker-data zpool/data/opt/docker-data
```
* Container-specific datasets
```
zfs create -p 'zpool/data/opt/docker-data/opsi-'"${CONTEXT}"'/grafana/data'
zfs create -p 'zpool/data/opt/docker-data/opsi-'"${CONTEXT}"'/mysql/data'
zfs create -p 'zpool/data/opt/docker-data/opsi-'"${CONTEXT}"'/opsi/data'
zfs create -p 'zpool/data/opt/docker-data/opsi-'"${CONTEXT}"'/redis/data'
```
All four opsi components, that it Grafana, MySQL, opsi and Redis have one subdirectory for their `data` volume. None of them require a `config` volume or other mounts.
* Create subdirs
```
mkdir -p '/opt/docker-data/opsi-'"${CONTEXT}"'/opsi/'{'.ssh','config','data','projects'}
```
* Change ownership
```
chown -R 472:472 'zpool/data/opt/docker-data/opsi-'"${CONTEXT}"'/grafana/data'
```
Here you'll want to accommodate the Grafana container. Per its [upstream Dockerfile](https://github.com/grafana/grafana/blob/main/Dockerfile) retrieved from `Dockerfile` in the [github.com/grafana/grafana](https://github.com/grafana/grafana) repository in August 2025 [all Grafana images that end up at Docker Hub](https://hub.docker.com/r/grafana/grafana/tags) are built with the directive:
```
USER "$GF_UID"
```
Where `"$GF_UID"` is populated with `ARG GF_UID="472"`.
## Additional files
With preparations out of the way opsi does not require any additional files present on your Docker host's filesystem, as bind mounts or via other means.
Head back up to [How to run](#how-to-run).
# Development
## Conventional commits
This project uses [Conventional Commits](https://www.conventionalcommits.org/) for its commit messages.
### Commit types
Commit _types_ besides `fix` and `feat` are:
- `refactor`: Keeping functionality while streamlining or otherwise improving function flow
- `docs`: Documentation for project or components
### Commit scopes
The following _scopes_ are known for this project. A Conventional Commits commit message may optionally use one of the following scopes or none:
- `mysql`: A change to how the `mysql` service component works
- `redis`: A change to how the `redis` service component works
- `grafana`: A change to how the `grafana` service component works
- `opsi_server`: A change to how the `opsi_server` service component works
- `build`: Build-related changes such as `Dockerfile` fixes and features.
- `mount`: Volume or bind mount-related changes.
- `net`: Networking, IP addressing, routing changes
- `meta`: Affects the project's repo layout, file names etc.