1. 程式人生 > >kubernetes in action - Overview

kubernetes in action - Overview

傳統的應用都是“monoliths”,意思就是大應用,即所有邏輯和模組都耦合在一起的

這樣明顯很挺多問題的,比如只能scale up,升級必須整體升級,擴容

所以我們就想把大應用,broken down成小,獨立的模組或元件,這樣元件可以獨立的升級,擴容,元件也可以用不同的語言實現,元件間通過協議通訊,每個元件就是一個微服務

微服務技術可以說是傳統元件技術,如以前的COM,Corba,基於docker的演進,對於使用者透明硬體,實現語言,元件間通過Restful或RPC互動

 

問題在於,你把一個大應用拆成很多小應用,是不是很難維護和部署?

好,你在一臺物理機上部署這麼多的小應用,並且獨立升級,那麼他們的依賴如果衝突怎麼辦?

並且每個元件跑的環境可能也不一樣,你所有機器都需要把這些環境都裝上?想想就要爆炸

所以任何設計都有利弊,monoliths應用有他的問題,但拆成微服務也會引入一堆新的問題,

之所以現在微服務那麼火,當然當年COM和Corba也是紅極一時而後沉寂,是因為docker技術的出現,現有docker和相關框架可以比較好的解決這些問題,比如kubernetes

在這些框架的基礎下,我們可以做到持續開發,甚至Devops,Noops

這裡需要理解Devops,不是把ops團隊幹掉,然後把ops的工作都交給Dev,而是說在框架的支援下,原來的維護和釋出已經變的非常簡單,那麼Dev也可以更高效的去做,沒有必要讓ops做,而ops也可以解放出來提供更好的基礎框架,這是一種良性迴圈,否則張口閉口Devops只是一種惡性迴圈。

 

那麼容器技術有啥牛逼的,原來的VM為啥不行?關鍵點就在“輕量”上

因為微服務是需要把大應用拆分成很多小應用,而這些小應用都是混布在機器上,首先考慮的問題就是隔離,

當然如果都用VM,隔離是沒問題,但是太浪費資源了,都用VM可能比原來用一個應用消耗的資源大一個量級

所以,container技術,是一種輕量的VM

最大的差異是,每個VM都會跑一個獨立的OS,kernel,而container會共用host上的OS

那麼對於VM而言,因為OS都是獨立的,所以他的隔離是很直覺的

那麼container既然是共享OS,是用什麼技術做到隔離的?

linux container主要是用了下面兩種技術做到的容器隔離

首先,Linux namespaces,

Linux會預設用一個namespace,你也可以建立更多的namespace,namespace之間下面這些資源命名是隔離的,也就是說這個namespace看不到其他namespace的pid或userid等其他資源

 Mount (mnt)

 Process ID (pid)

 Network (net)

 Inter-process communication (ipc)

 UTS (host name,domain name)

 User ID (user)

再者,是CGroup,Linux kernel feature that limits the resource usage of a process (or a group of processes).

被限制的程序,使用的資源,cpu,memory,可以限制在規定的範圍內

Cgroup其實也不完美,比如對於IO,只能限制IOPS,無論是磁碟還是網路,很難去限制真正的流量;Cpu也無法針對突發流量,使用率在瞬間超限,然後再被限制,這樣很容易影響到其他程序,除非明確綁核;

 

容器技術很久之前就有,但是一直到docker技術出現後,才被大家廣泛的關注和接受;原因在於docker是“Portable”的,通過docker image

Docker was the first container system that made containers easily portable across different machines.
It simplified the process of packaging up not only the application but also all its libraries and other dependencies,
even the whole OS file system, into a simple, portable package that can be used to provision the application to any other machine running Docker

docker image把整個執行環境,包含OS file system都package,這樣哪怕docker內和宿主機是不同的os核心都沒關係,比如一個是centos,一個是debian

docker image本身也是從vm image借鑑過來的,但docker image會更輕量

並且docker image一個很優秀的設計是他是分層的,所以如果很多image都用到一個layer,這個layer只需要被下載一次

A big difference between Docker-based container images and VM images is that
container images are composed of layers, which can be shared and reused across multiple images.

所以要理解,docker技術的核心是package技術,而不是隔離,docker的隔離是通過linux核心features,namespaces和cgroup來保證的,docker本身不管隔離的事

所以通俗的講,docker就是一個打包和管理包的技術,就類似maven,只是他管理的不是一個java jar包,而是一個image

 

之前說過container和VM的差別,那麼現在再具體看下docker container和VM的差別,加深理解

從這個圖我們可以看出,

首先,之前說的區別,VM是需要自帶OS kernel的,並且VM是完全獨立的;docker共享宿主機的OS,並且需要一個docker程序來管理

再者,對於VM,如果應用A和B需要同一個執行環境,我們需要把他們放在一個VM中,但這樣他們之間是不隔離的;對於docker,A和B需要跑在獨立的容器內,但是還要共享執行環境

那麼docker是怎麼做的,關鍵就是docker image是分層的,docker可以基於同一層去啟動容器;但這裡的layer是read-only的,所以如果一個container改變了環境的話,他會增加一個新的layer,把所有的變更放在這個新的layer中

 

下面來說k8s,

我們有了docker,容器可以在各個機器上遷來遷去,那麼如果我有很多容器,和主機,怎麼管理他們,靠手工的遷移和管理肯定是不合適的

那麼kubernetes就做這個事的,他可以看成cluster的作業系統,提供類似,service discovery, scaling, load-balancing, self-healing, and even leader election等功能

Kubernetes的架構如下,

首先,kubernetes節點分為,master和worker

