Docker是開發人員和系統管理員使用容器開發、部署和運行應用程序的平臺,使用Linux容器來部署應用程序稱爲集裝箱化,使用Docker能夠輕鬆部署應用程序。Docker 和傳統部署方式最大的不一樣在於,它將不會限制咱們使用任何工具,任何語言,任何版本的 runtime,Docker 將咱們的應用當作一個只提供網絡服務的盒子(也即容器),Kubernetes 則是對這些盒子進行更多自動化的操做,自動建立,自動重啓,自動擴容,自動調度,這個過程稱之爲容器編排。javascript
如今,容器編排技術給 Web 應用帶來了巨大的靈活性,讓咱們輕鬆建立須要的程序對外提供服務。和傳統的 IaaS 相比,不須要去關心雲主機申請,雲主機配置等信息,也不需考慮雲主機故障致使的服務不可用,由 Kubernetes 的副本控制器幫咱們完成雲主機故障發生後容器遷移。下面就讓咱們來看一下Docker和Kubernetes在前端應用開發的一些應用。html
和前端工具同樣,使用Docker容器以前,須要先安裝對應的工具。
Linux Debian/Ubuntu, 安裝 社區版DockerCE
Windows 一鍵安裝
macOS 一鍵安裝前端
須要說明的是,Windows7 系統,須要使用 VirtualBox 安裝 Linux 做爲 Docker 的宿主機,Windows10 Pro 會使用 Hyper-V 安裝 Linux 做爲 Docker 的宿主機。java
咱們可使用阿里雲的鏡像進行下載,而後再進行安裝。node
http://mirrors.aliyun.com/docker-toolbox/mac/docker-for-mac/
安裝完成後,啓動Docker便可。linux
在國內訪問默認的官方鏡像比較慢,咱們可使用阿里雲的鏡像加速,註冊阿里帳號並申請容器服務以後,而後點擊容器鏡像服務的鏡像加速地址查看地址,以下圖所示。
而後在Docker的Preferences中配置中添加加速地址,選擇Resources的Proxies填入阿里雲的鏡像加速地址,以下所示。nginx
w爲了方便使用和管理鏡像,咱們能夠點擊Docker官方地址來註冊Docker ID。註冊完成以後,就能夠在Mac版Docker桌面工具中進行登陸,並查看本身已有的鏡像,以下圖所示。git
打開終端,而後輸入命令docker
便可查看Docker支持的命令,以下所示。github
Usage: docker [OPTIONS] COMMAND A self-sufficient runtime for containers Options: --config string Location of client config files (default "/Users/crane/.docker") -c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use") -D, --debug Enable debug mode -H, --host list Daemon socket(s) to connect to -l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info") --tls Use TLS; implied by --tlsverify --tlscacert string Trust certs signed only by this CA (default "/Users/crane/.docker/ca.pem") --tlscert string Path to TLS certificate file (default "/Users/crane/.docker/cert.pem") --tlskey string Path to TLS key file (default "/Users/crane/.docker/key.pem") --tlsverify Use TLS and verify the remote -v, --version Print version information and quit Management Commands: app* Docker Application (Docker Inc., v0.8.0) builder Manage builds buildx* Build with BuildKit (Docker Inc., v0.3.1-tp-docker) checkpoint Manage checkpoints config Manage Docker configs container Manage containers context Manage contexts image Manage images manifest Manage Docker image manifests and manifest lists network Manage networks node Manage Swarm nodes plugin Manage plugins secret Manage Docker secrets service Manage services stack Manage Docker stacks swarm Manage Swarm system Manage Docker trust Manage trust on Docker images volume Manage volumes Commands: attach Attach local standard input, output, and error streams to a running container build Build an image from a Dockerfile commit Create a new image from a container's changes cp Copy files/folders between a container and the local filesystem create Create a new container deploy Deploy a new stack or update an existing stack diff Inspect changes to files or directories on a container's filesystem events Get real time events from the server exec Run a command in a running container export Export a container's filesystem as a tar archive history Show the history of an image images List images import Import the contents from a tarball to create a filesystem image info Display system-wide information inspect Return low-level information on Docker objects kill Kill one or more running containers load Load an image from a tar archive or STDIN login Log in to a Docker registry logout Log out from a Docker registry logs Fetch the logs of a container pause Pause all processes within one or more containers port List port mappings or a specific mapping for the container ps List containers pull Pull an image or a repository from a registry push Push an image or a repository to a registry rename Rename a container restart Restart one or more containers rm Remove one or more containers rmi Remove one or more images run Run a command in a new container save Save one or more images to a tar archive (streamed to STDOUT by default) search Search the Docker Hub for images start Start one or more stopped containers stats Display a live stream of container(s) resource usage statistics stop Stop one or more running containers tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE top Display the running processes of a container unpause Unpause all processes within one or more containers update Update configuration of one or more containers version Show the Docker version information wait Block until one or more containers stop, then print their exit codes Run 'docker COMMAND --help' for more information on a command.
關於這些命令的含義,能夠看Docker 經常使用命令web
好比,咱們要搜索nginx鏡像,那麼只須要使用下面的命令便可。
docker search nginx
搜索結果中標記【OFFICIAL】的爲官方鏡像,其餘爲用戶自定義鏡像,可根據實際須要選擇。
若是要獲取鏡像,可使用docker pull
命令,以下所示,獲取nginx。
# 拉取指定版本xxx鏡像 docker pull nginx:xxx # 拉取最新版本鏡像 docker pull nginx //等價於docker pull nginx:latest
鏡像拉取成功後,而後使用下面的命令啓動nginx容器,以下所示。
docker run -d -p 8080:80 --name docker-nginx nginx
上面的命令會將容器內部的80端口映射到了本機的8080端口,因此啓動成功後可使用http://localhost:8080/
訪問docker容器內部nginx80端口映射的地址,以下圖所示。
一些常見的啓動參數:
查看容器的基本命令以下所示。
# 查看運行中的容器 docker ps # 查看全部容器(包括正在運行和已經中止運行的) docker ps -a
中止容器命令使用的是kill命令,以下所示。
# 經過id直接關閉容器 # docker kill a0fbf4519279 # 經過容器名稱直接關閉容器 docker kill docker-nginx # 經過id直接容器 默認等待十秒 超時強制關閉 # docker stop a0fbf4519279 # 經過容器名稱關閉容器 默認等待十秒 超時強制關閉 等價於 docker stop -t=10 docker-nginx docker stop docker-nginx
從新啓動容器的命令以下。
# 啓動容器可經過容器id或者容器名稱 # 經過容器名稱啓動容器,若是已啓動則忽略 docker start docker-nginx # 經過容器名稱從新啓動容器,若是未啓動則直接啓動,若是已啓動則關閉再啓動 # docker restart docker-nginx
Docker 使用的是 C/S 結構,即客戶端/服務器體系結構。明白了 Docker 客戶端與 Docker 服務器進行交互時, Docker 服務端負責構建、運行和分發 Docker 鏡像。 而且Docker 客戶端和服務端能夠運行在一臺機器上,能夠經過 RESTful 、 stock 或網絡接口與遠程 Docker 服務端進行通訊。
而Docker 的底層核心原理是利用了 Linux 內核的 namespace 以及 cgroup 特性,其中 namespace 進行資源隔離,cgroup 進行資源配額, 其中 Linux 內核中一共有 6 種 namespace,分別對應以下。
在系統調用中有三個與namespace有關的函數,分別是clone、unshare和setns。
clone:若是想讓子進程擁有獨立的網絡地址,TCP/IP 協議棧,那麼就可使用clone,以下所示。參考:https://man7.org/linux/man-pages/man2/clone.2.html
clone(cb, *stack , CLONE_NEWNET, 0)
unshare:將當前進程轉移到新的 namespace 中, 例如使用 fork 或 vfork 建立的進程將默認共享父級資源,使用 unshare 將子進程從父級取消共享。參考:https://man7.org/linux/man-pages/man2/setns.2.html
setns:給指定的PID指定 namespace, 一般用於共享 namespace。參考:https://man7.org/linux/man-pages/man2/setns.2.html
Linux 的內核層支持了在系統調用中隔離 namespace, 經過給一個進程分配單獨的 namespace 從而讓其在各個資源維度進行隔離,每一個進程都能獲取到本身的主機名,IPC、 PID、IP和根文件系統、用戶組等信息,就像在一個獨佔系統中,不過雖然資源進行了隔離,可是內核仍是共享同一個,這也是比傳統虛擬機輕量的緣由之一。
另外只有資源進行隔離還不夠,要想保證真正的故障隔離,互不影響, 還須要對針對 CPU, 內存,GPU 等進行限制,由於若是一個程序出現死循環或者內存泄露也會致使別的程序沒法運行。
一個容器要想提供服務,就須要將自身的網絡暴露出去。Docker 是與宿主機上的環境是隔離的,要想暴露服務就須要顯示告訴 Docker 哪些端口容許外部訪問,在運行 docker run -p 80:80 nginx 時這裏就是將容器內部的 80 端口暴露到宿主機的 80 端口上,具體的端口轉發下面會具體分析一下。容器的網絡部分是容器中最重要的部分,也是構建大型集羣的基石,在咱們部署 Docker 的應用時,須要要對網絡有個基本的瞭解。
目前,Docker 提供了四種網絡模式,分別爲 Host、Container、 None和Bridge ,咱們可使用 --net 來進行指定網絡模式。
Host 模式不會單獨爲容器建立 network namespace, 容器內部直接使用宿主機網卡,此時容器內獲取 ip 爲宿主機 ip,端口綁定直接綁在宿主機網卡上,優勢是網絡傳輸時不用通過 NAT 轉換,效率更高速度更快。可是docker host上已經使用的端口就不能再用了,網絡的隔離性很差。
可使用以下的命令開啓Host模式。
docker run --net host nginx
Host模式的示意圖以下所示。
和指定的 container 共享 network namespace, 共享網絡配置,ip 地址和端口,其中沒法共享網絡模式爲 Host 的容器。新建立的容器不會建立本身的網卡,配置本身的 IP,而是和一個指定的容器共享 IP、端口範圍等。一樣,兩個容器除了網絡方面,其餘的如文件系統、進程列表等仍是隔離的,兩個容器的進程能夠經過 lo 網卡設備通訊。
開啓Host模式的命令以下。
docker run --net container:xxx_containerid nginx
使用none模式,Docker容器擁有本身的Network Namespace,可是,並不爲Docker容器進行任何網絡配置。也就是說,這個Docker容器沒有網卡、IP、路由等信息。須要咱們本身爲Docker容器添加網卡、配置IP等。指定爲 None 模式的容器內將不會分配網卡設備,僅有內部 lo 網絡。
啓動None 模式的命令以下所示。
docker run --net none busybox ifconfig
該模式爲默認模式,容器啓動時會被分配一個單獨的 network namespace,同時 Docker 在安裝/初始化時會在宿主機上建立一個名爲 docker0 的網橋,該網橋也做爲容器的默認網關,容器網絡會在該網關網段內進行 ip 的分配。
Bridge 模式的啓動命令以下所示。
docekr run --net bridge busybox ifconfig
Bridge模式的示意圖下圖所示。
而且,當咱們執行 docker run -p 3000:80 nginx
命令時,Docker 會在宿主機上建立下面一條 iptable 轉發規則。
在上圖中,當外部請求主機網卡 3000 端口時將它進行目的地址轉換(DNAT), 目的地址修改成 172.18.0.2,端口修改成 80,修改好目的地址後流量會從本機默認網卡通過 docker0 轉發到對應的容器,這樣當外部請求宿主機的 3000 端口,內部會將流量轉發給內部容器服務,從而實現服務的暴露,流程示意圖以下所示。
一樣 Docker 內部訪問外部接口也會進行源地址轉換(SNAT), 容器內部請求 google.com, 服務器上收到的則是主機網卡的 ip。
Bridge 模式因爲多了一層 NAT 轉換因此效率會比 Host 模式差一些,可是可以很好的隔離外部網絡環境,讓容器獨享 ip 且具備完整的端口空間。
上面四種網絡模式是 Docker 自帶的幾種工做方式,可是部署 Kubernetes 須要全部的容器都工做在一個局域網中,因此在部署集羣時須要多主機網絡插件的支持。
多主機網絡解決方案有 CNCF 推出的 CNI 規範以及 Docker 自帶的 CNM 方案,可是目前你們用的最多的仍是 CNI 規範,其中一種實現就是 Flannel。
Flannel 使用了報文嵌套技術來解決多主機網絡互通問題,將原始報文進行封包,指定包ip爲目的主機地址,等包到達主機後再進行拆包傳送到對應的容器。Flannel是 CoreOS 團隊針對 Kubernetes 設計的一個覆蓋網絡(Overlay Network)工具,其目的在於幫助每個使用 Kuberentes 的 CoreOS 主機擁有一個完整的子網。下圖顯示 flannel 使用效率更高的 UDP 協議來在主機間傳輸報文。
對上圖,咱們簡單的說明下:
目前主流跨主機通訊目前經常使用的有三種,分別是Overlay、hostgw和BGP技術。
hostgw: 經過修改主機路由表實現轉發,不須要拆包和封包,效率更高,但一樣限制比較多,只適合在相同局域網中的主機使用。
BGP:使用軟件實現的 BGP(邊界網關協議)以此向網絡中的路由器廣播路由規則。和 hostgw 同樣不須要拆包,可是實現成本較高。
在小規模場景下,使用 Docker 能夠一鍵部署應用確實很方便,可是當出現須要在幾百臺主機上進行多副本部署,須要管理這麼多主機的運行狀態以及服務的故障時須要在其餘主機重啓服務,想象一下就知道手動的方式不是一種可取的方案,這時候就須要利用 Kubernetes 這種更高維度的編排工具來管理了。
Kubernetes 簡稱 K8S,是Google 2014年建立管理的大規模容器管理技術。 簡單說 K8S 就是抽象了硬件資源,將 N 臺物理機或雲主機抽象成一個資源池,容器的調度交給 K8S 進行管理,CPU 不夠用就調度到一臺足夠使用的機器上,內存不知足要求就會尋找一臺有足夠內存的機器在上面建立對應的容器,服務由於某些緣由掛了, K8S 還會幫咱們自動遷移重啓。K8S是一個開源的平臺,實現了容器集羣的自動化部署、自動擴縮容、維護等功能。
Kubernetes 具備以下特色:
K8s集羣由兩節點組成:Master和Node。在Master上運行etcd,Api Server,Controller Manager和Scheduler四個組件。後三個組件構成了K8s的總控中心,負責對集羣中全部資源進行管控和調度。在每一個Node上運行kubect、proxy和docker daemon三個組件,負責對節點上的Pod的生命週期進行管理,以及實現服務代理的功能。另外全部節點上均可以運行kubectl命令行工具。
API Server做爲集羣的核心,負責集羣各功能模塊之間的通訊。集羣內的功能模塊經過Api Server將信息存入到etcd,其餘模塊經過Api Server讀取這些信息,從而實現模塊之間的信息交互。Node節點上的Kubelet每隔一個時間週期,經過Api Server報告自身狀態,Api Server接收到這些信息後,將節點信息保存到etcd中。Controller Manager中 的Node controller經過Api server按期讀取這些節點狀態信息,並作響應處理。Scheduler監聽到某個Pod建立的信息後,檢索全部符合該pod要求的節點列表,並將pod綁定到節點列表中最符合要求的節點上。若是scheduler監聽到某個Pod被刪除,則調用api server刪除該Pod資源對象。kubelet監聽pod信息,若是監聽到pod對象被刪除,則刪除本節點上的相應的pod實例,若是監聽到修改Pod信息,則會相應地修改本節點的Pod實例。
Kubernetes主要由如下幾個核心組件組成:
在Mac中安裝了Docker以後,會自動安裝了Kubernetes,正常狀況下,咱們只須要在Docker的Preferrences->Kubernetes中勾選Enable Kubernetes,而後點擊Apply按鈕便可。
可是因爲【牆】的問題,若是您是直接在Docker中啓用Kubernetes,Kubernetes的狀態會一直都是kubernetes is starting...,緣由是有一些Kubernetes依賴的鏡像不能正常的下載。
Github上有個開源項目能夠幫咱們手動拉取鏡像,執行下面命令拉去改項目代碼到本地。
https://github.com/xiangzhihong/k8s-docker-desktop-for-mac cd k8s-docker-desktop-for-mac sh load_images.sh
而後,執行下面的命令驗證是否安裝成功。
kubectl cluster-info
IaaS 就是 Infrastructure as a service, 所謂基礎設施即服務,開發者想要上線一個新應用須要申請主機,ip, 域名等一系列資源,而後登陸主機自行搭建所需環境,部署應用上線,這樣不只不利於大規模操做,並且還增長了出錯的可能,運維或開發這經常本身寫腳本自動化完成,遇到一些差別再手動修改腳本,很是痛苦。
K8S 則是將基礎設施可編程化,由原來的人工申請改成一個清單文件自動建立,開發者只須要提交一份文件,K8S 將會自動爲你分配建立所需的資源。對這些設施的 CRUD 均可以經過程序的方式自動化操做。
爲了瞭解 K8S 的基礎知識,下面來部署一個 Node SSR 應用。首先,初始化一個應用模版。
npm install create-next-app npx create-next-app next-app cd next-app
建立好工程後給添加一個 Dockerfile 用來構建服務的鏡像Dockerfile。
FROM node:8.16.1-slim as build COPY ./ /app WORKDIR /app RUN npm install RUN npm run build RUN rm -rf .git FROM node:8.16.1-slim COPY --from=build /app / EXPOSE 3000 WORKDIR /app CMD ["npm", "start"]
Dockerfile 作了兩部分優化:
而後使用下面的命令構建鏡像。
docker build . --tag next-app
以後咱們就能夠向 Kubernetes 提出咱們應用的要求了。爲了保證高可用,服務至少建立兩個副本,咱們還須要一個應用的域名當這個域名請求到咱們集羣上時自動轉發到咱們的服務上。那麼咱們對應的配置文件Deployment.yaml就能夠這麼寫。
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: app-ingress spec: rules: - host: next-app-server http: paths: - backend: serviceName: app-service servicePort: 80 --- kind: Service apiVersion: v1 metadata: name: app-service spec: selector: app: web ports: - port: 80 targetPort: 3000 --- apiVersion: apps/v1 kind: Deployment metadata: name: app-deployment spec: replicas: 2 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: next-app name: next-app imagePullPolicy: IfNotPresent ports: - containerPort: 3000
上面清單主要的功能是:
而後,執行下面的命令提交申請給 K8S。
kubectl apply -f ./Deployment.yaml
接着就能夠看到已經部署的 pod。
sh-4.4$ kubectl get pod NAME READY STATUS RESTARTS AGE app-deployment-594c48dbdb-4f4cg 1/1 Running 0 1m app-deployment-594c48dbdb-snj54 1/1 Running 0 1m
而後瀏覽器打開 Ingress 裏配置的域名便可訪問對應的應用(前提是這個域名可以打到你的 K8S 集羣節點上),以下圖所示。
K8S 僅僅負責容器的編排,實際上若是部署應用還須要外部 Pipeline 的支持,代碼的構建,靜態檢查,鏡像的打包由 Pipeline 完成。目前,國內用的比較多的發佈系統經常由下面幾個服務組成:GitLab/GitHub、Jenkins,、Sonar,、Harbor等。