diff --git a/Dockerfile b/Dockerfile index f50339c1390..11bb3cebab8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,12 @@ -######################## IMPORTANT ######################## -# -# There are 2 Dockerfiles which must be kept in sync: -# -# - Dockerfile -# - packaging/docker/Dockerfile -# -########################################################### +# syntax=docker/dockerfile:1 ARG BASE_IMAGE=alpine:3.15 ARG JS_IMAGE=node:16-alpine3.15 ARG GO_IMAGE=golang:1.19.3-alpine3.15 +ARG GO_SRC=go-builder +ARG JS_SRC=js-builder + FROM ${JS_IMAGE} as js-builder ENV NODE_OPTIONS=--max_old_space_size=8000 @@ -56,9 +52,25 @@ COPY public/app/plugins public/app/plugins COPY public/api-spec.json public/api-spec.json COPY pkg pkg COPY scripts scripts +COPY conf conf RUN make build-go +FROM ${BASE_IMAGE} as tgz-builder + +WORKDIR /tmp/grafana + +ARG GRAFANA_TGZ="grafana-latest.linux-x64-musl.tar.gz" + +COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz + +# add -v to make tar print every file it extracts +RUN tar x -z -f /tmp/grafana.tar.gz --strip-components=1 + +# helpers for COPY --from +FROM ${GO_SRC} as go-src +FROM ${JS_SRC} as js-src + # Final stage FROM ${BASE_IMAGE} @@ -106,7 +118,7 @@ RUN if grep -i -q alpine /etc/issue && [ `arch` = "x86_64" ]; then \ rm -f /etc/ld.so.cache; \ fi -COPY conf ./conf +COPY --from=go-src /tmp/grafana/conf ./conf RUN if [ ! $(getent group "$GF_GID") ]; then \ if grep -i -q alpine /etc/issue; then \ @@ -136,12 +148,14 @@ RUN if [ ! $(getent group "$GF_GID") ]; then \ chown -R "grafana:$GF_GID_NAME" "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \ chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" -COPY --from=go-builder /tmp/grafana/bin/*/grafana-server /tmp/grafana/bin/*/grafana-cli /tmp/grafana/bin/*/grafana ./bin/ -COPY --from=js-builder /tmp/grafana/public ./public +COPY --from=go-src /tmp/grafana/bin/grafana* /tmp/grafana/bin/*/grafana* ./bin/ +COPY --from=js-src /tmp/grafana/public ./public EXPOSE 3000 -COPY ./packaging/docker/run.sh /run.sh +ARG RUN_SH=./packaging/docker/run.sh + +COPY ${RUN_SH} /run.sh USER "$GF_UID" ENTRYPOINT [ "/run.sh" ] diff --git a/Makefile b/Makefile index de6c3a9f563..887df780d6a 100644 --- a/Makefile +++ b/Makefile @@ -158,11 +158,13 @@ shellcheck: $(SH_FILES) ## Run checks for shell scripts. build-docker-full: ## Build Docker image for development. @echo "build docker container" + DOCKER_BUILDKIT=1 \ docker build \ --tag grafana/grafana:dev . build-docker-full-ubuntu: ## Build Docker image based on Ubuntu for development. @echo "build docker container" + DOCKER_BUILDKIT=1 \ docker build \ --build-arg BASE_IMAGE=ubuntu:20.04 \ --build-arg GO_IMAGE=golang:1.19.3 \ diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile deleted file mode 100644 index 1b06e7d8664..00000000000 --- a/packaging/docker/Dockerfile +++ /dev/null @@ -1,113 +0,0 @@ -######################## IMPORTANT ######################## -# -# There are 2 Dockerfiles which must be kept in sync: -# -# - Dockerfile -# - packaging/docker/Dockerfile -# -########################################################### - -ARG BASE_IMAGE=alpine:3.15 - -FROM ${BASE_IMAGE} as tgz-builder - -WORKDIR /tmp/grafana - -ARG GRAFANA_TGZ="grafana-latest.linux-x64-musl.tar.gz" - -# Make sure we have Gnu tar -RUN if grep -i -q alpine /etc/issue; then \ - apk add --no-cache tar; \ - fi - -COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz - -# Change to tar xfzv to make tar print every file it extracts -RUN tar xzf /tmp/grafana.tar.gz --strip-components=1 -C /tmp/grafana - -# Final stage -FROM ${BASE_IMAGE} - -LABEL maintainer="Grafana Labs " - -ARG GF_UID="472" -ARG GF_GID="0" - -ENV PATH="/usr/share/grafana/bin:$PATH" \ - GF_PATHS_CONFIG="/etc/grafana/grafana.ini" \ - GF_PATHS_DATA="/var/lib/grafana" \ - GF_PATHS_HOME="/usr/share/grafana" \ - GF_PATHS_LOGS="/var/log/grafana" \ - GF_PATHS_PLUGINS="/var/lib/grafana/plugins" \ - GF_PATHS_PROVISIONING="/etc/grafana/provisioning" - -WORKDIR $GF_PATHS_HOME - -# Install dependencies -RUN if grep -i -q alpine /etc/issue; then \ - apk add --no-cache ca-certificates bash tzdata musl-utils && \ - apk info -vv | sort; \ - elif grep -i -q ubuntu /etc/issue; then \ - DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y ca-certificates curl tzdata && \ - apt-get autoremove -y && \ - rm -rf /var/lib/apt/lists/*; \ - else \ - echo 'ERROR: Unsupported base image' && /bin/false; \ - fi - -# glibc support for alpine x86_64 only -RUN if grep -i -q alpine /etc/issue && [ `arch` = "x86_64" ]; then \ - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk \ - -O /tmp/glibc-2.35-r0.apk && \ - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-bin-2.35-r0.apk \ - -O /tmp/glibc-bin-2.35-r0.apk && \ - apk add --no-cache --allow-untrusted /tmp/glibc-2.35-r0.apk /tmp/glibc-bin-2.35-r0.apk && \ - rm -f /lib64/ld-linux-x86-64.so.2 && \ - ln -s /usr/glibc-compat/lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 && \ - rm -f /tmp/glibc-2.35-r0.apk && \ - rm -f /tmp/glibc-bin-2.35-r0.apk && \ - rm -f /lib/ld-linux-x86-64.so.2 && \ - rm -f /etc/ld.so.cache; \ - fi - -COPY --from=tgz-builder /tmp/grafana/conf ./conf - -RUN if [ ! $(getent group "$GF_GID") ]; then \ - if grep -i -q alpine /etc/issue; then \ - addgroup -S -g $GF_GID grafana; \ - else \ - addgroup --system --gid $GF_GID grafana; \ - fi; \ - fi && \ - GF_GID_NAME=$(getent group $GF_GID | cut -d':' -f1) && \ - mkdir -p "$GF_PATHS_HOME/.aws" && \ - if grep -i -q alpine /etc/issue; then \ - adduser -S -u $GF_UID -G "$GF_GID_NAME" grafana; \ - else \ - adduser --system --uid $GF_UID --ingroup "$GF_GID_NAME" grafana; \ - fi && \ - mkdir -p "$GF_PATHS_PROVISIONING/datasources" \ - "$GF_PATHS_PROVISIONING/dashboards" \ - "$GF_PATHS_PROVISIONING/notifiers" \ - "$GF_PATHS_PROVISIONING/plugins" \ - "$GF_PATHS_PROVISIONING/access-control" \ - "$GF_PATHS_PROVISIONING/alerting" \ - "$GF_PATHS_LOGS" \ - "$GF_PATHS_PLUGINS" \ - "$GF_PATHS_DATA" && \ - cp conf/sample.ini "$GF_PATHS_CONFIG" && \ - cp conf/ldap.toml /etc/grafana/ldap.toml && \ - chown -R "grafana:$GF_GID_NAME" "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \ - chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" - -COPY --from=tgz-builder /tmp/grafana/bin/grafana-server /tmp/grafana/bin/grafana-cli ./bin/ -COPY --from=tgz-builder /tmp/grafana/public ./public - -EXPOSE 3000 - -COPY ./run.sh /run.sh - -USER "$GF_UID" -ENTRYPOINT [ "/run.sh" ] diff --git a/packaging/docker/build-enterprise.sh b/packaging/docker/build-enterprise.sh deleted file mode 100755 index 38cb080d427..00000000000 --- a/packaging/docker/build-enterprise.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -set -e - -UBUNTU_BASE=0 -TAG_SUFFIX="" - -while [ "$1" != "" ]; do - case "$1" in - "--ubuntu") - UBUNTU_BASE=1 - TAG_SUFFIX="-ubuntu" - echo "Ubuntu base image enabled" - shift - ;; - * ) - # unknown param causes args to be passed through to $@ - break - ;; - esac -done - -_raw_grafana_tag=$1 -_docker_repo=${2:-grafana/grafana-enterprise} - -if echo "$_raw_grafana_tag" | grep -q "^v"; then - _grafana_tag=$(echo "${_raw_grafana_tag}" | cut -d "v" -f 2) -elif echo "$_raw_grafana_tag" | grep -q "^main-"; then - _grafana_tag="main" -else - _grafana_tag="${_raw_grafana_tag}" -fi - -echo "Building and deploying ${_docker_repo}:${_grafana_tag}${TAG_SUFFIX}" - -docker build \ - --tag "${_docker_repo}:${_grafana_tag}${TAG_SUFFIX}" \ - --no-cache=true \ - . - -docker push "${_docker_repo}:${_grafana_tag}${TAG_SUFFIX}" - -if echo "$_raw_grafana_tag" | grep -q "^v" && echo "$_raw_grafana_tag" | grep -qv "beta"; then - docker tag "${_docker_repo}:${_grafana_tag}${TAG_SUFFIX}" "${_docker_repo}:latest${TAG_SUFFIX}" - docker push "${_docker_repo}:latest${TAG_SUFFIX}" -fi - - -if echo "${_raw_grafana_tag}" | grep -q "^main-" && [ ${UBUNTU_BASE} = "1" ]; then - docker tag "${_docker_repo}:${_grafana_tag}${TAG_SUFFIX}" "grafana/grafana-enterprise-dev:${_raw_grafana_tag}" - docker push "grafana/grafana-enterprise-dev:${_raw_grafana_tag}" -fi diff --git a/packaging/docker/build.sh b/packaging/docker/build.sh index f26c1523823..f1fcf443886 100755 --- a/packaging/docker/build.sh +++ b/packaging/docker/build.sh @@ -68,11 +68,16 @@ docker_build () { grafana_tgz=${GRAFANA_TGZ:-"grafana-latest.linux-${arch}${libc}.tar.gz"} tag="${_docker_repo}${repo_arch}:${_grafana_version}${TAG_SUFFIX}" + DOCKER_BUILDKIT=1 \ docker build \ --build-arg BASE_IMAGE=${base_image} \ --build-arg GRAFANA_TGZ=${grafana_tgz} \ + --build-arg GO_SRC=tgz-builder \ + --build-arg JS_SRC=tgz-builder \ + --build-arg RUN_SH=./run.sh \ --tag "${tag}" \ --no-cache=true \ + --file ../../Dockerfile \ . } diff --git a/pkg/build/docker/build.go b/pkg/build/docker/build.go index cf3a0684770..fabc06c1644 100644 --- a/pkg/build/docker/build.go +++ b/pkg/build/docker/build.go @@ -116,33 +116,49 @@ func BuildImage(version string, arch config.Architecture, grafanaDir string, use tags = append(tags, tag) args := []string{ - "build", "--build-arg", fmt.Sprintf("BASE_IMAGE=%s", baseImage), - "--build-arg", fmt.Sprintf("GRAFANA_TGZ=%s", archive), "--tag", tag, "--no-cache", + "build", + "-q", + "--build-arg", fmt.Sprintf("BASE_IMAGE=%s", baseImage), + "--build-arg", fmt.Sprintf("GRAFANA_TGZ=%s", archive), + "--build-arg", "GO_SRC=tgz-builder", + "--build-arg", "JS_SRC=tgz-builder", + "--build-arg", "RUN_SH=./run.sh", + "--tag", tag, + "--no-cache", + "--file", "../../Dockerfile", ".", } - log.Printf("Running Docker: docker %s", strings.Join(args, " ")) //nolint:gosec cmd := exec.Command("docker", args...) cmd.Dir = buildDir - cmd.Env = append(os.Environ(), "DOCKER_CLI_EXPERIMENTAL=enabled") - if output, err := cmd.CombinedOutput(); err != nil { - return []string{}, fmt.Errorf("building Docker image failed: %w\n%s", err, output) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = append(os.Environ(), "DOCKER_CLI_EXPERIMENTAL=enabled", "DOCKER_BUILDKIT=1") + log.Printf("Running Docker: DOCKER_CLI_EXPERIMENTAL=enabled DOCKER_BUILDKIT=1 %s", cmd) + if err := cmd.Run(); err != nil { + return []string{}, fmt.Errorf("building Docker image failed: %w", err) } if shouldSave { imageFile := fmt.Sprintf("%s-%s%s-%s.img", imageFileBase, version, tagSuffix, arch) //nolint:gosec cmd = exec.Command("docker", "save", tag, "-o", imageFile) cmd.Dir = buildDir - if output, err := cmd.CombinedOutput(); err != nil { - return []string{}, fmt.Errorf("saving Docker image failed: %w\n%s", err, output) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + log.Printf("Running Docker: %s", cmd) + if err := cmd.Run(); err != nil { + return []string{}, fmt.Errorf("saving Docker image failed: %w", err) } gcsURL := fmt.Sprintf("gs://grafana-prerelease/artifacts/docker/%s/%s", version, imageFile) //nolint:gosec - cmd = exec.Command("gsutil", "cp", imageFile, gcsURL) + cmd = exec.Command("gsutil", "-q", "cp", imageFile, gcsURL) cmd.Dir = buildDir - if output, err := cmd.CombinedOutput(); err != nil { - return []string{}, fmt.Errorf("storing Docker image failed: %w\n%s", err, output) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + log.Printf("Running gsutil: %s", cmd) + if err := cmd.Run(); err != nil { + return []string{}, fmt.Errorf("storing Docker image failed: %w", err) } log.Printf("Docker image %s stored to grafana-prerelease GCS bucket", imageFile) } @@ -152,8 +168,11 @@ func BuildImage(version string, arch config.Architecture, grafanaDir string, use //nolint:gosec cmd = exec.Command("docker", "tag", tag, additionalTag) cmd.Dir = buildDir - if output, err := cmd.CombinedOutput(); err != nil { - return []string{}, fmt.Errorf("tagging Docker image failed: %w\n%s", err, output) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + log.Printf("Running Docker: %s", cmd) + if err := cmd.Run(); err != nil { + return []string{}, fmt.Errorf("tagging Docker image failed: %w", err) } tags = append(tags, additionalTag) }