Skip to content

Commit

Permalink
Container: Add METHOD to build container using packages (processone#3983
Browse files Browse the repository at this point in the history
)

make-*: include musl build in make-binaries

Ctr actions: use github runners to provide bootstrap erlang

- adjust make-binaries script to use github runners' installed erlang
  for bootstrapping
- this reduces the need to build an unnecessary toolchain for glibc
  based binaries
  • Loading branch information
Saarko authored and badlop committed Mar 28, 2023
1 parent c71887d commit d15cf99
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 120 deletions.
245 changes: 155 additions & 90 deletions .github/container/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,120 +1,185 @@
FROM alpine:3.17 AS build
ARG VERSION=master

RUN apk upgrade --update musl \
&& apk add \
autoconf \
automake \
bash \
build-base \
curl \
elixir \
erlang-odbc \
erlang-reltool \
expat-dev \
file \
gd-dev \
git \
jpeg-dev \
libpng-dev \
libwebp-dev \
linux-pam-dev \
openssl \
openssl-dev \
sqlite-dev \
yaml-dev \
zlib-dev
#' Define default build variables
ARG ALPINE_VSN='3.17'
ARG UID='9000'
ARG USER='ejabberd'
ARG HOME="opt/$USER"
ARG METHOD='direct'
ARG BUILD_DIR="/$USER"
ARG VERSION='master'

################################################################################
#' METHOD='direct' - build and install ejabberd directly from source
FROM alpine:${ALPINE_VSN} AS direct

RUN apk -U add --no-cache \
autoconf \
automake \
bash \
build-base \
curl \
elixir \
erlang-odbc \
erlang-reltool \
expat-dev \
file \
gd-dev \
git \
jpeg-dev \
libpng-dev \
libwebp-dev \
linux-pam-dev \
openssl-dev \
sqlite-dev \
yaml-dev \
zlib-dev

RUN mix local.hex --force \
&& mix local.rebar --force

COPY . ./ejabberd

WORKDIR ejabberd
ARG BUILD_DIR
COPY / $BUILD_DIR/

WORKDIR $BUILD_DIR
RUN mv .github/container/ejabberdctl.template . \
&& ./autogen.sh \
&& ./configure --with-rebar=mix --enable-all \
&& make deps \
&& make rel

RUN cp -r _build/prod/rel/ejabberd/ /opt/ejabberd-$VERSION \
&& mkdir -p /opt/ejabberd \
&& mv /opt/ejabberd-$VERSION/conf /opt/ejabberd/conf

