Docker虛擬化管理:30分鐘教你學會用Docker

夜月歸途發表於2020-05-01

關於Docker的官方介紹網上太多了我就不貼了,就實際體驗來說Docker可以極大的簡化環境搭建及服務部署的操作流程,大大降低部署的時間成本,解放你的雙手

本文不會深入講解Docker底層架構及執行原理,也不會有一堆架構圖貼在這裡。該篇旨在讓你以最快的速度學會使用Docker,關於Docker的架構及其底層的一些知識,你可以在學會Docker的基本使用之後再去了解。開門見山講架構聊底層有點容易讓人犯迷糊,但在使用Docker之前你至少應該瞭解他的三大核心元件:倉庫、映象和容器,以及他們之前的關係。本文將通過一個MySQL示例帶你瞭解並使用Docker,待你對Docker有一個基本瞭解後你再回頭去看他的體系架構會容易理解。

三大核心元件

倉庫:倉庫是集中儲存映象的地方,我們本地安裝Docker之後,需要從倉庫中拉取映象。可以類比於Maven,有公有倉庫和私有倉庫之分。

映象:是一個Linux的檔案系統,它裡面存放著可以再Linux核心中執行的程式及其資料。

容器:是映象建立的執行例項,可以把它理解為一個精簡版的Linux系統,裡面執行著映象裡的程式。

為了更好的讓你理解這三者的關係,我打一個不恰當但很形象的比方,映象就相當於你weixin.exe檔案,容器相當於你安裝好的微信程式,微信程式(容器)需要你的weixin.exe檔案(映象)來安裝(建立),那麼倉庫就相當於應用商店了,你可以從商店下載你要的.exe檔案(映象)

微信程式安裝完成後你可以選擇執行或者關閉,Docker容器一樣可以執行和停止,微信程式你可以從系統解除安裝,Docker容器你同樣可以選擇刪除。

但有一點不同的地方是,weixin.exe檔案安裝完成你就可以刪除了,它和你的微信程式並沒有關係,刪掉安裝檔案不影響你微信程式的執行。但是映象不同,如果有容器正在使用這個映象,那麼這個映象是不能刪除的(刪除時會報Error不讓你刪)。

首發地址:https://www.guitu18.com/post/2020/01/20/66.html

安裝Docker

CentOS安裝Docker要求:

  1. 必須是64位作業系統
  2. 核心版本在3.8以上

你可以通過uname -r檢視你的系統核心:

[root@localhost ~]# uname -r
3.10.0-1062.18.1.el7.x86_64
[root@localhost ~]# 

yum方式安裝:

yum install docker -y

安裝完成你可以通過docker version檢視你的docker版本資訊:

[root@localhost ~]# docker version
Client:
 Version:         1.13.1
 API version:     1.26
 Package version: docker-1.13.1-109.gitcccb291.el7.centos.x86_64
 Go version:      go1.10.3
 Git commit:      cccb291/1.13.1
 Built:           Tue Mar  3 17:21:24 2020
 OS/Arch:         linux/amd64

Server:
 Version:         1.13.1
 API version:     1.26 (minimum version 1.12)
 Package version: docker-1.13.1-109.gitcccb291.el7.centos.x86_64
 Go version:      go1.10.3
 Git commit:      cccb291/1.13.1
 Built:           Tue Mar  3 17:21:24 2020
 OS/Arch:         linux/amd64
 Experimental:    false
 [root@localhost ~]# 

看到如上的資訊說明你的Docker安裝成功,你可以用 docker info 命令檢視更詳細的資訊。

配置映象加速

為了更愉快的使用Docker你可能還需要配置映象加速,可以類比於Maven的私服,使用國內的映象倉庫能讓你更快的拉取映象。

執行vim /etc/docker/daemon.json,修改為如下配置:

{
    "registry-mirrors":[
        "https://reg-mirror.qiniu.com/",
        "https://hub-mirror.c.163.com/"
    ]
}

重新載入配置及重啟Docker服務:

systemctl daemon-reload
systemctl restart docker

執行docker info你可以看到映象倉庫配置已經生效了。

拉取映象

Docker安裝和配置都搞定了,現在你要從蒼鷺下載映象了,這裡以 MySQL 5.7 為例:

# 5.7為版本號,你也可以安裝其他版本
docker pull mysql:5.7

拉取MySQL5.7映象

拉取成功後通過docker images命令檢視本地映象:

