前端專案docker:前端docker入門

睡魔的謊言發表於2020-12-10

1. Docker

1.1 什麼是Docker

Docker是開源的,基於Linux容器技術的引擎,統一了被隔離的應用程式訪問系統核心的API。試圖解決開發者的世紀難題在我的機器上可以跑

前端同學可以視映象為npm包,倉庫為npm倉庫。這樣更方便理解。

1.2 為什麼使用Docker

Docker是一種類似虛擬機器技術的縮減版,由於虛擬機器啟動過程過於漫長與虛擬化之後的硬體在執行程式的時候,並不能很好的契合物理機,比較典型的例子就是移動端開發,啟動虛擬系統的時候,過程十分的漫長。

我們經常開啟一個虛擬機器僅僅是需要隔離一個應用,但是虛擬機器建立佔用了一套完整的系統資源(guest os),存在著大材小用的問題,成本也息息相關。

而Docker隨著Linux功能的更新出現了,Docker本質僅隔離應用程式,共享當前系統核心。

下圖為虛擬機器與Docker架構對比:

下圖為容器虛擬機器功能對比:

這樣的話,Docker就可以進行秒啟動,因為Docker跳過了系統初始化(kernel init),直接使用了當前系統核心。但是這個也是有弊病的,比如 虛擬機器熱遷移 這個功能,Docker就做的不是很好。

使用Docker可以快速的搭建配置應用環境,簡化操作,確保執行環境一致性“一次編譯 到處執行”,應用級隔離,彈性伸縮,快速擴充。

1.3 Docker基本概念

1.3.1 映象

映象是一個特殊的檔案系統,除了提供容器執行時所需的程式、庫、資源、配置等檔案外,還包含了一些為執行時準備的一些配置引數(如匿名卷、環境變數、使用者等)。 映象不包含任何動態資料,其內容在構建之後也不會被改變。

映象利用(union file system)提供應用執行的只讀模板,它可以只提供一個功能,也可以由多個映象疊加建立多個功能服務。

1.3.2 容器

映象僅僅是定義隔離應用執行所需要的東西,容器則是執行這些映象的程式。在容器內部,提供了完整的 檔案系統、網路、程式空間等等。完全隔離於外部環境,不會受到其他應用的侵擾。

容器的讀寫必須使用**Volume**,或者宿主的儲存環境,容器在重啟或者關閉之後,存在於執行容器內部的資料將會丟失,每次啟動容器,都是通過映象建立一個新的容器。

1.3.3 倉庫

Docker 倉庫是集中存放映象檔案的場所。映象構建完成後,可以很容易的在當前宿主上執行,但是, 如果需要在其它伺服器上使用這個映象,我們就需要一個集中的儲存、分發映象的服務,Docker Registry (倉庫註冊伺服器)就是這樣的服務。有時候會把倉庫 (Repository) 和倉庫註冊伺服器 (Registry) 混為一談,並不嚴格區分。Docker 倉庫的概念跟 Git 類似,註冊伺服器可以理解為 GitHub 這樣的託管服務。實際上,一個 Docker Registry 中可以包含多個倉庫 (Repository) ,每個倉庫可以包含多個標籤 (Tag),每個標籤對應著一個映象。所以說,映象倉庫是 Docker 用來集中存放映象檔案的地方類似於我們之前常用的程式碼倉庫。

通常,一個倉庫會包含同一個軟體不同版本的映象,而標籤就常用於對應該軟體的各個版本 。我們可以通過<倉庫名>:<標籤>的格式來指定具體是這個軟體哪個版本的映象。如果不給出標籤,將以 latest 作為預設標籤.。

倉庫又可以分為兩種形式:

  • public(公有倉庫)
  • private(私有倉庫)

1.3.4 Docker client

Docker client是一個泛稱,用來向指定的Docker Engine發起請求,執行相應的容器管理操作.它既可以是Docker命令列工具,也可以是任何遵循了Docker API的客戶端.目前, 社群中維護著的Docker client種類非常豐富,涵蓋了包括C#(支援 Windows)、Java、Go、Ruby、JavaScript等常用語言,甚至還有使用Angular庫編寫的WebU格式的客戶端,足以滿足大多數使用者的需求。

