-
Notifications
You must be signed in to change notification settings - Fork 534
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize Docker build with bind mounts #208
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot wasn't able to review any files in this pull request.
Files not reviewed (2)
- .dockerignore: Language not supported
- Dockerfile: Language not supported
This commit further optimize the Docker builds on top of PR github#92 with: 1. Add .dockerignore file to exclude non-source code files [1]. 2. Use Alpine image variant for build stage to reduce download size. golang:1.23.7-alpine is 200 MB smaller than golang:1.23.7 [2][3]. 3. Replace COPY instruction with RUN --mount=type=bind. Bind mounts do not add unnecessary layers to the cache [4][5]. [1]: https://docs.docker.com/build-cloud/optimization/#dockerignore-files [2]: https://hub.docker.com/layers/library/golang/1.23.7-alpine/images/sha256-333d4ba78773b3a3ae9cf2cff8962df56effc5c9481faa355f211abf2baf175c [3]: https://hub.docker.com/layers/library/golang/1.23.7/images/sha256-2087a99c3235972660b3d35c1564d9d1a3f639dcace9c790acbabc7e938d1570 [4]: https://docs.docker.com/build/building/best-practices/#add-or-copy [5]: https://docs.docker.com/build/cache/optimize/#use-bind-mounts Signed-off-by: Eng Zer Jun <[email protected]>
`go build` will automatically download module dependencies. In many cases, that is a much smaller set of modules than what is downloaded by `go mod download`. Size of GOMODCACHE with `go mod download: $ go clean -i -r -cache -modcache $ go mod download $ du -sh ~/go/pkg/mod 186M /home/jun/go/pkg/mod Size of GOMODCACHE with `go build`: $ go clean -i -r -cache -modcache $ CGO_ENABLED=0 go build -ldflags="-s -w" cmd/github-mcp-server/main.go go: downloading github.com/spf13/viper v1.20.1 go: downloading github.com/mark3labs/mcp-go v0.18.0 go: downloading github.com/google/go-github/v69 v69.2.0 go: downloading github.com/sirupsen/logrus v1.9.3 go: downloading github.com/spf13/cobra v1.9.1 go: downloading golang.org/x/sys v0.31.0 go: downloading github.com/spf13/afero v1.14.0 go: downloading github.com/fsnotify/fsnotify v1.8.0 go: downloading github.com/spf13/cast v1.7.1 go: downloading github.com/go-viper/mapstructure/v2 v2.2.1 go: downloading github.com/subosito/gotenv v1.6.0 go: downloading gopkg.in/yaml.v3 v3.0.1 go: downloading github.com/spf13/pflag v1.0.6 go: downloading github.com/pelletier/go-toml/v2 v2.2.3 go: downloading github.com/sagikazarmark/locafero v0.9.0 go: downloading golang.org/x/text v0.23.0 go: downloading github.com/google/uuid v1.6.0 go: downloading github.com/yosida95/uritemplate/v3 v3.0.2 go: downloading github.com/sourcegraph/conc v0.3.0 go: downloading github.com/google/go-querystring v1.1.0 $ du -sh ~/go/pkg/mod 80M /home/jun/go/pkg/mod Reference: https://stackoverflow.com/a/68172023/7902371 Signed-off-by: Eng Zer Jun <[email protected]>
# Install dependencies | ||
COPY go.mod go.sum ./ | ||
RUN --mount=type=cache,target=/root/.cache/go-build go mod download | ||
|
||
COPY . ./ | ||
# Build the server | ||
RUN --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=${VERSION} -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ | ||
-o github-mcp-server cmd/github-mcp-server/main.go | ||
# go build automatically download required module dependencies to /go/pkg/mod | ||
RUN --mount=type=cache,target=/go/pkg/mod \ | ||
--mount=type=cache,target=/root/.cache/go-build \ | ||
--mount=type=bind,target=. \ | ||
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=${VERSION} -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ | ||
-o /bin/github-mcp-server cmd/github-mcp-server/main.go |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Size of GOMODCACHE
with go mod download
:
~/Desktop/github/github-mcp-server docker ❯ go clean -i -r -cache -modcache
~/Desktop/github/github-mcp-server docker ❯ go mod download
~/Desktop/github/github-mcp-server docker ❯ du -sh ~/go/pkg/mod
186M /home/jun/go/pkg/mod
Size of GOMODCACHE
with go build
:
~/Desktop/github/github-mcp-server docker !1 ❯ go clean -i -r -cache -modcache
~/Desktop/github/github-mcp-server docker !1 ❯ CGO_ENABLED=0 go build -ldflags="-s -w" cmd/github-mcp-server/main.go
go: downloading github.com/spf13/viper v1.20.1
go: downloading github.com/mark3labs/mcp-go v0.18.0
go: downloading github.com/google/go-github/v69 v69.2.0
go: downloading github.com/sirupsen/logrus v1.9.3
go: downloading github.com/spf13/cobra v1.9.1
go: downloading golang.org/x/sys v0.31.0
go: downloading github.com/spf13/afero v1.14.0
go: downloading github.com/fsnotify/fsnotify v1.8.0
go: downloading github.com/spf13/cast v1.7.1
go: downloading github.com/go-viper/mapstructure/v2 v2.2.1
go: downloading github.com/subosito/gotenv v1.6.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/spf13/pflag v1.0.6
go: downloading github.com/pelletier/go-toml/v2 v2.2.3
go: downloading github.com/sagikazarmark/locafero v0.9.0
go: downloading golang.org/x/text v0.23.0
go: downloading github.com/google/uuid v1.6.0
go: downloading github.com/yosida95/uritemplate/v3 v3.0.2
go: downloading github.com/sourcegraph/conc v0.3.0
go: downloading github.com/google/go-querystring v1.1.0
~/Desktop/github/github-mcp-server docker !1 ?1 ❯ du -sh ~/go/pkg/mod
80M /home/jun/go/pkg/mod
Running go build
results in a smaller GOMODCACHE
(~80 MB) compared to go mod download
(~186 MB). I found a great explanation on StackOverflow here: https://stackoverflow.com/a/68172023/7902371
The go build
step took 19.1 seconds, which is 2 seconds faster than go mod download
(4.1 seconds) + go build
(17.1 seconds).
[+] Building 37.1s (13/13) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 838B 0.0s
=> [internal] load metadata for gcr.io/distroless/base-debian12:latest 2.1s
=> [internal] load metadata for docker.io/library/golang:1.23.7-alpine 2.6s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 148B 0.0s
=> [build 1/4] FROM docker.io/library/golang:1.23.7-alpine@sha256:e438c135c348bd7677fde18d1576c2f57f265d5dfa1a6b26fca975d4aa40b3bb 13.6s
=> => resolve docker.io/library/golang:1.23.7-alpine@sha256:e438c135c348bd7677fde18d1576c2f57f265d5dfa1a6b26fca975d4aa40b3bb 0.0s
=> => sha256:e291f2c3258d0d58c0a39814b55a8dfa54665485847a55af621f3370f12deccc 2.08kB / 2.08kB 0.0s
=> => sha256:e438c135c348bd7677fde18d1576c2f57f265d5dfa1a6b26fca975d4aa40b3bb 10.29kB / 10.29kB 0.0s
=> => sha256:44b3a1c80b6c3a3083df7893a996ecea1e45d4f4fd5682951b6a13569bdb64e3 1.92kB / 1.92kB 0.0s
=> => sha256:f18232174bc91741fdf3da96d85011092101a032a93a388b79e99e69c2d5c870 3.64MB / 3.64MB 3.2s
=> => sha256:4a6853717b24861f4773c7c44c0700bc67775e6432b117e9f6cb5d80c1259c40 294.90kB / 294.90kB 3.1s
=> => sha256:6ca661eb024f09b95cfdf77781effc26400df52d97566fbd0fd4d45a67c3a3e6 74.06MB / 74.06MB 4.8s
=> => sha256:d509c56e130243a89eef696356c0605801f3be8cf6ae8bb68ab53854704a178e 126B / 126B 3.5s
=> => extracting sha256:f18232174bc91741fdf3da96d85011092101a032a93a388b79e99e69c2d5c870 0.3s
=> => sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 32B / 32B 3.5s
=> => extracting sha256:4a6853717b24861f4773c7c44c0700bc67775e6432b117e9f6cb5d80c1259c40 0.1s
=> => extracting sha256:6ca661eb024f09b95cfdf77781effc26400df52d97566fbd0fd4d45a67c3a3e6 8.4s
=> => extracting sha256:d509c56e130243a89eef696356c0605801f3be8cf6ae8bb68ab53854704a178e 0.0s
=> => extracting sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 820.15kB 0.0s
=> [stage-1 1/3] FROM gcr.io/distroless/base-debian12:latest@sha256:27769871031f67460f1545a52dfacead6d18a9f197db77110cfc649ca2a91f44 3.3s
=> => resolve gcr.io/distroless/base-debian12:latest@sha256:27769871031f67460f1545a52dfacead6d18a9f197db77110cfc649ca2a91f44 0.0s
=> => sha256:dd32b4a5eed124ce1e86eab9959b67c9d6f114a1578a07cb679b2e3955d80b68 1.71kB / 1.71kB 0.0s
=> => sha256:3d78e577de35c8bf231e34862a13cde36cd7b253b6a7af2158035251d2dc48c0 104.23kB / 104.23kB 0.8s
=> => sha256:bfb59b82a9b65e47d485e53b3e815bca3b3e21a095bd0cb88ced9ac0b48062bf 13.36kB / 13.36kB 0.8s
=> => sha256:4eff9a62d888790350b2481ff4a4f38f9c94b3674d26b2f2c85ca39cdef43fd9 547.59kB / 547.59kB 0.9s
=> => sha256:d65201b3fe8eb56abcec05aec99acec815e40b10b63b854ce02bec4575f64526 2.27kB / 2.27kB 0.0s
=> => sha256:27769871031f67460f1545a52dfacead6d18a9f197db77110cfc649ca2a91f44 1.51kB / 1.51kB 0.0s
=> => extracting sha256:3d78e577de35c8bf231e34862a13cde36cd7b253b6a7af2158035251d2dc48c0 0.0s
=> => sha256:a62778643d563b511190663ef9a77c30d46d282facfdce4f3a7aecc03423c1f3 67B / 67B 1.2s
=> => sha256:7c12895b777bcaa8ccae0605b4de635b68fc32d60fa08f421dc3818bf55ee212 188B / 188B 1.2s
=> => extracting sha256:bfb59b82a9b65e47d485e53b3e815bca3b3e21a095bd0cb88ced9ac0b48062bf 0.0s
=> => extracting sha256:4eff9a62d888790350b2481ff4a4f38f9c94b3674d26b2f2c85ca39cdef43fd9 0.3s
=> => sha256:3214acf345c0cc6bbdb56b698a41ccdefc624a09d6beb0d38b5de0b2303ecaf4 123B / 123B 1.4s
=> => extracting sha256:a62778643d563b511190663ef9a77c30d46d282facfdce4f3a7aecc03423c1f3 0.0s
=> => extracting sha256:7c12895b777bcaa8ccae0605b4de635b68fc32d60fa08f421dc3818bf55ee212 0.0s
=> => sha256:5664b15f108bf9436ce3312090a767300800edbbfd4511aa1a6d64357024d5dd 168B / 168B 1.6s
=> => sha256:0bab15eea81d0fe6ab56ebf5fba14e02c4c1775a7f7436fbddd3505add4e18fa 93B / 93B 1.6s
=> => extracting sha256:3214acf345c0cc6bbdb56b698a41ccdefc624a09d6beb0d38b5de0b2303ecaf4 0.0s
=> => sha256:4aa0ea1413d37a58615488592a0b827ea4b2e48fa5a77cf707d0e35f025e613f 385B / 385B 1.8s
=> => extracting sha256:5664b15f108bf9436ce3312090a767300800edbbfd4511aa1a6d64357024d5dd 0.0s
=> => extracting sha256:0bab15eea81d0fe6ab56ebf5fba14e02c4c1775a7f7436fbddd3505add4e18fa 0.0s
=> => sha256:9aee425378d2c16cd44177dc54a274b312897f5860a8e78fdfda555a0d79dd71 130.50kB / 130.50kB 2.4s
=> => sha256:da7816fa955ea24533c388143c78804c28682eef99b4ee3723b548c70148bba6 321B / 321B 2.1s
=> => extracting sha256:4aa0ea1413d37a58615488592a0b827ea4b2e48fa5a77cf707d0e35f025e613f 0.0s
=> => sha256:d00c3209d92970dad10c006c4d7120e5fd276349ce6e57dbad95a3a3f05411fc 5.83MB / 5.83MB 2.6s
=> => extracting sha256:da7816fa955ea24533c388143c78804c28682eef99b4ee3723b548c70148bba6 0.0s
=> => sha256:221438ca359c95b5f7ecb07541b094ae1e5ce63442a404e8a38b6d84b6a7bcb4 2.83MB / 2.83MB 3.0s
=> => extracting sha256:9aee425378d2c16cd44177dc54a274b312897f5860a8e78fdfda555a0d79dd71 0.0s
=> => extracting sha256:d00c3209d92970dad10c006c4d7120e5fd276349ce6e57dbad95a3a3f05411fc 0.3s
=> => extracting sha256:221438ca359c95b5f7ecb07541b094ae1e5ce63442a404e8a38b6d84b6a7bcb4 0.2s
=> [stage-1 2/3] WORKDIR /server 0.0s
=> [build 2/4] WORKDIR /build 0.0s
=> [build 3/4] RUN --mount=type=cache,target=/var/cache/apk apk add git 1.6s
=> [build 4/4] RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build --mount=type=bind,target=. CGO_ENABLED=0 go buil 19.1s
=> [stage-1 3/3] COPY --from=build /bin/github-mcp-server . 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:b9ddd66d7962298abd8380c7f2eee5cc87b9f7b4919068da8b2641a42a0ede68 0.0s
cc: @mntlty
This PR further optimize the Docker builds on top of PR #92 by introducing the following changes:
Add
.dockerignore
file to exclude non-source code files (https://docs.docker.com/build-cloud/optimization/#dockerignore-files).Use Alpine image variant for build stage to reduce download size.
golang:1.23.7-alpine
is 200 MB smaller thangolang:1.23.7
.Replace
COPY
instruction withRUN --mount=type=bind
instruction inDockerfile
.From https://docs.docker.com/build/building/best-practices/#add-or-copy:
From https://docs.docker.com/build/cache/optimize/#use-bind-mounts:
Before
7 build steps (
[build 7/7]
). Took 57.1 seconds to build with no cache.After
5 build steps (
[build 5/5]
). Took 38.9 seconds to build with no cache.