Kubernetes 演进史

Kubernetes-应用部署 - 图1
Kubernetes简称K8S(除去开头的K和结尾的S,中间正好有八个字符,故号称K8S)是谷歌开源的一个全新的基于容器技术的分布式架构方案,同时也是一个全新的容器编排工具。Kubernetes的前身是谷歌的Borg系统,始于2003年,它是在谷歌内部使用的一个容器管理系统,它管理着来自数千个不同应用程序的数十万个作业,跨越许多集群,而每个集群拥有多达数万台计算机。2013年,另外一个Omega系统在谷歌内部应用,Omega可以看作是Borg的延伸,在大规模集群管理以及性能方面优于Borg。但是Borg和Omega都是谷歌内部使用的系统,也就是所谓的闭源系统。直到2014年,谷歌以开源的形式推出了Kubernetes系统,同年6月7号在github上完成了第一次提交,随后的7月10号,微软,IBM,Redhat,Docker加入了Kubernetes社区。在2015年7月Kubernetes正式发布了v1.0版本,直到今年6月19号发布了v1.15版本,而且1.16版本正在紧张开发之中。可以看出短短四年已经发布了十六个版本,而且速度越来越快。其实在容器编排领域除了有Kubernetes,还有Docker Swarm和Mesos,在初期,这三者呈三足鼎立之势,但是现在Kubernetes却一枝独秀,Kubernetes已经发展成为一个Kubernetes生态圈,而且以Kubernetes为核心发展起来的的CloudNative的发展势头也很迅猛。

Kubernetes上部署应用

访问位于Kubernetes平台上的应用程序,用户请求到达Ingress资源,Ingress会根据配置的策略,找到相应的service,由于service和pod是通过labelSelector来完成映射的,所以请求到了service,自然而然就能找到相应的pod,从而请求就到了最终的目的地:运行应用程序的容器。流程简图如下
Kubernetes平台上”一切皆资源”,所以如果要部署一个可对外访问的应用程序,根据上图所示可以跟着下面的三步曲(deployment(Pod)—-> Service—-> Ingress)创建出deployment,service,ingress三种资源就可以。另外,本文以实践为主,至于pod的创建过程,service,ingress的详细信息后续会有相关文章介绍。

第一步:Deployment(Pod)的创建

Pod是Kubernetes最基本也最重要的概念,它是Kubernetes平台上资源调度的最小也是最基本单元。应用程序运行在容器内,pod管理着容器,而pod是被调度到node上面的,node可以是一个物理机或者虚拟机。容器,pod,node的关系犹如豌豆株,豌豆荚,豌豆的关系。如下图所示
Kubernetes-应用部署 - 图2
一个pod里面可以有多个容器,同一个pod内的多个容器共享pod的资源,比如共享卷,网络等。就像一个豌豆荚里面可以有多个豌豆,同一个豌豆荚里面的几个豌豆共用一个豌豆蒂来吸收营养。pod是运行在Kubernetes node节点上的,一个node上面可以运行多个pod,就像一株豌豆上面有多个豌豆荚。一个Kubernetes cluster一般包含多个node,就像一亩地里面一般有多株豌豆。

准备工作

Kubernetes平台上应用程序运行在容器内,容器是用镜像生成的,所以在部署pod之前,我们先将一个输出 HelloDevOps 的应用程序打包成容器镜像。应用程序的代码如下

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. "os"
  7. )
  8. func get_hostname() string {
  9. hostname, _ := os.Hostname()
  10. return hostname
  11. }
  12. func get_env_var() string {
  13. version := os.Getenv("VERSION")
  14. return version
  15. }
  16. func handler(w http.ResponseWriter, r *http.Request) {
  17. fmt.Fprintf(w, "Hello DevOps, pod name is %s, version is %s",get_hostname(), get_env_var())
  18. }
  19. func main() {
  20. http.HandleFunc("/", handler)
  21. log.Fatal(http.ListenAndServe(":9999", nil))
  22. }

接下来编写Dockerfile

  1. FROM golang:1.12.7-alpine3.9
  2. MAINTAINER devops008@sina.com
  3. WORKDIR /usr/src/app
  4. COPY main.go /usr/src/app
  5. EXPOSE 8888
  6. CMD ["go","run","main.go"]

然后用命令 docker build-t dllhb/go_demo:v1.0&&docker push dllhb/go_demo:v1.0 将镜像push到dockerhub上面,下面可以用此镜像创建一个pod。