1.3.5 Docker Engine

Docker Engine是Docker最核心的後臺程式,它負責響應來自Docker client的請求,然後將這些請求翻譯成系統呼叫完成容器管理操作。該程式會在後臺啟動一個API Server,負責接收由Docker client傳送的請求;接收到的請求將通過Docker Engine內部的一個路由分發排程,再由具體的函式來執行請求。

2. 實戰Docker

##2.1 安裝Docker

本文所有環境均執行在centos7下。

首先,移除所有老版本的Docker。

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
複製程式碼

如果,是一個全新環境,那可以跳過這一步。

為國內一些原因,所以按照官網安裝docker-ce大概率是裝不上的所以,我們需要國內映象來加速安裝。下面我們通過aliyun加速安裝。

# step 1: 安裝必要的一些系統工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 新增軟體源資訊
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 更新並安裝 Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 開啟Docker服務
sudo service docker start
複製程式碼

安裝完成之後可以執行docker version檢查是否安裝成功。

➜  ~ docker version
Client: Docker Engine - Community
 Version:           19.03.3
 API version:       1.40
 Go version:        go1.12.10
 Git commit:        a872fc2f86
 Built:             Tue Oct  8 00:58:10 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.3
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.10
  Git commit:       a872fc2f86
  Built:            Tue Oct  8 00:56:46 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
複製程式碼

##2.2 獲取一個映象

現在我們需要拉取一個nginx映象,部署一個nginx應用。

➜  ~ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
68ced04f60ab: Pull complete 
28252775b295: Pull complete 
a616aa3b0bf2: Pull complete 
Digest: sha256:2539d4344dd18e1df02be842ffc435f8e1f699cfc55516e2cf2cb16b7a9aea0b
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
複製程式碼

拉取完成之後,使用docker image ls檢視當前docker本地映象列表。

➜  ~ docker image ls
REPOSITORY                      TAG                            IMAGE ID            CREATED             SIZE
nginx                           latest                         6678c7c2e56c        13 hours ago        127MB
複製程式碼

重新執行相同的命令docker pull nginx會更新本地映象。

##2.3 執行一個Docker容器

建立一個shell指令碼檔案,寫入以下內容:

docker run \
	# 指定容器停止後的重啟策略:
	#		no:容器退出時不重啟 
	#		on-failure:容器故障退出(返回值非零)時重啟
	#		always:容器退出時總是重啟
	--restart=always \
	# 指定docker執行在後臺,如果不加-d,在執行完這條命令之後
	# 你退出命令列也會將這個docker容器退掉
	-d \
	# 將宿主機埠號繫結至容器埠上
	-p 8080:80 \
	# 指定容器暴露的埠,即修改映象的暴露埠
	--expose=80  \
	# 對映宿主目錄至
	-v /wwwroot:/usr/share/nginx/html \
	# 指定容器名字,後續可以通過名字進行容器管理,links特性需要使用名字
	--name=testdocker \
	# 用哪個映象初始化這個容器
	nginx:lastest
複製程式碼

我們要明確一點docker的容器網路是與宿主機隔離的,除非指定容器網路模式依託宿主機,否則是無法直接訪問的。

現在我們執行這個指令碼,接著開啟瀏覽器,輸入http://ip:8080就可以看到使用nginx映象執行的應用程式了。

