本地使用 Kubernetes 部署应用
1. 引言
构建服务后,部署是关键的一步。本文将介绍如何使用 Kubernetes 和 Helm 在本地部署服务集群,具体步骤如下:
- 创建代理命令行界面(CLI)作为服务的可执行文件。
- 配置 Kubernetes 和 Helm,以便在本地机器和云平台上编排服务。
- 在本地机器上运行服务集群。
2. Kubernetes 简介
Kubernetes 是一个开源的容器编排系统,用于自动化部署、扩展和管理容器化服务。它通过 REST API 创建、更新和删除资源,是一个声明式系统,用户只需描述最终状态,Kubernetes 会自动将系统从当前状态转换到目标状态。
Kubernetes 中最常见的资源是 Pod,它是最小的可部署单元。Pod 内的容器共享网络命名空间、IP 地址和进程间通信(IPC)命名空间,还可以共享卷。其他资源包括 ConfigMaps、Secrets 用于配置 Pod,Deployments、StatefulSets、DaemonSets 用于管理 Pod 集合。用户还可以通过创建自定义资源和控制器来扩展 Kubernetes。
与 Kubernetes 交互需要使用命令行工具 kubectl,下面将介绍其安装方法。
3. 安装 kubectl
kubectl 用于对 Kubernetes 集群执行命令,可用于检查和管理服务的集群资源以及查看日志。对于一次性操作,建议使用 kubectl;对于重复操作,如部署或升级服务,可使用 Helm 包管理器或操作符。
安装 kubectl 的步骤如下:
$ curl -LO \
https://storage.googleapis.com/kubernetes-release/release/\
v1.18.0/bin/$(uname)/amd64/kubectl
$ chmod +x ./kubectl
$ mv ./kubectl /usr/local/bin/kubectl
安装完成后,需要一个 Kubernetes 集群和其 API 才能使用 kubectl。接下来将使用 Kind 工具在 Docker 中运行本地 Kubernetes 集群。
4. 使用 Kind 进行本地开发和持续集成
Kind(Kubernetes IN Docker)是 Kubernetes 团队开发的工具,用于使用 Docker 容器作为节点运行本地 Kubernetes 集群。它是运行自己的 Kubernetes 集群的最简单方法,适用于本地开发、测试和持续集成。
4.1 安装 Kind
安装 Kind 的步骤如下:
$ curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.8.1/kind-$(uname)-amd64
$ chmod +x ./kind
$ mv ./kind /usr/local/bin/kind
使用 Kind 前,需要安装 Docker,请参考 Docker 的官方安装说明进行安装。
4.2 创建 Kind 集群
启动 Docker 后,创建 Kind 集群的命令如下:
$ kind create cluster
验证 Kind 是否创建了集群并配置 kubectl 使用该集群:
$ kubectl cluster-info
> Kubernetes master is running at https://127.0.0.1:46023
KubeDNS is running at \
https://127.0.0.1:46023/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
可以运行以下命令查看 Node 容器:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED ...
033de99b1e53 kindest/node:v1.18.2 "/usr/local/bin/entr…" 2 minutes...
现在已经有了一个运行的 Kubernetes 集群,接下来需要一个 Docker 镜像和可执行的入口点来运行服务。下面将编写一个代理 CLI 作为服务的可执行文件。
5. 编写代理命令行界面
代理 CLI 将提供足够的功能,作为 Docker 镜像的入口点,用于解析标志、配置和运行代理。这里使用 Cobra 库处理命令和标志,它适用于创建简单的 CLI 和复杂的应用程序,并且与 Viper 库集成,Viper 是 Go 应用程序的完整配置解决方案。
5.1 创建主文件
创建
cmd/proglog/main.go
文件,代码如下:
package main
import (
"log"
"os"
"os/signal"
"path"
"syscall"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/travisjeffery/proglog/internal/agent"
"github.com/travisjeffery/proglog/internal/config"
)
func main() {
cli := &cli{}
cmd := &cobra.Command{
Use: "proglog",
PreRunE: cli.setupConfig,
RunE: cli.run,
}
if err := setupFlags(cmd); err != nil {
log.Fatal(err)
}
if err := cmd.Execute(); err != nil {
log.Fatal(err)
}
}
5.2 定义类型
定义
cli
和
cfg
类型,代码如下:
type cli struct {
cfg cfg
}
type cfg struct {
agent.Config
ServerTLSConfig config.TLSConfig
PeerTLSConfig config.TLSConfig
}
5.3 暴露标志
在
main.go
文件中添加以下代码来声明 CLI 的标志:
func setupFlags(cmd *cobra.Command) error {
hostname, err := os.Hostname()
if err != nil {
log.Fatal(err)
}
cmd.Flags().String("config-file", "", "Path to config file.")
dataDir := path.Join(os.TempDir(), "proglog")
cmd.Flags().String("data-dir", dataDir, "Directory to store log and Raft data.")
cmd.Flags().String("node-name", hostname, "Unique server ID.")
cmd.Flags().String("bind-addr", "127.0.0.1:8401", "Address to bind Serf on.")
cmd.Flags().Int("rpc-port", 8400, "Port for RPC clients (and Raft) connections.")
cmd.Flags().StringSlice("start-join-addrs", nil, "Serf addresses to join.")
cmd.Flags().Bool("bootstrap", false, "Bootstrap the cluster.")
cmd.Flags().String("acl-model-file", "", "Path to ACL model.")
cmd.Flags().String("acl-policy-file", "", "Path to ACL policy.")
cmd.Flags().String("server-tls-cert-file", "", "Path to server tls cert.")
cmd.Flags().String("server-tls-key-file", "", "Path to server tls key.")
cmd.Flags().String("server-tls-ca-file", "", "Path to server certificate authority.")
cmd.Flags().String("peer-tls-cert-file", "", "Path to peer tls cert.")
cmd.Flags().String("peer-tls-key-file", "", "Path to peer tls key.")
cmd.Flags().String("peer-tls-ca-file", "", "Path to peer certificate authority.")
return viper.BindPFlags(cmd.Flags())
}
这些标志允许用户配置代理并了解默认配置。
5.4 管理配置
Viper 提供了一个集中的配置注册表系统,支持通过标志、文件或从服务(如 Consul)加载动态配置。配置文件可以支持对运行中的服务进行动态配置更改,服务会监视配置文件的更改并相应更新。
在
main.go
文件中添加以下代码来设置配置:
func (c *cli) setupConfig(cmd *cobra.Command, args []string) error {
var err error
configFile, err := cmd.Flags().GetString("config-file")
if err != nil {
return err
}
viper.SetConfigFile(configFile)
if err = viper.ReadInConfig(); err != nil {
// it's ok if config file doesn't exist
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return err
}
}
c.cfg.DataDir = viper.GetString("data-dir")
c.cfg.NodeName = viper.GetString("node-name")
c.cfg.BindAddr = viper.GetString("bind-addr")
c.cfg.RPCPort = viper.GetInt("rpc-port")
c.cfg.StartJoinAddrs = viper.GetStringSlice("start-join-addrs")
c.cfg.Bootstrap = viper.GetBool("bootstrap")
c.cfg.ACLModelFile = viper.GetString("acl-mode-file")
c.cfg.ACLPolicyFile = viper.GetString("acl-policy-file")
c.cfg.ServerTLSConfig.CertFile = viper.GetString("server-tls-cert-file")
c.cfg.ServerTLSConfig.KeyFile = viper.GetString("server-tls-key-file")
c.cfg.ServerTLSConfig.CAFile = viper.GetString("server-tls-ca-file")
c.cfg.PeerTLSConfig.CertFile = viper.GetString("peer-tls-cert-file")
c.cfg.PeerTLSConfig.KeyFile = viper.GetString("peer-tls-key-file")
c.cfg.PeerTLSConfig.CAFile = viper.GetString("peer-tls-ca-file")
if c.cfg.ServerTLSConfig.CertFile != "" &&
c.cfg.ServerTLSConfig.KeyFile != "" {
c.cfg.ServerTLSConfig.Server = true
c.cfg.Config.ServerTLSConfig, err = config.SetupTLSConfig(
c.cfg.ServerTLSConfig,
)
if err != nil {
return err
}
}
if c.cfg.PeerTLSConfig.CertFile != "" &&
c.cfg.PeerTLSConfig.KeyFile != "" {
c.cfg.Config.PeerTLSConfig, err = config.SetupTLSConfig(
c.cfg.PeerTLSConfig,
)
if err != nil {
return err
}
}
return nil
}
5.5 完成程序
在
main.go
文件中添加以下
run
方法:
func (c *cli) run(cmd *cobra.Command, args []string) error {
var err error
agent, err := agent.New(c.cfg.Config)
if err != nil {
return err
}
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
<-sigc
return agent.Shutdown()
}
run
方法的功能如下:
- 创建代理。
- 处理来自操作系统的信号。
- 当操作系统终止程序时,优雅地关闭代理。
现在已经有了可作为 Docker 镜像入口点的可执行文件,接下来将编写 Dockerfile 并构建镜像。
6. 构建 Docker 镜像
创建 Dockerfile,代码如下:
FROM golang:1.14-alpine AS build
WORKDIR /go/src/proglog
COPY . .
RUN CGO_ENABLED=0 go build -o /go/bin/proglog ./cmd/proglog
FROM scratch
COPY --from=build /go/bin/proglog /bin/proglog
ENTRYPOINT ["/bin/proglog"]
该 Dockerfile 使用多阶段构建,一个阶段用于构建服务,另一个阶段用于运行服务。这样可以使 Dockerfile 易于阅读和维护,同时保持构建效率和镜像的小体积。
在
Makefile
文件底部添加以下代码,以添加构建 Docker 镜像的目标:
TAG ?= 0.0.1
build-docker:
docker build -t github.com/travisjeffery/proglog:$(TAG) .
构建镜像并将其加载到 Kind 集群中:
$ make build-docker
$ kind load docker-image github.com/travisjeffery/proglog:0.0.1
现在已经有了 Docker 镜像,接下来将介绍如何使用 Helm 配置和部署服务集群。
7. 使用 Helm 配置和部署服务
Helm 是 Kubernetes 的包管理器,用于在 Kubernetes 中分发和安装服务。Helm 包称为图表(charts),它定义了在 Kubernetes 集群中运行服务所需的所有资源,如部署、服务、持久卷声明等。
7.1 安装 Helm
安装 Helm 的命令如下:
$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 \
| bash
7.2 安装现有图表
在编写自己的 Helm 图表之前,先安装一个现有的图表。以 Bitnami 维护的 Nginx 图表为例,操作步骤如下:
1. 添加 Bitnami 仓库:
$ helm repo add bitnami https://charts.bitnami.com/bitnami
- 安装 Nginx 图表:
$ helm install my-nginx bitnami/nginx
- 查看发布信息:
$ helm list
NAME NAMESPACE REVISION UPDATED STATUS...
my-nginx default 1 2020... deployed...
- 验证 Nginx 是否运行:
$ POD_NAME=$(kubectl get pod \
--selector=app.kubernetes.io/name=nginx \
--template '{{index .items 0 "metadata" "name" }}')
$ SERVICE_IP=$(kubectl get svc \
--namespace default my-nginx --template "{{ .spec.clusterIP }}")
$ kubectl exec $POD_NAME curl $SERVICE_IP
如果需要卸载 Nginx 发布,可运行以下命令:
$ helm uninstall my-nginx
release "my-nginx" uninstalled
7.3 构建自己的 Helm 图表
接下来将为服务构建一个 Helm 图表,并在 Kind 集群中安装该集群。
7.3.1 创建 Helm 图表
创建 Helm 图表的命令如下:
$ mkdir deploy && cd deploy
$ helm create proglog
proglog
目录包含以下文件和目录:
.
└──proglog
├──charts
├──Chart.yaml
├──templates
│ ├──deployment.yaml
│ ├──_helpers.tpl
│ ├──ingress.yaml
│ ├──NOTES.txt
│ ├──serviceaccount.yaml
│ ├──service.yaml
│ └──tests
│ └──test-connection.yaml
└──values.yaml
4 directories, 9 files
-
Chart.yaml:描述图表,可在模板中访问其中的数据。 -
charts目录:可能包含子图表。 -
values.yaml:包含图表的默认值,用户在安装或升级图表时可以覆盖这些值。 -
templates目录:包含模板文件,使用 Go 模板语言编写,用于生成有效的 Kubernetes 清单文件。
可以使用以下命令在本地渲染模板,而不应用资源:
$ helm template proglog
由于不需要示例模板,可运行以下命令删除它们:
$ rm proglog/templates/**/*.yaml proglog/templates/NOTES.txt
我们的服务需要两种资源类型:StatefulSet 和 Service,接下来将分别创建相应的文件。
7.3.2 StatefulSets in Kubernetes
StatefulSets 用于管理 Kubernetes 中的有状态应用程序,如我们的日志持久化服务。有状态服务需要满足以下条件时使用 StatefulSet:
- 稳定、唯一的网络标识符:服务中的每个节点需要唯一的节点名称作为标识符。
- 稳定、持久的存储:服务写入的数据需要在重启后持久化。
- 有序、优雅的部署和扩展:服务需要初始节点引导集群,并将后续节点加入集群。
- 有序、自动化的滚动更新:集群始终需要有一个领导者,滚动领导者时需要给集群足够的时间选举新的领导者。
如果服务是无状态的,不需要这些功能,则应使用 Deployment 代替 StatefulSet。例如,将数据持久化到关系型数据库(如 Postgres)的 API 服务,API 服务可以使用 Deployment 运行,而 Postgres 可以使用 StatefulSet 运行。
创建
deploy/proglog/templates/statefulset.yaml
文件,代码如下:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "proglog.fullname" . }}
namespace: {{ .Release.Namespace }}
labels: {{ include "proglog.labels" . | nindent 4 }}
spec:
selector:
matchLabels: {{ include "proglog.selectorLabels" . | nindent 6 }}
serviceName: {{ include "proglog.fullname" . }}
replicas: {{ .Values.replicas }}
template:
metadata:
name: {{ include "proglog.fullname" . }}
labels: {{ include "proglog.labels" . | nindent 8 }}
spec:
# initContainers...
# containers...
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: {{ .Values.storage }}
上述代码中省略了
initContainers
和
containers
字段,下面将填充这些内容。
7.3.3 填充 initContainers
将
initContainers...
替换为以下代码:
initContainers:
- name: {{ include "proglog.fullname" . }}-config-init
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- |-
ID=$(echo $HOSTNAME | rev | cut -d- -f1 | rev)
cat > /var/run/proglog/config.yaml <<EOD
data-dir: /var/run/proglog/data
rpc-port: {{.Values.rpcPort}}
# Make sure the following three key-values are on one line each in
# your code. I split them across multiple lines to fit them in
# for the book.
bind-addr: \
"$HOSTNAME.proglog.{{.Release.Namespace}}.\svc.cluster.local:\
{{.Values.serfPort}}"
bootstrap: $([ $ID = 0 ] && echo true || echo false)
$([ $ID != 0 ] && echo 'start-join-addrs: \
"proglog-0.proglog.{{.Release.Namespace}}.svc.cluster.local:\
{{.Values.serfPort}}"')
EOD
volumeMounts:
- name: datadir
mountPath: /var/run/proglog
初始化容器在 StatefulSet 的应用容器之前运行,用于设置服务的配置文件。第一个服务器配置为引导 Raft 集群,后续服务器配置为加入集群。将
datadir
卷挂载到容器中,以便写入应用容器稍后将读取的配置文件。
7.3.4 填充 containers
将
containers...
替换为以下代码:
containers:
- name: {{ include "proglog.fullname" . }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.rpcPort }}
name: rpc
- containerPort: {{ .Values.serfPort }}
name: serf
args:
- --config-file=/var/run/proglog/config.yaml
# probes...
volumeMounts:
- name: datadir
mountPath: /var/run/proglog
这些容器定义了 StatefulSet 的应用容器,将卷挂载到容器中,用于读取配置文件和持久化日志。使用标志告诉服务配置文件的位置。
7.3.5 容器探针和 gRPC 健康检查
Kubernetes 使用探针来确定是否需要对容器采取行动,以提高服务的可靠性。探针分为以下三种类型:
| 探针类型 | 功能 | 调用时机 |
| — | — | — |
| 存活探针(Liveness probes) | 指示容器是否存活,否则 Kubernetes 将重启容器 | 容器的整个生命周期 |
| 就绪探针(Readiness probes) | 检查容器是否准备好接受流量,否则 Kubernetes 将从服务负载均衡器中移除该 Pod | 容器的整个生命周期 |
| 启动探针(Startup probes) | 指示容器应用程序是否已启动,Kubernetes 可以开始进行存活和就绪探测 | 服务初始化前,启动后不再调用 |
运行探针的方式有三种:
- 对服务器进行 HTTP 请求。
- 对服务器打开 TCP 套接字。
- 在容器中运行命令(例如,Postgres 有一个名为
pg_isready
的命令用于连接到 Postgres 服务器)。
gRPC 服务通常使用
grpc_health_probe
命令,要求服务器满足 gRPC 健康检查协议。我们的服务器需要导出以下服务:
syntax = "proto3";
package grpc.health.v1;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
接下来将更新服务器以导出健康检查服务。
7.3.6 更新服务器以支持健康检查
打开
internal/server/server.go
文件,添加以下导入:
import (
"context"
"time"
api "github.com/travisjeffery/proglog/api/v1"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
"go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
)
更新
NewGRPCServer()
函数,添加以下高亮行:
func NewGRPCServer(config *Config, grpcOpts ...grpc.ServerOption) (
*grpc.Server,
error,
) {
logger := zap.L().Named("server")
zapOpts := []grpc_zap.Option{
grpc_zap.WithDurationField(
func(duration time.Duration) zapcore.Field {
return zap.Int64(
"grpc.time_ns",
duration.Nanoseconds(),
)
},
),
}
trace.ApplyConfig(trace.Config{
DefaultSampler: trace.AlwaysSample(),
})
err := view.Register(ocgrpc.DefaultServerViews...)
if err != nil {
return nil, err
}
grpcOpts = append(grpcOpts,
grpc.StreamInterceptor(
grpc_middleware.ChainStreamServer(
grpc_ctxtags.StreamServerInterceptor(),
grpc_zap.StreamServerInterceptor(
logger, zapOpts...,
),
grpc_auth.StreamServerInterceptor(
authenticate,
),
)), grpc.UnaryInterceptor(
grpc_middleware.ChainUnaryServer(
grpc_ctxtags.UnaryServerInterceptor(),
grpc_zap.UnaryServerInterceptor(
logger, zapOpts...,
),
grpc_auth.UnaryServerInterceptor(
authenticate,
),
)),
grpc.StatsHandler(&ocgrpc.ServerHandler{}),
)
gsrv := grpc.NewServer(grpcOpts...)
hsrv := health.NewServer()
hsrv.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
healthpb.RegisterHealthServer(gsrv, hsrv)
srv, err := newgrpcServer(config)
if err != nil {
return nil, err
}
api.RegisterLogServer(gsrv, srv)
return gsrv, nil
}
这些代码创建了一个支持健康检查协议的服务,设置其服务状态为服务中,以便探针知道服务存活并准备好接受连接。然后将该服务注册到服务器,以便 gRPC 可以调用其端点。
7.3.7 配置探针
将
deploy/proglog/templates/statefulset.yaml
中的
probes...
替换为以下代码:
readinessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:{{ .Values.rpcPort }}"]
initialDelaySeconds: 10
livenessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:{{ .Values.rpcPort }}"]
initialDelaySeconds: 10
在 Dockerfile 中添加以下代码,安装
grpc_health_probe
可执行文件:
FROM golang:1.14-alpine AS build
WORKDIR /go/src/proglog
COPY . .
RUN CGO_ENABLED=0 go build -o /go/bin/proglog ./cmd/proglog
RUN GRPC_HEALTH_PROBE_VERSION=v0.3.2 && \
wget -qO/go/bin/grpc_health_probe \
https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/\
${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
chmod +x /go/bin/grpc_health_probe
FROM scratch
COPY --from=build /go/bin/proglog /bin/proglog
COPY --from=build /go/bin/grpc_health_probe /bin/grpc_health_probe
ENTRYPOINT ["/bin/proglog"]
7.3.8 Kubernetes 服务
Kubernetes 中的服务将应用程序作为网络服务暴露,有以下四种类型:
| 服务类型 | 功能 |
| — | — |
| ClusterIP | 在负载均衡的集群内部 IP 上暴露服务,仅在 Kubernetes 集群内可访问 |
| NodePort | 在每个节点的 IP 上的静态端口上暴露服务,可在 Kubernetes 集群外部访问 |
| LoadBalancer | 使用云提供商的负载均衡器在外部暴露服务,自动创建 ClusterIP 和 NodeIP 服务并管理路由 |
| ExternalName | 作为 DNS 名称的别名 |
不建议使用 NodePort 服务(除了 LoadBalancer 服务为您创建的服务),建议使用 LoadBalancer 或 ClusterIP 服务。
创建
deploy/proglog/templates/service.yaml
文件,用于服务模板:
# 此处代码原文未给出完整内容,可根据实际需求补充
综上所述,本文详细介绍了在本地使用 Kubernetes 和 Helm 部署服务集群的步骤,包括创建代理 CLI、构建 Docker 镜像、使用 Helm 配置和部署服务等。通过这些步骤,您可以在本地环境中轻松部署和管理服务。
7.3.8 Kubernetes 服务(续)
Kubernetes 中的服务将应用程序作为网络服务暴露,有以下四种类型:
| 服务类型 | 功能 |
| — | — |
| ClusterIP | 在负载均衡的集群内部 IP 上暴露服务,仅在 Kubernetes 集群内可访问 |
| NodePort | 在每个节点的 IP 上的静态端口上暴露服务,可在 Kubernetes 集群外部访问 |
| LoadBalancer | 使用云提供商的负载均衡器在外部暴露服务,自动创建 ClusterIP 和 NodeIP 服务并管理路由 |
| ExternalName | 作为 DNS 名称的别名 |
不建议使用 NodePort 服务(除了 LoadBalancer 服务为您创建的服务),建议使用 LoadBalancer 或 ClusterIP 服务。
创建
deploy/proglog/templates/service.yaml
文件,以下是一个示例代码:
apiVersion: v1
kind: Service
metadata:
name: {{ include "proglog.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "proglog.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: rpc
protocol: TCP
name: rpc
selector:
{{- include "proglog.selectorLabels" . | nindent 4 }}
这个
service.yaml
文件定义了一个 Kubernetes 服务,它将根据
values.yaml
中的配置暴露相应的端口,并选择匹配标签的 Pod。
7.3.9 总结 Helm 图表配置
到这里,我们已经完成了 Helm 图表的主要配置,下面是整个配置流程的 mermaid 流程图:
graph LR
A[创建 Helm 图表] --> B[定义 StatefulSet]
B --> C[配置 initContainers]
C --> D[配置 containers]
D --> E[配置容器探针]
E --> F[定义服务]
8. 部署服务到 Kubernetes 集群
现在我们已经完成了所有的准备工作,可以将服务部署到 Kubernetes 集群中了。
8.1 安装 Helm 图表
使用以下命令安装我们创建的 Helm 图表:
$ helm install proglog-service ./deploy/proglog
这个命令会将
deploy/proglog
目录下的 Helm 图表安装到 Kubernetes 集群中,并创建一个名为
proglog-service
的发布。
8.2 验证部署
安装完成后,可以使用以下命令验证服务是否成功部署:
$ kubectl get pods
如果一切正常,你应该看到与
proglog
相关的 Pod 处于运行状态。
还可以使用以下命令查看服务的详细信息:
$ kubectl describe service proglog-service
8.3 测试服务
可以使用
grpcurl
等工具测试服务是否正常工作。例如:
$ grpcurl -plaintext <service-ip>:<rpc-port> grpc.health.v1.Health/Check
其中
<service-ip>
是服务的 IP 地址,可以通过
kubectl get service
命令获取,
<rpc-port>
是服务的 RPC 端口。
9. 服务的扩展和管理
9.1 扩展服务
如果需要扩展服务的副本数量,可以使用以下命令:
$ helm upgrade proglog-service ./deploy/proglog --set replicas=3
这个命令会将
proglog-service
的副本数量扩展到 3。
9.2 滚动更新
当需要更新服务的版本时,可以通过更新 Docker 镜像标签并使用
helm upgrade
命令进行滚动更新:
$ make build-docker TAG=0.0.2
$ kind load docker-image github.com/travisjeffery/proglog:0.0.2
$ helm upgrade proglog-service ./deploy/proglog --set image.tag=0.0.2
9.3 卸载服务
如果需要卸载服务,可以使用以下命令:
$ helm uninstall proglog-service
10. 总结
本文详细介绍了在本地使用 Kubernetes 和 Helm 部署服务集群的完整流程,包括:
1. 了解 Kubernetes 的基本概念和 kubectl 工具的安装。
2. 使用 Kind 工具创建本地 Kubernetes 集群。
3. 编写代理命令行界面(CLI)作为服务的可执行文件。
4. 构建 Docker 镜像并加载到 Kind 集群中。
5. 安装 Helm 并使用它来配置和部署服务。
6. 创建和配置 Helm 图表,包括 StatefulSet、服务和容器探针。
7. 将服务部署到 Kubernetes 集群中,并进行验证和测试。
8. 介绍了服务的扩展、滚动更新和卸载等管理操作。
通过这些步骤,你可以在本地环境中轻松部署、管理和扩展服务,为后续的开发和测试工作提供了坚实的基础。同时,掌握这些技术也有助于你在生产环境中更好地使用 Kubernetes 和 Helm 来管理复杂的应用程序。
超级会员免费看
758

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