master,Control plane,包含API server,用於通訊,client和control plane,或多個control plane之間;Scheduler,顧名思義,負責排程應用到各個worker nodes;ETCD,類似zk,儲存配置,並保證一致性;Controller Manager,負責叢集級別的管理,監控worker nodes,節點failover等

woker node,首先要個Container Runtime,如docker程序來執行容器;Kubelet,用於和master通訊,並管理改woker上的所有容器;Kube-proxy,類似SLB,做服務訪問load balance的

下面通過一個例子來看下,使用者是如何通過kubernetes來提交應用的,

1. 使用者首先要把應用相關的docker image提交到image registry

2. 然後使用者需要寫,App descriptor,用於描述應用中各個container是如何組織的
這裡有個概念是,pods,可以理解成容器分組,在一個pods中,會被要求在一起執行,排程的時候也是按照一個整體呼叫,並且container之間也是不完全隔離的
所以在descriptor中,需要將container分成pods,並且給出每個pods的併發數

3. 接著就把應用提交給master,master會將各個pods排程到woker,通過woker上的kubelet,kubelet會讓節點上的docker runtime把容器啟動起來

4,docker runtime按照之前的步驟,先去image registry下載,然後啟動容器即可

 

下面開始action,Docker篇

1. 啟動docker

docker run <image> 

docker run <image>:<tag>

例子,執行busybox image,傳入引數 echo “hello world”

 

2. 建立docker image

首先,需要有一個要跑在docker中的程式,這裡用個js,

const http = require('http');
const os = require('os');
console.log("Kubia server starting...");
var handler = function(request, response) {
  console.log("Received request from " + request.connection.remoteAddress);
  response.writeHead(200);
  response.end("You've hit " + os.hostname() + "\n");
};
var www = http.createServer(handler);
www.listen(8080);

然後,需要寫個Dockerfile,

FROM node:7   #基於的image layer,“node” container image, tag 7

ADD app.js /app.js  #把app.js放到容器的root目錄

ENTRYPOINT ["node", "app.js"]  #容器啟動的時候執行那個命令,這裡是“node app.js”

最終呼叫,docker build建立image,

docker build -t kubia .

這裡再通過這個例子看下,image layer的分層,

可以看到,對於dockerfile中每一行命令,都會產生一個layer

You may think that each Dockerfile creates only a single new layer, but that’s not the case. When building an image, a new layer is created for each individual command

in the Dockerfile.

這個時候,我們可以檢視剛剛建立的image,

現在你可以用docker run,啟動這個容器,

docker run --name kubia-container -p 8080:8080 -d kubia

--name,容器名字
-p,Port 8080 on the local machine will be mapped to port 8080 inside the container,docker的埠是隔離的,所以你想從外面訪問,需要和宿主機的埠匹配上
-d,daemon,後臺程式;

容器啟動後,可以通過 http://localhost:8080 來訪問

 

3. 檢視容器

 docker ps   #檢視容器基本資訊

 docker inspect kubia-container  #檢視容器相關的所有資訊

 登入到容器內部,

 docker exec -it kubia-container bash 

-i, which makes sure STDIN is kept open. You need this for entering commands into the shell.

-t, which allocates a pseudo terminal (TTY).

這裡需注意,因為容器的程序其實是跑在宿主機的os上,所以容器核心宿主機上都可以看到這個容器程序,但是PID不一樣,docker內的PID是隔離的

 

4. 停止和刪除容器

docker stop kubia-container  #停止的容器,可以用docker ps -a檢視到

docker rm kubia-container   #刪除容器

 

5. 上傳和註冊容器image

經過上面的步驟,容器已經可以在local正常使用,但是如果要跨機器使用,需要把image註冊到docker hub上

先要給image加tag,因為docker hub只允許使用者上傳,以使用者docker hub id開頭的image

docker tag kubia luksa/kubia #如果dockerhub id是luksa

docker push luksa/kubia  #上傳

這樣你就可以在其他機器上,這樣啟動這個image

docker run -p 8080:8080 -d luksa/kubia

 

Kubernetes篇

叢集版本kubernetes按照比較麻煩,所以一般是用miniKube,先啟動miniKube

minikube start

然後,即可以用kubectl來連線kubernetes,kubectl就是一個client,用於連線kubernetes的APIServer

你可以看叢集情況,

也可以看到所有節點的情況,

看某一個節點,

kubectl describe node gke-kubia-85f6-node-0rr

現在開始部署應用到kubernetes,

$ kubectl run kubia --image=luksa/kubia --port=8080 --generator=run/v1

replicationcontroller "kubia" created

這裡看到啟動應用的時候,建立的是一個replicationcontroller,因為應用部署的時候需要多併發,所以需要rc來管理各個應用的replica

部署完後,我們怎麼看部署的應用,
對於kubernetes,應用的部署的粒度是pod,而不是container

A pod is a group of one or more tightly related containers that will always run together on the same worker node and in the same Linux namespace(s).

pod類似邏輯machine,pod內部的container不是完全隔離,是共用同一個linux namespaces的

container,pod,work node的關係如下,

那麼我們就可以看看pod的狀態,

詳細的資訊,

 

接著,如何把pod所提供的服務暴露給外部使用者?這裡就需要建立一個service,把rc暴露出來,因為rc管理的pod是動態的,臨時的,如果掛了,會拉起新的,但服務的ip不能老變,所以需要一個service做層proxy

kubectl expose rc kubia --type=LoadBalancer --name kubia-http

service "kubia-http" exposed

檢視services的狀態,

所以整個應用的元件圖如下,

 

kubernetes可以動態擴容的,下面可以看到擴容前後的情況