创建Deployment(Pod)
  1. apiVersion: extensions/v1beta1
  2. kind: Deployment
  3. metadata:
  4. name: hello-devops
  5. labels:
  6. app: hello-devops
  7. spec:
  8. replicas: 1
  9. template:
  10. metadata:
  11. labels:
  12. app: hello-devops
  13. spec:
  14. containers:
  15. - name: hello-devops
  16. image: dllhb/go_demo:v1.0
  17. resources:
  18. limits:
  19. cpu: "200m"
  20. memory: "200Mi"
  21. requests:
  22. cpu: "100m"
  23. memory: "50Mi"
  24. imagePullPolicy: IfNotPresent
  25. ports:
  26. - containerPort: 8888

然后用 kubectl-n devops create-f devops.yaml 在devops namespace下面创建出deployment,并查看pod的状态。

  1. $ kubectl -n devops get pods
  2. NAME READY STATUS RESTARTS AGE
  3. hello-devops-78dd67d9cc-klxlm 1/1 Running 0 17s

应用程序在容器里面运行,然后pod负责管理,每个pod在创建成功后会被分配给一个IP,其他pod可以通过这个IP来跟它进行通信,但是pod的IP在pod重启以后就会发生变化,这样导致了其他pod无法跟它进行通信。在此情况下,抽象一个叫做Service的资源,Service具有一个唯一指定的名字,一旦创建便不会改变,直到被删除。通俗来讲,Service是一组具有相同label的pod所提供服务的一种抽象。这样Service和pod就形成了映射。以Service name之不变而解决了pod IP的常变之大问题。

第二步:Service的创建

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: hello-devops-svc
  5. labels:
  6. app: hello-devops
  7. spec:
  8. ports:
  9. - port: 8888
  10. name: http
  11. selector:
  12. app: hello-devops

然后执行 kubectl-n devops-f service.yaml 在devops namespace下面创建出service,并查看

  1. $ kubectl -n devops get svc
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. hello-devops-svc ClusterIP 172.21.246.13 <none> 8888/TCP 71s

部署应用的目的是为了能够给用户提供可用的服务,也就是说服务要能够对外访问,这就涉及到了服务暴露这个话题。Kubernetes服务暴露的形式有三种:NodeIP+Port, LoadBalancer,Ingress。如果要通过NodeIP+Port的方式暴露服务,需要在Kubernetes节点上面打开相应的端口,对于生产线上的应用,这种方式存在一定的安全隐患;另外,云厂商提供的Kubernetes cluster中的node如果由于升级等原因重启之后NodeIP很可能发生改变。所以NodeIP+Port是Kubernetes早期广泛应用的服务暴露方式。LoadBalancer一般是云厂商提供的一种服务暴露方式,如果要自己搭建,会显得费时费力,所以我们选择用Ingress来对服务进行暴露。Ingress的实现是通过Ingress策略定义和一个具体的Ingress Controller实现结合起来,实现服务暴露。至于Ingress Controller和Ingress策略定义后续会有文章进行详解。本文的目的在于实践,可以根据Ingress资源的定义方式直接创建Ingress资源来完成服务暴露。

第三步:Ingress的创建

Ingress 如果开启了TLS,那么需要对应的创建一个secret资源,Kubernetes的secret资源主要用来存放一些敏感信息,比如密码,token等。可以先用openssl命令生成tls.cert和tls.key文件

  1. $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=devops.test.com"

然后用这两个文件来创建secret

  1. $ kubectl -n devops create secret tls test-tls --key=/tmp/tls.key --cert=/tmp/tls.crt

将下述内容写入一个ingress.yaml文件

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: hello-devops-ingress
  5. annotations:
  6. nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
  7. spec:
  8. tls:
  9. - hosts:
  10. - hello-devops.test.com
  11. secretName: test-tls
  12. rules:
  13. - host: hello-devops.test.com
  14. http:
  15. paths:
  16. - path: /
  17. backend:
  18. serviceName: hello-devops-svc
  19. servicePort: 8888

然后使用命令 kubectl-n devops create-f ingress.yaml 在devops namespace下面创建Ingress资源并查看

  1. $ kubectl -n devops get ing
  2. NAME HOSTS ADDRESS PORTS AGE
  3. hello-devops-ingress devops.test.com 10.208.70.22 80, 443 100s

用curl命令查看 curl-k-s https://devops.test.com 可以看到如下内容

  1. $ curl -k -s https://devops.test.com
  2. Hello DevOps, pod name is hello-devops-78dd67d9cc-klxlm

至此,通过创建deployment,service,ingress资源完成了Hello DevOps的应用部署。简单的demo可以按照这种简单的几个步骤在Kubernetes上面完成部署,但是当微服务的数量增加,资源的管理文件也将是海量的,不仅仅只有上面的deployment,service,ingress,还将会有secret,configmap,pvc等。这时候就需要用Helm来完成这些繁琐文件的管理了,所以敬请期待后续关于Helm的相关文章。