Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc Docker + Go version upgrade #12921

Merged
merged 2 commits into from
Oct 13, 2024
Merged

Misc Docker + Go version upgrade #12921

merged 2 commits into from
Oct 13, 2024

Conversation

bep
Copy link
Member

@bep bep commented Oct 9, 2024

As to the Docker image adjustments, I have tested OK:

  • css.TailwindCSS
  • PostCSS
  • Libsass (extended build is now default)
  • Dart Sass
  • The hugoDocs (Go modules, Git etc.) and misc others Go modules setups.
  • It sets ENV HUGO_CACHEDIR=/cache allowing the Hugo cache to survive builds.

On MacOS, an example build line would be:

docker run --rm -v .:/project -v $HOME/Library/Caches/hugo_cache:/cache imagename build

Starting the server:

docker run --rm -v .:/project -v $HOME/Library/Caches/hugo_cache:/cache imagename server --bind="0.0.0.0"

This is the entry point file:

#!/bin/sh

# Check if a custom hugo-docker-entrypoint.sh file exists.
if [ -f hugo-docker-entrypoint.sh ]; then
  # Execute the custom entrypoint file.
  sh hugo-docker-entrypoint.sh "$@"
  exit $?
fi

# Check if a package.json file exists.
if [ -f package.json ]; then
  # Check if node_modules exists.
  if [ ! -d node_modules ]; then
    # Install npm packages.
    # Note that we deliberately do not use `npm ci` here, as it would fail if the package-lock.json file is not up-to-date,
    # which would be the case if you run the container with a different OS or architecture than the one used to create the package-lock.json file.
    npm i
  fi
fi

exec "hugo" "$@"

If a custom hugo-docker-entrypoint.sh script exists in the root of the Hugo project, that script will be executed instead of the default entrypoint script.

The previous build workflow used emulation to build the Docker image,
which results in a somewhat complicated push-by-digest and merge
workflow to create a multi-platform image.

This commit changes the Docker build to use cross-compilation instead,
resulting in a faster and more straightforward build.

Signed-off-by: David Karlsson <[email protected]>
@bep bep force-pushed the feat/miscdockergo branch 6 times, most recently from 4cc240a to a1813dc Compare October 10, 2024 16:52
@bep
Copy link
Member Author

bep commented Oct 10, 2024

@jmooring I have spent some hours today trying to get up to speed re. the current Docker "scene"; I'm reasonably happy but the Dockerfile setup, and it should support all but 3) in your list in #12885.

I wouldn't mind if we as the next step added a hugo-extras (or something) with Asciidoc etc. added, but I think it would be good to have one base image that covered 90% of the use cases.

On my MacBook Pro M1 the image size increases from 135MB to to 406MB, which I suspect some will complain about, but it's not dramatic enough for me to even consider breaking it up into multiple setups.

What do you think? The entrypoint/npm install setup is a little opinionated, but I think it should work. I have tested it would most of my Hugo projects with a package.json and it seems to work fine (even with the new Tailwind).

What do you think?

/cc @dvdksn

@bep bep force-pushed the feat/miscdockergo branch 2 times, most recently from 0d16f77 to 6501676 Compare October 11, 2024 07:09
@dvdksn
Copy link
Contributor

dvdksn commented Oct 11, 2024

I'm taking a look at the build failure, looks like something related to cross-comp with the new Dockerfile

@dvdksn
Copy link
Contributor

dvdksn commented Oct 11, 2024

OK so here are some changes that I came up with;

  • Currently, the final image is based on the base stage which is pinning the platform to $BUILDPLATFORM - we need the final image to use the target platform, so I've decoupled base and final.
    • I added a GO_VERSION arg to make sure they're both using the same golang version.
    • It seems like gcc/g++ are also runtime dependencies for libsass and libwebp, so we need to install them in both the build and final stages.
  • I added clang and lld back for the C cross-compilation. I don't know too much about how this works but they're recommended for use with xx
  • Re-enabled CGO
  • Fixed the install_runtimedeps_default.sh script to use TARGETARCH instead of BUILDARCH, since we need different versions in the runtime stage.
diff --git a/Dockerfile b/Dockerfile
index 6da83a3cc..921215c59 100755
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,14 +2,15 @@
 # Twitter:      https://twitter.com/gohugoio
 # Website:      https://gohugo.io/
 
