add prometheus
All checks were successful
Build and Publish / BuildAndDeploy (push) Successful in 49s

This commit is contained in:
matst80
2024-11-09 11:46:00 +01:00
parent cb96e75f99
commit cfbb2e29c2
8 changed files with 260 additions and 16 deletions

View File

@@ -6,7 +6,7 @@ metadata:
app: cart-actor app: cart-actor
name: cart-actor name: cart-actor
spec: spec:
replicas: 3 replicas: 2
selector: selector:
matchLabels: matchLabels:
app: cart-actor app: cart-actor
@@ -50,11 +50,23 @@ spec:
env: env:
- name: TZ - name: TZ
value: "Europe/Stockholm" value: "Europe/Stockholm"
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
--- ---
kind: Service kind: Service
apiVersion: v1 apiVersion: v1
metadata: metadata:
name: cart-actor name: cart-actor
annotations:
prometheus.io/port: "8080"
prometheus.io/scrape: "true"
prometheus.io/path: "/metrics"
spec: spec:
selector: selector:
app: cart-actor app: cart-actor
@@ -89,4 +101,16 @@ spec:
service: service:
name: cart-actor name: cart-actor
port: port:
number: 8080 number: 8080
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: cart-scaler
spec:
scaleTargetRef:
kind: Deployment
name: cart-actor
minReplicas: 1
maxReplicas: 5
targetCPUUtilizationPercentage: 50

9
go.mod
View File

@@ -5,5 +5,14 @@ go 1.23.0
toolchain go1.23.2 toolchain go1.23.2
require ( require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
golang.org/x/sys v0.22.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect google.golang.org/protobuf v1.34.2 // indirect
) )

2
go.sum
View File

@@ -18,6 +18,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=

28
main.go
View File

