14、本地及云端使用 Kubernetes 部署应用指南

Kubernetes本地与云端部署应用指南

本地及云端使用 Kubernetes 部署应用指南

本地部署服务

在本地部署服务时,我们需要进行一系列的配置和操作。首先,定义“无头”服务,其配置文件如下:

# DeployLocally/deploy/proglog/templates/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 }}

“无头”服务不会将流量负载均衡到单个 IP,适用于分布式服务有自己的服务发现机制的情况。通过定义选择器,Kubernetes 的端点控制器会更改 DNS 配置,为每个 Pod 生成类似 proglog-{{id}}.proglog.{{namespace}}.svc.cluster.local 的 DNS 记录,服务器可利用这些记录相互发现。

接下来,我们要在完全限定域名上通告 Raft。具体操作如下:
1. 修改 Config 结构体 :在 internal/log/config.go 中,将 Config 结构体修改为:

// DeployLocally/internal/log/config.go
type Config struct {
    Raft struct {
        raft.Config
        BindAddr string
        StreamLayer *StreamLayer
        Bootstrap bool
    }
    Segment struct {
        MaxStoreBytes uint64
        MaxIndexBytes uint64
        InitialOffset uint64
    }
}
  1. 修改 DistributedLog 的引导代码 :在 internal/log/distributed.go 中,使用配置的绑定地址:
// DeployLocally/internal/log/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 中,设置地址:
// DeployLocally/internal/log/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 中的配置 :在 agent.go 中,更新 setupMux() setupLog() 函数来配置多路复用器和 Raft 实例:
// DeployLocally/internal/agent/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
    // ...
}

完成上述配置后,我们就可以安装 Helm 图表并部署服务了,具体步骤如下:
1. 查看 Helm 渲染结果 :运行命令 $ helm template proglog deploy/proglog
2. 修改 values.yaml 文件 :打开 deploy/proglog/values.yaml ,将内容替换为:

# DeployLocally/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,可通过 $ kubectl get pods 查看。
  2. 端口转发 :当三个 Pod 都准备好后,运行 $ kubectl port-forward pod/proglog-0 8400 8400 ,这样就可以从外部程序访问运行在 Kubernetes 内部的服务。
  3. 编写获取服务器列表的程序 :创建 cmd/getservers/main.go 文件,内容如下:
// DeployLocally/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)
    }
}
  1. 运行程序获取服务器列表 :运行 $ 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"
云端部署服务

本地部署完成后,我们可以将服务部署到云端。这里以 Google Cloud Platform (GCP) 为例,具体步骤如下:
1. 创建 Google Kubernetes Engine (GKE) 集群
- 注册 Google Cloud 账号 :打开 GCP 注册表单 ,登录现有 Google 账号或创建新账号,按照表单提示完成注册并开始免费试用。
- 创建 Kubernetes 集群 :导航到 Kubernetes Engine 服务 ,点击“创建集群”,将集群名称从默认的 cluster-1 改为 proglog ,保持位置类型为默认的“区域”,在主版本部分选择“发布通道”并选择当前的常规通道(如 1.16.11-gke.5 ),然后点击“创建”按钮。等待集群创建完成,出现绿色对勾表示集群准备就绪。
- 安装并认证 gcloud :按照 Google Cloud Developer Tools 页面 的说明为你的操作系统安装最新的 Cloud SDK。安装完成后,运行 $ gcloud auth login 进行认证。接着,获取项目 ID 并配置 gcloud 默认使用该项目:

$ PROJECT_ID=$(gcloud projects list | tail -n 1 | cut -d' ' -f1)
$ gcloud config set project $PROJECT_ID
  1. 将服务镜像推送到 Google 的容器注册表 :运行以下命令将镜像推送到注册表:
$ 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
  1. 配置 kubectl :运行 $ gcloud container clusters get-credentials proglog --zone us-central1-c ,该命令会更新 kubeconfig 文件,使 kubectl 和 Helm 能够与 GKE 集群通信。

虽然我们已经完成了 GKE 集群的设置,但当前的部署设置无法使服务在互联网上可用。接下来,我们将使用 Metacontroller 创建自定义控制器来解决这个问题。

使用 Metacontroller 创建自定义控制器

Metacontroller 是一个 Kubernetes 插件,可让我们使用简单的脚本编写和部署自定义控制器。以下是具体步骤:
1. 安装 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
- 创建命名空间并安装 Metacontroller 图表:
$ kubectl create namespace metacontroller
$ helm install metacontroller metacontroller
  1. 添加每个 Pod 一个负载均衡器的钩子
    • 创建 service-per-pod.yaml 文件 :在 deploy/proglog/templates/service-per-pod.yaml 中定义 DecoratorController 和 Metacontroller 配置:
