手摸手帶你 Docker 從入門到實踐

SHERlocked93 發表於 2020-09-23

bg

在下最近遇到要在伺服器上安裝 Mysql、Nginx、EasyMock 等工具的場景,這裡記錄一下我使用 Docker 安裝的過程,希望也能在類似的場景中幫助到大家~

本文前備知識需要一些 Linux 的一些基本命令,推介先看一下 <半小時搞會 CentOS 入門必備基礎知識> 這篇文章。

CentOS 版本: 7.6

Nginx 版本: 1.16.1

Docker 版本: 19.03.12

你多學一樣本事,就少說一句求人的話

1. 介紹

1.1 出現的原因

前後端開發到測試到生產的過程中,經常會遇到一個問題,明明我在本地跑沒問題,為什麼到測試環境或者生產環境就報錯了了呢,常常這是因為開發、測試、生產的環境與配置不同導致的。

折騰過環境配置的人都明白其中麻煩,換一臺系統、虛擬機器、機器,就又要重來一次,費力費時。由於環境和配置的原因,各種奇奇怪怪因為環境和配置的 Bug,總是像打地鼠遊戲裡面的地鼠一樣不斷冒出來 🐹

Docker

Docker 對這個問題給出了一個很好的解決方案,通過映象將除了系統之外所需要的系統環境由下而上打包,達到服務跨平臺的無縫運作。也就是說,安裝的時候,把特定的環境一模一樣地搬過來,從而解決「在我的電腦上能跑,在 xx 環境就跑不了」的情況。

另外一個重要的原因,就是輕量,基於容器的虛擬化,Docker 的映象僅包含業務執行所需的 runtime 環境,一個 CentOS/Ubuntu 基礎映象僅 170M,因為輕量一個宿主機可以輕鬆安裝數百個容器。

1.2 是什麼

Docker 是基於 Go 語言實現的雲開源專案,從 2013 年釋出到現在一直廣受關注。Docker 可以讓你像使用集裝箱一樣快速的組合成應用,並且可以像運輸標準集裝箱一樣,儘可能的遮蔽程式碼層面的差異。它將應用程式與該程式的依賴,打包在一個檔案裡面。執行這個檔案,就會生成一個虛擬容器。

程式在這個虛擬容器裡執行,就好像在真實的物理機上執行一樣。有了 Docker,就不用擔心環境問題。

本文就不對比虛擬機器跟 Docker 的區別和優劣了,每個文章都有,說爛了,想了解的話可以百度一下 😂,我這裡就不多說了,下面直接看看怎麼安裝怎麼用起來吧。

2. 安裝 & 配置

2.1 Mac 下安裝

在下直接使用 Homebrew Cask 來安裝,Mac 下:

# Homebrew 安裝
$ braw cask install docker

即可,安裝完輸入命令,直接報錯!

➜  ~ docker
zsh: command not found: docker  # 報錯

遇到這個報錯別擔心,安裝完之後要在應用列表裡面雙擊 Docker 應用,輸入密碼之後就可以使用這個命令了 😅。

2.2 CentOS 下安裝

Docker 要求 CentOS 版本必須在 6.5 及以上才可以安裝。

# 安裝
$ sudo yum install yum-utils device-mapper-persistent-data lvm2
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum install docker-ce

# 開啟 Docker
$ sudo systemctl start docker

在 Windows 上可以直接下載安裝包來安裝,或者 Mac 上不使用 Homebrew 也可以去官網直接下載安裝包來安裝,百度一下到處都是安裝方法,其他的就不用多說。

3. 簡單配置並跑起來

3.1 配置映象加速

在 MacOS 的 Docker 配置 Perferences -> Docker Engine 或者 Windows 的 Settings -> Deamon 中的 JSON 中增加一項 registry-mirrors 如下

Docker映象加速配置

配置完之後在命令列中 docker info 就可以檢視到我們配置的映象加速地址了。

➜  ~ sudo docker info
...
 Registry Mirrors:
  https://reg-mirror.qiniu.com/
  http://hub-mirror.c.163.com/
  https://registry.docker-cn.com/
...

如果你的系統的 Docker 沒有客戶端,比如 CentOS 中,可以直接修改 deamon 配置檔案:

# 修改/建立 docker 的 deamon 配置檔案
$ sudo vi /etc/docker/daemon.json

# 修改為如下配置
{
  "experimental": false,
  "debug": true,
  "registry-mirrors": [
    "https://reg-mirror.qiniu.com",
    "http://hub-mirror.c.163.com",
    "https://registry.docker-cn.com"
  ]
}