2.3.1 命令引數簡明版

Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]    
02.  
03.  -d, --detach=false         指定容器執行於前臺還是後臺,預設為false     
04.  -i, --interactive=false   開啟STDIN,用於控制檯互動    
05.  -t, --tty=false            分配tty裝置,該可以支援終端登入,預設為false    
06.  -u, --user=""              指定容器的使用者    
07.  -a, --attach=[]            登入容器(必須是以docker run -d啟動的容器)  
08.  -w, --workdir=""           指定容器的工作目錄   
09.  -c, --cpu-shares=0        設定容器CPU權重,在CPU共享場景使用    
10.  -e, --env=[]               指定環境變數,容器中可以使用該環境變數    
11.  -m, --memory=""            指定容器的記憶體上限    
12.  -P, --publish-all=false    指定容器暴露的埠    
13.  -p, --publish=[]           指定容器暴露的埠   
14.  -h, --hostname=""          指定容器的主機名    
15.  -v, --volume=[]            給容器掛載儲存卷,掛載到容器的某個目錄    
16.  --volumes-from=[]          給容器掛載其他容器上的卷,掛載到容器的某個目錄  
17.  --cap-add=[]               新增許可權,許可權清單詳見:http://linux.die.net/man/7/capabilities    
18.  --cap-drop=[]              刪除許可權,許可權清單詳見:http://linux.die.net/man/7/capabilities    
19.  --cidfile=""               執行容器後,在指定檔案中寫入容器PID值,一種典型的監控系統用法    
20.  --cpuset=""                設定容器可以使用哪些CPU,此引數可以用來容器獨佔CPU    
21.  --device=[]                新增主機裝置給容器,相當於裝置直通    
22.  --dns=[]                   指定容器的dns伺服器    
23.  --dns-search=[]            指定容器的dns搜尋域名,寫入到容器的/etc/resolv.conf檔案    
24.  --entrypoint=""            覆蓋image的入口點    
25.  --env-file=[]              指定環境變數檔案,檔案格式為每行一個環境變數    
26.  --expose=[]                指定容器暴露的埠,即修改映象的暴露埠    
27.  --link=[]                  指定容器間的關聯,使用其他容器的IP、env等資訊    
28.  --lxc-conf=[]              指定容器的配置檔案,只有在指定--exec-driver=lxc時使用    
29.  --name=""                  指定容器名字,後續可以通過名字進行容器管理,links特性需要使用名字    
30.  --net="bridge"             容器網路設定:  
31.                                bridge 使用docker daemon指定的網橋       
32.                                host    //容器使用主機的網路    
33.                                container:NAME_or_ID  >//使用其他容器的網路,共享IP和PORT等網路資源    
34.                                none 容器使用自己的網路(類似--net=bridge),但是不進行配置   
35.  --privileged=false         指定容器是否為特權容器,特權容器擁有所有的capabilities    
36.  --restart="no"             指定容器停止後的重啟策略:  
37.                                no:容器退出時不重啟    
38.                                on-failure:容器故障退出(返回值非零)時重啟   
39.                                always:容器退出時總是重啟    
40.  --rm=false                 指定容器停止後自動刪除容器(不支援以docker run -d啟動的容器)    
41.  --sig-proxy=true           設定由代理接受並處理訊號,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理    
複製程式碼

2.4 出入容器

我們可以使用docker exec -it [docker container id] /bin/bash 來進入正在執行的容器。

而要退出容器,有兩種方式:

  1. 直接在命令列輸入exit就退出
  2. 使用快捷鍵ctrl+P Q也會退出

以上兩種方式均可從容器中退出,並且保持容器在後臺執行。

##2.5 自定義一個映象Dockerfile

Dockerfile 分為四部分:基礎映象資訊、維護者資訊、映象操作指令和容器啟動時執行指令。

這裡我使用了一份簡單的node啟動開發環境的Dockerfile。

# 1. 設定來源的基礎映象
FROM node:12.0
# 指定後續 RUN、CMD、ENTRYPOINT 指令的工作目錄
WORKDIR /workspace
# 在上一次通過WORKDIR指定的目錄執行 RUN 後續命令
RUN npm install --registry=https://registry.npm.taobao.org
# 初始化暴露 8080 8001 8800埠號
# 以下埠號 也可以在docker run時暴露出去
EXPOSE 8080
EXPOSE 8001
EXPOSE 8800
# 預設執行的命令,如果在宿主機通過docker run -it /bin/bash進入時,以下命令不會被執行
# 完全不被覆蓋的指令為 ENTRYPOINT
CMD ["npm","run","dev-server"]
複製程式碼

儲存退出編輯,執行docker build -t nodeapp:v1.0 . 注意 最後一個.表示當前目錄。

在執行完成之後,使用docker image ls檢視是否有已經編譯完成的映象即可。

