d
ockerf
ile c
onverter
CLI to convert Dockerfiles to use Chainguard Images and APKs in FROM and RUN lines etc.
You can install dfc
from Homebrew:
brew install chainguard-dev/tap/dfc
You can also install dfc
from source:
go install github.com/chainguard-dev/dfc@latest
You can also use the dfc
container image (from Docker Hub or cgr.dev
):
docker run --rm -v "$PWD":/work chainguard/dfc
docker run --rm -v "$PWD":/work cgr.dev/chainguard/dfc
Convert Dockerfile and print converted contents to terminal:
dfc ./Dockerfile
Save the output to new Dockerfile called Dockerfile.chainguard
:
dfc ./Dockerfile > ./Dockerfile.chainguard
You can also pipe from stdin:
cat ./Dockerfile | dfc -
Convert the file in-place using --in-place
/ -i
(saves backup in .bak
file):
dfc --in-place ./Dockerfile
mv ./Dockerfile.bak ./Dockerfile # revert
Note: the Dockerfile
and Dockerfile.chainguard
in the root of this repo are not actually for building dfc
, they
are symlinks to files in the testdata/
folder so users can run the commands in this README.
By default, FROM lines that have been mapped to Chainguard images will use "ORG" as a placeholder under cgr.dev
:
FROM cgr.dev/ORG/<image>
To configure your cgr.dev
namespace use the --org
flag:
dfc --org="example.com" ./Dockerfile
Resulting in:
FROM cgr.dev/example.com/<image>
If mistakenly ran dfc
with no configuration options and just want to replace the ORG
in the converted file, you can run something like this:
sed "s|/ORG/|/example.com/|" ./Dockerfile > dfc.tmp && mv dfc.tmp ./Dockerfile
To use an alternative registry domain and root namespace, use the --registry
flag:
dfc --registry="r.example.com/cgr-mirror" ./Dockerfile
Resulting in:
FROM r.example.com/cgr-mirror/<image>
Note: the --registry
flag takes precedence over the --org
flag.
If you need to use a modified version of the default, embedded package map
file packages.yaml
, use the --mappings
flag:
dfc --mappings="./packages.yaml" ./Dockerfile
Want to submit an update to the default packages.yaml
?
Please open a GitHub pull request.
For complete before and after examples, see the testdata/
folder.
echo "FROM node" | dfc -
Result:
FROM cgr.dev/ORG/node:latest-dev
echo "RUN apt-get update && apt-get install -y nano" | dfc -
Result:
RUN apk add -U nano
cat <<DOCKERFILE | dfc -
FROM node
RUN apt-get update && apt-get install -y nano
DOCKERFILE
Result:
FROM cgr.dev/ORG/node:latest-dev
USER root
RUN apk add -U nano
dfc
detects the package manager being used and maps this to
a supported distro in order to properly convert RUN lines.
The following platforms are recognized:
OS | Package manager |
---|---|
Alpine ("alpine") | apk |
Debian/Ubuntu ("debian") | apt-get / apt |
Fedora/RedHat/UBI ("fedora") | yum / dnf / microdnf |
For each FROM
line in the Dockerfile, dfc
attempts to replace the base image with an equivalent Chainguard Image.
For each RUN
line in the Dockerfile, dfc
attempts to detect the use of a known package manager (e.g. apt-get
/ yum
/ apk
), extract the names of any packages being installed, try to map them via packages.yaml
, and replacing the old install with apk add -U <packages>
.
If dfc
has detected the use of a package manager and ended up converting a RUN line,
then USER root
will be appended under the last FROM
line.
In the future we plan to handle this more elegantly, but this is the current state.
For each ARG
line in the Dockerfile, dfc
checks if the ARG is used as a base image in a subsequent FROM
line. If it is, and the ARG has a default value that appears to be a base image, then dfc
will modify the default value to use a Chainguard Image instead.
Since adding users and groups in Chainguard Images in Dockerfiles requires
adduser
/ addgroup
(via busybox), when we detect the use of
useradd
or groupadd
commands in RUN
lines, we will automatically try to
convert them to the equivalent adduser
/ addgroup
commands.
If we see that you have installed the shadow
package
(which actually provides useradd
and groupadd
), then we do not modify
these commands and leave them as is.
The syntax for the tar
command is slightly different in busybox than it is
in the GNU version which is present by default on various distros.
For that reason, we will attempt to convert tar
commands in RUN
lines
using the GNU syntax to use the busybox syntax instead.
Get converted Dockerfile as JSON using --json
/ -j
:
dfc --json ./Dockerfile
Pipe it to jq
:
dfc -j ./Dockerfile | jq
Reconstruct the Dockerfile pre-conversion:
dfc -j ./Dockerfile | jq -r '.lines[]|(.extra + .raw)'
Reconstruct the Dockerfile post-conversion:
dfc -j ./Dockerfile | jq -r '.lines[]|(.extra + (if .converted then .converted else .raw end))'
Convert and strip comments:
dfc -j ./Dockerfile | jq -r '.lines[]|(if .converted then .converted else .raw end)'
Get list of all distros detected from RUN lines:
dfc -j ./Dockerfile | jq -r '.lines[].run.distro' | grep -v null | sort -u
Get list of package managers detected from RUN lines:
dfc -j ./Dockerfile | jq -r '.lines[].run.manager' | grep -v null | sort -u
Get all the packages initially detected during parsing:
dfc -j ./Dockerfile | jq -r '.lines[].run.packages' | grep '"' | cut -d'"' -f 2 | sort -u | xargs
The package github.com/chainguard-dev/dfc/pkg/dfc
can be imported in Go and you can
parse and convert Dockerfiles on your own without the dfc
CLI:
package main
import (
"context"
"fmt"
"log"
"strings"
"github.com/chainguard-dev/dfc/pkg/dfc"
)
var (
raw = []byte(strings.TrimSpace(`
FROM node
RUN apt-get update && apt-get install -y nano
`))
org = "example.com"
)
func main() {
ctx := context.Background()
// Parse the Dockefile bytes
dockerfile, err := dfc.ParseDockerfile(ctx, raw)
if err != nil {
log.Fatalf("ParseDockerfile(): %v", err)
}
// Convert
converted, err := dockerfile.Convert(ctx, dfc.Options{
Organization: org,
})
if err != nil {
log.Fatalf("dockerfile.Convert(): %v", err)
}
// Print converted Dockerfile content
fmt.Println(converted)
}
- Incomplete Conversion: The tool makes a best effort to convert Dockerfiles but does not guarantee that the converted Dockerfiles will be buildable by Docker.
- Comment and Spacing Preservation: While the tool attempts to preserve comments and spacing, there may be cases where formatting is altered during conversion.
- Dynamic Variables: The tool may not handle dynamic variables in Dockerfiles correctly, especially if they are used in complex expressions.
- Unsupported Directives: Some Dockerfile directives may not be fully supported or converted, leading to potential build issues.
- Package Manager Commands: The tool focuses on converting package manager commands but may not cover all possible variations or custom commands.
- Multi-stage Builds: While the tool supports multi-stage builds, it may not handle all edge cases, particularly with complex stage dependencies.
- Platform-Specific Features: The tool may not account for platform-specific features or optimizations in Dockerfiles.
- Security Considerations: The tool does not perform security checks on the converted Dockerfiles, and users should review the output for potential vulnerabilities.
For issues related strictly to dfc
as an open source tool,
please open a GitHub issue.
Chainguard customers: please share issues or feature requests with your support contact so we can prioritize and escalate internally (with or without a GitHub issue/PR).
Interested in Chainguard Images and want to get in touch with sales? Use this form.