13、本地使用 Kubernetes 部署应用

本地使用 Kubernetes 部署应用

1. 引言

构建服务后,部署是关键的下一步。本文将介绍如何使用 Kubernetes 和 Helm 在本地部署服务集群,涵盖创建代理命令行界面(CLI)、安装必要工具、构建 Docker 镜像以及使用 Helm 配置和部署服务等内容。

2. 什么是 Kubernetes

Kubernetes 是一个开源的编排系统,用于自动化部署、扩展和操作运行在容器中的服务。它通过 REST API 来创建、更新和删除资源,是一个声明式系统,用户只需描述最终目标状态,Kubernetes 会自动将系统从当前状态转变为目标状态。

常见的 Kubernetes 资源包括:
- Pods :Kubernetes 中最小的可部署单元,可看作逻辑主机,多个容器可共享同一网络命名空间、IP 地址和 IPC 命名空间。
- ConfigMaps 和 Secrets :用于配置 Pods。
- Deployments、StatefulSets、DaemonSets :用于管理 Pod 集合。

此外,还可以通过创建自定义资源和控制器来扩展 Kubernetes。

3. 安装 kubectl

kubectl 是 Kubernetes 的命令行工具,用于对 Kubernetes 集群运行命令,如检查和管理服务的集群资源、查看日志等。

安装步骤如下:

$ 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

4. 使用 Kind 进行本地开发和持续集成

Kind(Kubernetes IN Docker)是 Kubernetes 团队开发的工具,用于使用 Docker 容器作为节点运行本地 Kubernetes 集群,适合本地开发、测试和持续集成。

4.1 安装 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

4.2 安装 Docker

需根据操作系统参考 Docker 的官方安装说明进行安装。

4.3 创建 Kind 集群

$ kind create cluster

4.4 验证集群创建

$ kubectl cluster-info

4.5 查看 Node 容器

$ docker ps

5. 编写代理命令行界面(CLI)

使用 Cobra 库处理命令和标志,结合 Viper 库进行配置管理。

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 暴露标志

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 管理配置

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 {
        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 完成程序编写

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()
}

6. 构建 Docker 镜像

6.1 创建 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"]

6.2 添加 Makefile 目标

TAG ?= 0.0.1
build-docker:
    docker build -t github.com/travisjeffery/proglog:$(TAG) .

6.3 构建并加载镜像

$ make build-docker
$ kind load docker-image github.com/travisjeffery/proglog:0.0.1

7. 使用 Helm 配置和部署服务

Helm 是 Kubernetes 的包管理器,用于分发和安装服务。

7.1 安装 Helm

$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 \
| bash

7.2 安装现有图表

$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm install my-nginx bitnami/nginx

7.3 查看版本

$ helm list

7.4 验证 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

7.5 卸载 Nginx 版本

$ helm uninstall my-nginx

7.6 构建自己的 Helm 图表

$ mkdir deploy && cd deploy
$ helm create proglog

7.7 移除示例模板

$ rm proglog/templates/**/*.yaml proglog/templates/NOTES.txt

7.8 创建 StatefulSet

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:
                - 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}}
                        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
            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
                  volumeMounts:
                    - name: datadir
                      mountPath: /var/run/proglog
            volumeClaimTemplates:
                - metadata:
                    name: datadir
                  spec:
                    accessModes: [ "ReadWriteOnce" ]
                    resources:
                        requests:
                            storage: {{ .Values.storage }}

7.9 容器探针和 gRPC 健康检查

Kubernetes 使用探针来提高服务的可靠性,包括 liveness 探针、readiness 探针和 startup 探针。

7.9.1 更新服务器以导出健康检查服务
import (
    // ...
    "google.golang.org/grpc/health"
    "google.golang.org/grpc/health/grpc_health_v1"
)

func NewGRPCServer(config *Config, grpcOpts ...grpc.ServerOption) (*grpc.Server, error) {
    // ...
    hsrv := health.NewServer()
    hsrv.SetServingStatus("", grpc_health_v1.HealthCheckResponse_SERVING)
    grpc_health_v1.RegisterHealthServer(gsrv, hsrv)
    // ...
    return gsrv, nil
}
7.9.2 更新 StatefulSet 模板以包含探针
readinessProbe:
    exec:
        command: ["/bin/grpc_health_probe", "-addr=:{{ .Values.rpcPort }}"]
    initialDelaySeconds: 10
