mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
- Removes version numbers from infra components (elixir/relay) - Removes version bumping from Rust workspace members that don't get published - Splits release publishing into `gateway-`, `headless-client-`, and `gui-client-` - Removes auto-deploying new infrastructure when a release is published. Use the Deploy Production workflow instead. Fixes #4397
324 lines
9.7 KiB
Docker
324 lines
9.7 KiB
Docker
ARG ALPINE_VERSION="3.19.1"
|
|
ARG ERLANG_VERSION="26.2.2"
|
|
ARG ERLANG_DOWNLOAD_SHA256="d537ff4ac5d8c1cb507aedaf7198fc1f155ea8aa65a8d83edb35c2802763cc28"
|
|
ARG ELIXIR_VERSION="1.16.2"
|
|
ARG ELIXIR_DOWNLOAD_SHA256="f53d06f3e4041c50e65b750e5d56fec9cc7c6a44510786937c6a5bb0666a7207"
|
|
|
|
FROM alpine:${ALPINE_VERSION} as base
|
|
|
|
# Important! Update this no-op ENV variable when this Dockerfile
|
|
# is updated with the current date. It will force refresh of all
|
|
# of the base images and things like `apk add` won't be using
|
|
# old cached versions when the Dockerfile is built.
|
|
ENV REFRESHED_AT=2023-10-05 \
|
|
LANG=C.UTF-8 \
|
|
HOME=/app/ \
|
|
TERM=xterm
|
|
|
|
# Add tagged repos as well as the edge repo so that we can selectively install edge packages
|
|
ARG ALPINE_VERSION
|
|
RUN set -xe \
|
|
&& ALPINE_MINOR_VERSION=$(echo ${ALPINE_VERSION} | cut -d'.' -f1,2) \
|
|
&& echo "@main http://dl-cdn.alpinelinux.org/alpine/v${ALPINE_MINOR_VERSION}/main" >> /etc/apk/repositories \
|
|
&& echo "@community http://dl-cdn.alpinelinux.org/alpine/v${ALPINE_MINOR_VERSION}/community" >> /etc/apk/repositories \
|
|
&& echo "@edge http://dl-cdn.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories
|
|
|
|
RUN set -xe \
|
|
# Upgrade Alpine and base packages
|
|
&& apk --no-cache --update-cache --available upgrade \
|
|
# Install bash, Erlang/OTP and Elixir runtime dependencies
|
|
&& apk add --no-cache --update-cache \
|
|
bash \
|
|
libstdc++ \
|
|
ca-certificates \
|
|
ncurses \
|
|
openssl \
|
|
pcre \
|
|
unixodbc \
|
|
zlib \
|
|
# Update ca certificates
|
|
&& update-ca-certificates --fresh
|
|
|
|
FROM base AS build_erlang
|
|
|
|
# Install bash and Erlang/OTP deps
|
|
RUN set -xe \
|
|
&& apk add --no-cache --update-cache --virtual .fetch-deps \
|
|
curl \
|
|
libgcc \
|
|
lksctp-tools \
|
|
zlib-dev
|
|
|
|
# Install Erlang/OTP build deps
|
|
RUN set -xe \
|
|
&& apk add --no-cache --virtual .build-deps \
|
|
dpkg-dev \
|
|
dpkg \
|
|
gcc \
|
|
g++ \
|
|
libc-dev \
|
|
linux-headers \
|
|
make \
|
|
autoconf \
|
|
ncurses-dev \
|
|
openssl-dev \
|
|
unixodbc-dev \
|
|
lksctp-tools-dev \
|
|
tar
|
|
|
|
# Download OTP
|
|
ARG ERLANG_VERSION
|
|
ARG ERLANG_DOWNLOAD_SHA256
|
|
WORKDIR /tmp/erlang-build
|
|
RUN set -xe \
|
|
&& curl -fSL -o otp-src.tar.gz "https://github.com/erlang/otp/releases/download/OTP-${ERLANG_VERSION}/otp_src_${ERLANG_VERSION}.tar.gz" \
|
|
&& tar -xzf otp-src.tar.gz -C /tmp/erlang-build --strip-components=1 \
|
|
# && sha256sum otp-src.tar.gz && exit 1 \
|
|
&& echo "${ERLANG_DOWNLOAD_SHA256} otp-src.tar.gz" | sha256sum -c -
|
|
|
|
# Configure & Build
|
|
RUN set -xe \
|
|
&& export ERL_TOP=/tmp/erlang-build \
|
|
&& export CPPFLAGS="-D_BSD_SOURCE $CPPFLAGS" \
|
|
&& export gnuArch="$(dpkg-architecture --query DEB_HOST_GNU_TYPE)" \
|
|
&& ./configure \
|
|
--build="$gnuArch" \
|
|
--prefix=/usr/local \
|
|
--sysconfdir=/etc \
|
|
--mandir=/usr/share/man \
|
|
--infodir=/usr/share/info \
|
|
--without-javac \
|
|
--without-jinterface \
|
|
--without-wx \
|
|
--without-debugger \
|
|
--without-observer \
|
|
--without-cosEvent \
|
|
--without-cosEventDomain \
|
|
--without-cosFileTransfer \
|
|
--without-cosNotification \
|
|
--without-cosProperty \
|
|
--without-cosTime \
|
|
--without-cosTransactions \
|
|
--without-et \
|
|
--without-gs \
|
|
--without-ic \
|
|
--without-megaco \
|
|
--without-orber \
|
|
--without-percept \
|
|
--without-odbc \
|
|
--without-typer \
|
|
--enable-threads \
|
|
--enable-shared-zlib \
|
|
--enable-dynamic-ssl-lib \
|
|
--enable-ssl=dynamic-ssl-lib \
|
|
$(if [[ "${TARGET}" != *"amd64"* ]]; then echo "--disable-jit"; fi) \
|
|
&& $( \
|
|
if [[ "${TARGETARCH}" == *"amd64"* ]]; \
|
|
then export CFLAGS="-g -O2 -fstack-clash-protection -fcf-protection=full"; \
|
|
else export CFLAGS="-g -O2 -fstack-clash-protection"; fi \
|
|
) \
|
|
&& make -j$(getconf _NPROCESSORS_ONLN)
|
|
|
|
# Install to temporary location, strip the install, install runtime deps and copy to the final location
|
|
RUN set -xe \
|
|
&& make DESTDIR=/tmp install \
|
|
&& cd /tmp && rm -rf /tmp/erlang-build \
|
|
&& find /tmp/usr/local -regex '/tmp/usr/local/lib/erlang/\(lib/\|erts-\).*/\(man\|doc\|obj\|c_src\|emacs\|info\|examples\)' | xargs rm -rf \
|
|
&& find /tmp/usr/local -name src | xargs -r find | grep -v '\.hrl$' | xargs rm -v || true \
|
|
&& find /tmp/usr/local -name src | xargs -r find | xargs rmdir -vp || true \
|
|
# Strip install to reduce size
|
|
&& scanelf --nobanner -E ET_EXEC -BF '%F' --recursive /tmp/usr/local | xargs -r strip --strip-all \
|
|
&& scanelf --nobanner -E ET_DYN -BF '%F' --recursive /tmp/usr/local | xargs -r strip --strip-unneeded \
|
|
&& runDeps="$( \
|
|
scanelf --needed --nobanner --format '%n#p' --recursive /tmp/usr/local \
|
|
| tr ',' '\n' \
|
|
| sort -u \
|
|
| awk 'system("[ -e /tmp/usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
|
|
)" \
|
|
&& ln -s /tmp/usr/local/lib/erlang /usr/local/lib/erlang \
|
|
&& /tmp/usr/local/bin/erl -eval "beam_lib:strip_release('/tmp/usr/local/lib/erlang/lib')" -s init stop > /dev/null \
|
|
&& (/usr/bin/strip /tmp/usr/local/lib/erlang/erts-*/bin/* || true) \
|
|
&& apk add --no-cache --virtual .erlang-runtime-deps $runDeps lksctp-tools ca-certificates
|
|
|
|
# Cleanup after Erlang install
|
|
RUN set -xe \
|
|
&& apk del .fetch-deps .build-deps \
|
|
&& rm -rf /var/cache/apk/*
|
|
|
|
WORKDIR ${HOME}
|
|
|
|
CMD ["erl"]
|
|
|
|
FROM base AS build_elixir
|
|
|
|
# Install Elixir build deps
|
|
RUN set -xe \
|
|
&& apk add --no-cache --virtual .build-deps \
|
|
make \
|
|
curl \
|
|
tar \
|
|
git
|
|
|
|
# Download Elixir
|
|
ARG ELIXIR_VERSION
|
|
ARG ELIXIR_DOWNLOAD_SHA256
|
|
WORKDIR /tmp/elixir-build
|
|
RUN set -xe \
|
|
&& curl -fSL -o elixir-src.tar.gz "https://github.com/elixir-lang/elixir/archive/refs/tags/v${ELIXIR_VERSION}.tar.gz" \
|
|
&& mkdir -p /tmp/usr/local/src/elixir \
|
|
&& tar -xzC /tmp/usr/local/src/elixir --strip-components=1 -f elixir-src.tar.gz \
|
|
# && sha256sum elixir-src.tar.gz && exit 1 \
|
|
&& echo "${ELIXIR_DOWNLOAD_SHA256} elixir-src.tar.gz" | sha256sum -c - \
|
|
&& rm elixir-src.tar.gz
|
|
|
|
COPY --from=build_erlang /tmp/usr/local /usr/local
|
|
|
|
# Compile Elixir
|
|
RUN set -xe \
|
|
&& cd /tmp/usr/local/src/elixir \
|
|
&& make DESTDIR=/tmp install clean \
|
|
&& find /tmp/usr/local/src/elixir/ -type f -not -regex "/tmp/usr/local/src/elixir/lib/[^\/]*/lib.*" -exec rm -rf {} + \
|
|
&& find /tmp/usr/local/src/elixir/ -type d -depth -empty -delete \
|
|
&& rm -rf /tmp/elixir-build \
|
|
&& apk del .build-deps
|
|
|
|
# Cleanup apk cache
|
|
RUN rm -rf /var/cache/apk/*
|
|
|
|
WORKDIR ${HOME}
|
|
|
|
CMD ["iex"]
|
|
|
|
FROM base as elixir
|
|
|
|
WORKDIR ${HOME}
|
|
|
|
# Copy Erlang/OTP and Elixir installations
|
|
COPY --from=build_erlang /tmp/usr/local /usr/local
|
|
COPY --from=build_elixir /tmp/usr/local /usr/local
|
|
|
|
# Install hex + rebar
|
|
RUN set -xe \
|
|
&& mix local.hex --force \
|
|
&& mix local.rebar --force
|
|
|
|
CMD ["bash"]
|
|
|
|
FROM elixir as compiler
|
|
|
|
WORKDIR /app
|
|
|
|
# Install build deps
|
|
RUN apk add --update --no-cache \
|
|
make \
|
|
git \
|
|
nodejs \
|
|
npm \
|
|
build-base
|
|
|
|
# Add pnpm
|
|
RUN npm i -g pnpm
|
|
|
|
# Copy only the files needed to fetch the dependencies,
|
|
# to leverage Docker layer cache for them
|
|
COPY mix.exs mix.lock ./
|
|
COPY apps/domain/mix.exs ./apps/domain/mix.exs
|
|
COPY apps/web/mix.exs ./apps/web/mix.exs
|
|
COPY apps/api/mix.exs ./apps/api/mix.exs
|
|
COPY config config
|
|
COPY sha.exs .
|
|
|
|
# Fetch and compile the dependencies
|
|
ARG MIX_ENV="prod"
|
|
RUN mix deps.get --only ${MIX_ENV}
|
|
RUN mix deps.compile --skip-umbrella-children
|
|
|
|
# Copy the files needed to fetch asset deps
|
|
COPY apps/web/assets/package.json ./apps/web/assets/
|
|
COPY apps/web/assets/pnpm-lock.yaml ./apps/web/assets/
|
|
|
|
# Install npm deps and assets pipeline
|
|
RUN cd apps/web \
|
|
&& mix assets.setup
|
|
|
|
# Tailwind needs assets and app directories to look for used classes,
|
|
# so we can't optimize further at this stage
|
|
COPY priv priv
|
|
COPY apps apps
|
|
|
|
# Install pipeline and compile assets for Web app
|
|
RUN cd apps/web \
|
|
&& mix assets.deploy
|
|
|
|
# Copy the rest of the application files and compile them
|
|
RUN mix compile
|
|
|
|
FROM elixir as builder
|
|
|
|
# Install build deps
|
|
RUN apk add --update --no-cache \
|
|
git
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy the compiled dependencies from the previous step
|
|
# leveraging the possible layer cache
|
|
COPY --from=compiler /app /app
|
|
|
|
COPY rel rel
|
|
|
|
ARG APPLICATION_NAME
|
|
ARG MIX_ENV="prod"
|
|
RUN mix release ${APPLICATION_NAME}
|
|
|
|
# start a new build stage so that the final image will only contain
|
|
# the compiled release and other runtime necessities
|
|
|
|
FROM base AS runtime
|
|
|
|
RUN set -xe \
|
|
# Install Firezone runtime deps
|
|
&& apk add --no-cache --update-cache \
|
|
curl \
|
|
tini
|
|
|
|
# Create default user and home directory, set owner to default
|
|
RUN set -xe \
|
|
&& mkdir -p /app \
|
|
&& adduser -s /bin/sh -u 1001 -G root -h /app -S -D default \
|
|
&& chown -R 1001:0 /app
|
|
|
|
WORKDIR /app
|
|
|
|
ARG APPLICATION_NAME
|
|
ENV APPLICATION_NAME=$APPLICATION_NAME
|
|
|
|
# Only copy the final release from the build stage
|
|
ARG MIX_ENV="prod"
|
|
COPY --from=builder /app/_build/${MIX_ENV}/rel/${APPLICATION_NAME} ./
|
|
|
|
# Populate the SHA pointing to this build
|
|
ARG GIT_SHA
|
|
RUN echo ${GIT_SHA} > ./GIT_SHA
|
|
|
|
# Change user to "default" to limit runtime privileges
|
|
USER default
|
|
|
|
# This is critical when you run this container in containers where
|
|
# running process would get a PID 1.
|
|
#
|
|
# BEAM is usually not started by itself but via some shell script (eg. the one generated by
|
|
# Elixir 1.9 releases or Distillery) and this script is not designed to become an init script
|
|
# for a Docker container and it DOES NOT reap zombie processes.
|
|
#
|
|
# So whenever a child process runs and terminates inside the same container it would result in memory and
|
|
# PID leak to a point where host VM would get unresponsive.
|
|
#
|
|
# A good example why you would start a process within container is the `ping` command for liveness probes.
|
|
# It starts a VM to issue an RPC command to a live node and then terminates, but would never be reaped.
|
|
#
|
|
# Tini would become an entrypoin script and would take care of zombie reaping no matter how you start the VM.
|
|
ENTRYPOINT ["/sbin/tini", "--"]
|
|
|
|
CMD bin/server
|