From 2d5b244f1376fe7bb555ec344270142d72e5fb55 Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Tue, 9 Jul 2024 23:30:02 -0700 Subject: [PATCH] feat: add the ability to build distroless images for optimizely --- Makefile | 4 +- scripts/Makefile.ci | 77 +++++++++++++++++++++-- scripts/ci_create_packages.sh | 1 + scripts/dockerfiles/Dockerfile.alpine | 5 +- scripts/dockerfiles/Dockerfile.distroless | 16 +++++ 5 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 scripts/dockerfiles/Dockerfile.distroless diff --git a/Makefile b/Makefile index ac7c6f60..254f0df4 100644 --- a/Makefile +++ b/Makefile @@ -49,13 +49,13 @@ cover: check-go static ## runs test suite with coverage profiling cover-html: cover ## generates test coverage html report $(GOCMD) tool cover -html=$(COVER_FILE) -setup: check-go ## installs all dev and ci dependencies, but does not install golang +setup: check-go ## installs all dev and ci dependencies, but does not install golang ## "go get" won't work for newer go versions, need to use "go install github.com/rakyll/statik" ifeq (,$(wildcard $(GOLINT))) curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(GOPATH)/bin v1.54.2 endif ifeq (,$(wildcard $(GOPATH)/bin/statik)) - GO111MODULE=off go get -u github.com/rakyll/statik + go install github.com/rakyll/statik@latest endif lint: check-go static ## runs `golangci-lint` linters defined in `.golangci.yml` file diff --git a/scripts/Makefile.ci b/scripts/Makefile.ci index bba54b82..b823610d 100644 --- a/scripts/Makefile.ci +++ b/scripts/Makefile.ci @@ -1,20 +1,85 @@ .DEFAULT_GOAL := help +# To customize the version of the image, consider setting the value of the +# APP_VERSION variable. For example, to use the current git revision as a +# version, use: +# +# APP_VERSION:=$(shell git rev-parse HEAD) +# +# At the command line this would read, for example: +# +# make \ +# APP_VERSION=$(git rev-parse HEAD) \ +# CONTAINERIZER=podman \ +# ci_build_dockerimage_distroless + +# Make the image builder customizable so that we can use alternatives such as +# podman to build these images. +# +# For example, in order to build the distroless image with podman, one can run +# +# make APP_VERSION=4.0.0 CONTAINERIZER=podman ci_build_dockerimage_distroless +CONTAINERIZER:=docker + +# The latest available version of Alpine from https://hub.docker.com/_/golang +ALPINE_VERSION:=3.20 + +# The latest release version of go to address security vulnerabilities. +# See the latest version available at: https://hub.docker.com/_/golang +GIMME_GO_VERSION:=1.22.5 + +# Should one wish to have the agent image be deployed from a custom artifact +# registry such as the Google Artifact Registry (GAR), one may want to +# customize this tag prefix appropriately. +IMAGE_TAG_PREFIX:="optimizely/agent" + ci_build_static_binary: ## build static binary CGO_ENABLED=0 $(GOBUILD) $(LDFLAGS) -o $(GOBIN)/$(TARGET) cmd/optimizely/main.go ci_build_dockerimage: ## build minimal docker image of optimizely - docker build \ + $(CONTAINERIZER) build \ -f scripts/dockerfiles/Dockerfile.static \ - -t optimizely/agent:${APP_VERSION} \ - -t optimizely/agent:latest \ + -t "${IMAGE_TAG_PREFIX}:${APP_VERSION}" \ + -t "${IMAGE_TAG_PREFIX}:latest" \ --build-arg GO_VERSION=${GIMME_GO_VERSION:.x=} \ . ci_build_dockerimage_alpine: ## build alpine docker image of optimizely - docker build \ + $(CONTAINERIZER) build \ -f scripts/dockerfiles/Dockerfile.alpine \ - -t optimizely/agent:${APP_VERSION}-alpine \ - -t optimizely/agent:alpine \ + -t "${IMAGE_TAG_PREFIX}:${APP_VERSION}-alpine" \ + -t "${IMAGE_TAG_PREFIX}:alpine" \ --build-arg GO_VERSION=${GIMME_GO_VERSION:.x=} \ + --build-arg ALPINE_VERSION=${ALPINE_VERSION:.x=} \ . + +# Distroless images are tiny, have small attack surface, and security-oriented +# deployments may consider using them. +# +# For more information about distroless, please see: +# https://github.com/GoogleContainerTools/distroless +ci_build_dockerimage_distroless: ## build distroless image of optimizely + $(CONTAINERIZER) build \ + -f scripts/dockerfiles/Dockerfile.distroless \ + -t "${IMAGE_TAG_PREFIX}:${APP_VERSION}-distroless" \ + -t "${IMAGE_TAG_PREFIX}:distroless" \ + --build-arg GO_VERSION=${GIMME_GO_VERSION:.x=} \ + . + +# PHONY target to build all of the above container images. +_ci_build_dockerimage_all: ci_build_dockerimage ci_build_dockerimage_alpine ci_build_dockerimage_distroless +ci_build_dockerimage_all: _ci_build_dockerimage_all ## build all container images + +push_image: ## push container image + $(CONTAINERIZER) push "${IMAGE_TAG_PREFIX}:${APP_VERSION}" + $(CONTAINERIZER) push "${IMAGE_TAG_PREFIX}:latest" + +push_image_alpine: ## push alpine container image + $(CONTAINERIZER) push "${IMAGE_TAG_PREFIX}:${APP_VERSION}-alpine" + $(CONTAINERIZER) push "${IMAGE_TAG_PREFIX}:alpine" + +push_image_distroless: ## push distroless container image + $(CONTAINERIZER) push "${IMAGE_TAG_PREFIX}:${APP_VERSION}-distroless" + $(CONTAINERIZER) push "${IMAGE_TAG_PREFIX}:distroless" + +push_all_images: push_image push_image_alpine push_image_distroless ## push all container images diff --git a/scripts/ci_create_packages.sh b/scripts/ci_create_packages.sh index 039120ca..af2da965 100755 --- a/scripts/ci_create_packages.sh +++ b/scripts/ci_create_packages.sh @@ -6,6 +6,7 @@ if [[ $TRAVIS_OS_NAME == "linux" ]]; then cd $TRAVIS_BUILD_DIR make -e ci_build_dockerimage make -e ci_build_dockerimage_alpine + make -e ci_build_dockerimage_distroless elif [[ $TRAVIS_OS_NAME == "osx" ]]; then echo "we're on osx" else diff --git a/scripts/dockerfiles/Dockerfile.alpine b/scripts/dockerfiles/Dockerfile.alpine index 07dfc6e2..6a6d74b0 100644 --- a/scripts/dockerfiles/Dockerfile.alpine +++ b/scripts/dockerfiles/Dockerfile.alpine @@ -1,5 +1,6 @@ ARG GO_VERSION -FROM golang:$GO_VERSION-alpine3.17 as builder +ARG ALPINE_VERSION +FROM golang:$GO_VERSION-alpine${ALPINE_VERSION} as builder # hadolint ignore=DL3018 RUN addgroup -S agentgroup && adduser -S agentuser -G agentgroup RUN apk add --no-cache make gcc libc-dev git curl @@ -7,7 +8,7 @@ WORKDIR /go/src/github.com/optimizely/agent COPY . . RUN make setup build -FROM alpine:3.17 +FROM alpine:${ALPINE_VERSION} RUN apk add --no-cache ca-certificates COPY --from=builder /go/src/github.com/optimizely/agent/bin/optimizely /optimizely COPY --from=builder /etc/passwd /etc/passwd diff --git a/scripts/dockerfiles/Dockerfile.distroless b/scripts/dockerfiles/Dockerfile.distroless new file mode 100644 index 00000000..25e11333 --- /dev/null +++ b/scripts/dockerfiles/Dockerfile.distroless @@ -0,0 +1,16 @@ +ARG GO_VERSION +FROM golang:${GO_VERSION} as builder +RUN addgroup -u 1000 agentgroup && \ + useradd -u 1000 agentuser -g agentgroup +WORKDIR /go/src/github.com/optimizely/agent +COPY . . +RUN make setup build && \ + make ci_build_static_binary + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /go/src/github.com/optimizely/agent/bin/optimizely /optimizely +COPY --from=builder /etc/passwd /etc/passwd +COPY --from=builder /etc/group /etc/group +USER agentuser:agentgroup +ENTRYPOINT ["/optimizely"]