Skip to content
This repository was archived by the owner on Aug 4, 2025. It is now read-only.

Commit 857433d

Browse files
MSeveyMatthew Sevey
andauthored
feat: docker compose (#64)
* feat: create dockerfile for sidecar * feat: add dockerfile for main app * feat: add docker compose * fix: fix app dockerfile and add entrypoint * fix: running docker compose * feat: new github ci workflow, testing and debuging dockercompose * fix: fixing various docker networking issues * fix: docker compose running locally * chore: remove debug system prune --------- Co-authored-by: Matthew Sevey <[email protected]>
1 parent 355e857 commit 857433d

File tree

10 files changed

+284
-36
lines changed

10 files changed

+284
-36
lines changed

.github/workflows/docker-build.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Build and Push Docker Images
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [ main ]
7+
tags: [ 'v*' ]
8+
9+
env:
10+
REGISTRY: ghcr.io
11+
12+
jobs:
13+
build-and-push:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
packages: write
18+
strategy:
19+
matrix:
20+
include:
21+
- name: arda-pocd
22+
dockerfile: ./Dockerfile
23+
- name: tx-sidecar
24+
dockerfile: ./cmd/tx-sidecar/Dockerfile
25+
26+
steps:
27+
- name: Checkout repository
28+
uses: actions/checkout@v4
29+
30+
- name: Log in to Container Registry
31+
uses: docker/login-action@v3
32+
with:
33+
registry: ${{ env.REGISTRY }}
34+
username: ${{ github.actor }}
35+
password: ${{ secrets.GITHUB_TOKEN }}
36+
37+
- name: Extract metadata
38+
id: meta
39+
uses: docker/metadata-action@v5
40+
with:
41+
images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.name }}
42+
tags: |
43+
type=ref,event=branch
44+
type=ref,event=pr
45+
type=semver,pattern={{version}}
46+
type=semver,pattern={{major}}.{{minor}}
47+
type=semver,pattern={{major}}
48+
type=sha,prefix=sha-
49+
50+
- name: Set up Docker Buildx
51+
uses: docker/setup-buildx-action@v3
52+
53+
- name: Build and push Docker image
54+
id: build-and-push
55+
uses: docker/build-push-action@v5
56+
with:
57+
context: .
58+
file: ${{ matrix.dockerfile }}
59+
push: true
60+
tags: ${{ steps.meta.outputs.tags }}
61+
labels: ${{ steps.meta.outputs.labels }}
62+
platforms: linux/amd64,linux/arm64
63+
cache-from: type=gha
64+
cache-to: type=gha,mode=max
65+
66+
- name: Output image digest
67+
run: |
68+
echo "${{ matrix.name }} image digest: ${{ steps.build-and-push.outputs.digest }}"
69+

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
runs-on: ubuntu-latest
2020
steps:
2121
- name: Checkout
22-
uses: actions/checkout@v2
22+
uses: actions/checkout@v4
2323
with:
2424
fetch-depth: 0
2525

@@ -46,7 +46,7 @@ jobs:
4646
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4747

4848
- name: Publish the Release
49-
uses: softprops/action-gh-release@v1
49+
uses: softprops/action-gh-release@v2
5050
if: ${{ steps.vars.outputs.should_release == 'true' }}
5151
with:
5252
tag_name: ${{ steps.vars.outputs.tag_name }}

Dockerfile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
############### Stage 1 – build ###############
2+
FROM golang:1.24-alpine AS builder
3+
ARG IGNITE_VERSION=v28.10.0
4+
5+
RUN apk add --no-cache build-base git curl bash
6+
RUN curl -L "https://get.ignite.com/cli@${IGNITE_VERSION}!" | bash
7+
8+
WORKDIR /src
9+
COPY . .
10+
11+
# put ./build on the PATH so `which` can see the binary
12+
ENV PATH="/src/build:${PATH}"
13+
14+
RUN ignite chain build && ignite chain init --home .arda-poc
15+
16+
# figure out where the node binary is *at runtime* and copy it out
17+
RUN BIN=$(which arda-pocd 2>/dev/null || true) \
18+
&& if [ -z "$BIN" ]; then echo "arda-pocd not found on PATH"; exit 1; fi \
19+
&& install -Dm755 "$BIN" /out/arda-pocd \
20+
&& cp -r .arda-poc /out/.arda-poc-template
21+
22+
############### Stage 2 – runtime ###############
23+
FROM alpine:3.19
24+
RUN apk update && apk add --no-cache bash curl
25+
26+
# binary + template only
27+
COPY --from=builder /out/arda-pocd /usr/local/bin/arda-pocd
28+
COPY --from=builder /out/.arda-poc-template /template/.arda-poc
29+
30+
# copy in entrypoint script
31+
COPY scripts/entrypoint.sh /usr/local/bin/entrypoint.sh
32+
RUN chmod +x /usr/local/bin/entrypoint.sh
33+
34+
# declare persistent state location
35+
VOLUME ["/data"]
36+
ENV ARDA_HOME=/data/.arda-poc
37+
WORKDIR /data
38+
39+
EXPOSE 26657 1317 9090 4500
40+
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

