K8s 实战之概念、集群部署与服务配置

K8s 实战之概念、集群部署与服务配置

本文是对于 Kubernetes 实战系列文章的提炼。javascript

Kubernetes [koo-ber-nay'-tice] 是 Google 基于 Borg 开源的容器编排调度引擎,其支持多种底层容器虚拟化技术,具备完备的功能用于支撑分布式系统以及微服务架构,同时具有超强的横向扩容能力;它提供了自动化容器的部署和复制,随时扩展或收缩容器规模,将容器组织成组,而且提供容器间的负载均衡,提供容器弹性等特性。做为 CNCF(Cloud Native Computing Foundation)最重要的组件之一,可谓云操做系统;它的目标不只仅是一个编排系统,而是提供一个规范,可让你来描述集群的架构,定义服务的最终状态。html

设计理念

与通常的 PaaS 平台相比,K8s 也是支持服务部署、自动运维、资源调度、扩缩容、自我修复、负载均衡,服务发现等功能,而其独特之处就是其对于基础设施层进行了较好的能力抽象。K8s 并无处理具体的存储、网络这些差别性极大的部分,而是作云无关,开始实现各种 interface,作各类抽象。好比容器运行时接口(CRI)、容器网络接口(CNI)、容器存储接口(CSI)。这些接口让 Kubernetes 变得无比开放,而其自己则能够专一于内部部署及容器调度。前端

Kubernetes 有相似于 Linux 的分层架构,以下图所示:java

  • 基础设施层:包括容器运行时、网络、存储等。
  • 核心层:Kubernetes 最核心的功能,对外提供 API 构建高层的应用,对内提供插件式应用执行环境。
  • 应用层:部署(无状态、有状态应用、Job 等)和路由(服务发现、负载均衡等)
  • 管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态 Provision 等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy 等)
  • 接口层:kubectl 命令行工具、客户端 SDK 以及集群联邦
  • 生态系统:在接口层之上的庞大容器集群管理调度的生态系统,能够划分为两个范畴:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS 应用、ChatOps 等外部生态以及 CRI、CNI、CSI、镜像仓库、Cloud Provider、集群自身的配置和管理等内部生态。

Kubernetes 中全部的配置都是经过 API 对象的 spec 去设置的,也就是用户经过配置系统的理想状态来改变系统,这是 Kubernetes 重要设计理念之一,即全部的操做都是声明式(Declarative)的而不是命令式(Imperative)的。声明式操做在分布式系统中的好处是稳定,不怕丢操做或运行屡次,例如设置副本数为 3 的操做运行屡次也仍是一个结果,而给副本数加 1 的操做就不是声明式的,运行屡次结果就错了。python

相对于命令式操做,声明式操做会更稳定且更容易被用户接受,由于该 API 中隐含了用户想要操做的目标对象,而这些对象恰好都是名词性质的,好比 Service、Deployment、PV 等;且声明式的配置文件更贴近“人类语言”,好比 YAML、JSON。声明式的设计理念有助于实现控制闭环,持续观测、校订,最终将运行状态达到用户指望的状态;感知用户的行为并执行。好比修改 Pod 数量,应用升级/回滚等等。调度器是核心,但它只是负责从集群节点中选择合适的 Node 来运行 Pods,显然让调度器来实现上诉的功能不太合适,而须要有专门的控制器组件来实现。mysql

组件与对象

Kubernetes 的各类功能都离不开它定义的资源对象,这些对象均可以经过 API 被提交到集群的 Etcd 中。API 的定义和实现都符合 HTTP REST 的格式,用户能够经过标准的 HTTP 动词(POST、PUT、GET、DELETE)来完成对相关资源对象的增删改查。经常使用的资源对象,好比 Deployment、DaemonSet、Job、PV 等。API 的抽象也意在这部分资源对象的定义。Kubernetes 有新的功能实现,通常会建立新的资源对象,而功能也依托于该对象进行实现。nginx

类别 名称
资源对象 Pod、ReplicaSet、ReplicationController、Deployment、StatefulSet、DaemonSet、Job、CronJob、HorizontalPodAutoscaling、Node、Namespace、Service、Ingress、Label、CustomResourceDefinition
存储对象 Volume、PersistentVolume、Secret、ConfigMap
策略对象 SecurityContext、ResourceQuota、LimitRange
身份对象 ServiceAccount、Role、ClusterRole

