一文零基礎教你學會 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

設置存儲庫

  1. 更新 apt 軟體包快取
sudo apt-get update
  1. 在機器上首次安裝的需先設置 Docker 存儲庫,由於 apt 源使用 HTTPS 以確保軟體下載過程中不被篡改。因此,我們首先需要添加使用 HTTPS 傳輸的軟體包以及 CA 證書。
$ sudo apt-get install       apt-transport-https       ca-certificates       curl       gnupg-agent       software-properties-common
  1. 添加 Docker 的官方 GPG 密鑰
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  1. 向 source.list 中添加 Docker 軟體源
$ sudo add-apt-repository      "deb [arch=amd64] https://download.docker.com/linux/ubuntu      $(lsb_release -cs)      stable"

安裝 Docker EC(社區版)

  1. 更新 apt 軟體包快取
sudo apt-get update
  1. 安裝
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 psdocker container ls 命令用來查看正在運行的容器列表,這個時候是沒有正在運行的容器實例的,因為在以上 docker container run hello-world 命令執行之後 hello-world 就會停止,容器也會隨著自動停止,但並不是所有的容器運行之後也都會停止的,例如 Nginx 後面會進行實踐。

$ docker ps

通過 docker ps--alldocker 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

發布鏡像實現共享

  1. 登陸 Docker,已登陸的可以忽略這一步
$ docker login
  1. 為本地鏡像打標籤,tag 不寫默認為 latest
# docker image tag [imageName] [username]/[repository]:[tag]  $ docker image tag hello-docker mayjun/hello-docker
  1. 發布鏡像文件
# 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