Skip to content

Commit b0a79c1

Browse files
authored
Use ko in favour of docker build (#708)
<!-- Provide a brief summary of your changes --> ## Motivation and Context <!-- Why is this change needed? What problem does it solve? --> The following PR leverages ko instead of docker build for building the container image. The motivation for the move is that ko has been one of the widely adopted ways to build Go applications, but overall its benefits are: * Build time is much faster - around 70-80% * The resulting image size is much smaller (also more secure) - around 50% smaller The changes in this PR are: * Updated the workflows for staging and release to prod to use it * Updated the local development + the integration tests to use it (the integration tests overwrite the base image from chainguards one to alpine mostly because we rely on wget to check the health status and you don't get that in the stripped down one) From the user point of view everything should continue to work as it is. ## How Has This Been Tested? <!-- Have you tested this in a real application? Which scenarios were tested? --> ## Breaking Changes <!-- Will users need to update their code or configurations? --> ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [ ] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [ ] My code follows the repository's style guidelines - [ ] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context <!-- Add any other context, implementation notes, or design decisions --> --------- Signed-off-by: Radoslav Dimitrov <[email protected]>
1 parent 2f2339d commit b0a79c1

File tree

11 files changed

+183
-120
lines changed

11 files changed

+183
-120
lines changed

.dockerignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ jobs:
5454
go-version-file: 'go.mod'
5555
cache: true
5656

57+
- name: Set up ko
58+
uses: ko-build/[email protected]
59+
5760
- name: Run all tests
5861
run: make test-all
5962

.github/workflows/deploy-staging.yml

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ env:
1414
PULUMI_VERSION: "3.188.0"
1515

1616
jobs:
17-
docker-push:
18-
name: Build Docker Image
17+
ko-push:
18+
name: Build and Push Image with ko
1919
runs-on: ubuntu-latest
2020
permissions:
2121
contents: read
@@ -24,8 +24,14 @@ jobs:
2424
- name: Checkout repository
2525
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
2626

27-
- name: Set up Docker Buildx
28-
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
27+
- name: Set up Go
28+
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00
29+
with:
30+
go-version-file: 'go.mod'
31+
cache: true
32+
33+
- name: Set up ko
34+
uses: ko-build/[email protected]
2935

3036
- name: Log in to Container Registry
3137
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef
@@ -34,40 +40,31 @@ jobs:
3440
username: ${{ github.actor }}
3541
password: ${{ secrets.GITHUB_TOKEN }}
3642

37-
- name: Get build timestamp
38-
id: build-time
39-
run: echo "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
40-
41-
- name: Extract metadata
42-
id: meta
43-
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
44-
with:
45-
images: ghcr.io/${{ github.repository }}
46-
tags: |
47-
type=sha,prefix=main-{{date 'YYYYMMDD'}}-,enable={{is_default_branch}}
48-
type=raw,value=main,enable={{is_default_branch}}
43+
- name: Get build timestamp and tags
44+
id: build-info
45+
run: |
46+
echo "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
47+
echo "date=$(date -u +%Y%m%d)" >> $GITHUB_OUTPUT
48+
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
4949
50-
- name: Build and push Docker image
51-
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
52-
with:
53-
context: .
54-
file: ./Dockerfile
55-
platforms: linux/amd64,linux/arm64
56-
push: true
57-
tags: ${{ steps.meta.outputs.tags }}
58-
labels: ${{ steps.meta.outputs.labels }}
59-
cache-from: type=gha
60-
cache-to: type=gha,mode=max
61-
build-args: |
62-
VERSION=${{ steps.meta.outputs.version }}
63-
GIT_COMMIT=${{ github.sha }}
64-
BUILD_TIME=${{ steps.build-time.outputs.timestamp }}
50+
- name: Build and push with ko
51+
env:
52+
KO_DOCKER_REPO: ghcr.io/${{ github.repository }}
53+
VERSION: main-${{ steps.build-info.outputs.date }}-${{ steps.build-info.outputs.short_sha }}
54+
GIT_COMMIT: ${{ github.sha }}
55+
BUILD_TIME: ${{ steps.build-info.outputs.timestamp }}
56+
run: |
57+
# Build and push multi-platform image
58+
ko build ./cmd/registry \
59+
--bare \
60+
--platform=linux/amd64,linux/arm64 \
61+
--tags=main-${{ steps.build-info.outputs.date }}-${{ steps.build-info.outputs.short_sha }},main
6562
6663
deploy-staging:
6764
name: Deploy to Staging
6865
runs-on: ubuntu-latest
6966
environment: staging
70-
needs: docker-push
67+
needs: ko-push
7168
concurrency:
7269
group: deploy-staging
7370
cancel-in-progress: false

.github/workflows/release.yml

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,22 @@ jobs:
4141
env:
4242
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4343

44-
docker:
44+
ko-push:
45+
name: Build and Push Image with ko
4546
runs-on: ubuntu-latest
4647
needs: goreleaser
4748
steps:
4849
- name: Checkout
4950
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
5051

51-
- name: Set up Docker Buildx
52-
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
52+
- name: Set up Go
53+
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00
54+
with:
55+
go-version-file: 'go.mod'
56+
cache: true
57+
58+
- name: Set up ko
59+
uses: ko-build/[email protected]
5360

5461
- name: Log in to Container Registry
5562
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef
@@ -58,31 +65,23 @@ jobs:
5865
username: ${{ github.actor }}
5966
password: ${{ secrets.GITHUB_TOKEN }}
6067

61-
- name: Get build timestamp
62-
id: build-time
63-
run: echo "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
64-
65-
- name: Extract metadata
66-
id: meta
67-
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
68-
with:
69-
images: ghcr.io/${{ github.repository }}
70-
tags: |
71-
type=semver,pattern={{version}}
72-
type=raw,value=latest
68+
- name: Get build timestamp and version
69+
id: build-info
70+
run: |
71+
echo "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
72+
# Extract version from tag (e.g., refs/tags/v1.2.3 -> v1.2.3)
73+
VERSION="${GITHUB_REF#refs/tags/}"
74+
echo "version=$VERSION" >> $GITHUB_OUTPUT
7375
74-
- name: Build and push Docker image
75-
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
76-
with:
77-
context: .
78-
file: ./Dockerfile
79-
platforms: linux/amd64,linux/arm64
80-
push: true
81-
tags: ${{ steps.meta.outputs.tags }}
82-
labels: ${{ steps.meta.outputs.labels }}
83-
cache-from: type=gha
84-
cache-to: type=gha,mode=max
85-
build-args: |
86-
VERSION=${{ steps.meta.outputs.version }}
87-
GIT_COMMIT=${{ github.sha }}
88-
BUILD_TIME=${{ steps.build-time.outputs.timestamp }}
76+
- name: Build and push with ko
77+
env:
78+
KO_DOCKER_REPO: ghcr.io/${{ github.repository }}
79+
VERSION: ${{ steps.build-info.outputs.version }}
80+
GIT_COMMIT: ${{ github.sha }}
81+
BUILD_TIME: ${{ steps.build-info.outputs.timestamp }}
82+
run: |
83+
# Build and push multi-platform image with version tag and latest
84+
ko build ./cmd/registry \
85+
--bare \
86+
--platform=linux/amd64,linux/arm64 \
87+
--tags=${{ steps.build-info.outputs.version }},latest

.ko.dockerignore

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Build artifacts
2+
bin/
3+
*.exe
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test and coverage files
9+
coverage.out
10+
coverage.html
11+
*.test
12+
13+
# Database files
14+
*.db
15+
.db/
16+
17+
# Deployment files
18+
deploy/
19+
20+
# Documentation
21+
docs/
22+
23+
# Tests
24+
tests/
25+
26+
# Git
27+
.git/
28+
.gitignore
29+
30+
# GitHub Actions
31+
.github/
32+
33+
# Docker files (no longer used)
34+
Dockerfile
35+
.dockerignore
36+
docker-compose.yml
37+
38+
# IDE and editor files
39+
.vscode/
40+
.idea/
41+
*.swp
42+
*.swo
43+
*~
44+
45+
# Temporary files
46+
tmp/
47+
temp/
48+
*.tmp
49+
50+
# OS files
51+
.DS_Store
52+
Thumbs.db

.ko.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# ko configuration for MCP Registry
2+
# Documentation: https://ko.build/configuration/
3+
4+
# Default base image for all builds
5+
# Using Chainguard's static image for minimal, secure containers (~2MB)
6+
defaultBaseImage: cgr.dev/chainguard/static:latest
7+
8+
# Default platforms for multi-architecture builds
9+
defaultPlatforms:
10+
- linux/amd64
11+
- linux/arm64
12+
13+
# Build configuration
14+
builds:
15+
- id: registry
16+
# Main package to build
17+
main: ./cmd/registry
18+
# Inject version information at build time via ldflags
19+
ldflags:
20+
- -s -w
21+
- -X main.Version={{ .Env.VERSION }}
22+
- -X main.GitCommit={{ .Env.GIT_COMMIT }}
23+
- -X main.BuildTime={{ .Env.BUILD_TIME }}
24+
env:
25+
- CGO_ENABLED=0
26+
27+
# SBOM (Software Bill of Materials) configuration
28+
sbom: spdx
29+
30+
# Base import path handling
31+
# Set to false to preserve full import paths in image names
32+
baseImportPaths: false

Dockerfile

Lines changed: 0 additions & 42 deletions
This file was deleted.

Makefile

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: help build test test-unit test-integration test-endpoints test-publish test-all lint lint-fix validate validate-schemas validate-examples check dev-compose clean publisher generate-schema check-schema
1+
.PHONY: help build test test-unit test-integration test-endpoints test-publish test-all lint lint-fix validate validate-schemas validate-examples check ko-build ko-rebuild dev-compose dev-down clean publisher generate-schema check-schema
22

33
# Default target
44
help: ## Show this help message
@@ -83,11 +83,23 @@ check: dev-down lint validate test-all ## Run all checks (lint, validate, unit t
8383
@echo "All checks passed!"
8484

8585
# Development targets
86-
dev-compose: ## Start development environment with Docker Compose (builds image automatically)
86+
ko-build: ## Build registry image using ko (loads into local docker daemon)
87+
@echo "Building registry with ko..."
88+
VERSION=dev-$$(git rev-parse --short HEAD) \
8789
GIT_COMMIT=$$(git rev-parse HEAD) \
88-
GIT_COMMIT_SHORT=$$(git rev-parse --short HEAD) \
8990
BUILD_TIME=$$(date -u +%Y-%m-%dT%H:%M:%SZ) \
90-
docker compose up --build
91+
KO_DOCKER_REPO=ko.local \
92+
ko build --preserve-import-paths --tags=dev --sbom=none ./cmd/registry
93+
@echo "Image built: ko.local/github.com/modelcontextprotocol/registry/cmd/registry:dev"
94+
95+
ko-rebuild: ## Rebuild with ko and restart registry container
96+
@$(MAKE) ko-build
97+
@echo "Restarting registry container..."
98+
@docker compose restart registry
99+
100+
dev-compose: ko-build ## Start development environment with Docker Compose (builds with ko first)
101+
@echo "Starting Docker Compose..."
102+
docker compose up
91103

92104
dev-down: ## Stop development environment
93105
docker compose down

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ Often (but not always) ideas flow through this pipeline:
3232
#### Pre-requisites
3333

3434
- **Docker**
35-
- **Go 1.24.x**
35+
- **Go 1.24.x**
36+
- **ko** - Container image builder for Go ([installation instructions](https://ko.build/install/))
3637
- **golangci-lint v2.4.0**
3738

3839
#### Running the server
@@ -44,6 +45,8 @@ make dev-compose
4445

4546
This starts the registry at [`localhost:8080`](http://localhost:8080) with PostgreSQL. The database uses ephemeral storage and is reset each time you restart the containers, ensuring a clean state for development and testing.
4647

48+
**Note:** The registry uses [ko](https://ko.build) to build container images. The `make dev-compose` command automatically builds the registry image with ko and loads it into your local Docker daemon before starting the services.
49+
4750
By default, the registry seeds from the production API with a filtered subset of servers (to keep startup fast). This ensures your local environment mirrors production behavior and all seed data passes validation. For offline development you can seed from a file without validation with `MCP_REGISTRY_SEED_FROM=data/seed.json MCP_REGISTRY_ENABLE_REGISTRY_VALIDATION=false make dev-compose`.
4851

4952
The setup can be configured with environment variables in [docker-compose.yml](./docker-compose.yml) - see [.env.example](./.env.example) for a reference.

docker-compose.yml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
services:
22
registry:
3-
image: modelcontextprotocol/registry:dev
4-
pull_policy: never
3+
# Built using ko (see Makefile dev-compose target)
4+
# Image format: ko.local/<import-path>:<tag>
5+
image: ko.local/github.com/modelcontextprotocol/registry/cmd/registry:dev
56
container_name: registry
6-
build:
7-
dockerfile: Dockerfile
8-
args:
9-
- VERSION=dev-${GIT_COMMIT_SHORT:-unknown}
10-
- GIT_COMMIT=${GIT_COMMIT:-unknown}
11-
- BUILD_TIME=${BUILD_TIME:-unknown}
127
depends_on:
138
postgres:
149
condition: service_healthy
@@ -30,6 +25,8 @@ services:
3025
- MCP_REGISTRY_ENABLE_REGISTRY_VALIDATION=${MCP_REGISTRY_ENABLE_REGISTRY_VALIDATION:-true}
3126
ports:
3227
- 8080:8080
28+
volumes:
29+
- ./data:/data:ro
3330
restart: "unless-stopped"
3431

3532
postgres:

0 commit comments

Comments
 (0)