+ARG GO_VERSION="1.23.2"
+
 FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx
-FROM --platform=$BUILDPLATFORM golang:1.23.2-alpine AS base
-FROM base AS build
+FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS build
 
 # Set up cross-compilation helpers
 COPY --from=xx / /
-
-# gcc/g++ are required to build libsass and libwebp libraries for the extended version.
+RUN apk add clang lld
+ARG TARGETPLATFORM
 RUN xx-apk add --no-scripts --no-cache gcc g++
 
 # Optionally set HUGO_BUILD_TAGS to "none" or "nodeploy" when building like so:
@@ -20,7 +21,7 @@ ARG HUGO_BUILD_TAGS="extended"
 ENV GOPROXY=https://proxy.golang.org
 ENV GOCACHE=/root/.cache/go-build
 ENV GOMODCACHE=/go/pkg/mod
-ARG TARGETPLATFORM
+ENV CGO_ENABLED=1
 
 WORKDIR /go/src/github.com/gohugoio/hugo
 
@@ -36,7 +37,10 @@ RUN --mount=target=. \
     xx-verify /usr/bin/hugo
 EOT
 
-FROM base AS final
+FROM golang:${GO_VERSION}-alpine AS final
+
+# libsass and libwebp also require gcc/g++ as runtime dependencies
+RUN apk add --no-scripts --no-cache gcc g++
 
 COPY --from=build /usr/bin/hugo /usr/bin/hugo
 
@@ -60,8 +64,8 @@ VOLUME /project
 WORKDIR /project
 USER hugo:hugo
 ENV HUGO_CACHEDIR=/cache
-ARG BUILDARCH
-ENV BUILDARCH=${BUILDARCH}
+ARG TARGETARCH
+ENV ARCH=${TARGETARCH/amd64/x64}
 
 COPY scripts/docker scripts/docker
 
diff --git a/scripts/docker/install_runtimedeps_default.sh b/scripts/docker/install_runtimedeps_default.sh
index 0b6c2c617..4ca88fd67 100755
--- a/scripts/docker/install_runtimedeps_default.sh
+++ b/scripts/docker/install_runtimedeps_default.sh
@@ -4,17 +4,10 @@ set -ex
 
 export DART_SASS_VERSION=1.79.3
 
-# If $BUILDARCH=arm64, then we need to install the arm64 version of Dart Sass,
-# otherwise we install the x64 version.
-ARCH="x64"
-if [ "$BUILDARCH" = "arm64" ]; then
-    ARCH="arm64"
-fi
-
 cd /tmp
 curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-${ARCH}.tar.gz 
 ls -ltr
 tar -xf dart-sass-${DART_SASS_VERSION}-linux-${ARCH}.tar.gz
 rm dart-sass-${DART_SASS_VERSION}-linux-${ARCH}.tar.gz && \
 # The dart-sass folder is added to the PATH by the caller.
-mv dart-sass /var/hugo/bin
\ No newline at end of file
+mv dart-sass /var/hugo/bin

@bep bep force-pushed the feat/miscdockergo branch 2 times, most recently from 01114fb to 9de2ec4 Compare October 11, 2024 08:38
@dvdksn
Copy link
Contributor

dvdksn commented Oct 11, 2024

I noticed you pushed some more changes, and I also noticed that the way dart-sass is installed could be improved, e.g. we install curl in the runtime image which isn't very good, we only want the dart-sass binary. So I made installing dart-sass a separate stage and copy the artifact from there.

edit: I realized my diff doesn't apply after the force pushes, so here's the Dockerfile I have. I have tested the image with docker/docs where it's working. With this file, you can delete scripts/docker/install_runtimedeps_default.sh because those instructions are inlined.

(note: to run the server with the Docker image you need to also bind to 0.0.0.0, so the docker run command becomes: docker run -p 1313:1313 -v .:/project hugo server --bind 0.0.0.0.)

# GitHub:       https://github.com/gohugoio
# Twitter:      https://twitter.com/gohugoio
# Website:      https://gohugo.io/

ARG GO_VERSION="1.23.2"

FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS build

# Set up cross-compilation helpers
COPY --from=xx / /
RUN apk add clang lld
ARG TARGETPLATFORM
RUN xx-apk add --no-scripts --no-cache gcc g++