Makefile

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,34 @@ prod:
170170
cp config.toml ~/.$(APPNAME)/config/config.toml
171171
$(APPNAME)d start
172172
.PHONY: prod
173+
174+
################
175+
### Docker ###
176+
################
177+
178+
# Individual build commands
179+
docker-build-main:
180+
@echo "--> Building arda-pocd docker image"
181+
@docker build -t arda-pocd-ignite -f Dockerfile .
182+
.PHONY: docker-build-main
183+
184+
docker-build-tx-sidecar:
185+
@echo "--> Building tx-sidecar docker image"
186+
@docker build -t tx-sidecar-compose -f cmd/tx-sidecar/Dockerfile .
187+
.PHONY: docker-build-tx-sidecar
188+
189+
# Docker Compose commands
190+
dc-up:
191+
@echo "--> Starting docker-compose services"
192+
@docker-compose up -d --build
193+
.PHONY: dc-up
194+
195+
dc-down:
196+
@echo "--> Stopping docker-compose services"
197+
@docker-compose down
198+
.PHONY: dc-down
199+
200+
dc-logs:
201+
@echo "--> Tailing logs for all services"
202+
@docker-compose logs -f
203+
.PHONY: dc-logs

cmd/tx-sidecar/Dockerfile

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
# Stage 1: Build the application
3+
FROM golang:1.24-alpine AS builder
4+
5+
WORKDIR /src
6+
7+
# Copy go.mod and go.sum files
8+
COPY go.mod go.sum ./
9+
RUN go mod download
10+
11+
# Copy the rest of the application source code
12+
COPY . .
13+
14+
# Build the application
15+
# We are building from the root of the project, so we need to specify the path to the main package
16+
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/tx-sidecar ./cmd/tx-sidecar
17+
18+
# Stage 2: Create the final image
19+
FROM alpine:3.19
20+
21+
WORKDIR /app
22+
23+
# Copy the built binary from the builder stage
24+
COPY --from=builder /app/tx-sidecar .
25+
26+
# The swagger documentation seems to be served by the application, so we copy it as well.
27+
COPY ./cmd/tx-sidecar/docs ./docs
28+
COPY config.yml .
29+
30+
VOLUME /app/local_data
31+
32+
# Expose the port the application runs on
33+
EXPOSE 8080
34+
35+
# Run the application
36+
ENTRYPOINT ["./tx-sidecar"]

cmd/tx-sidecar/main.go

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ import (
3939
"github.com/joho/godotenv"
4040
)
4141

