Container: Add METHOD to build container using packages (#3983)

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
This commit is contained in:
Saarko 2022-11-18 21:55:17 +01:00 committed by Badlop
parent c71887db43
commit d15cf994a2
5 changed files with 274 additions and 114 deletions

View File

@ -1,56 +1,103 @@
FROM alpine:3.17 AS build
ARG VERSION=master
#' 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'
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
################################################################################
#' 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
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 cp -p 'tools/captcha'*'.sh' "/opt/ejabberd-$VERSION/lib"
RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib
RUN [ ! -d .ejabberd-modules ] || cp -r .ejabberd-modules /opt/ejabberd/
RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \
&& find "$HOME-$VERSION/releases" -name 'COOKIE' -delete
RUN find "/opt/ejabberd-$VERSION/bin" -name 'ejabberd' -delete \
&& find "/opt/ejabberd-$VERSION/releases" -name 'COOKIE' -delete
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"
RUN export PEM=/opt/ejabberd/conf/server.pem \
&& curl -o "/opt/ejabberd/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \
################################################################################
#' 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 \
@ -58,63 +105,81 @@ RUN export PEM=/opt/ejabberd/conf/server.pem \
-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"
-subj "/CN=localhost"
FROM alpine:3.17
ENV HOME=/opt/ejabberd
ARG VERSION=master
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/*
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/*
ARG UID
RUN chown -R $UID:$UID $HOME
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
################################################################################
#' 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
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
################################################################################
#' 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"]

View File

@ -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/**'
@ -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:
@ -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

View File

@ -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:

View File

@ -117,7 +117,8 @@ odbc_tar="$odbc_dir.tar.gz"
rel_tar="$rel_name-$mix_vsn.tar.gz"
ct_jobs=$(nproc)
src_dir="$root_dir/src"
platform='x86_64-pc-linux-gnu'
platform=$(gcc -dumpmachine)
platform_libc=$(echo $platform | sed "s/\-/\ /g" | awk '{print $NF}')
targets='x86_64-linux-gnu aarch64-linux-gnu'
build_start=$(date '+%F %T')
have_current_deps='false'
@ -257,10 +258,37 @@ create_common_config()
CT_CC_LANG_CXX=y
CT_ARCH_64=y
CT_KERNEL_LINUX=y
CT_LOG_PROGRESS_BAR=n
EOF
}
#.
#' Create Crosstool-NG configuration file for glibc.
create_gnu_config()
{
local file="$1"
create_common_config "$file"
cat >>"$file" <<-'EOF'
CT_LINUX_V_3_16=y
CT_GLIBC_V_2_19=y
CT_GLIBC_KERNEL_VERSION_NONE=y
CT_LOG_PROGRESS_BAR=n
EOF
}
#.
#' Create Crosstool-NG configuration file for musl.
create_musl_config()
{
local file="$1"
create_common_config "$file"
cat >>"$file" <<-'EOF'
CT_EXPERIMENTAL=y
CT_LIBC_MUSL=y
CT_MUSL_V_1_2_2=y
EOF
}
#.
@ -269,8 +297,10 @@ create_common_config()
create_x64_config()
{
local file="$1"
local libc="$2"
create_common_config "$file"
create_${libc}_config "$file"
cat >>"$file" <<-'EOF'
CT_ARCH_X86=y
@ -282,8 +312,10 @@ create_x64_config()
create_arm64_config()
{
local file="$1"
local libc="$2"
create_common_config "$file"
create_${libc}_config "$file"
cat >>"$file" <<-'EOF'
CT_ARCH_ARM=y
@ -319,6 +351,13 @@ add_otp_path()
if [ "$mode" = 'native' ]
then native_otp_bin="$prefix/bin"
# for github runners to build for non-native systems
# https://github.com/marketplace/actions/setup-erlang-otp-with-optional-elixir-and-mix-and-or-rebar3
elif [ ! -z "${INSTALL_DIR_FOR_OTP-}" ] && [ ! -z "${INSTALL_DIR_FOR_ELIXIR-}" ]
then
native_otp_bin="$INSTALL_DIR_FOR_OTP/bin"
native_elixir_bin="$INSTALL_DIR_FOR_ELIXIR/bin"
export PATH="$native_elixir_bin:$PATH"
fi
export PATH="$native_otp_bin:$PATH"
}
@ -437,8 +476,9 @@ build_toolchain()
{
local target="$1"
local prefix="$2"
local libc="$3"
local arch=$(arch_name "$target")
if [ -d "$prefix" ]
then
info "Using existing toolchain in $prefix ..."
@ -458,9 +498,9 @@ build_toolchain()
cd "$OLDPWD"
fi
info "Building toolchain for $arch ..."
info "Building toolchain for $arch-$libc ..."
cd "$root_dir"
create_${arch}_config 'defconfig'
create_${arch}_config 'defconfig' "$libc"
ct-ng defconfig
sed -i -e "s|^CT_ZLIB_VERSION=.*|CT_ZLIB_VERSION=\"$zlib_vsn\"|" .config
sed -i -e 's|^CT_ZLIB_MIRRORS=.*|CT_ZLIB_MIRRORS="https://github.com/madler/zlib/releases/download/v${CT_ZLIB_VERSION} https://www.zlib.net/ https://www.zlib.net/fossils/"|' .config
@ -477,6 +517,7 @@ build_deps()
local mode="$1"
local target="$2"
local prefix="$3"
local libc="$4"
local arch="$(arch_name "$target")"
local target_src_dir="$prefix/src"
local saved_path="$PATH"
@ -507,7 +548,7 @@ build_deps()
tar -xJf "$src_dir/$pam_tar"
cd "$OLDPWD"
info "Building Termcap $termcap_vsn for $arch ..."
info "Building Termcap $termcap_vsn for $arch-$libc ..."
cd "$target_src_dir/$termcap_dir"
$configure --prefix="$prefix"
cat >'config.h' <<-'EOF'
@ -535,24 +576,24 @@ build_deps()
make install
cd "$OLDPWD"
info "Building zlib $zlib_vsn for $arch ..."
info "Building zlib $zlib_vsn for $arch-$libc ..."
cd "$target_src_dir/$zlib_dir"
CFLAGS="$CFLAGS -O3 -fPIC" ./configure --prefix="$prefix" --static
make
make install
cd "$OLDPWD"
info "Building OpenSSL $ssl_vsn for $arch ..."
info "Building OpenSSL $ssl_vsn for $arch-$libc ..."
cd "$target_src_dir/$ssl_dir"
CFLAGS="$CFLAGS -O3 -fPIC" ./Configure no-shared no-ui-console \
--prefix="$prefix" \
--openssldir="$prefix" \
"linux-${target%-linux-gnu}"
"linux-${target%-linux-*}"
make build_libs
make install_dev
cd "$OLDPWD"
info "Building Expat $expat_vsn for $arch ..."
info "Building Expat $expat_vsn for $arch-$libc ..."
cd "$target_src_dir/$expat_dir"
$configure --prefix="$prefix" --enable-static --disable-shared \
--without-docbook \
@ -561,7 +602,7 @@ build_deps()
make install
cd "$OLDPWD"
info "Building LibYAML $yaml_vsn for $arch ..."
info "Building LibYAML $yaml_vsn for $arch-$libc ..."
cd "$target_src_dir/$yaml_dir"
$configure --prefix="$prefix" --enable-static --disable-shared \
CFLAGS="$CFLAGS -O3 -fPIC"
@ -569,7 +610,7 @@ build_deps()
make install
cd "$OLDPWD"
info "Building SQLite $sqlite_vsn for $arch ..."
info "Building SQLite $sqlite_vsn for $arch-$libc ..."
cd "$target_src_dir/$sqlite_dir"
$configure --prefix="$prefix" --enable-static --disable-shared \
CFLAGS="$CFLAGS -O3 -fPIC"
@ -577,7 +618,7 @@ build_deps()
make install
cd "$OLDPWD"
info "Building ODBC $odbc_vsn for $arch ..."
info "Building ODBC $odbc_vsn for $arch-$libc ..."
cd "$target_src_dir/$odbc_dir"
$configure --prefix="$prefix" --enable-static --disable-shared \
CFLAGS="$CFLAGS -O3 -fPIC"
@ -594,7 +635,7 @@ build_deps()
make install
cd "$OLDPWD"
info "Building libpng $png_vsn for $arch ..."
info "Building libpng $png_vsn for $arch-$libc ..."
cd "$target_src_dir/$png_dir"
$configure --prefix="$prefix" --enable-static --disable-shared \
CFLAGS="$CFLAGS -O3 -fPIC"
@ -602,7 +643,7 @@ build_deps()
make install
cd "$OLDPWD"
info "Building JPEG $jpeg_vsn for $arch ..."
info "Building JPEG $jpeg_vsn for $arch-$libc ..."
cd "$target_src_dir/$jpeg_dir"
$configure --prefix="$prefix" --enable-static --disable-shared \
CFLAGS="$CFLAGS -O3 -fPIC"
@ -610,7 +651,7 @@ build_deps()
make install
cd "$OLDPWD"
info "Building WebP $webp_vsn for $arch ..."
info "Building WebP $webp_vsn for $arch-$libc ..."
cd "$target_src_dir/$webp_dir"
$configure --prefix="$prefix" --enable-static --disable-shared \
CFLAGS="$CFLAGS -O3 -fPIC"
@ -618,7 +659,7 @@ build_deps()
make install
cd "$OLDPWD"
info "Building LibGD $gd_vsn for $arch ..."
info "Building LibGD $gd_vsn for $arch-$libc ..."
cd "$target_src_dir/$gd_dir"
$configure --prefix="$prefix" --enable-static --disable-shared \
--with-zlib="$prefix" \
@ -640,7 +681,7 @@ build_deps()
make install
cd "$OLDPWD"
info "Building Erlang/OTP $otp_vsn for $arch ..."
info "Building Erlang/OTP $otp_vsn for $arch-$libc ..."
if [ "$mode" = 'cross' ]
then
add_otp_path "$mode" "$prefix"
@ -669,7 +710,7 @@ build_deps()
fi
cd "$OLDPWD"
info "Building Elixir $elixir_vsn for $arch ..."
info "Building Elixir $elixir_vsn for $arch-$libc ..."
cd "$target_src_dir/$elixir_dir"
make install PREFIX="$prefix"
cd "$OLDPWD"
@ -684,11 +725,12 @@ build_rel()
local mode="$1"
local target="$2"
local prefix="$3"
local libc="$4"
local arch="$(arch_name "$target")"
local rel_dir="$PWD/_build/prod"
local target_data_dir="$prefix/$rel_name"
local target_dst_dir="$prefix/$rel_name-$rel_vsn"
local target_dst_tar="$rel_name-$rel_vsn-linux-$arch.tar.gz"
local target_dst_tar="$rel_name-$rel_vsn-linux-$libc-$arch.tar.gz"
local saved_path="$PATH"
#
@ -723,7 +765,7 @@ build_rel()
fi
if [ $have_current_deps = false ]
then build_deps "$mode" "$target" "$prefix"
then build_deps "$mode" "$target" "$prefix" "$libc"
fi
add_otp_path "$mode" "$prefix"
@ -738,7 +780,7 @@ build_rel()
info "Removing old $rel_name builds"
rm -rf '_build' 'deps'
info "Building $rel_name $rel_vsn for $arch ..."
info "Building $rel_name $rel_vsn for $arch-$libc ..."
./autogen.sh
eimp_cflags='-fcommon'
eimp_libs='-lwebp -ljpeg -lpng -lz -lm'
@ -782,7 +824,7 @@ build_rel()
unset host_alias build_alias
fi
info "Putting together $rel_name $rel_vsn archive for $arch ..."
info "Putting together $rel_name $rel_vsn archive for $arch-$libc ..."
mkdir "$target_dst_dir"
tar -C "$target_dst_dir" -xzf "$rel_dir/$rel_tar"
create_data_dir "$target_dst_dir" "$target_data_dir"
@ -870,15 +912,16 @@ export LC_ALL='C.UTF-8' # Elixir insists on a UTF-8 environment.
for target in $targets
do
prefix="$build_dir/$(arch_name "$target")"
libc="$(echo $target | sed "s/\-/\ /g" | awk '{print $NF}')"
prefix="$build_dir/$(arch_name "$target")-$libc"
toolchain_dir="$ct_prefix_dir/$target"
if [ "$(uname -m)-linux-gnu" = "$target" ]
if [ "$(uname -m)-linux-$platform_libc" = "$target" ]
then mode='native'
else mode='cross'
fi
build_toolchain "$target" "$toolchain_dir"
build_rel "$mode" "$target" "$prefix"
build_toolchain "$target" "$toolchain_dir" "$libc"
build_rel "$mode" "$target" "$prefix" "$libc"
done
save_built_dep_vsns

View File

@ -80,7 +80,7 @@ create_help_file()
local file="$1"
cat >"$file" <<-EOF
This is the $rel_name $rel_vsn-$iteration installer for linux-$arch
This is the $rel_name $rel_vsn-$iteration installer for linux-gnu-$arch
Visit:
$home_url
@ -354,7 +354,7 @@ create_setup_script()
for arch in $architectures
do
tar_name="$rel_name-$rel_vsn-linux-$arch.tar.gz"
tar_name="$rel_name-$rel_vsn-linux-gnu-$arch.tar.gz"
installer_name="$rel_name-$rel_vsn-$iteration-linux-$arch.run"
test -e "$tar_name" || tools/make-binaries