24/12/2021 - DOCKER, GIT, GO, KUBERNETES
Bu, birden çok dilin aynı depo altında tutulduğu bir monorepo tasarımının sadece bir örneğidir. Her dilin kendi klasörü vardır. Tüm dilleri aynı depoda tutmak yerine monorepoyu dil bazında da bölebilirsiniz. Her şey size ve ihtiyaçlarınıza bağlıdır.
Burada size bir fikir vermesi için Go dili ile çalışacağız. Üç servis olacak ve her biri birbiriyle konuşabilecek. Test söz konusu olduğunda, istediğimiz servisi çalıştırmak için ismini verebileceğimiz bir Docker imajımız olacak. Daha sonra bu imajı, üç servisi de çalıştırmak için Kubernetes'e dağıtacağız. Tüm hizmetlerin dışarıdan ulaşabileceğimiz uç noktaları vardır. Ayrıca, servisten servise iletişim için bazı uç noktalar olacaktır.
Aklınızda bulundurmanız gereken şey, bunun tamamlanmamış bir örnek olduğu göz önüne alındığında, bazı gereksiz tekrarlar ve daha az değerli parçalar olacağıdır. Ana amaç, size bir fikir vermektir!
├── go # where go stuff lives
├── doc # where documentation lives (meant to cover all languages)
├── infra # where infrastructure stuff lives (meant to cover all languages)
├── js # where javascript stuff lives
└── php # where php stuff lives
├── .gitignore
├── go
│ ├── cmd
│ │ └── monorepo
│ │ └── main.go
│ ├── go.mod
│ ├── pkg
│ │ └── client
│ │ └── http.go
│ └── svc
│ ├── account
│ │ └── main.go
│ ├── exam
│ │ └── main.go
│ └── student
│ └── main.go
├── infra
│ ├── dev
│ │ ├── Makefile
│ │ └── docker
│ │ └── docker-compose.yaml
│ ├── prod
│ │ ├── Makefile
│ │ ├── docker
│ │ │ └── go
│ │ │ └── Dockerfile
│ │ └── k8s
│ │ └── go
│ │ ├── account.yaml
│ │ ├── exam.yaml
│ │ └── student.yaml
│ └── qa
│ ├── docker
│ └── k8s
├── js
│ └── ... put your stuff here
└── php
└── ... put your stuff here
go/bin
version: "3.4"
services:
monorepo-mysql:
container_name: "monorepo-mysql"
image: "mysql:5.7.24"
command:
- "--character-set-server=utf8mb4"
- "--collation-server=utf8mb4_unicode_ci"
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: "root"
.PHONY: help
help: ## Display available commands.
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
# DOCKER -----------------------------------------------------------------------
.PHONY: dev-docker-up
dev-docker-up: ## Bring dev environment up in attached mode.
docker-compose -f docker/docker-compose.yaml up --build
.PHONY: dev-docker-down
dev-docker-down: ## Stop and clear dev environment.
docker-compose -f docker/docker-compose.yaml down
docker system prune --volumes --force
.PHONY: dev-docker-config
dev-docker-config: ## Echo dev environment config.
docker-compose -f docker/docker-compose.yaml config
FROM golang:1.17.5-alpine3.15 as build
WORKDIR /source
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o monorepo cmd/monorepo/main.go
FROM alpine:3.15
COPY --from=build /source/monorepo /monorepo
EXPOSE 8000
ENTRYPOINT ["./monorepo", "--svc"]
.PHONY: help
help: ## Display available commands.
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
# CI/CD ------------------------------------------------------------------------
.PHONY: docker-push-go
docker-push-go: ## Build, tag and push go image to registry then clean up.
DOCKER_BUILDKIT=0 docker build -t you/monorepo-go:latest -f docker/go/Dockerfile ../../go
docker push you/monorepo-go:latest
docker rmi you/monorepo-go:latest
docker system prune --volumes --force
.PHONY: k8s-deploy-go
k8s-deploy-go: ## Deploy go applications.
kubectl apply -f k8s/go/account.yaml
kubectl apply -f k8s/go/exam.yaml
kubectl apply -f k8s/go/student.yaml
Ben burada üç yaml dosyasını da eklemeyeğim ama siz ekleyin. Bunu yaptığınızda, account
kelimesini student
ve exam
ile değiştirmeniz yeterlidir.
apiVersion: v1
kind: Service
metadata:
name: svc-account
namespace: prod
spec:
type: ClusterIP
selector:
app: account
ports:
- port: 8000
targetPort: 8000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep-account
namespace: prod
labels:
app: account
spec:
replicas: 1
selector:
matchLabels:
app: account
template:
metadata:
labels:
app: account
spec:
containers:
- name: go
image: you/monorepo-go:latest
args:
- account
ports:
- containerPort: 8000
package main
import (
"flag"
"fmt"
"log"
"os"
"monorepo/svc/account"
"monorepo/svc/exam"
"monorepo/svc/student"
)
const usage = `Description: Service to run
Usage: %s [options]
Options:
`
func main() {
var svc string
flag.StringVar(&svc, "svc", svc, "Service name.")
flag.Usage = func() {
_, _ = fmt.Fprintf(flag.CommandLine.Output(), usage, os.Args[0])
flag.PrintDefaults()
}
flag.Parse()
switch svc {
case "account":
account.Start()
case "exam":
exam.Start()
case "student":
student.Start()
}
log.Println("Unknown service")
}
package client
import (
"net/http"
)
func HTTPRequest(uri string) error {
req, err := http.NewRequest("GET", uri, nil)
if err != nil {
return err
}
if _, err := http.DefaultTransport.RoundTrip(req); err != nil {
return err
}
return nil
}
package account
import (
"log"
"net/http"
"monorepo/pkg/client"
)
func Start() {
rtr := http.DefaultServeMux
rtr.HandleFunc("/pay-debt", func(http.ResponseWriter, *http.Request) {
log.Println("/pay-debt")
if err := client.HTTPRequest("http://svc-exam:8000/unlock-results"); err != nil {
log.Println(err)
}
})
srv := &http.Server{Handler: rtr, Addr: "0.0.0.0:8000"}
if err := srv.ListenAndServe(); err != nil {
log.Fatalln(err)
}
}
package exam
import (
"log"
"net/http"
"monorepo/pkg/client"
)
func Start() {
rtr := http.DefaultServeMux
rtr.HandleFunc("/unlock-results", func(http.ResponseWriter, *http.Request) {
log.Println("/unlock-results")
})
rtr.HandleFunc("/publish-results", func(http.ResponseWriter, *http.Request) {
log.Println("/publish-results")
if err := client.HTTPRequest("http://svc-student:8000/publish-results"); err != nil {
log.Println(err)
}
})
srv := &http.Server{Handler: rtr, Addr: "0.0.0.0:8000"}
if err := srv.ListenAndServe(); err != nil {
log.Fatalln(err)
}
}
package student
import (
"log"
"net/http"
"monorepo/pkg/client"
)
func Start() {
rtr := http.DefaultServeMux
rtr.HandleFunc("/pay-debt", func(http.ResponseWriter, *http.Request) {
log.Println("/pay-debt")
if err := client.HTTPRequest("http://svc-account:8000/pay-debt"); err != nil {
log.Println(err)
}
})
rtr.HandleFunc("/publish-results", func(http.ResponseWriter, *http.Request) {
log.Println("/publish-results")
})
srv := &http.Server{Handler: rtr, Addr: "0.0.0.0:8000"}
if err := srv.ListenAndServe(); err != nil {
log.Fatalln(err)
}
}
module monorepo
go 1.17
Go servislerini ve Docker imajını oluşturmak için monorepo/infra/prod$ make docker-push-go
komutunu çalıştırın. Servisleri yerel ortamda manuel olarak test etmek istiyorsanız aşağıdakileri yapabilirsiniz. Daha sonra student hizmetini test etmek için http://0.0.0.0:8888
adresini kullanabilirsiniz.
monorepo$ docker build -t you/monorepo-go:latest -f infra/prod/docker/go/Dockerfile go/
monorepo$ docker run --name monorepo-student -d -p 8888:8000 you/monorepo-go:latest student
$ docker ps
IMAGE COMMAND PORTS NAMES
you/monorepo-go:latest "./monorepo --svc student" 0.0.0.0:8888->8000/tcp, :::8888->8000/tcp monorepo-student
Namaspace bilgisini yaratmak için öncelikle $ kubectl create namespace prod
komutunu çalıştırın ve daha sonra monorepo/infra/prod$ make k8s-deploy-go
komutu ile servisin iletimini gerçekleştirin. Kaynakları aşağıda gösterildiği gibi görmelisiniz.
$ kubectl -n prod get all
NAME READY STATUS RESTARTS AGE
pod/dep-account-6db8bb9c68-v5dr6 1/1 Running 0 18s
pod/dep-exam-54b78448d-nd6js 1/1 Running 0 17s
pod/dep-student-6fc98cc9dd-qpxwc 1/1 Running 0 17s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/svc-account ClusterIP 10.108.90.798000/TCP 18s
service/svc-exam ClusterIP 10.108.226.1808000/TCP 18s
service/svc-student ClusterIP 10.109.116.758000/TCP 17s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/dep-account 1/1 1 1 18s
deployment.apps/dep-exam 1/1 1 1 17s
deployment.apps/dep-student 1/1 1 1 17s
NAME DESIRED CURRENT READY AGE
replicaset.apps/dep-account-6db8bb9c68 1 1 1 18s
replicaset.apps/dep-exam-54b78448d 1 1 1 17s
replicaset.apps/dep-student-6fc98cc9dd 1 1 1 17s
Öncelikle aşağıdaki komutla servislerinizi yerel ortamınızdan erişilebilir hale getirin.
$ kubectl -n prod port-forward service/svc-student 8001:8000
Forwarding from 127.0.0.1:8001 -> 8000
Forwarding from [::1]:8001 -> 8000
$ kubectl -n prod port-forward service/svc-exam 8002:8000
Forwarding from 127.0.0.1:8002 -> 8000
Forwarding from [::1]:8002 -> 8000
$ kubectl -n prod port-forward service/svc-account 8003:8000
Forwarding from 127.0.0.1:8003 -> 8000
Forwarding from [::1]:8003 -> 8000
Aşağıdaki uç noktaları kullanabilir ve emin olmak için pod günlüklerini izleyebilirsiniz.
http://127.0.0.1:8001/pay-debt (external for you to consume)
http://127.0.0.1:8001/publish-results (internal for exam service to consume)
# Exam
http://127.0.0.1:8002/publish-results (external for you to consume)
http://127.0.0.1:8002/unlock-results (internal for account service to consume)
# Account
http://127.0.0.1:8003/pay-debt (internal for student service to consume)