原文地址:樑桂釗的部落格
部落格地址:blog.720ui.com
歡迎關注公眾號:「服務端思維」。一群同頻者,一起成長,一起精進,打破認知的侷限性。
30 分鐘快速入門 Docker 教程
一、歡迎來到 Docker 世界
1. Docker 與虛擬化
在沒有 Docker 的時代,我們會使用硬體虛擬化(虛擬機器)以提供隔離。這裡,虛擬機器通過在作業系統上建立了一箇中間虛擬軟體層 Hypervisor ,並利用物理機器的資源虛擬出多個虛擬硬體環境來共享宿主機的資源,其中的應用執行在虛擬機器核心上。但是,虛擬機器對硬體的利用率存在瓶頸,因為虛擬機器很難根據當前業務量動態調整其佔用的硬體資源,因此容器化技術得以流行。其中,Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後釋出到任何流行的 Linux 機器上。
Docker 容器不使用硬體虛擬化,它的守護程式是宿主機上的一個程式,換句話說,應用直接執行在宿主機核心上。因為容器中執行的程式和計算機的作業系統之間沒有額外的中間層,沒有資源被冗餘軟體的執行或虛擬硬體的模擬而浪費掉。
Docker 的優勢不僅如此,我們來比較一番。
特性 | Docker | 虛擬機器 |
---|---|---|
啟動速度 | 秒級 | 分鐘級 |
交付/部署 | 開發、測試、生產環境一致 | 無成熟體系 |
效能 | 近似物理機 | 效能損耗大 |
體量 | 極小(MB) | 較大(GB) |
遷移/擴充套件 | 跨平臺,可複製 | 較為複雜 |
2. 映象、容器和倉庫
Docker 由映象(Image)、容器(Container)、倉庫(Repository) 三部分組成。
Docker 的映象可以簡單的類比為電腦裝系統用的系統盤,包括作業系統,以及必要的軟體。例如,一個映象可以包含一個完整的 centos 作業系統環境,並安裝了 Nginx 和 Tomcat 伺服器。注意的是,映象是隻讀的。這一點也很好理解,就像我們燒錄的系統盤其實也是可讀的。我們可以使用 docker images
來檢視本地映象列表。
Docker 的容器可以簡單理解為提供了系統硬體環境,它是真正跑專案程式、消耗機器資源、提供服務的東西。例如,我們可以暫時把容器看作一個 Linux 的電腦,它可以直接執行。那麼,容器是基於映象啟動的,並且每個容器都是相互隔離的。注意的是,容器在啟動的時候基於映象建立一層可寫層作為最上層。我們可以使用 docker ps -a
檢視本地執行過的容器。
Docker 的倉庫用於存放映象。這一點,和 Git 非常類似。我們可以從中心倉庫下載映象,也可以從自建倉庫下載。同時,我們可以把製作好的映象 commit 到本地,然後 push 到遠端倉庫。倉庫分為公開倉庫和私有倉庫,最大的公開倉庫是官方倉庫 Dock Hub,國內的公開倉庫也有很多選擇,例如阿里雲等。
3. Docker 促使開發流程變更
筆者認為,Docker 對開發流程的影響在於使環境標準化。例如,原來我們存在三個環境:開發(日常)環境、測試環境、生產環境。這裡,我們對於每個環境都需要部署相同的軟體、指令碼和執行程式,如圖所示。事實上,對於啟動指令碼內容都是一致的,但是沒有統一維護,經常會出問題。此外,對於執行程式而言,如果所依賴的底層執行環境不一致,也會造成困擾和異常。
現在,我們通過引入 Docker 之後,我們只需要維護一個 Docker 映象。換句話說,多套環境,一個映象,實現系統級別的一次構建到處執行。此時,我們把執行指令碼標準化了,把底層軟體映象化了,然後對於相同的將要部署的程式實行標準化部署。因此,Docker 為我們提供了一個標準化的運維模式,並固化運維步驟和流程。
通過這個流程的改進,我們更容易實現 DevOps 的目標,因為我們的映象生成後可以跑在任何系統,並快速部署。此外,使用 Docker 的很大動力是基於 Docker 實現彈性排程,以更充分地利用機器資源,節省成本。
哈哈,筆者在使用 Docker 過程中,還發現了一些很棒的收益點,例如我們釋出回滾的時候只需要切換 TAG 並重啟即可。還比如,我們對環境升級,也只需要升級基礎映象,那麼新構建的應用映象,自動會引用新的版本。(歡迎補充~~~)
二、從搭建 Web 伺服器開始說起
1. 環境先行,安裝 Docker
現在,我們需要安裝以下步驟安裝 Docker。
- 註冊帳號:在 hub.docker.com/ 註冊賬號。
- 下載安裝
官方下載地址:(Mac):download.docker.com/mac/stable/… 阿里雲下載地址(Mac):mirrors.aliyun.com/docker-tool… 阿里雲下載地址(Windows): mirrors.aliyun.com/docker-tool…
- 安裝指南 這裡,雙擊剛剛下載的 Doker.dmg 安裝包進行安裝。
安裝完成後啟動, Mac 頂部導航欄出現了一個圖示,通過選單可以進行 docker 配置和退出等操作。
官方指南:docs.docker.com/install/ 阿里雲指南(Linux):yq.aliyun.com/articles/11…
- 設定加速服務
市面上有很多加速服務的提供商,如:DaoCloud,阿里雲等。這裡,筆者使用的是阿里雲。(注意的是,筆者作業系統是 Mac,其他操作系列參見阿里雲操作文件)
右鍵點選桌面頂欄的 docker 圖示,選擇 Preferences ,在 Daemon 標籤(Docker 17.03 之前版本為 Advanced 標籤)下的 Registry mirrors 列表中將https://xxx.mirror.aliyuncs.com
加到"registry-mirrors"的陣列裡,點選 Apply & Restart 按鈕,等待 Docker 重啟並應用配置的映象加速器。
- 檢視版本
至此,我們已經安裝完成了。這裡,我們來檢視版本。
docker version
複製程式碼
檢視結果,如下所示。
2. 實幹派,從搭建 Web 伺服器開始
我們作為實幹派,那麼先來搭建一個 Web 伺服器吧。然後,筆者帶你慢慢理解這個過程中,做了什麼事情。首先,我們需要拉取 centos 映象。
docker run -p 80 --name web -i -t centos /bin/bash
複製程式碼
緊接著,我們安裝 nginx 伺服器,執行以下命令:
rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
複製程式碼
安裝完 Nginx 源後,就可以正式安裝 Nginx 了。
yum install -y nginx
複製程式碼
至此,我們再輸入 whereis nginx
命令就可以看到安裝的路徑了。最後,我們還需要將 Nginx 跑起來。
nginx
複製程式碼
現在,我們執行 ctrl + P + Q
切換到後臺。然後,通過 docker ps -a
來檢視隨機分配的埠。
這裡,筆者分配的埠是 32769
,那麼通過瀏覽器訪問 http://127.0.0.1:32769
即可。
大功告成,哈哈哈~
3. 覆盤理解全過程
現在,我們來理解下這個流程。首先,我們輸入 docker run -p 80 --name web -i -t centos /bin/bash
命令會執行互動式容器,其中 -i
選項告訴 Docker 容器保持標準輸入流對容器開放,即使容器沒有終端連線,另一個 -t
選項告訴 Docker 為容器分配一個虛擬終端,以便於我們接下來安裝 Nginx 伺服器。(筆者備註:Docker 還支援輸入 -d
選項告訴 Docker 在後臺執行容器的守護程式)
Docker 會為我們建立的每一個容器自動生成一個隨機的名稱。事實上,這種方式雖然便捷,但是可讀性很差,並且對我們後期維護的理解成本會比較大。因此,我們通過 --name web
選項告訴 Docker 建立一個名稱是 web
的容器。此外,我們通過 -p 80
告訴 Docker 開放 80 埠,那麼, Nginx 才可以對外通過訪問和服務。但是,我們的宿主機器會自動做埠對映,比如上面分配的埠是 32769
,注意的是,如果關閉或者重啟,這個埠就變了,那麼怎麼解決固定埠的問題,筆者會在後面詳細剖析和帶你實戰。
這裡,還有一個非常重要的知識點 docker run
。Docker 通過 run 命令來啟動一個新容器。Docker 首先在本機中尋找該映象,如果沒有安裝,Docker 在 Docker Hub 上查詢該映象並下載安裝到本機,最後 Docker 建立一個新的容器並啟動該程式。
但是,當第二次執行 docker run
時,因為 Docker 在本機中已經安裝該映象,所以 Docker 會直接建立一個新的容器並啟動該程式。
注意的是,docker run
每次使用都會建立一個新的容器,因此,我們以後再次啟動這個容器時,只需要使用命令 docker start
即可。這裡, docker start
的作用在用重新啟動已存在的映象,而docker run
包含將映象放入容器中 docker create
,然後將容器啟動 docker start
,如圖所示。
現在,我們可以在上面的案例的基礎上,通過 exit
命令關閉 Docker 容器。當然,如果我們執行的是後臺的守護程式,我們也可以通過 docker stop web
來停止。注意的是,docker stop
和 docker kill
略有不同,docker stop
傳送 SIGTERM 訊號,而 docker kill
傳送SIGKILL 訊號。然後,我們使用 docker start
重啟它。
docker start web
複製程式碼
Docker 容器重啟後會沿用 docker run
命令指定的引數來執行,但是,此時它還是後臺執行的。我們必須通過 docker attach
命令切換到執行互動式容器。
docker attach web
複製程式碼
4. 不止如此,還有更多命令
Docker 提供了非常豐富的命令。所謂一圖勝千言,我們可以從下面的圖片瞭解到很多資訊和它們之前的用途。(可以直接跳過閱讀,建議收藏,便於擴充套件閱讀)
如果希望獲取更多資訊,可以閱讀官方使用文件。
Command | Description |
---|---|
docker attach | Attach local standard input, output, and error streams to a running container |
docker build | Build an image from a Dockerfile |
docker builder | Manage builds |
docker checkpoint | Manage checkpoints |
docker commit | Create a new image from a container’s changes |
docker config | Manage Docker configs |
docker container | Manage containers |
docker cp | Copy files/folders between a container and the local filesystem |
docker create | Create a new container |
docker deploy | Deploy a new stack or update an existing stack |
docker diff | Inspect changes to files or directories on a container’s filesystem |
docker engine | Manage the docker engine |
docker events | Get real time events from the server |
docker exec | Run a command in a running container |
docker export | Export a container’s filesystem as a tar archive |
docker history | Show the history of an image |
docker image | Manage images |
docker images | List images |
docker import | Import the contents from a tarball to create a filesystem image |
docker info | Display system-wide information |
docker inspect | Return low-level information on Docker objects |
docker kill | Kill one or more running containers |
docker load | Load an image from a tar archive or STDIN |
docker login | Log in to a Docker registry |
docker logout | Log out from a Docker registry |
docker logs | Fetch the logs of a container |
docker manifest | Manage Docker image manifests and manifest lists |
docker network | Manage networks |
docker node | Manage Swarm nodes |
docker pause | Pause all processes within one or more containers |
docker plugin | Manage plugins |
docker port | List port mappings or a specific mapping for the container |
docker ps | List containers |
docker pull | Pull an image or a repository from a registry |
docker push | Push an image or a repository to a registry |
docker rename | Rename a container |
docker restart | Restart one or more containers |
docker rm | Remove one or more containers |
docker rmi | Remove one or more images |
docker run | Run a command in a new container |
docker save | Save one or more images to a tar archive (streamed to STDOUT by default) |
docker search | Search the Docker Hub for images |
docker secret | Manage Docker secrets |
docker service | Manage services |
docker stack | Manage Docker stacks |
docker start | Start one or more stopped containers |
docker stats | Display a live stream of container(s) resource usage statistics |
docker stop | Stop one or more running containers |
docker swarm | Manage Swarm |
docker system | Manage Docker |
docker tag | Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE |
docker top | Display the running processes of a container |
docker trust | Manage trust on Docker images |
docker unpause | Unpause all processes within one or more containers |
docker update | Update configuration of one or more containers |
docker version | Show the Docker version information |
docker volume | Manage volumes |
docker wait | Block until one or more containers stop, then print their exit codes |
官方閱讀連結:docs.docker.com/engine/refe…
5. 進階:倉庫與軟體安裝的簡化
還記得筆者在文章開頭介紹的「映象、容器和倉庫」嗎?Docker 的倉庫用於存放映象。我們可以從中心倉庫下載映象,也可以從自建倉庫下載。同時,我們可以把製作好的映象從本地推送到遠端倉庫。
首先,筆者先引入一個知識點:Docker 的映象就是它的檔案系統,一個映象可以放在另外一個映象的上層,那麼位於下層的就是它的父映象。所以,Docker 會存在很多映象層,每個映象層都是隻讀的,並且不會改變。當我們建立一個新的容器時,Docker 會構建出一個映象棧,並在棧的最頂層新增一個讀寫層,如圖所示。
現在,我們可以通過 docker images
命令檢視本地的映象。
docker images
複製程式碼
查詢結果,如圖所示。
這裡,對幾個名詞解釋一下含義。
- REPOSITORY:倉庫名稱。
- TAG: 映象標籤,其中 lastest 表示最新版本。注意的是,一個映象可以有多個標籤,那麼我們就可以通過標籤來管理有用的版本和功能標籤。
- IMAGE ID :映象唯一ID。
- CREATED :建立時間。
- SIZE :映象大小。
那麼,如果第一次我們通過 docker pull centos:latest
拉取映象,那麼當我們執行 docker run -p 80 --name web -i -t centos /bin/bash
時,它就不會再去遠端獲取了,因為本機中已經安裝該映象,所以 Docker 會直接建立一個新的容器並啟動該程式。
事實上,官方已經提供了安裝好 Nginx 的映象,我們可以直接使用。現在,我們通過拉取映象的方式重新構建一個 Web 伺服器。首先,我們通過 docker search
來查詢映象。我們獲取到 Nginx 的映象清單。
docker search nginx
複製程式碼
補充一下,我們也可以通過訪問 Docker Hub (hub.docker.com/)搜尋倉庫,那麼 star 數越多,說明它越靠譜,可以放心使用。
現在,我們通過 docker pull nginx
拉取最新的 Nginx 的映象。當然,我們也可以通過 docker pull nginx:latest
來操作。
docker pull nginx
複製程式碼
然後,我們建立並執行一個容器。與前面不同的是,我們通過 -d
選項告訴 Docker 在後臺執行容器的守護程式。並且,通過 8080:80
告訴 Docker 8080 埠是對外開放的埠,80 埠對外開放的埠對映到容器裡的埠號。
docker run -p 8080:80 -d --name nginx nginx
複製程式碼
我們再通過 docker ps -a
來檢視,發現容器已經後臺執行了,並且後臺執行了 nginx 命令,並對外開放 8080 埠。
因此,通過瀏覽器訪問 http://127.0.0.1:8080
即可。
6. 其他選擇,使用替代註冊伺服器
Docker Hub 不是軟體的唯一來源,我們也可以切換到國內的其他替代註冊伺服器,例如阿里雲。我們可以登入 cr.console.aliyun.com 搜尋,並拉取公開的映象。
現在,我們輸入 docker pull
命令進行拉取。
docker pull registry.cn-hangzhou.aliyuncs.com/qp_oraclejava/orackejava:8u172_DCEVM_HOTSWAPAGEN_JCE
複製程式碼
這裡,筆者繼續補充一個知識點:註冊伺服器的地址。事實上,註冊伺服器的地址是有一套規範的。完整格式是:[倉庫主機/][使用者名稱/]容器短名[:標籤]。這裡,倉庫主機是 registry.cn-hangzhou.aliyuncs.com,使用者名稱是 qp_oraclejava,容器短名是 orackejava,標籤名是 8u172_DCEVM_HOTSWAPAGEN_JCE。事實上,我們上面通過 docker pull centos:latest
拉取映象,相當於 docker pull registry.hub.docker.com/centos:latest
。
三、構建我的映象
通過上面的學習,筆者相信你已經對 Docker 使用有了一個大致的瞭解,就好比我們通過 VMware 安裝了一個系統,並讓它跑了起來,那麼我們就可以在這個 Linux 系統(CentOS 或者 Ubuntu ) 上面工作我們想要的任何事情。事實上,我們還會經常把我們安裝好的 VMware 系統進行快照備份並實現克隆來滿足我們下次快速的複製。這裡,Docker 也可以構建定製內容的 Docker 映象,例如上面我們使用官方提供的安裝好 Nginx 的 Docker 映象。注意的是,我們通過基於已有的基礎映象,在上面新增映象層的方式構建新映象而已。
總結一下,Docker 提供自定義映象的能力,它可以讓我們儲存對基礎映象的修改,並再次使用。那麼,我們就可以把作業系統、執行環境、指令碼和程式打包在一起,並在宿主機上對外提供服務。
Docker 構建映象有兩種方式,一種方式是使用 docker commit
命令,另外一種方式使用 docker build
命令和 Dockerfile
檔案。其中,不推薦使用 docker commit
命令進行構建,因為它沒有使得整個流程標準化,因此,在企業的中更加推薦使用 docker build
命令和 Dockerfile
檔案來構建我們的映象。我們使用Dockerfile
檔案可以讓構建映象更具備可重複性,同時保證啟動指令碼和執行程式的標準化。
1. 構建第一個 Dockerfile 檔案
現在,我們繼續實戰。這裡,我們把一開始搭建的 Web 伺服器構建一個映象。首先,我們需要建立一個空的 Dokcerfile 檔案。
mkdir dockerfile_test
cd dockerfile_test/
touch Dockerfile
nano Dockerfile
複製程式碼
緊接著,我們需要編寫一個 Dockerfile 檔案,程式碼清單如下
FROM centos:7
MAINTAINER LiangGzone "lianggzone@163.com"
RUN rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
RUN yum install -y nginx
EXPOSE 80
複製程式碼
最後,我們通過 docker build
命令進行構建。
docker build -t="lianggzone/nginx_demo:v1" .
複製程式碼
現在, 我們來通過 docker images
看下我們的新映象吧。
2. 理解 Dockerfile 全過程
哇,我們通過編寫一個 Dockerfile 檔案順利構建了一個新的映象。這個過程簡單得讓人無法相信。現在,讓我們來理解一下這個全過程吧。首先, FROM centos:7
是 Dockerfile 必須要的第一步,它會從一個已經存在的映象執行一個容器,換句話說,Docker 需要依賴於一個基礎映象進行構建。這裡,我們指定 centos 作為基礎映象,它的版本是 7 (CentOS 7)。然後,我們通過 MAINTAINER LiangGzone "lianggzone@163.com"
指定該映象的作者是 LiangGzone,郵箱是 lianggzone@163.com。這有助於告訴使用者它的作者和聯絡方式。接著,我們執行兩個 RUN 指令進行 Nginx 的下載安裝,最後通過 EXPOSE 80
暴露 Dokcer 容器的 80 埠。注意的是,Docker 的執行順序是從上而下執行的,所以我們要明確整個流程的執行順序。除此之外,Docker 在執行每個指令之後都會建立一個新的映象層並且進行提交。
我們使用 docker build
命令進行構建,指定 - t
告訴 Docker 映象的名稱和版本。注意的是,如果沒有指定任何標籤,Docker 將會自動為映象設定一個 lastest 標籤。還有一點,我們最後還有一個 .
是為了讓 Docker 到當前本地目錄去尋找 Dockerfile 檔案。注意的是,Docker 會在每一步構建都會將結果提交為映象,然後將之前的映象層看作快取,因此我們重新構建類似的映象層時會直接複用之前的映象。如果我們需要跳過,可以使用 --no-cache
選項告訴 Docker 不進行快取。
3. Dockerfile 指令詳解
Dockerfile 提供了非常多的指令。筆者這裡特別整理了一份清單,建議收藏檢視。
指令辨別一:RUN、CMD、ENTRYPOINT
RUN
、 CMD
、 ENTRYPOINT
三個指令的用途非常相識,不同在於,RUN
指令是在容器被構建時執行的命令,而CMD
、 ENTRYPOINT
是啟動容器時執行 shell 命令,而 RUN
會被 docker run
命令覆蓋,但是 ENTRYPOINT
不會被覆蓋。事實上,docker run
命令指定的任何引數都會被當作引數再次傳遞給 ENTRYPOINT
指令。CMD
、 ENTRYPOINT
兩個指令之間也可以一起使用。例如,我們 可以使用 ENTRYPOINT
的 exec 形式設定固定的預設命令和引數,然後使用任一形式的 CMD
來設定可能更改的其他預設值。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
複製程式碼
指令辨別二:ADD、COPY
ADD
、 COPY
指令用法一樣,唯一不同的是 ADD
支援將歸檔檔案(tar, gzip, bzip2, etc)做提取和解壓操作。注意的是,COPY
指令需要複製的目錄一定要放在 Dockerfile 檔案的同級目錄下。
4. 將映象推送到遠端倉庫
遠端倉庫:Docker Hub
映象構建完畢之後,我們可以將它上傳到 Docker Hub 上面。首先,我們需要通過 docker login
保證我們已經登入了。緊接著,我們使用 docker push
命令進行推送。
docker push lianggzone/nginx_demo:v1
複製程式碼
這裡,我們瞭解下它的使用,格式是 docker push [OPTIONS] NAME[:TAG]
,其中,筆者設定 NAME 是 lianggzone/nginx_demo,TAG 是 v1。 (筆者注:推送 Docker Hub 速度很慢,耐心等待) 最後,上傳完成後訪問:hub.docker.com/u/lianggzon…/,如圖所示。
遠端倉庫:阿里雲
同時,我們也可以使用國內的倉庫,比如阿里雲。首先,在終端中輸入訪問憑證,登入 Registry 例項。如果你不知道是哪個,可以訪問 cr.console.aliyun.com/cn-hangzhou…。
docker login --username=帳號 registry.cn-hangzhou.aliyuncs.com
複製程式碼
現在,將映象推送到阿里雲映象倉庫。其中, docker tag [IMAGE_ID] registry.cn-hangzhou.aliyuncs.com/[名稱空間]/[映象名稱]:[版本]
和 docker push registry.cn-hangzhou.aliyuncs.com/[名稱空間]/[映象名稱]:[版本]
命令的使用如下所示。
docker tag 794c07361565 registry.cn-hangzhou.aliyuncs.com/lianggzone/nginx_demo:v1
docker push registry.cn-hangzhou.aliyuncs.com/lianggzone/nginx_demo:v1
複製程式碼
最後,上傳完成後訪問:cr.console.aliyun.com/cn-hangzhou…,如圖所示。
5. Dockerfile 的 Github 原始碼地址
這裡,附上我整理的 Dockerfile 的倉庫。後面,筆者會陸續更新用到的一些常用檔案,歡迎 star 關注。
附:參考資料
- 《Docker實戰》
- 《第一本Docker書》
- Docker 命令參考文件
- Dockerfile 映象構建參考文件
(完,轉載請註明作者及出處。)
寫在末尾
【服務端思維】:我們一起聊聊服務端核心技術,探討一線網際網路的專案架構與實戰經驗。同時,擁有眾多技術大牛的「後端圈」大家庭,期待你的加入,一群同頻者,一起成長,一起精進,打破認知的侷限性。
更多精彩文章,盡在「服務端思維」!