Publish erlang-builder and erlang-runtime as :latest, use them in prod and API test builds, auto-collect NIF libs, and sync CI to pull/push bases before eventhub build. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+23
-9
@@ -25,6 +25,29 @@ jobs:
|
|||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to registry
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
env:
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
bash scripts/retry-docker-build.sh 5 30 -- \
|
||||||
|
bash -c 'echo "${REGISTRY_PASSWORD}" | docker login git.sabilin.com -u "${REGISTRY_USER}" --password-stdin'
|
||||||
|
|
||||||
|
- name: Ensure Erlang base images (registry :latest)
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
FORCE=""
|
||||||
|
if CHANGED="$(git diff --name-only HEAD~1..HEAD 2>/dev/null || true)" \
|
||||||
|
&& echo "${CHANGED}" | grep -q '^docker/erlang/'; then
|
||||||
|
FORCE="--force-build"
|
||||||
|
fi
|
||||||
|
bash scripts/build-erlang-base-images.sh ${FORCE}
|
||||||
|
|
||||||
|
- name: Push Erlang base images
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
run: bash scripts/build-erlang-base-images.sh --push
|
||||||
|
|
||||||
- name: Build eventhub image
|
- name: Build eventhub image
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -56,15 +79,6 @@ jobs:
|
|||||||
- name: Run API tests (local CT)
|
- name: Run API tests (local CT)
|
||||||
run: docker run --rm eventhub-api-tests:latest
|
run: docker run --rm eventhub-api-tests:latest
|
||||||
|
|
||||||
- name: Login to registry
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
env:
|
|
||||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
|
||||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
bash scripts/retry-docker-build.sh 5 30 -- \
|
|
||||||
bash -c 'echo "${REGISTRY_PASSWORD}" | docker login git.sabilin.com -u "${REGISTRY_USER}" --password-stdin'
|
|
||||||
|
|
||||||
- name: Push tested eventhub image
|
- name: Push tested eventhub image
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
# ============================================================
|
# API common_test — только builder-стадия (полный OTP + компиляторы).
|
||||||
# Одноэтапный Dockerfile
|
ARG ERLANG_BUILDER_IMAGE=git.sabilin.com/eventhub/erlang-builder:latest
|
||||||
# ============================================================
|
FROM ${ERLANG_BUILDER_IMAGE}
|
||||||
FROM erlang:28-alpine
|
|
||||||
|
|
||||||
# Устанавливаем инструменты для сборки и runtime-зависимости
|
WORKDIR /app
|
||||||
RUN apk add \
|
|
||||||
make musl-dev \
|
|
||||||
rust cargo openssl-dev libsodium-dev
|
|
||||||
|
|
||||||
# Копируем конфигурацию и исходники
|
|
||||||
COPY rebar.config ./
|
COPY rebar.config ./
|
||||||
COPY include/ include/
|
COPY include/ include/
|
||||||
COPY src/ src/
|
COPY src/ src/
|
||||||
COPY test/api_*_SUITE.erl test/
|
COPY test/api_*_SUITE.erl test/
|
||||||
COPY test/api/ test/api/
|
COPY test/api/ test/api/
|
||||||
|
|
||||||
# Компилируем и запускаем тесты
|
|
||||||
RUN rebar3 compile
|
RUN rebar3 compile
|
||||||
|
|
||||||
CMD rebar3 ct --sname ci_api_test -v
|
CMD ["rebar3", "ct", "--sname", "ci_api_test", "-v"]
|
||||||
|
|||||||
+19
-22
@@ -1,12 +1,12 @@
|
|||||||
|
# EventHub production image (multi-stage).
|
||||||
|
# Требует erlang-builder / erlang-runtime — см. scripts/build-erlang-base-images.sh
|
||||||
|
ARG ERLANG_BUILDER_IMAGE=git.sabilin.com/eventhub/erlang-builder:latest
|
||||||
|
ARG ERLANG_RUNTIME_IMAGE=git.sabilin.com/eventhub/erlang-runtime:latest
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Этап 1: Сборка
|
# Этап 1: Сборка
|
||||||
# ============================================================
|
# ============================================================
|
||||||
FROM erlang:28-alpine AS builder
|
FROM ${ERLANG_BUILDER_IMAGE} AS builder
|
||||||
|
|
||||||
# RUN apk add --no-cache \
|
|
||||||
RUN apk add \
|
|
||||||
git curl make gcc musl-dev \
|
|
||||||
rust cargo openssl-dev libsodium-dev
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY rebar.config ./
|
COPY rebar.config ./
|
||||||
@@ -16,38 +16,35 @@ RUN rebar3 get-deps
|
|||||||
COPY include/ include/
|
COPY include/ include/
|
||||||
COPY src/ src/
|
COPY src/ src/
|
||||||
|
|
||||||
# Копируем sys.config из src/config/ в config/
|
|
||||||
COPY src/config/sys.config ./config/sys.config
|
COPY src/config/sys.config ./config/sys.config
|
||||||
COPY src/config/vm.args ./config/vm.args
|
COPY src/config/vm.args ./config/vm.args
|
||||||
|
|
||||||
|
COPY docker/scripts/collect-runtime-libs.sh /usr/local/bin/collect-runtime-libs.sh
|
||||||
|
RUN chmod +x /usr/local/bin/collect-runtime-libs.sh
|
||||||
|
|
||||||
RUN rebar3 as prod release
|
RUN rebar3 as prod release
|
||||||
RUN rebar3 as prod tar
|
RUN rebar3 as prod tar
|
||||||
RUN mkdir -p /app/release && \
|
RUN mkdir -p /app/release && \
|
||||||
tar -xzf _build/prod/rel/eventhub/eventhub-*.tar.gz -C /app/release
|
tar -xzf _build/prod/rel/eventhub/eventhub-*.tar.gz -C /app/release
|
||||||
|
|
||||||
|
# NIF (argon2 → libsodium/openssl): ldd + копия .so с builder (ABI = Alpine builder)
|
||||||
|
RUN collect-runtime-libs.sh /app/release /opt/runtime-libs
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Этап 2: Финальный образ
|
# Этап 2: Финальный образ
|
||||||
# ============================================================
|
# ============================================================
|
||||||
FROM alpine:3.20
|
FROM ${ERLANG_RUNTIME_IMAGE}
|
||||||
|
|
||||||
# RUN apk add --no-cache \
|
|
||||||
RUN apk add \
|
|
||||||
openssl libstdc++ libgcc ncurses-libs libsodium
|
|
||||||
COPY --from=builder /app/release /app
|
COPY --from=builder /app/release /app
|
||||||
COPY --from=builder /usr/lib/libcrypto.* /usr/lib/
|
# Доп. .so, если версия apk-openssl на runtime не совпала с builder (страховка)
|
||||||
COPY --from=builder /usr/lib/libssl.* /usr/lib/
|
COPY --from=builder /opt/runtime-libs/ /
|
||||||
|
|
||||||
RUN mkdir -p /app/data && chmod 777 /app/data
|
COPY docker/scripts/verify-native-libs.sh /usr/local/bin/verify-native-libs.sh
|
||||||
|
RUN chmod +x /usr/local/bin/verify-native-libs.sh \
|
||||||
|
&& verify-native-libs.sh /app
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
EXPOSE 8080 8081 8445 8446
|
EXPOSE 8080 8081 8445 8446
|
||||||
|
|
||||||
ENV PATH="/app/erts-16.3.1/bin:$PATH"
|
|
||||||
|
|
||||||
ENV RELX_REPLACE_OS_VARS=true
|
ENV RELX_REPLACE_OS_VARS=true
|
||||||
|
|
||||||
CMD /app/bin/eventhub foreground
|
CMD ["/app/bin/eventhub", "foreground"]
|
||||||
# COPY docker/entrypoint.sh /app/entrypoint.sh
|
|
||||||
# RUN chmod +x /app/entrypoint.sh
|
|
||||||
#
|
|
||||||
# ENTRYPOINT ["/app/entrypoint.sh"]
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# Базовые Erlang-образы из registry (:latest) или локальная сборка
|
||||||
|
bash scripts/build-erlang-base-images.sh
|
||||||
|
|
||||||
# Основной образ EventHub (единственный для нод)
|
# Основной образ EventHub (единственный для нод)
|
||||||
docker build -t eventhub:latest -f docker/Dockerfile .
|
docker build -t eventhub:latest -f docker/Dockerfile .
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# EventHub — builder: OTP 28 + toolchain для rebar3 release и CT.
|
||||||
|
# erlang:28-alpine ≈ Alpine 3.23 — версия должна совпадать с Dockerfile.runtime (ALPINE_VERSION).
|
||||||
|
# Публикуется как git.sabilin.com/eventhub/erlang-builder:latest
|
||||||
|
FROM erlang:28-alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
git curl make gcc musl-dev \
|
||||||
|
rust cargo openssl-dev libsodium-dev \
|
||||||
|
file
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# EventHub — runtime: libs для relx-release (ERTS внутри release).
|
||||||
|
# ALPINE_VERSION MUST match erlang:28-alpine (сейчас 3.23).
|
||||||
|
# Публикуется как git.sabilin.com/eventhub/erlang-runtime:latest
|
||||||
|
ARG ALPINE_VERSION=3.23
|
||||||
|
FROM alpine:${ALPINE_VERSION}
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
openssl libstdc++ libgcc ncurses-libs libsodium \
|
||||||
|
file \
|
||||||
|
&& mkdir -p /app/data \
|
||||||
|
&& chmod 777 /app/data
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Собирает .so, нужные NIF/priv из release (ldd), для runtime-слоя.
|
||||||
|
# Исключает musl/libc — их даёт базовый alpine-образ.
|
||||||
|
# Usage: collect-runtime-libs.sh <release_dir> <output_root>
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
RELEASE="${1:?release_dir}"
|
||||||
|
OUT="${2:?output_root}"
|
||||||
|
|
||||||
|
mkdir -p "${OUT}"
|
||||||
|
|
||||||
|
is_base_lib() {
|
||||||
|
case "$1" in
|
||||||
|
/lib/ld-musl-*|*/ld-musl-*|*/libc.musl-*) return 0 ;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_lib() {
|
||||||
|
lib="$1"
|
||||||
|
[ -e "${lib}" ] || return 0
|
||||||
|
is_base_lib "${lib}" && return 0
|
||||||
|
dest="${OUT}${lib}"
|
||||||
|
mkdir -p "$(dirname "${dest}")"
|
||||||
|
if [ -L "${lib}" ]; then
|
||||||
|
cp -a "${lib}" "${dest}"
|
||||||
|
else
|
||||||
|
cp -L "${lib}" "${dest}" 2>/dev/null || cp "${lib}" "${dest}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2044
|
||||||
|
for so in $(find "${RELEASE}" -name '*.so' 2>/dev/null); do
|
||||||
|
ldd "${so}" 2>/dev/null | while read -r line; do
|
||||||
|
case "${line}" in
|
||||||
|
*'=>'*)
|
||||||
|
lib=$(echo "${line}" | awk '{print $3}')
|
||||||
|
[ -n "${lib}" ] && copy_lib "${lib}"
|
||||||
|
;;
|
||||||
|
/lib/*|/usr/lib/*)
|
||||||
|
lib=$(echo "${line}" | awk '{print $1}')
|
||||||
|
copy_lib "${lib}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "collect-runtime-libs: $(find "${OUT}" -name '*.so*' 2>/dev/null | wc -l) files under ${OUT}"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Падает, если у .so из release есть unresolved ldd-зависимости.
|
||||||
|
# Usage: verify-native-libs.sh <release_dir>
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
RELEASE="${1:?release_dir}"
|
||||||
|
failed=0
|
||||||
|
|
||||||
|
# shellcheck disable=SC2044
|
||||||
|
for so in $(find "${RELEASE}" -name '*.so' 2>/dev/null); do
|
||||||
|
if ldd "${so}" 2>/dev/null | grep -q 'not found'; then
|
||||||
|
echo "ERROR: missing libs for ${so}:"
|
||||||
|
ldd "${so}" 2>/dev/null | grep 'not found' || true
|
||||||
|
failed=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${failed}" -ne 0 ]; then
|
||||||
|
echo "verify-native-libs: FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "verify-native-libs: OK"
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Erlang builder + runtime в registry (git.sabilin.com/eventhub/*:latest).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/build-erlang-base-images.sh # pull latest или собрать локально
|
||||||
|
# ./scripts/build-erlang-base-images.sh --push # + push :latest в registry
|
||||||
|
# ./scripts/build-erlang-base-images.sh --force-build # пересобрать без pull
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PUSH=0
|
||||||
|
FORCE=0
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "${arg}" in
|
||||||
|
--push) PUSH=1 ;;
|
||||||
|
--force-build) FORCE=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
REGISTRY="${REGISTRY:-git.sabilin.com/eventhub}"
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
BUILDER="${REGISTRY}/erlang-builder:latest"
|
||||||
|
RUNTIME="${REGISTRY}/erlang-runtime:latest"
|
||||||
|
|
||||||
|
check_alpine_sync() {
|
||||||
|
local erlang_ver runtime_ver erlang_mm
|
||||||
|
erlang_ver="$(docker run --rm erlang:28-alpine cat /etc/alpine-release)"
|
||||||
|
runtime_ver="$(grep '^ARG ALPINE_VERSION=' "${ROOT}/docker/erlang/Dockerfile.runtime" | cut -d= -f2)"
|
||||||
|
erlang_mm="$(echo "${erlang_ver}" | cut -d. -f1-2)"
|
||||||
|
if [[ "${erlang_mm}" != "${runtime_ver}" ]]; then
|
||||||
|
echo "ERROR: erlang:28-alpine uses Alpine ${erlang_ver}, runtime pins ${runtime_ver}"
|
||||||
|
echo " Обновите ALPINE_VERSION в docker/erlang/Dockerfile.runtime"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Alpine sync OK: ${erlang_mm} (erlang image ${erlang_ver})"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_one() {
|
||||||
|
local file="$1"
|
||||||
|
local tag="$2"
|
||||||
|
local extra=()
|
||||||
|
if docker image inspect "${tag}" >/dev/null 2>&1; then
|
||||||
|
extra+=(--cache-from "${tag}")
|
||||||
|
fi
|
||||||
|
bash "${ROOT}/scripts/retry-docker-build.sh" 5 60 -- \
|
||||||
|
docker buildx build \
|
||||||
|
--network=host \
|
||||||
|
--pull \
|
||||||
|
"${extra[@]}" \
|
||||||
|
--file "${file}" \
|
||||||
|
--tag "${tag}" \
|
||||||
|
--load \
|
||||||
|
--provenance=false \
|
||||||
|
--sbom=false \
|
||||||
|
"${ROOT}"
|
||||||
|
}
|
||||||
|
|
||||||
|
try_pull_bases() {
|
||||||
|
docker pull "${BUILDER}" >/dev/null 2>&1 && docker pull "${RUNTIME}" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
need_build=1
|
||||||
|
if (( FORCE == 0 )) && try_pull_bases; then
|
||||||
|
echo "Using erlang base images from registry:"
|
||||||
|
echo " ${BUILDER}"
|
||||||
|
echo " ${RUNTIME}"
|
||||||
|
need_build=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( need_build )); then
|
||||||
|
echo "Building erlang base images locally..."
|
||||||
|
check_alpine_sync
|
||||||
|
build_one "${ROOT}/docker/erlang/Dockerfile.builder" "${BUILDER}"
|
||||||
|
build_one "${ROOT}/docker/erlang/Dockerfile.runtime" "${RUNTIME}"
|
||||||
|
echo "=== erlang base images built ==="
|
||||||
|
echo " ${BUILDER}"
|
||||||
|
echo " ${RUNTIME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( PUSH )); then
|
||||||
|
bash "${ROOT}/scripts/retry-docker-build.sh" 5 30 -- docker push "${BUILDER}"
|
||||||
|
bash "${ROOT}/scripts/retry-docker-build.sh" 5 30 -- docker push "${RUNTIME}"
|
||||||
|
echo "Pushed ${BUILDER} and ${RUNTIME}"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user