From 1b6b858bdfbede3f2b173d6b703845e83f0897a6 Mon Sep 17 00:00:00 2001 From: Andreas Maier Date: Sat, 28 Sep 2024 07:44:37 +0200 Subject: [PATCH] Support for building a docker container Signed-off-by: Andreas Maier --- .dockerignore | 33 ++++++++++++++++ .github/workflows/test.yml | 8 ++++ Dockerfile | 80 ++++++++++++++++++++++++++++++++++++++ Makefile | 19 +++++++++ changes/81.feature.rst | 1 + docs/usage.rst | 39 +++++++++++++++++++ 6 files changed, 180 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 changes/81.feature.rst diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..04a3765 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,33 @@ +# Note: /dist/ is needed in the docker build to access the distribution archive + +# User specific configuration files +/myconfig/ + +# Make targets +/*.done +/done/*.done + +# Git stuff not needed in Dockerfile +/.git +/.gitignore + +# Make build +/build/ +/*.egg-info/ +/AUTHORS +/ChangeLog +/MANIFEST + +# Make test +__pycache__/ +/.pytest_cache/ +/htmlcov/ +/.coverage + +# Make builddoc +/build_docs/ + +# Other +*.swp +.DS_Store +/try/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 484a644..1a7a880 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -213,6 +213,14 @@ jobs: RUN_TYPE: ${{ steps.set-run-type.outputs.result }} run: | make test + - name: Run docker build + # The docker command is not preinstalled on macos or Windows + if: runner.os == 'Linux' + env: + PACKAGE_LEVEL: ${{ matrix.package_level }} + RUN_TYPE: ${{ steps.set-run-type.outputs.result }} + run: | + make docker - name: Send coverage result to coveralls.io env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3b7462c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,80 @@ +# Dockerfile for zhmc-log-forwarder project +# +# This image runs the zhmc_log_forwarder command. +# +# The log forwarder config file needs to be made available to the container +# using some mount option and specified with -c. +# +# Example docker command to run the exporter using a locally built version of this image: +# +# docker run --rm -v $(pwd)/myconfig:/root/myconfig zhmc_log_forwarder -c /root/myconfig/config.yaml -v + +FROM python:3.12-alpine as builder + +# Path name of binary distribution archive of zhmc-log-forwarder package +ARG bdist_file +RUN : "${bdist_file:?Build argument bdist_file is required}" + +# Install some packages onto this minimal Alpine image: +# - git - in case the Python requirements use git+https links +# - gcc musl-dev - in case Python wheels based on C need to be built (e.g. for rpds) +RUN apk add git gcc musl-dev + +# Make sure the installed Python commands are found +ENV PATH=/root/.local/bin:$PATH + +# Install the Python package of this project +COPY ${bdist_file} /tmp/${bdist_file} +RUN pip install --user /tmp/${bdist_file} + +# Show the installed Linux packages +RUN echo "Installed Linux packages:" \ + && apk info -v + +# Show the installed Python packages +RUN echo "Installed Python packages:" \ + && pip list + +# Display files in 'rpds' Python package (verifying that it can be imported) +RUN echo "Files in rpds Python package:" \ + && python -c "import rpds, os, sys; rpds_dir=os.path.dirname(rpds.__file__); print(rpds_dir); sys.stdout.flush(); os.system(f'ls -al {rpds_dir}')" + +# The Python 'rpds' package (used by 'jsonschema') has a shared library that is +# built during its installation, and thus depends on APIs of the system and +# the C library of the builder OS used in the first stage of this Dockerfile. +# Therefore, the OS used in the final stage needs to be compatible with the +# builder OS. We use the same OS image to make sure. +FROM python:3.12-alpine + +# Version of the zhmc-log-forwarder package +ARG package_version +RUN : "${package_version:?Build argument package_version is required}" + +# Image build date in ISO-8601 format +ARG build_date +RUN : "${build_date:?Build argument build_date is required}" + +# Git commit ID of the zhmc-log-forwarder repo used to build the image +ARG git_commit +RUN : "${git_commit:?Build argument git_commit is required}" + +# Set image metadata +LABEL org.opencontainers.image.title="IBM Z HMC Log Forwarder" +LABEL org.opencontainers.image.version="${package_version}" +LABEL org.opencontainers.image.authors="Andreas Maier" +LABEL org.opencontainers.image.created="${build_date}" +LABEL org.opencontainers.image.url="https://github.com/zhmcclient/zhmc-log-forwarder" +LABEL org.opencontainers.image.documentation="https://zhmc-log-forwarder.readthedocs.io" +LABEL org.opencontainers.image.source="https://github.com/zhmcclient/zhmc-log-forwarder" +LABEL org.opencontainers.image.licenses="Apache Software License 2.0" +LABEL org.opencontainers.image.revision="${git_commit}" + +# Copy the installed Python packages from the builder image +COPY --from=builder /root/.local /root/.local + +# Make sure the installed Python commands are found +ENV PATH=/root/.local/bin:$PATH + +EXPOSE 9291 +ENTRYPOINT ["zhmc_log_forwarder"] +CMD ["--help"] diff --git a/Makefile b/Makefile index 956770b..06463c5 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,10 @@ module_name := zhmc_log_forwarder # version indicator by 1. package_version := $(shell $(PYTHON_CMD) -m setuptools_scm) +# Docker image +docker_image_name := zhmc_log_forwarder +docker_image_tag := latest + # Python versions python_version := $(shell $(PYTHON_CMD) -c "import sys; sys.stdout.write('{}.{}.{}'.format(*sys.version_info[0:3]))") python_mn_version := $(shell $(PYTHON_CMD) -c "import sys; sys.stdout.write('{}.{}'.format(*sys.version_info[0:2]))") @@ -230,6 +234,7 @@ help: @echo " release_publish - Publish to PyPI when releasing a version (requires VERSION and optionally BRANCH to be set)" @echo " start_branch - Create a start branch when starting a new version (requires VERSION and optionally BRANCH to be set)" @echo " start_tag - Create a start tag when starting a new version (requires VERSION and optionally BRANCH to be set)" + @echo " docker - Build local Docker image $(docker_image_name):$(docker_image_tag)" @echo ' uninstall - Uninstall package from active Python environment' @echo ' clean - Remove any temporary files' @echo ' clobber - Remove any build products (includes uninstall+clean)' @@ -284,6 +289,10 @@ builddoc: $(doc_build_file) all: install develop check_reqs check test build builddoc authors @echo '$@ done.' +.PHONY: docker +docker: $(done_dir)/docker_$(pymn)_$(PACKAGE_LEVEL).done + @echo "Makefile: $@ done." + .PHONY: release_branch release_branch: @bash -c 'if [ -z "$(VERSION)" ]; then echo ""; echo "Error: VERSION env var is not set"; echo ""; false; fi' @@ -396,6 +405,7 @@ clean: -$(call RMDIR_R_FUNC,.eggs) -$(call RM_FUNC,MANIFEST MANIFEST.in AUTHORS ChangeLog .coverage) -$(call RM_R_FUNC,*.pyc *.tmp tmp_*) + docker image prune --force @echo 'Done: Cleaned out all temporary files.' @echo '$@ done.' @@ -462,6 +472,15 @@ $(bdist_file) $(version_file): $(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done $(PYTHON_CMD) -m build --wheel --outdir $(dist_dir) -C--universal . @echo "Makefile: Done building the wheel distribution archive: $(bdist_file)" +$(done_dir)/docker_$(pymn)_$(PACKAGE_LEVEL).done: $(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done Dockerfile .dockerignore $(bdist_file) + @echo "Makefile: Building Docker image $(docker_image_name):$(docker_image_tag)" + -$(call RM_FUNC,$@) + docker build --tag $(docker_image_name):$(docker_image_tag) --build-arg bdist_file=$(bdist_file) --build-arg package_version=$(subst +,.,$(package_version)) --build-arg build_date="$(shell date -Iseconds)" --build-arg git_commit="$(shell git rev-parse HEAD)" . + docker run --rm $(docker_image_name):$(docker_image_tag) --version + docker image list --filter reference=$(docker_image_name) + @echo "Makefile: Done building Docker image" + echo "done" >$@ + $(done_dir)/pylint_$(pymn)_$(PACKAGE_LEVEL).done: $(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done Makefile $(pylint_rc_file) $(check_py_files) @echo "Makefile: Running Pylint" -$(call RM_FUNC,$@) diff --git a/changes/81.feature.rst b/changes/81.feature.rst new file mode 100644 index 0000000..f995e33 --- /dev/null +++ b/changes/81.feature.rst @@ -0,0 +1 @@ +Added support for building a Docker container that runs the log forwarder. diff --git a/docs/usage.rst b/docs/usage.rst index 7b97d5b..7e202bc 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -31,6 +31,45 @@ With the virtual Python environment active, follow the steps in log forwarder. +Running in a Docker container +----------------------------- + +If you want to run the log forwarder in a Docker container you can create the +container as follows, using the Dockerfile provided in the Git repository. + +* Clone the Git repository of the log forwarder and switch to the clone's root + directory: + + .. code-block:: bash + + $ git clone https://github.com/zhmcclient/zhmc-log-forwarder + $ cd zhmc-log-forwarder + +* Build a local Docker image as follows: + + .. code-block:: bash + + $ make docker + + This builds a container image named 'zhmc_log_forwarder:latest' in your local + Docker environment. + + The log forwarder config file is not included in the image, and needs to be + provided when running the image. + +* Run the local Docker image as follows: + + .. code-block:: bash + + $ docker run --rm -v $(pwd)/myconfig:/root/myconfig zhmc_log_forwarder -c /root/myconfig/config.yaml -v + + In this command, the log forwarder config file is provided on the local system + as ``./myconfig/config.yaml``. The ``-v`` option of 'docker run' mounts the + ``./myconfig`` directory to ``/root/myconfig`` in the container's file system. + The ``-c`` option of the log forwarder references the config file as it + appears in the container's file system. + + Setting up the HMC ------------------