Skip to content

Commit

Permalink
Build statically linked binaries (amacneil#156)
Browse files Browse the repository at this point in the history
Prior to this commit, we released two linux binaries:

* `dbmate-linux-amd64` (built with cgo, dynamically linked)
* `dbmate-linux-musl-amd64` (built without cgo, statically linked, no sqlite support)

The statically linked binary is desirable for alpine linux users (or anyone else using musl libc or minimal docker images). The original reason for having two separate binaries was that the easiest method to create a static binary for go is to set `CGO_ENABLED=0`, but unfortunately this also prevented us from building sqlite (which requires cgo).

With this commit, all linux and windows binaries are explicitly statically linked while leaving cgo enabled. Hat tip to https://www.arp242.net/static-go.html which explained the necessary flags to enable this.

As an added bonus, the `dbmate` docker image now now uses a `scratch` base rather than `gcr.io/distroless/base`, reducing the image size from 26.7 MB to 9.8 MB.
  • Loading branch information
amacneil authored Aug 11, 2020
1 parent df461ff commit 4581aca
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 20 deletions.
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# build image
FROM techknowlogick/xgo:go-1.14.x as build
WORKDIR /src
ENTRYPOINT []
CMD ["/bin/bash"]

# enable cgo to build sqlite
ENV CGO_ENABLED 1
Expand Down Expand Up @@ -30,7 +28,10 @@ RUN go mod download
COPY . ./
RUN make build

ENTRYPOINT []
CMD ["/bin/bash"]

# runtime image
FROM gcr.io/distroless/base
FROM scratch
COPY --from=build /src/dist/dbmate-linux-amd64 /dbmate
ENTRYPOINT ["/dbmate"]
25 changes: 14 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# no static linking for macos
LDFLAGS := -ldflags '-s'
# statically link binaries (to support alpine + scratch containers)
STATICLDFLAGS := -ldflags '-s -extldflags "-static"'
# avoid building code that is incompatible with static linking
TAGS := -tags netgo,osusergo,sqlite_omit_load_extension

.PHONY: all
all: build lint test

.PHONY: test
test:
go test -v ./...
go test -v $(TAGS) $(STATICLDFLAGS) ./...

.PHONY: fix
fix:
Expand All @@ -31,19 +36,17 @@ build: clean build-linux-amd64

.PHONY: build-linux-amd64
build-linux-amd64:
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 \
go build $(LDFLAGS) -o dist/dbmate-linux-amd64 .
GOOS=linux GOARCH=amd64 \
go build $(TAGS) $(STATICLDFLAGS) -o dist/dbmate-linux-amd64 .

.PHONY: build-all
build-all: clean build-linux-amd64
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
go build $(LDFLAGS) -o dist/dbmate-linux-musl-amd64 .
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc-5 CXX=aarch64-linux-gnu-g++-5 \
go build $(LDFLAGS) -o dist/dbmate-linux-arm64 .
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 CC=o64-clang CXX=o64-clang++ \
go build $(LDFLAGS) -o dist/dbmate-macos-amd64 .
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix \
go build $(LDFLAGS) -o dist/dbmate-windows-amd64.exe .
GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc-5 CXX=aarch64-linux-gnu-g++-5 \
go build $(TAGS) $(STATICLDFLAGS) -o dist/dbmate-linux-arm64 .
GOOS=darwin GOARCH=amd64 CC=o64-clang CXX=o64-clang++ \
go build $(TAGS) $(LDFLAGS) -o dist/dbmate-macos-amd64 .
GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix \
go build $(TAGS) $(STATICLDFLAGS) -o dist/dbmate-windows-amd64.exe .
ls -lh dist

.PHONY: docker-make
Expand Down
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,12 +337,6 @@ $ dbmate -u "postgres://[email protected]:5432/myapp_test?sslmode=disable" up

The only advantage of using `dbmate -e TEST_DATABASE_URL` over `dbmate -u $TEST_DATABASE_URL` is that the former takes advantage of dbmate's automatic `.env` file loading.

## FAQ

**How do I use dbmate under Alpine linux?**

Alpine linux uses [musl libc](https://www.musl-libc.org/), which is incompatible with how we build SQLite support (using [cgo](https://golang.org/cmd/cgo/)). If you want Alpine linux support, and don't mind sacrificing SQLite support, please use the `dbmate-linux-musl-amd64` build found on the [releases page](https://github.com/amacneil/dbmate/releases).

## Alternatives

Why another database schema migration tool? Dbmate was inspired by many other tools, primarily [Active Record Migrations](http://guides.rubyonrails.org/active_record_migrations.html), with the goals of being trivial to configure, and language & framework independent. Here is a comparison between dbmate and other popular migration tools.
Expand Down

0 comments on commit 4581aca

Please sign in to comment.