How to build an image from Dockerfile using Earthly target? - earthly

I would like to build and image from Dockerfile using Earthly.
You might be wondering why do I want that, because I can describe images right inside of Earthfile, but I have 2 reasons for using external Dockerfile:
ADD command (which I need to download file by URL) is not supported by Earthly yet
I would like to use a heredoc syntax for embedding file's content into container right from Dockerfile. This requires # syntax=docker/dockerfile:1.4, which is again not available in Earthfile
So, here is what I tried to do.
My approximate Dockerfile looks like:
# syntax=docker/dockerfile:1.4
FROM gcr.io/distroless/java17:nonroot
WORKDIR /opt/app
ADD --chown=nonroot https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.4.7/applicationinsights-agent-3.4.7.jar agent.jar
COPY <<EOF /opt/app/applicationinsights.json
{
"instrumentation": {}
}
EOF
And this is how I try to build it with Earthly:
base-image:
FROM earthly/dind:alpine
WORKDIR /build
ENV DOCKER_BUILDKIT=1 # <---- required to support heredoc syntax
COPY distroless-runtime-17.Dockerfile Dockerfile
WITH DOCKER --allow-privileged
RUN docker build . -t base-17-image
END
While the WITH DOCKER RUN part gets executed successfully, I do not know how to use the result of base-image target in other targets to package my app using the resulting base image. The FROM base-17-image just fails as if it does not exist (and this tag really does not exist - docker run base-17-image fails with the same reason).

It turned out to be very easy and natively supported:
The whole recipe is just 2 lines of code:
base-image:
FROM DOCKERFILE -f distroless-runtime-17.Dockerfile .
and the result can of the above step can be reused to package your application as: FROM +base-image

Related

Not able to see folder added in windows dockerfile

Am pulling a windows servercore:lts2019 image as my base image, adding a folder to it and creating my own image called "mygitlabpath/windows-2019". The contents of the Dockerfile are as follows:
FROM mcr.microsoft.com/windows/servercore:ltsc2019
ADD folder-z c:/windows/system32/config/systemprofile/folder-z
SHELL ["powershell"]
RUN ls c:/windows/system32/config/systemprofile/folder-z ( at this step i see all contents of folder-z)
Now I use this image i created and try to access c:/windows/system32/config/systemprofile/folder-z but there is no such folder called folder-z :
image: mygitlabpath/windows-2019
stages:
- build
build:
stage: build
script:
- ls c:/windows/system32/config/systemprofile/ ( at this step i expect to see folder-z.. but i dont)
What is that am missing? Any help is appreciated
Thanks
You should use COPY instead of ADD. Works for me

Docker image retag for build promotion