# DeployToCloud/deploy/proglog/templates/service-per-pod.yaml
{{ 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 }}
- **创建 `hooks` 目录并添加钩子文件**:
$ mkdir deploy/proglog/hooks
- **添加创建服务的钩子**:在 `deploy/proglog/hooks/create-service-per-pod.jsonnet` 中添加以下内容:
// DeployToCloud/deploy/proglog/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)
    ]
}
- **添加删除服务的钩子**:在 `deploy/proglog/hooks/delete-service-per-pod.jsonnet` 中添加以下内容:
// DeployToCloud/deploy/proglog/hooks/delete-service-per-pod.jsonnet
function(request) {
    attachments: [],
    finalized: std.length(request.attachments['Service.v1']) == 0
}
- **更新 `StatefulSet` 的元数据**:在 `deploy/proglog/templates/statefulset.yaml` 中添加注解:
# DeployToCloud/deploy/proglog/templates/statefulset.yaml
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:
  # ...

完成上述所有步骤后,我们的服务应该会为每个 Pod 创建一个负载均衡器服务。现在可以将服务部署到 GKE 集群并进行测试了。

总结

通过以上步骤,我们完成了服务在本地和云端的部署。本地部署让我们熟悉了 Kubernetes 和 Helm 图表的使用,而云端部署则借助 GKE 和 Metacontroller 实现了服务在互联网上的可用性。这种部署方式具有良好的可扩展性和灵活性,能够满足不同场景下的需求。

流程图

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B(本地部署服务):::process
    B --> C(定义“无头”服务):::process
    C --> D(在完全限定域名上通告 Raft):::process
    D --> E(安装 Helm 图表):::process
    E --> F(端口转发并测试服务):::process
    F --> G(云端部署服务):::process
    G --> H(创建 GKE 集群):::process
    H --> I(推送服务镜像到容器注册表):::process
    I --> J(配置 kubectl):::process
    J --> K(使用 Metacontroller 创建自定义控制器):::process
    K --> L(安装 Metacontroller):::process
    L --> M(添加服务 - 每个 Pod 的负载均衡器钩子):::process
    M --> N(更新 StatefulSet 元数据):::process
    N --> O([结束]):::startend

表格

步骤 操作内容 相关文件或命令
本地部署 定义“无头”服务 DeployLocally/deploy/proglog/templates/service.yaml
在完全限定域名上通告 Raft internal/log/config.go internal/log/distributed.go distributed_test.go agent.go
安装 Helm 图表 $ helm template proglog deploy/proglog $ helm install proglog deploy/proglog
端口转发并测试服务 $ kubectl port-forward pod/proglog-0 8400 8400 $ go run cmd/getservers/main.go
云端部署 创建 GKE 集群 注册表单、Kubernetes Engine 服务
推送服务镜像到容器注册表 $ gcloud auth configure-docker $ docker tag ... $ docker push ...
配置 kubectl $ gcloud container clusters get-credentials proglog --zone us-central1-c
使用 Metacontroller 安装 Metacontroller $ cd deploy $ helm create metacontroller
添加服务 - 每个 Pod 的负载均衡器钩子 deploy/proglog/templates/service-per-pod.yaml deploy/proglog/hooks/create-service-per-pod.jsonnet deploy/proglog/hooks/delete-service-per-pod.jsonnet
更新 StatefulSet 元数据 deploy/proglog/templates/statefulset.yaml

本地及云端使用 Kubernetes 部署应用指南

本地部署与云端部署要点回顾

在前面的内容中,我们详细介绍了服务在本地和云端的部署流程。本地部署主要包括定义“无头”服务、在完全限定域名上通告 Raft、安装 Helm 图表、端口转发并测试服务等步骤。云端部署则以 Google Cloud Platform (GCP) 为例,涵盖了创建 Google Kubernetes Engine (GKE) 集群、将服务镜像推送到 Google 的容器注册表、配置 kubectl 等操作。同时,为了使服务在互联网上可用,我们使用 Metacontroller 创建了自定义控制器,为每个 Pod 创建负载均衡器服务。

本地部署详细步骤总结

本地部署的具体步骤如下:
1. 定义“无头”服务 :使用 DeployLocally/deploy/proglog/templates/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 }}
  1. 在完全限定域名上通告 Raft
    • 修改 internal/log/config.go 中的 Config 结构体。
    • 修改 internal/log/distributed.go 中的 DistributedLog 引导代码。
    • 更新 distributed_test.go 中的日志配置。
    • 更新 agent.go 中的 setupMux() setupLog() 函数。
  2. 安装 Helm 图表
    • 运行 $ helm template proglog deploy/proglog 查看渲染结果。
    • 修改 deploy/proglog/values.yaml 文件。
    • 运行 $ helm install proglog deploy/proglog 安装图表。
  3. 端口转发并测试服务
    • 运行 $ kubectl port-forward pod/proglog-0 8400 8400 进行端口转发。
    • 编写 cmd/getservers/main.go 文件并运行 $ go run cmd/getservers/main.go 测试服务。
