傳統的應用都是「monoliths」,意思就是大應用,即全部邏輯和模塊都耦合在一塊兒的java
這樣明顯很挺多問題的,好比只能scale up,升級必須總體升級,擴容node
因此咱們就想把大應用,broken down成小,獨立的模塊或組件,這樣組件能夠獨立的升級,擴容,組件也能夠用不一樣的語言實現,組件間經過協議通訊,每一個組件就是一個微服務linux
微服務技術能夠說是傳統組件技術,如之前的COM,Corba,基於docker的演進,對於用戶透明硬件,實現語言,組件間經過Restful或RPC交互docker
問題在於,你把一個大應用拆成不少小應用,是否是很難維護和部署?shell
好,你在一臺物理機上部署這麼多的小應用,而且獨立升級,那麼他們的依賴若是衝突怎麼辦?centos
而且每一個組件跑的環境可能也不同,你全部機器都須要把這些環境都裝上?想一想就要爆炸bash
因此任何設計都有利弊,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能夠動態擴容的,下面能夠看到擴容先後的狀況