14、本地与云端的 Kubernetes 服务部署指南

本地与云端的 Kubernetes 服务部署指南

1. 本地部署服务

1.1 定义“无头”服务

首先,我们需要定义一个“无头”服务。“无头”服务不会将流量负载均衡到单个 IP 上,适用于分布式服务有自己的服务发现机制的情况。以下是 service.yaml 文件的代码:

apiVersion: v1
kind: Service
metadata:
  name: {{ include "proglog.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels: {{ include "proglog.labels" . | nindent 4 }}
spec:
  clusterIP: None
  publishNotReadyAddresses: true
  ports:
    - name: rpc
      port: {{ .Values.rpcPort }}
      targetPort: {{ .Values.rpcPort }}
    - name: serf-tcp
      protocol: "TCP"
      port: {{ .Values.serfPort }}
      targetPort: {{ .Values.serfPort }}
    - name: serf-udp
      protocol: "UDP"
      port: {{ .Values.serfPort }}
      targetPort: {{ .Values.serfPort }}
  selector: {{ include "proglog.selectorLabels" . | nindent 4 }}

通过定义这个服务,Kubernetes 的端点控制器会更改 DNS 配置,为每个 Pod 返回指向它的记录,例如 proglog-{{id}}.proglog.{{namespace}}.svc.cluster.local ,服务器可以使用这些记录来相互发现。

1.2 使用完全限定域名宣传 Raft

为了让节点能正确地向集群和客户端宣传自己,我们需要使用完全限定域名来配置 Raft 的地址。具体操作如下:
1. 修改 config.go 文件

type Config struct {
    Raft struct {
        raft.Config
        BindAddr string
        StreamLayer *StreamLayer
        Bootstrap bool
    }
    Segment struct {
        MaxStoreBytes uint64
        MaxIndexBytes uint64
        InitialOffset uint64
    }
}
  1. 修改 distributed.go 文件
if l.config.Raft.Bootstrap && !hasState {
    config := raft.Configuration{
        Servers: []raft.Server{{
            ID: config.LocalID,
            Address: raft.ServerAddress(l.config.Raft.BindAddr),
        }},
    }
    err = l.raft.BootstrapCluster(config).Error()
}
  1. 修改 distributed_test.go 文件
config := log.Config{}
config.Raft.StreamLayer = log.NewStreamLayer(ln, nil, nil)
config.Raft.LocalID = raft.ServerID(fmt.Sprintf("%d", i))
config.Raft.HeartbeatTimeout = 50 * time.Millisecond
config.Raft.ElectionTimeout = 50 * time.Millisecond
config.Raft.LeaderLeaseTimeout = 50 * time.Millisecond
config.Raft.CommitTimeout = 5 * time.Millisecond
config.Raft.BindAddr = ln.Addr().String()
  1. 修改 agent.go 文件
func (a *Agent) setupMux() error {
    addr, err := net.ResolveTCPAddr("tcp", a.Config.BindAddr)
    if err != nil {
        return err
    }
    rpcAddr := fmt.Sprintf(
        "%s:%d",
        addr.IP.String(),
        a.Config.RPCPort,
    )
    ln, err := net.Listen("tcp", rpcAddr)
    if err != nil {
        return err
    }
    a.mux = cmux.New(ln)
    return nil
}

func (a *Agent) setupLog() error {
    // ...
    logConfig := log.Config{}
    logConfig.Raft.StreamLayer = log.NewStreamLayer(
        raftLn,
        a.Config.ServerTLSConfig,
        a.Config.PeerTLSConfig,
    )
    rpcAddr, err := a.Config.RPCAddr()
    if err != nil {
        return err
    }
    logConfig.Raft.BindAddr = rpcAddr
    logConfig.Raft.LocalID = raft.ServerID(a.Config.NodeName)
    logConfig.Raft.Bootstrap = a.Config.Bootstrap
    // ...
}

1.3 安装 Helm 图表

完成上述配置后,我们可以安装 Helm 图表来运行服务集群。具体步骤如下:
1. 查看 Helm 渲染结果

$ helm template proglog deploy/proglog

运行该命令后,你会发现仓库仍设置为默认的 nginx 。打开 deploy/proglog/values.yaml 文件,将其内容替换为:

# Default values for proglog.
image:
  repository: github.com/travisjeffery/proglog
  tag: 0.0.1
  pullPolicy: IfNotPresent
serfPort: 8401
rpcPort: 8400
replicas: 3
storage: 1Gi
  1. 安装 Helm 图表
$ helm install proglog deploy/proglog

等待几秒钟,Kubernetes 会设置三个 Pod。你可以使用以下命令列出这些 Pod:

$ kubectl get pods

当所有三个 Pod 都准备好后,我们可以请求 API。使用以下命令将 Pod 的端口转发到本地计算机的端口:

$ kubectl port-forward pod/proglog-0 8400 8400

现在,我们可以从 Kubernetes 外部的程序在 :8400 端口请求服务。创建一个 cmd/getservers/main.go 文件,内容如下:

package main

import (
    "context"
    "flag"
    "fmt"
    "log"
    api "github.com/travisjeffery/proglog/api/v1"
    "google.golang.org/grpc"
)

func main() {
    addr := flag.String("addr", ":8400", "service address")
    flag.Parse()
    conn, err := grpc.Dial(*addr, grpc.WithInsecure())
    if err != nil {
        log.Fatal(err)
    }
    client := api.NewLogClient(conn)
    ctx := context.Background()
    res, err := client.GetServers(ctx, &api.GetServersRequest{})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("servers:")
    for _, server := range res.Servers {
        fmt.Printf("\t- %v\n", server)
    }
}

运行以下命令请求服务并打印服务器列表:

$ go run cmd/getservers/main.go

你应该会看到如下输出:

servers:
  - id:"proglog-0" rpc_addr:"proglog-0.proglog.default.svc.cluster.local:8400"
  - id:"proglog-1" rpc_addr:"proglog-1.proglog.default.svc.cluster.local:8400"
  - id:"proglog-2" rpc_addr:"proglog-2.proglog.default.svc.cluster.local:8400"

这意味着集群中的所有三个服务器都已成功加入集群并相互协调!

1.4 本地部署流程总结

下面是本地部署的流程图:

graph LR
    A[定义“无头”服务] --> B[使用完全限定域名宣传 Raft]
    B --> C[安装 Helm 图表]
    C --> D[请求 API 验证]

2. 云端部署服务

2.1 选择云平台

目前有三个主要的云平台:Google Cloud Platform (GCP)、Amazon Web Services (AWS) 和 Microsoft Azure。它们都提供类似的功能集和自己的 Kubernetes 服务。在本文中,我们将选择 Google Cloud Platform 进行服务部署。

2.2 创建 Google Kubernetes Engine 集群

2.2.1 注册 Google Cloud 账户

打开 GCP 注册表单 ,登录现有的 Google 账户或创建一个新账户。按照表单说明填写详细信息,直到开始免费试用。

2.2.2 创建 Kubernetes 集群
  1. 导航到 Kubernetes Engine 服务 ,点击“Create cluster”打开集群创建表单。
  2. 将名称字段从默认的 cluster-1 更改为 proglog
  3. 保持位置类型为默认的“Zonal”。
  4. 在主版本部分,选择“Release channel”单选按钮,并选择当前的常规频道,如 1.16.11-gke.5
  5. 点击页面底部的“Create”按钮。页面将刷新并显示一个加载图标,表示 GCP 正在配置集群。当集群准备好时,你会看到一个绿色的对勾。
2.2.3 安装并认证 gcloud

Google Cloud 提供了一个云软件开发工具包 (SDK),其中包含与 Google 服务交互所需的各种工具和库。我们需要安装 gcloud CLI 来与 Google Cloud API 交互并配置 Docker。
1. 按照 Google Cloud 开发者工具页面 上的说明为你的操作系统安装最新的 Cloud SDK。
2. 安装完成后,使用以下命令对 CLI 进行认证:

$ gcloud auth login
  1. 获取项目的 ID,并配置 gcloud 默认使用该项目:
$ PROJECT_ID=$(gcloud projects list | tail -n 1 | cut -d' ' -f1)
$ gcloud config set project $PROJECT_ID

注意,如果你开启了新的终端会话,需要再次设置 PROJECT_ID 环境变量。

2.2.4 将服务镜像推送到 Google 的容器注册表

为了让 GKE 集群的节点能够拉取我们的服务镜像,我们需要将其推送到 Google 的容器注册表。具体步骤如下:
1. 配置 Docker 使用 Google 的容器注册表并使用 gcloud 作为凭证助手:

$ gcloud auth configure-docker
  1. 为镜像创建一个新的标签:
$ docker tag github.com/travisjeffery/proglog:0.0.1 gcr.io/$PROJECT_ID/proglog:0.0.1
  1. 将镜像推送到注册表:
$ docker push gcr.io/$PROJECT_ID/proglog:0.0.1
2.2.5 配置 kubectl

使用以下命令更新 kubeconfig 文件,使 kubectl 和 Helm 能够与 GKE 集群通信:

$ gcloud container clusters get-credentials proglog --zone us-central1-c

运行该命令后,会在 ~/.kube/config 文件中生成一个新的条目,指向你的 GKE 集群。

2.3 使用 Metacontroller 创建自定义控制器

为了将我们的服务暴露到互联网上,我们需要为每个 Pod 创建一个负载均衡器服务。Kubernetes 本身不支持自动创建和删除这些负载均衡器,因此我们可以使用 Metacontroller 来实现这个功能。

2.3.1 安装 Metacontroller
  1. 我们需要应用几个 YAML 文件来定义 Metacontroller 的 API 和 RBAC 授权,以允许这些 API 管理 Kubernetes 集群的资源。Metacontroller 提供了两种 API:
    • CompositeController :用于基于某个父资源管理子资源,如 Deployment StatefulSet 控制器。
    • DecoratorController :用于为资源添加行为,我们将使用这个控制器来实现每个 Pod 一个服务的功能。
  2. 使用 Helm 安装 Metacontroller。从项目的根目录运行以下命令来定义 Metacontroller 的 Helm 图表:
$ cd deploy
$ helm create metacontroller
$ rm metacontroller/templates/**/*.yaml metacontroller/templates/NOTES.txt metacontroller/values.yaml
$ MC_URL=https://raw.githubusercontent.com/GoogleCloudPlatform/metacontroller/master/manifests/
$ curl -L $MC_URL/metacontroller-rbac.yaml > metacontroller/templates/metacontroller-rbac.yaml
$ curl -L $MC_URL/metacontroller.yaml > metacontroller/templates/metacontroller.yaml
  1. 创建一个命名空间并安装 Metacontroller 图表:
$ kubectl create namespace metacontroller
$ helm install metacontroller metacontroller
2.3.2 添加每个 Pod 一个负载均衡器的钩子
  1. 创建 deploy/proglog/templates/service-per-pod.yaml 文件,定义 DecoratorController 和 Metacontroller 配置:
{{ if .Values.service.lb }}
apiVersion: metacontroller.k8s.io/v1alpha1
kind: DecoratorController
metadata:
  name: service-per-pod
spec:
  resources:
    - apiVersion: apps/v1
      resource: statefulsets
      annotationSelector:
        matchExpressions:
          - {key: service-per-pod-label, operator: Exists}
          - {key: service-per-pod-ports, operator: Exists}
  attachments:
    - apiVersion: v1
      resource: services
  hooks:
    sync:
      webhook:
        url: "http://service-per-pod.metacontroller/create-service-per-pod"
    finalize:
      webhook:
        url: "http://service-per-pod.metacontroller/delete-service-per-pod"
---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metacontroller
  name: service-per-pod-hooks
data:
  {{ (.Files.Glob "hooks/*").AsConfig | indent 2 }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service-per-pod
  namespace: metacontroller
spec:
  replicas: 1
  selector:
    matchLabels:
      app: service-per-pod
  template:
    metadata:
      labels:
        app: service-per-pod
    spec:
      containers:
        - name: hooks
          image: metacontroller/jsonnetd:0.1
          imagePullPolicy: Always
          workingDir: /hooks
          volumeMounts:
            - name: hooks
              mountPath: /hooks
      volumes:
        - name: hooks
          configMap:
            name: service-per-pod-hooks
---
apiVersion: v1
kind: Service
metadata:
  name: service-per-pod
  namespace: metacontroller
spec:
  selector:
    app: service-per-pod
  ports:
    - port: 80
      targetPort: 8080
{{ end }}
  1. 创建 hooks 目录:
$ mkdir deploy/proglog/hooks
  1. hooks 目录中添加 create-service-per-pod.jsonnet 文件,用于创建服务:
function(request) {
    local statefulset = request.object,
    local labelKey = statefulset.metadata.annotations["service-per-pod-label"],
    local ports = statefulset.metadata.annotations["service-per-pod-ports"],
    attachments: [
        {
            apiVersion: "v1",
            kind: "Service",
            metadata: {
                name: statefulset.metadata.name + "-" + index,
                labels: {app: "service-per-pod"}
            },
            spec: {
                type: "LoadBalancer",
                selector: {
                    [labelKey]: statefulset.metadata.name + "-" + index
                },
                ports: [
                    {
                        local parts = std.split(portnums, ":"),
                        port: std.parseInt(parts[0]),
                        targetPort: std.parseInt(parts[1]),
                    }
                    for portnums in std.split(ports, ",")
                ]
            }
        }
        for index in std.range(0, statefulset.spec.replicas - 1)
    ]
}
  1. hooks 目录中添加 delete-service-per-pod.jsonnet 文件,用于删除服务:
function(request) {
    attachments: [],
    finalized: std.length(request.attachments['Service.v1']) == 0
}
  1. 更新 statefulset.yaml 文件,设置注解以指示 Kubernetes 装饰这个 StatefulSet 并为每个 Pod 创建一个服务:
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: {{ include "proglog.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels: {{ include "proglog.labels" . | nindent 4 }}
  {{ if .Values.service.lb }}
  annotations:
    service-per-pod-label: "statefulset.kubernetes.io/pod-name"
    service-per-pod-ports: "{{.Values.rpcPort}}:{{.Values.rpcPort}}"
  {{ end }}
spec:
  # ...

2.4 云端部署流程总结

下面是云端部署的流程图:

graph LR
    A[选择云平台] --> B[创建 GKE 集群]
    B --> C[安装并认证 gcloud]
    C --> D[推送镜像到容器注册表]
    D --> E[配置 kubectl]
    E --> F[安装 Metacontroller]
    F --> G[添加负载均衡器钩子]

通过以上步骤,我们完成了服务在本地和云端的部署,并使用 Metacontroller 实现了每个 Pod 一个负载均衡器服务的功能,从而将服务暴露到互联网上。

3. 部署要点总结

3.1 本地部署要点

步骤 要点 代码/命令示例
定义“无头”服务 用于分布式服务的服务发现,Kubernetes 端点控制器会更改 DNS 配置 service.yaml 文件定义
使用完全限定域名宣传 Raft 修改多个文件配置 Raft 地址 config.go distributed.go distributed_test.go agent.go 文件修改
安装 Helm 图表 查看渲染结果并修改 values.yaml ,然后安装图表 helm template proglog deploy/proglog
helm install proglog deploy/proglog
请求 API 验证 转发端口并运行程序请求服务 kubectl port-forward pod/proglog-0 8400 8400
go run cmd/getservers/main.go

3.2 云端部署要点

步骤 要点 代码/命令示例
选择云平台 选择 Google Cloud Platform -
创建 GKE 集群 注册账户、创建集群、安装并认证 gcloud gcloud auth login
gcloud config set project $PROJECT_ID
推送镜像到容器注册表 配置 Docker,打标签并推送镜像 gcloud auth configure-docker
docker tag github.com/travisjeffery/proglog:0.0.1 gcr.io/$PROJECT_ID/proglog:0.0.1
docker push gcr.io/$PROJECT_ID/proglog:0.0.1
配置 kubectl 获取集群凭证 gcloud container clusters get-credentials proglog --zone us-central1-c
安装 Metacontroller 定义 API 和 RBAC 授权,使用 Helm 安装 kubectl create namespace metacontroller
helm install metacontroller metacontroller
添加负载均衡器钩子 创建相关文件并更新 statefulset.yaml deploy/proglog/templates/service-per-pod.yaml
create-service-per-pod.jsonnet
delete-service-per-pod.jsonnet

4. 部署过程中的注意事项

4.1 本地部署注意事项

  • Helm 图表配置 :在查看 Helm 渲染结果时,要注意 values.yaml 文件的配置,确保仓库、标签等信息正确。
  • 端口转发 :使用 kubectl port-forward 时,要确保本地端口未被占用,否则会导致转发失败。
  • 代码修改 :在修改多个文件配置 Raft 地址时,要仔细检查代码,避免出现语法错误。

4.2 云端部署注意事项

  • Google Cloud 账户 :注册账户时要确保提供的信息准确,开启免费试用需要绑定信用卡。
  • gcloud 配置 :在切换终端会话时,要记得重新设置 PROJECT_ID 环境变量,否则会影响后续操作。
  • 镜像推送 :推送镜像到 Google 的容器注册表时,要确保网络连接正常,否则可能会出现推送失败的情况。
  • Metacontroller 安装 :安装 Metacontroller 时,要确保相关 YAML 文件下载正确,避免安装失败。

5. 总结与展望

5.1 总结

通过上述的本地和云端部署步骤,我们成功地将服务部署到了本地的 Kubernetes 集群和 Google Cloud Platform 的 GKE 集群中。在本地部署中,我们学习了如何定义“无头”服务、使用完全限定域名宣传 Raft、安装 Helm 图表以及验证服务的可用性。在云端部署中,我们选择了 Google Cloud Platform,完成了创建 GKE 集群、推送镜像、配置 kubectl 以及使用 Metacontroller 创建自定义控制器等操作,实现了每个 Pod 一个负载均衡器服务的功能,将服务暴露到了互联网上。

5.2 展望

在未来的部署过程中,我们可以进一步优化部署流程,例如使用自动化脚本完成本地和云端的部署操作,提高部署效率。同时,我们可以探索更多的云平台和 Kubernetes 相关技术,如使用 Amazon Web Services 或 Microsoft Azure 进行部署,或者使用 Kubernetes 的 Operator 模式来扩展集群功能。此外,我们还可以加强服务的监控和管理,确保服务的稳定性和可靠性。

graph LR
    A[本地部署] --> B[定义“无头”服务]
    A --> C[使用完全限定域名宣传 Raft]
    A --> D[安装 Helm 图表]
    A --> E[请求 API 验证]
    F[云端部署] --> G[选择云平台]
    F --> H[创建 GKE 集群]
    F --> I[安装并认证 gcloud]
    F --> J[推送镜像到容器注册表]
    F --> K[配置 kubectl]
    F --> L[安装 Metacontroller]
    F --> M[添加负载均衡器钩子]
    N[部署要点总结] --> A
    N --> F
    O[注意事项] --> A
    O --> F
    P[总结与展望] --> N
    P --> O

通过以上的流程和操作,我们可以更加高效、稳定地将服务部署到本地和云端环境中,为后续的开发和运营打下坚实的基础。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值