I am trying to promote a docker image in my Jenkins pipeline using Jenkins docker plugin but I am able to do so as I am getting following error.
"docker tag" requires exactly 2 arguments.
See 'docker tag --help'.
Usage: docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
I can see the following in the logs
docker tag artifactory.mycompany.com/docker-dev/appname/dev:latest artifactory.mycompany.com/docker-dev/appname/dev:artifactory.mycompany.com/docker-dev/appname/dev:latest artifactory.mycompany.com/docker-dev/appname/test:latest
Pipeline code:
testImage = docker.image("artifactory.mycompany.com/docker-dev/appname/dev:latest")
testImage.pull()
testImage.push("artifactory.mycompany.com/docker-dev/appname/dev:latest artifactory.mycompany.com/docker-dev/appname/test:latest" )
Any idea what's wrong here...
Edit#1:
If I do following the I get different error.
testImage = docker.image("artifactory.mycompany.com/docker-dev/appname/dev:latest")
testImage.pull()
testImage.tag("artifactory.mycompany.com/docker-dev/appname/test:latest")
testImage.push( )
Error:
Error parsing reference: "artifactory.mycompany.com/docker-dev/appnamee/dev:artifactory.mycompany.com/docker-dev/appname/test:latest" is not a valid repository/tag: invalid reference format
It seems that you are running into some confusion on what each docker command does and how to add new tags to an existing docker on your workspace.
On Jenkins world, docker commands behave like this
docker.image takes a single argument, composing IMAGE_NAME:TAG
docker.tag with a single argument, will assume TAG (this command will not change 'IMAGE_NAME', it will only change the TAG part)
docker.push takes a single optional argument, TAG, meant to push an already existing Image with a different tag only (not with a different IMAGE_NAME)
On your pipeline, you are trying to change the IMAGE_NAME part of the docker identifier, since, none of the above commands help you.
NEW SOLUTION
Another way of approaching this issue is to make the IMAGE_NAME change via shell, and then use the Jenkins plugins to map and push the images
sh("docker tag ORIGINAL_IMAGE_NAME:ORIGINAL_TAG NEW_IMAGE_NAME:NEW_TAG")
newImage = docker.image("NEW_IMAGE_NAME:NEW_TAG")
# docker plugin should find the image on the localhost, so there is no need to pull it form the registry
newImage.push
on your code, something like
sh ('docker tag artifactory.mycompany.com/docker-dev/appname/dev:latest artifactory.mycompany.com/docker-dev/appname/test:latest')
testImage2 = docker.image('artifactory.mycompany.com/docker-dev/appname/test:latest')
and then, push each image independently from the other
testImage.push()
testImage2.push()
DID NOT WORK
You could try supplying 2 arguments to docker.tag, such as
docker.tag (ORIGINAL_IMAGE_NAME:ORIGINAL_TAG, NEW_IMAGE_NAME:NEW_TAG)
in your case, something like
testImage.tag ("artifactory.mycompany.com/docker-dev/appname/dev:latest" "artifactory.mycompany.com/docker-dev/appname/test:latest")
and then, push each image independently from the other
testImage.push (ORIGINAL_IMAGE_NAME:ORIGINAL_TAG)
testImage.push (NEW_IMAGE_NAME:NEW_TAG)

Building a minimal container for a Go program

I want to build a tiny container image from scratch using Buildah to run a Go app.
Apart from the app itself, what other libraries etc need to be included. I am thinking that glibc is needed - is there anything else?
So in summary, I think I am asking "what are all the external dependencies that a compiled Go app needs on Linux?"
#Dave C gave the information to correctly answer this. Using ldd with the test app returned:
[bryon#localhost resttest]$ ldd restest
linux-vdso.so.1 (0x00007fff139fe000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fbad6ce2000)
libc.so.6 => /lib64/libc.so.6 (0x00007fbad691f000)
/lib64/ld-linux-x86-64.so.2 (0x00007fbad6f02000)
[bryon#localhost resttest]$
So for those looking to build a minimal container with Buildah, the BASH script to generate it would look like this:
#!/bin/bash
#
# Run this shell script after you have run the command: "buildah unshare"
#
git clone https://github.com/bryonbaker/resttest.git
cd resttest
go build restest.go
container=$(buildah from scratch)
mnt=$(buildah mount $container)
mkdir $mnt/bin
mkdir $mnt/lib64
buildah config --workingdir /bin $container
buildah copy $container restest /bin/restest
buildah copy $container /lib64/libpthread.so.0 /lib64
buildah copy $container /lib64/libc.so.6 /lib64
buildah copy $container /lib64/ld-linux-x86-64.so.2 /lib64
buildah config --port 8000 $container
#
# This step is not working properly.
# Need to run with podman -p 8000:8000 --entrypoint /bin/restest restest:latest
buildah config --entrypoint /bin/restest $container
buildah commit --format docker $container restest:latest
This generates a 14MB container for a simple microservice! There are no additional files to be worrying about for vulnerabilities etc.
I have a small defect I can't work out on entrypoints so I am overriding the entrypoint on start, but to test it run:
podman -p8000:8000 --entrypoint /bin/restest restest:latest
Then just type the following in a Terminal session:
curl http://localhost:8000
So thanks Dave C!
I know this is quite late answer, but it does tell how to build the slimmiest image for Golang programs. It is based on the question Deployment using image from scratch fails to start
The trick is to build statically linked executable and place it into the empty image called scratch. The image contains just a single file, that exact executable. It is the smallest image possible.
Docker file:
FROM golang:latest as builder
# The Dockerfile expects the source code of the application
# to reside in ./src/ directory
COPY src /src
WORKDIR /src
# Build statically linked file and strip debug information
# The Dockerfile expects the `main` package to be at the root of the module
RUN CGO_ENABLED=0 go build -ldflags="-extldflags=-static -s -w" -o executable
# scratch is an empty image
FROM scratch
# If you need /bin/sh and a few utilities, uncomment
# the following line. It increases the image by 5.5 MB
# FROM alpine:latest
COPY --from=builder /src/executable /executable
# copy other files if needed
ENTRYPOINT ["/executable"]
The Dockerfile expects the source code to be in src directory
<project_root>
|_ Dockerfile
|_ src/
|_ go.mod
|_ package_main.go # file with `package main` and `func main()`
|_ other source files
The command docker build ./ -t my-minimal-go produces the image named my-minimal-go:latest
To prove that it is the minimal image, save it to TAR and study the contents:
docker image save my-minimal-go:latest > my-minimal-go.tar
tar tf my-minimal-go.tar
The contents is something like
84ebda22f9b32043fdcb7bb70c559f0ee91cac60b4b92f1ce424662afec6d4b9.json
e622775ad65d50bc0b9f30e6ce58ee7670f752c63c3ca70caba4f9165efdca80/
e622775ad65d50bc0b9f30e6ce58ee7670f752c63c3ca70caba4f9165efdca80/VERSION
e622775ad65d50bc0b9f30e6ce58ee7670f752c63c3ca70caba4f9165efdca80/json
e622775ad65d50bc0b9f30e6ce58ee7670f752c63c3ca70caba4f9165efdca80/layer.tar
manifest.json
repositories
And to see the list of files in the image:
docker image save my-minimal-go:latest | tar x --wildcards '*layer.tar' -O | tar t
Output:
executable
Just a single file, the minimal image.
I am assuming you have included the app dependencies in your docker image.
You won't require any external dependency to build a docker image. Just base image from Go is sufficient to build and run on Linux machines.
# Start from the latest Go base image
FROM golang:latest
# Set the Current Working Directory inside the container
WORKDIR /app
# Copy go mod and sum files
COPY go.mod go.sum ./
# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed
RUN go mod download

Issue with gdkpixbuff

I'm working on a iMX6, with Yocto, and I'm trying to use a Gstreamer plugin : gdkpixbufoverlay on my board, but I got the following error:
gst-launch-1.0 autovideosrc ! gdkpixbufoverlay location=image.png ! autovideosink
(gst-launch-1.0:441): GdkPixbuf-WARNING **: Cannot open pixbuf loader module file '/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache': No such file or directory
This likely means that your installation is broken.
Try running the command
gdk-pixbuf-query-loaders > /usr/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
to make things work again for the time being.
Setting pipeline to PAUSED ...
ERROR: Pipeline doesn't want to pause.
ERROR: from element /GstPipeline:pipeline0/GstGdkPixbufOverlay:gdkpixbufoverlay0: Could not load overlay image.
Additional debug info:
../../../gst-plugins-good-1.6.3/ext/gdk_pixbuf/gstgdkpixbufoverlay.c(508): gst_gdk_pixbuf_overlay_start (): /GstPipeline:pipeline0/GstGdkPixbufOverlay:gdkpixbufoverlay0:
Couldn't recognize the image file format for file 'image.png'
Setting pipeline to NULL ...
Freeing pipeline ...
So I tried to run the command advised, but I did not work either. I took a look at the output:
/usr/lib/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders
# GdkPixbuf Image Loader Modules file
# Automatically generated file, do not edit
# Created by gdk-pixbuf-query-loaders from gdk-pixbuf-2.32.3
#
# LoaderDir = /usr/lib/gdk-pixbuf-2.0/2.10.0/loaders
#
And the output is full of comments. I think something is wrong on my board but I can't find where.
Did I need some other packages to make gdkpixbuf work?
I tested on Poky distribution, on krogoth and pyro branch, and the result is the same.
Add to your recipe (the image-YOUR_RECIPE_NAME.bb file) the necessary package and sub-packages names as rows.
In my case the package got split, so in order to use PNG and JPEG image overlays, my recipe looks like the following:
...
gdk-pixbuf \
gdk-pixbuf-loader-png \
gdk-pixbuf-loader-jpeg \
...
After saving the changes, don't forget to bitbake it:
bitbake image-YOUR_RECIPE_NAME

Docker-Compose: Composing with Dockerfiles that need relative imports

To begin, this is my project hierarchy:
myproj/
- commons1/
- com1_file1.go
- ...
- commons2/
- com2_file1.go
- ...
- module1/
- mod1_file1.go
- Dockerfile
- ...
- module2/
- mod2_file1.go
- Dockerfile
- ...
- docker-compose.yml
What I'd like to do is that when module1 and module2 containers start up, they each have a copy of all the commonsN directories in their GOPATH's so that each can access the common libraries exposed by each of the commonsN directories.
For example, I would like to see something like this in the container for module1:
/go/
- src/
- commons1/
- com1_file1.go
- ...
- commons2/
- com2_file1.go
- ...
- module1/
- mod1_file1.go
- ...
Reason being is that this is basically how my local GOPATH looks (with the addition of the other modules of course) so that I can do something like this in my source files:
package main
import(
"fmt"
"myproj/commons1"
)
func main() {
fmt.Println("Some thing from common library :", commons1.SomethingFromCommons)
}
From my naive understanding of Docker, it appears I'm not allowed to modify my Dockerfiles to do something along the lines of COPY ../commons1 /go/src/commons1, so I'm wondering how I would go about accomplishing this?
I would strongly prefer to not go the Github route since the source code is all behind company proxies and whatnot and I'm imagining configuring all that is going to take way longer than simply copying some directories.
Edit
I have updated my docker-compose.yml file to look something like this per suggestion from barat:
version: '2'
services:
module1:
volumes:
- ./commons1:/go/src/myproj/commons1
build: module1/
Dockerfile for module1 looks like this:
FROM golang:1.8.0
RUN mkdir -p /go/src/app
WORKDIR /go/src/app
COPY . /go/src/app
RUN go get -d -v
RUN go install -v
ENTRYPOINT /go/bin/app
EXPOSE 8080
docker-compose build fails on the go get -d -v with error:
package myproj/commons1: unrecognized import path "myproj/commons1" (import path does not begin with hostname)
If myproj/commons1 was copied into /go/src/, then this shouldn't be an issue right? I'm guessing then it hasn't been copied over then?
You could build an image including commons1 and commons2 that your other images are based on.
FROM golang:1.8.0
RUN mkdir -p /go/src/myproj/commons1 && mkdir -p /go/src/myproj/commons2
COPY commons1/ /go/src/myproj/commons1/
COPY commons2/ /go/src/myproj/commons2/
The downside is this requires an external build step whenever you update one of the common projects:
docker build -t me/myproj:commons .
Then your compose apps can rely on the commons image instead of golang and build as normal without the volumes.
FROM me/myproj:commons
...
So problem was the go get -d -v command since it was complaining myproj/commons1 wasn't installed in $GOPATH/src basically. This I of course suspect was because Docker Compose wasn't mounting the volumes I mentioned before it ran the go get on docker-compose build so I made a work around in my docker-compose.yml but it is far from elegant:
version: '2'
services:
module1:
volumes:
- ./commons1:/go/src/myproj/commons1
build: module1/
ports:
- "8080:8080"
command: bash -c "go get -d -v && go install -v && /go/bin/app
This is obviously far from ideal because my Go binary is rebuilt every time I do a docker-compose up regardless of whether or not I ran docker-compose build.
This is also problematic because I wanted to use dockerize for certain containers to wait until another container has started up completely and it becomes quite messy now I think.

Resources