k8s入门

零、环境版本说明

1、docker 19.03.2

docker --version
Docker version 19.03.2, build 6a30dfc

2、kubectl

kubectl version
Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.6", GitCommit:"96fac5cd13a5dc064f7d9f4f23030a6aeface6cc", GitTreeState:"clean", BuildDate:"2019-08-19T11:13:49Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:09:08Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}

3、minikube v1.5.2

minikube version
minikube version: v1.5.2
commit: 792dbf92a1de583fcee76f8791cff12e0c9440ad

4、Mac OS 10.14.6

一、构建测试 docker 服务

1、为了尽量简化例子,我们要部署的服务是用 Nginx 来 serve 一个简单的 HTML 文件 html/index.html

mkdir dockerhello
cd dockerhello/
mkdir html
echo '<h1>Hello Docker!</h1>' > html/index.html

2、编写Dockerfile
在当前目录创建一个叫 Dockerfile 的新文件,包含下面的内容:

FROM nginx
COPY html/* /usr/share/nginx/html

3、构建镜像
docker build -t dockerhello:0.1 .
4、容器运行
Nginx 默认监听在 80 端口,所以我们把宿主机的 8090 端口映射到容器的 80 端口
docker run --name dockerhello -d -p 8090:80 dockerhello:0.1
我们可以在浏览器中输入 http://127.0.0.1:8090/
看到我们docker容器已经运行起来了
5、引入Kubernetes前的准备
在现实的生产环境中 Docker 本身是一个相对底层的容器引擎,在有很多服务器的集群中,不太可能以上面的方式来管理任务和资源。所以我们需要 Kubernetes 这样的系统来进行任务的编排和调度。在进入下一步前,别忘了把实验用的容器清理掉
docker rm -f dockerhello

二、安装 Kubernetes

需要安装三样东西:
Kubernetes 的命令行客户端 kubctl、
一个可以在本地跑起来的 Kubernetes 环境 Minikube、
以及给 Minikube 使用的虚拟化引擎 hyperkit

brew install kubectl
brew install minikube
brew install docker-machine-driver-hyperkit

Minikube 默认的虚拟化引擎是 VirtualBox,而 hyperkit 是一个更轻量、性能更好的替代。
它需要以 root 权限运行,所以安装完要把所有者改为 root:wheel,并把 setuid 权限打开

sudo chown root:wheel /usr/local/opt/docker-machine-driver-hyperkit/bin/docker-machine-driver-hyperkit
sudo chmod u+s /usr/local/opt/docker-machine-driver-hyperkit/bin/docker-machine-driver-hyperkit

然后就可以启动 Minikube 了
minikube start --vm-driver hyperkit
如果你在第一次启动 Minikube 时遇到错误或被中断,后面重试仍然失败时,可以尝试运行 minikube delete 把集群删除,重新来过。

$ minikube delete
🔥  正在删除 hyperkit 中的“minikube”…
💔  “minikube”集群已删除。
🔥  Successfully deleted profile "minikube"

Minikube 启动时会自动配置 kubectl,把它指向 Minikube 提供的 Kubernetes API 服务。可以用下面的命令确认

$ kubectl config current-context
minikube

四、Kubernetes 架构简介

典型的 Kubernetes 集群包含一个 master 和很多 node。
Master 是控制集群的中心,node 是提供 CPU、内存和存储资源的节点。
Master 上运行着多个进程,包括面向用户的 API 服务、负责维护集群状态的 Controller Manager、负责调度任务的 Scheduler 等。
每个 node 上运行着维护 node 状态并和 master 通信的 kubelet,以及实现集群网络服务的 kube-proxy。
作为一个开发和测试的环境,Minikube 会建立一个有一个 node 的集群,用下面的命令可以看到:

$ kubectl get nodes
NAME       STATUS   ROLES    AGE    VERSION
minikube   Ready    master   4m7s   v1.16.2

五、部署一个单实例服务

1、创建Pod
我们先尝试像文章开始介绍 Docker 时一样,部署一个简单的服务。Kubernetes 中部署的最小单位是 pod,而不是 Docker 容器。实时上 Kubernetes 是不依赖于 Docker 的,完全可以使用其他的容器引擎在 Kubernetes 管理的集群中替代 Docker。在与 Docker 结合使用时,一个 pod 中可以包含一个或多个 Docker 容器。但除了有紧密耦合的情况下,通常一个 pod 中只有一个容器,这样方便不同的服务各自独立地扩展。

K8S 是运行在 xhyve 建立的虚拟环境下。所以本地的 docker 命令是无法连接到 K8S 所依赖的 docker-daemon 的。当然,你可以通过 minikube ssh 进入虚拟环境,再使用 docker 命令进行观察。

好在 Minikube 自带了 Docker 引擎,所以我们需要重新配置客户端,让 docker 命令行与 Minikube 中的 Docker 进程通讯:eval $(minikube docker-env)
eval $(minikube docker-env -u) 取消与 minikube 中的 docker 进行绑定

在运行上面的命令后,再运行 docker image ls 时只能看到一些 Minikube 自带的镜像,就看不到我们刚才构建的 dockerhello:0.1 镜像了。
所以在继续之前,要重新构建一遍我们的镜像,这里顺便改一下名字,叫它 k8s-demo:0.1,执行命令
docker build -t k8s-demo:0.1 .
然后创建一个叫 pod.yml 的定义文件:

apiVersion: v1
kind: Pod
metadata:
  name: k8s-demo
spec:
  containers:
    - name: k8s-demo
      image: k8s-demo:0.1
      ports:
        - containerPort: 80

这里定义了一个叫 k8s-demo 的 Pod,使用我们刚才构建的 k8s-demo:0.1 镜像。
这个文件也告诉 Kubernetes 容器内的进程会监听 80 端口。
然后把它跑起来:

$ kubectl create -f pod.yml
pod/k8s-demo created

kubectl 把这个文件提交给 Kubernetes API 服务,然后 Kubernetes Master 会按照要求把 Pod 分配到 node 上。用下面的命令可以看到这个新建的 Pod:

$ kubectl get pods
NAME       READY   STATUS    RESTARTS   AGE
k8s-demo   1/1     Running   0          2m56s

因为我们的镜像在本地,并且这个服务也很简单,所以运行 kubectl get pods 的时候 STATUS 已经是 running。要是使用远程镜像(比如 Docker Hub 上的镜像),你看到的状态可能不是 Running,就需要再等待一下。

2、创建Service
虽然这个 pod 在运行,但是我们是无法像之前测试 Docker 时一样用浏览器访问它运行的服务的。可以理解为 pod 都运行在一个内网,我们无法从外部直接访问。要把服务暴露出来,我们需要创建一个 Service。Service 的作用有点像建立了一个反向代理和负载均衡器,负责把请求分发给后面的 pod。
创建一个 Service 的定义文件 svc.yml

apiVersion: v1
kind: Service
metadata:
  name: k8s-demo-svc
  labels:
    app: k8s-demo
spec:
  type: NodePort
  ports:
    - port: 80
      nodePort: 30050
  selector:
    app: k8s-demo

这个 service 会把容器的 80 端口从 node 的 30050 端口暴露出来。
注意文件最后两行的 selector 部分,这里决定了请求会被发送给集群里的哪些 pod。
这里的定义是所有包含「app: k8s-demo」这个标签的 pod。
然而我们之前部署的 pod 并没有设置标签

$ kubectl describe pods | grep Labels
Labels:             <none>

所以要先更新一下 pod.yml,把标签加上(注意在 metadata: 下增加了 labels 部分)

apiVersion: v1
kind: Pod
metadata:
  name: k8s-demo
  labels:
    app: k8s-demo
spec:
  containers:
    - name: k8s-demo
      image: k8s-demo:0.1
      ports:
        - containerPort: 80

然后更新 pod 并确认成功新增了标签:

$ kubectl apply -f pod.yml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
pod/k8s-demo configured
$ kubectl describe pods | grep Labels
Labels:             app=k8s-demo

然后就可以创建这个 service 了

$ kubectl create -f svc.yml
service/k8s-demo-svc created

用下面的命令可以得到暴露出来的 URL,在浏览器里访问,就能看到我们之前创建的网页了

$ minikube service k8s-demo-svc --url
http://192.168.64.2:30050

六、横向扩展、滚动更新、版本回滚

1、横向扩展
在正式环境中我们需要让一个服务不受单个节点故障的影响,并且还要根据负载变化动态调整节点数量,所以不可能像上面一样逐个管理 pod。Kubernetes 的用户通常是用 Deployment 来管理服务的。一个 deployment 可以创建指定数量的 pod 部署到各个 node 上,并可完成更新、回滚等操作。
a、把刚才部署的 pod 删除(但是保留 service,下面还会用到)

$ kubectl delete pod k8s-demo
pod "k8s-demo" deleted

b、创建一个定义文件 deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-demo-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: k8s-demo
  template:
    metadata:
      labels:
        app: k8s-demo
    spec:
      containers:
        - name: k8s-demo-pod
          image: k8s-demo:0.1
          ports:
            - containerPort: 80

注意replicas: 3 指定了这个 deployment 要有 3 个 pod,后面的部分和之前的 pod 定义类似。

c、创建deployment
提交这个文件,创建一个 deployment

$ kubectl create -f deployment.yml
deployment.apps/k8s-demo-deployment created

用下面的命令可以看到这个 deployment 的副本集(replica set),有 3 个 pod 在运行

$ kubectl get rs
NAME                             DESIRED   CURRENT   READY   AGE
k8s-demo-deployment-7c4cf5fbbf   3         3         3       107s

查看 K8S 上所有命名空间下的 Pod,可以看到我们创建的3个pod节点

$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                   READY   STATUS    RESTARTS   AGE
default       k8s-demo-deployment-7c4cf5fbbf-bblgl   1/1     Running   0          3d2h
default       k8s-demo-deployment-7c4cf5fbbf-dk678   1/1     Running   0          3d2h
default       k8s-demo-deployment-7c4cf5fbbf-wcngc   1/1     Running   0          3d2h

2、滚动更新
假设我们对项目做了一些改动,要发布一个新版本。这里作为示例,我们只把 HTML 文件的内容改一下, 然后构建一个新版镜像 k8s-demo:0.2

echo '<h1>Hello Kubernetes! 0.2</h1>' > html/index.html
docker build -t k8s-demo:0.2 .

然后更新 deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-demo-deployment
spec:
  replicas: 3
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  selector:
    matchLabels:
      app: k8s-demo
  template:
    metadata:
      labels:
        app: k8s-demo
    spec:
      containers:
        - name: k8s-demo-pod
          image: k8s-demo:0.2
          ports:
            - containerPort: 80

这里有三个改动,
第一个是更新了镜像版本号 image: k8s-demo:0.2,
第二是增加了 minReadySeconds: 10
指在更新了一个 pod 后,需要在它进入正常状态后 10 秒再更新下一个 pod
第三是增加了strategy 部分。
maxUnavailable: 1 指同时处于不可用状态的 pod 不能超过一个;
maxSurge: 1 指多余的 pod 不能超过一个。
这样 Kubernetes 就会逐个替换 service 后面的 pod。
运行下面的命令开始更新,这里的 --record=true 让 Kubernetes 把这行命令记到发布历史中备查。

$ kubectl apply -f deployment.yml --record=true
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
deployment.apps/k8s-demo-deployment configured

查看各个 pod 的状态

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
k8s-demo-deployment-545967bfcb-l4pb4   1/1     Running   0          71s
k8s-demo-deployment-545967bfcb-lmcgq   1/1     Running   0          84s
k8s-demo-deployment-545967bfcb-t2r44   1/1     Running   0          84s

如果节点多一些,可以看到有一个节点是 Terminating 状态,
面的命令可以显示发布的实时状态

kubectl rollout status deployment k8s-demo-deployment

这时如果刷新浏览器,就可以看到更新的内容「Hello Kubernetes! 0.2」。

3、版本回滚
假设新版发布后,我们发现有严重的 bug,需要马上回滚到上个版本,可以用这个很简单的操作
Kubernetes 会按照既定的策略替换各个 pod,与发布新版本类似,只是这次是用老版本替换新版本

$ kubectl rollout undo deployment k8s-demo-deployment --to-revision=1
deployment.apps/k8s-demo-deployment rolled back

$ kubectl rollout status deployment k8s-demo-deployment
Waiting for deployment "k8s-demo-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "k8s-demo-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "k8s-demo-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "k8s-demo-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "k8s-demo-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "k8s-demo-deployment" rollout to finish: 2 of 3 updated replicas are available...
Waiting for deployment "k8s-demo-deployment" rollout to finish: 2 of 3 updated replicas are available...
deployment "k8s-demo-deployment" successfully rolled out

在回滚结束之后,刷新浏览器就可以确认网页内容又改回了「Hello Docker!」。

4、删除部署

$ kubectl get deploy
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
k8s-demo-deployment   3/3     3            3           4d23h

$ kubectl delete deploy k8s-demo-deployment
deployment.apps "k8s-demo-deployment" deleted

$ kubectl get deploy
No resources found.

七、Kubernetes架构和网络通信

Master 节点启动时,会运行一个 kube-apiserver 进程,它提供了集群管理的 API 接口,是集群内各个功能模块之间数据交互和通信的中心枢纽,并且它也提供了完备的集群安全机制。
在 Node 节点上,使用 K8S 中的 kubelet 组件,在每个 Node 节点上都会运行一个 kubelet 进程,它负责向 Master 汇报自身节点的运行情况,如 Node 节点的注册、终止、定时上报健康状况等,以及接收 Master 发出的命令,创建相应 Pod。
在 K8S 中,Pod 是最基本的操作单元,它与 docker 的容器有略微的不同,因为 Pod 可能包含一个或多个容器(可以是 docker 容器),这些内部的容器是共享网络资源的,即可以通过 localhost 进行相互访问。
关于 Pod 内是如何做到网络共享的,每个 Pod 启动,内部都会启动一个 pause 容器(google的一个镜像),它使用默认的网络模式,而其他容器的网络都设置给它,以此来完成网络的共享问题。


Kubernetes架构.png

参考文献
https://segmentfault.com/a/1190000010179260
https://www.jianshu.com/p/8b89a6f8ef87

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343