I primarily want to use Bazel because I want to have deterministic dependencies. Vendoring in Go by default pull from head, which isn't the behavior I want - especially when working with a remote team. I initially used govendor to pull deps at a specific commit into a vendor directory in my source tree - that commit was defined in the Bazel file. This turned out to be clunky - I was pulling deps twice.
Since the App Engine Flex Environment allows you to deploy any Docker container in Google Container Registry, I decided instead to use Bazel's docker_build rule to just build my container.
This is mostly straight forward. The only catch is that I wanted to have the base layer of y container be the GAE Go base layer. There isn't currently any way to define a remote base layer in Bazel's rules. The solution here is to build a tarball using the base layer, and then use a new_http_archive rule to pull down the tarball. Here are the steps to do that:
$ gcloud docker pull gcr.io/google-appengine/golang:1.6
$ docker save gcr.io/google-appengine/golang -o aegolang1.6
$ gzip aegolang1.6.tar
I then uploaded the tarball to a Google Cloud Storage bucket. And added the following modifications to my Bazel files.
in WORKSPACE:
new_http_archive(
name = "appengine_go",
url = "https://storage.googleapis.com/path_to_tarball/base_image.tar.gz",
type = "tar.gz",
build_file = "appenginego.BUILD",
)
Notice I've defined a build_file above. So now we have to fill that in as well - so I created a new file called appenginego.BUILD with the following contents:
load("@bazel_tools//tools/build_defs/docker:docker.bzl", "docker_build")
# Extract .xz files
genrule(
name = "aego_tar",
srcs = ["aegolang1.6.tar.gz"],
outs = ["aego_tar.tar"],
cmd = "cat $< | gunzip >$@",
)
docker_build(
name = "aego",
tars = [":aego_tar"],
visibility = ["//visibility:public"],
)
And then I use that in my application build file:
load("@bazel_tools//tools/build_defs/docker:docker.bzl", "docker_build")
docker_build(
name = "aego",
base = "@appengine_go//:aego",
)
docker_build(
name = "image",
files = [":api"],
entrypoint = ["/api"],
base = ":aego",
ports = ["8080"],
repository = "gcr.io/your_repo"
)
The final steps for deployment here are to upload the new container to GCR and then deploy.
First you'll want to load your image into docker. You can do this by running the following command:
$ bazel run src/path/to/your/build/rule:image
Now issuing a "docker images" call should list your newly built image as gcr.io/your_repo/path_to_your_build.
Next, do the actual push to gcr:
$ gcloud docker push gcr.io/your_repo/path_to_your_build
Finally to deploy your awesome new container you'll run:
$ gcloud app deploy --image-url=gcr.io/your_repo/path_to_your_build:image
And that's it. This allows you to completely use Bazel for building and handling external deps without worrying about manual vendoring work. ᕕ( ᐛ )ᕗ