This repository has been archived by the owner on Jul 19, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathDockerfile
225 lines (166 loc) · 6.15 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# This Dockerfile builds invoicer twice: once on Alpine, once on Debian.
# If the binaries are the same, one is compressed, and copied to the `final` stage.
# invoicer version to be build
ARG VERSION=v0.8.1
# Target CPU archtecture of built Invoicer binary
ARG ARCH
# Define default versions so that they don't have to be repreated throughout the file
ARG VER_GO=1.13
ARG VER_ALPINE=3.11
ARG USER=invoicer
ARG DIR=/data/
# NOTE: You should only override this if you know what you're doing
ARG TAGS="osusergo,netgo,static_build"
#
## This stage builds `invoicer` in an Alpine environment
#
FROM golang:${VER_GO}-alpine${VER_ALPINE} AS alpine-builder
# Provided by Docker by default
ARG TARGETVARIANT
# These two should only be set for cross-compilation
ARG GOARCH
ARG GOARM
# Capture ARGs defined globally
ARG VERSION
ARG TAGS
# Only set GOOS if GOARCH is set
ENV GOOS ${GOARCH:+linux}
# If GOARM is not set, but TARGETVARIANT is set - hardcode GOARM to 6
ENV GOARM ${GOARM:-${TARGETVARIANT:+6}}
ENV GCO_ENABLED 0
ENV LDFLAGS "-s -w -buildid= -X main.version=${VERSION}"
ENV BINARY /go/bin/invoicer
RUN apk add --no-cache musl-dev file git gcc
RUN mkdir -p /go/src/
COPY ./ /go/src/
WORKDIR /go/src/
## Print versions of software used for this build
# NOTE: sha256sum is part of busybox on Alpine
RUN busybox | head -n 1
RUN file --version
RUN git --version
RUN gcc --version
RUN uname -a
RUN env && go version && go env
## Build invoicer binary.
# The first line gets hash of the last git-commit & second one prints it.
# And here's all other flags explained:
# `-x` [verbocity++] print all executed commands
# `-v` [verbocity++] print names of compiled packages
# `-mod=readonly` [reproducibility] do not change versions of used packages no matter what
# `-trimpath` [reproducibility] make sure absolute paths are not included anywhere in the binary
# `-tags` [reproducibility] tell Go to build a static binary, see more: https://github.com/golang/go/issues/26492
# `-ldflags`
# `-s` [size--] do not include symbol table and debug info
# `-w` [size--] do not include DWARF symbol table
# `-buildid` [reproducibility] while this should always be the same in our setup, clear it just-in-case
# `-X` [info] is used twice to inject version, and git-hash into the built binary
#
# NOTE: all of this has to happen in a single `RUN`, because it's impossible to set ENV var in Docker to
# an output of an expression.
RUN export GIT_HASH="$(git rev-parse HEAD)"; \
echo "Building git tag: ${GIT_HASH}"; \
go build -x -v -trimpath -mod=readonly -tags="${TAGS}" \
-ldflags="${LDFLAGS} -X main.gitHash=${GIT_HASH}" \
-o "${BINARY}"
# Print rudimentary info about the built binary
RUN sha256sum "${BINARY}"
RUN file -b "${BINARY}"
RUN du "${BINARY}"
#
## This stage builds `invoicer` in a Debian environment
#
# NOTE: Comments that would be identical to Alpine stage skipped for brevity
FROM golang:${VER_GO}-buster AS debian-builder
ARG TARGETVARIANT
ARG GOARCH
ARG GOARM
ARG VERSION
ARG TAGS
ENV GOOS ${GOARCH:+linux}
ENV GOARM ${GOARM:-${TARGETVARIANT:+6}}
ENV GCO_ENABLED 0
ENV LDFLAGS "-s -w -buildid= -X main.version=${VERSION}"
ENV BINARY /go/bin/invoicer
RUN apt-get update \
&& apt-get -y install file git
RUN mkdir -p /go/src/
COPY ./ /go/src/
WORKDIR /go/src/
RUN sha256sum --version
RUN make --version
RUN file --version
RUN git --version
RUN uname -a
RUN env && go version && go env
RUN export GIT_HASH="$(git rev-parse HEAD)"; \
echo "Building git hash: ${GIT_HASH}"; \
go build -x -v -trimpath -mod=readonly -tags="${TAGS}" \
-ldflags="${LDFLAGS} -X main.gitHash=${GIT_HASH}" \
-o "${BINARY}"
RUN sha256sum "${BINARY}"
RUN file -b "${BINARY}"
RUN du "${BINARY}"
#
## This stage compares previously built binaries, and only proceeds if they are identical
#
FROM alpine:${VER_ALPINE} AS cross-check
# Install utilities used later
RUN apk add --no-cache file upx
# Prepare destination directories for previously built binaries
RUN mkdir -p /bin /alpine /debian
# Copy binaries from prior builds
COPY --from=alpine-builder /go/bin/invoicer /alpine/
COPY --from=debian-builder /go/bin/invoicer /debian/
# Print binary info PRIOR comparison & compression
RUN sha256sum /debian/invoicer /alpine/invoicer
RUN file /debian/invoicer /alpine/invoicer
RUN du /debian/invoicer /alpine/invoicer
# Compare built binaries
RUN diff -q /alpine/invoicer /debian/invoicer
# If identical, proceed to move one binary into `/bin/`
RUN mv /alpine/invoicer /bin/
# Compress, and be verbose about it
RUN upx -v /bin/invoicer
# Print binary info PAST compression
RUN sha256sum /bin/invoicer
RUN file -b /bin/invoicer
RUN du /bin/invoicer
#
## This stage is used to generate /etc/{group,passwd,shadow} files & avoid RUN-ing commands in the `final` layer,
# which would break cross-compiled images.
#
FROM alpine:${VER_ALPINE} AS perms
ARG USER
ARG DIR
# NOTE: Default GID == UID == 1000
RUN adduser --disabled-password \
--home ${DIR} \
--gecos "" \
${USER}
# Needed to prevent `VOLUME ${DIR}` creating it with `root` as owner
USER ${USER}
RUN mkdir -p ${DIR}
#
## This is the final image that gets shipped to Docker Hub
#
# NOTE: `${ARCH:+${ARCH}/}` - if ARCH is set, append `/` to it, leave it empty otherwise
FROM ${ARCH:+${ARCH}/}alpine:${VER_ALPINE} AS final
ARG USER
ARG DIR
# Hai 👋
LABEL maintainer="Damian Mee (@meeDamian)"
# Copy only the relevant parts from the `perms` image
COPY --from=perms /etc/group /etc/passwd /etc/shadow /etc/
# From `perms`, copy *the contents* of `${DIR}` (ie. nothing), and set correct owner for destination `${DIR}`
COPY --from=perms --chown=${USER}:${USER} ${DIR} ${DIR}
# Copy the binary from the cross-check stage
COPY --from=cross-check /bin/invoicer /usr/local/bin/
USER ${USER}
# Expose the volume to communicate config, log, etc through (default: /data/)
VOLUME ${DIR}
# Expose port Invoicer listens on
EXPOSE 8080
# Specify the start command and entrypoint as the invoicer daemon.
ENTRYPOINT ["invoicer"]
CMD ["-config", "/data/invoicer.conf"]