• Logo
    Rockcraft
  • github.com/canonical/rockcraft
  • More resources
    • Forum
    • GitHub
Contents Menu Expand Light mode Dark mode Auto light/dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
Rockcraft 1.5.3 documentation
Rockcraft 1.5.3 documentation
  • Tutorials
    • 1. Create a Hello World rock
    • 2. Install slices in a rock
    • 3. Containerise a PyPI package
    • 4. Bundle a Node.js app within a rock
    • 5. Build a rock for a Flask application
  • How-to guides
    • Get started - quick guide
    • Cut existing slices with Chisel
    • Create a package slice for Chisel
    • Install a custom package slice
    • Publish a slice definition
    • Contribute to Rockcraft documentation
    • Build the documentation
    • Use Rockcraft's GitHub Action
    • Convert an entrypoint to a Pebble layer
    • Publish a rock to a registry
    • Migrate a Docker image to a chiselled rock
    • Chisel an existing rock
    • Include local and remote files
    • Override a plugin's build
  • Reference
    • rockcraft.yaml
      • Part properties
    • Rockcraft commands
      • build
      • clean
      • expand-extensions
      • extensions
      • init
      • list-extensions
      • pack
      • prime
      • pull
      • stage
      • version
    • Extensions
      • flask-framework
    • Rockcraft plugins
      • .NET plugin
      • Ant plugin
      • Autotools plugin
      • CMake plugin
      • Dump plugin
      • Go plugin
      • Make plugin
      • Maven plugin
      • Meson plugin
      • Nil plugin
      • NPM Plugin
      • Python plugin
      • Qmake plugin
      • Rust plugin
      • SCons plugin
    • Parts and Steps
  • Explanation
    • Why use Rockcraft?
    • Chisel
    • Overlay step
    • Rocks
    • Bases
    • Pebble
    • From prime step to OCI layer
    • Filesets
    • Parts
    • Lifecycle details
    • Dump Plugin
Back to top
View this page
Edit this page

Migrate a popular Docker image to a chiselled rock¶

