Skip to content

Instantly share code, notes, and snippets.

@thirdgen88
Last active October 8, 2022 20:25
Show Gist options
  • Save thirdgen88/c6439d82b5ea720d62ab0a7e00e7b1a1 to your computer and use it in GitHub Desktop.
Save thirdgen88/c6439d82b5ea720d62ab0a7e00e7b1a1 to your computer and use it in GitHub Desktop.
ICC 2022 Containerized Ignition - Session Notes

Running Ignition in a Containerized Environment

📖 Below are some links to resources referenced in the session presentation.

🛠 Curious about how I made some of the presentation content?

  • asciinema - Recording terminal sessions to .cast files.
  • agg - Convert .cast files to efficiently sized animated GIFs.
  • gifsicle - Modify .gif files from agg to remove looping (or a lot more).

🧩 Other Files in this Gist and a brief description

  • Dockerfile - Aggressively commented version of the Dockerfile from the Ignition Derived Image Example repo. You'll still want to probably just use that repo as a baseline, but the extra comments here shed some useful light on things.
  • docker-compose-bind-mount.yml - Example showing a bind-mount and the init container concept covered in the session.
# Example shown in the session for bind-mount with init container via Docker Compose
---
services:
init-gateway:
image: inductiveautomation/ignition:8.1.19
entrypoint: sh -c
volumes:
# We mount this in the container as `/data` to not conflict with built-in files
# at `/usr/local/bin/ignition/data`
- ./gateway-data:/data
command:
# Seed the files from the image to our /data if marker file not present, skip
# if the marker file exists (and implicitly exit cleanly with status code 0)
- |
if [ ! -f /data/.ignition-seed-complete ]; then
touch /data/.ignition-seed-complete ;
cp -dpR /usr/local/bin/ignition/data/* /data/ ;
fi
gateway:
image: inductiveautomation/ignition:8.1.19
ports:
- 8088:8088
volumes:
- ./gateway-data:/usr/local/bin/ignition/data
environment:
GATEWAY_ADMIN_PASSWORD: icc2022
ACCEPT_IGNITION_EULA: "Y"
IGNITION_EDITION: standard
# Use depends_on to only launch this container if our init container exits cleanly
depends_on:
init-gateway:
condition: service_completed_successfully
command: >
-n Ignition-icc2022
# syntax=docker/dockerfile:1.4
# ^-- this line at the top indicates the "features" to consider in this Dockerfile.
# ref: https://docs.docker.com/engine/reference/builder/#syntax
# This argument is used to direct which image tag of the upstream Ignition image to use.
ARG IGNITION_VERSION=${IGNITION_VERSION}
# We label this first stage of our multi-stage build "prep"
FROM inductiveautomation/ignition:${IGNITION_VERSION} as prep
# Install some prerequisite packages--these are programs that our helper scripts
# will be using. We're not worrying about cleaning up the package indexes here
# because we're not in the final stage that will become the real image.
RUN apt-get update && apt-get install -y wget ca-certificates jq zip unzip sqlite3
# These arguments have download links and file integrity checksums. We use indirection to reference these based on the
# middle identifiers like `AZUREIOTINJECTOR` or `MQTTENGINE`. This way we can supply just those identifiers in the
# `SUPPLEMENTAL_MODULES` arg when performing the build.
ARG SUPPLEMENTAL_AZUREIOTINJECTOR_DOWNLOAD_URL="https://files.inductiveautomation.com/third-party/cirrus-link/4.0.11/Azure-Injector-signed.modl"
ARG SUPPLEMENTAL_AZUREIOTINJECTOR_DOWNLOAD_SHA256="17babfcc28386262b5b67c5d7109dd4e0c346461d7a9add7f086fddd952574c0"
ARG SUPPLEMENTAL_MQTTTRANSMISSION_DOWNLOAD_URL="https://files.inductiveautomation.com/third-party/cirrus-link/4.0.11/MQTT-Transmission-signed.modl"
ARG SUPPLEMENTAL_MQTTTRANSMISSION_DOWNLOAD_SHA256="15e6b7f548dc909ffba1af44527e494e918192295835390f2e921a93f243ca07"
ARG SUPPLEMENTAL_MQTTTRANSMISSIONNIGHTLY_DOWNLOAD_URL="https://ignition-modules-nightly.s3.amazonaws.com/Ignition8/MQTT-Transmission-signed.modl"
ARG SUPPLEMENTAL_MQTTTRANSMISSIONNIGHTLY_DOWNLOAD_SHA256="notused"
ARG SUPPLEMENTAL_MQTTENGINE_DOWNLOAD_URL="https://files.inductiveautomation.com/third-party/cirrus-link/4.0.11/MQTT-Engine-signed.modl"
ARG SUPPLEMENTAL_MQTTENGINE_DOWNLOAD_SHA256="bebfdab4f423b5ffa40b3e4114781145dadd0eadb3500f48ca904365a838e0c9"
ARG SUPPLEMENTAL_MQTTENGINENIGHTLY_DOWNLOAD_URL="https://ignition-modules-nightly.s3.amazonaws.com/Ignition8/MQTT-Engine-signed.modl"
ARG SUPPLEMENTAL_MQTTENGINENIGHTLY_DOWNLOAD_SHA256="notused"
ARG SUPPLEMENTAL_MQTTDISTRIBUTOR_DOWNLOAD_URL="https://files.inductiveautomation.com/third-party/cirrus-link/4.0.11/MQTT-Distributor-signed.modl"
ARG SUPPLEMENTAL_MQTTDISTRIBUTOR_DOWNLOAD_SHA256="a27fdf7b2e9ca74e4eac571283aae0829cefbe4c19776506cd2b092d543175e2"
ARG SUPPLEMENTAL_MQTTDISTRIBUTORNIGHTLY_DOWNLOAD_URL="https://ignition-modules-nightly.s3.amazonaws.com/Ignition8/MQTT-Distributor-signed.modl"
ARG SUPPLEMENTAL_MQTTDISTRIBUTORNIGHTLY_DOWNLOAD_SHA256="notused"
# This is the only argument that we expect to be supplied during the build, but there is handling for it
# being omitted below (in the case of no supplemental modules desired).
ARG SUPPLEMENTAL_MODULES
# Set working directory for this prep image and
WORKDIR /root
# Configure shell to be bash since we're going to use some more advanced syntax below that `sh` (the default)
# won't be able to accept. Also set default some options for bash that will help ensure that exits from
# sub-shells bubble up and report an error (and thus break the build).
SHELL [ "/usr/bin/env", "-S", "bash", "-euo", "pipefail", "-O", "inherit_errexit", "-c" ]
# Retrieve all targeted modules and verify their integrity. This helper script contains a lot of the logic and
# is designed to be used standalone. Note that `--chmod=0755` may not be supported by builders other than more
# recent versions of BuildKit. If you run into errors here, just omit it, and then put a
# `RUN chmod a+rx retrieve-modules.sh` statement after it to make sure it is executable. The `chmod` technique is
# especially nice for final images since it only results in a single layer.
COPY --chmod=0755 retrieve-modules.sh .
# Run our `retrieve-modules.sh` helper script, and feed it our comma-delimited list of identifiers, defaulting to
# a blank string. See Shell Parameter expansion in Bash user guide: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
# This helper script will indirect to the ARG's listed above and download the modules from their respective location
# as well as validate their file integrity. The image build will fail if a given downloaded module doesn't match
# the prescribed SHA256 hash.
RUN ./retrieve-modules.sh \
-m "${SUPPLEMENTAL_MODULES:-}"
# Set CERTIFICATES/EULAS acceptance in gateway backup config db
# First we'll copy this base gateway backup file from our build context...
COPY base.gwbk .
# Then we'll copy in our helper scripts:
# - register-module.sh - used to auto-accept the Certificate and EULA for a given module in the SQLite Ignition DB
# - register-password.sh - used to set the base admin username/password instead of relying on password reset.
COPY --chmod=0755 register-module.sh register-password.sh ./
# We'll set a default for `GATEWAY_ADMIN_USERNAME`, which is consumed by the `register-password.sh` script.
ARG GATEWAY_ADMIN_USERNAME="admin"
# Okay, a lot to unpack here, let's go step by step. To start, we're using Docker build secrets here to make sure
# that the secret we're bringing in (the password to be set) is treated safely (i.e. tmpfs mounted). See this
# page for more information: https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information
RUN --mount=type=secret,id=gateway-admin-password \
# First we'll extract the embedded SQLite Config DB from the gateway backup
unzip -q base.gwbk db_backup_sqlite.idb && \
# Then we'll reconfigure our shell environment to allow nullglob...
shopt -s nullglob; \
# ... so that when we invoke this for-loop and there are no modules, it just quietly skips onward
for module in *.modl; do \
# Invoke our helper script and feed it the module filename and the config db. We'll run this
# for each module that we downloaded above.
./register-module.sh \
-f "${module}" \
-d db_backup_sqlite.idb; \
done; \
# Turn off the nullglob since we're done with it
shopt -u nullglob && \
# Invoke our `register-password.sh` helper script, which will modify the first entry in the `default`
# user source within the specified Ignition Config DB (from the GWBK). This particular invocation expects
# there to be a file (our mounted secret) with the contents being the password.
./register-password.sh \
-u "${GATEWAY_ADMIN_USERNAME}" \
-f /run/secrets/gateway-admin-password \
-d db_backup_sqlite.idb && \
# Finally, re-integrate the modified config db into the base gateway backup (zip file)
zip -q -f base.gwbk db_backup_sqlite.idb || \
# This special handling accounts for the situation where the zip command above exits with
# code 12 due to no action performed (the file is not changed).
if [[ ${ZIP_EXIT_CODE:=$?} == 12 ]]; then \
echo "No changes to internal database needed during module registration."; \
else \
# if something else went wrong, then we exit with the associated code which will fail the build.
echo "Unknown error (${ZIP_EXIT_CODE}) encountered during re-packaging of config db, exiting." && \
exit ${ZIP_EXIT_CODE}; \
fi
# Final Image - the layers from this point forward will end up in the final image.
# Note: we're starting with all of the layers from the upstream inductiveautomation/ignition image.
FROM inductiveautomation/ignition:${IGNITION_VERSION} as final
# Add supplemental packages, such as git if needed/desired
# First we'll update the apt package index, since it isn't present in the upstream image.
# These are intentionally purged during the image build because they'll be out-of-date and
# we don't want to bake that in.
RUN apt-get update && \
# We'll use noninteractive mode to make sure that `apt-get` doesn't try to prompt
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
# Here is the list of packages we're installing...
git && \
# ... and here we're wiping the cached package index back out now that we're done.
rm -rf /var/lib/apt/lists/*
# Notice that the above is a single `RUN` command to prevent accumulation of a ton of image layers
# Embed modules and base gwbk from prep image as well as entrypoint shim
# Notice that we're using `--from` here to copy not from our build context, but from the first
# stage of our multi-stage build, the one we named `prep`.
COPY --from=prep /root/*.modl ${IGNITION_INSTALL_LOCATION}/user-lib/modules/
COPY --from=prep /root/base.gwbk ${IGNITION_INSTALL_LOCATION}/base.gwbk
COPY --chmod=0755 docker-entrypoint-shim.sh /usr/local/bin/
# Supplement other default environment variables--this is a way you can preload configuration into
# your derived image. You can always override these when you run the container, such as you might do
# with `IGNITION_EDITION=edge`, for example.
ENV ACCEPT_IGNITION_EULA=Y \
IGNITION_EDITION=standard \
GATEWAY_MODULES_ENABLED=all
# Target the entrypoint shim for any custom logic prior to gateway launch
ENTRYPOINT [ "docker-entrypoint-shim.sh" ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment