本地与云端的 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
}
}
- 修改
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()
}
- 修改
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()
- 修改
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
- 安装 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 集群
- 导航到 Kubernetes Engine 服务 ,点击“Create cluster”打开集群创建表单。
- 将名称字段从默认的
cluster-1更改为proglog。 - 保持位置类型为默认的“Zonal”。
- 在主版本部分,选择“Release channel”单选按钮,并选择当前的常规频道,如
1.16.11-gke.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
- 获取项目的 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
- 为镜像创建一个新的标签:
$ 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
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
- 我们需要应用几个 YAML 文件来定义 Metacontroller 的 API 和 RBAC 授权,以允许这些 API 管理 Kubernetes 集群的资源。Metacontroller 提供了两种 API:
-
CompositeController:用于基于某个父资源管理子资源,如Deployment和StatefulSet控制器。 -
DecoratorController:用于为资源添加行为,我们将使用这个控制器来实现每个 Pod 一个服务的功能。
-
- 使用 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
- 创建一个命名空间并安装 Metacontroller 图表:
$ kubectl create namespace metacontroller
$ helm install metacontroller metacontroller
2.3.2 添加每个 Pod 一个负载均衡器的钩子
- 创建
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 }}
- 创建
hooks目录:
$ mkdir deploy/proglog/hooks
- 在
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)
]
}
- 在
hooks目录中添加delete-service-per-pod.jsonnet文件,用于删除服务:
function(request) {
attachments: [],
finalized: std.length(request.attachments['Service.v1']) == 0
}
- 更新
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
通过以上的流程和操作,我们可以更加高效、稳定地将服务部署到本地和云端环境中,为后续的开发和运营打下坚实的基础。
超级会员免费看

33

被折叠的 条评论
为什么被折叠?



