How to convert an 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 [ -g 'daemon off;' ]
environment:
TZ: UTC
on-failure: shutdown
nginx-debug:
override: replace
startup: disabled
command: nginx-debug [ -g 'daemon off;' ]
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: "[email protected]"
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 [ -g 'daemon off;' ]
environment:
TZ: UTC
on-failure: shutdown
nginx-debug:
override: replace
startup: disabled
command: nginx-debug [ -g 'daemon off;' ]
environment:
TZ: UTC
on-failure: shutdown
This Rockcraft recipe is almost 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 pack
You should see something like this:
Launching instance...
Retrieved base [email protected] for amd64
Extracted [email protected]
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 rockcraft.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 8000:80 custom-nginx-rock:latest
The Pebble daemon will start without any NGINX service, although you could
still later on ask for either service to be started. For instance, you can
start the nginx
service by typing:
docker exec nginx-pebble-service pebble start nginx
We could have chosen to make one of the two services run on startup by changing
its corresponding startup
field value to enabled
.
Once you start one of the services, your container should be deployed and
running the nginx
or nginx-debug
service, and you should be able to see
the NGINX landing page by accessing port 8080 on you localhost:
curl localhost:8000
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>