RUN cp -p 'tools/captcha'*'.sh' "/opt/ejabberd-$VERSION/lib"
WORKDIR /rootfs
ARG VERSION
ARG HOME
RUN mkdir -p $HOME $HOME-$VERSION \
&& cp -r $BUILD_DIR/_build/prod/rel/ejabberd/* $HOME-$VERSION \
&& mv $HOME-$VERSION/conf $HOME/conf

RUN [ ! -d .ejabberd-modules ] || cp -r .ejabberd-modules /opt/ejabberd/
RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib

RUN find "/opt/ejabberd-$VERSION/bin" -name 'ejabberd' -delete \
&& find "/opt/ejabberd-$VERSION/releases" -name 'COOKIE' -delete
RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \
&& find "$HOME-$VERSION/releases" -name 'COOKIE' -delete

RUN export PEM=/opt/ejabberd/conf/server.pem \
&& curl -o "/opt/ejabberd/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \
RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \
&& sed -i '/^loglevel:/a \ \
\nca_file: /opt/ejabberd/conf/cacert.pem \
\ncertfiles: \
\n - /opt/ejabberd/conf/server.pem' "$HOME/conf/ejabberd.yml"

################################################################################
#' METHOD='package' - install ejabberd from binary tarball package
FROM alpine:${ALPINE_VSN} AS package
COPY tarballs/ejabberd-*-linux-musl-*.tar.gz /tmp/
WORKDIR /rootfs
ARG HOME
RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \
&& mkdir -p $home_root_dir \
&& ARCH=$(uname -m | sed -e 's/x86_64/x64/;s/aarch64/arm64/') \
&& tar -xzf /tmp/ejabberd-*-linux-musl-$ARCH.tar.gz -C $home_root_dir

################################################################################
#' Prepare ejabberd for runtime
FROM ${METHOD} AS ejabberd
RUN apk -U add --no-cache \
git \
libcap-utils \
openssl

WORKDIR /rootfs
ARG HOME
RUN mkdir -p usr/local/bin $HOME/conf $HOME/database $HOME/logs $HOME/upload

ARG BUILD_DIR
RUN if [ ! -d $HOME/.ejabberd-modules ]; \
then \
if [ -d $BUILD_DIR/.ejabberd-modules ]; \
then cp -r $BUILD_DIR/.ejabberd-modules $HOME; \
else git clone https://github.com/processone/ejabberd-contrib --depth 1 \
$HOME/.ejabberd-modules/sources/ejabberd-contrib; \
fi \
fi

RUN export PEM=$HOME/conf/server.pem \
&& openssl req -x509 \
-batch \
-nodes \
-newkey rsa:4096 \
-keyout $PEM \
-out $PEM \
-days 3650 \
-subj "/CN=localhost" \
&& sed -i '/^loglevel:/a \ \
\nca_file: /opt/ejabberd/conf/cacert.pem \
\ncertfiles: \
\n - /opt/ejabberd/conf/server.pem' "/opt/ejabberd/conf/ejabberd.yml"

FROM alpine:3.17
ENV HOME=/opt/ejabberd
ARG VERSION=master

RUN apk upgrade --update musl \
&& apk add \
expat \
freetds \
gd \
jpeg \
libgd \
libpng \
libstdc++ \
libwebp \
linux-pam \
ncurses-libs \
sqlite \
sqlite-libs \
tini \
unixodbc \
yaml \
zlib \
&& ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so \
&& rm -rf /var/cache/apk/*

COPY --from=build /opt /opt
RUN echo -e \
"#!/bin/sh \
\n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \
\nexport CONFIG_DIR=/opt/ejabberd/conf \
\nexport LOGS_DIR=/opt/ejabberd/logs \
\nexport SPOOL_DIR=/opt/ejabberd/database \
\nexec /opt/ejabberd-$VERSION/bin/ejabberdctl \"\$@\"" > /usr/local/bin/ejabberdctl \
&& chmod +x /usr/local/bin/ejabberdctl

RUN addgroup ejabberd -g 9000 \
&& adduser -s /bin/sh -D -G ejabberd ejabberd -u 9000 \
&& mkdir -p $HOME/conf $HOME/database $HOME/logs $HOME/upload \
&& chown -R ejabberd:ejabberd $HOME
-subj "/CN=localhost"

RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \
&& setcap 'cap_net_bind_service=+ep' $(find $home_root_dir -name beam.smp) \
&& echo -e \
"#!/bin/sh \
\n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \
\nexport CONFIG_DIR=/$HOME/conf \
\nexport LOGS_DIR=/$HOME/logs \
\nexport SPOOL_DIR=/$HOME/database \
\nexec /$(find $home_root_dir -name ejabberdctl) \"\$@\"" \
> usr/local/bin/ejabberdctl \
&& chmod +x usr/local/bin/*

ARG UID
RUN chown -R $UID:$UID $HOME

################################################################################
#' METHOD='package' - install runtime dependencies
FROM alpine:${ALPINE_VSN} AS runtime-package
RUN apk -U upgrade --available --no-cache \
&& apk add --no-cache \
libcap2 \
tini

################################################################################
#' METHOD='direct' - install runtime dependencies
FROM runtime-package AS runtime-direct
RUN apk add --no-cache \
expat \
freetds \
gd \
jpeg \
libgd \
libpng \
libstdc++ \
libwebp \
linux-pam \
ncurses-libs \
sqlite \
sqlite-libs \
unixodbc \
yaml \
zlib \
&& ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so

################################################################################
#' Finalize runtime environment
FROM runtime-${METHOD} AS runtime
ARG USER
ARG UID
ARG HOME
RUN addgroup $USER -g $UID \
&& adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER

################################################################################
#' Build together production image
FROM scratch AS prod
ARG USER
ARG HOME

COPY --from=runtime / /
COPY --from=ejabberd /rootfs /

HEALTHCHECK \
--interval=1m \
--timeout=5s \
--start-period=5s \
--retries=10 \
CMD /usr/local/bin/ejabberdctl status
CMD ejabberdctl status

WORKDIR $HOME
USER ejabberd
VOLUME ["$HOME"]
WORKDIR /$HOME
USER $USER
VOLUME ["/$HOME"]
EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443

ENTRYPOINT ["/sbin/tini","--","/usr/local/bin/ejabberdctl"]
CMD ["foreground"]
ENTRYPOINT ["/sbin/tini","--"]
CMD ["ejabberdctl", "foreground"]
42 changes: 41 additions & 1 deletion .github/workflows/container.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Container

on:
schedule:
- cron: '22 2 */6 * *' # every 6 days to avoid gha cache being evicted
push:
paths-ignore:
- '.devcontainer/**'
Expand All @@ -21,12 +23,49 @@ jobs:
permissions:
packages: write
steps:

