Docker虛擬化管理:30分鐘教你學會用Docker
關於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不讓你刪)。
首發地址://www.guitu18.com/post/2020/01/20/66.html
安裝Docker
CentOS安裝Docker要求:
- 必須是64位操作系統
- 內核版本在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":[
"//reg-mirror.qiniu.com/",
"//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
拉取成功後通過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運行生成的數據庫文件了:
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
,方法如下:
-
臨時關閉:
setenforce 0
-
永久關閉:
vim /etc/selinux/config
,修改SELINUX
的值為disabled
然後重啟機器即可,看圖:
再次運行容器就能看到成功提示了。
常用命令
通過上面的示例基本已經知道了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文件構建鏡像完成。