-
Notifications
You must be signed in to change notification settings - Fork 2
/
Dockerfile
143 lines (121 loc) · 6.31 KB
/
Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# syntax = docker.io/docker/dockerfile:1.12
# The Alpine and Go base images must use the same release of Alpine.
ARG ALPINE_BASE=docker.io/library/alpine:3.21
ARG GOLANG_BASE=docker.io/library/golang:1.23-alpine3.21
# The Node.js image does not require any particular OS.
ARG NODEJS_BASE=docker.io/library/node:22-alpine
# See https://gstreamer.freedesktop.org/news/.
ARG GSTREAMER_VERSION=1.24.10
# Mirror of https://gitlab.freedesktop.org/gstreamer/gstreamer.git.
# Since this is formally marked as a "public mirror" in the GitHub web UI, my
# understanding is that GitHub (the entity I trust to host and build Hypcast
# itself) does the mirroring. Cloning from Git and doing a Meson build from the
# checkout isn't as good as building real ".apk" files from a source tarball.
# But until I put in the work to make this GStreamer build more than a pile of
# shameful hacks, I'd rather use Microsoft's bandwidth and resources than those
# of the freedesktop.org maintainers.
ARG GSTREAMER_REPO=https://github.com/GStreamer/gstreamer.git
# Let's get the client build out of the way, since it's much simpler than
# everything that follows.
FROM --platform=$BUILDPLATFORM $NODEJS_BASE AS client-build
ENV BUILD_PATH=/dist
RUN \
--mount=type=bind,source=client,target=/mnt/client,rw \
--mount=type=cache,id=hypcast.node_modules,target=/mnt/client/node_modules \
--mount=type=cache,id=hypcast.yarn,target=/usr/local/share/.cache/yarn \
cd /mnt/client && \
yarn install --frozen-lockfile && \
yarn build
# This Dockerfile is designed to produce multi-architecture images without
# emulating the target architecture on the build host (which can be very slow).
# The images are based on Alpine Linux with a custom build of GStreamer, where
# all C components are built with LLVM.
#
# Why a custom GStreamer? Alpine 3.16 and up ship gst-plugins-ugly without the
# mpeg2dec plugin, which is an absolute requirement for Hypcast. As a bonus, we
# can reduce the image size by only including plugins we actually need. We
# still use Alpine's versions of GLib and the underlying codecs.
#
# Why LLVM? Alpine does not ship a full set of gcc-based cross toolchains for
# every build host architecture (e.g. no x86_64 toolchain on aarch64 hosts).
# Even if it did, the consistency of the LLVM-based setup provides greater
# confidence that a build executed on one architecture will work on others.
# The build sysroot layer provides development headers and important support
# files for cross-compilation to the target platform. Other build layers will
# mount it as necessary.
FROM --platform=$BUILDPLATFORM $ALPINE_BASE AS build-sysroot
ARG TARGETARCH TARGETVARIANT
COPY build/hypcast-buildenv.sh /hypcast-buildenv.sh
RUN \
source /hypcast-buildenv.sh && \
sysroot_init gcc libc-dev libstdc++-dev glib-dev a52dec-dev libmpeg2-dev opus-dev x264-dev
# The GStreamer build base layer sets up parts of the GStreamer build that are
# common to all target platforms. (TODO: Can we not clone GStreamer from Git?
# Does Cerbero support the level of build customization we're looking for?)
FROM --platform=$BUILDPLATFORM $ALPINE_BASE AS gst-build-base
RUN apk add --no-cache bash git clang lld llvm pkgconf meson flex bison glib-dev
ARG GSTREAMER_REPO GSTREAMER_VERSION
RUN git clone -b $GSTREAMER_VERSION --depth 1 $GSTREAMER_REPO /tmp/gstreamer
WORKDIR /tmp/gstreamer
COPY build/hypcast-buildenv.sh /hypcast-buildenv.sh
COPY build/gstreamer-build.bash .
# The GStreamer build layer cross-compiles GStreamer for a specific target
# platform, with everything installed under the /gstreamer directory. The build
# output includes the shared libraries themselves along with headers and
# pkg-config manifests, so it is both mounted into the server build and copied
# to the final image as necessary.
FROM --platform=$BUILDPLATFORM gst-build-base AS gst-build
ARG TARGETARCH TARGETVARIANT
RUN \
--mount=type=bind,from=build-sysroot,source=/sysroot,target=/sysroot \
./gstreamer-build.bash
# The server build base layer sets up parts of the server build that are common
# to all target platforms.
FROM --platform=$BUILDPLATFORM $GOLANG_BASE AS server-build-base
RUN apk add --no-cache clang lld pkgconf
COPY build/hypcast-buildenv.sh /hypcast-buildenv.sh
RUN \
--mount=type=bind,target=/mnt/hypcast \
--mount=type=cache,id=hypcast.go-pkg,target=/go/pkg \
--mount=type=cache,id=hypcast.go-build,target=/root/.cache/go-build \
cd /mnt/hypcast && go mod download
# The server build layer compiles the hypcast-server binary using a sysroot
# that combines the Alpine and GStreamer headers and pkg-config manifests. See
# hypcast-buildenv.sh for the setup of important environment variables.
FROM --platform=$BUILDPLATFORM server-build-base AS server-build
ARG TARGETARCH TARGETVARIANT
RUN \
--mount=type=bind,from=build-sysroot,source=/sysroot,target=/sysroot,rw \
--mount=type=bind,from=gst-build,source=/gstreamer/usr/local,target=/sysroot/usr/local \
--mount=type=bind,target=/mnt/hypcast \
--mount=type=cache,id=hypcast.go-pkg,target=/go/pkg \
--mount=type=cache,id=hypcast.go-build,target=/root/.cache/go-build \
cd /mnt/hypcast && \
source /hypcast-buildenv.sh && \
go build -v \
-ldflags="-extld=$CC -s -w" -buildmode=pie \
-o /hypcast-server \
./cmd/hypcast-server
# The target sysroot layer bootstraps the root filesystem for the target image.
# Note that this will be a distroless-style image that does not resemble a
# typical Alpine Linux environment, as we can't run the apk scripts required to
# fully set up packages like Busybox (apk would try to run them using the
# target architecture's shell, which requires emulation).
FROM --platform=$BUILDPLATFORM $ALPINE_BASE AS target-sysroot
ARG TARGETARCH TARGETVARIANT
COPY build/hypcast-buildenv.sh /hypcast-buildenv.sh
RUN \
source /hypcast-buildenv.sh && \
sysroot_init tini libstdc++ glib a52dec libmpeg2 opus x264-libs
# The final image simply assembles the results of previous build steps.
FROM scratch AS target
COPY --link --from=target-sysroot /sysroot /
COPY --link --from=gst-build /gstreamer /
COPY --link --from=server-build /hypcast-server /opt/hypcast/bin/hypcast-server
COPY --link --from=client-build /dist /opt/hypcast/share/www
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
EXPOSE 9200
ENTRYPOINT [ \
"/sbin/tini", "--", \
"/opt/hypcast/bin/hypcast-server", \
"-assets", "/opt/hypcast/share/www" ]