From e6b7efe28ef33467df1d64f526b0059d2b782039 Mon Sep 17 00:00:00 2001 From: Nicolas Georger Date: Tue, 25 Jul 2023 18:34:25 -0400 Subject: [PATCH] no gosu --- .github/workflows/build-custom-image.yaml | 9 +- Dockerfile | 138 +++++---------------- Dockerfile.orig | 132 ++++++++++++++++++++ charts/Chart.yaml | 2 +- charts/templates/05-ghost-statefulset.yaml | 1 + charts/values.yaml | 1 + docker-compose.yml | 4 +- docker-entrypoint.sh | 2 +- docker-entrypoint.sh.bak | 22 ++++ 9 files changed, 195 insertions(+), 116 deletions(-) create mode 100644 Dockerfile.orig mode change 100755 => 100644 docker-entrypoint.sh create mode 100755 docker-entrypoint.sh.bak diff --git a/.github/workflows/build-custom-image.yaml b/.github/workflows/build-custom-image.yaml index a0b9bff..aaad1cd 100644 --- a/.github/workflows/build-custom-image.yaml +++ b/.github/workflows/build-custom-image.yaml @@ -20,12 +20,7 @@ on: jobs: docker: runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - platform: - - linux/amd64 - - linux/arm64 + steps: - name: Checkout @@ -91,7 +86,7 @@ jobs: uses: docker/build-push-action@v4 with: context: . - platforms: ${{ matrix.platform }} + platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index 22274d9..b5e8020 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,130 +1,58 @@ # https://docs.ghost.org/faq/node-versions/ # https://github.com/nodejs/Release (looking for "LTS") # https://github.com/TryGhost/Ghost/blob/v4.1.2/package.json#L38 -FROM node:18-bookworm - -# grab gosu for easy step-down from root -# https://github.com/tianon/gosu/releases -ENV GOSU_VERSION 1.16 -RUN set -eux; \ - # save list of currently installed packages for later so we can clean up - savedAptMark="$(apt-mark showmanual)"; \ - apt-get update && apt-get upgrade -y; \ - apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ - rm -rf /var/lib/apt/lists/*; \ - \ - dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ - wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ - wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ - \ - # verify the signature - export GNUPGHOME="$(mktemp -d)"; \ - gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ - gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ - gpgconf --kill all; \ - rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ - \ - # clean up fetch dependencies - apt-mark auto '.*' > /dev/null; \ - [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ - apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ - \ - chmod +x /usr/local/bin/gosu; \ - # verify that the binary works - gosu --version; \ - gosu nobody true +FROM node:18-bookworm-slim ENV NODE_ENV production +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update && apt-get upgrade -y +RUN apt-get install -y --no-install-recommends g++ make python3 libvips-dev nano && apt-get clean && rm -rf /var/lib/apt/lists/* + + + # ENV GHOST_CLI_VERSION 1.24.1 -RUN set -eux; \ - npm install -g "ghost-cli@latest"; \ - npm cache clean --force +RUN npm install -g "ghost-cli@latest" && npm cache clean --force ENV GHOST_INSTALL /var/lib/ghost ENV GHOST_CONTENT /var/lib/ghost/content -ARG GHOST_VERSION +ARG GHOST_VERSION=5.55.1 ENV GHOST_VERSION $GHOST_VERSION -RUN set -eux; \ - mkdir -p "$GHOST_INSTALL"; \ - chown node:node "$GHOST_INSTALL"; \ - \ - savedAptMark="$(apt-mark showmanual)"; \ - aptPurge=; \ - \ - installCmd='gosu node ghost install "$GHOST_VERSION" --db mysql --dbhost mysql --no-prompt --no-stack --no-setup --dir "$GHOST_INSTALL"'; \ - if ! eval "$installCmd"; then \ - aptPurge=1; \ - apt-get update && apt-get upgrade -y; \ - apt-get install -y --no-install-recommends g++ make python3; \ - eval "$installCmd"; \ - fi; \ - \ -# Tell Ghost to listen on all ips and not prompt for additional configuration - cd "$GHOST_INSTALL"; \ - gosu node ghost config --no-prompt --ip '::' --port 2368 --url 'http://localhost:2368'; \ - gosu node ghost config paths.contentPath "$GHOST_CONTENT"; \ - \ +RUN mkdir -pv "$GHOST_INSTALL" && \ + chown node:node -Rf "$GHOST_INSTALL" + +USER node +WORKDIR $GHOST_INSTALL + +RUN ghost install "$GHOST_VERSION" --db mysql --dbhost mysql --no-prompt --no-stack --no-setup --dir "$GHOST_INSTALL" && \ + # Tell Ghost to listen on all ips and not prompt for additional configuration + cd "$GHOST_INSTALL" && ghost config --no-prompt --ip '::' --port 2368 --url 'http://localhost:2368' && \ + ghost config paths.contentPath "$GHOST_CONTENT" # make a config.json symlink for NODE_ENV=development (and sanity check that it's correct) - gosu node ln -s config.production.json "$GHOST_INSTALL/config.development.json"; \ - readlink -f "$GHOST_INSTALL/config.development.json"; \ - \ +RUN ln -s config.production.json "$GHOST_INSTALL/config.development.json" && \ + readlink -f "$GHOST_INSTALL/config.development.json" # need to save initial content for pre-seeding empty volumes - mv "$GHOST_CONTENT" "$GHOST_INSTALL/content.orig"; \ - mkdir -p "$GHOST_CONTENT"; \ - chown node:node "$GHOST_CONTENT"; \ - chmod 1777 "$GHOST_CONTENT"; \ - \ +USER root +RUN mv "$GHOST_CONTENT" "$GHOST_INSTALL/content.orig" && \ + mkdir -pv "$GHOST_CONTENT" && \ + chown node:node "$GHOST_CONTENT" && \ + chmod 1777 "$GHOST_CONTENT" && \ # force install a few extra packages manually since they're "optional" dependencies # (which means that if it fails to install, like on ARM/ppc64le/s390x, the failure will be silently ignored and thus turn into a runtime error instead) # see https://github.com/TryGhost/Ghost/pull/7677 for more details - cd "$GHOST_INSTALL/current"; \ -# scrape the expected versions directly from Ghost/dependencies - packages="$(node -p ' \ - var ghost = require("./package.json"); \ - var transform = require("./node_modules/@tryghost/image-transform/package.json"); \ - [ \ - "sharp@" + transform.optionalDependencies["sharp"], \ - "sqlite3@" + ghost.optionalDependencies["sqlite3"], \ - ].join(" ") \ - ')"; \ - if echo "$packages" | grep 'undefined'; then exit 1; fi; \ - for package in $packages; do \ - installCmd='gosu node yarn add "$package" --force'; \ - if ! eval "$installCmd"; then \ - # must be some non-amd64 architecture pre-built binaries aren't published for, so let's install some build deps and do-it-all-over-again - aptPurge=1; \ - apt-get update && apt-get upgrade -y; \ - apt-get install -y --no-install-recommends g++ make python3; \ - case "$package" in \ - # TODO sharp@*) apt-get install -y --no-install-recommends libvips-dev ;; \ - sharp@*) apt-get install -y --no-install-recommends libvips-dev ;; \ - # sharp@*) echo >&2 "sorry: libvips 8.10 in Debian bullseye is not new enough (8.12.2+) for sharp 0.30 😞"; continue ;; \ - esac; \ - \ - eval "$installCmd --build-from-source"; \ - fi; \ - done; \ - \ - if [ -n "$aptPurge" ]; then \ - apt-mark showmanual | xargs apt-mark auto > /dev/null; \ - [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ - apt-get purge -y --auto-remove; \ - rm -rf /var/lib/apt/lists/*; \ - fi; \ - \ - gosu node yarn cache clean; \ - gosu node npm cache clean --force; \ - npm cache clean --force; \ + cd "$GHOST_INSTALL/current" && \ + yarn cache clean && \ + npm cache clean --force && \ rm -rv /tmp/yarn* /tmp/v8* - +COPY --chmod=0777 docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh && apt-get autoclean && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +USER node WORKDIR $GHOST_INSTALL VOLUME $GHOST_CONTENT -COPY --chmod=0777 docker-entrypoint.sh /usr/local/bin/ -RUN chmod +x /usr/local/bin/docker-entrypoint.sh ENV PATH $PATH:/usr/local/bin:$GHOST_INSTALL/current/node_modules/.bin ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/Dockerfile.orig b/Dockerfile.orig new file mode 100644 index 0000000..22274d9 --- /dev/null +++ b/Dockerfile.orig @@ -0,0 +1,132 @@ +# https://docs.ghost.org/faq/node-versions/ +# https://github.com/nodejs/Release (looking for "LTS") +# https://github.com/TryGhost/Ghost/blob/v4.1.2/package.json#L38 +FROM node:18-bookworm + +# grab gosu for easy step-down from root +# https://github.com/tianon/gosu/releases +ENV GOSU_VERSION 1.16 +RUN set -eux; \ + # save list of currently installed packages for later so we can clean up + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update && apt-get upgrade -y; \ + apt-get install -y --no-install-recommends ca-certificates gnupg wget; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + \ + # verify the signature + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + gpgconf --kill all; \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + \ + # clean up fetch dependencies + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + \ + chmod +x /usr/local/bin/gosu; \ + # verify that the binary works + gosu --version; \ + gosu nobody true + +ENV NODE_ENV production + +# ENV GHOST_CLI_VERSION 1.24.1 +RUN set -eux; \ + npm install -g "ghost-cli@latest"; \ + npm cache clean --force + +ENV GHOST_INSTALL /var/lib/ghost +ENV GHOST_CONTENT /var/lib/ghost/content + +ARG GHOST_VERSION +ENV GHOST_VERSION $GHOST_VERSION + +RUN set -eux; \ + mkdir -p "$GHOST_INSTALL"; \ + chown node:node "$GHOST_INSTALL"; \ + \ + savedAptMark="$(apt-mark showmanual)"; \ + aptPurge=; \ + \ + installCmd='gosu node ghost install "$GHOST_VERSION" --db mysql --dbhost mysql --no-prompt --no-stack --no-setup --dir "$GHOST_INSTALL"'; \ + if ! eval "$installCmd"; then \ + aptPurge=1; \ + apt-get update && apt-get upgrade -y; \ + apt-get install -y --no-install-recommends g++ make python3; \ + eval "$installCmd"; \ + fi; \ + \ +# Tell Ghost to listen on all ips and not prompt for additional configuration + cd "$GHOST_INSTALL"; \ + gosu node ghost config --no-prompt --ip '::' --port 2368 --url 'http://localhost:2368'; \ + gosu node ghost config paths.contentPath "$GHOST_CONTENT"; \ + \ +# make a config.json symlink for NODE_ENV=development (and sanity check that it's correct) + gosu node ln -s config.production.json "$GHOST_INSTALL/config.development.json"; \ + readlink -f "$GHOST_INSTALL/config.development.json"; \ + \ +# need to save initial content for pre-seeding empty volumes + mv "$GHOST_CONTENT" "$GHOST_INSTALL/content.orig"; \ + mkdir -p "$GHOST_CONTENT"; \ + chown node:node "$GHOST_CONTENT"; \ + chmod 1777 "$GHOST_CONTENT"; \ + \ +# force install a few extra packages manually since they're "optional" dependencies +# (which means that if it fails to install, like on ARM/ppc64le/s390x, the failure will be silently ignored and thus turn into a runtime error instead) +# see https://github.com/TryGhost/Ghost/pull/7677 for more details + cd "$GHOST_INSTALL/current"; \ +# scrape the expected versions directly from Ghost/dependencies + packages="$(node -p ' \ + var ghost = require("./package.json"); \ + var transform = require("./node_modules/@tryghost/image-transform/package.json"); \ + [ \ + "sharp@" + transform.optionalDependencies["sharp"], \ + "sqlite3@" + ghost.optionalDependencies["sqlite3"], \ + ].join(" ") \ + ')"; \ + if echo "$packages" | grep 'undefined'; then exit 1; fi; \ + for package in $packages; do \ + installCmd='gosu node yarn add "$package" --force'; \ + if ! eval "$installCmd"; then \ + # must be some non-amd64 architecture pre-built binaries aren't published for, so let's install some build deps and do-it-all-over-again + aptPurge=1; \ + apt-get update && apt-get upgrade -y; \ + apt-get install -y --no-install-recommends g++ make python3; \ + case "$package" in \ + # TODO sharp@*) apt-get install -y --no-install-recommends libvips-dev ;; \ + sharp@*) apt-get install -y --no-install-recommends libvips-dev ;; \ + # sharp@*) echo >&2 "sorry: libvips 8.10 in Debian bullseye is not new enough (8.12.2+) for sharp 0.30 😞"; continue ;; \ + esac; \ + \ + eval "$installCmd --build-from-source"; \ + fi; \ + done; \ + \ + if [ -n "$aptPurge" ]; then \ + apt-mark showmanual | xargs apt-mark auto > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ + apt-get purge -y --auto-remove; \ + rm -rf /var/lib/apt/lists/*; \ + fi; \ + \ + gosu node yarn cache clean; \ + gosu node npm cache clean --force; \ + npm cache clean --force; \ + rm -rv /tmp/yarn* /tmp/v8* + +WORKDIR $GHOST_INSTALL +VOLUME $GHOST_CONTENT + +COPY --chmod=0777 docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh +ENV PATH $PATH:/usr/local/bin:$GHOST_INSTALL/current/node_modules/.bin +ENTRYPOINT ["docker-entrypoint.sh"] + +EXPOSE 2368 +CMD ["node", "current/index.js"] diff --git a/charts/Chart.yaml b/charts/Chart.yaml index 33bf3a9..9dda9b9 100644 --- a/charts/Chart.yaml +++ b/charts/Chart.yaml @@ -13,7 +13,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.1 +version: 1.1.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. diff --git a/charts/templates/05-ghost-statefulset.yaml b/charts/templates/05-ghost-statefulset.yaml index d2f5335..b7a1b68 100644 --- a/charts/templates/05-ghost-statefulset.yaml +++ b/charts/templates/05-ghost-statefulset.yaml @@ -23,6 +23,7 @@ spec: value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.ghostOnKubernetes.ghostOnKubernetes.image.repository }}:{{ .Values.ghostOnKubernetes.ghostOnKubernetes.image.tag }} name: {{ include "ghost-on-kubernetes.fullname" . }}-ghost + imagePullPolicy: {{ .Values.ghostOnKubernetes.ghostOnKubernetes.image.pullPolicy }} ports: - containerPort: {{ .Values.ghostConfigProd.port }} name: http diff --git a/charts/values.yaml b/charts/values.yaml index 5e50d6a..60a87fc 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -23,6 +23,7 @@ ghostOnKubernetes: image: repository: ghcr.io/sredevopsdev/ghost-on-kubernetes tag: main + imagePullPolicy: Always replicas: 1 diff --git a/docker-compose.yml b/docker-compose.yml index 70605c6..9cdfdd6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,12 +4,12 @@ version: "3" services: ghost: image: ghcr.io/sredevopsdev/ghost-on-kubernetes:main - command: ["gosu", "node", "node", "current/index.js"] + command: ["node", "current/index.js"] platform: linux/amd64 container_name: ghost volumes: - ./content:/var/lib/ghost/content - - ./ghost-local.db:/var/lib/ghost/content/data/ghost-local.db + - ./ghost-local2.db:/var/lib/ghost/content/data/ghost-local.db environment: NODE_ENV: development database__client: sqlite3 diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh old mode 100755 new mode 100644 index f4614d4..7354a13 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -4,7 +4,7 @@ set -e # allow the container to be started with `--user` if [[ "$*" == node*current/index.js* ]] && [ "$(id -u)" = '0' ]; then find "$GHOST_CONTENT" \! -user node -exec chown node '{}' + - exec gosu node "$BASH_SOURCE" "$@" + exec "$BASH_SOURCE" "$@" fi if [[ "$*" == node*current/index.js* ]]; then diff --git a/docker-entrypoint.sh.bak b/docker-entrypoint.sh.bak new file mode 100755 index 0000000..f4614d4 --- /dev/null +++ b/docker-entrypoint.sh.bak @@ -0,0 +1,22 @@ +#!/bin/bash +set -e + +# allow the container to be started with `--user` +if [[ "$*" == node*current/index.js* ]] && [ "$(id -u)" = '0' ]; then + find "$GHOST_CONTENT" \! -user node -exec chown node '{}' + + exec gosu node "$BASH_SOURCE" "$@" +fi + +if [[ "$*" == node*current/index.js* ]]; then + baseDir="$GHOST_INSTALL/content.orig" + for src in "$baseDir"/*/ "$baseDir"/themes/*; do + src="${src%/}" + target="$GHOST_CONTENT/${src#$baseDir/}" + mkdir -p "$(dirname "$target")" + if [ ! -e "$target" ]; then + tar -cC "$(dirname "$src")" "$(basename "$src")" | tar -xC "$(dirname "$target")" + fi + done +fi + +exec "$@"