[root@localhost ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker.io/mysql     5.7                 f965319e89de        3 hours ago         448 MB
[root@localhost ~]# 

建立容器

有了映象,你需要用它建立一個容器才能執行,建立並執行MySQL容器:

docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

命令引數說明(後面會有更加詳細的說明):
-d:後臺執行
-p:埠對映,前面的為宿主機埠,後面的為容器埠,這裡我將宿主機的3306埠指向了容器的3306埠
--name:為啟動的容器命名
-e:指定容器內的環境變數,這裡指配置MySQL的Root使用者密碼為:123456

執行成功後會返回容器ID,檢視已建立的容器:docker ps -a

[root@localhost ~]# docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
8e1cd060075db23c61cb31cecb3a3321df92cf56ea7086476cc21e8709382d19
[root@localhost ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
8e1cd060075d        mysql:5.7           "docker-entrypoint..."   3 seconds ago       Up 1 second         0.0.0.0:3306->3306/tcp, 33060/tcp   mysql
[root@localhost ~]# 

可以看到剛才建立的MySQL容器已經在執行了,現在你可以通過${IP}:3306連線MySQL資料庫了(記得放行埠或者關閉防火牆)。

掛載目錄

通過上面的步驟,你已經通過Docker執行起你的第一個容器MySQL了,而且你也能通過宿主機的埠連線到容器中的MySQL。但這樣做還不是很安全,關於這個我們要先簡單瞭解一下容器和宿主機之間的關係。

容器內和宿主機的檔案系統是獨立的(雖然整個容器也是以檔案的形式存放在宿主機的某個目錄上的),包括他們之間的網路,也是獨立的。剛才執行的MySQL容器有一個 -p 3306:3306 的引數,這個引數就是對映宿主機和容器之間的埠的,你也可以配置成比如 -p 1234:3306 ,這樣你通過訪問宿主機的1234埠就能訪問到容器的3306埠。

那麼再回到檔案系統,容器本身也是一個精簡版的Linux系統,只不過他執行在宿主機上依賴於宿主機的硬體。容器內部也是有著一套獨立的檔案系統的,且隨著容器的刪除,所有存在於容器內的所有檔案都將被清除。剛才我們建立的那個MySQL容器,只要我們刪除容器,資料庫裡的所有資料都將清除,這顯然不是我們想看到的。

Docker的run命令提供了一個-v的引數,允許我們容器內部目錄掛載為宿主機的本地目錄,這樣容器內的程式在操作這個目錄時,其實操作的是宿主機的目錄。那麼我們可以把程式執行的關鍵資料掛載到宿主機上的目錄,比如MySQL的資料庫檔案,程式執行的日誌檔案等等。這樣一來當我們在刪除容器時,因為這些目錄是存在於宿主機的,所以不會隨著容器一起刪除,從而實現了容器資料的持久化。

還是以剛才的MySQL容器為例,我們先刪掉剛才的容器:

# mysql為容器名稱,也可以是容器ID,通過 docker ps -a 檢視容器資訊
docker stop mysql
docker rm mysql

接著用下面的命令建立並執行MySQL容器,增加了一個 -v 引數:

# 在宿主機建立掛載目錄
mkdir -p /usr/local/mysql/conf
mkdir -p /usr/local/mysql/logs
mkdir -p /usr/local/mysql/data
# 建立並執行容器
docker run -p 3306:3306 --name mysql \
-v /usr/local/mysql/conf:/etc/mysql \
-v /usr/local/mysql/logs:/var/log/mysql \
-v /usr/local/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7

-v /usr/local/mysql/data:/var/lib/mysql

表示將宿主機的 /usr/local/mysql/data 目錄掛載到容器內的 /var/lib/mysql 目錄,那麼容器內的MySQL程式操作的資料實際上是寫入了宿主機的 /usr/local/mysql/data 目錄了,其他兩項同理。

這裡掛載的三個目錄分別為資料庫執行的配置、日誌和資料,也是MySQL程式最重要的資料,即便這個容器刪除了,只要這些資料還在,我們就能通過重新建立容器恢復全部資料。

而且這樣掛載以後,我們無需進入容器,直接在宿主機操作這幾個目錄裡的檔案就能同步體現到容器內部,比如修改一些配置,匯出資料之類的,不用進入容器直接在宿主機操作即可。

用以上命令執行容器之後,在你的宿主機的/usr/local/mysql/data 目錄就能看到MySQL執行生成的資料庫檔案了:

容器內MySQL執行後生成的資料檔案,已經掛載到宿主機上了

Docker的資料卷掛載(我習慣稱之為掛載目錄)功能非常重要,我們執行的任何程式都有需要持久化的資料,如果這些資料直接放在容器內部是非常不安全的。而且掛載目錄還可以實現直接在宿主機操作容器內的資料,也能做到容器間的資料共享,用Docker一定要養成掛載重要資料到宿主機的習慣。

錯誤排查

上面這個命令是直接後臺執行容器的,如果需要除錯可以把 -d 引數修改為 -it 引數以在前臺執行,在某些情況下你很可能會遇到類似於下面這些錯誤(可以通過前臺執行檢視到):

chown: changing ownership of '/var/lib/mysql/': Permission denied
# 或者
mysqld: Can't create/write to file '/var/lib/mysql/is_writable' (Errcode: 13 - Permission denied)

如果出現上述問題,那麼你需要關閉SELINUX,方法如下:

  1. 臨時關閉:setenforce 0

  2. 永久關閉:vim /etc/selinux/config,修改 SELINUX 的值為 disabled 然後重啟機器即可,看圖:

    修改SELINUX配置

再次執行容器就能看到成功提示了。

常用命令

通過上面的示例基本已經知道了Docker是怎樣工作的,下面是一些基本命令,包括最常用的目錄掛載功能等命令說明:

docker version|info
# 顯示Docker資訊,常用於檢視Docker版本或檢測Docker是否正確安裝。

docker images
# 列出機器上的映象(images)及資訊:REPOSITORY、TAG、IMAGE ID、CREATED、SIZE。
# IMAGE ID列其實是縮寫,要顯示完整則帶上--no-trunc選項。
# -a 列出本地所有的映象(含中間映像層,預設情況下,過濾掉中間映像層)
# -no-trunc 顯示完整的映象資訊
# -q 靜默模式,只顯示映象ID

docker search tomcat
# 在docker index中搜尋image,搜尋的範圍是官方映象和所有個人公共映象。NAME列的 / 後面是倉庫的名字。

docker pull tomcat
# 從docker registry server 中下拉image或repository。
# 語法:docker pull [OPTIONS] NAME[:TAG|@DIGEST]
# -a 拉取所有 tagged 映象
# --disable-content-trust 忽略映象的校驗,預設開啟
# 上面的命令沒有指定引數,在docker v1.2版本及以前,會下載官方映象的tomcat倉庫裡的所有映象
# 而從v.13開始只會下載tag為latest的映象,也可以明確指定具體的映象
# 如:docker pull tomcat:8,後面的8為tomcat版本tag。

docker run -d --name tomcat -p 8081:8080 tomcat:8
# 啟動容器,語法為:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# -d 後臺執行容器,並返回容器ID,不使用-d我們會看到tomcat啟動日誌,此時Ctrl+C容器會停止執行。
# -e 指定容器內的環境變數
# -name 指定容器的名稱,如果不指定docker會幫我們取一個名字。
# -p 埠對映,宿主機埠:容器埠,上面我們就將宿主機的8081對映到容器的8080埠
# 	此時我們訪問宿主機的ip:8081就能訪問容器內的tomcat了。
# -i 以互動模式執行容器,通常與 -t 同時使用。
# -t 為容器重新分配一個偽輸入終端,通常與 -i 同時使用
# -v 目錄掛載,本地目錄:容器目錄

docker ps
# 檢視容器的資訊,預設顯示當前正在執行中的容器
# -a 檢視所有容器
# -l 顯示最新啟動的一個容器(包括已停止的)

docker start|stop|restart CONTAINER_ID
# 啟動/停止/重啟容器,需要用到容器ID,可以使用docker ps -a 檢視所有容器的ID。

docker attach CONTAINER_ID|NAME
# 進入容器,後面跟容器ID或者NANE,可以使用docker ps -a 檢視所有容器的ID。
# 可以認為這是一個過時的命令,更多的docker使用者會考慮使用docker exec來實現相同的功能
# 但是出於docker官方並沒有刪除這個命令,我們還是有必要學習一下的。
# 進入容器後輸入exit命令可以退出,同時容器停止執行,如若想退出但不停止容器,可以用快捷鍵Ctrl+P+Q退出。

docker exec -i -t CONTAINER_ID|NAME /bin/bash
# 進入容器,推薦使用這種方式。
# 語法:docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
# -i 以互動模式執行容器,通常與 -t 同時使用。
# -t 為容器重新分配一個偽輸入終端,通常與 -i 同時使用
# 示例:docker exec -it mycentos /bin/sh /root/start.sh
# 上面這條命令表示在容器 mycentos 中以互動模式執行容器內 /root/start.sh 指令碼

docker rm CONTAINER_ID|NAME
# 刪除一個或多少容器,如:docker rm $(docker ps -a -q),為刪除所有停止的容器
# -l 移除容器間的網路連線,而非容器本身
# -v 刪除與容器關聯的卷
# -f 通過SIGKILL訊號強制刪除一個執行中的容器

docker rmi CONTAINER_ID|NAME
# 刪除本地一個或多少映象
# -f 強制刪除;
# --no-prune 不移除該映象的過程映象,預設移除;

docker build
# 使用 Dockerfile 建立映象,語法:docker build [OPTIONS] PATH | URL | -
# -f 指定要使用的Dockerfile路徑
#  -t,--tag 指定映象的名字及標籤:name:tag或者name,可以在一次構建中為一個映象設定多個標籤

Dockerfile

Dockerfile 是一個用來構建映象的文字檔案,文字內容包含了一條條構建映象所需的指令和說明。

初學者一開始可不必關注Dockerfile,待你熟悉Docker的整個體系結構及其執行方式後,再回頭看這個就會一目瞭然了。

FROM
# 指定基礎映象,必須為第一個命令
    FROM <image>
    FROM <image>:<tag>
    FROM <image>@<digest>
# 示例
    FROM docker.io/centos:latest
MAINTAINER
# 維護者資訊
    MAINTAINER <name>
# 示例
    MAINTAINER guitu "xianjian-mail@qq.com"
RUN
# 用於在映象容器中執行命令,其有以下兩種命令執行方式
#shell執行
    RUN <command>
#exec執行
    RUN ["executable", "param1", "param2"]
#示例
    RUN apk update
    RUN ["executable", "param1", "param2"]
    RUN ["/etc/execfile", "arg1", "arg1"]
# RUN指令建立的中間映象會被快取,並會在下次構建中使用。
# 如果不想使用這些快取映象,可以在構建時指定--no-cache引數,如:docker build --no-cache
ADD 
# 將當前目錄下的檔案複製到容器中,tar型別檔案會自動解壓(網路壓縮資源不會被解壓),可以訪問網路資源,類似wget
    ADD <src>... <dest>
    ADD ["<src>",... "<dest>"]
    # 第二中形式用於支援包含空格的路徑
# 示例
    ADD hom* /mydir/          # 新增所有以"hom"開頭的檔案
    ADD hom?.txt /mydir/      # ? 替代一個單字元,例如:"home.txt"
    ADD test relativeDir/     # 新增 "test" 到 `WORKDIR`/relativeDir/
    ADD test /absoluteDir/    # 新增 "test" 到 /absoluteDir/
# <src>可以是Dockerfile所在目錄的一個相對路徑,也可以是一個URL,還可以是一個tar檔案(會自動解壓為目錄)
# 如果檔案或目錄不與Dockerfile在同一目錄會提示 no such file or directory
COPY
# 功能類似ADD,但是是不會自動解壓檔案,也不能訪問網路資源
CMD
# 構建容器後呼叫,也就是在容器啟動時才進行呼叫。
    CMD ["executable","param1","param2"] (執行可執行檔案,優先)
    CMD ["param1","param2"] (設定了ENTRYPOINT,則直接呼叫ENTRYPOINT新增引數)
    CMD command param1 param2 (執行shell內部命令)
# 示例
    CMD echo "This is a test." | wc -
    CMD ["/usr/bin/wc","--help"]
# CMD不同於RUN,CMD用於指定在容器啟動時所要執行的命令,而RUN用於指定映象構建時所要執行的命令。
ENTRYPOINT
# 配置容器,使其可執行化。配合CMD可省去"application",只使用引數。
    ENTRYPOINT ["executable", "param1", "param2"] (可執行檔案, 優先)
    ENTRYPOINT command param1 param2 (shell內部命令)
#示例
    FROM ubuntu
    ENTRYPOINT ["top", "-b"]
    CMD ["-c"]
# ENTRYPOINT與CMD非常類似,不同的是通過docker run執行的命令不會覆蓋ENTRYPOINT,而docker run命令中指定的任何引數,都會被當做引數再次傳遞給ENTRYPOINT。
# Dockerfile中只允許有一個ENTRYPOINT命令,多指定時會覆蓋前面的設定,而只執行最後的ENTRYPOINT指令。
LABEL
# 用於為映象新增後設資料
    LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 示例
    LABEL version="1.0" description="這是一個Web伺服器" by="IT筆錄"
# 使用LABEL指定後設資料時,一條LABEL指定可以指定一或多條後設資料,指定多條後設資料時不同後設資料之間通過空格分隔。
# 推薦將所有的後設資料通過一條LABEL指令指定,以免生成過多的中間映象。
ENV
# 設定環境變數
    ENV <key> <value>  
    # <key>之後的所有內容均會被視為其<value>的組成部分,因此,一次只能設定一個變數
    ENV <key>=<value> ...  
    # 可以設定多個變數,每個變數為一個"<key>=<value>"的鍵值對
    # 如果<key>中包含空格,可以使用\來進行轉義,也可以通過""來進行標示;另外,反斜線也可以用於續行
# 示例
    ENV JAVA_HOME /docker/jdk
VOLUME
# 用於指定持久化目錄
    VOLUME ["/path/to/dir"]
# 示例
    VOLUME ["/data"]
    VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
# 一個卷可以存在於一個或多個容器的指定目錄,該目錄可以繞過聯合檔案系統,並具有以下功能:
    # 卷可以容器間共享和重用
    # 容器並不一定要和其它容器共享卷
    # 修改卷後會立即生效
    # 對卷的修改不會對映象產生影響
    # 卷會一直存在,直到沒有任何容器在使用它
WORKDIR
# 工作目錄,類似於cd命令
    WORKDIR /path/to/workdir
# 示例
    WORKDIR /a  (這時工作目錄為/a)
    WORKDIR b  (這時工作目錄為/a/b)
    WORKDIR c  (這時工作目錄為/a/b/c)
# 通過WORKDIR設定工作目錄後,Dockerfile中其後的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都會在該目錄下執行。
# 在使用docker run執行容器時,可以通過-w引數覆蓋構建時所設定的工作目錄。
USER
# 指定執行容器時的使用者名稱或 UID,後續的 RUN 也會使用指定使用者。
# 使用USER指定使用者時,可以使用使用者名稱、UID或GID,或是兩者的組合。
# 當服務不需要管理員許可權時,可以通過該命令指定執行使用者。並且可以在之前建立所需要的使用者
  USER user
  USER user:group
  USER uid
  USER uid:gid
  USER user:gid
  USER uid:group
# 示例
  USER www
# 使用USER指定使用者後,Dockerfile中其後的命令RUN、CMD、ENTRYPOINT都將使用該使用者。
# 映象構建完成後,通過docker run執行容器時,可以通過-u引數來覆蓋所指定的使用者。
ARG
# 用於指定傳遞給構建執行時的變數
    ARG <name>=[<default value>]
# 示例
    ARG site
    ARG build_user=www
ONBUILD
# 用於設定映象觸發器
  ONBUILD [INSTRUCTION]
# 示例
  ONBUILD ADD . /app/src
  ONBUILD RUN /usr/local/bin/python-build --dir /app/src
# 當所構建的映象被用做其它映象的基礎映象,該映象中的觸發器將會被鑰觸發

構建映象

瞭解了上面的命令之後,我們可以嘗試著建立我的第一個自制映象了,以下是我的Dockerfile示例:

#使用的基礎映象
FROM docker.io/centos:latest
#作者資訊
MAINTAINER guitu "xianjian-mail@qq.com"
#安裝SVN
RUN yum install -y subversion
#新增JAVA環境,下方的檔案請換成你Dockerfile目錄下的檔案,壓縮包在構建映象時會自動解壓
ADD jdk-8u231-linux-x64.tar.gz /docker/
ADD apache-tomcat-8.0.53.tar.gz /docker/
#新增環境變數
ENV JAVA_HOME /docker/jdk1.8.0_231
ENV TOMCAT_HOME /docker/apache-tomcat-8.0.53
ENV PATH $PATH:$JAVA_HOME/bin:$TOMCAT_HOME/bin
#指定工作目錄
WORKDIR /docker/apache-tomcat-8.0.53
#暴露8080埠
EXPOSE 8080
#啟動時執行tomcat
CMD ["bin/startup.sh && tail -f logs/catalina.out"]

通過Dockerfile建立映象:

docker build -t mytomcat8:v0.1 .

通過Dockerfile檔案構建映象完成。

相關文章