一文零基礎教你學會 Docker 入門到實踐
- 2019 年 10 月 8 日
- 筆記
沒有人不愛惜他的生命,但很少人珍視他的時間。—— 梁實秋
Docker 自 2013 年發布至今一直備受關注,從招聘面試角度來看有些職位對於了解 Docker、K8S 這些也有一些加分項,同時學習 Docker 也是後續學習 K8S 的基礎,但是對於 Docker 很多人也需並不了解,其實 Docker 也並沒有那麼難,本文從 Docker 入門到應用實踐為大家進行講解,中間也列舉了很多實例,希望能幫助大家更好的理解。
作者簡介:五月君,Nodejs Developer,熱愛技術、喜歡分享的 90 後青年,公眾號「Nodejs技術棧」,Github 開源項目 https://www.nodejs.red
Docker 入門到實踐路線圖

Docker初識
為什麼要使用 Docker
Docker 可以將應用以集裝箱的方式進行打包,通過鏡像的方式可以實現在不同的環境下進行快速部署,在團隊中還可實現一次打包,多次共享,使用 Docker 可以輕鬆的為任何應用創建一個輕量級的、可移植的、自給自足的容器。
例如,我們在本地將編譯測試通過的程式打包成鏡像,可以快速的在伺服器環境中進行部署,有時也能解決不同的開發環境造成的問題 「明明我本地是好的,但是一到伺服器就不行」。
為什麼要使用 Docker?總結下來其有以下優點:
- 高效的利用系統資源(節約成本)
- 持續交付與部署(敏捷)
- 多平台的遷移更容易(可移植性)
- 容易的沙箱機制(安全性)
Docker 架構一瞥
中間部位為我們進行 Docker 操作的宿主機,其運行了一個 Docker daemon 的核心守護程式,負責構建、運行和分發 Docker 容器。
左邊為 Docker 客戶端,其與 Docker 守護進程進行通訊,客戶端會將 build、pull、run 命令發送到 Docker 守護進程進行執行。
右邊為 Docler 註冊表存儲 Docker 鏡像,是一個所有 Docker 用戶共享 Docker 鏡像的服務,Docker daemon 與之進行交互。