- name: Check out repository code
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Cache build directory
uses: actions/cache@v3
with:
path: ~/build/
key: ${{runner.os}}-ctr-ct-ng-1.25.0

- name: Get erlang/OTP version for bootstrapping
run: |
echo "OTP_VSN=$(awk '/^otp_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV
echo "ELIXIR_VSN=$(awk '/^elixir_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV
- name: Install prerequisites
run: |
sudo apt-get -qq update
sudo apt-get -qq install makeself
# https://github.com/crosstool-ng/crosstool-ng/blob/master/testing/docker/ubuntu21.10/Dockerfile
sudo apt-get -qq install build-essential autoconf bison flex gawk
sudo apt-get -qq install help2man libncurses5-dev libtool libtool-bin
sudo apt-get -qq install python3-dev texinfo unzip
- name: Install erlang/OTP
uses: erlef/setup-beam@v1
with:
otp-version: ${{ env.OTP_VSN }}
elixir-version: ${{ env.ELIXIR_VSN }}
version-type: strict

- name: Build musl-libc based binary archives
run: |
sed -i "s|targets='.*'|targets='x86_64-linux-musl aarch64-linux-musl'|" tools/make-binaries
mv .github/container/ejabberdctl.template .
CHECK_DEPS=false tools/make-binaries
- name: Collect packages
run: |
mkdir tarballs
mv ejabberd-*.tar.gz tarballs
- name: Checkout ejabberd-contrib
uses: actions/checkout@v3
with:
Expand Down Expand Up @@ -64,6 +103,7 @@ jobs:
uses: docker/build-push-action@v4
with:
build-args: |
METHOD=package
VERSION=${{ steps.gitdescribe.outputs.ver }}
cache-from: type=gha
cache-to: type=gha,mode=max
Expand Down
12 changes: 12 additions & 0 deletions CONTAINER.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,18 @@ Example using environment variables (see full example [docker-compose.yml](https
Generating a Container Image
============================
> By default, `.github/container/Dockerfile` builds this container by directly compiling ejabberd,
> it is a fast and direct method.
>
> However, a problem with QEMU prevents building the container in QEMU using Erlang/OTP 25
> for the `arm64` architecture.
>
> Providing `build-args: METHOD=package` is an alternate method to build the container
> used by the Github Actions workflow that provides `amd64` and `arm64` container images.
> It first builds an ejabberd binary package, and later installs it in the image.
> That method avoids using QEMU, so it can build `arm64` container images, but is extremely
> slow the first time it's used, and consequently not recommended for general use.

This container image includes ejabberd as a standalone OTP release built using Elixir.

That OTP release is configured with:
Expand Down
Loading

0 comments on commit d15cf99

Please sign in to comment.