livenessProbe:
    exec:
        command: ["/bin/grpc_health_probe", "-addr=:{{ .Values.rpcPort }}"]
    initialDelaySeconds: 10
7.9.3 更新 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.10 创建服务

apiVersion: v1
kind: Service
metadata:
    name: {{ include "proglog.fullname" . }}
    namespace: {{ .Release.Namespace }}
    labels: {{ include "proglog.labels" . | nindent 4 }}
spec:
    type: ClusterIP
    ports:
        - port: {{ .Values.rpcPort }}
          targetPort: rpc
          protocol: TCP
          name: rpc
        - port: {{ .Values.serfPort }}
          targetPort: serf
          protocol: TCP
          name: serf
    selector:
        {{ include "proglog.selectorLabels" . | nindent 4 }}

通过以上步骤,我们可以在本地使用 Kubernetes 和 Helm 成功部署服务集群。整个过程涵盖了从环境搭建到服务部署的各个环节,确保服务的稳定运行和可维护性。

下面是一个简单的 mermaid 流程图,展示了部署服务的主要步骤:

graph LR
    A[安装 kubectl] --> B[安装 Kind]
    B --> C[创建 Kind 集群]
    C --> D[编写代理 CLI]
    D --> E[构建 Docker 镜像]
    E --> F[安装 Helm]
    F --> G[构建 Helm 图表]
    G --> H[部署服务]

同时,为了更清晰地展示不同类型的服务在 Kubernetes 中的特点,我们可以使用表格:
| 服务类型 | 描述 | 适用场景 |
| — | — | — |
| ClusterIP | 暴露服务在集群内部的负载均衡 IP 上,仅在集群内可访问 | 内部服务通信 |
| NodePort | 暴露服务在每个节点的静态端口上,可在集群外部访问 | 测试或临时访问 |
| LoadBalancer | 使用云提供商的负载均衡器将服务暴露到外部 | 生产环境对外服务 |
| ExternalName | 作为 DNS 名称的别名 | 指向外部服务 |

以上内容详细介绍了如何在本地使用 Kubernetes 和 Helm 部署服务集群,希望对大家有所帮助。

8. 深入理解 Kubernetes 资源与 Helm 配置

8.1 Kubernetes 资源类型总结

在 Kubernetes 中,不同的资源类型有着不同的用途,下面为你总结常见资源类型及其作用:
| 资源类型 | 作用 | 适用场景 |
| — | — | — |
| Pods | 最小的可部署单元,多个容器可共享网络和存储 | 运行单个或多个紧密关联的容器 |
| ConfigMaps | 存储配置数据,供 Pods 使用 | 分离配置和代码 |
| Secrets | 存储敏感信息,如密码、密钥等 | 保护敏感数据 |
| Deployments | 管理无状态应用的部署和更新 | 无状态服务,如 Web 服务器 |
| StatefulSets | 管理有状态应用,提供稳定的网络标识和持久存储 | 数据库、分布式系统等 |
| DaemonSets | 在每个节点上运行一个 Pod 实例 | 系统监控、日志收集等 |
| Services | 暴露应用为网络服务 | 实现应用的网络访问 |

8.2 Helm 图表结构解析

Helm 图表的结构对于理解和定制服务部署非常重要,下面是 Helm 图表的主要目录和文件说明:

.
└──proglog
    ├──charts
    ├──Chart.yaml
    ├──templates
    │   ├──statefulset.yaml
    │   ├──service.yaml
    │   ├──_helpers.tpl
    │   └──tests
    │       └──test-connection.yaml
    └──values.yaml
  • Chart.yaml :描述图表的元数据,如名称、版本、描述等。
  • values.yaml :包含图表的默认值,用户可以在安装或升级时覆盖这些值。
  • templates :存储模板文件,使用 Go 模板语言渲染生成 Kubernetes 清单文件。
  • charts :可包含子图表,但在本文示例中未使用。

9. 服务部署与监控优化