42+
// getEnv is a helper function to read an environment variable or return a fallback value.
43+
func getEnv(key, fallback string) string {
44+
if value, ok := os.LookupEnv(key); ok {
45+
return value
46+
}
47+
return fallback
48+
}
49+
50+
var (
51+
blockchainRestAPIURL = getEnv("BLOCKCHAIN_REST_API_URL", "http://localhost:1317")
52+
grpcAddr = getEnv("GRPC_ADDR", "localhost:9090")
53+
nodeRPCURL = getEnv("NODE_RPC_URL", "http://localhost:26657")
54+
faucetURL = getEnv("FAUCET_URL", "http://localhost:4500")
55+
)
56+
4257
func init() {
4358
zlog.Logger = zlog.Output(zerolog.ConsoleWriter{Out: os.Stdout})
4459
}
@@ -257,8 +272,9 @@ func NewServer(clientCtx client.Context, grpcAddr string) (*Server, error) {
257272
// Ensure that the faucet account from config exists in the keyring.
258273
if _, err := s.clientCtx.Keyring.Key(s.faucetName); err != nil {
259274
zlog.Warn().Msgf("faucet user '%s' from config.yml not found in keyring: %v", s.faucetName, err)
275+
} else {
276+
zlog.Info().Msgf("Faucet user '%s' found in keyring.", s.faucetName)
260277
}
261-
zlog.Info().Msgf("Using '%s' as the faucet account.", s.faucetName)
262278

263279
// Ensure faucet user has the 'bank' role.
264280
if faucetUserData, ok := s.users[s.faucetName]; ok {
@@ -329,7 +345,7 @@ func (s *Server) adminLoginHandler(c *fiber.Ctx) error {
329345

330346
// passthroughGET proxies a GET request to the blockchain REST API and returns the response as-is.
331347
func passthroughGET(path string, c *fiber.Ctx) error {
332-
baseURL := "http://localhost:1317"
348+
baseURL := blockchainRestAPIURL
333349
// Compose the full URL
334350
url := baseURL + path
335351
resp, err := http.Get(url)
@@ -391,45 +407,40 @@ func getMortgagesPassthrough(c *fiber.Ctx) error {
391407
// isBlockchainRunning checks if both the blockchain REST API and gRPC API are accessible
392408
func isBlockchainRunning() bool {
393409
// Check REST API
394-
baseURL := os.Getenv("BLOCKCHAIN_REST_API_URL")
395-
if baseURL == "" {
396-
baseURL = "http://localhost:1317"
397-
}
410+
baseURL := blockchainRestAPIURL
398411
restURL := baseURL + "/cosmos/base/tendermint/v1beta1/node_info"
412+
zlog.Info().Msgf("Checking blockchain REST API at %s", restURL)
399413

400-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
401-
defer cancel()
414+
restCtx, restCancel := context.WithTimeout(context.Background(), 10*time.Second)
415+
defer restCancel()
402416

403-
req, err := http.NewRequestWithContext(ctx, "GET", restURL, nil)
417+
req, err := http.NewRequestWithContext(restCtx, "GET", restURL, nil)
404418
if err != nil {
419+
zlog.Error().Msgf("Blockchain not ready: failed to create request for REST API: %v", err)
405420
return false
406421
}
407422

408423
resp, err := http.DefaultClient.Do(req)
409424
if err != nil {
425+
zlog.Error().Msgf("Blockchain not ready: failed to make request to REST API: %v", err)
410426
return false
411427
}
412428
defer resp.Body.Close()
413429

414-
if resp.StatusCode != 200 {
430+
if resp.StatusCode != http.StatusOK {
431+
zlog.Error().Msgf("Blockchain not ready: REST API returned status %d", resp.StatusCode)
415432
return false
416433
}
417434

418435
// Check gRPC API
419-
grpcHost := "localhost"
420-
if strings.Contains(baseURL, "://") {
421-
if u, err := url.Parse(baseURL); err == nil {
422-
grpcHost = u.Hostname()
423-
}
424-
}
425-
grpcAddr := fmt.Sprintf("%s:9090", grpcHost)
436+
zlog.Info().Msgf("Checking blockchain gRPC API at %s", grpcAddr)
426437

427-
// Try to establish a gRPC connection
428-
grpcConn, err := grpc.Dial(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(), grpc.WithTimeout(5*time.Second))
438+
conn, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
429439
if err != nil {
440+
zlog.Error().Msgf("Blockchain not ready: NewClient failed for gRPC: %v", err)
430441
return false
431442
}
432-
defer grpcConn.Close()
443+
defer conn.Close()
433444

434445
return true
435446
}
@@ -464,8 +475,12 @@ func main() {
464475
zlog.Fatal().Msgf("Failed to parse node URI: %v", err)
465476
}
466477
host := strings.Split(parsedURL.Host, ":")[0]
467-
grpcAddr := fmt.Sprintf("%s:9090", host)
478+
clientCtxGrpcAddr := fmt.Sprintf("%s:9090", host)
468479

480+
if clientCtxGrpcAddr != grpcAddr {
481+
zlog.Warn().Msgf("Using grpc addresses don't match: %s != %s", clientCtxGrpcAddr, grpcAddr)
482+
}
483+
// Use the client context's grpc address if it's set, otherwise use the default.
469484
server, err := NewServer(clientCtx, grpcAddr)
470485
if err != nil {
471486
zlog.Fatal().Msgf("Failed to create server: %v", err)
@@ -557,3 +572,4 @@ func main() {
557572
zlog.Fatal().Msgf("Failed to start server: %v", err)
558573
}
559574
}
575+

config.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ version = "0.38.17"
1616

1717
# TCP or UNIX socket address of the ABCI application,
1818
# or the name of an ABCI application compiled in with the CometBFT binary
19-
proxy_app = "tcp://127.0.0.1:26658"
19+
proxy_app = "tcp://0.0.0.0:26658"
2020

2121
# A custom human readable name for this node
2222
moniker = "ERES Validator"
@@ -195,7 +195,7 @@ tls_cert_file = ""
195195
tls_key_file = ""
196196

197197
# pprof listen address (https://golang.org/pkg/net/http/pprof)
198-
pprof_laddr = "localhost:6060"
198+
pprof_laddr = "0.0.0.0:6060"
199199

200200
#######################################################
201201
### P2P Configuration Options ###

docker-compose.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
services:
2+
arda:
3+
build:
4+
context: .
5+
dockerfile: Dockerfile
6+
image: arda-pocd
7+
ports:
8+
- "26657:26657" # Tendermint RPC
9+
- "1317:1317" # REST API
10+
- "9090:9090" # gRPC
11+
- "4500:4500" # Faucet
12+
volumes:
13+
- arda_state:/data # <-- named volume, not host bind
14+
environment:
15+
- ARDA_HOME=/data/.arda-poc
16+
17+
sidecar:
18+
build:
19+
context: .
20+
dockerfile: cmd/tx-sidecar/Dockerfile
21+
image: tx-sidecar-compose
22+
volumes:
23+
- arda_state:/data # share chain state
24+
- sidecar_data:/app/local_data # own DB
25+
ports:
26+
- "8080:8080"
27+
environment:
28+
- BLOCKCHAIN_REST_API_URL=http://arda:1317
29+
- GRPC_ADDR=arda:9090
30+
- NODE_RPC_URL=http://arda:26657
31+
- FAUCET_URL=http://arda:4500
32+
- ARDA_HOME=/data/.arda-poc # <-- tell sidecar where to find the keyring
33+
depends_on:
34+
- arda
35+
36+
volumes:
37+
arda_state:
38+
sidecar_data:

0 commit comments

Comments
 (0)