Docker 鏡像與容器概述
參考 https://docs.docker.com/engine/docker-overview/
什麼是 Docker 鏡像
Docker 會把應用程式及依賴打包進鏡像(Images)里,提供了容器運行時所需的程式、庫、資源、配置等文件外,還包含了一些為運行時準備的一些配置參數(如匿名卷、環境變數、用戶等),通過這個鏡像文件可生成 Docker 容器。
例如:這個鏡像文件包含了一個完整的 Ubuntu 系統,我們可以在 Ubuntu 鏡像基礎之上安裝了 Redis、Mysql 等其它應用程式,可以回顧下 Docker 架構一瞥 在 DOCKER_HOST 裡面有個 images。
另外在製作好鏡像文件之後可以拷貝到其它機器使用,它是通用的,鏡像的製作可以基於 Dockerfile 構建後面會講解。
什麼是 Docker 容器
容器是鏡像的可運行實例,你可以使用 Docker API 創建、啟動、停止、移動或刪除它,
在默認情況下,容器與其它容器及其主機是隔離的,擁有自己的獨立進程空間、網路配置。
容器由其鏡像以及在創建或啟動容器時提供的任何配置選項定義。當容器被刪除時,對其狀態的任何未存儲在持久存儲中的更改都會消失。
Docker安裝
Docker 是一個開源的商業產品,提供了社區版(CE)和企業版(EE),以下也都是基於企業版進行介紹,我這裡作業系統採用的 Linux 下 Ubuntu 系統,更多安裝方式也可參照官網安裝指南 https://docs.docker.com/install/
更改 docker 源
這個看情況,因為 Docker 的源在國外,中國訪問速度可能會不穩定,有需要的可以按照以下步驟更換為中國源
- 編輯 /etc/docker/daemon.json 文件,輸入 docker-cn 鏡像源地址
$ sudo vim /etc/docker/daemon.json { "registry-mirrors": ["https://registry.docker-cn.com"] }
- 重啟 Docker 服務
$ sudo service docker restart
設置存儲庫
- 更新 apt 軟體包快取
sudo apt-get update
- 在機器上首次安裝的需先設置 Docker 存儲庫,由於 apt 源使用 HTTPS 以確保軟體下載過程中不被篡改。因此,我們首先需要添加使用 HTTPS 傳輸的軟體包以及 CA 證書。
$ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
- 添加 Docker 的官方 GPG 密鑰
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- 向 source.list 中添加 Docker 軟體源
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
安裝 Docker EC(社區版)
- 更新 apt 軟體包快取
sudo apt-get update
- 安裝
sudo apt-get install docker-ce docker-ce-cli containerd.io
執行以下命令使用腳本自動安裝,這也是最簡化的安裝流程,它會檢測你當前使用的 Linux 版本,選擇合適的安裝包進行安裝,
sudo wget -qO- https://get.docker.com | sh
添加 Docker 用戶組
由於 Docker 操作需要擁有 root 許可權,為避免每次都輸入 sudo,可以把用戶加入 Docker 用戶組,執行以下命令
# https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user $ sudo usermod -aG docker $USER
驗證
安裝完成後,運行下面的命令,驗證是否安裝成功
$ docker -v Docker version 19.03.2, build 6a30dfc
鏡像構建初探
上面對 Docker 的鏡像和容器做了簡要概述,有個初步的了解之後,再來看下 Docker 鏡像和容器的實踐。
抓取 image 文件到本地
hello-world 為鏡像名字,docker image pull 為抓取鏡像命令,Docker 官方提供的 image 文件都放在 library 默認組裡,library/hello-world 也就為 image 文件的位置。
$ docker image pull hello-world # 以下為抓取過程中的日誌資訊 Using default tag: latest latest: Pulling from library/hello-world 1b930d010525: Pull complete Digest: sha256:451ce787d12369c5df2a32c85e5a03d52cbcef6eb3586dd03075f3034f10adcd Status: Downloaded newer image for hello-world:latest docker.io/library/hello-world:latest
查看 image 文件列表
image 文件抓取成功通過 docker images 或 docker image ls 命令查看當前都有哪些鏡像
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest fce289e99eb9 8 months ago 1.84kB
運行 image 文件
執行 docker container run
命令會生成一個正在運行的容器實例,另外 docker container run
發現本地沒有指定的 image 文件,其自身還有自動抓取 image 文件功能,就是上面講解的 docker image pull
命令
$ docker container run hello-world Hello from Docker! # 以下內容省略 ...
查看容器列表
使用 docker ps
或 docker container ls
命令用來查看正在運行的容器列表,這個時候是沒有正在運行的容器實例的,因為在以上 docker container run hello-world
命令執行之後 hello-world
就會停止,容器也會隨著自動停止,但並不是所有的容器運行之後也都會停止的,例如 Nginx 後面會進行實踐。
$ docker ps
通過 docker ps--all
或 docker container ls--all
命令可以查看所有的容器實例,包含已經停止的
$ docker ps --all CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a662ec198a83 hello-world "/hello" 10 minutes ago Exited (0) 10 minutes ago exciting_wing
構建一個 Nginx 鏡像
以下命令會用 nginx 鏡像啟動一個容器,命名為 nginxserver,並映射到 8081 埠
$ docker container run --name nginxserver -d -p 8081:80 nginx
好了,我們現在就可以使用 http://localhost:8081/ 來訪問這個 Nginx 伺服器,由於我這裡是在虛擬機上安裝的 Docker 因此要使用我的虛擬機地址 http://192.168.6.128:8081/ 進行訪問,同樣如果你是在虛擬機、雲伺服器上安裝的 Docker 也要使用相應的 ip 來訪問,如果是在本機直接 localhost 就可以。以下為 Nginx 默認的歡迎頁面。

