多年来,我们一直投入大量的个人时间和精力,与大家分享我们的知识。然而,我们现在需要你的帮助来维持这个博客的运行。你所要做的就是点击网站上的一个广告,否则它将由于托管等费用而不幸被关闭。谢谢你。
这只是一个单库设计的例子,多语言被保存在同一个资源库中。每种语言都有自己的文件夹。你也可以把monorepo分成不同的语言,而不是把所有的语言放在同一个版本库里。这一切都取决于你和你的需求。
在这里,我们将使用Go语言来给你一个概念。将会有三个服务,每个服务都可以相互对话。当涉及到测试时,我们将建立一个Docker镜像,其中包含一个二进制文件,你通过服务名称来启动它。然后,我们将把这个镜像部署到Kubernetes生产集群,以运行所有三个服务。所有服务都有端点供我们从外部调用。同时,也会有一些端点用于服务与服务之间的通信。
你需要记住的是,鉴于这是一个假的/未完成的例子,会有一些重复和不太重视的部分。主要的一点是给你一个概念!
结构
描述
├── 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
文件
.gitignore
go/bin
dev/docker-compose.yaml
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"
dev/Makefile
.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
prod/Dockerfile
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"]
prod/Makefile
.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
prod/k8s文件
我不打算复制所有三个yaml文件,但你可以。如果你这样做,只需用student 和exam 替换account 字样。
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
go/cmd/main.go
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")
}
go/pkg/http.go
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
}
go/account/main.go
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)
}
}
go/exam/main.go
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)
}
}
go/student/main.go
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
Docker推送
运行monorepo/infra/prod$ make docker-push-go ,为go服务构建并推送docker镜像。如果你想在本地环境中手动测试服务,你可以做以下工作。然后你可以使用http://0.0.0.0:8888 来测试学生服务端点。
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
部署到Kubernetes
首先运行$ kubectl create namespace prod 命令来创建命名空间,然后运行monorepo/infra/prod$ make k8s-deploy-go 命令来部署go服务。你应该看到如下所示的资源:
$ 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.79 8000/TCP 18s
service/svc-exam ClusterIP 10.108.226.180 8000/TCP 18s
service/svc-student ClusterIP 10.109.116.75 8000/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
测试
首先,用下面的命令使你的服务可以从你的本地环境访问:
$ 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
现在你可以使用下面的端点,并观察pod日志进行确认:
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)