Featured image of post 个人单机服务器微服务实践

个人单机服务器微服务实践

微服务的搭建, etcd, go-zero

前言

  • 虽然去年就已经开始接触微服务, 并且在个人服务器运行了起来, 但是都是使用直连模式, 今天正好有空升级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
}

服务注册发现 && 配置中心

  • 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_PATHSERVICE_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

  • 当我更新代码时, 会选择部署的服务, 最终部署后的效果如下图