# 修改完 :wq 重啟
$ sudo systemctl restart docker

3.2 Hello World !

然後就可以快樂跑起來我們第一個 Docker 指令 Hello World 了

Docker跑起Helloworld

Good start ! 🎉

4. 映象 & 容器 & 倉庫

映象和容器的關係就像類和類的例項,一個映象可以同時跑多個容器,單個容器例項又可以建立新的映象。如下圖:

映象容器倉庫

下面解釋一下這個圖裡面出現的元素

概念說明
Docker 映象 Images用於建立 Docker 容器的只讀模板,比如 Ubuntu 16.04系統、Nginx 1.16.0 等,是一個特殊的檔案系統,包括容器執行時需要的程式、庫、資源、引數等,但不包含任何動態資料,內容在構建後也不會被改變,一個映象可以建立多個容器
Docker 容器 Container容器是獨立執行、相互隔離的一個或一組應用,是映象建立的執行例項,實質是程式,可以看作為一個簡易版的 Linux 環境 + 執行在其中的應用程式
Docker 客戶端 Client客戶端通過命令列或者其他工具使用 Docker SDK (https://docs.docker.com/devel... 與 Docker 的守護程式通訊
Docker 主機 Host一個物理或者虛擬的機器用於執行 Docker 守護程式和容器
Docker 倉庫 Repository集中存放映象檔案的地方,分為公有倉庫和私有倉庫。
Docker 註冊伺服器 Registry是一個集中儲存、分發映象的服務,官方的叫 Docker Hub。一個 Docker Registry 中可包含多個倉庫,每個倉庫可以包含多個標籤 Tag 的映象,不同的標籤對應不同的版本
Docker MachineDocker Machine 是一個簡化 Docker 安裝的命令列工具,通過一個簡單的命令列即可在相應的平臺上安裝Docker,比如 VirtualBox、 Digital Ocean、Microsoft Azure

容器的生命週期圖示

容器的生命週期

容器的五個核心狀態,也就是圖中色塊表示的:Created、Running、Paused、Stopped、Deleted:

  1. Created:容器已經被建立,容器所需的相關資源已經準備就緒,但容器中的程式還未處於執行狀態。
  2. Running:容器正在執行,也就是容器中的應用正在執行。
  3. Paused:容器已暫停,表示容器中的所有程式都處於暫停 ( 不是停止 ) 狀態。
  4. Stopped:容器處於停止狀態,佔用的資源和沙盒環境都依然存在,只是容器中的應用程式均已停止。
  5. Deleted:容器已刪除,相關佔用的資源及儲存在 Docker 中的管理資訊也都已釋放和移除。

本文主要關注於使用,就不太贅述這些狀態的切換等,下面直接上手。

5. 基本使用

5.1 操作命令

# 開啟 Docker 開機自啟動
$ sudo systemctl enable docker

# 關閉 Docker 開機自啟動
$ sudo systemctl disable docker

5.2 映象命令

# 去下載映象,先從本地找,沒有去映象,最後沒有去 hub,標籤不寫預設為 lastest
$ docker pull [映象名]:[標籤Tag]

# 列出本機的所有 image 檔案,-a 顯示本地所有映象(包括中間映象),-q 只顯示映象ID,--digests 顯示映象的摘要資訊
$ docker image ls
$ docker images

# 刪除 image 檔案, -f 強制刪除映象
$ docker rmi [映象名][:標籤Tag]
$ docker rmi [映象名1][:標籤Tag] [映象名2][:標籤Tag]    # 刪多個
$ docker rmi $(docker ps -a -q)    # 刪全部,後面是子命令

# 查詢映象名稱,--no-trunc 顯示完整的映象描述,--filter=stars=30 列出star不少於指定值的映象,--filter=is-automated=true 列出自動構建型別的映象
$ docker search [關鍵字]

# 下載映象,標籤 tag 不寫預設為 lastest,也可以自己加比如 :3.2.0
$ docker pull [映象名][:標籤Tag]

5.3 容器命令

# 列出本機正在執行的容器,-a 列出本機所有容器包括終止執行的容器,-q 靜默模式只顯示容器編號,-l 顯示最近建立的容器
$ docker container ls     # 等價於下面這個命令
$ docker ps

# 新建並啟動容器
$ docker run [option] [容器名] 

# 啟動容器
$ docker start [容器ID]/[容器Names]

# 重啟容器
$ docker restart [容器ID]/[容器Names]

# 終止容器執行
$ docker kill [容器ID]  # 強行終止,相當於向容器裡面的主程式發出 SIGKILL 訊號,那些正在進行中的操作會全部丟失
$ docker kill $(docker ps -a -q) # 強行終止所有容器
$ docker stop [容器ID]  # 從容終止,相當於向容器裡面的主程式發出 SIGTERM 訊號,然後過一段時間再發出 SIGKILL 訊號
$ docker stop $(docker ps -a -q) # 終止所有容器

# 終止執行的容器檔案,依然會佔據硬碟空間,可以使用 docker container rm 命令刪除,-f 強制刪除可以刪除正在執行的容器
$ docker rm [容器ID]
$ docker rm `docker ps -aq`    # 刪除所有已經停止的容器,因為沒停止的rm刪不了需要加-f

# 檢視容器的輸出,-t加入時間戳,-f跟隨最新日誌列印,--tail數字顯示最後多少條,如果docker run時,沒有使用-it,就要用這個命令檢視輸出
$ docker logs [容器ID]

# 檢視容器程式資訊
$ docker top [容器ID]/[容器Names]
$ docker port [容器ID]/[容器Names]

# 退出容器
$ exit           # 容器退出
ctrl + p + q     # 容器退出,快捷鍵

# 進入容器
$ docker attach [容器ID]      # 退出容器時會讓容器停止,本機的輸入直接輸到容器中
$ docker exec -it [容器ID]    # 退出容器時不會讓容器停止,在已執行的容器中執行命令,不建立和啟動新的容器

# 設定容器在docker啟動時自動啟動
$ docker container update --restart=always [容器名字]

這裡要特別說一下 docker runoption,因為最常用:

  1. --name 為容器指定一個名稱;
  2. -d 容器啟動後進入後臺,並返回容器 ID,即啟動守護式容器;
  3. -P 隨機埠對映;
  4. -p 80:8080 將本地 80 埠對映到容器的 8080 埠;
  5. bash 容器啟動以後,內部第一個執行的命令。這裡啟動 bash,保證使用者可以使用 Shell;
  6. -i 以互動模式執行容器,通常與 -t 同時使用;
  7. -t 為容器重新分配一個偽輸入終端,容器的 Shell 會對映到當前的 Shell,然後在本機視窗輸入的命令,就會傳入容器,通常與 -i 同時使用;
  8. --rm 在容器終止執行後自動刪除容器檔案;
  9. --restart=always 設定容器自啟動;
  10. -v /xxx:/yyy 對映命令,把本機的 xxx 目錄對映到容器中的 yyy 目錄,也就是說改變本機的 xxx 目錄下的內容, 容器 yyy 目錄中的內容也會改變;

比如我在 CentOS 下跑起來一個 CentOS 的 Docker 容器:

# 下載
$ docker pull centos

# 在上面下載的 centos 映象基礎上,新建一個容器名為 mycentos0901 的 centos 例項,並進入這個容器的 bash
$ docker run -it --name mycentos0901 0d120b6ccaa8

[[email protected] /]   # 進入容器,下面輸入命令,注意這裡 root 後面的一串 ID
$ ls       # 可以看到centos的根目錄檔案列表
$ docker   # bash: docker: command not found 這個容器沒有安裝docker

是不是很神奇,我們可以在一開始的 CentOS 下面執行 docker ps 來檢視容器列表:

image-20200901225909737

你會發現上面那個 ID,正是下面列表中跑起來的這個容器的 ID,映象的 ID 也是我們前面 pull 下來的 CentOS 映象 ID,名字也是我們起的 mycentos0901

如果 docker run 之後報 Conflict. The container name "xxxx" is already in use by container 就直接執行 docker rm $(docker ps -a -q) 刪除已停止的容器,或者精確刪除 docker rm [containerID] 也可以,就可以了。

5.4 幾個常見場景的命令使用

守護式啟動容器

使用 centos 以後臺模式啟動一個容器 docker run -d --name mycentos0903 0d120b6ccaa8,啟動之後 docker ps -a 檢視,發現容器並不在執行中,這是因為 Docker 的執行機制:Docker 容器後臺執行,必須有一個前臺程式

容器執行的命令如果不是那些一直掛起的命令,比如 toptail ,執行結束會自動退出。所以為了讓容器持續在後臺執行,那麼需要將執行的程式以前臺程式的形式執行。

比如這裡在後臺執行一個命令,這個命令一直在列印 docker run -d centos /bin/sh -c "while true; do echo hello zzyy; sleep 2; done",然後我們 logs 檢視一下:

docker_logs

退出容器後對容器操作

退出容器後可以通過 exec 方法對正在執行的容器進行操作:

image-20200911142617186

在容器中拷貝檔案到外部

拷貝檔案使用 cp 命令

$ docker cp [容器ID]/[容器Names]:[要拷貝的檔案目錄] [本機目錄]   # 容器檔案拷貝到本機
$ docker cp [本機目錄] [容器ID]/[容器Names]:[要拷貝的檔案目錄]   # 本機檔案拷貝到容器

cp 不僅能把容器中的檔案/資料夾拷貝到本機,也可以把本機中的檔案/資料夾拷貝到容器。

演示一下,這裡先到容器裡面建立一個無聊的檔案 xixi.txt,然後拷貝到本機:

image-20200921210352644

實用的時候,我們可以拷貝配置、日誌等檔案到本地。

6. 安裝 MySQL

# 查詢映象
$ docker search mysql

# 下載映象,實測沒配置映象加速的時候會比較慢,配置了就好一些
$ docker pull mysql

# 檢視映象
$ docker images

# 建立並執行容器
$ docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=888888 -v /Users/sherlocked93/Personal/configs/mysql.d:/etc/mysql/conf.d --name localhost-mysql mysql

稍微解釋一下上面的引數:

  1. -p 3307:3306 將本機的 3307 埠對映到 mysql 容器的 3306 埠,根據需要自行更改;
  2. -e MYSQL_ROOT_PASSWORD=<string> 設定遠端登入的 root 使用者密碼;
  3. --name <string> 可選,設定容器別名;
  4. -v xxx/mysql.d:/etc/mysql/conf.d 將本地目錄下設定資料夾對映到容器的 /etc/mysql/conf.d
  5. -v xxx/logs:/logs 將本機指定目錄下的 logs 目錄掛載到容器的 /logs
  6. -v xxx/data:/var/lib/mysql 將主機制定目錄下的 data 目錄掛載到容器的 /var/lib/mysql

執行截圖:

安裝Mysql

然後去 Navicat 中就可以連線到 MySQL 了。

這也太爽了!真的是幾行命令就裝好了啊,比之前真是快樂多了 😂

7. 安裝 Nginx

Nginx 的安裝和其他的類似,如果你還不太瞭解 Nginx 如何使用,可以參看 <Nginx 從入門到實踐,萬字詳解> 這篇文章,看完基本就瞭解如何使用和配置了。

# 查詢/下載映象
$ docker search nginx
$ docker pull nginx

image-20200922203203685

然後建立一個臨時的容器,目的是把預設配置拷貝到本機,我這裡把配置檔案放到 /mnt 目錄下,主要是三個配置資料夾:

  1. /etc/nginx 放置 Nginx 配置檔案;
  2. /var/log/nginx/ 放置 Nginx 日誌檔案;
  3. /usr/share/nginx/html/ 放置 Nginx 前端靜態檔案都放在這個資料夾;

分別把這幾個目錄都拷貝到本機的 /mnt 資料夾下的 nginxnginx_logshtml 資料夾。

剛剛建立的臨時容器沒用了 docker rm -f [臨時容器ID] 把臨時容器幹掉,然後 docker run 重新建立 Nginx 容器:

$ docker run -d --name localhost-nginx -p 8082:80 \
-v /mnt/nginx:/etc/nginx \
-v /mnt/nginx_logs:/var/log/nginx \
-v /mnt/html:/usr/share/nginx/html \
--privileged=true nginx

--privileged=true 表示容器內部對掛載的目錄擁有讀寫等特權。

其他配置剛剛上面之前已經講過,應該不用講了。

image-20200922204931582

然後在你自己瀏覽器上就可以訪問了,如果是雲伺服器,記得開放對應埠。

8. 安裝 Easy Mock

因為 Easy Mock 依賴 Redis 和 MongoDB,因此本地環境使用 docker-compose 來搭建 Easy Mock 應該算是最佳實踐了。

安裝 docker-compose

官方文件:https://docs.docker.com/compose/install/

首先你得確定擁有 docker 環境,如果你是 Windows / Mac 使用者,那麼安裝客戶端,就會自帶 docker-compose 了。

因為本次我們是在雲伺服器 CentOS7.6 上搭建,所以我們需要自行安裝 docker-compose,執行如下命令,下載當前穩定版本的 docker-compose

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

修改檔案許可權為可執行檔案

$ sudo chmod +x /usr/local/bin/docker-compose

驗證是否安裝成功

$ docker-compose version

編寫 docker-compose.yml 配置檔案

可以參考官方文件給出的部署文件,也可以參考我下面的配置過程。

首先新建檔案 docker-compose.yml 並將下面 docker-compose 檔案內容複製進入 docker-compose.yml,然後將內容中註釋位置替換為自己需要的本地地址

version: '3'

services:
  mongodb:
    image: mongo:3.4.1
    volumes:
      #  /apps/easy-mock/data/db 是資料庫檔案存放地址,根據需要修改為本地地址
      - '/apps/easy-mock/data/db:/data/db'
    networks:
      - easy-mock
    restart: always

  redis:
    image: redis:4.0.6
    command: redis-server --appendonly yes
    volumes:
      #  /apps/easy-mock/data/redis 是 redis 資料檔案存放地址,根據需要修改為本地地址
      - '/apps/easy-mock/data/redis:/data'
    networks:
      - easy-mock
    restart: always

  web:
    image: easymock/easymock:1.6.0
    # easy-mock 官方給出的檔案,這裡是 npm start,這裡修改為 npm run dev
    command: /bin/bash -c "npm run dev:server"
    ports:
      - 7300:7300  # 改為你自己期望的對映
    volumes:
      # 日誌地址,根據需要修改為本地地址
      - '/apps/easy-mock/logs:/home/easy-mock/easy-mock/logs'
    networks:
      - easy-mock
    restart: always

networks:
  easy-mock:

啟動 Easy Mock

在 docker-compose 檔案目錄下,執行如下命令:

$ docker-compose up -d

如果遇到 easymock docker 例項報檔案許可權錯誤

Error: EACCES: permission denied....

要在專案根目錄執行以下命令

$ chmod 777 /yourfile/logs

然後就可以通過瀏覽器上的 你的域名.com:7300 訪問到 easy-mock 了!

如果你覺得域名後面跟著埠號挺難看的,你可以通過配置 Nginx 的二級域名來訪問你部署的 easy-mock,配置二級域名的方法參見 這篇文章

9. 視覺化管理

關於視覺化查詢工具,這裡就簡單推介一個 LazyDocker,由於是在終端執行的,而且支援鍵盤操作和滑鼠點選,就挺騷氣的,有了這個一些查詢語句可以少打幾次了。

lzd

安裝比較簡單,執行下面的命令:

$ docker run --rm -it -v \
/var/run/docker.sock:/var/run/docker.sock \
-v ~/.config/lazydocker:/.config/jesseduffield/lazydocker \
lazyteam/lazydocker

可以設定一個終端的 alias

$ alias lzd='docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock -v ~/.config/lazydocker:/.config/jesseduffield/lazydocker lazyteam/lazydocker'

然後你在終端輸入 lzd 就可以瀏覽你的映象、容器、日誌、配置、狀態等等內容了。

10. 結語

由於在下目前使用 Docker 的主要場景是 MySQL、Nginx 之類工具的安裝,所以本文所介紹的內容也大多屬於這個場景。

篇幅原因 Docker 還有一些內容本文沒有介紹,但上面的內容已基本滿足日常的使用,其他 Docker 的內容可以關注一下在下的後續文章~


網上的帖子大多深淺不一,甚至有些前後矛盾,在下的文章都是學習過程中的總結,如果發現錯誤,歡迎留言指出~

參考文件:

  1. Empowering App Development for Developers | Docker 官方網站
  2. Docker核心技術(基礎篇)
  3. Docker安裝mysql
  4. Docker文件
  5. Docker-compose文件
  6. 使用 docker 執行 easy-mock - 知乎
  7. docker-compose easy-mock - 簡書
  8. 使用 docker 執行 easy-mock | CodingDiary
  9. easymock官方docker倉庫:easy-mock/easy-mock-docker
  10. 使用docker安裝nginx

作者其他高贊文章:

  1. JS 中可以提升幸福度的小技巧
  2. Vue 使用中的小技巧
  3. Nginx 從入門到實踐,萬字詳解!
  4. 半小時搞會 CentOS 入門必備基礎知識
  5. 手摸手 Webpack 多入口配置實踐
  6. 前端路由跳轉基本原理

PS:本人部落格地址 Github - SHERlocked93/blog,也歡迎大家關注我的公眾號【前端下午茶】,一起加油吧~

手摸手帶你 Docker 從入門到實踐