# Optionally set HUGO_BUILD_TAGS to "none" or "nodeploy" when building like so:
# docker build --build-arg HUGO_BUILD_TAGS=nodeploy .
#
# We build the extended version by default.
ARG HUGO_BUILD_TAGS="extended"
ENV GOPROXY=https://proxy.golang.org
ENV GOCACHE=/root/.cache/go-build
ENV GOMODCACHE=/go/pkg/mod
ENV CGO_ENABLED=1

WORKDIR /go/src/github.com/gohugoio/hugo

# For  --mount=type=cache the value of target is the default cache id, so
# for the go mod cache it would be good if we could share it with other Go images using the same setup,
# but the go build cache needs to be per platform.
# See this comment: https://github.com/moby/buildkit/issues/1706#issuecomment-702238282
RUN --mount=target=. \
    --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build,id=go-build-$TARGETPLATFORM <<EOT
    set -ex
    xx-go build -tags "$HUGO_BUILD_TAGS" -ldflags "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=docker" -o /usr/bin/hugo
    xx-verify /usr/bin/hugo
EOT

FROM alpine:latest AS dart-sass
ARG TARGETARCH
ARG ARCH=${TARGETARCH/amd64/x64}
ARG DART_SASS_VERSION=1.79.3
WORKDIR /tmp
RUN apk add --no-cache curl
RUN <<EOT
    set -ex
    curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-${ARCH}.tar.gz
    tar -xf dart-sass-${DART_SASS_VERSION}-linux-${ARCH}.tar.gz
EOT

FROM golang:${GO_VERSION}-alpine AS final

# Install runtime dependencies
RUN apk add --no-cache \
    g++ \
    gcc \
    git \
    libc6-compat \
    libstdc++ \
    nodejs \
    npm \
    runuser

COPY --from=dart-sass /tmp/dart-sass /var/hugo/bin/dart-sass
COPY --from=build /usr/bin/hugo /usr/bin/hugo
ENV PATH="/var/hugo/bin:/var/hugo/bin/dart-sass:$PATH"

RUN <<EOT
    mkdir -p /var/hugo/bin /cache
    addgroup -Sg 1000 hugo
    adduser -Sg hugo -u 1000 -h /var/hugo hugo
    chown -R hugo: /var/hugo /cache
    # For the Hugo's Git integration to work.
    runuser -u hugo -- git config --global --add safe.directory /project
    # See https://github.com/gohugoio/hugo/issues/9810
    runuser -u hugo -- git config --global core.quotepath false
EOT

USER hugo:hugo
VOLUME /project
WORKDIR /project
ENV HUGO_CACHEDIR=/cache

RUN sass --version
RUN hugo version

COPY scripts/docker/entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["--help"]

@bep
Copy link
Member Author

bep commented Oct 11, 2024

@dvdksn thanks for the input; one of my goals of this work is to remove (at least most of) the guess work from the Docker setup (e.g. "It seems like gcc/g++ are also runtime dependencies for libsass and libwebp"; we need a libc implementation, but not the full grown compiler).

@dvdksn
Copy link
Contributor

dvdksn commented Oct 11, 2024

OK, then I think you can just remove

    g++ \
    gcc \

from the Dockerfile above and it should work

@bep bep force-pushed the feat/miscdockergo branch 11 times, most recently from a9cf8bb to 7c47bd4 Compare October 11, 2024 11:26
@bep bep force-pushed the feat/miscdockergo branch 2 times, most recently from 9125f37 to 4dc3c95 Compare October 11, 2024 12:45
* Rename /site to /project
* Add ldflags
* Add go and node to the default image
* Add Dart Sass to the default image
* Build the extended version by default
* Add "npm i" install support with custom entry script override
* Adjust cache logic to speed up CGO rebuilds

Closes #12920
See #12885
@bep bep marked this pull request as ready for review October 11, 2024 13:10
@jmooring
Copy link
Member

@bep Support for asciidoc, pandoc, and rst can wait for another day. I think this covers > 95% of use cases. I don't have any opinion on size... I suppose if someone wants something thinner they can grow their own, as many have already done.

@bep bep merged commit 41f69a7 into master Oct 13, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants