Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Creating custom Atomic trees, images, and installers - Part 1

January 8, 2015
Brent Baude
Related topics:
Containers
Related products:
Red Hat Enterprise Linux

Share:

    Shipping_containers_at_ClydeNot too soon after people start using Atomic images, the question of customization soon follows. It is a natural progression for most people when they use Atomic. There are a number of different ways to accomplish using custom images not withstanding using docker and containers. The Atomic tool called 'rpm-ostree-toolbox' is emerging as the best tool for customizing Atomic.

    The 'rpm-ostree-toolbox' main command is actually a wrapper (much like virsh) for three subcommands: treecompose, imagefactory, and installer.  With these three subcommands, you can create:

    • a custom Atomic tree
    • customized disk images (qcow2 and the like) based on your tree
    • install media (ISO) that installs your tree

    In this first of two blogs, I will discuss how to set up the rpm-ostree-toolbox tooling, create a standard (default) tree, and then create a custom tree.  Then I will share some common commands that can be used to inspect your tree.  And finally, we will boot a generic CentOS7 Atomic image and consume our custom tree. I will be using a standard Fedora 21 image to perform all of these actions.

    Setting up the required tools

    To install the required packages, we simply need to install 'rpm-ostree-toolbox' and 'pykickstart'.

    [baude@localhost ~]$ sudo yum install pykickstart rpm-ostree-toolbox

    Obtaining the CentOS Atomic git repository

    The rpm-ostree-toolbox tooling uses a variety of configuration files to drive its output.  These files are kept in a git repository which provides almost all the default values you will need to create the outputs you need.  The git repository is at: https://github.com/baude/sig-atomic-buildscripts/tree/master

    To obtain these, simply issue a:

    [baude@localhost ~]$ git clone https://github.com/baude/sig-atomic-buildscripts

    Reviewing the configuration files for CentOS

    There are several key files in the buildscripts git repository that warrant a brief review and explanation.

    • config.ini
    • JSON files
    • centos-atomic-7.tdl
    • REPO files

    config.inI

    The config.ini file is the key control file for rpm-ostree-toolbox itself.  For example, it defines the OS, version, where input and output files are, and which JSON files are to be used. The rpm-ostree-toolbox commands are designed to be run against an unchanged git repository like the one one we cloned earlier.  Therefore, to build, a standard or default tree, you should not have to change the config.ini file at all.  Once you begin to build customized trees, you will need to either edit the config.ini or create a new one.

    To familiarize yourself with the config.ini file and its values, it is best to open and review it.  It is heavily commented and self-explanatory.

    JSON files

    The two important JSON files in the git repository, for our purposes, is centos-atomic-base.json and centos-atomic-host.json.  In fact, the latter includes the first one.  These two files describe the parts that go into making trees, images, and installer media.  For example, it lists which yum repositories should be used, which RPMs will make up the package manifest, and the name of the OS among other things.

    centos-atomic-7.tdl

    This file is used by the imagefactory subcommand and is a required piece of input. The key value in the tdl file points to a yum repository which includes the required packages for an Atomic install tree.

    REPO files

    In the CentOS repository, you will also find several .repo files.  These repo files can be and are consumed in each of the rpm-ostree-tool subcommands.  They direct the tooling where to look for packages that are needed throughout the entire process.  The repo files are also listed in the JSON files so that they are included.  If you need to add a custom repository, add it to the JSON file and make a respective .repo file.  One note, each repo file may only list one repository.

     

    Composing a tree

    As mentioned earlier, rpm-ostree-toolbox consists of three subcommands.  The first is called treecompose.  You can see these subcommands with the --help switch.

    [bbaude@localhost ]$ sudo rpm-ostree-toolbox --help
    Builtins:
      treecompose - Use rpm-ostree to compose RPMs to a tree
      imagefactory - Use ImageFactory to create a disk image
      installer - Use Lorax to create an installable ISO and PXE boot loader

    For our purposes, this is the first command you should run when customizing Atomic.  In turn, this tree content can be consumed by an existing CentOS Atomic image, used to create a new CentOS Atomic image, or used to be embedded into a CentOS Atomic installer.

    We can also get help on the subcommand by passing the subcommand and the --help switch.

    [bbaude@localhost ]$ sudo rpm-ostree-toolbox treecompose --help
      usage: rpmostreecompose-main [-h] -c CONFIG [--ostreerepo OSTREEREPO]
                                   [-p PROFILE] [-V VERSIONING] [-v]
      Compose OSTree tree
      optional arguments:
      -h, --help show this help message and exit
      -c CONFIG, --config CONFIG
          Path to config file
      --ostreerepo OSTREEREPO
          Path to OSTree repository (default: ${pwd}/repo)
      -p PROFILE, --profile PROFILE
          Profile to compose (references a stanza in the config file)
      -V VERSIONING, --versioning VERSIONING
          Version to mark compose
      -v, --verbose verbose output

    The minimum input for a treecompose is to point to the config.ini it should use and provide a --ostreerepo argument.  Putting ostree content under /srv is somewhat or a standard so I will define the --ostreerepo argument as /srv/rpm-ostree/centos-atomic-host/7/.  With these two pieces of information, we are ready to create our first tree:

     [bbaude@localhost ]$ sudo rpm-ostree-toolbox treecompose -c /home/baude/sig-atomic-buildscripts/config.ini --ostreerepo /srv/rpm-ostree/centos-atomic-host/7/

    The treecompose process will now begin.  First it parses all the input files and then downloads the required RPMs that will end up in the tree.  It will then end up installing those RPMs into the tree and running a SELinux relabel on it.

    Customizing the tree

    One of the motivations for making your own tree is so that you can customize it.  And in following through with this example, I am going to add vim to my tree. There is no defined, concrete process on how you should customize your trees.  You could simply edit the centos-atomic-host.json file and add 'vim' to the packages list.  But I have found that my best practice for this actually is an opportunity for Atomic to shine.  A single Atomic repository can house more than one tree.  The trees are referred to by their ref.  A ref is simply the name of a tree and is somewhat akin to a branch in git.

    My recommendation is to leave the default ref and create a new ref that refers to your tree in the same repo.  This can be done by creating a new treefile for our custom tree.

    [bbaude@localhost ]$ cat sig-atomic-buildscripts/centos-atomic-host-vim.json
    {
     "include": "centos-atomic-host.json",
     "packages": ["vim"]
    }
    
    

    Note in the new json file above (centos-atomic-host-vim.json), we have added two simple keys and values.  Because our new treefile is considered to be complimentary to the tree we have already made, we include centos-atomic-host.json, This means all the packages and values in that and subsequent include files will be honoured.  And rpm-ostree-toolbox will also amend the package list, which here only includes vim, to the other packages described in the included json files.

    The best way to implement calling this new tree file is to create a new profile in our config.ini. As an example, consider this new vim profile I have added to the config.ini:

    [vim]
    tree_name = vim
    ref = %(os_name)s/%(release)s/%(arch)s/%(tree_name)s
    tree_file = %(os_name)s-vim.json

    Note here how I define the tree_name as vim.  It was standard in the our default tree compose.  As a result, the ref will end in /vim.  And finally, the tree_file definition points to our new tree file we defined earlier.

    Be sure to watch the format of the json file and adhere to it very closely.  Once you have completed your change, you can recompose the tree with the same command we used earlier but this time we add -p vim to build for the vim profile:

    [baude@localhost]$ sudo rpm-ostree-toolbox treecompose -c /home/baude/sig-atomic-buildscripts/config.ini --ostreerepo /srv/rpm-ostree/centos-atomic-host/7/ -p vim

    Inspecting a composed tree

    Once your second tree compose completes, there a couple of handy commands you can use to probe and inspect them.   Most of these commands need two pieces of information about your tree:

    • repo -- where your composed tree is (/srv/rpm-ostree/centos-atomic-host/7/repo/)
    • ref -- what is the ref name of the tree.  Refs are similar to a name of a tree and point to a generated file system tree.

    We can determine the ref from a composed tree using the ostree command like so:

    [baude@localhost ]$ ostree refs --repo=/srv/rpm-ostree/centos-atomic-host/7/
    centos-atomic-host/7/x86_64/standard
    centos-atomic-host/7/x86_64/vim

    Note how we see two refs in our repo.  The standard ref is the default (unchanged) tree we created, whereas the vim ref is our custom tree.

    Suppose we want to list which RPMs were actually used to create the tree.  The manifest in the JSON files are only the packages you wanted included but this does not include the RPM dependencies that were also resolved.  Or in our case, perhaps we want to verify that vim was truly added.  We can use rpm-ostree to perform that query:

    [baude@localhost ]$ rpm-ostree rpm --repo=/srv/rpm-ostree/centos-atomic-host/7/ list centos-atomic-host/7/x86_64/vim | grep vim
     vim-common-2:7.4.160-1.el7.x86_64
     vim-enhanced-2:7.4.160-1.el7.x86_64
     vim-filesystem-2:7.4.160-1.el7.x86_64
     vim-minimal-2:7.4.160-1.el7.x86_64

    You can also peek into what the composed tree would like when extracted out.  This is handy for looking for files or directories within the tree compose.  For example, if you want to see what is in /boot we can use ostree to get that information:

    [baude@localhost ]$ ostree ls --repo=/srv/rpm-ostree/centos-atomic-host/7/ centos-atomic-host/7/x86_64/vim /boot
    d00555 0 0 0 /boot
    -00644 0 0 170 /boot/.vmlinuz-3.10.0-123.9.3.el7.x86_64.hmac
    -00600 0 0 2841475 /boot/System.map-3.10.0-123.9.3.el7.x86_64
    -00644 0 0 122093 /boot/config-3.10.0-123.9.3.el7.x86_64
    -00600 0 0 6832770 /boot/initramfs-3.10.0-123.9.3.el7.x86_64.img-ae094c0414df1b83583362a328716dec3af53dbc3df00adc797a4a1586c1063d
    -00644 0 0 228621 /boot/symvers-3.10.0-123.9.3.el7.x86_64.gz
    -00755 0 0 4905984 /boot/vmlinuz-3.10.0-123.9.3.el7.x86_64-ae094c0414df1b83583362a328716dec3af53dbc3df00adc797a4a1586c1063d
    d00755 0 0 0 /boot/efi
    d00755 0 0 0 /boot/grub
    d00755 0 0 0 /boot/grub2

     

    You can also query the tree to understand its history with ostree and its log command.  This, much like git, will provide you with commit IDs that you can use to look at delta history of an image.  Here I will query both the standard and vim refs.

    [bbaude@localhost ]$ ostree log --repo=/srv/rpm-ostree/centos-atomic-host/7/ centos-atomic-host/7/x86_64/standard
    commit 205377fc9b152918cd20fd21b1c228f1b7a08d9e2ab20dfb6df81a7d269055c6
    Date: 2014-12-17 20:26:00 +0000
    
    
    [bbaude@localhost ]$ ostree log --repo=/srv/rpm-ostree/centos-atomic-host/7/ centos-atomic-host/7/x86_64/vim
    commit c0498ec94732b35254f39df5c5a6805369b7f42601a745b0f9c6a06dac3804ff
    Date: 2014-12-17 20:34:35 +0000

    With the two commit IDs, we can now get a diff of two trees.  Diff output from ostree can be very verbose and lengthy, which really is the point.  So in this case, I will just grep for 'vim' and only pipe out the first 10 lines using head.

    [bbaude@localhost ]$ ostree diff --repo=/srv/rpm-ostree/centos-atomic-host/7/ 205377fc9b152918cd20fd21b1c228f1b7a08d9e2ab20dfb6df81a7d269055c6 c0498ec94732b35254f39df5c5a6805369b7f42601a745b0f9c6a06dac3804ff | grep vim | head -n10
    A /usr/bin/rvim
    A /usr/bin/vim
    A /usr/bin/vimdiff
    A /usr/bin/vimtutor
    A /usr/etc/profile.d/vim.csh
    A /usr/etc/profile.d/vim.sh
    A /usr/etc/vimrc
    A /usr/share/doc/vim-common-7.4.160
    A /usr/share/doc/vim-common-7.4.160/Changelog.rpm
    A /usr/share/doc/vim-common-7.4.160/LICENSE

    Here we can see that vim was indeed added to our custom tree.

    Upgrading to our custom tree

    To now consume and use our custom tree, we will need a test image that we can boot.  Note that CentOS recently announced CentOS7 Atomic images, grab an image from there and boot it up. Once booted, the first thing we need to do is somehow serve our new tree content from the host (where the tree is composed) to our test CentOS Atomic guest.

    The ostree application comes with a subcommand that can start a simple httpd server that is more than capable.  The subcommand is called trivial-httpd and will run on a random port when started.  On the host, you must provide trivial-httpd with the repo you want served:

    [baude@localhost ]$ ostree trivial-httpd -p - /srv/rpm-ostree/centos-atomic-host/7/
    57556

    Author's Note: You should use a production webserver for you custom trees if you are doing more than development oriented work.  One key advantage of using something like Apache is that you will not be dealing with port assignments.

    The CentOS7 image I referenced earlier is booted in a KVM guest on my laptop.  We can check that the communication between the trivial-httpd and my guest is working using curl, the IP of where the trivial-httpd is running and the port number that was assigned:

    [centos@atomic-baude ~]$ curl http://192.168.122.228:57556/config
    [core]
    repo_version=1
    mode=archive-z2

    Author's Note: Firewalld will block this communication by default.  You can add a custom rule to allow it or take a different action.

    The successful curl indicates the communication is working.  The next step is to make our CentOS7 image (guest) aware of the remote tree we have built.  This is done with the ostree command on the guest image.  You should substitute the IP address of the system hosting your content and the port number given to you by trivial-httpd.

    [centos@atomic-baude ~]$ sudo ostree remote add withvim http://192.168.122.228:57556 --no-gpg-verify

    The addition of that tree (or repo) is now stored in /etc/ostree/remotes.d/ on the CentOS guest.  If you need to delete the repo, you just delete the file in that directory associated with your remote add.  We can now rebase our guest with our new tree using the rpm-ostree command with the rebase subcommand.

    [centos@atomic-baude ~]$ sudo rpm-ostree rebase withvim:centos-atomic-host/7/x86_64/vim
    Receiving objects: 36% (146/403) 7.3 MB
    ...
     vim-common-2:7.4.160-1.el7.x86_64
     vim-enhanced-2:7.4.160-1.el7.x86_64
     vim-filesystem-2:7.4.160-1.el7.x86_64
     which-2.20-7.el7.x86_64
    [centos@atomic-baude ~]$

    The content from our custom tree has now been put onto our guest.  The references to vim in the RPM list is a good hint.  But note, it is not available yet until we reboot the system.  For proof, let's try to run vim.

    [centos@atomic-baude ~]$ vim
    -bash: vim: command not found

    It clearly is not there.  To make the rebase take effect, we reboot.  When you reboot your image, you may notice in the bootloader two entries (where only one was present earlier).  The image ending in -1 is the initial or original image and the one ending in -0 is our new tree.  Once the guest is booted up, we can check for vim again.

    [centos@atomic-baude ~]$ file /usr/bin/vim
    /usr/bin/vim: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x530e3b1a3084847f35bfddc8527f432c028b7ca0, stripped

    And there is clear proof it is now present.

    Wrap Up

    This concludes the part 1 of customizing and creating Atomic builds.  Reviewing what we have learned, you should now be able to:

    • Build the required tools to customize Atomic
    • Obtain the control files from the CentOS git repository
    • Compose a customized tree
    • Inspect and explore your new tree
    • Rebase an image to your new tree

    In part 2 of the blog, we focus on creating custom disk images and isos.

     

    Last updated: April 5, 2018

    Recent Posts

    • How to run a fraud detection AI model on RHEL CVMs

    • How we use software provenance at Red Hat

    • Alternatives to creating bootc images from scratch

    • How to update OpenStack Services on OpenShift

    • How to integrate vLLM inference into your macOS and iOS apps

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue