go build fails to find local dependencies on docker - go

I'm trying to create a dockerfile for my go server but it keeps failing as it does not recognize some local dependencies (they are modules on the code itself, not external dependencies).
example:
import (
"<private-repo-url>/src/cmd/http-api/bootstrap" // this a local module that's part of the server
"go.uber.org/fx"
)
func main() {
fx.New(bootstrap.Module).Run()
}
Here's the error:
=> ERROR [7/7] RUN go build -a -o ./server 0.3s
------
> [7/7] RUN go build -a -o ./server:
#10 0.295 server.go:4:2: no required module provides package <private-repo-url>/src/cmd/http-api/bootstrap; to add it:
#10 0.295 go get <private-repo-url>/src/cmd/http-api/bootstrap
------
executor failed running [/bin/sh -c go build -a -o ./server]: exit code: 1
Please note that this private-repo-url corresponds to this application's repository (it's not an external dependency).
Here's the Dockerfile
FROM golang:1.17
WORKDIR /balrog
# Copy dependency definitions and download them
ADD go.mod .
ADD go.sum .
RUN go mod download
# Build the binary
ADD ./src .
ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
RUN go build -a -o ./server
#Run the server
CMD ["/server"]
And the mod.go file:
module <private-repo-url>
go 1.16
require (
github.com/gin-gonic/gin v1.7.7
github.com/google/uuid v1.3.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/sirupsen/logrus v1.8.1
go.uber.org/fx v1.15.0
)
I've read about GO111MODULE saying it should be on, and I also read that it's enabled by default from 1.17 (here).
Also according to the official docker image (in dockerhub) the right way is using go get and go install after copying all the files. This approach lead me to a slightly different problem which is that the docker can not access the repository (because it's private) and adding credentials to the docker is something I'd like to avoid.
I tried to play arround with the environment variable GOVCS setting it's value like:
ENV GOVCS=github.com:git,gitlab.com:off
But it still did fail with the same error.
Finally I tried with the replace, I figured that if I removed the from the local dependencies it would work, so I executed (inside the Dockerfile) this:
RUN go mod edit -replace <private-repo-url>=./
Again it did fail with:
=> ERROR [builder 10/10] RUN go build -a -o ./server 0.3s
------
> [builder 10/10] RUN go build -a -o ./server:
#17 0.299 server.go:4:2: module <private-repo-url>/src provides package <private-repo-url>/src/cmd/http-api/bootstrap and is replaced but not required; to add it:
#17 0.299 go get <private-repo-url>/src
#17 0.299 server.go:5:2: no required module provides package go.uber.org/fx; to add it:
#17 0.299 go get go.uber.org/fx
Is there any way to prevent go builder/package installer to look for these files externally? As both go mod and go get + go install try to access this private repository and fail as they do not have access. But they should not try to access it on the first place as it's the application's repository... Or is that I'm doing something wrong (clearly or I wouldn't be here), missing something?

ADD ./src . - that copies the contents of src to the current folder, stripping away the src part.
It should just be COPY . ./
Also note that it's not recommended to have a src subfolder in your source tree - the folder that contains go.mod is already the source tree.

Related

How to build an image from Dockerfile using Earthly target?

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

Github Action : golang cannot find package

I set sample github action to my repository. snippet is here.
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go#v2
with:
go-version: ^1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout#v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
but job is fail where Get dependencies. error is here.
package github.com/<organization-account>/<repo-name>/api/domain/repo: cannot find package "github.com/<organization-account>/<repo-name>/api/domain/repo" in any of:
/opt/hostedtoolcache/go/1.14.4/x64/src/github.com/<organization-account>/<repo-name>/api/domain/repo (from $GOROOT)
/home/runner/go/src/github.com/<organization-account>/<repo-name>/api/domain/repo (from $GOPATH)
of course. My code is work at local when go run main.go. I have go.mod, go.sum.
This is not a correct answer for the OP, but may help someone reaching here from searching.
In the root of your go project:
go mod init
go mod tidy
Commit and push to github, and it should work now.
You need to setup a token for go get or go mod to download private repos, thats why you're getting a 404.
First, you need to add the private repos to the GO_PRIVATE environment variable.
env:
GO_PRIVATE: github.com/<organization-account>/*
Then you need to configure git to use that token.
- name: Setup Git
run: git config --global url."https://${{ secrets.TOKEN }}:#github.com/".insteadOf "https://github.com"
- name: Get dependencies
run: go mod download
Add the token to the actions secrets in github.com
I don't use dep, but you should use go mod download instead of go get or dep since you have a mod file. I don't know why you're using dep AND go modules AND using go get at the same time. Weird.

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

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.

Compile GO source code (on regular intervals)

I have a GO compiler available (i.e. installed in a dir ~/xxxx/bin/go).
Suppose to call this version GO.1
I now clone the GO repository from GitHub (ex. into ~/yyyy/)
Setting $GOROOT_BOOTSTRAP=~/xxxx/ I can compile it
as cd ~/yyyy/src && ./all.bash
obtaining the GO binary in ~/yyyy/bin (Suppose to call this GO.2)
I'd like now to use this new GO.2 binary ~/yyyy/bin/go to recompile its new source (~/yyyy/src) having a final 'good' GO.3
I cannot just set $GOROOT_BOOTSTRAP to ~/yyyy/ or play with some others env vars (at least I tried many attempts with no success).
The only way I've found to do this is:
copy ~/yyyy/ (about 800MB!) onto ~/xxxx/ (so that GO.2
overwrites GO.1 and the $GOROOT_BOOTSTRAP is unchanged)
redo cd ~/yyyy/src && ./all.bash
This cannot be the expected behaviour.
I'd like to have the most up-to-date binary GO.x just with a git pull && cd ./src && ./all.bash and no much more than this.
Any hint is welcome.

Resources