一文看懂-Docker容器化
- 2019 年 10 月 3 日
- 筆記
一、Docker簡介
1.1 什麼是docker
docker的英文意思是 碼頭工人,意思就是搬運東西的意思,其實這和docker的特點是一樣的,docker提供的就是一種容器化搬運東西(我們的軟件、程序)的過程。docker自己本來是運行在操作系統上一個程序軟件,它會提供一個容器環境,使我們的程序獨立地運行在容器中,所以說,官方給docker起的這個名字也真是應景。
就連圖標也是這麼生動形象,富有詩意,讓人浮想聯翩。。。。(這是去幼兒園的車,還沒有拐進大學城)
試想下邊這樣一個場景:當我們把我們的web網站做成分佈式的時候,我們就要加服務器,然後在各個服務器配置web所需要的配置,比如:數據庫、web服務器、運行時啥,這樣的我們的網站才能跑起來,但是每當我們加服務器的時候,我們都要再重新配置一下,很繁瑣,有了docker,我們就可以把我們的網站和所需要的環境配置好,打成一個包(docker鏡像),然後在服務器上安裝docker,用docker拉取打包好的鏡像,直接run(容器)起來就行了,什麼都不用管了,很方便,更加的便於管理,鏡像中修改配置,重新更新,所有的容器就也能修改了,我們的網站也就修改更新了,特別的方便。
1.2 docker的特點
docker的特點包含在它的口號中
第一句,是“Build, Ship and Run”。(搭建、運輸、運行)
第二句,“Build once,Run anywhere(一次搭建,到處運行)”。
看了上邊的場景,我們可以理解這兩句話了,現在哪個軟件不是一次搭建、到處運行,真的都不好意思說,從最開始的java,到現在的各種跨平台前端框架(Flutter、ReactNative、Ionic啥的)、小程序框架(Taro),這是解放開發人員減少開發成本提高開發效率的三連擊造福碼農的事情啊,各種應用層出不窮,叫我們情何以堪、不堪回首、首當其衝、衝鋒陷陣啊,一不小心就暴露了我的文化。
1.3 docker和虛擬機
一說到docker,相信大家之前也了解過,那就必須要和虛擬機做一下比較,其實docker是和虛擬機是類似的東西,我們應該知道虛擬機就是在我們的操作系統上虛擬出來一個電腦,然后里邊可以安裝、運行各種各樣的軟件,和我們真的電腦是差不多的,我們可以拿着這個虛擬好的電腦(其實是一個文件)在按了虛擬機的其他電腦上可以直接運行,裡邊的東西就不用我們來回安裝和配置了,也是很方便的。
docker其實提供的也是這麼一種的技術,只不過它比虛擬機效率更加的高,啟動快,佔用資源小等一系列的優點,而且虛擬機比較笨重,這是因為虛擬機和docker來實現思想上有本質的區別,我們可以通過下邊的兩張圖可以對比一下:
虛擬機的運作原理:是虛擬電腦的硬件資源,把硬件資源分配出來,然後虛擬出來多個操作系統,虛擬出來的是一個完整的電腦。
docker的運作原理:虛擬的軟件資源,把電腦中的網絡、存儲啥的分成幾份虛擬成容器,我們的軟件運行在容器中,每個容易只佔用電腦的部分所需要的資源,並不是一個完整的電腦。
大概就像上邊那麼理解吧,反正也不知道是對不對,但是看起來應該不錯。
所以,從運作原理上來看,docker更加的輕量級,虛擬機更加的笨重,docker啟動也十分的快,部署起來也方便,所以越來越多的人開始使用docker起來。
下邊是一張虛擬機和docker的對比,這裡要注意一下:但是docker本身並不是容器,而是創建容器的工具;而虛擬機它就是虛擬機了。
二、docker中的核心概念
下面我們來介紹使用docker的過程中必須要掌握的概念,理解這些概念對docker的使用和學習是非常必要的。
2.1 鏡像(Image)
鏡像到底是個什麼東西呢,很多人在學習docker的時候都是一頭霧水的,可是是歪果仁對鏡像情有獨鍾吧,好多東西都有鏡像的概念。比如我們安裝系統的.iso文件,其實就是鏡像,這裡你就可以把鏡像認為是一種模板。我們可以使用docker根據這個模板創建容器來運行,其實更可以理解為鏡像是好比github上的倉庫一樣,我們可以克隆下來源代碼然後運行,運行起來的代碼可以是一個網站、一個應用程序啥的,這就可以叫做容器。說白了,鏡像就是一堆靜態的模板,運行起來的鏡像就是容器。鏡像一般需要我們拉取下來,是只讀的,這個我們克隆github上的倉庫是一樣一樣的。
docker鏡像中有分層的概念,就是一個鏡像可能基於好幾個鏡像,比如一個web運行環境可能需要操作系統ubuntu、數據庫mysql、.net core runtime運行時,那我們拉取的這個鏡像就會包好這好幾個鏡像,這就好像我們前邊說的打包好的運行環境一樣,直接就拉下來一個小電腦一樣。
2.2 容器(Container)
當我們拉取了一個鏡像,然後run一下,就會根據這個鏡像運行出來一個容器,運行的容器就好像我們的應用程序一樣,可以訪問可以停止,我們運用多次run命令,就運行了很多很多容器,也可以說是鏡像的實例。從這個角度來看,我們可以把鏡像看作是類,容器看作new出來的實例,也是很合適的。
2.3 倉庫(Repository)
存放鏡像的地方就是倉庫,就好比存放代碼的地方是github一樣,我們就把github稱為代碼的倉庫,github算是最大的倉庫。那麼存放docker鏡像的地方我們叫做dockerhub,是docker的中央倉庫。其實已經有dockerhub這個網站了(https://hub.docker.com/),這就是 存放docker鏡像的官方倉庫,好多官方的也保存在這裡,保證了鏡像的安全性和可靠性,我們可以從上邊拉取一下鏡像來運行我們的軟件。當然我們也可以製作好我們自己鏡像推送上去,不過這些肯定是要官方審核的,防止有些人寫入一些惡意代碼。不過我們可以推到我們自己的dockerhub上去,供我們自己使用,這個就好我們的github賬號一樣了,屬於私有鏡像了。
2.4 數據卷(Volumn)
實際上我們的容器就好像是一個簡易版的操作系統,只不過系統中只安裝了我們的程序運行所需要的環境,前邊說到我們的容器是new出來的實例,既然是new出來的實例那就會銷毀,那如果銷毀了我們的程序產生出的需要持久化的數據怎麼辦呢,容器運行的時候我們可以進容器去查看,容器一旦銷毀就什麼都沒有了。所以數據卷就是來解決這個問題的,是用來做數據持久化到我們的宿主機上容器間的數據共享,簡單的說就是將宿主機的目錄映射到容器中的目錄,應用程序在容器中的目錄讀寫數據會同步到宿主機上,這樣容器產生的數據就可以持久化了,比如我們的數據庫容器,就可以把數據存到我們宿主機上的真實磁盤上了。
docker中基本的概念已經說了,下面我們就可以來使用docker了。
三、docker的安裝與使用
由於docker的服務端只能運行在linux操作系統上,當然我們學習也可以用windows來安裝docker,但是比較麻煩,需要開啟一系列的配置,我們還是暫時在一台裝有虛擬機的linux系統上來學習docker。這裡我們以linux ubuntu 16.04 版本來學習。
3.1 docker的安裝
我們先執行命令安裝docker
1. 執行 sudo apt-get update 更新軟件包
2. 執行 sudo apt-get -y install docker.io 安裝docker
3. 輸入 docker version 檢查docker是否安裝成功
如果有下面的輸出,說明docker安裝成功
3.2 docker的使用
docker –help 查看幫助信息,一般的軟件都會有這個命令,不再多說。
docker version 查看docker的版本。
docker info 查看docker的基本信息,有多少個容器、鏡像什麼的。
docker images 查看本機上的所有鏡像。
REPOSITORY:倉庫,也是鏡像名稱。
TAG:標籤,也是版本號,鏡像會有不同的版本號。
IMAGE ID:鏡像id,根據這個id我們可以區分不同的鏡像,也可以對某個鏡像進行操作。
CREATED:創建時間。
SIZE:鏡像的大小。
當前我的機器上沒有一個鏡像,顯示如下:
docker pull <鏡像名稱>:[標籤名稱]:拉取鏡像,默認不寫標籤名稱拉取最新的鏡像。
我們輸入docker pull hello-world 拉取最新的hello-world鏡像
docker run <鏡像名稱> :運行一個鏡像,這時候就變成一個容器了,相當於new 一個Image了。下邊我們run hello-world 運行這個鏡像,輸出以下信息:
至此,我們的docker的hello-world已經運行成功了,接下來我們來啟動一個nginx服務器,來從外邊訪問我們容器里的nginx看看。
同樣我們先拉取nginx鏡像 docker pull nginx,然後等待拉取完成,運行nginx容器,docker run -p 8080:80 nginx
然後我們用自帶的瀏覽器來訪問8080端口,出現welcom to nginx 表示啟動成功。
docker的使用方法基本就是上邊這樣了,接下來我們就可以看一下詳細的參數。
3.3 docker的命令
3.3.1 鏡像命令:
docker images 查看本機的鏡像。
REPOSITORY:倉庫,也是鏡像名稱。
TAG:標籤,也是版本號,鏡像會有不同的版本號。
IMAGE ID:鏡像id,根據這個id我們可以區分不同的鏡像,也可以對某個鏡像進行操作。
CREATED:創建時間。
SIZE:鏡像的大小。
docker rmi 刪除本地的鏡像,加上一個參數-f表示強制刪除,因為有時候若有運行的相關容器的時候是不能刪除的,如:docker rmi -f nginx 強行停止容器並刪除鏡像,不管是否有佔用情況。
docker search 根據鏡像名稱搜索遠程倉庫中的鏡像,可以看一下查到所有相關名稱的鏡像,可以選擇我們要拉取哪個鏡像,下邊是搜索nignx相關的鏡像,紅色部分ok 說明是官方鏡像。
docker pull <鏡像名稱>:[標籤名稱]:拉取鏡像,默認不寫標籤名稱拉取最新的鏡像。
docker push 推送鏡像,當我們製作了我們自己的鏡像時,我們就可以推送到我們自己的docker hub上去。
3.3.2 容器命令
有了鏡像我們就可以new一個鏡像實例了,也就是我們所說的容器。
docker run :基於某個鏡像運行一個容器,如果本地有這個鏡像就根據本地的鏡像創建,如果沒有,就去遠程拉取一個鏡像再創建,參數如下:
-d:啟動一個容器,後台運行,不會佔用我們當前的控制台,一般都要加上,之前我們啟動nginx沒有指定這個參數,就會佔用當前控制台,會一直掛起,有了這個命令就不會佔用了。
-i:以交互模式運行容器,通常會和-t一起來使用(-it)。
-t:為容器也創建一個命令行窗口,是容器內容的命令行窗口,比如我們拉取一個ubuntu的鏡像,我們想要在這個操作系統鏡像裡邊執行一些命令,那就需要這個參數了。
-P:這個是大寫的P,指定宿主機的隨機端口映射到容器內部的端口。
-p:這個是小寫的p,指定某個具體端口映射到容器內部端口,比如前邊我們用-p 8080:80,就是讓宿主機的8080端口映射到容器內的80端口,這樣我們就可以在外部用8080端口訪問我們容器內部的nginx了(默認容器必須有一個外部的映射端口,不然訪問不了)。
-v:指定宿主機與容器內部的目錄映射,就是之前的數據卷所需要的參數,好實現數據的持久化和同步。
–name=”mynginx”:為容器指定一個名稱,如果沒有指定,那就分配一個隨機名稱。
下面用參數重啟啟動我們的nginx鏡像:docker run -itd -p 8848:80 –name=”mynginx” nginx
docker ps 顯示正在運行的容器,加一個參數-a 可以看到停止中的容器
docker stop 停止容器 。
docker kill 強制停止容器。
docker restart 重啟容器。
docker rm 刪除容器,刪除後容器就不在了,就不能重啟和停止了。
docker inspect 查看容器的詳細信息。
以上就是差不多我們常用的命令,具體的其他更多的功能我們可以查看官方文檔 https://docs.docker.com/get-started/。
四、Dockerfile
上邊都是我們拉取別人的鏡像,我們實際上也可以製作自己的專屬鏡像,根據我們的需要,配置好我們自己的鏡像來使用。Dockerfile就是用來構建我們的鏡像的文件,在裡邊可以寫一些命令來構建我們的鏡像,構建好後發佈到docker hub就可以供別人拉取使用了。
Dockerfile是一個沒有後綴名的文本文件,我們通過寫入一些命令來實現鏡像的構建。下面我們先來看看Dockerfile是怎樣來編寫的。下邊的命令標識形式<> 代表需要的參數,[ ] 是可選參數。
FROM:指定基礎鏡像,所有構建的鏡像都必須有一個基礎鏡像,且FROM命令必須是Dockerfile的第一個命令。
FROM <image> [AS <name>] 指定從一個鏡像構建 AS 起一個新的鏡像名字。
FROM <image>[:<tag>] [AS <name>] 指定鏡像的版本tag。
例如:FROM mysql:5.0 AS database
MAINTAINER:鏡像維護人的信息。
MAINTAINER <name>
例如:MAINTAINER haha [email protected]
RUN:構建鏡像時要執行的命令。
RUN <command>
例如:RUN [“executable”, “param1”, “param2”] RUN [“dotnet restore”,”*.csproj”]
ADD:將本地的文件添加複製到容器中去,壓縮包會解壓,可以訪問網絡上的文件,會自動下載。
ADD <src> <dest>
例如:ADD *.csproj /app 添加csproj文件到容器中的app目錄下。
COPY:功能和ADD一樣,但是只是複製,不會解壓或者下載文件。
CMD:啟動容器後執行的命令,和RUN不一樣,RUN是在構建鏡像是要運行的命令。當使用docker run運行容器的時候,這個可以在命令行被覆蓋。
CMD [“executable”, “param1”, “param2”]
ENTRYPOINT:也是執行命令,和CMD一樣,只是這個命令不會被命令行覆蓋。
ENTRYPOINT [“executable”, “param1”, “param2”]
例如:ENTRYPOINT [“donnet”, “myapp.dll”]
LABEL:為鏡像添加元數據,key-value形式的。
LABEL <key>=<value> <key>=<value> <key>=<value> ….
例如:LABEL version=”1.0″ description=”這是一個web應用”
ENV:設置環境變量,有些容器運行時會需要某些環境變量 比如:JAVA_HOME。
ENV <key> <value> 一次設置一個環境變量。
ENV <key>=<value> <key>=<value> <key>=<value> …. 設置多個環境變量。
例如:ENV JAVA_HOME /usr/java1.8/
EXPOSE:暴露對外的端口。
EXPOSE <port>
例如:EXPOSE 80
這裡指的是容器內部程序的端口,雖然會和宿主機的一樣,但是其實是兩個端口,容器運行時,需要用-p 映射外部端口才能訪問到容器內的端口。
VOLUME:指定數據持久化的目錄,官方語言叫做掛載。
VOLUME /var/log 指定容器中需要被掛載的目錄,會把這個目錄映射到宿主機的一個隨機目錄上,實現數據的持久化和同步。
VOLUME [“/var/log”,”/var/test”…..] 指定容器中多個需要被掛載的目錄,會把這些目錄映射到宿主機的多個隨機目錄上,實現數據的持久化和同步。
VOLUME /var/data var/log 指定容器中的var/log目錄掛載到宿主機上的/var/data目錄,這種形式可以手動指定宿主機上的目錄。
WORKDIR:設置工作目錄,設置完工作目錄之後 ,上邊的RUN、CMD、COPY、ADD等的工作目錄都變成這個了。
WORKDIR <path>
例如:WORKDIR /app/test
USER:指定運行命令時所使用的用戶,為了安全和權限起見,有的用戶可能權限高,有的用戶可能權限低,根據要執行的命令選擇不同的用戶。
USER <user>:[<group>]
例如:USER test
ARG:設置構建鏡像是要傳遞的參數。
ARG <name>[=<value>]
例如:ARG name=sss
以上是差不多構建Dockerfile時所使用的命令的,構建時命令是從上到下順序執行的,可能後邊的命令需要前邊命令的結果,使用 docker build 編譯Dockerfile 就可以構建我們的鏡像了。
docker build 可以使用參數 -f 指定Dockerfile的目錄,默認是在當前目錄下找;-t 指定構建的鏡像的名稱和標籤,例如:docker build -f ./aa/bb -t myimage:1.0
五、構建我們自己的鏡像
有了上面的基礎,我們就可以構建我們自己的鏡像了,然後還可以上傳到我們自己的docker hub上供別人拉取使用。接下里我們就來實現以下。這裡參考了官方的構建步驟:https://docs.docker.com/engine/examples/dotnetcore/
5.1 創建一個mvc項目
首先找一個目錄,創建一個文件夾DockerDemo,然後在該目錄下執行命令dotnet new mvc,創建我們的mvc程序,如下:
5.2 添加Dockerfile文件
我們直接添加一個名稱為Dockerfile的文件,然後輸入一下命令:
#構建sdk鏡像,是為了進行編譯、發佈我們的web應用 FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env # 在容器中設置一個/app目錄 WORKDIR /app # 複製csproj文件 到當前目錄下(app目錄下) 並執行dotnet restore 還原包 COPY *.csproj ./ RUN dotnet restore # 再複製其他剩餘的文件到當前目錄,執行發佈命令,用Release模式 並且輸出到out文件夾 COPY . ./ RUN dotnet publish -c Release -o out #構建運行時鏡像 FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 # 設置運行時鏡像一個/app目錄 WORKDIR /app # 複製之前的鏡像的產物中的 /app/out目錄下的文件 到當前新鏡像的 /app錄下(當前目錄) COPY --from=build-env /app/out . # 執行dotnet 命令,運行我們的web應用 ENTRYPOINT ["dotnet", "DockerDemo.dll"]
我用vscode添加Dockerfile直接變成了一個鯨魚的圖標,說明是一個Docker文件,還有高亮:
5.3 構建Docker鏡像
我們直接把DockerDemo這個文件夾直接複製到虛擬機中,我複製到了Desktop下:
然後進入到DockerDemo目錄下,直接執行命令 docker build -t dockerdemo . ,這句命令的意思是 使用當前目錄下的Dockerfile構建鏡像dockerdemo,注意構建的鏡像名稱必須為小寫,否則會報錯。
然後就是等待構建了,可能會比較慢, 如果想快的話,可以換成國內的鏡像源,這裡就不說了,因為我也懶得換。
這次構建過程會分為兩步,因為有兩個鏡像,構建完成後,我們使用docker images 查看,會發現有我們新構建的鏡像dockerdemo:
5.4 運行容器
接下來我們輸入命令 docker run -d -p 8848:80 –name myapp dockerdemo,運行一個容器,名稱叫做myapp
使用docker ps 查看運行中的容器:
5.5 訪問我們的mvc程序
在虛擬機的瀏覽器中輸入 http://localhost:8848,或者在你的電腦上輸入虛擬機的地址來訪問 http://192.168.226.130:8848/ (這個是我的地址),顯示如下,表名成功:
六、發佈鏡像
我們已經構建好了我們的鏡像,接下來我們就把鏡像推送到我們自己的docker倉庫中去,這一步和github是完全一樣的,就像把我們的代碼推送到github上一樣。
6.1 創建賬號
首先,我們需要創建一個dockerhub賬號,登錄https://hub.docker.com/,根據提示註冊賬號,這一步就不細說了。
6.2 登錄
命令行輸入docker login,輸入我們的賬號密碼,進行登錄。
6.3 推送鏡像
推送鏡像之前,首先要標記我們的鏡像,也就是給我們的鏡像起一個名字,使用命令 docker tag <image> <username>/<repository>:<tag>,下邊我們就標記我們剛才的dockerdemo的鏡像:
使用命令 docker tag dockerdemo emmaccc/dockerdemo:1.0.0
查看我們剛才標記過的鏡像:
使用命令 docker push emmaccc/dockerdemo:1.0.0 推送鏡像,等待一段時間後推送成功,就可以在自己dockerhub賬號上看見了
6.4 使用鏡像
推送成功之後,我們就可以在任意一台安裝了docker的計算機上來拉取我們鏡像,直接run啟動我們的web應用了。
docker run -p 4396:80 emmaccc/dockerdemo:1.0.0,運行之後,無需擔心各種依賴、配置,也不用在你的新主機上安裝任何東西,直接run運行就行了,這也docker獨特的魅力所在。
六、總結
學習docker,前前後後看了好幾遍,究竟是個什麼玩意啊,好半天才看懂,docker的基本使用就差不多了,接下來估計又是k8s了,唉,東西真多,感覺全it界都在造輪子吧,非要搞這個搞那個,沒辦法,還是得跟着大佬們走,如果你真的還不明白,那就送你一句話。
web放tomcat里,tomcat放docker里,docker放k8s里,k8s放操作系統上,繞了一大圈,扯淡呢,咋不直接web放操作系統上直接運行呢。