% -*- mode: latex; fill-column: 120; -*-
The \href{https://github.com/OpenCHAMI/image-builder}{Image Builder} is
designed to use layered building of system images, as shown below to enable the
use of different layers which can be modified and added to to build out
different capabilities into image layers so a change of one layer does not
require a change and rebuild of all other layers.

\begin{center}
\begin{tcolorbox}[]
\small The image-build is an optional piece of the \OpenCHAMI{} software stack.
If you have an existing image build process that creates the necessary boot
artifacts you can use that instead.
\end{tcolorbox}
\end{center}

First let's define the lowest layer of the image which disable the installation
of documentation files to reduce the size of the image:

% begin_ohpc_run
% ohpc_validation_comment Define nodocs image layer
\begin{lstlisting}[language=bash,keywords={},literate={BOSSHORT}{\baseosshort{}}1 {BASEURL}{\baseurl{}}1 {OSNAME}{\OS{}}1]
[sms](*\#*) echo -e "%_excludedocs 1" >> /opt/ohpc/admin/images/data/rpmmacros
[sms](*\#*) cat > /opt/ohpc/admin/images/nodocs.yaml <<EOF
[sms](*\#*) options:
[sms](*\#*)   layer_type: 'base'
[sms](*\#*)   name: 'BOSSHORT-nodocs'
[sms](*\#*)   publish_tags: '10'
[sms](*\#*)   pkg_manager: 'dnf'
[sms](*\#*)   parent: 'scratch'
[sms](*\#*)   publish_registry: 'ohpc-lenovo-sms:5000/c'
[sms](*\#*)   registry_opts_push:
[sms](*\#*)     - '--tls-verify=false'
[sms](*\#*) copyfiles:
[sms](*\#*)   - src: '/data/rpmmacros'
[sms](*\#*)     dest: '/root/.rpmmacros'
[sms](*\#*) EOF
\end{lstlisting}
% ohpc_validation_newline
% end_ohpc_run

Build this first layer:

% begin_ohpc_run
% ohpc_comment_header Build base layer
\begin{lstlisting}[language=bash,literate={-}{-}1,keywords={},upquote=true,keepspaces]
[sms](*\#*) podman run \
		--rm \
		--device /dev/fuse \
		--network host \
		-v /opt/ohpc/admin/images/data:/data \
		-v /opt/ohpc/admin/images/nodocs.yaml:/home/builder/config.yaml \
		ghcr.io/openchami/image-build:latest image-build \
			--config config.yaml \
			--log-level INFO
\end{lstlisting}
% ohpc_validation_newline
% end_ohpc_run

Now let's define a base layer we can use to build on. This base layer will
install common packages and also create out initramfs. In this recipe it's
created to build an initramfs that supports liveOS so the image will boot into
memory.

% begin_ohpc_run
% ohpc_validation_comment Define base image layer
\begin{lstlisting}[language=bash,keywords={},literate={BOSSHORT}{\baseosshort{}}1 {BASEURL}{\baseurl{}}1 {OSNAME}{\OS{}}1]
[sms](*\#*) cat > /opt/ohpc/admin/images/base.yaml <<EOF
[sms](*\#*) options:
[sms](*\#*)   layer_type: 'base'
[sms](*\#*)   name: 'BOSSHORT-base'
[sms](*\#*)   publish_tags: '10'
[sms](*\#*)   pkg_manager: 'dnf'
[sms](*\#*)   parent: '${sms_name}:5000/${compute_prefix}/BOSSHORT-nodocs:10'
[sms](*\#*)   publish_registry: '${sms_name}:5000/${compute_prefix}'
[sms](*\#*)   registry_opts_pull:
[sms](*\#*)     - '--tls-verify=false'
[sms](*\#*)   registry_opts_push:
[sms](*\#*)     - '--tls-verify=false'
[sms](*\#*) repos:
[sms](*\#*)   - alias: 'BOSSHORT_BaseOS'
[sms](*\#*)     url: 'BASEURL10/BaseOS/x86_64/os/'
[sms](*\#*)     gpg: 'BASEURL/RPM-GPG-KEY-OSNAME-10'
[sms](*\#*)   - alias: 'BOSSHORT_AppStream'
[sms](*\#*)     url: 'BASEURL10/AppStream/x86_64/os/'
[sms](*\#*)     gpg: 'BASEURL/RPM-GPG-KEY-OSNAME-10'
[sms](*\#*) package_groups:
[sms](*\#*)   - 'Minimal Install'
[sms](*\#*)   - 'Development Tools'
[sms](*\#*) packages:
[sms](*\#*)   - kernel
[sms](*\#*)   - wget
[sms](*\#*)   - dracut-live
[sms](*\#*)   - cloud-init
[sms](*\#*)   - chrony
[sms](*\#*)   - rsyslog
[sms](*\#*)   - sudo
[sms](*\#*) cmds:
[sms](*\#*)   - cmd: >
[sms](*\#*)       DRACUT_NO_XATTR=1 dracut --add "dmsquash-live livenet network-manager"
[sms](*\#*)       --kver "\$(basename /lib/modules/*)"
[sms](*\#*)       -N -f --logfile /tmp/dracut.log 2>/dev/null
[sms](*\#*)     loglevel: INFO
[sms](*\#*)   - cmd: 'echo DRACUT LOG:; cat /tmp/dracut.log'
[sms](*\#*)     loglevel: INFO
[sms](*\#*) EOF
\end{lstlisting}
% ohpc_validation_newline
% end_ohpc_run

Now lets build this base layer. This will take about 5 minutes but we should
not have to rebuild it very often.

% begin_ohpc_run
% ohpc_comment_header Build base layer
\begin{lstlisting}[language=bash,literate={-}{-}1,keywords={},upquote=true,keepspaces]
[sms](*\#*) podman run \
		--rm \
		--device /dev/fuse \
		--network host \
		-v /opt/ohpc/admin/images/base.yaml:/home/builder/config.yaml \
		ghcr.io/openchami/image-build:latest image-build \
			--config config.yaml \
			--log-level DEBUG
\end{lstlisting}
% ohpc_validation_newline
% end_ohpc_run

Now we can build on top of the base layer by setting it to be the parent of the compute-base layer

% begin_ohpc_run
% ohpc_comment_header Define the next layer
\begin{lstlisting}[language=bash,keywords={}]
[sms](*\#*) cat > /opt/ohpc/admin/images/compute-base.yaml << EOF
[sms](*\#*) options:
[sms](*\#*)   layer_type: 'base'
[sms](*\#*)   name: 'compute-base'
[sms](*\#*)   publish_tags:
[sms](*\#*)     - '10'
[sms](*\#*)   pkg_manager: 'dnf'
[sms](*\#*)   parent: '${sms_name}:5000/${compute_prefix}/BOSSHORT-base:10'
[sms](*\#*)   publish_registry: '${sms_name}:5000/${compute_prefix}'
[sms](*\#*)   registry_opts_pull:
[sms](*\#*)     - '--tls-verify=false'
[sms](*\#*)   registry_opts_push:
[sms](*\#*)     - '--tls-verify=false'
[sms](*\#*) repos:
[sms](*\#*)   - alias: 'Epel10'
[sms](*\#*)     url: 'https://dl.fedoraproject.org/pub/epel/10z/Everything/x86_64/'
[sms](*\#*)     gpg: 'https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-10'
[sms](*\#*) package_groups:
[sms](*\#*)   - 'InfiniBand Support'
[sms](*\#*) packages:
[sms](*\#*)   - vim
[sms](*\#*)   - nfs-utils
[sms](*\#*)   - tcpdump
[sms](*\#*)   - traceroute
[sms](*\#*)   - git
[sms](*\#*)   - http://repos.openhpc.community/OpenHPC/4/EL_10/x86_64/ohpc-release-4-1.el10.x86_64.rpm
[sms](*\#*) cmds:
[sms](*\#*)   - cmd: dnf config-manager --set-enabled crb
[sms](*\#*)     loglevel: INFO
[sms](*\#*) EOF
\end{lstlisting}
% ohpc_validation_newline
% end_ohpc_run

Then we can build it. Any modifications to the above configuration will only
require a rebuild of this layer

% begin_ohpc_run
% ohpc_comment_header Build the next layer
\begin{lstlisting}[language=bash,literate={-}{-}1,keywords={},upquote=true,keepspaces]
[sms](*\#*) podman run \
                --rm \
                --device /dev/fuse \
                --network host \
                -v /opt/ohpc/admin/images/compute-base.yaml:/home/builder/config.yaml \
                ghcr.io/openchami/image-build:latest image-build \
                        --config config.yaml \
                        --log-level DEBUG
\end{lstlisting}
% ohpc_validation_newline
% end_ohpc_run

You can keep adding layers as you see fit, and branch off if so desired. We
will use the next layer to add additional packages later on so no need to build
it just yet.

% begin_ohpc_run
% ohpc_validation_comment Define a production compute layer
\begin{lstlisting}[language=bash,keywords={}]
[sms](*\#*) cp /etc/hosts /opt/ohpc/admin/images/data
[sms](*\#*) cat > /opt/ohpc/admin/images/compute-prod.yaml << EOF
[sms](*\#*) options:
[sms](*\#*)   layer_type: 'base'
[sms](*\#*)   name: 'compute-prod'
[sms](*\#*)   publish_tags:
[sms](*\#*)     - '10'
[sms](*\#*)   pkg_manager: 'dnf'
[sms](*\#*)   parent: '${sms_name}:5000/${compute_prefix}/compute-base:10'
[sms](*\#*)   publish_registry: '${sms_name}:5000/${compute_prefix}'
[sms](*\#*)   registry_opts_pull:
[sms](*\#*)     - '--tls-verify=false'
[sms](*\#*)   registry_opts_push:
[sms](*\#*)     - '--tls-verify=false'
[sms](*\#*)   # Publish SquashFS image to local S3
[sms](*\#*)   publish_s3: 'http://${sms_name}:9000'
[sms](*\#*)   s3_prefix: 'compute/base/'
[sms](*\#*)   s3_bucket: 'boot-images'
[sms](*\#*)
[sms](*\#*)   # Publish OCI image to container registry
[sms](*\#*)   #
[sms](*\#*)   # This is the only way to be able to reuse this image as
[sms](*\#*)   # a parent for another image layer.
[sms](*\#*)   publish_registry: '${sms_name}:5000/${compute_prefix}'
[sms](*\#*)   registry_opts_push:
[sms](*\#*)     - '--tls-verify=false'
[sms](*\#*) copyfiles:
[sms](*\#*)   - src: '/data/hosts'
[sms](*\#*)     dest: '/etc/hosts'
[sms](*\#*)   - src: '/data/slurmd'
[sms](*\#*)     dest: '/etc/sysconfig/slurmd'
[sms](*\#*) packages:
[sms](*\#*)   - ohpc-base-compute
[sms](*\#*)   - ohpc-slurm-client
[sms](*\#*)   - pdsh-ohpc
[sms](*\#*)   - lmod-ohpc
[sms](*\#*) cmds:
[sms](*\#*)   - cmd: systemctl disable firewalld
[sms](*\#*)     loglevel: INFO
[sms](*\#*)   - cmd: echo '${sms_ip}:/home /home nfs nfsvers=3,nodev,nosuid 0 0' >> /etc/fstab
[sms](*\#*)     loglevel: INFO
[sms](*\#*)   - cmd: echo '${sms_ip}:/opt/ohpc/pub /opt/ohpc/pub nfs nfsvers=3,nodev,nosuid 0 0' >> /etc/fstab
[sms](*\#*)     loglevel: INFO
[sms](*\#*)   - cmd: mkdir -p /opt/ohpc/pub
[sms](*\#*)     loglevel: INFO
[sms](*\#*)   - cmd: echo "account required pam_slurm.so" >> /etc/pam.d/sshd
[sms](*\#*)     loglevel: INFO
[sms](*\#*)   - cmd: systemctl enable munge slurmd
[sms](*\#*)     loglevel: INFO
[sms](*\#*)   - cmd: ln -sf ../usr/share/zoneinfo/UTC /etc/localtime
[sms](*\#*)     loglevel: INFO
[sms](*\#*) EOF
\end{lstlisting}
% ohpc_validation_newline
% end_ohpc_run