这里咱们选择几个关键对象进行介绍。git

部署(Deployment)

部署表示用户对 Kubernetes 集群的一次更新操做。部署是一个比 RS 应用模式更广的 API 对象,能够是建立一个新的服务,更新一个新的服务,也能够是滚动升级一个服务。滚动升级一个服务,实际是建立一个新的 RS,而后逐渐将新 RS 中副本数增长到理想状态,将旧 RS 中的副本数减少到 0 的复合操做;这样一个复合操做用一个 RS 是不太好描述的,因此用一个更通用的 Deployment 来描述。以 Kubernetes 的发展方向,将来对全部长期伺服型的的业务的管理,都会经过 Deployment 来管理。程序员

服务(Service)

RC、RS 和 Deployment 只是保证了支撑服务的微服务 Pod 的数量,可是没有解决如何访问这些服务的问题。若是说 Deployment 是负责保证 Pod 组的正常运行,那么 Service 就是用于保证以合理的网络来链接到该组 Pod。github

一个 Pod 只是一个运行服务的实例,随时可能在一个节点上中止,在另外一个节点以一个新的 IP 启动一个新的 Pod,所以不能以肯定的 IP 和端口号提供服务。要稳定地提供服务须要服务发现和负载均衡能力。服务发现完成的工做,是针对客户端访问的服务,找到对应的的后端服务实例。在 K8 集群中,客户端须要访问的服务就是 Service 对象。每一个 Service 会对应一个集群内部有效的虚拟 IP,集群内部经过虚拟 IP 访问一个服务。Service 有三种类型:

  • ClusterIP:默认类型,自动分配一个仅 Cluster 内部能够访问的虚拟 IP。
  • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就能够经过 <NodeIP>:NodePort 来访问该服务。
  • LoadBalancer:在 NodePort 的基础上,借助 Cloud Provider 建立一个外部的负载均衡器,并将请求转发到 <NodeIP>:NodePort

在 Kubernetes 集群中微服务的负载均衡是由 Kube-proxy 实现的。Kube-proxy 是 Kubernetes 集群内部的负载均衡器。它是一个分布式代理服务器,在 Kubernetes 的每一个节点上都有一个;这一设计体现了它的伸缩性优点,须要访问服务的节点越多,提供负载均衡能力的 Kube-proxy 就越多,高可用节点也随之增多。与之相比,咱们平时在服务器端作个反向代理作负载均衡,还要进一步解决反向代理的负载均衡和高可用问题。

集群部署

Kubernetes 实战系列中咱们介绍了 Docker 本地搭建,基于 Ubuntu 手动搭建集群以及基于 Rancher 快速搭建集群等方式。使用 Rancher 能够自动和可视化的完成 Kubernetes 集群的安装工做,省去的繁琐的人工安装过程,然您快速投入的业务开发中。

$ docker run -d --restart=unless-stopped -p 80:80 -p 443:443 rancher/rancher

先在 Master 节点安装 Rancher server、control、etcd 和 worker。选择网络组件为 Flannel,同时在自定义主机运行命令中选择主机角色、填写主机的内网和外网 IP。

咱们须要将脚本复制到对应的机器上运行,而后 Rancher 将自动建立 Kubernetes 集群,并默认在 80 端口运行 Web Server。添加 Node 节点时只须要在 Rancher 的 Web 界面上找到您刚安装的集群并选择【编辑集群】并选择节点角色为 Worker 便可增长一台 Kubenretes 集群节点。

Helm

Helm 是由 Deis 发起的一个开源工具,有助于简化部署和管理 Kubernetes 应用。在本章的实践中,咱们也会使用 Helm 来简化不少应用的安装操做。

在 Linux 中可使用 Snap 安装 Heml:

$ sudo snap install helm --classic

# 经过键入以下命令,在 Kubernetes 群集上安装 Tiller
$ helm init --upgrade

在缺省配置下, Helm 会利用 "gcr.io/kubernetes-helm/tiller" 镜像在 Kubernetes 集群上安装配置 Tiller;而且利用 "https://kubernetes-charts.storage.googleapis.com" 做为缺省的 stable repository 的地址。因为在国内可能没法访问 "gcr.io", "storage.googleapis.com" 等域名,阿里云容器服务为此提供了镜像站点。请执行以下命令利用阿里云的镜像来配置 Helm:

