本地使用 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 部署服务集群有了更深入的了解,希望这些知识能帮助你在实际项目中更好地应用和管理服务。
超级会员免费看
759

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



