OCI container

This app part provides the possibility for simplified integration and run of OCI (Open Container Initiative) container images of apps on the PLCnext device. For this topic the container engine Podman is used in conjunction with systemd to allow efficient and reliable management of containerized applications, leveraging Podman's rootless and deamonless architecture while maintaining full control over service management through systemd.

An OCI container will run although as a systemd service. Which means that systemd starts the containerized application and manages its entire lifecycle (start, stop, etc.). That will bring further useful functionalities such as:

  • Automatically starting the OCI container service on system boot
  • systemd features such as defining the order in which the containerized service runs and needed dependency checks
  • Service restart on error

The integration of OCI containers to systemd is done via Podman systemd units (formerly named Quadlets). For this purpose the app developer creates corresponding Podman unit files for the OCI container. The app part allows to define container services (*.container), pods (*.pod) and networks (*.network) unit files. The Podman generator converts the Podman unit files (formerly Quadlet files, see Minimal systemd unit file) to systemd service unit files, see Podman generate systemd.

General preparation steps to integrate an OCI container app part:

  • Generate your OCI image and save it as "tar.gz".
  • Write your own unit file accoding to the official Podman and systemd documentation.
    You can find more information on how to map your existing (docker) compose.yml parameters in a unit file section here for *.container, *.pod and *.network unit files. More unit file types are currently not supported by our AppManager.

The AppManager is responsible for integrating and generating all files needed to run the OCI containers provided by the apps. The developer provides the OCI image and unit files within the app container file and extends the app_info.json file, see Steps to create and integrate an OCI container.

Rootless operation

OCI containers provided by the PLCnext Technology Apps will run in rootless mode under the "plcnext_firmware" user.

The systemd unit files of started PLCnext Technology Apps with embedded OCI containers will be saved/generated automatically by the AppManager into the following search directory:

~/.config/containers/systemd/<App Identifier>/<container-name>_<App Identifier>.container

Minimal systemd unit file for integrating an OCI container

A .container unit file is the minimal necessary file to integrate an OCI container in a PLCnext Technology App.

Following a quite minimalistic example unit file:

[Unit]
Description=A simple quadlet test  

[Container]
# Container Name
ContainerName=busybox-custom  

# Image ID Image=b788295ed81332f3b9de83d69591b52266245db88536469f1aa8421269c7c227  

PublishPort= ${PORT}:80  

Volume=${APP_PERSISTENT_DIR}/data:/data  

[Service]
Restart=always  

[Install]
WantedBy=default.target

"Unit": 

Describes the systemd service and its dependencies. This is standard in all systemd unit files and defines the service meta data.

  • Description: Description of systemd service or related OCI container

 

"Container":

This section is Quadlet specific and defines the OCI container related options.

  • ContainerName: Specifies the name of the OCI container instance
  • Image: ID of OCI container image to use. See entry provided in the app part configuration: Necessary additions in the app description file
    • Only offline images provided by the app container are supported.
  • PublishPort: In this example, maps the specified host port ( ${PORT}) to the port 80 inside the container and is used to expose a webservice running inside the container.
  • Volume: Defines a volume mount from the host to the OCI container.
    • For storing persistent data it is mandatory to use the dedicated ${ARP_PATH_APPS_DATA_DIR} for the path root.
    • For storing temporary data it is mandatory to use the dedicated ${ARP_PATH_APPS_TEMP_DIR} for the path root.
    • Mounting system files is only possible with the rights of the plcnext_firmware user or plcnext group and should be used with care. Use the ":ro" addition whenever possible.

 

"Service":

Handles how the OCI container service should be started, stopped and restarted. Quadlet auto-generates the complex commands here, so users don't need to explicitly define ExecStart, ExecStop, etc.

  • Restart=always: the service is restarted automatically if the container stops for any reason, whether it's a crash, an error, or even a clean exit, systemd will restart the OCI container service to ensure it stays running.

 

"Install":

Defines when and how the service should be enabled and which target it should be associated with.

  • WantedBy=default.target: Default systemd target that the system boots into.

 

Environment variables can be used in the whole file and allows end users to adapt the configuration when necessary. As an example, the value of the environment variable ${PORT} will by set by the AppManager for the generated unit file according the environment file. The initial values for environment variables must be set in the app_info.json under the "ocicontainer" part and can be changed by the end user when necessary.

Environment file

The environment file is generated by the AppManager on app installation based on the environment variables entries defined in the OCI container app part configuration of the corresponding app and is used to persist environment variables. The layout follows a simple key-value pairs format, which can look like this:


envName1=value
envName2=value

 

In addition to custom environment variables specified by the app developer, the system also defines two predefined system variables:

  • APP_PERSISTENT_DIR: Points to the persistent directory specific to the app
    ${ARP_PATH_APPS_DATA_DIR}>/<App Identifier>/
  • APP_TEMPORARY_DIR: Points to the temporary directory specific to the app
    ${ARP_PATH_APPS_TEMP_DIR}>/<App Identifier>/
  • APP_NAME: App name from the app_info.json file
  • APP_ID: App identifier from the app_info.json file
  • APP_MOUNT_PATH: Path to read only mounted app container folder, which only exists when the app is started.

Remark: These both system variables are not included in the environment file and cannot be overridden.  

The following applies:

  • Contains variables that can later (after app installation) be changed by the user (e.g. port).
  • These variables can also be used by the app developer in the Podman unit (Quadlet) file.
  • Environment variables cannot be used in systemd generator, thats why those are replaced/merged by the AppManager within the generated Quadlet file.

Necessary additions in the app description file

