前言
- 虽然去年就已经开始接触微服务, 并且在个人服务器运行了起来, 但是都是使用直连模式, 今天正好有空升级
go-zero
的代码, 同时更新架构
框架
-
因为去年用的就是
go-zero
, 今天只是更新的版本 https://github.com/zeromicro/go-zero -
代码目录基本就是根目录
app
下会有双层服务, 根目录下有pkg
公用代码,比如配置中心获取, 然后是公共构建Dockerfile
(所有服务) -
Dockerfile
使用双阶段构建, 然后构建的时候会传递参数SERVICE_PATH
表示要构建的服务docker build -t xxx:xx --build-arg SERVICE_PATH=${SERVICE_PATH} .
FROM golang:alpine3.18 AS builder
LABEL stage=gobuilder
## 定义项目的根目录
ARG SERVICE_PATH
ENV CGO_ENABLED 0
RUN echo -e 'https://mirrors.aliyun.com/alpine/v3.18/main/\nhttps://mirrors.aliyun.com/alpine/v3.18/community/' > /etc/apk/repositories && apk update --no-cache && apk add --no-cache tzdata
WORKDIR /build
COPY . .
RUN go env -w GO111MODULE=on
RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
RUN go mod download
RUN go build -ldflags="-s -w" -o /build/main /build/app/${SERVICE_PATH}
## 使用空镜像构建
FROM scratch
## 定义项目的根目录
ARG SERVICE_PATH
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
ENV TZ Asia/Shanghai
WORKDIR /app
COPY --from=builder /build/main /app/main
ENTRYPOINT ["./main"]
- 单机部署同一个服务多份, 端口会占用, 所以会动态获取一个可用的端口, 然后在
go-zero
启动时使用这个生成的端口(而不是配置文件中的)
func GetAvailablePort() (int, error) {
address, err := net.ResolveTCPAddr("tcp", "0.0.0.0:0")
if err != nil {
return 0, err
}
listener, err := net.ListenTCP("tcp", address)
if err != nil {
return 0, err
}
defer listener.Close()
return listener.Addr().(*net.TCPAddr).Port, nil
}
服务注册发现 && 配置中心
- 个人搭建最主要的就是追求低成本且能正常运行起来
- 服务注册&&发现用的 https://github.com/etcd-io/etcd
- 配置中心也用的
etcd
(只在启动的时候拉取), 然后配置管理使用的 https://github.com/evildecay/etcdkeeper etcdkeeper
效果很不错, 内置支持json, yaml
等配置
docker-compose
用来搭建基础服务
version: '3'
services:
etcd:
container_name: etcd
image: bitnami/etcd:3
restart: always
environment:
- ETCDCTL_API=3
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_ADVERTISE_CLIENT_URLS=http://127.0.0.1:2379
volumes:
- "./data/etcd:/bitnami/etcd/data"
# ports:
# - "2379:2379"
# - "2380:2380"
network_mode: host
etcdkeeper:
hostname: etcdkeeper
image: evildecay/etcdkeeper:v0.7.6
# ports:
# - "8080:8080"
network_mode: host
restart: always
CI
- 我个人使用的是coding, 个人免费注册, 然后每个月有
10
小时的构建额度, 基本上够用了 - 会在构建的时候注入环境变量
SERVICE_PATH
和SERVICE_NUMBER
, 标识本次构建的服务和部署的服务数量 SERVICE_PATH
会在前面说的docker build
参数使用SERVICE_NUMBER
就是启动多少个容器, 伪代码如下
script {
def count = SERVICE_NUMBER.toInteger();
echo "部署: ${count} 个"
for (int i = 0; i < count; i++) {
sshCommand(
remote: remoteConfig,
command: "docker run --cpus=0.5 --memory=100m -d --net=host --restart=always --name ${SERVICE_NAME}-${i} ${IMAGE_NAME}:${CI_BUILD_NUMBER}",
sudo: true,
)
echo "${SERVICE_NAME}-${i}..."
}
}
End
- 当我更新代码时, 会选择部署的服务, 最终部署后的效果如下图