再分別看下目前的 image 列表和正在運行的容器
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 5a3221f0137b 3 weeks ago 126MB hello-world latest fce289e99eb9 8 months ago 1.84kB $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b7bf26745b3f nginx "nginx -g 'daemon of…" 23 minutes ago Up 23 minutes 0.0.0.0:8081->80/tcp nginxserver
終止容器
通過 docker container kill[containID]
命令終止正在運行的容器
# docker container kill [containID] $ docker container kill b7bf26745b3f
刪除容器文件
上面的終止容器並不會刪除容器文件,僅僅是容器停止運行,通過 docker ps –all 命令查看所有的容器列表
$ docker ps --all CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b7bf26745b3f nginx "nginx -g 'daemon of…" 29 minutes ago Exited (137) About a minute ago nginxserver a662ec198a83 hello-world "/hello" 49 minutes ago Exited (0) 49 minutes ago exciting_wing
由於已經終止容器文件依然會佔據著我們的磁碟空間,在不使用的情況可通過 docker container rm[containerID]
命令刪除
$ docker container rm b7bf26745b3f a662ec198a83
執行以上命令之後,再使用 docker ps--all
命令,此時容器列表就為空了。
刪除鏡像文件
同樣刪除一個鏡像文件也很簡單執行 docker rmi[imageID]
命令即可
$ docker rmi 5a3221f0137b fce289e99eb9
Dockerfile實踐
Dockerfile 是由一系列的參數、命令構成的可執行腳本,用來構建、訂製 Docker 鏡像。本節通過一個 Node.js 的簡單項目為例,介紹下如何編寫 Dockerfile 文件、如何在 Docker 容器里運行 Node.js 項目。
Nodejs項目準備
/usr/src/nodejs/hello-docker 目錄下新建 app.js
// /usr/src/nodejs/hello-docker/app.js const http = require('http'); const PORT = 30010; const server = http.createServer((req, res) => { res.end('Hello Docker'); }) server.listen(PORT, () => { console.log('Running on http://localhost:', PORT); });
/usr/src/nodejs/hello-docker 目錄下新建 package.json
// /usr/src/nodejs/hello-docker/package.json { "name": "hello-docker", "version": "1.0.0", "description": "", "author": "May", "main": "app.js", "scripts": { "start": "node app.js" }, "dependencies": { } }
Dockerfile 編寫
首先在項目根目錄下創建 .dockerignore 文件,把不需要打包進 Docker Image 里的文件進行過濾
# /usr/src/nodejs/hello-docker/.dockerignore .git node_modules
Dockerfile
項目根目錄下新建 Dockerfile 文件
# /usr/src/nodejs/hello-docker/Dockerfile FROM node:10.0 # 在容器中創建一個目錄 RUN mkdir -p /usr/src/nodejs/ # 定位到容器的工作目錄 WORKDIR /usr/src/nodejs/ # RUN/COPY 是分層的,package.json 提前,只要沒修改,就不會重新安裝包 COPY package.json /usr/src/app/package.json RUN cd /usr/src/app/ RUN npm i # 把當前目錄下的所有文件拷貝到 Image 的 /usr/src/nodejs/ 目錄下 COPY . /usr/src/nodejs/ EXPOSE 30010 CMD npm start
- FROM:FROM 是構建鏡像的基礎源鏡像,該 Image 文件繼承官方的 node image
- RUN:後面跟的是在容器中執行的命令
- WORKDIR:容器的工作目錄
- COPY:拷貝文件至容器的工作目錄下,.dockerignore 指定的文件不會拷貝
- EXPOSE:將容器內的某個埠導出供外部訪問
- CMD:Dockerfile 執行寫一個 CMD 否則後面的會被覆蓋,CMD 後面的命令是容器每次啟動執行的命令,多個命令之間可以使用 && 鏈接,例如 CMD git pull && npm start
構建 hello-docker 鏡像文件
Dockerfile 文件創建好之後,使用 docker image build
命令創建鏡像文件,-t 參數用來指定鏡像的文件名稱,最後一個 . 也不要省略,表示 Dockerfile 文件的所在目錄
$ docker image build -t hello-docker .
執行以上命令之後,我們來查看下新生成的鏡像文件 hello-docker
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-docker latest 6b1c2775591e 4 minutes ago 675MB node 10.0 1c1272350058 16 months ago 675MB
運行容器
鏡像構建成功之後通過 docker container run 命令來生成一個容器,幾個參數說明:
- -d:表明容器的運行模式在後台
- -p:埠映射,將本機的 30000 埠映射到容器的 30010 埠,這樣在外網就可通過 30000 埠訪問到我們的服務
- hello-docker:為我們的鏡像名字
$ docker container run -d -p 30000:30010 hello-docker
執行以上命令之後通過 docker ps 查看我們剛剛運行的容器資訊
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c2891d477edf hello-docker "/bin/sh -c 'npm sta…" 15 seconds ago Up 14 seconds 0.0.0.0:30000->30010/tcp pedantic_mestorf
不出什麼意外,此時我們的 Node.js 服務已經運行在 Docker 容器的虛擬環境里了,訪問 curl http://localhost:30000
可以進行測試。
$ curl http://localhost:30000 Hello Docker
檢查日誌
查看運行日誌,「c2891d477edf」 為容器 ID
$ docker logs -f c2891d477edf > [email protected] start /usr/src/nodejs/hello-docker > node app.js Running on http://localhost: 30010
容器進入退出
為了方便排查內部容器文件,可以通過 docker exec -it c2891d477edf /bin/sh 命令進入容器,c2891d477edf 為容器 ID
$ docker exec -it c2891d477edf /bin/sh $ ls # 列出目錄列表 Dockerfile app.js package-lock.json package.json
由於我們已經啟動了 hello-docker 這個服務,在容器里再次操作 node app.js 就會報埠衝突
$ node app events.js:167 throw er; // Unhandled 'error' event ^ Error: listen EADDRINUSE :::30010
按下 Ctrl + d (或者輸入 exit)退出容器
Registry實踐
Registry 是一個註冊伺服器,是一個集中存放鏡像倉庫的地方,這裡著重介紹下 Docker Hub,它是官方維護的一個公共倉庫,我們的大部分需求也都可從這裡下載。
註冊 Docker 帳號
在開始之前你需要先去 Docker 官網註冊一個帳號 https://hub.docker.com/ 後續講解發布鏡像需要用到
鏡像搜索
使用 docker search鏡像名稱
可以搜索你需要的鏡像,搜索結果會根據 STARS 進行排序
$ docker search nginx NAME DESCRIPTION STARS OFFICIAL AUTOMATED nginx Official build of Nginx. 11935 [OK] jwilder/nginx-proxy Automated Nginx reverse proxy for docker con… 1651 [OK] richarvey/nginx-php-fpm Container running Nginx + PHP-FPM capable of… 740 [OK] ...
鏡像拉取
搜索到需要的鏡像後執行 docker pull 命令拉取鏡像
$ docker pull nginx
發布鏡像實現共享
- 登陸 Docker,已登陸的可以忽略這一步
$ docker login
- 為本地鏡像打標籤,tag 不寫默認為 latest
# docker image tag [imageName] [username]/[repository]:[tag] $ docker image tag hello-docker mayjun/hello-docker
- 發布鏡像文件
# docker image push [username]/[repository]:[tag] $ docker image push mayjun/hello-docker
鏡像發布成功之後,在自己的個人用戶下也可以看到鏡像資訊

