Development Techniques for Customizing UBI or Runtime Image Builds

Mindwatering Incorporated

Author: Tripp W Black

Created: 05/28 at 11:46 AM

 

Category:
RH OpenShift
Reference

Overview:
Notes regarding updating UBIs or Runtime Images.



Using oc new-app vs. oc new-build:

oc new-app:
- Creates build, creates application, creates deployment, and service resources, and deploys the app.
- The RUNTIME_ENV environmental variable(s) for the runtime pods of the deployment are not in the build YAML, but stored in the deployment object.
- Command oc new-build Docker --strategy option can be added to specify Docker or Source build (e.g. --strategy Docker), which otherwise is automatically determined.

Examples:
- Automatic stategy:
$ oc new-app --name <app-name> --build-env BUILD_ENV=<build_var_value1> --env RUNTIME_ENV=<runtime_var_value1> -i <image_stream_name>:<version> --context-dir <context_folder> https://<repository_fqdn>/<project_namespace>/<app_name_folder>

- Docker strategy:
$ oc new-app --name <app-name> --strategy Docker --context-dir <context-dir> https://<repository_fqdn>/<project_namespace>/<app_name_folder>

oc new-build:
- Creates build and creates image stream as needed
- Creates build object
- Useful in CI/CD strategies which manage image building and deployment separately
- Can only pass build environmental variables using -e and -env
- Use -env BUILD_LOGLEVEL to increase log level for troubleshooting (e.g. --build-env BUILD_LOGLEVEL=3)

Example:
$ oc new-build --name <app-name> --build-env BUILD_ENV=<build_var_value1> -i <image_stream_name>:<version> --context-dir <context_folder> https://<repository_fqdn>/<project_namespace>/<app_name_folder>

View builds:
$ oc get builds
<view pending and completed builds>

View last run logs:
$ oc logs buildconfig/app
<view logs>

View last app build logs:
$ oc logs build/<app-name>
<view logs>

Watch/follow the logs as the build generates them:
$ oc logs -f buildconfig/app
<view logs as they happen>

Troubleshoot post build failures:
- Get pod names:
$ oc get pods
<view pods created and if ready>

- Start interactive shell:
$ oc debug deploy/<app-name>
...
sh-4.x$


Declare/Using Volumes:
- Volume instruction provides explicit definition
- Allows visibility for administrators deploying the image to see storage requirements/usage


Expose Ports:
- Always use non-privileged ports 1024 and higher
- Use EXPOSE instruction for administrators deploying the image
- EXPOSE supports assignment of multiple ports
- Used by oc new-app command for the definitions added to Deployment and Service resources
- Exposed ports displayed on the Advanced options --> Target port drop-down list.
- Web Console UI supports only single assignment


Modifying an UBI or Runtime UBI Containerfile:
- Root group (0) must own the directories and files of the running container processes (+g rwx or g=u)
- As arbitrary users are not root, the container ports must run above the privileged port (< 1024)
- Containerfile RUN root chgrp example:

FROM registry.mindwatering.net:8443/namespace/appnm-123-minimal:1.2.3

ENV PORT=8080

USER root
RUN chgrp -R 0 directory && chmod -R g=rwx directory
USER 1001

ADD ...

RUN appnm ...

CMD appnm start



Application Start-up and Termination Pod Lifecycle and Script Framework:
- Application script with start-up and shutdown procedures commonly in entrypoint script or pod lifecycle PostStart, PreStop, and StopSignal hooks
- Pod lifecycle hooks run concurrently with container process, and will delay the pod status until the hook(s) complete
- Pod lifecycle hooks override signal definitions in the container image
- pod lifecycle signal hook example from K8s docs:

spec:
  restartPolicy: always
  os:
    name: linux
  containers:
    - name: my-container
      image: container-image:latest
      lifecycle:
        postStart:
          exec:
            command: ["/bin/sh", "-c", "echo Hello world - postStart handler > /usr/share/message"]
        preStop:
          httpGet:
            path: /preshutdowntask
            port: 8080
          exec:
            command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
        stopSignal: SIGUSR1


- entrypoint script example from RH docs:

#!/bin/env bash

function graceful_shutdown() {
  kill -SIGTERM "$java_pid"
  wait "$java_pid"
  exit 0
}

# Trap the SIGTERM signal
trap graceful_shutdown SIGTERM

...script omitted...

# Start the application
java -jar example.jar &
java_pid=$!

...script omitted...

# Wait for the process to finish
wait "$java_pid"



Reduce Image Size Techniques:
- Start with smallest image template required.
- Avoid having too many RUN instructions in a Containerfile. Whenever possible, combine multiple commands into a single RUN instruction.
- Create a .containerignore or .dockerignore file in the build context directory to prevent unnecessary files and directories from being copied from source into the image.
- Use multistage Containerfiles. with the first stage of the Containerfile which builds the application, and the second/final stage which includes only the necessary runtime dependencies. (not multistage chained builds - that's different)


Working Directories:
Better maintainability and troubleshooting:
- WORKDIR with absolute paths
- WORKDIR instead of multiple RUN instructions entailing switching directories and then running execs


Environment Variables:
Better maintainability and closer to IaC:
- Abstract ENV instructions from fixed paths in Containerfile instructions to ENV variables/parameters
- Shift sizing, configuration, versioning, $PATH updates, etc. to ENV variables/parameters
- ARG instructions useful to set ENV variables at build time useful


previous page

×