$ helm init --upgrade -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.5.1 --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

# 删除默认的源
$ helm repo remove stable

# 增长新的国内镜像源
$ helm repo add stable https://burdenbear.github.io/kube-charts-mirror/
$ helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

# 查看 Helm 源添加状况
$ helm repo list

Helm 的常见命令以下:

# 查看在存储库中可用的全部 Helm Charts
$ helm search

# 更新 Charts 列表以获取最新版本
$ helm repo update

# 查看某个 Chart 的变量
$ helm inspect values stable/mysql

# 查看在群集上安装的 Charts 列表
$ helm list

# 删除某个 Charts 的部署
$ helm del --purge wordpress-test

# 为 Tiller 部署添加受权
$ kubectl create serviceaccount --namespace kube-system tiller
$ kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
$ kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

kubectl

信息检索

get 命令用于获取集群的一个或一些 resource 信息。使用--help 查看详细信息。kubectl 的帮助信息、示例至关详细,并且简单易懂。建议你们习惯使用帮助信息。kubectl 能够列出集群全部 resource 的详细。resource 包括集群节点、运行的 pod,ReplicationController,service 等。

$ kubectl get [(-o|--output=)json|yaml|wide|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE [NAME | -l label] | TYPE/NAME ...) [flags] [flags]

运行与管理

kubectl run 和 docker run 同样,它能将一个镜像运行起来,咱们使用 kubectl run 来将一个 sonarqube 的镜像启动起来。

$ kubectl run sonarqube --image=sonarqube:5.6.5 --replicas=1 --port=9000

deployment "sonarqube" created

# 该命令为咱们建立了一个 Deployment
$ kubectl get deployment
NAME        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
sonarqube   1         1         1            1           5m

咱们也能够直接以交互方式运行某个镜像:

$ kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il

K8s 将镜像运行在 Pod 中以方便实施卷和网络共享等管理,使用 get pods 能够清楚的看到生成了一个 Pod:

$ kubectl get pods
NAME                         READY     STATUS    RESTARTS   AGE
sonarqube-1880671902-s3fdq   1/1       Running   0          6m

$ 交互式运行 Pod 中的某个命令
$ kubectl exec -it sonarqube-1880671902-s3fdq -- /bin/bash

kubectl 能够用于删除建立好的 Deployment 与 Pod:

$ kubectl delete pods sonarqube-1880671902-s3fdq
$ kubectl delete deployment sonarqube

kubectl 通用能够基于 Yaml 文件进行应用的生命周期管理:

# 建立
$ kubectl create -f yamls/mysql.yaml

# 删除
$ kubectl delete -f yamls/mysql.yaml

# 同时建立多个
$ kubectl create -f yamls/

# 同时删除多个
$ kubectl delete -f yamls/

上下文切换

在 K8s 集群安装完毕以后,能够下载集群的配置文件到本地 kubectl 配置中:

mkdir $HOME/.kube
scp root@<master-public-ip>:/etc/kubernetes/kube.conf $HOME/.kube/config

而后能够来查看当前的上下文

$ unset KUBECONFIG
$ kubectl config current-context # 查看当前载入的上下文
$ kubectl config get-contexts # 浏览可用的上下文
$ kubectl config use-context context-name # 切换到指定上下文

服务配置

Kubernetes 实战/典型应用一节中,咱们介绍了许多常见的中间件的配置部署方式。这里以简单的 HTTP 服务器为例,介绍常见的服务配置流程。

Deployment & Service

K8s Boilerplates 中咱们定义了简单的 Nginx 的部署与服务,分别用于集群构建与对外的服务暴露:

# nginx-deployment-service.yaml
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nginx
  replicas: 3 # tells deployment to run 1 pods matching the template
  template: # create pods using pod definition in this template
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx
spec:
  externalTrafficPolicy: Local
  ports:
    - name: http
      port: 80
  selector:
    app: nginx
  type: NodePort
$ kubectl create -f https://raw.githubusercontent.com/wx-chevalier/Backend-Boilerplates/master/K8s/Base/nginx-deployment-service.yaml

