本地及云端使用 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
}
}
- 修改
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()
}
- 更新日志配置 :在
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()
- 更新
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
- 安装 Helm 图表 :运行命令
$ helm install proglog deploy/proglog。等待几秒后,Kubernetes 会创建三个 Pod,可通过$ kubectl get pods查看。 - 端口转发 :当三个 Pod 都准备好后,运行
$ kubectl port-forward pod/proglog-0 8400 8400,这样就可以从外部程序访问运行在 Kubernetes 内部的服务。 - 编写获取服务器列表的程序 :创建
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)
}
}
- 运行程序获取服务器列表 :运行
$ 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
- 将服务镜像推送到 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
- 配置
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
- 添加每个 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 }}
- 在完全限定域名上通告 Raft
- 修改
internal/log/config.go中的Config结构体。 - 修改
internal/log/distributed.go中的DistributedLog引导代码。 - 更新
distributed_test.go中的日志配置。 - 更新
agent.go中的setupMux()和setupLog()函数。
- 修改
- 安装 Helm 图表
- 运行
$ helm template proglog deploy/proglog查看渲染结果。 - 修改
deploy/proglog/values.yaml文件。 - 运行
$ helm install proglog deploy/proglog安装图表。
- 运行
- 端口转发并测试服务
- 运行
$ 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"
- 云端部署验证 :可以通过访问负载均衡器的 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 地址 |
Kubernetes本地与云端部署应用指南
超级会员免费看
928

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



