How to convert a popular entrypoint to a Pebble layer¶
This guide will show you how to take an existing Docker image entrypoint
and convert it into a Pebble layer, aka the list of one or more services
which is defined in rockcraft.yaml
and then taken by the ROCK’s
Pebble entrypoint.
Reference entrypoint¶
For this guide, the reference Docker image entrypoint will be NGINX. The official Debian-based NGINX image’s Dockerfile can be found here.
In summary, this Dockerfile is basically installing NGINX into the image and then defining the OCI entrypoint to be a custom shell script which parses the first argument given to it at container deployment time, and then configures and launches NGINX accordingly.
Design the Pebble services¶
A Pebble layer
is composed of metadata, checks and services. The latter is present in
rockcraft.yaml
as a top-level field
and it represents the services which are loaded by the Pebble entrypoint when
deploying a ROCK.
Given the reference entrypoint, this guide’s goal is to create two services:
one for nginx
and another for nginx-debug
. The following services
snippet does just that:
services:
nginx:
override: replace
startup: disabled
command: nginx [ -h ]
environment:
TZ: UTC
on-failure: shutdown
nginx-debug:
override: replace
startup: disabled
command: nginx-debug [ -h ]
environment:
TZ: UTC
on-failure: shutdown
This is defining two separate Pebble services which are disabled by default
at startup, have the same environment variable, but are executed with
different commands (nginx
and nginx-debug
).
Build the ROCK¶
Copy the above snippet and incorporate it into the rockcraft.yaml
file
which will be used to build your ROCK, as shown below:
name: custom-nginx-rock
base: "ubuntu:22.04"
version: latest
summary: An NGINX ROCK
description: |
A ROCK equivalent of the official NGINX Docker image from Docker Hub.
license: Apache-2.0
platforms:
amd64:
package-repositories:
- type: apt
url: https://nginx.org/packages/mainline/ubuntu
key-id: 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
suites:
- jammy
components:
- nginx
priority: always
parts:
nginx-user:
plugin: nil
overlay-script: |
set -x
useradd -R $CRAFT_OVERLAY -M -U -r nginx
nginx:
plugin: nil
after:
- nginx-user
stage-packages:
- nginx
- tzdata
# Services to be loaded by the Pebble entrypoint
services:
nginx:
override: replace
startup: disabled
command: nginx [ -h ]
environment:
TZ: UTC
on-failure: shutdown
nginx-debug:
override: replace
startup: disabled
command: nginx-debug [ -h ]
environment:
TZ: UTC
on-failure: shutdown
This Rockcraft recipe is fully declarative, with the creation of the “nginx” user being the only scripted step.
To reproduce what the reference NGINX Dockerfile is doing, notice the use of
package-repositories
in this rockcraft.yaml
file, allowing you to also
make use of NGINX’s 3rd party package repository (even using the same
GPG key ID as the one used in the Dockerfile).
NOTE: to add custom configuration files, you can use the dump
plugin.
Now, build the final custom NGINX ROCK with:
rockcraft
You should see something like this:
Launching instance...
Retrieved base ubuntu:22.04 for amd64
Extracted ubuntu:22.04
Refreshing repositories | (4.6s)
Package repositories installed
Executed: pull nginx-user
Executed: pull nginx
Executed: pull pebble
Executed: overlay nginx-user
Executed: overlay nginx
Executed: overlay pebble
Executed: build nginx-user
Executed: skip pull nginx-user (already ran)
Executed: skip overlay nginx-user (already ran)
Executed: skip build nginx-user (already ran)
Executed: stage nginx-user (required to build 'nginx')
Executed: build nginx
Executed: build pebble
Executed: skip stage nginx-user (already ran)
Executed: stage nginx
Executed: stage pebble
Executed: prime nginx-user
Executed: prime nginx
Executed: prime pebble
Executed parts lifecycle
Exported to OCI archive 'custom-nginx-rock_latest_amd64.rock'
Then copy the resulting ROCK (from the OCI archive format) to the Docker daemon via:
sudo /snap/rockcraft/current/bin/skopeo --insecure-policy copy oci-archive:custom-nginx-rock_latest_amd64.rock docker-daemon:custom-nginx-rock:latest
And finally, run the container:
docker run -d --name nginx-pebble-service -p 8080:80 custom-nginx-rock:latest --args nginx -g 'daemon off;' \; start nginx
Notice the given command start nginx
, as this is Pebble’s client syntax to
let the Pebble daemon know that the nginx
service defined in
rockcraft.yaml
(which is disabled by default) should be enabled at startup.
Otherwise, the Pebble daemon would start without any NGINX service, although
you could still later on ask for that service to be started (via something like
docker exec <container-name> start nginx
).
At this point, your container should be deployed and running the nginx
service, and you should be able to see the NGINX landing page by accessing
port 8080 on you localhost:
curl localhost:8080
For which you should see the following output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>