$ kubectl get pod

NAME                                             READY   STATUS    RESTARTS   AGE
nginx-56db997f77-2q6qz                           1/1     Running   0          3m21s
nginx-56db997f77-fv2zs                           1/1     Running   0          3m21s
nginx-56db997f77-wx2q5                           1/1     Running   0          3m21s

$ kubectl get deployment

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
nginx                           3/3     3            3           3m36s

$ kubectl get svc

NAME                            TYPE           CLUSTER-IP      EXTERNAL-IP                              PORT(S)                      AGE
kubernetes                      ClusterIP      10.43.0.1       <none>                                   443/TCP                      21h
nginx                           NodePort       10.43.8.50      <none>                                   80:32356/TCP                 4m5s

Ingress

Ingress 是一种 Kubernetes 资源,也是将 Kubernetes 集群内服务暴露到外部的一种方式。ngress 只是一个统称,其由 Ingress 和 Ingress Controller 两部分组成。Ingress 用做将原来须要手动配置的规则抽象成一个 Ingress 对象,使用 YAML 格式的文件来建立和管理。Ingress Controller 用做经过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化。

目前可用的 Ingress Controller 类型有不少,好比:Nginx、HAProxy、Traefik 等,Nginx Ingress 使用 ConfigMap 来管理 Nginx 配置。

Helm 安装 Ingress

$ helm install --name nginx-ingress --set "rbac.create=true,controller.service.externalIPs[0]=172.19.157.1,controller.service.externalIPs[1]=172.19.157.2,controller.service.$
xternalIPs[2]=172.19.157.3" stable/nginx-ingress

NAME:   nginx-ingress
LAST DEPLOYED: Tue Aug 20 14:50:13 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                      DATA  AGE
nginx-ingress-controller  1     0s

==> v1/Pod(related)
NAME                                            READY  STATUS             RESTARTS  AGE
nginx-ingress-controller-5f874f7bf4-nvsvv       0/1    ContainerCreating  0         0s
nginx-ingress-default-backend-6f598d9c4c-vj4v8  0/1    ContainerCreating  0         0s

==> v1/Service
NAME                           TYPE          CLUSTER-IP    EXTERNAL-IP                             PORT(S)                     AGE
nginx-ingress-controller       LoadBalancer  10.43.115.59  172.19.157.1,172.19.157.2,172.19.157.3  80:32122/TCP,443:32312/TCP  0s
nginx-ingress-default-backend  ClusterIP     10.43.8.65    <none>                                  80/TCP                      0s

==> v1/ServiceAccount
NAME           SECRETS  AGE
nginx-ingress  1        0s

==> v1beta1/ClusterRole
NAME           AGE
nginx-ingress  0s

==> v1beta1/ClusterRoleBinding
NAME           AGE
nginx-ingress  0s

==> v1beta1/Deployment
NAME                           READY  UP-TO-DATE  AVAILABLE  AGE
nginx-ingress-controller       0/1    1           0          0s
nginx-ingress-default-backend  0/1    1           0          0s

==> v1beta1/PodDisruptionBudget
NAME                           MIN AVAILABLE  MAX UNAVAILABLE  ALLOWED DISRUPTIONS  AGE
nginx-ingress-controller       1              N/A              0                    0s
nginx-ingress-default-backend  1              N/A              0                    0s

部署完成后咱们能够看到 Kubernetes 服务中增长了 nginx-ingress-controller 和 nginx-ingress-default-backend 两个服务。nginx-ingress-controller 为 Ingress Controller,主要作为一个七层的负载均衡器来提供 HTTP 路由、粘性会话、SSL 终止、SSL 直通、TCP 和 UDP 负载平衡等功能。nginx-ingress-default-backend 为默认的后端,当集群外部的请求经过 Ingress 进入到集群内部时,若是没法负载到相应后端的 Service 上时,这种未知的请求将会被负载到这个默认的后端上。

$ kubectl get svc
NAME                            TYPE           CLUSTER-IP      EXTERNAL-IP                              PORT(S)                      AGE
kubernetes                      ClusterIP      10.43.0.1       <none>                                   443/TCP                      20h
nginx-ingress-controller        LoadBalancer   10.43.115.59    172.19.157.1,172.19.157.2,172.19.157.3   80:32122/TCP,443:32312/TCP   77m
nginx-ingress-default-backend   ClusterIP      10.43.8.65      <none>                                   80/TCP                       77m

