Skip to content

Commit

Permalink
feat: build go images
Browse files Browse the repository at this point in the history
  • Loading branch information
Apoorva64 committed Oct 13, 2024
1 parent e3911b5 commit 159d987
Show file tree
Hide file tree
Showing 8 changed files with 488 additions and 13 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/docker-build-gateway.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Docker Build and Push Gateway Images
on:
push:
branches:
- main
- fix-ci
pull_request:

permissions: write-all

jobs:
build-push-gateway:
runs-on: ubuntu-latest
strategy:
matrix:
application: [adapter, dataManager, AlerteDetector, pre-treatment, transmitter]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: downcase REPO name as output
id: downcase
run: |
echo "::set-output name=downcase::$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')"
- name: Build and push api image
uses: docker/build-push-action@v5
with:
platforms: linux/amd64,linux/arm64
push: true
context: gateway/${{ matrix.application }}
tags: ghcr.io/${{ steps.downcase.outputs.downcase }}/gateway-${{ matrix.application }}:${{ github.sha }}, ghcr.io/${{ steps.downcase.outputs.downcase }}/gateway-${{ matrix.application }}:${{ github.ref_name }}

47 changes: 47 additions & 0 deletions gateway/AlerteDetector/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Stage 1: Build Stage
# Use a specific version of the official Golang image as the base image
FROM golang:1.22-bullseye AS build

# Create a non-root user for running the application
RUN useradd -u 1001 nonroot

# Set the working directory inside the container
WORKDIR /app

# Copy only the go.mod file to install dependencies efficiently and leverage layer caching
COPY go.mod ./

# Set the GIN_MODE environment variable to release
ENV GIN_MODE=release


# Use cache mounts to speed up the installation of existing dependencies
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go mod download

# Copy the entire application source code
COPY . .

# Compile the application during build and statically link the binary
RUN go build \
-ldflags="-linkmode external -extldflags -static" \
-tags netgo \
-o go-app

# Stage 2: Deployable Image
# Use a minimal scratch image as the base image for the final image
FROM scratch

# Copy the /etc/passwd file from the build stage to provide non-root user information
COPY --from=build /etc/passwd /etc/passwd

# Copy the compiled application binary from the build stage to the final image
COPY --from=build /app/go-app /go-app

# Use the non-root user created in the build stage
USER nonroot


# Define the command to run the application when the container starts
CMD ["./go-app"]
47 changes: 47 additions & 0 deletions gateway/adapter/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Stage 1: Build Stage
# Use a specific version of the official Golang image as the base image
FROM golang:1.22-bullseye AS build

# Create a non-root user for running the application
RUN useradd -u 1001 nonroot

# Set the working directory inside the container
WORKDIR /app

# Copy only the go.mod file to install dependencies efficiently and leverage layer caching
COPY go.mod ./

# Set the GIN_MODE environment variable to release
ENV GIN_MODE=release


# Use cache mounts to speed up the installation of existing dependencies
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go mod download

# Copy the entire application source code
COPY . .

# Compile the application during build and statically link the binary
RUN go build \
-ldflags="-linkmode external -extldflags -static" \
-tags netgo \
-o go-app

# Stage 2: Deployable Image
# Use a minimal scratch image as the base image for the final image
FROM scratch

# Copy the /etc/passwd file from the build stage to provide non-root user information
COPY --from=build /etc/passwd /etc/passwd

# Copy the compiled application binary from the build stage to the final image
COPY --from=build /app/go-app /go-app

# Use the non-root user created in the build stage
USER nonroot


# Define the command to run the application when the container starts
CMD ["./go-app"]
54 changes: 41 additions & 13 deletions gateway/batch-send/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
# Stage 1: Build Stage
# Use a specific version of the official Golang image as the base image
FROM golang:1.22-bullseye AS build

FROM golang:1.23
# Create a non-root user for running the application
RUN useradd -u 1001 nonroot

# Set destination for COPY
WORKDIR /app
# Set the working directory inside the container
WORKDIR /app

# Download Go modules
COPY go.mod go.sum ./
RUN go mod download
# Copy only the go.mod file to install dependencies efficiently and leverage layer caching
COPY go.mod ./

# Copy the source code. Note the slash at the end, as explained in
# https://docs.docker.com/reference/dockerfile/#copy
COPY *.go ./
# Set the GIN_MODE environment variable to release
ENV GIN_MODE=release

# Build
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/batch-send

# Run the compiled binary
CMD ["/app/batch-send"]
# Use cache mounts to speed up the installation of existing dependencies
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go mod download

# Copy the entire application source code
COPY . .

# Compile the application during build and statically link the binary
RUN go build \
-ldflags="-linkmode external -extldflags -static" \
-tags netgo \
-o go-app