云端部署详细步骤总结

云端部署以 GCP 为例,步骤如下:
1. 创建 GKE 集群
- 注册 Google Cloud 账号。
- 创建 Kubernetes 集群,修改集群名称、选择主版本等。
- 安装并认证 gcloud ,获取项目 ID 并配置默认项目。
2. 推送服务镜像到容器注册表 :运行相关命令将镜像推送到 gcr.io 注册表。
3. 配置 kubectl :运行 $ gcloud container clusters get-credentials proglog --zone us-central1-c 更新 kubeconfig 文件。

使用 Metacontroller 创建自定义控制器步骤总结

使用 Metacontroller 创建自定义控制器的步骤如下:
1. 安装 Metacontroller
- 定义 Metacontroller 的 Helm 图表。
- 创建命名空间并安装 Metacontroller 图表。
2. 添加每个 Pod 一个负载均衡器的钩子
- 创建 service-per-pod.yaml 文件定义配置。
- 创建 hooks 目录并添加创建和删除服务的钩子文件。
- 更新 StatefulSet 的元数据。

部署过程中的注意事项

在整个部署过程中,有一些注意事项需要我们关注:
1. 环境变量的设置 :在云端部署时, PROJECT_ID 环境变量在不同的终端会话中需要重新设置,否则可能会导致后续命令执行失败。
2. 网络配置 :在本地部署进行端口转发时,要确保端口没有被其他程序占用,否则会出现端口冲突的问题。在云端部署使用 Metacontroller 创建负载均衡器服务时,要确保网络配置正确,以保证服务能够正常对外提供。
3. 文件路径和权限 :在创建和修改文件时,要确保文件路径正确,并且有足够的权限进行操作。例如,在创建 hooks 目录和添加钩子文件时,如果权限不足,可能会导致文件创建失败。

部署效果验证

完成所有部署步骤后,我们可以通过以下方式验证部署效果:
1. 本地部署验证 :运行 $ 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. 云端部署验证 :可以通过访问负载均衡器的 IP 地址来验证服务是否在互联网上可用。如果能够正常访问服务,则说明云端部署成功。
总结与展望

通过本次部署实践,我们掌握了使用 Kubernetes 在本地和云端部署服务的完整流程。本地部署为我们提供了一个快速验证和测试的环境,而云端部署则使服务能够在互联网上对外提供。使用 Metacontroller 创建自定义控制器的方法,为我们解决了服务在云端的负载均衡和对外暴露问题,提高了服务的可用性和可扩展性。

在未来的部署工作中,我们可以进一步优化部署流程,例如使用自动化脚本实现一键部署,提高部署效率。同时,还可以考虑使用其他云平台进行部署,比较不同平台的优缺点,选择最适合项目需求的平台。

流程图

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B(本地部署):::process
    B --> C{部署成功?}:::decision
    C -->|是| D(云端部署):::process
    C -->|否| E(检查本地部署问题):::process
    E --> B
    D --> F{部署成功?}:::decision
    F -->|是| G(使用 Metacontroller 优化):::process
    F -->|否| H(检查云端部署问题):::process
    H --> D
    G --> I{优化成功?}:::decision
    I -->|是| J(验证部署效果):::process
    I -->|否| K(检查 Metacontroller 问题):::process
    K --> G
    J --> L([结束]):::startend

表格

部署阶段 步骤 操作内容 相关文件或命令
本地部署 1 定义“无头”服务 DeployLocally/deploy/proglog/templates/service.yaml
2 在完全限定域名上通告 Raft internal/log/config.go internal/log/distributed.go distributed_test.go agent.go
3 安装 Helm 图表 $ helm template proglog deploy/proglog $ helm install proglog deploy/proglog
4 端口转发并测试服务 $ kubectl port-forward pod/proglog-0 8400 8400 $ go run cmd/getservers/main.go
云端部署 1 创建 GKE 集群 注册表单、Kubernetes Engine 服务
2 推送服务镜像到容器注册表 $ gcloud auth configure-docker $ docker tag ... $ docker push ...
3 配置 kubectl $ gcloud container clusters get-credentials proglog --zone us-central1-c
使用 Metacontroller 1 安装 Metacontroller $ cd deploy $ helm create metacontroller
2 添加服务 - 每个 Pod 的负载均衡器钩子 deploy/proglog/templates/service-per-pod.yaml deploy/proglog/hooks/create-service-per-pod.jsonnet deploy/proglog/hooks/delete-service-per-pod.jsonnet
3 更新 StatefulSet 元数据 deploy/proglog/templates/statefulset.yaml
验证部署效果 1 本地部署验证 $ go run cmd/getservers/main.go
2 云端部署验证 访问负载均衡器 IP 地址
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值