$ kubectl --namespace default get services -o wide -w nginx-ingress-controller

NAME                       TYPE           CLUSTER-IP     EXTERNAL-IP                              PORT(S)                      AGE   SELECTOR
nginx-ingress-controller   LoadBalancer   10.43.115.59   172.19.157.1,172.19.157.2,172.19.157.3   80:32122/TCP,443:32312/TCP   77m   app=nginx-ingress,component=controller,release=nginx-ingress

因为咱们采用了 externalIP 方式对外暴露服务, 因此 nginx-ingress-controller 会在三台节点宿主机上的 暴露 80/443 端口。咱们能够在任意节点上进行访问,由于咱们尚未在 Kubernetes 集群中建立 Ingress 资源,因此直接对 ExternalIP 的请求被负载到了 nginx-ingress-default-backend 上。nginx-ingress-default-backend 默认提供了两个 URL 进行访问,其中的 /healthz 用做健康检查返回 200,而 / 返回 404 错误。

$ curl 127.0.0.1/
# default backend - 404

$ curl 127.0.0.1/healthz/
# 返回的是 200

后续咱们若是须要建立自身的 Ingress 配置,能够参考以下方式:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: example
  namespace: foo
spec:
  rules:
    - host: www.example.com
      http:
        paths:
          - backend:
              serviceName: exampleService
              servicePort: 80
            path: /
  # This section is only required if TLS is to be enabled for the Ingress
  tls:
    - hosts:
        - www.example.com
      secretName: example-tls

若是但愿使用 TLS,那么须要建立包含证书与 Key 的 Secret:

apiVersion: v1
kind: Secret
metadata:
  name: example-tls
  namespace: foo
data:
  tls.crt: <base64 encoded cert>
  tls.key: <base64 encoded key>
type: kubernetes.io/tls

WordPress

Helm 安装完毕后,咱们来测试部署一个 WordPress 应用:

$ helm install --name wordpress-test --set "ingress.enabled=true,persistence.enabled=false,mariadb.persistence.enabled=false" stable/wordpress

NAME:  wordpress-test
...

这里咱们使用 Ingress 负载均衡进行访问,能够经过以下方式访问到服务:

$ kubectl get ingress

NAME                             HOSTS             ADDRESS                                  PORTS   AGE
wordpress.local-wordpress-test   wordpress.local   172.19.157.1,172.19.157.2,172.19.157.3   80      59m

$ curl -I http://wordpress.local -x 127.0.0.1:80

HTTP/1.1 200 OK
Server: nginx/1.15.6
Date: Tue, 20 Aug 2019 07:55:21 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/7.0.27
Link: <http://wordpress.local/wp-json/>; rel="https://api.w.org/"

也能够根据 Charts 的说明,利用以下命令得到 WordPress 站点的管理员用户和密码:

echo Username: user
echo Password: $(kubectl get secret --namespace default wordpress-test-wordpress -o jsonpath="{.data.wordpress-password}" | base64 --decode)

==> v1beta1/Role
NAME           AGE
nginx-ingress  0s

==> v1beta1/RoleBinding
NAME           AGE
nginx-ingress  0s

延伸阅读

某熊的技术之路指北 ☯ 就是对笔者不一样领域方面沉淀下的知识仓库的导航与索引,便于读者快速地寻找到本身须要的内容。路漫漫其修远兮,吾正上下而求索,也但愿能给全部碰见过笔者痕迹的同窗些许帮助,在浩瀚银河间能顺利达到一个又一个彼岸。

您能够经过如下导航来在 Gitbook 中阅读笔者的系列文章,涵盖了技术资料概括、编程语言与理论、Web 与大前端、服务端开发与基础架构、云计算与大数据、数据科学与人工智能、产品设计等多个领域:

此外,你还可前往 xCompass 交互式地检索、查找须要的文章/连接/书籍/课程;或者在 MATRIX 文章与代码索引矩阵中查看文章与项目源代码等更详细的目录导航信息。最后,你也能够关注微信公众号:『某熊的技术之路』以获取最新资讯。

相关文章
相关标签/搜索