Search code examples
gogoogle-cloud-platformgoogle-container-registrygovendorgoogle-container-builder

Google Cloud Container Builder - Build Docker container from Go source with vendored dependencies


Background

Related question: Google Container Builder: How to install govendor dependencies during build step?

I am trying to use Google Cloud Container Builder to automate the building of my Docker containers using Build Triggers.

My code is in Go, and I have a vendor folder (checked in to Git) in my project root which contains all of my Go dependencies.

My project has four binaries that need to be Dockerized, structured as follows:

vendor/
   ...
program1/
    program1.go
    main/
        main.go
        Dockerfile
program2/
    program2.go
    main/
        main.go
        Dockerfile
...

Each program's Dockerfile is simple:

FROM alpine
ADD main main
ENTRYPOINT ["/main"]

I have a Build Trigger set up to track my master branch. The trigger runs the following build request (cloudbuild.yaml), which makes use of the open sourced Docker build step:

steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/program1:0.1.15-$SHORT_SHA', '.']
  dir: 'program1/main'

  ... (repeated for each program)
  (images, tags omitted)

To summarize, my current build process is as follows:

  1. Edit code.
  2. Build each Go executable using go build. The executable is called main, and is saved in the programX/main/ directory, alongside main.go.
  3. Commit and push code (since the main executables are tracked by Git) to my master branch.
  4. Build Trigger makes four Docker images using the main file built in step 1.

Goal

I would like to eliminate Step 1 from my build process, so that I no longer need to compile my executables locally, and do not need to track my main executables in Git.

In sum, here is my ideal process:

  1. Edit code, commit, push to remote.
  2. Build Trigger compiles all four programs, builds all four images.
  3. Relax :)

Attempted solution

I used the open source Go build step, as follows:

cloudbuild.yaml: (updated)

steps:
- id: 'build-program1'
  name: 'gcr.io/cloud-builders/go'
  args: ['build', '-a', '-installsuffix', 'cgo', '-ldflags', '''-w''', '-o', 'main', './main.go']
  env: ['PROJECT_ROOT=/workspace', 'CGO_ENABLED=0', 'GOOS=linux']
  dir: 'program1/main'
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/program1:0.1.15-$SHORT_SHA', '.']
  dir: 'program1/main'
  waitFor: ['build-program1']

  ... (repeated for each program)
  (images, tags omitted)

After trying various combinations of setting PROJECT_ROOT and GOPATH in the env field of the build-programX, I kept getting the same error for every single package used in my project (file path varies):

cannot find package "github.com/acoshift/go-firebase-admin" in any of
Step #0 - "build-program1": /usr/local/go/src/github.com/acoshift/go-firebase-admin (from $GOROOT) 
Step #0 - "build-program1": /workspace/auth/main/gopath/src/github.com/acoshift/go-firebase-admin (from $GOPATH)

It's not even looking for a vendor directory?

What next?

My guess is that one of the following is true:

  1. I am not specifying the GOPATH/PROJECT_ROOT correctly in the build request file. But if so, what is the correct setting?
  2. My project is not structured correctly.
  3. What I am trying to do is impossible :(*
  4. I need to make a custom build step, somehow.
  5. The version of Go used is old - but how can I check this?

I can find no examples online of what I am trying to achieve, and I find GCP's documentation on this subject quite lacking.

Any help would be greatly appreciated!


Solution

  • Fixed the problem (but not exactly sure how...), thanks to these changes:

    1. Set PROJECT_ROOT to my_root, such that the code I want to compile sits at my_root/program1/main/main.go (thanks to John Asmuth for his answer: https://stackoverflow.com/a/46526875/6905609)
    2. Remove the dirs field for the Go build step
    3. Set the -o flag to ./program1/main/main, and the final build arg to ./program1/main/main.go

    Previously I was cding into the program1/main directory during the build step, and for some reason go build was looking for packages within my_root/program1/main instead of my_root. Weird!