如果你想在別的機器上也使用這個鏡像,直接 docker pull 拉取即可,實現鏡像的共享。
DockerCompose實踐
Compose 是 Docker 官方開源的一個項目,可以管理多個 Docker 容器組成一個應用,例如 Web 服務,除了服務本身還有資料庫、Redis、Nginx 等一系列相關聯服務需要安裝。
有個 Compose 的支援,我們只需要定義一個 YAML 格式的配置文件( docker-compose.yml
),來編寫一個項目所需要的多個容器配置及調用關係,通過簡單的命令即可同時開始或者關閉這些容器。
二進位安裝
https://github.com/docker/compose/releases
# compose 下載之後通過管道的方式輸入至 /usr/local/bin/docker-compose # uname -s 查找是什麼系統,例如:Linux # uname -m 查找是什麼版本,例如:x86_64 $ curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose # 把這個文件變成可執行的 $ chmod +x /usr/local/bin/docker-compose
查看安裝是否成功
$ docker-compose --version docker-compose version 1.25.0-rc2, build 661ac20e
Docker Compose 搭建 WordPress 個人部落格
WordPress 是一個免費開源的個人部落格系統,使用的也是比較多的,並且也有 Docker 鏡像,使用 Docker 部署還是非常簡單的。
在 /usr/src/wordpress 目錄下,建立 docker-compose.yml 配置文件,寫入如下內容:
mysql: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORD=123456 - MYSQL_DATABASE=wordpress web: image: wordpress links: - mysql environment: - WORDPRESS_DB_PASSWORD=123456 ports: - "192.168.6.128:8080:80" working_dir: /var/www/html volumes: - wordpress:/var/www/html
啟動容器,瀏覽器輸入 http://192.168.6.128:8080 即可看到效果,可以親自實踐下
# -d 參數表示後台啟動 $ docker-compose up -d
關閉容器,執行以下命令需要在 docker-compose.yml 配置文件同級目錄下
$ docker-compose stop