這個時候,可能有些人有個提問,就是每次都需要npm install安裝檔案嗎?

其實,如果你的node應用包不會變化,而你這個映象又是專為此應用開發的,可以考慮使用ADD指令,將node_modules追加至docker映象中。(現實基本不會如此處理,因為外部如果對映了資料捲進來會覆蓋目錄,此處只是為了演示像映象追加檔案。)

##2.6 多容器啟動:Docker-compose

Docker-compose 需要單獨安裝。

我們來假設一個場景,我們啟動了一個前端專案。需要啟動nginx執行前臺專案,啟動一個資料庫來記錄資料,保證整個應用的完整。那麼這就是docker-compose的用武之地了。

首先要知道,docker-compose由以下兩種型別組成:

  • 服務 (service):一個應用容器,實際上可以執行多個相同映象的例項。
  • 專案 (project):由一組關聯的應用容器組成的一個完整業務單元。

我們回到之前建立Dockerfile的目錄中,編寫一個docker-compose.yml檔案,配置多容器。

version: '1'
services:
  web:
    build: .
    ports:
     - "8080:80"
		volumes:
		 - /wwwroot:/usr/share/nginx/html
  redis:
    image: "redis:alpine"
複製程式碼

執行命令docker-compose up之後,我們通過docker stats可以看到兩個(web、redis)docker已經啟動了。

訪問http://ip:8080就可以看到跟之前一樣的網頁了。

2.7 網路

由於應有隔離,你無法在外網直接訪問到宿主機上的docker容器。所以我們需要將宿主機上的埠繫結到容器上。

2.3已經介紹瞭如何繫結埠與匯出容器埠。我們這裡瞭解一下容器互聯。

# 執行命令建立一個docker網路
$ docker network create -d bridge my-net
# 建立兩個容器 加入my-net網路
$ docker run -it --rm --name busybox1 --network my-net busybox sh
$ docker run -it --rm --name busybox2 --network my-net busybox sh
# 接著我們進入busybox1
$ docker exec -it busybox1 /bin/bash
# ping 另外一個容器,可以看到他的IP資訊
$ root@busybox1:ping busybox2
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms
複製程式碼

3. 擴充知識

3.1 Docker原理

Docker使用Go語言編寫,並且使用了一系列Linux核心提供的特性來實現其功能。

一個能執行Docker的系統分為兩大部分:

  • Linux的核心元件
  • Docker相關元件

Docker使用的Linux核心模組功能包括下列各項:

  • Cgroup – 用來分配硬體資源
  • Namespace – 用來隔離不同Container的執行空間
  • AUFS(chroot) – 用來建立不同Container的檔案系統
  • SELinux – 用來確保Container的網路的安全
  • Netlink – 用來讓不同Container之間的行程進行溝通
  • Netfilter – 建立Container埠為基礎的網路防火牆封包過濾
  • AppArmor – 保護Container的網路及執行安全
  • Linux Bridge – 讓不同Container或不同主機上的Container能溝通

3.2 mac window執行Docker原理

使用虛擬機器執行Linux,然後在Linux中執行Docker Engine。在本機執行Docker client。

3.3 不可使用 CMD ['node']啟動Docker

前端同學關注點

為什麼不能用CMD ['node','app.js']作為預設啟動,因為在 Node.js 的官方最佳實踐裡有寫到 "Node.js was not designed to run as PID 1 which leads to unexpected behaviour when running inside of Docker."。

 

這個問題涉及linux執行機制,簡單的說,就是 linux pid為1的程式是系統守護的程式,將會接收所有孤兒程式。並且在適當的時候傳送關閉訊號給這些程式。

但是,docker中pid 1的程式為node,而node並沒有做回收孤兒程式的事情。所以,如果你的應用跑類似爬蟲之類的應用,執行完畢之後將程式掛到pid 1 上,慢慢的容器就會BOOM。

解決方案:

1. 用`/bin/bash`啟動。

2. 在`docker run`後面追加`--init`用於初始化一個docker的程式為pid 1。docker提供的程式可以回收所有孤兒程式。

作者:愛創課堂前端技術分享
 

相關文章