@@ -8,9 +8,20 @@ import (
"time" "time"
messages "git.tornberg.me/go-cart-actor/proto" messages "git.tornberg.me/go-cart-actor/proto"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
opsProcessed = promauto.NewCounter(prometheus.CounterOpts{
Name: "cart_grain_spawned_total",
Help: "The total number of spawned grains",
})
) )
func spawn(id CartId) (*CartGrain, error) { func spawn(id CartId) (*CartGrain, error) {
opsProcessed.Inc()
ret := &CartGrain{ ret := &CartGrain{
Id: id, Id: id,
Items: []CartItem{}, Items: []CartItem{},
@@ -100,9 +111,11 @@ func (s *PoolServer) Serve() *http.ServeMux {
return mux return mux
} }
var clientName = os.Getenv("NODE_NAME")
func main() { func main() {
// Create a new instance of the server // Create a new instance of the server
storage, err := NewDiskStorage("data/state.gob") storage, err := NewDiskStorage(fmt.Sprintf("data/%s_state.gob", clientName))
if err != nil { if err != nil {
log.Printf("Error loading state: %v\n", err) log.Printf("Error loading state: %v\n", err)
} }
@@ -111,7 +124,13 @@ func main() {
storage: storage, storage: storage,
} }
syncedPool := NewSyncedPool(app.pool) syncedPool, err := NewSyncedPool(app.pool, clientName)
if err != nil {
log.Fatalf("Error creating synced pool: %v\n", err)
}
// if local
syncedPool.AddRemote("localhost")
rpcHandler, err := NewGrainHandler(app.pool, ":1337") rpcHandler, err := NewGrainHandler(app.pool, ":1337")
if err != nil { if err != nil {
@@ -123,10 +142,11 @@ func main() {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/api/", http.StripPrefix("/api", syncedServer.Serve())) mux.Handle("/api/", http.StripPrefix("/api", syncedServer.Serve()))
mux.HandleFunc("GET /add/remote/{host}", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("GET /add/remote/{host}", func(w http.ResponseWriter, r *http.Request) {
remotePool := NewRemoteGrainPool(fmt.Sprintf("%s:1337", r.PathValue("host"))) syncedPool.AddRemote(r.PathValue("host"))
syncedPool.AddRemote(remotePool)
}) })
mux.HandleFunc("GET /save", app.HandleSave) mux.HandleFunc("GET /save", app.HandleSave)
mux.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", mux) http.ListenAndServe(":8080", mux)
} }

View File

@@ -19,7 +19,7 @@ type CartPacket struct {
DataLength uint16 DataLength uint16
} }
type ResponsePacket struct { type Packet struct {
Version uint16 Version uint16
MessageType uint16 MessageType uint16
DataLength uint16 DataLength uint16
@@ -45,7 +45,7 @@ func SendPacket(conn io.Writer, messageType uint16, datafn func(w io.Writer) err
if err != nil { if err != nil {
return err return err
} }
binary.Write(conn, binary.LittleEndian, ResponsePacket{ binary.Write(conn, binary.LittleEndian, Packet{
Version: 1, Version: 1,
MessageType: messageType, MessageType: messageType,
DataLength: uint16(len(data)), DataLength: uint16(len(data)),
@@ -55,7 +55,7 @@ func SendPacket(conn io.Writer, messageType uint16, datafn func(w io.Writer) err
} }
func SendRawResponse(conn io.Writer, data []byte) error { func SendRawResponse(conn io.Writer, data []byte) error {
binary.Write(conn, binary.LittleEndian, ResponsePacket{ binary.Write(conn, binary.LittleEndian, Packet{
Version: 1, Version: 1,
MessageType: ResponseBody, MessageType: ResponseBody,
DataLength: uint16(len(data)), DataLength: uint16(len(data)),
@@ -76,7 +76,7 @@ func SendProxyResponse(conn io.Writer, data any) error {
} }
func ReceivePacket(conn io.Reader) (uint16, []byte, error) { func ReceivePacket(conn io.Reader) (uint16, []byte, error) {
var packet ResponsePacket var packet Packet
err := binary.Read(conn, binary.LittleEndian, &packet) err := binary.Read(conn, binary.LittleEndian, &packet)
if err != nil { if err != nil {
return packet.MessageType, nil, err return packet.MessageType, nil, err

View File

@@ -14,5 +14,8 @@ message AddItem {
string Image = 6; string Image = 6;
} }
message Negotiate {
repeated Host host = 1;
}

View File

@@ -1,22 +1,173 @@
package main package main
import (
"encoding/binary"
"fmt"
"io"
"log"
"net"
"strings"
)
type Quorum interface {
Negotiate(knownHosts []string) ([]string, error)
ListChanged([]CartId) error
}
type RemoteHost struct {
Host string
Pool *RemoteGrainPool
connection net.Conn
}
type SyncedPool struct { type SyncedPool struct {
listener net.Listener
Hostname string
local *GrainLocalPool local *GrainLocalPool
remotes []*RemoteGrainPool remotes []RemoteHost
remoteIndex map[CartId]*RemoteGrainPool remoteIndex map[CartId]*RemoteGrainPool
} }
func NewSyncedPool(local *GrainLocalPool) *SyncedPool { func NewSyncedPool(local *GrainLocalPool, hostname string) (*SyncedPool, error) {
return &SyncedPool{ listen := fmt.Sprintf("%s:1338", hostname)
l, err := net.Listen("tcp", listen)
if err != nil {
return nil, err
}
pool := &SyncedPool{
Hostname: hostname,
local: local, local: local,
remotes: make([]*RemoteGrainPool, 0), listener: l,
remotes: make([]RemoteHost, 0),
remoteIndex: make(map[CartId]*RemoteGrainPool), remoteIndex: make(map[CartId]*RemoteGrainPool),
} }
go func() {
for {
conn, err := l.Accept()
if err != nil {
log.Printf("Error accepting connection: %v\n", err)
continue
}
log.Printf("Got connection from %s", conn.RemoteAddr().String())
go pool.handleConnection(conn)
}
}()
return pool, nil
}
const (
RemoteNegotiate = uint16(3)
RemoteGrainChanged = uint16(4)
)
func (p *SyncedPool) handleConnection(conn net.Conn) {
defer conn.Close()
var packet Packet
for {
err := binary.Read(conn, binary.LittleEndian, &packet)
if err != nil {
if err == io.EOF {
break
}
log.Printf("Error in connection: %v\n", err)
}
// if packet.Version != 1 {
// log.Printf("Invalid version %d\n", packet.Version)
// return
// }
switch packet.MessageType {
case RemoteNegotiate:
data := make([]byte, packet.DataLength)
conn.Read(data)
knownHosts := strings.Split(string(data), ";")
log.Printf("Negotiated with remote, found %v hosts\n", knownHosts)
for _, h := range knownHosts {
err = p.AddRemote(h)
if err != nil {
log.Printf("Error adding remote %s: %v\n", h, err)
}
}
SendPacket(conn, RemoteNegotiate, func(w io.Writer) error {
hostnames := make([]string, 0, len(p.remotes))
for _, r := range p.remotes {
hostnames = append(hostnames, r.Host)
}
w.Write([]byte(strings.Join(hostnames, ";")))
return nil
})
case RemoteGrainChanged:
// remote grain changed
log.Printf("Remote grain changed\n")
for err == nil {
id := make([]byte, 16)
_, err = conn.Read(id)
log.Printf("Remote grain %s changed\n", id)
}
}
}
} }
func (p *SyncedPool) AddRemote(remote *RemoteGrainPool) { func (h *RemoteHost) Negotiate(knownHosts []string) ([]string, error) {
SendPacket(h.connection, RemoteNegotiate, func(w io.Writer) error {
w.Write([]byte(strings.Join(knownHosts, ";")))
return nil
})
t, data, err := ReceivePacket(h.connection)
if err != nil {
return nil, err
}
if t != RemoteNegotiate {
return nil, fmt.Errorf("unexpected message type %d", t)
}
return strings.Split(string(data), ";"), nil
}
func (p *SyncedPool) Negotiate(knownHosts []string) ([]string, error) {
allHosts := make(map[string]struct{}, 0)
for _, r := range p.remotes {
hosts, err := r.Negotiate(knownHosts)
if err != nil {
return nil, err
}
for _, h := range hosts {
allHosts[h] = struct{}{}
}
}
ret := make([]string, 0, len(allHosts))
for h := range allHosts {
ret = append(ret, h)
}
return ret, nil
}
func (p *SyncedPool) ListChanged(ids []CartId) error {
return nil
}
func (p *SyncedPool) AddRemote(address string) error {
for _, r := range p.remotes {
if r.Host == address {
log.Printf("Remote %s already exists\n", address)
return fmt.Errorf("remote %s already exists", address)
}
}
connection, err := net.Dial("tcp", fmt.Sprintf("%s:1338", address))
if err != nil {
return err
}
pool := NewRemoteGrainPool(fmt.Sprintf(address, 1337))
remote := RemoteHost{
connection: connection,
Pool: pool,
Host: address,
}
p.remotes = append(p.remotes, remote) p.remotes = append(p.remotes, remote)
// get all available grains from remote, and start syncing log.Printf("Added remote %s\n", remote.Host)
return nil
} }
func (p *SyncedPool) Process(id CartId, messages ...Message) ([]byte, error) { func (p *SyncedPool) Process(id CartId, messages ...Message) ([]byte, error) {

35
synced-pool_test.go Normal file
View File

@@ -0,0 +1,35 @@
package main
import (
"testing"
"time"
)
func TestConnection(t *testing.T) {
// TestConnection tests the connection to the server
t.Log("Testing connection to server")
localPool := NewGrainLocalPool(100, time.Minute, func(id CartId) (*CartGrain, error) {
return &CartGrain{
Id: id,
storageMessages: []Message{},
Items: []CartItem{},
TotalPrice: 0,
}, nil
})
pool, err := NewSyncedPool(localPool, "localhost")
if err != nil {
t.Errorf("Error creating pool: %v", err)
}
err = pool.AddRemote("localhost")
if err != nil {
t.Errorf("Error adding remote: %v", err)
}
allHosts, err := pool.Negotiate([]string{"kalle", "pelle"})
if err != nil {
t.Errorf("Error negotiating: %v", err)
}
if len(allHosts) != 1 {
t.Errorf("Expected 1 host, got %d", len(allHosts))
}
}