Skip to content

Commit 7da2dd1

Browse files
author
Natalie Arellano
committed
Move tutorial to create extension guide
Signed-off-by: Natalie Arellano <[email protected]>
1 parent faa06a4 commit 7da2dd1

File tree

10 files changed

+389
-220
lines changed

10 files changed

+389
-220
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
+++
2+
title="Extension Author Guide"
3+
weight=4
4+
include_summaries=true
5+
expand=true
6+
aliases=[
7+
"/docs/create-extension/"
8+
]
9+
+++
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
+++
2+
title="Create an extension"
3+
weight=4
4+
summary="This is a step-by-step tutorial for creating a CNB Image Extension for `curl`."
5+
+++
6+
7+
<!--+if false+-->
8+
## Prerequisites
9+
10+
Before we get started, make sure you've got the following installed:
11+
12+
{{< download-button href="https://store.docker.com/search?type=edition&offering=community" color="blue" >}} Install Docker {{</>}}
13+
{{< download-button href="/docs/install-pack" color="pink" >}} Install pack {{</>}}
14+
15+
## Overview
16+
<!--+end+-->
17+
18+
This is a step-by-step tutorial for creating a CNB Image Extension for `curl`.
19+
20+
- [Set up your local environment](/docs/buildpack-author-guide/create-buildpack/setup-local-environment)
21+
- [See a build that requires base image extension in order to succeed](/docs/extension-author-guide/create-extension/why-dockerfiles)
22+
- [Building blocks of an extension](/docs/extension-author-guide/create-extension/building-blocks-extension)
23+
- [Generating a build.Dockerfile for your application](/docs/extension-author-guide/create-extension/build-dockerfile)
24+
- [Generating a run.Dockerfile for your application](/docs/extension-author-guide/create-extension/run-dockerfile)
25+
26+
<!--+if false+-->
27+
---
28+
29+
<a href="/docs/extension-author-guide/create-extension/setup-local-environment" class="button bg-pink">Start Tutorial</a>
30+
<!--+end+-->
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
+++
2+
title="Generating a build.Dockerfile"
3+
weight=404
4+
+++
5+
6+
### Examine `tree` extension
7+
8+
#### detect
9+
10+
* `cat $workspace/samples/extensions/tree/bin/detect` - the extension always detects (because its exit code is `0`) and provides a dependency
11+
called `tree` by writing to the build plan.
12+
13+
#### generate
14+
15+
* `cat extensions/tree/bin/generate` - the extension generates a `build.Dockerfile` that installs `tree` on the builder
16+
image
17+
18+
### Re-create our builder with `hello-extensions` updated to require `tree`
19+
20+
* Edit `$workspace/samples/buildpacks/hello-extensions/bin/detect` to uncomment the first set of lines that
21+
output `[[requires]]` to the build plan
22+
* `$workspace/pack/out/pack builder create $registry_namespace/extensions-builder --config $workspace/samples/builders/alpine/builder.toml --publish`
23+
24+
### See a build in action (run failure case)
25+
26+
* Build the application
27+
image: `$workspace/pack/out/pack build hello-extensions --builder $registry_namespace/extensions-builder --lifecycle-image $LIFECYCLE_IMAGE --verbose`
28+
- you should see:
29+
30+
```
31+
[detector] ======== Results ========
32+
[detector] pass: samples/[email protected]
33+
[detector] pass: samples/[email protected]
34+
[detector] Resolving plan... (try #1)
35+
[detector] samples/tree 0.0.1
36+
[detector] samples/hello-extensions 0.0.1
37+
[detector] Running generate for extension samples/[email protected]
38+
...
39+
[extender] Found build Dockerfile for extension 'samples/tree'
40+
[extender] Applying the Dockerfile at /layers/generated/build/samples_tree/Dockerfile...
41+
...
42+
[extender] Running build command
43+
[extender] ---> Hello Extensions Buildpack
44+
[extender] tree v1.8.0 (c) 1996 - 2018 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro
45+
...
46+
Successfully built image hello-extensions
47+
```
48+
49+
* See the image fail to run: `docker run hello-extensions` - you should
50+
see `ERROR: failed to launch: path lookup: exec: "curl": executable file not found in $PATH`.
51+
* What happened: our builder uses run image `cnbs/sample-stack-run:alpine` which does not have `curl` installed, so our
52+
process failed to launch
53+
54+
<!--+ if false+-->
55+
---
56+
57+
<a href="/docs/extension-author-guide/create-extension/run-dockerfile" class="button bg-pink">Next Step</a>
58+
<!--+ end +-->
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
+++
2+
title="Building blocks of a CNB Image Extension"
3+
weight=403
4+
+++
5+
6+
### Examine `tree` extension
7+
8+
* `tree $workspace/samples/extensions/tree`
9+
10+
You should see something akin to the following:
11+
12+
```
13+
.
14+
├── bin
15+
│ ├── detect <- similar to a buildpack ./bin/detect
16+
│ ├── generate <- similar to a buildpack ./bin/build
17+
├── extension.toml <- similar to a buildpack buildpack.toml
18+
```
19+
20+
* The `extension.toml` describes the extension, containing information such as its name, ID, and version, as well as the
21+
buildpack API that it implements. Though extensions are not buildpacks, they are expected to conform to the buildpack
22+
API except where noted. Consult the [spec](https://github.com/buildpacks/spec/blob/buildpack/main/image_extension.md)
23+
for further details.
24+
* `./bin/detect` is invoked during the `detect` phase. It analyzes application source code to determine if the extension
25+
is needed and contributes [build plan](/docs/reference/spec/buildpack-api/#build-plan) entries (much like a
26+
buildpack `./bin/detect`). Just like for buildpacks, a `./bin/detect` that exits with code `0` is considered to have
27+
passed detection, and fails otherwise.
28+
* `./bin/generate` is invoked during the `generate` phase (a new lifecycle phase that happens after `detect`). It
29+
outputs either or both of `build.Dockerfile` or `run.Dockerfile` for extending the builder or run image, respectively.
30+
* Only a limited set of Dockerfile instructions is supported - consult
31+
the [spec](https://github.com/buildpacks/spec/blob/buildpack/main/image_extension.md)
32+
for further details.
33+
* In the [initial implementation](/docs/features/dockerfiles#phased-approach), `run.Dockerfile` instructions are
34+
limited to a single `FROM` instruction (effectively, it is only possible to switch the run-time base image to a
35+
pre-created image i.e., no dynamic image modification is allowed). Consult
36+
the [spec](https://github.com/buildpacks/spec/blob/buildpack/main/image_extension.md)
37+
for further details.
38+
39+
We'll take a closer look at the executables for the `tree` extension in the next step.
40+
41+
<!--+ if false+-->
42+
---
43+
44+
<a href="/docs/extension-author-guide/create-extension/build-dockerfile" class="button bg-pink">Next Step</a>
45+
<!--+ end +-->
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
+++
2+
title="Generating a run.Dockerfile"
3+
weight=405
4+
+++
5+
6+
### Examine `curl` extension
7+
8+
#### detect
9+
10+
* `cat $workspace/samples/extensions/curl/bin/detect` - the extension always detects and provides a dependency
11+
called `curl`
12+
13+
#### generate
14+
15+
* `cat extensions/curl/bin/generate` - the extension generates a `run.Dockerfile` that switches the run image to
16+
reference `run-image-curl`
17+
18+
### Build a run image for `curl` extension to use
19+
20+
* `cat $workspace/samples/stacks/alpine/run/curl.Dockerfile` - this is a simple Dockerfile that creates a CNB run image
21+
from the `curl` base image by adding the required CNB user configuration and `io.buildpacks.stack.id` label; the
22+
Dockerfile could come from anywhere, but we include it in the `stacks` directory for convenience
23+
* `docker build --tag run-image-curl --file $workspace/samples/stacks/alpine/run/curl.Dockerfile .`
24+
25+
### Re-create our builder with `hello-extensions` updated to require `curl`
26+
27+
* Edit `$workspace/samples/buildpacks/hello-extensions/bin/detect` to uncomment the second set of lines that
28+
output `[[requires]]` to the build plan
29+
* `$workspace/pack/out/pack builder create $registry_namespace/extensions-builder --config $workspace/samples/builders/alpine/builder.toml --publish`
30+
31+
### See a build in action (success case)
32+
33+
* Build the application
34+
image: `$workspace/pack/out/pack build hello-extensions --builder $registry_namespace/extensions-builder --lifecycle-image $LIFECYCLE_IMAGE --verbose`
35+
- you should see:
36+
37+
```
38+
[detector] ======== Results ========
39+
[detector] pass: samples/[email protected]
40+
[detector] pass: samples/[email protected]
41+
[detector] pass: samples/[email protected]
42+
[detector] Resolving plan... (try #1)
43+
[detector] samples/tree 0.0.1
44+
[detector] samples/curl 0.0.1
45+
[detector] samples/hello-extensions 0.0.1
46+
[detector] Running generate for extension samples/[email protected]
47+
...
48+
[detector] Running generate for extension samples/[email protected]
49+
...
50+
[detector] Checking for new run image
51+
[detector] Found a run.Dockerfile configuring image 'run-image-curl' from extension with id 'samples/curl'
52+
...
53+
[extender] Found build Dockerfile for extension 'samples/tree'
54+
[extender] Applying the Dockerfile at /layers/generated/build/samples_tree/Dockerfile...
55+
...
56+
[extender] Running build command
57+
[extender] ---> Hello Extensions Buildpack
58+
[extender] tree v1.8.0 (c) 1996 - 2018 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro
59+
...
60+
Successfully built image hello-extensions
61+
```
62+
63+
* See the image run successfully: `docker run hello-extensions` - you should see something akin
64+
to `curl 7.85.0-DEV (x86_64-pc-linux-musl)`
65+
* What happened: now that `hello-extensions` requires both `tree` and `curl` in its build plan, both extensions are
66+
included in the build and provide the needed dependencies for build and launch, respectively
67+
* The `tree` extension installs `tree` at build time, as before
68+
* The `curl` extension switches the run image to `run-image-curl`, which has `curl` installed. Now our `curl` process
69+
can succeed!
70+
71+
## What's next?
72+
73+
The `tree` and `curl` examples are very simple, but we can unlock powerful new features with this functionality.
74+
Platforms could have several run images available, each tailored to a specific language family, thus limiting the number
75+
of installed dependencies for each image to the minimum necessary to support the targeted language. Image extensions
76+
could be used to switch the run image to that most appropriate for the current application. Similarly, builder images
77+
could be kept lean if image extensions are used to dynamically install the needed dependencies depending on the
78+
requirements of each application.
79+
80+
In the future, both run image switching and run image modification will be supported, opening the door to other use
81+
cases. Consult the [RFC](https://github.com/buildpacks/rfcs/pull/173) for further information.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
+++
2+
title="Set up your local environment"
3+
weight=401
4+
+++
5+
6+
Let's walk through a build that uses extensions, step by step. We will see an image extension that installs `curl` on
7+
the builder image, and switches the run image to an image that has `curl` installed.
8+
9+
### Ensure Docker is running
10+
11+
* `docker version`
12+
13+
If you see output similar to the following, you're good to go! Otherwise, start Docker and check again.
14+
15+
```
16+
Client: Docker Engine - Community
17+
Version: 20.10.9
18+
API version: 1.41
19+
Go version: go1.16.8
20+
Git commit: c2ea9bc
21+
Built: Mon Oct 4 16:08:29 2021
22+
OS/Arch: linux/amd64
23+
Context: default
24+
Experimental: true
25+
26+
Server: Docker Engine - Community
27+
Engine:
28+
Version: 20.10.9
29+
API version: 1.41 (minimum version 1.12)
30+
Go version: go1.16.8
31+
Git commit: 79ea9d3
32+
Built: Mon Oct 4 16:06:34 2021
33+
OS/Arch: linux/amd64
34+
Experimental: false
35+
```
36+
37+
### Setup workspace directory
38+
39+
* `workspace=<your preferred workspace directory>`
40+
41+
### Clone the pack repo and build it (FIXME: remove when pack with extensions-phase-2 support is released)
42+
43+
* `cd $workspace`
44+
* `git clone [email protected]:buildpacks/pack.git`
45+
* `cd pack`
46+
* `git checkout extensions-phase-2`
47+
* `make clean build`
48+
49+
### Clone the samples repo
50+
51+
* `cd $workspace`
52+
* `git clone https://github.com/buildpacks/samples.git`
53+
* `cd samples`
54+
* `git checkout extensions-phase-2` (FIXME: remove when `extensions-phase-2` merged)
55+
56+
<!--+ if false +-->
57+
---
58+
59+
<a href="/docs/extension-author-guide/create-extension/why-dockerfiles" class="button bg-pink">Next Step</a>
60+
<!--+ end+-->
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
+++
2+
title="Why Dockerfiles"
3+
weight=402
4+
+++
5+
6+
Let's see a build that requires base image extension in order to succeed.
7+
8+
### Examine `hello-extensions` buildpack
9+
10+
#### detect
11+
12+
* `cat $workspace/samples/buildpacks/hello-extensions/bin/detect` - the buildpack always detects but doesn't require any
13+
dependencies (as the output build plan is empty)
14+
15+
#### build
16+
17+
* `cat $workspace/samples/buildpacks/hello-extensions/bin/build` - the buildpack tries to use `tree` during the build
18+
phase, and defines a launch process called `curl` that runs `curl --version` at runtime
19+
20+
### Create a builder with extensions and publish it
21+
22+
* Ensure experimental features are enabled: `$workspace/pack/out/pack config experimental true`
23+
* Download the latest lifecycle tarball from the GitHub release
24+
page: https://github.com/buildpacks/lifecycle/releases/tag/v0.15.0-rc.1 (FIXME: update to 0.15.0 when released)
25+
* Edit `$workspace/samples/builders/alpine/builder.toml` to add the following at the end of the file:
26+
27+
```
28+
[lifecycle]
29+
uri = <path to lifecycle tarball in previous step>
30+
```
31+
32+
* Ensure you are authenticated with an OCI registry: `docker login` should succeed
33+
* Set your preferred registry namespace: `registry_namespace=<your preferred registry namespace>`
34+
* For now, it is necessary for the builder to be pushed to a registry for `pack build` with image extensions to
35+
succeed
36+
* `$workspace/pack/out/pack builder create $registry_namespace/extensions-builder --config $workspace/samples/builders/alpine/builder.toml --publish`
37+
38+
### See a build in action (build failure case)
39+
40+
* Ensure experimental features are enabled: `$workspace/pack/out/pack config experimental true`
41+
* Set the lifecycle image for `pack` to use in the untrusted builder workflow (as the trusted workflow that uses
42+
the `creator` is not currently supported): `LIFECYCLE_IMAGE=buildpacksio/lifecycle:0.15.0-rc.1` (FIXME: update to
43+
0.15.0 when released)
44+
* Build the application image (note that the "source" directory is effectively ignored in our
45+
example): `$workspace/pack/out/pack build hello-extensions --builder $registry_namespace/extensions-builder --lifecycle-image $LIFECYCLE_IMAGE --verbose --pull-policy always`
46+
- you should see:
47+
48+
```
49+
[detector] ======== Results ========
50+
[detector] pass: samples/[email protected]
51+
[detector] pass: samples/[email protected]
52+
[detector] Resolving plan... (try #1)
53+
[detector] skip: samples/[email protected] provides unused tree
54+
[detector] 1 of 2 buildpacks participating
55+
[detector] samples/hello-extensions 0.0.1
56+
...
57+
[extender] Running build command
58+
[extender] ---> Hello Extensions Buildpack
59+
[extender] /cnb/buildpacks/samples_hello-extensions/0.0.1/bin/build: line 6: tree: command not found
60+
[extender] ERROR: failed to build: exit status 127
61+
```
62+
63+
* What happened: our builder doesn't have `tree` installed, so the `hello-extensions` buildpack failed to build (as it
64+
tries to run `tree --version` in its `./bin/build` script). Even though there is a `samples/tree` extension that
65+
passed detection (`pass: samples/[email protected]`), because the `hello-extensions` buildpack didn't require `tree` in the
66+
build plan, the extension was omitted from the detected group (`skip: samples/[email protected] provides unused tree`). Let's
67+
take a look at what the `samples/tree` extension does...
68+
69+
<!--+ if false+-->
70+
---
71+
72+
<a href="/docs/extension-author-guide/create-extension/building-blocks-extension" class="button bg-pink">Next Step</a>
73+
<!--+ end +-->

0 commit comments

Comments
 (0)