Adding customizations to images

After you download the image for the runtime that you want to customize, you can build a new custom image by adding your software customizations to the downloaded image.

Customizations allow a wide range of modifications to the image, including the installation of:
  • Conda and pip packages & operating system dependencies
  • Jupyter notebook extensions
  • JupyterLab extensions
  • R packages in RStudio images

General steps for creating custom images

Create the following three scripts in an empty directory and customize each script to fit your needs.
  1. In an empty directory, create the following Dockerfile:
    ARG base_image_tag
    FROM ${base_image_tag}
    
    # ============
    # root section
    # ============
    USER root:root
    
    COPY install-as-root.sh /tmp/install/
    RUN umask 022 \
     && bash /tmp/install/install-as-root.sh \
     && /opt/ibm/build/bin/microdnf-clean.sh \
     && rm -rf /tmp/install
    # The microdnf-clean.sh script also grants required permissions.
    
    # -----------------------------------------
    # Change display name of the Jupyter kernel
    # -----------------------------------------
    # Changing the display name of the kernel allows for distinguishing different
    # custom images from the default runtime images when editing a notebook.
    # It is good practice, but not strictly required.
    
    RUN sed -i -e '/display_name/{s/",/ with modifications",/}' \
       /opt/ibm/run/kernels/*/kernel.json
    
    # ============
    # user section
    # ============
    USER wsbuild:wsbuild
    
    COPY --chown=wsbuild install-as-user.sh /tmp/install/
    RUN umask 002 \
     && bash /tmp/install/install-as-user.sh \
     && /opt/ibm/build/bin/fix-conda-permissions.sh \
     && conda run -n $DSX_KERNEL_CONDENV pip cache purge \
     && conda clean --tarballs \
     && rm -rf /tmp/install
    
    # ====
    # conda
    # ====
    USER nobody:wsbuild
  2. Edit the Dockerfile script:
    • In the FROM line at the top, replace ${base_image_tag} with the :tag of the runtime image on which you base your customization.
    • If you add files to the custom image, put COPY statements into the root section or user section of the Dockerfile. Files and directories that should be writeable for users must be owned by group wsbuild (gid 3000) and have group write permission.
    • Optionally, in the RUN sed line, edit the comment to include information for notebook editors that tells them which custom image they are using.
  3. Create the following install-as-root.sh script:
    #!/bin/bash
    set -e -o pipefail
    # =============================================================================
    # Commands in this script will be executed as root. Use this to...
    # - install operating system packages
    # - modify the installed JupyterLab and Notebook servers
    #
    # The IBM runtime images are based on Red Hat UBI minimal.
    # The package manager is microdnf, it uses yum repositories.
    # =============================================================================
    # update all operating system packages
    microdnf update -y
  4. Add commands at the end of install-as-root.sh if you want to install operating system packages or to perform other modifications that require root permission. For example, to install the package poppler-utils and its dependencies from the RedHat UBI repositories, add: microdnf install -y --nodocs poppler-utils
  5. Finally, create the install-as-user.sh script:
    #!/bin/bash
    set -e -o pipefail
    source activate $DSX_KERNEL_CONDENV
    # subsequent commands affect the conda environment for user code
  6. Edit install-as-user.sh and add commands at the end of script if you want to install Python or R packages into the conda environment where user code will be executed. For example, to install the package py4j from the Anaconda main channel, and the package seawater from PyPI, add:
    conda install py4j
    
    pip install seawater

Install all conda packages before pip packages. Also, install pip packages from public repositories like PyPI before you add pip packages or wheels using COPY to the custom image.

Examples of customizations

The following examples show different ways in which runtimes can be customized. In addition to the installation of libraries through conda and pip, extensions such as JupyterLab and Jupyter notebook extensions can be installed. Furthermore it is possible to customize RStudio using the same approach as outlined above. However, in addition to Python-based environments, it is also possible to install packages from CRAN.

Installing Jupyter notebook extensions
This code snippet shows the required changes in install-as-user.sh to install and enable the two notebook extensions codefolding and freeze:
#!/bin/bash
set -e -o pipefail
source /opt/ibm/build/bin/installutils.sh
source activate $DSX_SYSTEM_CONDENV
pip install jupyter_contrib_nbextensions
jupyter contrib nbextension install  --symlink
jupyter nbextension enable codefolding/main --system
jupyter nbextension enable freeze/main --system
Installing JupyterLab extensions
This code snippet shows the required changes in install-as-user.sh to install and enable the two JupyterLab extensions jupyterlab-spreadsheet-editor and lckr-jupyterlab-variableinspector:
#!/bin/bash
set -e -o pipefail
source /opt/ibm/build/bin/installutils.sh
conda run -n $DSX_SYSTEM_CONDENV pip install \
   lckr-jupyterlab-variableinspector \
   jupyterlab-spreadsheet-editor
fix-condenv-access-system
Installing R packages in RStudio
This code snippet shows the required changes in install-as-user.sh to install the R package foreign:
#!/bin/bash
set -e -o pipefail
source activate $DSX_KERNEL_CONDENV
# subsequent commands affect the conda environment for user code
# ========================================================
# Install R packages from CRAN or tar.gz file or conda repository
# below example installed foreign test package
# users can add any other additional packages similar way
# =======================================================
R -e "install.packages('foreign', repos='http://cran.rstudio.com')"