Migrate CI to docker buildx and other improvements (#4765)

* Migrate CI to docker buildx and other improvements

## Motivation
- Improve build times in forks. Specially when rerunning builds because of some flaky test.
- Start using `docker buildx` to pave the way for multiplatform builds.

## Performance improvements
These timings were taken for the `kind_integration.yml` workflow when we merged and rerun the lodash bump PR (#4762)

Before these improvements:
- when merging: `24:18`
- when rerunning after merge (docker cache warm): `19:00`
- when running the same changes in a fork (no docker cache): `32:15`

After these improvements:
- when merging: `25:38`
- when rerunning after merge (docker cache warm): `19:25`
- when running the same changes in a fork (docker cache warm): `19:25`

As explained below, non-forks and forks now use the same cache, so the important take is that forks will always start with a warm cache and we'll no longer see long build times like the `32:15` above.
The downside is a slight increase in the build times for non-forks (up to a little more than a minute, depending on the case).

## Build containers in parallel
The `docker_build` job in the `kind_integration.yml`, `cloud_integration.yml` and `release.yml` workflows relied on running `bin/docker-build` which builds all the containers in sequence. Now each container is built in parallel using a matrix strategy.

## New caching strategy
CI now uses `docker buildx` for building the container images, which allows using an external cache source for builds, a location in the filesystem in this case. That location gets cached using actions/cache, using the key `{{ runner.os }}-buildx-${{ matrix.target }}-${{ env.TAG }}` and the restore key `${{ runner.os }}-buildx-${{ matrix.target }}-`.

For example when building the `web` container, its image and all the intermediary layers get cached under the key `Linux-buildx-web-git-abc0123`. When that has been cached in the `main` branch, that cache will be available to all the child branches, including forks. If a new branch in a fork asks for a key like `Linux-buildx-web-git-def456`, the key won't be found during the first CI run, but the system falls back to the key `Linux-buildx-web-git-abc0123` from `main` and so the build will start with a warm cache (more info about how keys are matched in the [actions/cache docs](https://docs.github.com/en/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key)).

## Packet host no longer needed
To benefit from the warm caches both in non-forks and forks like just explained, we're required to ditch doing the builds in Packet and now everything runs in the github runners VMs.
As a result there's no longer separate logic for non-forks and forks in the workflow files; `kind_integration.yml` was greatly simplified but `cloud_integration.yml` and `release.yml` got a little bigger in order to use the actions artifacts as a repository for the images built. This bloat will be fixed when support for [composite actions](https://github.com/actions/runner/blob/users/ethanchewy/compositeADR/docs/adrs/0549-composite-run-steps.md) lands in github.

## Local builds
You still are able to run `bin/docker-build` or any of the `docker-build.*` scripts. And to make use of buildx, run those same scripts after having set the env var `DOCKER_BUILDKIT=1`. Using buildx supposes you have installed it, as instructed [here](https://github.com/docker/buildx).

## Other
- A new script `bin/docker-cache-prune` is used to remove unused images from the cache. Without that the cache grows constantly and we can rapidly hit the 5GB limit (when the limit is attained the oldest entries get evicted).
- The `go-deps` dockerfile base image was changed from `golang:1.14.2` (ubuntu based) to `golang-1:14.2-alpine` also to conserve cache space.

# Addressed separately in #4875:

Got rid of the `go-deps` image and instead added something similar on top of all the Dockerfiles dealing with `go`, as a first stage for those Dockerfiles. That continues to serve as a way to pre-populate go's build cache, which speeds up the builds in the subsequent stages. That build should in theory be rebuilt automatically only when `go.mod` or `go.sum` change, and now we don't require running `bin/update-go-deps-shas`. That script was removed along with all the logic elsewhere that used it, including the `go_dependencies` job in the `static_checks.yml` github workflow.

The list of modules preinstalled was moved from `Dockerfile-go-deps` to a new script `bin/install-deps`. I couldn't find a way to generate that list dynamically, so whenever a slow-to-compile dependency is found, we have to make sure it's included in that list.

Although this simplifies the dev workflow, note that the real motivation behind this was a limitation in buildx's `docker-container` driver that forbids us from depending on images that haven't been pushed to a registry, so we have to resort to building the dependencies as a first stage in the Dockerfiles.
This commit is contained in:
Alejandro Pedraza 2020-07-22 14:27:45 -05:00 committed by GitHub
parent 46d22f8b04
commit 5e789ba152
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 237 additions and 395 deletions

View File

@ -7,6 +7,7 @@
**/node_modules **/node_modules
bin bin
!bin/fetch-proxy !bin/fetch-proxy
!bin/install-deps
!bin/web !bin/web
**/Dockerfile* **/Dockerfile*
Dockerfile* Dockerfile*

View File

@ -8,35 +8,14 @@ on:
- main - main
env: env:
GH_ANNOTATION: true GH_ANNOTATION: true
DOCKER_BUILDKIT: 1
jobs: jobs:
# todo: Keep in sync with `release.yml`
docker_build: docker_build:
name: Docker build
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
steps: strategy:
- name: Checkout code matrix:
# actions/checkout@v2 target: [proxy, controller, web, cni-plugin, debug, cli-bin, grafana]
uses: actions/checkout@722adc6 name: Docker build (${{ matrix.target }})
- name: Setup SSH config for Packet
run: |
mkdir -p ~/.ssh/
touch ~/.ssh/id && chmod 600 ~/.ssh/id
echo "${{ secrets.DOCKER_SSH_CONFIG }}" > ~/.ssh/config
echo "${{ secrets.DOCKER_PRIVATE_KEY }}" > ~/.ssh/id
echo "${{ secrets.DOCKER_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
ssh linkerd-docker docker version
- name: Build docker images
env:
DOCKER_HOST: ssh://linkerd-docker
DOCKER_TRACE: 1
run: |
export PATH="`pwd`/bin:$PATH"
bin/docker-build
# todo: Keep in sync with `release.yml`
docker_push:
name: Docker push
runs-on: ubuntu-18.04
needs: [docker_build]
steps: steps:
- name: Checkout code - name: Checkout code
# actions/checkout@v2 # actions/checkout@v2
@ -45,6 +24,24 @@ jobs:
run: | run: |
. bin/_tag.sh . bin/_tag.sh
echo ::set-env name=TAG::$(CI_FORCE_CLEAN=1 bin/root-tag) echo ::set-env name=TAG::$(CI_FORCE_CLEAN=1 bin/root-tag)
. bin/_docker.sh
echo ::set-env name=DOCKER_REGISTRY::$DOCKER_REGISTRY
echo ::set-env name=DOCKER_BUILDKIT_CACHE::${{ runner.temp }}/.buildx-cache
- name: Cache docker layers
# actions/cache@v2.0.0
uses: actions/cache@b820478
with:
path: ${{ env.DOCKER_BUILDKIT_CACHE }}
key: ${{ runner.os }}-buildx-${{ matrix.target }}-${{ env.TAG }}
restore-keys: |
${{ runner.os }}-buildx-${{ matrix.target }}-
- name: Build docker images
env:
DOCKER_TRACE: 1
run: |
docker buildx create --driver docker-container --use
bin/docker-build-${{ matrix.target }}
- name: Configure gcloud - name: Configure gcloud
# linkerd/linkerd2-action-gcloud@v1.0.1 # linkerd/linkerd2-action-gcloud@v1.0.1
uses: linkerd/linkerd2-action-gcloud@308c4df uses: linkerd/linkerd2-action-gcloud@308c4df
@ -52,28 +49,21 @@ jobs:
cloud_sdk_service_account_key: ${{ secrets.CLOUD_SDK_SERVICE_ACCOUNT_KEY }} cloud_sdk_service_account_key: ${{ secrets.CLOUD_SDK_SERVICE_ACCOUNT_KEY }}
gcp_project: ${{ secrets.GCP_PROJECT }} gcp_project: ${{ secrets.GCP_PROJECT }}
gcp_zone: ${{ secrets.GCP_ZONE }} gcp_zone: ${{ secrets.GCP_ZONE }}
- name: Docker SSH setup
run: |
mkdir -p ~/.ssh/
touch ~/.ssh/id && chmod 600 ~/.ssh/id
echo "${{ secrets.DOCKER_SSH_CONFIG }}" > ~/.ssh/config
echo "${{ secrets.DOCKER_PRIVATE_KEY }}" > ~/.ssh/id
echo "${{ secrets.DOCKER_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
ssh linkerd-docker docker version
- name: Push docker images to registry - name: Push docker images to registry
env:
DOCKER_HOST: ssh://linkerd-docker
run: | run: |
export PATH="`pwd`/bin:$PATH" . bin/_docker.sh
bin/docker-push-deps docker_push "${{ matrix.target }}" "$TAG"
bin/docker-push $TAG docker_retag "${{ matrix.target }}" "$TAG" main
bin/docker-retag-all $TAG main docker_push "${{ matrix.target }}" main
bin/docker-push main - name: Prune docker layers cache
# changes generate new images while the existing ones don't get removed
# so we manually do that to avoid bloating the cache
run: bin/docker-cache-prune
# todo: Keep in sync with `release.yml` # todo: Keep in sync with `release.yml`
cloud_integration_tests: cloud_integration_tests:
name: Cloud integration tests name: Cloud integration tests
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
needs: [docker_push] needs: [docker_build]
steps: steps:
- name: Checkout code - name: Checkout code
# actions/checkout@v2 # actions/checkout@v2
@ -90,14 +80,14 @@ jobs:
id: install_cli id: install_cli
run: | run: |
TAG="$(CI_FORCE_CLEAN=1 bin/root-tag)" TAG="$(CI_FORCE_CLEAN=1 bin/root-tag)"
image="gcr.io/linkerd-io/cli-bin:$TAG" CMD="$PWD/target/release/linkerd2-cli-$TAG-linux"
id=$(bin/docker create $image) bin/docker-pull-binaries $TAG
bin/docker cp "$id:/out/linkerd-linux" "$HOME/.linkerd" $CMD version --client
"$HOME/.linkerd" version --client
# validate CLI version matches the repo # validate CLI version matches the repo
[[ "$TAG" == "$($HOME/.linkerd version --short --client)" ]] [[ "$TAG" == "$($CMD version --short --client)" ]]
echo "Installed Linkerd CLI version: $TAG" echo "Installed Linkerd CLI version: $TAG"
echo "::set-output name=tag::$TAG" echo "::set-env name=CMD::$CMD"
echo "::set-env name=TAG::$TAG"
- name: Create GKE cluster - name: Create GKE cluster
# linkerd/linkerd2-action-gcloud@v1.0.1 # linkerd/linkerd2-action-gcloud@v1.0.1
uses: linkerd/linkerd2-action-gcloud@308c4df uses: linkerd/linkerd2-action-gcloud@308c4df
@ -107,17 +97,15 @@ jobs:
gcp_zone: ${{ secrets.GCP_ZONE }} gcp_zone: ${{ secrets.GCP_ZONE }}
preemptible: false preemptible: false
create: true create: true
name: testing-${{ steps.install_cli.outputs.tag }}-${{ github.run_id }} name: testing-${{ env.TAG }}-${{ github.run_id }}
num_nodes: 2 num_nodes: 2
- name: Run integration tests - name: Run integration tests
env: env:
GITCOOKIE_SH: ${{ secrets.GITCOOKIE_SH }} GITCOOKIE_SH: ${{ secrets.GITCOOKIE_SH }}
run: | run: |
export PATH="`pwd`/bin:$PATH"
echo "$GITCOOKIE_SH" | bash echo "$GITCOOKIE_SH" | bash
version="$($HOME/.linkerd version --client --short | tr -cd '[:alnum:]-')" bin/tests --skip-kind-create "$CMD"
bin/tests --skip-kind-create "$HOME/.linkerd"
- name: CNI tests - name: CNI tests
run: | run: |
export TAG="$($HOME/.linkerd version --client --short)" export TAG="$($CMD version --client --short)"
go test -cover -race -v -mod=readonly ./cni-plugin/test -integration-tests go test -cover -race -v -mod=readonly ./cni-plugin/test -integration-tests

View File

@ -9,10 +9,14 @@ on:
- main - main
env: env:
GH_ANNOTATION: true GH_ANNOTATION: true
DOCKER_BUILDKIT: 1
jobs: jobs:
docker_build: docker_build:
name: Docker build
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
strategy:
matrix:
target: [proxy, controller, web, cni-plugin, debug, cli-bin, grafana]
name: Docker build (${{ matrix.target }})
steps: steps:
- name: Checkout code - name: Checkout code
# actions/checkout@v2 # actions/checkout@v2
@ -24,39 +28,35 @@ jobs:
. bin/_docker.sh . bin/_docker.sh
echo ::set-env name=DOCKER_REGISTRY::$DOCKER_REGISTRY echo ::set-env name=DOCKER_REGISTRY::$DOCKER_REGISTRY
- name: Setup SSH config for Packet echo ::set-env name=DOCKER_BUILDKIT_CACHE::${{ runner.temp }}/.buildx-cache
if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork - name: Cache docker layers
run: | # actions/cache@v2.0.0
mkdir -p ~/.ssh/ uses: actions/cache@b820478
touch ~/.ssh/id && chmod 600 ~/.ssh/id with:
echo "${{ secrets.DOCKER_SSH_CONFIG }}" > ~/.ssh/config path: ${{ env.DOCKER_BUILDKIT_CACHE }}
echo "${{ secrets.DOCKER_PRIVATE_KEY }}" > ~/.ssh/id key: ${{ runner.os }}-buildx-${{ matrix.target }}-${{ env.TAG }}
echo "${{ secrets.DOCKER_KNOWN_HOSTS }}" > ~/.ssh/known_hosts restore-keys: |
ssh linkerd-docker docker version ${{ runner.os }}-buildx-${{ matrix.target }}-
echo ::set-env name=DOCKER_HOST::ssh://linkerd-docker
- name: Build docker images - name: Build docker images
env: env:
DOCKER_TRACE: 1 DOCKER_TRACE: 1
run: | run: |
export PATH="`pwd`/bin:$PATH" docker buildx create --driver docker-container --use
bin/docker-build bin/docker-build-${{ matrix.target }}
- name: Prune docker layers cache
# changes generate new images while the existing ones don't get removed
# so we manually do that to avoid bloating the cache
run: bin/docker-cache-prune
- name: Create artifact with CLI and image archives - name: Create artifact with CLI and image archives
env: env:
ARCHIVES: /home/runner/archives ARCHIVES: /home/runner/archives
run: | run: |
mkdir -p $ARCHIVES mkdir -p $ARCHIVES
docker save "$DOCKER_REGISTRY/${{ matrix.target }}:$TAG" > $ARCHIVES/${{ matrix.target }}.tar
for image in proxy controller web cni-plugin debug cli-bin grafana; do
docker save "$DOCKER_REGISTRY/$image:$TAG" > $ARCHIVES/$image.tar || tee save_fail &
done
# save windows cli into artifacts # save windows cli into artifacts
cp -r ./target/cli/windows/linkerd $ARCHIVES/linkerd-windows.exe if [ '${{ matrix.target }}' == 'cli-bin' ]; then
cp -r ./target/cli/windows/linkerd $ARCHIVES/linkerd-windows.exe
# Wait for `docker save` background processes to complete. Exit early fi
# if any job failed.
wait < <(jobs -p)
test -f save_fail && exit 1 || true
# `with.path` values do not support environment variables yet, so an # `with.path` values do not support environment variables yet, so an
# absolute path is used here. # absolute path is used here.
# #
@ -92,7 +92,6 @@ jobs:
- name: Run CLI Integration tests - name: Run CLI Integration tests
run: | run: |
go test --failfast --mod=readonly ".\test\cli" --linkerd=$PWD\image-archives\linkerd-windows.exe --cli-tests -v go test --failfast --mod=readonly ".\test\cli" --linkerd=$PWD\image-archives\linkerd-windows.exe --cli-tests -v
# todo: Keep in sync with `release.yml`
kind_integration_tests: kind_integration_tests:
strategy: strategy:
matrix: matrix:
@ -127,31 +126,12 @@ jobs:
. bin/_docker.sh . bin/_docker.sh
echo ::set-env name=DOCKER_REGISTRY::$DOCKER_REGISTRY echo ::set-env name=DOCKER_REGISTRY::$DOCKER_REGISTRY
- name: Setup SSH config for Packet - name: Download image archives
if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork
run: |
mkdir -p ~/.ssh/
touch ~/.ssh/id && chmod 600 ~/.ssh/id
echo "${{ secrets.DOCKER_SSH_CONFIG }}" > ~/.ssh/config
echo "${{ secrets.DOCKER_PRIVATE_KEY }}" > ~/.ssh/id
echo "${{ secrets.DOCKER_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
- name: Download image archives (Forked repositories)
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork
# actions/download-artifact@v1 # actions/download-artifact@v1
uses: actions/download-artifact@18f0f59 uses: actions/download-artifact@18f0f59
with: with:
name: image-archives name: image-archives
- name: Load cli-bin image into local docker images - name: Load cli-bin image into local docker images
if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork
run: |
# `docker load` only accepts input from STDIN, so pipe the image
# archive into the command.
#
# In order to pipe the image archive, set `DOCKER_HOST` for a single
# command and `docker save` the CLI image from the Packet host.
DOCKER_HOST=ssh://linkerd-docker docker save "$DOCKER_REGISTRY/cli-bin:$TAG" | docker load
- name: Load cli-bin image into local docker images (Forked repositories)
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork
run: docker load < image-archives/cli-bin.tar run: docker load < image-archives/cli-bin.tar
- name: Install CLI - name: Install CLI
run: | run: |
@ -162,10 +142,5 @@ jobs:
# Validate the CLI version matches the current build tag. # Validate the CLI version matches the current build tag.
[[ "$TAG" == "$($HOME/.linkerd version --short --client)" ]] [[ "$TAG" == "$($HOME/.linkerd version --short --client)" ]]
- name: Run integration tests - name: Run integration tests
if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork
run: |
bin/tests --images --images-host ssh://linkerd-docker --name ${{ matrix.integration_test }} "$HOME/.linkerd"
- name: Run integration tests (Forked repositories)
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork
run: | run: |
bin/tests --images --name ${{ matrix.integration_test }} "$HOME/.linkerd" bin/tests --images --name ${{ matrix.integration_test }} "$HOME/.linkerd"

View File

@ -1,10 +1,6 @@
# #
# This file is a mostly a concatenation of `kind_integration.yml` and # This file is a mostly a concatenation of `kind_integration.yml` and
# `cloud_integration.yml`, specifically for release. Once GitHub Actions # `cloud_integration.yml`, specifically for release.
# supports YAML anchors, we should be able to share most of the content
# between these files:
# https://github.community/t5/GitHub-Actions/Support-for-YAML-anchors/m-p/30336
#
name: Release name: Release
on: on:
@ -13,53 +9,14 @@ on:
- "*" - "*"
env: env:
GH_ANNOTATION: true GH_ANNOTATION: true
DOCKER_BUILDKIT: 1
jobs: jobs:
# todo: Keep in sync with `cloud_integration.yml`
docker_build: docker_build:
name: Docker build
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
steps: strategy:
- name: Checkout code matrix:
# actions/checkout@v2 target: [proxy, controller, web, cni-plugin, debug, cli-bin, grafana]
uses: actions/checkout@722adc6 name: Docker build (${{ matrix.target }})
- name: Setup SSH config for Packet
run: |
mkdir -p ~/.ssh/
touch ~/.ssh/id && chmod 600 ~/.ssh/id
echo "${{ secrets.DOCKER_SSH_CONFIG }}" > ~/.ssh/config
echo "${{ secrets.DOCKER_PRIVATE_KEY }}" > ~/.ssh/id
echo "${{ secrets.DOCKER_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
ssh linkerd-docker docker version
- name: Build docker images
env:
DOCKER_HOST: ssh://linkerd-docker
DOCKER_TRACE: 1
run: |
export PATH="`pwd`/bin:$PATH"
bin/docker-build
- name: Create artifact with Windows CLI binary
env:
ARCHIVES: /home/runner/archives
run: |
mkdir -p $ARCHIVES
# save windows cli into artifacts
cp -r ./target/cli/windows/linkerd $ARCHIVES/linkerd-windows.exe
# `with.path` values do not support environment variables yet, so an
# absolute path is used here.
#
# https://github.com/actions/upload-artifact/issues/8
- name: Upload artifact
# actions/upload-artifact@v1
uses: actions/upload-artifact@3446296
with:
name: image-archives
path: /home/runner/archives
# todo: Keep in sync with `cloud_integration.yml`
docker_push:
name: Docker push
runs-on: ubuntu-18.04
needs: [docker_build]
steps: steps:
- name: Checkout code - name: Checkout code
# actions/checkout@v2 # actions/checkout@v2
@ -68,6 +25,48 @@ jobs:
run: | run: |
. bin/_tag.sh . bin/_tag.sh
echo ::set-env name=TAG::$(CI_FORCE_CLEAN=1 bin/root-tag) echo ::set-env name=TAG::$(CI_FORCE_CLEAN=1 bin/root-tag)
. bin/_docker.sh
echo ::set-env name=DOCKER_REGISTRY::$DOCKER_REGISTRY
echo ::set-env name=DOCKER_BUILDKIT_CACHE::${{ runner.temp }}/.buildx-cache
- name: Cache docker layers
# actions/cache@v2.0.0
uses: actions/cache@b820478
with:
path: ${{ env.DOCKER_BUILDKIT_CACHE }}
key: ${{ runner.os }}-buildx-${{ matrix.target }}-${{ env.TAG }}
restore-keys: |
${{ runner.os }}-buildx-${{ matrix.target }}-
- name: Build docker images
env:
DOCKER_TRACE: 1
run: |
docker buildx create --driver docker-container --use
bin/docker-build-${{ matrix.target }}
- name: Prune docker layers cache
# changes generate new images while the existing ones don't get removed
# so we manually do that to avoid bloating the cache
run: bin/docker-cache-prune
- name: Create artifact with CLI
# windows_static_cli_tests below needs this because it can't create linux containers
# inside windows
if: matrix.target == 'cli-bin'
env:
ARCHIVES: /home/runner/archives
run: |
mkdir -p $ARCHIVES
cp -r ./target/cli/windows/linkerd $ARCHIVES/linkerd-windows.exe
# `with.path` values do not support environment variables yet, so an
# absolute path is used here.
#
# https://github.com/actions/upload-artifact/issues/8
- name: Upload artifact
if: matrix.target == 'cli-bin'
# actions/upload-artifact@v1
uses: actions/upload-artifact@3446296
with:
name: image-archives
path: /home/runner/archives
- name: Configure gcloud - name: Configure gcloud
# linkerd/linkerd2-action-gcloud@v1.0.1 # linkerd/linkerd2-action-gcloud@v1.0.1
uses: linkerd/linkerd2-action-gcloud@308c4df uses: linkerd/linkerd2-action-gcloud@308c4df
@ -75,23 +74,12 @@ jobs:
cloud_sdk_service_account_key: ${{ secrets.CLOUD_SDK_SERVICE_ACCOUNT_KEY }} cloud_sdk_service_account_key: ${{ secrets.CLOUD_SDK_SERVICE_ACCOUNT_KEY }}
gcp_project: ${{ secrets.GCP_PROJECT }} gcp_project: ${{ secrets.GCP_PROJECT }}
gcp_zone: ${{ secrets.GCP_ZONE }} gcp_zone: ${{ secrets.GCP_ZONE }}
- name: Docker SSH setup
run: |
mkdir -p ~/.ssh/
touch ~/.ssh/id && chmod 600 ~/.ssh/id
echo "${{ secrets.DOCKER_SSH_CONFIG }}" > ~/.ssh/config
echo "${{ secrets.DOCKER_PRIVATE_KEY }}" > ~/.ssh/id
echo "${{ secrets.DOCKER_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
ssh linkerd-docker docker version
- name: Push docker images to registry - name: Push docker images to registry
env:
DOCKER_HOST: ssh://linkerd-docker
run: | run: |
export PATH="`pwd`/bin:$PATH" . bin/_docker.sh
bin/docker-push-deps docker_push "${{ matrix.target }}" "$TAG"
bin/docker-push $TAG docker_retag "${{ matrix.target }}" "$TAG" main
bin/docker-retag-all $TAG main docker_push "${{ matrix.target }}" main
bin/docker-push main
# todo: Keep in sync with `kind_integration.yml` # todo: Keep in sync with `kind_integration.yml`
windows_static_cli_tests: windows_static_cli_tests:
name: Static CLI tests (windows) name: Static CLI tests (windows)
@ -117,7 +105,6 @@ jobs:
- name: Run CLI Integration tests - name: Run CLI Integration tests
run: | run: |
go test --failfast --mod=readonly ".\test\cli" --linkerd=$PWD\image-archives\linkerd-windows.exe --cli-tests -v go test --failfast --mod=readonly ".\test\cli" --linkerd=$PWD\image-archives\linkerd-windows.exe --cli-tests -v
# todo: Keep in sync with `kind_integration.yml`
kind_integration_tests: kind_integration_tests:
strategy: strategy:
matrix: matrix:
@ -147,41 +134,21 @@ jobs:
${{ runner.os }}-go- ${{ runner.os }}-go-
- name: Set environment variables from scripts - name: Set environment variables from scripts
run: | run: |
. bin/_tag.sh TAG="$(CI_FORCE_CLEAN=1 bin/root-tag)"
echo ::set-env name=TAG::$(CI_FORCE_CLEAN=1 bin/root-tag) CMD="$PWD/target/release/linkerd2-cli-$TAG-linux"
echo "::set-env name=CMD::$CMD"
. bin/_docker.sh echo "::set-env name=TAG::$TAG"
echo ::set-env name=DOCKER_REGISTRY::$DOCKER_REGISTRY
- name: Setup SSH config for Packet
run: |
mkdir -p ~/.ssh/
touch ~/.ssh/id && chmod 600 ~/.ssh/id
echo "${{ secrets.DOCKER_SSH_CONFIG }}" > ~/.ssh/config
echo "${{ secrets.DOCKER_PRIVATE_KEY }}" > ~/.ssh/id
echo "${{ secrets.DOCKER_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
- name: Load cli-bin image into local docker images
run: |
# `docker load` only accepts input from STDIN, so pipe the image
# archive into the command.
#
# In order to pipe the image archive, set `DOCKER_HOST` for a single
# command and `docker save` the CLI image from the Packet host.
DOCKER_HOST=ssh://linkerd-docker docker save "$DOCKER_REGISTRY/cli-bin:$TAG" | docker load
- name: Install CLI
run: |
# Copy the CLI out of the local cli-bin container.
container_id=$(docker create "$DOCKER_REGISTRY/cli-bin:$TAG")
docker cp $container_id:/out/linkerd-linux $HOME/.linkerd
# Validate the CLI version matches the current build tag.
[[ "$TAG" == "$($HOME/.linkerd version --short --client)" ]]
- name: Run integration tests - name: Run integration tests
run: bin/tests --images --images-host ssh://linkerd-docker --name ${{ matrix.integration_test }} "$HOME/.linkerd" run: |
bin/docker-pull-binaries $TAG
# Validate the CLI version matches the current build tag.
[[ "$TAG" == "$($CMD version --short --client)" ]]
bin/tests --images --name ${{ matrix.integration_test }} "$CMD"
# todo: Keep in sync with `cloud_integration.yml` # todo: Keep in sync with `cloud_integration.yml`
cloud_integration_tests: cloud_integration_tests:
name: Cloud integration tests name: Cloud integration tests
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
needs: [docker_push] needs: [docker_build]
steps: steps:
- name: Checkout code - name: Checkout code
# actions/checkout@v2 # actions/checkout@v2
@ -198,13 +165,13 @@ jobs:
id: install_cli id: install_cli
run: | run: |
TAG="$(CI_FORCE_CLEAN=1 bin/root-tag)" TAG="$(CI_FORCE_CLEAN=1 bin/root-tag)"
image="gcr.io/linkerd-io/cli-bin:$TAG" CMD="$PWD/target/release/linkerd2-cli-$TAG-linux"
id=$(bin/docker create $image) bin/docker-pull-binaries $TAG
bin/docker cp "$id:/out/linkerd-linux" "$HOME/.linkerd" $CMD version --client
$HOME/.linkerd version --client
# validate CLI version matches the repo # validate CLI version matches the repo
[[ "$TAG" == "$($HOME/.linkerd version --short --client)" ]] [[ "$TAG" == "$($CMD version --short --client)" ]]
echo "Installed Linkerd CLI version: $TAG" echo "Installed Linkerd CLI version: $TAG"
echo "::set-env name=CMD::$CMD"
echo "::set-output name=tag::$TAG" echo "::set-output name=tag::$TAG"
- name: Create GKE cluster - name: Create GKE cluster
# linkerd/linkerd2-action-gcloud@v1.0.1 # linkerd/linkerd2-action-gcloud@v1.0.1
@ -221,13 +188,11 @@ jobs:
env: env:
GITCOOKIE_SH: ${{ secrets.GITCOOKIE_SH }} GITCOOKIE_SH: ${{ secrets.GITCOOKIE_SH }}
run: | run: |
export PATH="`pwd`/bin:$PATH"
echo "$GITCOOKIE_SH" | bash echo "$GITCOOKIE_SH" | bash
version="$($HOME/.linkerd version --client --short | tr -cd '[:alnum:]-')" bin/tests --skip-kind-create "$CMD"
bin/tests --skip-kind-create "$HOME/.linkerd"
- name: CNI tests - name: CNI tests
run: | run: |
export TAG="$($HOME/.linkerd version --client --short)" export TAG="$($CMD version --client --short)"
go test -cover -race -v -mod=readonly ./cni-plugin/test -integration-tests go test -cover -race -v -mod=readonly ./cni-plugin/test -integration-tests
choco_pack: choco_pack:
# only runs for stable tags. The conditionals are at each step level instead of the job level # only runs for stable tags. The conditionals are at each step level instead of the job level

View File

@ -8,29 +8,6 @@ on:
branches: branches:
- main - main
jobs: jobs:
go_dependencies:
name: Go dependencies
runs-on: ubuntu-18.04
steps:
- name: Checkout code
# actions/checkout@v2
uses: actions/checkout@722adc6
- name: Dump env
run: env | sort
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Dump job context
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: echo "$JOB_CONTEXT"
- name: Validate go deps
run: |
. bin/_tag.sh
for f in $( grep -lR --include=Dockerfile\* go-deps: . ) ; do
validate_go_deps_tag $f
done
go_lint: go_lint:
name: Go lint name: Go lint
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04

View File

@ -19,7 +19,6 @@ about testing from source can be found in the [TEST.md](TEST.md) guide.
- [Rust](#rust) - [Rust](#rust)
- [Dependencies](#dependencies) - [Dependencies](#dependencies)
- [Updating protobuf dependencies](#updating-protobuf-dependencies) - [Updating protobuf dependencies](#updating-protobuf-dependencies)
- [Updating Docker dependencies](#updating-docker-dependencies)
- [Updating ServiceProfile generated - [Updating ServiceProfile generated
code](#updating-serviceprofile-generated-code) code](#updating-serviceprofile-generated-code)
- [Helm Chart](#helm-chart) - [Helm Chart](#helm-chart)
@ -365,22 +364,6 @@ DOCKER_TRACE=1 bin/docker-build-proxy
bin/protoc-go.sh bin/protoc-go.sh
``` ```
### Updating Docker dependencies
The Go Docker images rely on base dependency images with hard-coded SHA's:
`gcr.io/linkerd-io/go-deps` depends on
- [`go.mod`](go.mod)
- [`Dockerfile-go-deps`](Dockerfile-go-deps)
When Go dependencies change, run the following:
```bash
go mod tidy
bin/update-go-deps-shas
```
### Updating ServiceProfile generated code ### Updating ServiceProfile generated code
The [ServiceProfile client code](./controller/gen/client) is generated by The [ServiceProfile client code](./controller/gen/client) is generated by
@ -541,12 +524,6 @@ build_architecture
"workflow.yml" -> "docker-retag-all"; "workflow.yml" -> "docker-retag-all";
"workflow.yml" -> "lint"; "workflow.yml" -> "lint";
"update-go-deps-shas" -> "_tag.sh";
"update-go-deps-shas" -> "cli/Dockerfile-bin";
"update-go-deps-shas" -> "controller/Dockerfile";
"update-go-deps-shas" -> "grafana/Dockerfile";
"update-go-deps-shas" -> "web/Dockerfile";
"web" -> "go-run"; "web" -> "go-run";
} }
build_architecture build_architecture

View File

@ -1,5 +1,13 @@
ARG RUNTIME_IMAGE=debian:buster-20200514-slim ARG RUNTIME_IMAGE=debian:buster-20200514-slim
# Precompile key slow-to-build dependencies
FROM golang:1.14.2-alpine as go-deps
WORKDIR /linkerd-build
COPY go.mod go.sum ./
COPY bin/install-deps bin/
RUN go mod download
RUN ./bin/install-deps
FROM debian:buster-20200514-slim as fetch FROM debian:buster-20200514-slim as fetch
RUN apt-get update && apt-get install -y ca-certificates curl jq RUN apt-get update && apt-get install -y ca-certificates curl jq
WORKDIR /build WORKDIR /build
@ -9,7 +17,7 @@ RUN (proxy=$(bin/fetch-proxy $(cat proxy-version)) && \
mv "$proxy" linkerd2-proxy) mv "$proxy" linkerd2-proxy)
## compile proxy-identity agent ## compile proxy-identity agent
FROM gcr.io/linkerd-io/go-deps:61149d15 as golang FROM go-deps as golang
WORKDIR /linkerd-build WORKDIR /linkerd-build
COPY pkg/flags pkg/flags COPY pkg/flags pkg/flags
COPY pkg/tls pkg/tls COPY pkg/tls pkg/tls

View File

@ -14,6 +14,12 @@ export DOCKER_REGISTRY=${DOCKER_REGISTRY:-gcr.io/linkerd-io}
# When set, causes docker's build output to be emitted to stderr. # When set, causes docker's build output to be emitted to stderr.
export DOCKER_TRACE=${DOCKER_TRACE:-} export DOCKER_TRACE=${DOCKER_TRACE:-}
# When set, use `docker buildx` and use the github actions cache to store/retrieve images
export DOCKER_BUILDKIT=${DOCKER_BUILDKIT:-}
# buildx cache directory. Needed if DOCKER_BUILDKIT is used
export DOCKER_BUILDKIT_CACHE=${DOCKER_BUILDKIT_CACHE:-}
docker_repo() { docker_repo() {
repo=$1 repo=$1
@ -42,12 +48,27 @@ docker_build() {
rootdir=$( cd "$bindir"/.. && pwd ) rootdir=$( cd "$bindir"/.. && pwd )
log_debug " :; docker build $rootdir -t $repo:$tag -f $file $*" if [ -n "$DOCKER_BUILDKIT" ]; then
docker build "$rootdir" \ cache_params=""
-t "$repo:$tag" \ if [ -n "$DOCKER_BUILDKIT_CACHE" ]; then
-f "$file" \ cache_params="--cache-from type=local,src=${DOCKER_BUILDKIT_CACHE} --cache-to type=local,dest=${DOCKER_BUILDKIT_CACHE},mode=max"
"$@" \ fi
> "$output" log_debug " :; docker buildx $rootdir $cache_params --load -t $repo:$tag -f $file $*"
# shellcheck disable=SC2086
docker buildx build "$rootdir" $cache_params \
--load \
-t "$repo:$tag" \
-f "$file" \
"$@" \
> "$output"
else
log_debug " :; docker build $rootdir -t $repo:$tag -f $file $*"
docker build "$rootdir" \
-t "$repo:$tag" \
-f "$file" \
"$@" \
> "$output"
fi
echo "$repo:$tag" echo "$repo:$tag"
} }

View File

@ -6,12 +6,6 @@ git_sha_head() {
git rev-parse --short=8 HEAD git rev-parse --short=8 HEAD
} }
go_deps_sha() {
bindir=$( cd "${BASH_SOURCE[0]%/*}" && pwd )
rootdir=$( cd "$bindir"/.. && pwd )
cat "$rootdir/go.mod" "$rootdir/Dockerfile-go-deps" | shasum - | awk '{print $1}' |cut -c 1-8
}
clean_head() { clean_head() {
[ -n "${CI_FORCE_CLEAN:-}" ] || git diff-index --quiet HEAD -- [ -n "${CI_FORCE_CLEAN:-}" ] || git diff-index --quiet HEAD --
} }
@ -43,33 +37,3 @@ clean_head_root_tag() {
exit 3 exit 3
fi fi
} }
validate_tag() {
file=$1
shift
image=$1
shift
sha=$1
shift
dockerfile_tag=$(grep -oe "$image"':[^ ]*' "$file") || true
deps_tag="$image:$sha"
if [ -n "$dockerfile_tag" ] && [ "$dockerfile_tag" != "$deps_tag" ]; then
echo "Tag in \"$file\" does not match source tree:
$dockerfile_tag (\"$file\")
$deps_tag (source)"
return 3
fi
}
# This function should be called by any docker-build-* script that relies on Go
# dependencies. To confirm the set of scripts that should call this function,
# run:
# $ grep -ER 'docker-build-go-deps' .
validate_go_deps_tag() {
file=$1
validate_tag "$file" gcr.io/linkerd-io/go-deps "$(go_deps_sha)"
}

View File

@ -16,13 +16,6 @@ rootdir=$( cd "$bindir"/.. && pwd )
. "$bindir"/_tag.sh . "$bindir"/_tag.sh
dockerfile=$rootdir/cli/Dockerfile-bin dockerfile=$rootdir/cli/Dockerfile-bin
validate_go_deps_tag "$dockerfile"
(
"$bindir"/docker-build-go-deps
) >/dev/null
tag=$(head_root_tag) tag=$(head_root_tag)
docker_build cli-bin "$tag" "$dockerfile" --build-arg LINKERD_VERSION="$tag" docker_build cli-bin "$tag" "$dockerfile" --build-arg LINKERD_VERSION="$tag"
IMG=$(docker_repo cli-bin):$tag IMG=$(docker_repo cli-bin):$tag

View File

@ -17,11 +17,8 @@ rootdir=$( cd "$bindir"/.. && pwd )
dockerfile=$rootdir/cni-plugin/Dockerfile dockerfile=$rootdir/cni-plugin/Dockerfile
validate_go_deps_tag "$dockerfile"
( (
"$bindir"/docker-build-base "$bindir"/docker-build-base
"$bindir"/docker-build-go-deps
) >/dev/null ) >/dev/null
docker_build cni-plugin "$(head_root_tag)" "$dockerfile" docker_build cni-plugin "$(head_root_tag)" "$dockerfile"

View File

@ -16,12 +16,5 @@ rootdir=$( cd "$bindir"/.. && pwd )
. "$bindir"/_tag.sh . "$bindir"/_tag.sh
dockerfile=$rootdir/controller/Dockerfile dockerfile=$rootdir/controller/Dockerfile
validate_go_deps_tag "$dockerfile"
(
"$bindir"/docker-build-go-deps
) >/dev/null
tag=$(head_root_tag) tag=$(head_root_tag)
docker_build controller "$tag" "$dockerfile" --build-arg LINKERD_VERSION="$tag" docker_build controller "$tag" "$dockerfile" --build-arg LINKERD_VERSION="$tag"

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
# Builds (or pulls) our go-deps docker image.
set -eu
if [ $# -ne 0 ]; then
echo "no arguments allowed for ${0##*/}, given: $*" >&2
exit 64
fi
bindir=$( cd "${BASH_SOURCE[0]%/*}" && pwd )
# shellcheck source=_docker.sh
. "$bindir"/_docker.sh
# shellcheck source=_tag.sh
. "$bindir"/_tag.sh
tag=$(go_deps_sha)
if (docker_pull go-deps "$tag"); then
echo "$(docker_repo go-deps):$tag"
else
rootdir=$( cd "$bindir"/.. && pwd )
docker_build go-deps "$tag" "$rootdir/Dockerfile-go-deps"
fi

View File

@ -17,12 +17,6 @@ rootdir=$( cd "$bindir"/.. && pwd )
dockerfile=$rootdir/Dockerfile-proxy dockerfile=$rootdir/Dockerfile-proxy
validate_go_deps_tag "$dockerfile"
(
"$bindir"/docker-build-go-deps
) >/dev/null
get_extra_options() { get_extra_options() {
options= options=
for var in http_proxy https_proxy no_proxy; do for var in http_proxy https_proxy no_proxy; do

View File

@ -16,12 +16,5 @@ rootdir=$( cd "$bindir"/.. && pwd )
. "$bindir"/_tag.sh . "$bindir"/_tag.sh
dockerfile=$rootdir/web/Dockerfile dockerfile=$rootdir/web/Dockerfile
validate_go_deps_tag "$dockerfile"
(
"$bindir"/docker-build-go-deps
) >/dev/null
tag=$(head_root_tag) tag=$(head_root_tag)
docker_build web "$tag" "$dockerfile" --build-arg LINKERD_VERSION="$tag" docker_build web "$tag" "$dockerfile" --build-arg LINKERD_VERSION="$tag"

21
bin/docker-cache-prune Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
# This script deletes all the files under the buildkit cache blob directory that
# are not referred to in the cache manifest file.
set -eu
export DOCKER_BUILDKIT_CACHE=${DOCKER_BUILDKIT_CACHE:-}
manifest_sha=$(jq -r .manifests[0].digest < "${DOCKER_BUILDKIT_CACHE}/index.json")
manifest=${manifest_sha#"sha256:"}
files=("$manifest")
while IFS= read -r line; do files+=("$line"); done <<< "$(jq -r '.manifests[].digest | sub("^sha256:"; "")' < "${DOCKER_BUILDKIT_CACHE}/blobs/sha256/$manifest")"
for file in "${DOCKER_BUILDKIT_CACHE}"/blobs/sha256/*; do
name=$(basename "$file")
# shellcheck disable=SC2199
if [[ ! "${files[@]}" =~ ${name} ]]; then
printf 'pruned from cache: %s\n' "$file"
rm -f "$file"
fi
done

View File

@ -21,5 +21,3 @@ tag=$(head_root_tag)
for img in cli-bin cni-plugin controller debug grafana proxy web ; do for img in cli-bin cni-plugin controller debug grafana proxy web ; do
docker_image $img "$tag" docker_image $img "$tag"
done done
docker_image go-deps "$(go_deps_sha)"

View File

@ -6,8 +6,5 @@ bindir=$( cd "${BASH_SOURCE[0]%/*}" && pwd )
# shellcheck source=_docker.sh # shellcheck source=_docker.sh
. "$bindir"/_docker.sh . "$bindir"/_docker.sh
# shellcheck source=_tag.sh
. "$bindir"/_tag.sh
docker_pull base 2020-06-08.01 || true docker_pull base 2020-06-08.01 || true
docker_pull go-deps "$(go_deps_sha)" || true

View File

@ -6,8 +6,5 @@ bindir=$( cd "${BASH_SOURCE[0]%/*}" && pwd )
# shellcheck source=_docker.sh # shellcheck source=_docker.sh
. "$bindir"/_docker.sh . "$bindir"/_docker.sh
# shellcheck source=_tag.sh
. "$bindir"/_tag.sh
docker_push base 2020-06-08.01 docker_push base 2020-06-08.01
docker_push go-deps "$(go_deps_sha)"

26
Dockerfile-go-deps → bin/install-deps Normal file → Executable file
View File

@ -1,25 +1,15 @@
# Go dependencies #!/usr/bin/env sh
#
# Fetches all required Go dependencies. All Linkerd sources are omitted from the
# resulting image so that artifacts may be built from source over this image.
#
# When this file is changed, run `bin/update-go-deps-shas`.
FROM golang:1.14.2 # This script is used in the multiple Dockerfiles for caching
# some of the slow-to-build go dependencies.
WORKDIR /linkerd-build set -eu
COPY go.mod go.sum ./ bindir=$( cd "${0%/*}" && pwd )
rootdir=$( cd "$bindir"/.. && pwd )
cd "$rootdir"
RUN go mod download CGO_ENABLED=0 GOOS=linux go install -mod=readonly \
# Precompile key slow-to-build dependencies. This list doesn't need to be
# complete for the build to work correctly; the completeness of this list
# only affects the speed of incremental rebuilds of Dependent Dockerfiles.
#
# This list was derived from the output of `find /go/pkg -type f`
# after building the controller.
RUN CGO_ENABLED=0 GOOS=linux go install -mod=readonly \
github.com/golang/protobuf/jsonpb \ github.com/golang/protobuf/jsonpb \
github.com/grpc-ecosystem/go-grpc-prometheus \ github.com/grpc-ecosystem/go-grpc-prometheus \
github.com/prometheus/client_golang/api \ github.com/prometheus/client_golang/api \

View File

@ -1,13 +0,0 @@
#!/usr/bin/env bash
set -eu
# Updates the tag for `linkerd-io/go-deps` across all Dockerfiles in this repository.
# shellcheck source=_tag.sh
sha=$(. "${0%/*}"/_tag.sh ; go_deps_sha)
for f in $( grep -lR --include=Dockerfile\* go-deps: "$(cd "${0%/*}"/.. && pwd)" | xargs ) ; do
sed -E -i.bak -e "s|linkerd-io/go-deps:[^ ]+|linkerd-io/go-deps:$sha|" "$f"
rm "$f".bak
done

View File

@ -1,5 +1,13 @@
# Precompile key slow-to-build dependencies
FROM golang:1.14.2-alpine as go-deps
WORKDIR /linkerd-build
COPY go.mod go.sum ./
COPY bin/install-deps bin/
RUN go mod download
RUN ./bin/install-deps
## compile binaries ## compile binaries
FROM gcr.io/linkerd-io/go-deps:61149d15 as golang FROM go-deps as golang
WORKDIR /linkerd-build WORKDIR /linkerd-build
COPY cli cli COPY cli cli
COPY charts charts COPY charts charts

View File

@ -1,5 +1,13 @@
# Precompile key slow-to-build dependencies
FROM golang:1.14.2-alpine as go-deps
WORKDIR /linkerd-build
COPY go.mod go.sum ./
COPY bin/install-deps bin/
RUN go mod download
RUN ./bin/install-deps
## compile cni-plugin utility ## compile cni-plugin utility
FROM gcr.io/linkerd-io/go-deps:61149d15 as golang FROM go-deps as golang
WORKDIR /linkerd-build WORKDIR /linkerd-build
COPY pkg pkg COPY pkg pkg
COPY controller controller COPY controller controller

View File

@ -1,5 +1,13 @@
# Precompile key slow-to-build dependencies
FROM golang:1.14.2-alpine as go-deps
WORKDIR /linkerd-build
COPY go.mod go.sum ./
COPY bin/install-deps bin/
RUN go mod download
RUN ./bin/install-deps
## compile controller service ## compile controller service
FROM gcr.io/linkerd-io/go-deps:61149d15 as golang FROM go-deps as golang
WORKDIR /linkerd-build WORKDIR /linkerd-build
COPY controller/gen controller/gen COPY controller/gen controller/gen
COPY pkg pkg COPY pkg pkg

View File

@ -1,3 +1,11 @@
# Precompile key slow-to-build dependencies
FROM golang:1.14.2-alpine as go-deps
WORKDIR /linkerd-build
COPY go.mod go.sum ./
COPY bin/install-deps bin/
RUN go mod download
RUN ./bin/install-deps
## bundle web assets ## bundle web assets
FROM node:14-buster as webpack-bundle FROM node:14-buster as webpack-bundle
RUN curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.21.1 --network-concurrency 1 RUN curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.21.1 --network-concurrency 1
@ -21,7 +29,7 @@ COPY web/app ./web/app
RUN ./bin/web build RUN ./bin/web build
## compile go server ## compile go server
FROM gcr.io/linkerd-io/go-deps:61149d15 as golang FROM go-deps as golang
WORKDIR /linkerd-build WORKDIR /linkerd-build
RUN mkdir -p web RUN mkdir -p web
COPY web/main.go web COPY web/main.go web