If the optional JSON object "ocicontainer" is present in the app description file, the AppManager knows that a configuration for integration an OCI container is located in the app.  

The "ocicontainer" entry is structured as follows:

"ocicontainer" :
  {
    "quadletFiles" :
      [
        { "path": "/path/to/unitFile.container" },
        { "path": "/path/to/unitFile.pod" },
        { "path": "<path to quadlet file"> }"
      ]
      "environmentVariables": 
      [
       {
          "name": "varName1",
          "value": "1"
        },
        {
          "name": "varName2",
          "value": "2"
        }
      ],
      "images":
      [
        {
          "name"  : "<name>",
          "id": "<idHash>",
          "path"   : "<path to image file>"
        }
       ...
      ]
  }

"quadletFiles":

Specifies the paths to the Podman unit (Quadlet) file(s) according to the specification within the app file system.

  • At least one .container file is mandatory.

"environmentVariables":

Optional entry. Environment variables for the OCI container Podman unit (Quadlet) files as key-value pair JSON objects:

  • name: Name of environment variable
  • value: Value of environment variable

"images":

This entry describes the container images to be used for the OCI container. It must contain at least one image info config item. Every image config item contains the following fields:

Steps to create and integrate an OCI container

Create the Podman images for your containerized application:

  • See preparation steps.
  • Test the functionality of the OCI container application.
  • See Podman build.

Integrate the OCI container to your app:

  • Copy the image files for you OCI container application to your app directory.
  • Copy/Create the Quadlet files for the application to/in the app directory:
    • Used environment variables must be defined in the section for environment variables of the "ocicontainer" app part configuration.
  • Ensure that the needed support for temporary/persistent data storage is enabled an the needed working directories are configured in the "datastorage" app part configuration.
  • Add the optional app part "ocicontainer" for the shared libraries in your app description file app_info.json with the needed configuration.

Specifications and restrictions

General for OCI container App Part support:

  • Several OCI Containers from different Apps may be active in the system at the same time.
  • A PLCnext Technology App can contain only one OCI Container AppPart (entry).
  • The names of the activated OCI Containers daemons must not occur more than once.
  • Activation/deactivation of an OCI Container App Part does not require a system/firmware restart.
  • Bind mounts of the OCI container apps, as specified by the compose file, must be located in the persistent data folder of the app.
  • The lifetime of container processes is not tied to the firmware runtime.

Restrictions for Podman systemd units:

  • Pod Units:
    • App parts will not run in a Pod, but the user can use it if needed.
  • Network Units:
    • No restrictions
  • Volume Units:
    • Not supported for this step
  • Kube Units:
    • Not supported for this step
  • Image Units:
    • Not allowed for this step
    • Images can be added only offline: App Container must contain the image files.

Demo OCI container app

To demonstrate an app with an OCI container app part a simple Busybox image was created, which is installed in an app and started as a systemd service. The app description for the demo Hello OCI container app is as follows:

{ 
   "plcnextapp": { 
      "name": "PLCnextHelloOciContainerApp", 
      "identifier": "00000000000123", 
      "version": "0.9 beta", 
      "target": "AXC F 2152,AXC F 1152", 
      "minfirmware_version": "24.7.0", 
      "manufacturer": "Phoenix Contact Electronics" 
   }, 
   "datastorage": { 
      "persistentdata": true, 
      "temporarydata": true, 
      "directoriesToCreate": { 
          "temporary": [ 
              {
                 "path": "temp_test" 
              }
          ], 
          "persistent": [ 
              {
                 "path": "www" 
              }
          ] 
      } 
    }, 
    "ocicontainer" : { 
        "environmentVariables": [ 
          { 
              "name": "PORT", 
              "value": "2200"  
          } 
      ], 
      "quadletFiles": [ 
               {
                   "path":"/hello-world.container" 
               }
      ], 
      "images": [ 
          { 
              "name" : "plcnext-hello-world", 
              "id" : "57f067446d29f661ee55e675aff86527f4817933f10592f6f3d34870b99fed04", 
              "path" : "/images/plcnext-hello-world.tar.gz"
          } 
      ] 
    } 
}
  • Data storage: Both of persistent and temporary data storage are enabled.
    • The folder "temp_test" is created in the app temporary data folder.
    • The folder "www" is created in the app persistent data folder.
  • OCI container:
    • Environment variables: The user defined environment variable PORT is used in the OCI container Quadlet file
    • Quadlet files: one .container Quadlet file is integrated.
      • Environment variables are replaced with the corresponding value on Quadlet file merge.
      • The created temporara folder www is used as volume (by "datastorage" app part).
      • The image from the item "images" of the OCI container app part config is used.
  • Images: Defines the images to load, which is references in the given Quadlet file.

The referenced file plcnext-hello-world.tar.gz has the following content:

[Unit]
Description=Hello world demo app  

[Container]
# Container Name
ContainerName=plcnext-hello-world_${APP_UNIQUE_NAME}  

# Image ID
Image=57f067446d29f661ee55e675aff86527f4817933f10592f6f3d34870b99fed04  

PublishPort=${PORT}:8000  

# Volumes
Volume=${APP_PERSISTENT_DIR}/www:/www  

#Admin user ID #Group=1002  

[Service]
Restart=always  

[Install]
WantedBy=default.target

This Quadlet file used the environment variables, container image and temporary data folder created by the App configration in app_info.json.

Support in the PLCnext firmware

The OCI container app part type is supported in the PLCnext firmware from version 2025.0.0.

 

 


•  Web browser recommendation: Chrome/Edge 88 or newer, Firefox ESR 90 or neweror Safari  • 
• Published/reviewed: 2024-11-21 • Revision 17 •