# Stage 2: Deployable Image
# Use a minimal scratch image as the base image for the final image
FROM scratch

# Copy the /etc/passwd file from the build stage to provide non-root user information
COPY --from=build /etc/passwd /etc/passwd

# Copy the compiled application binary from the build stage to the final image
COPY --from=build /app/go-app /go-app

# Use the non-root user created in the build stage
USER nonroot


# Define the command to run the application when the container starts
CMD ["./go-app"]
171 changes: 171 additions & 0 deletions gateway/batch-send/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package main

import (
"bytes"
"context"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/golang/snappy"
"github.com/prometheus/prometheus/prompb"
"io"
"net/http"
"time"
)

type TimeSeries struct {
Labels []Label
Sample []Sample
}

type Label struct {
Name string
Value string
}

type Sample struct {
Time time.Time
Value float64
}

// ClientOption is used to set custom client options.
type ClientOption func(opts *clientOptions)

// HttpClient option allows configuring custom HTTP client.
func HttpClient(client *http.Client) ClientOption {
return func(opts *clientOptions) {
opts.httpClient = client
}
}

type clientOptions struct {
httpClient *http.Client
}

func NewClient(endpoint string, options ...ClientOption) *Client {
opts := clientOptions{
httpClient: &http.Client{Timeout: 30 * time.Second},
}
for _, opt := range options {
opt(&opts)
}
p := &Client{
endpoint: endpoint,
opts: &opts,
}
return p
}

// Client is Prometheus Remote Write client.
type Client struct {
endpoint string
opts *clientOptions
}

type WriteOption func(opts *writeOptions)

// WriteHeaders allows passing custom HTTP headers. Once common use case is to pass `X-Scope-OrgID` header for Cortex tenant.
func WriteHeaders(headers map[string]string) WriteOption {
return func(opt *writeOptions) {
opt.headers = headers
}
}

type writeOptions struct {
headers map[string]string
}

type WriteRequest struct {
TimeSeries []TimeSeries
}

type WriteResponse struct {
}

// Write sends HTTP requests to Prometheus Remote Write compatible API endpoint including Prometheus, Cortex and VictoriaMetrics.
func (p *Client) Write(ctx context.Context, req *WriteRequest, options ...WriteOption) (*WriteResponse, error) {
opts := writeOptions{}
for _, opt := range options {
opt(&opts)
}
// Marshal proto and compress.
pbBytes, err := proto.Marshal(&prompb.WriteRequest{
Timeseries: toProtoTimeSeries(req.TimeSeries),
})
if err != nil {
return nil, fmt.Errorf("promwrite: marshaling remote write request proto: %w", err)
}

compressedBytes := snappy.Encode(nil, pbBytes)

// Prepare http request.
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint, bytes.NewBuffer(compressedBytes))
if err != nil {
return nil, err
}
httpReq.Header.Add("X-Prometheus-Remote-Write-Version", "0.1.0")
httpReq.Header.Add("Content-Encoding", "snappy")
httpReq.Header.Set("Content-Type", "application/x-protobuf")
for k, v := range opts.headers {
httpReq.Header.Add(k, v)
}

// Send http request.
httpResp, err := p.opts.httpClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("promwrite: sending remote write request: %w", err)
}
defer httpResp.Body.Close()

if st := httpResp.StatusCode; st/100 != 2 {
msg, _ := io.ReadAll(httpResp.Body)
return nil, &WriteError{
err: fmt.Errorf("promwrite: expected status %d, got %d: %s", http.StatusOK, st, string(msg)),
code: st,
}
}
return &WriteResponse{}, nil
}

func toProtoTimeSeries(timeSeries []TimeSeries) []prompb.TimeSeries {
res := make([]prompb.TimeSeries, len(timeSeries))
for i, ts := range timeSeries {
labels := make([]prompb.Label, len(ts.Labels))
for j, lb := range ts.Labels {
labels[j] = prompb.Label{
Name: lb.Name,
Value: lb.Value,
}
}
pbTs := prompb.TimeSeries{
Labels: labels,
Samples: toProtoSamples(ts.Sample),
}
res[i] = pbTs
}
return res
}

func toProtoSamples(sample []Sample) []prompb.Sample {
res := make([]prompb.Sample, len(sample))
for i, s := range sample {
res[i] = prompb.Sample{
Timestamp: s.Time.UnixNano() / int64(time.Millisecond),
Value: s.Value,
}
}
return res
}

// WriteError returned if HTTP call is finished with response status code, but it was not successful.
type WriteError struct {
err error
code int
}

func (e *WriteError) Error() string {
return e.err.Error()
}

func (e *WriteError) StatusCode() int {
return e.code
}
Loading

0 comments on commit 159d987

Please sign in to comment.