9.1 服务部署最佳实践

为了确保服务的稳定部署和高效运行,以下是一些服务部署的最佳实践:
1. 使用有意义的标签 :为 Kubernetes 资源添加标签,方便管理和查询。
2. 设置合理的资源限制 :为 Pods 设置资源请求和限制,避免资源过度使用或不足。
3. 使用滚动更新 :对于 Deployment 和 StatefulSet,使用滚动更新策略,确保服务的连续性。
4. 配置健康检查 :使用 liveness、readiness 和 startup 探针,及时发现和处理故障。
5. 自动化部署 :结合 CI/CD 工具,实现服务的自动化部署和更新。

9.2 服务监控与日志管理

服务监控和日志管理对于及时发现和解决问题至关重要。可以使用以下工具和方法:
- Prometheus 和 Grafana :用于监控服务的性能指标,如 CPU、内存、网络等。
- ELK Stack(Elasticsearch、Logstash、Kibana) :用于收集、存储和分析服务日志。
- Kubernetes Dashboard :提供可视化界面,方便查看和管理 Kubernetes 资源。

10. 故障排查与问题解决

10.1 常见故障排查步骤

当服务出现问题时,可以按照以下步骤进行排查:
1. 查看 Pod 状态 :使用 kubectl get pods 命令查看 Pod 的状态,判断是否有异常。
2. 查看日志 :使用 kubectl logs 命令查看 Pod 的日志,查找错误信息。
3. 检查资源使用情况 :使用 kubectl top pods kubectl top nodes 命令查看 Pod 和节点的资源使用情况。
4. 检查网络连接 :使用 kubectl exec 命令进入 Pod 内部,检查网络连接是否正常。
5. 查看事件信息 :使用 kubectl get events 命令查看 Kubernetes 集群的事件信息,了解是否有异常事件发生。

10.2 常见问题解决方案

以下是一些常见问题的解决方案:
- Pod 无法启动 :检查容器镜像是否正确、资源请求是否足够、配置文件是否有误等。
- 服务无法访问 :检查 Service 配置是否正确、网络策略是否允许访问等。
- 资源耗尽 :调整资源限制、扩展节点或优化服务性能。

11. 总结与展望

11.1 总结

通过本文的介绍,我们学习了如何在本地使用 Kubernetes 和 Helm 部署服务集群。从安装必要的工具,如 kubectl、Kind 和 Helm,到编写代理 CLI、构建 Docker 镜像,再到构建和部署 Helm 图表,我们详细了解了整个部署过程。同时,我们还介绍了 Kubernetes 资源类型、Helm 图表结构、服务部署最佳实践、监控与日志管理以及故障排查等方面的知识。

11.2 展望

随着云计算和容器技术的不断发展,Kubernetes 和 Helm 在服务部署和管理中的应用将越来越广泛。未来,我们可以进一步探索以下方面:
- 多集群管理 :使用工具如 KubeFed 管理多个 Kubernetes 集群。
- 安全加固 :加强 Kubernetes 集群的安全防护,如使用 RBAC、网络策略等。
- 自动化运维 :结合机器学习和人工智能技术,实现服务的自动化运维和优化。

下面是一个 mermaid 流程图,展示了故障排查的主要步骤:

graph LR
    A[服务出现问题] --> B[查看 Pod 状态]
    B --> C{Pod 状态是否正常}
    C -- 是 --> D[查看日志]
    C -- 否 --> E[检查资源使用情况]
    D --> F{是否找到错误信息}
    F -- 是 --> G[解决问题]
    F -- 否 --> H[检查网络连接]
    E --> I{资源是否耗尽}
    I -- 是 --> J[调整资源限制]
    I -- 否 --> H
    H --> K{网络连接是否正常}
    K -- 是 --> L[查看事件信息]
    K -- 否 --> M[检查网络配置]
    L --> N{是否发现异常事件}
    N -- 是 --> G
    N -- 否 --> O[进一步分析]
    M --> G
    J --> G
    O --> G

通过以上内容,我们对本地使用 Kubernetes 和 Helm 部署服务集群有了更深入的了解,希望这些知识能帮助你在实际项目中更好地应用和管理服务。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值