Prerequisites¶

  • snap enabled system (https://snapcraft.io)

  • LXD installed (https://linuxcontainers.org/lxd/getting-started-cli/)

  • skopeo installed (https://github.com/containers/skopeo). Skopeo will also be automatically installed as a Rockcraft dependency

  • Docker installed (https://snapcraft.io/docker)

  • a text editor

Install Rockcraft¶

Install Rockcraft on your host:

sudo snap install rockcraft --classic

Project Setup¶

For this tutorial, the reference Docker image will be Microsoft’s .NET Runtime 6.0. The target base Ubuntu release will be Jammy, and the target architecture will be amd64.

Create a new directory, write the reference Dockerfile (pasted below) into a text editor and save it as Dockerfile:

ARG REPO=mcr.microsoft.com/dotnet/runtime-deps

# Installer image
FROM amd64/buildpack-deps:jammy-curl AS installer

# Retrieve .NET Runtime
RUN dotnet_version=6.0.16 \
    && curl -fSL --output dotnet.tar.gz https://dotnetcli.azureedge.net/dotnet/Runtime/$dotnet_version/dotnet-runtime-$dotnet_version-linux-x64.tar.gz \
    && dotnet_sha512='c8891b791a51e7d2c3164470dfd2af2ce59af3c26404e84075277e307df7dcd1e3ccf1a1a3c2655fe2eea8a30f8349b7adbbe5de4cedfee52da06729a505d8f5' \
    && echo "$dotnet_sha512  dotnet.tar.gz" | sha512sum -c - \
    && mkdir -p /dotnet \
    && tar -oxzf dotnet.tar.gz -C /dotnet \
    && rm dotnet.tar.gz

# .NET runtime image
FROM $REPO:6.0.24-jammy-amd64

# .NET Runtime version
ENV DOTNET_VERSION=6.0.16

COPY --from=installer ["/dotnet", "/usr/share/dotnet"]

RUN ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet

For the sake of comparison, start by building this Docker image by running:

docker build -t dotnet-runtime:reference .

The output should look as follows:

[+] Building 0.6s (10/10) FINISHED
=> [internal] load .dockerignore0.0s
=> => transferring context: 2B0.0s
=> [internal] load build definition from Dockerfile    0.0s
=> => transferring dockerfile: 881B   0.0s
=> [internal] load metadata for mcr.microsoft.com/dotnet/runtime-deps:6.0.16-jammy-amd64    0.1s
=> [internal] load metadata for docker.io/amd64/buildpack-deps:jammy-curl    0.6s
=> [stage-1 1/3] FROM mcr.microsoft.com/dotnet/runtime-deps:6.0.16-jammy-amd64@sha256:e764c6f0cc866a1f2932  0.0s
=> [installer 1/2] FROM docker.io/amd64/buildpack-deps:jammy-curl@sha256:e1f00c6daf4cd328bbef9c52e6c60f18a  0.0s
=> CACHED [installer 2/2] RUN dotnet_version=6.0.16&& curl -fSL --output dotnet.tar.gz https://dotnet  0.0s
=> CACHED [stage-1 2/3] COPY --from=installer [/dotnet, /usr/share/dotnet]   0.0s
=> CACHED [stage-1 3/3] RUN ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet   0.0s
=> exporting to image   0.0s
=> => exporting layers  0.0s
=> => writing image sha256:a24cab51d4d02019dafcd22a2e2a3e1e6d033f9bbf1cb401d465cb2426bb2264 0.0s
=> => naming to docker.io/library/dotnet-runtime:reference   0.0s

Now, inspect this .NET reference image’s size:

docker images dotnet-runtime:reference

The output should look as follows:

REPOSITORY TAG IMAGE ID CREATED SIZE
dotnet-runtime   reference   a24cab51d4d0   4 minutes ago   187MB

And make sure it is functional:

docker run --rm dotnet-runtime:reference dotnet --info

The output should look as follows:

global.json file:
Not found

Host:
Version:6.0.16
Architecture: x64
Commit: 1e620a42e7

.NET SDKs installed:
No SDKs were found.

.NET runtimes installed:
Microsoft.NETCore.App 6.0.16 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

Download .NET:
https://aka.ms/dotnet-download

Learn about .NET Runtimes and SDKs:
https://aka.ms/dotnet/runtimes-sdk-info

Convert Dockerfile to rockcraft.yaml file¶

From a quick analysis of the reference Dockerfile above, the following requirements must be met:

  • R1. The rock must be based on Ubuntu Jammy

  • R2. There is no predefined Entrypoint or default command

  • R3. The rock must have version 6.0 of the .NET Runtime installed

  • R4. /usr/bin/dotnet must be a symbolic link to the .NET binary

With these requirements in mind, and in the same directory as the Dockerfile from above, write the following into a text editor and save it as rockcraft.yaml:

name: dotnet-runtime
summary: .NET Runtime 6.0
description: A Chiselled rock for the .NET Runtime 6.0
version: chiselled

# Use a "bare" base for an even smaller rock
base: bare

# Meeting requirement R1 by making sure the rock builds on Jammy
build-base: [email protected]

license: Apache-2.0

# Target architecture is amd64
platforms:
  amd64:  # Make sure this value matches your computer's architecture

# For meeting requirement R2, simply don't specify any entrypoint (aka "services")

parts:
  install-dotnet-runtime:
    plugin: nil

    # Based on requirement R3, install version 6.0 of the .NET runtime "libs" slice
    stage-packages:
      - base-files_base
      - dotnet-runtime-6.0_libs
    
    # Based on requirement R4, create the symbolic link
    override-prime: |
      craftctl default
      ln -s /usr/lib/dotnet/dotnet usr/bin/

Note the subtle chiselling of the .NET Runtime package in the rockcraft.yaml file above. You are requesting Rockcraft to install the libs slice of the dotnet-runtime deb, which is defined in the ubuntu-22.04 Chisel release.

Pack the Chiselled Rock with Rockcraft¶

To build the rock, run:

rockcraft pack --verbosity debug

The output should be similar to:

2023-04-19 15:52:48.045 Starting Rockcraft 0.0.1.dev1
...
2023-04-19 15:52:48.214 Launching instance...
2023-04-19 15:52:48.214 Executing on host: lxc remote list --format=yaml
...
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:57.784 Executing parts lifecycle: build install-dotnet-runtime
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:57.784 Executing action
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:57.784 execute action install-dotnet-runtime:Action(part_name='install-dotnet-runtime', step=Step.BUILD, action_type=ActionType.RUN, reason=None, project_vars=None, properties=ActionProperties(changed_files=None, changed_dirs=None))
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:57.785 load state file: /root/parts/install-dotnet-runtime/state/pull
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:58.081 :: 2023/04/19 13:56:58 Consulting release repository...
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:58.349 :: 2023/04/19 13:56:58 Fetching current ubuntu-22.04 release...
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:58.351 :: 2023/04/19 13:56:58 Processing ubuntu-22.04 release...
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:58.369 :: 2023/04/19 13:56:58 Selecting slices...
2023-04-19 15:58:25.138 :: 2023-04-19 13:56:58.369 :: 2023/04/19 13:56:58 Fetching ubuntu 22.04 jammy suite details...
2023-04-19 15:58:25.138 :: 2023-04-19 13:57:04.548 :: 2023/04/19 13:57:04 Release date: Thu, 21 Apr 2022 17:16:08 UTC
2023-04-19 15:58:25.138 :: 2023-04-19 13:57:04.549 :: 2023/04/19 13:57:04 Fetching index for ubuntu 22.04 jammy main component...
2023-04-19 15:58:25.138 :: 2023-04-19 13:57:05.684 :: 2023/04/19 13:57:05 Fetching index for ubuntu 22.04 jammy universe component...
2023-04-19 15:58:25.138 :: 2023-04-19 13:57:25.215 :: 2023/04/19 13:57:25 Fetching ubuntu 22.04 jammy-security suite details...
2023-04-19 15:58:25.138 :: 2023-04-19 13:57:25.289 :: 2023/04/19 13:57:25 Release date: Wed, 19 Apr 2023 12:55:48 UTC
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:25.289 :: 2023/04/19 13:57:25 Fetching index for ubuntu 22.04 jammy-security main component...
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:30.489 :: 2023/04/19 13:57:30 Fetching index for ubuntu 22.04 jammy-security universe component...
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:32.522 :: 2023/04/19 13:57:32 Fetching ubuntu 22.04 jammy-updates suite details...
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:32.667 :: 2023/04/19 13:57:32 Release date: Wed, 19 Apr 2023 13:29:07 UTC
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:32.668 :: 2023/04/19 13:57:32 Fetching index for ubuntu 22.04 jammy-updates main component...
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:33.631 :: 2023/04/19 13:57:33 Fetching index for ubuntu 22.04 jammy-updates universe component...
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:37.908 :: 2023/04/19 13:57:37 Fetching pool/main/b/base-files/base-files_12ubuntu4.3_amd64.deb...
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:38.157 :: 2023/04/19 13:57:38 Fetching pool/main/g/glibc/libc6_2.35-0ubuntu3.1_amd64.deb...
2023-04-19 15:58:25.139 :: 2023-04-19 13:57:40.834 :: 2023/04/19 13:57:40 Fetching pool/main/g/gcc-12/libgcc-s1_12.1.0-2ubuntu1~22.04_amd64.deb...
2023-04-19 15:58:25.140 :: 2023-04-19 13:57:41.262 :: 2023/04/19 13:57:41 Fetching pool/main/g/gcc-12/libstdc++6_12.1.0-2ubuntu1~22.04_amd64.deb...
2023-04-19 15:58:25.140 :: 2023-04-19 13:57:42.001 :: 2023/04/19 13:57:42 Fetching pool/universe/d/dotnet6/dotnet-host_6.0.116-0ubuntu1~22.04.1_amd64.deb...
2023-04-19 15:58:25.140 :: 2023-04-19 13:57:42.119 :: 2023/04/19 13:57:42 Fetching pool/universe/d/dotnet6/dotnet-hostfxr-6.0_6.0.116-0ubuntu1~22.04.1_amd64.deb...
2023-04-19 15:58:25.140 :: 2023-04-19 13:57:42.237 :: 2023/04/19 13:57:42 Fetching pool/main/i/icu/libicu70_70.1-2_amd64.deb...
2023-04-19 15:58:25.140 :: 2023-04-19 13:57:44.046 :: 2023/04/19 13:57:44 Fetching pool/main/u/ust/liblttng-ust-common1_2.13.1-1ubuntu1_amd64.deb...
2023-04-19 15:58:25.140 :: 2023-04-19 13:57:44.146 :: 2023/04/19 13:57:44 Fetching pool/main/n/numactl/libnuma1_2.0.14-3ubuntu2_amd64.deb...
2023-04-19 15:58:25.141 :: 2023-04-19 13:57:44.247 :: 2023/04/19 13:57:44 Fetching pool/main/u/ust/liblttng-ust-ctl5_2.13.1-1ubuntu1_amd64.deb...
2023-04-19 15:58:25.141 :: 2023-04-19 13:57:44.355 :: 2023/04/19 13:57:44 Fetching pool/main/u/ust/liblttng-ust1_2.13.1-1ubuntu1_amd64.deb...
2023-04-19 15:58:25.141 :: 2023-04-19 13:57:44.571 :: 2023/04/19 13:57:44 Fetching pool/main/o/openssl/libssl3_3.0.2-0ubuntu1.8_amd64.deb...
2023-04-19 15:58:25.141 :: 2023-04-19 13:57:45.111 :: 2023/04/19 13:57:45 Fetching pool/main/l/llvm-toolchain-13/libunwind-13_13.0.1-2ubuntu2.1_amd64.deb...
2023-04-19 15:58:25.141 :: 2023-04-19 13:57:45.213 :: 2023/04/19 13:57:45 Fetching pool/main/x/xz-utils/liblzma5_5.2.5-2ubuntu1_amd64.deb...
2023-04-19 15:58:25.141 :: 2023-04-19 13:57:53.384 :: 2023/04/19 13:57:53 Fetching pool/main/libu/libunwind/libunwind8_1.3.2-2build2_amd64.deb...
2023-04-19 15:58:25.141 :: 2023-04-19 13:57:53.523 :: 2023/04/19 13:57:53 Fetching pool/main/z/zlib/zlib1g_1.2.11.dfsg-2ubuntu9.2_amd64.deb...
2023-04-19 15:58:25.141 :: 2023-04-19 13:57:53.982 :: 2023/04/19 13:57:53 Fetching pool/universe/d/dotnet6/dotnet-runtime-6.0_6.0.116-0ubuntu1~22.04.1_amd64.deb...
...
2023-04-19 15:56:00.566 :: 2023-04-19 13:55:59.756 Created new layer
2023-04-19 15:56:00.566 :: 2023-04-19 13:55:59.758 Adding Pebble entrypoint
2023-04-19 15:56:00.566 :: 2023-04-19 13:55:59.758 Configuring entrypoint...
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.767 Entrypoint set to ['/bin/pebble', 'enter']
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.767 Adding metadata
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.767 Configuring labels and annotations...
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.785 Labels and annotations set to ['org.opencontainers.image.version=chiselled', 'org.opencontainers.image.title=dotnet-runtime', 'org.opencontainers.image.ref.name=dotnet-runtime', 'org.opencontainers.image.licenses=Apache-2.0', 'org.opencontainers.image.created=2023-04-19T13:55:59.767870+00:00', 'org.opencontainers.image.base.digest=13155b5ad26816d4107ee499de072069a701c9fe183f7e347e8d88fee16e69c2']
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.792 Setting the ROCK's Control Data
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.797 Adding to layer: /tmp/tmpdqjmducj/.rock as '.rock'
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.797 Adding to layer: /tmp/tmpdqjmducj/.rock/metadata.yaml as '.rock/metadata.yaml'
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.803 Control data written
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.804 Metadata added
2023-04-19 15:56:00.567 :: 2023-04-19 13:55:59.804 Exporting to OCI archive
2023-04-19 15:56:00.567 :: 2023-04-19 13:56:00.148 Exported to OCI archive 'dotnet-runtime_chiselled_amd64.rock'

At the end of the process, a file named dotnet-runtime_chiselled_amd64.rock should be present in the current directory. That’s your chiselled rock, as an OCI archive.

Test the rock¶

First, import the recently created rock into Docker:

sudo rockcraft.skopeo --insecure-policy copy oci-archive:dotnet-runtime_chiselled_amd64.rock docker-daemon:dotnet-runtime:chiselled

Now inspect the chiselled .NET Runtime rock the same way as it was done for the reference Docker image:

docker images dotnet-runtime:chiselled

Which should print something like:

REPOSITORY TAG IMAGE ID CREATED SIZE
dotnet-runtime   chiselled   4e0951d180e3   About a minute ago   124MB

And make sure this rock is as functional as the reference Docker image:

docker run --rm dotnet-runtime:chiselled exec dotnet --info

The output should be similar to:

global.json file:
Not found

Host:
Version:6.0.16
Architecture: x64
Commit: 1e620a42e7

.NET SDKs installed:
No SDKs were found.

.NET runtimes installed:
Microsoft.NETCore.App 6.0.16 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]

Download .NET:
https://aka.ms/dotnet-download

Learn about .NET Runtimes and SDKs:
https://aka.ms/dotnet/runtimes-sdk-info

Conclusion¶

In this tutorial, you have migrated from an imperative container build recipe (Dockerfile) to a declarative one (rockcraft.yaml), without any overhead on the final recipe’s size or complexity.

The resulting rock ended up being 63MB smaller than the reference one, while offering the same .NET Runtime functionality.

Copyright © 2022-2024, Canonical Group Ltd
Last updated on Jul 05, 2024
Show source
Ask a question on Discourse
Open a GitHub issue for this page
Edit this page on GitHub
Contents
  • Migrate a popular Docker image to a chiselled rock
    • Prerequisites
    • Install Rockcraft
    • Project Setup
    • Convert Dockerfile to rockcraft.yaml file
    • Pack the Chiselled Rock with Rockcraft
    • Test the rock
    • Conclusion