Docker基礎教程

1. Docker容器介紹

Docker是一個開源的容器引擎,可讓開發者把他的應用和依賴環境打包到一個可移植的容器環境中。php

容器: 能夠理解爲一個輕量級的「虛擬機」,應用程序的運行環境。html

image

Docker的特色:python

  • 應用隔離
  • 輕量級的虛擬化方案
  • 擴展性,能夠輕鬆擴展出成千上萬的容器實例。
  • 移植性,統一開發、測試、生產環境,能夠在任意環境運行容器實例。

2. 容器與虛擬機的區別

虛擬機和容器的架構對比:mysql

image

經過對比容器和虛擬機的架構圖,虛擬機擁有獨佔的操做系統,虛擬化比較完全,容器是共享操做系統經過資源隔離使得容器擁有獨立的環境;虛擬機和容器都共享硬件資源。linux

所以容器比較「輕」,啓動一個容器可能就是一個進程,能夠秒級啓動容器。nginx

3. Docker架構

image

主要包含下面幾個部分:c++

  • Docker守護進程 (Docker daemon)
    負責管理鏡像、容器、容器網絡、數據卷等。
  • Client
    負責發送Docker操做指令, 平常主要經過client完成鏡像和容器的管理。
  • 鏡像 (Image)
    即容器的模版,鏡像是能夠繼承的,鏡像主要經過Dockerfile 文件定義。
  • 鏡像倉庫 (Registry)
    相似git倉庫, 只不過鏡像倉庫用於存儲鏡像和管理鏡像的版本。
  • 容器
    容器是經過鏡像建立的,因此說容器是一個鏡像運行的實例,相似面向對象編程中類和對象的關係。

4. 安裝docker

下面以centos安裝docker ce社區版本爲例:git

//先安裝一些依賴的驅動和配置yum
$ yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2
  
$ yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

//安裝docker
$ yum install docker-ce docker-ce-cli containerd.io

//啓動docker
$ systemctl start docker

//測試下是否安裝成功, 這裏運行一個hello-world鏡像,正常的話會輸出 Hello from Docker!
$ docker run hello-world

 

5. 容器基本用法

5.1. 快速入門

下面經過打包一個靜態網站講解容器的基本用法。
首先對於一個靜態網站,咱們須要什麼樣的環境才能跑起來?github

對於靜態網站,咱們只須要一個Nginx軟件就能跑起來,下面一步步講解怎麼經過容器部署這個網站。
下面是網站的項目目錄結構:redis

├── Dockerfile      // Docker鏡像定義文件 └── site //網站代碼目錄,只是一個簡單的靜態網站,只有一個頁面 └── index.html 

5.1.1. 定義鏡像

建立鏡像,咱們首先須要定一個鏡像,定義鏡像的目的說白了就是咱們要在容器裏面安裝什麼東西

下面是Dockerfile的定義:'#' 井號表示註釋.

#FROM 指令表示要繼承的鏡像,這裏繼承nginx官方鏡像,版本號是1.15.6
#定義鏡像,都是經過繼承某個已經存在的鏡像開始
FROM nginx:1.15.6

#設置工做目錄
WORKDIR /workspace

#/usr/share/nginx/html目錄是nginx官方鏡像的默認網站目錄。
#複製site目錄的內容, 到/usr/share/nginx/html目錄。
COPY ./site/* /usr/share/nginx/html

#刪除掉/workspace的內容,由於上面的指令已經把網站內容複製到指定的目錄,這裏的內容如今沒用了
RUN set -ex \
    && rm -rf /workspace
    
#重新設置工做空間
WORKDIR /usr/share/nginx/html

#聲明容器須要暴露的端口
#EXPOSE 80
#容器啓動命令,這裏啓動nginx, 參數-g daemon off; 的意思是關閉nginx的守護進程模式。
#CMD ["nginx", "-g", "daemon off;"]

# EXPOSE 和 CMD命令都被註釋掉了,由於nginx基礎鏡像已經定義過了,因此這裏不須要重複定義,這裏只是爲了講解目的,說明下容器是怎麼運行nginx的。

 

5.1.2. 建立鏡像

下面咱們經過docker客戶端建立鏡像。

//先切換到項目的根目錄
//經過docker build命令建立鏡像
//格式: docker build -t 鏡像名:版本號 上下文路徑
//上下文路徑的意思就是咱們須要把那個目錄上傳到docker守護進程做爲鏡像建立的工做目錄。
//這裏把當前目錄做爲上下文環境目錄
$ docker build -t demo1:v1.0.0 .

執行成功會輸出以下結果:
Sending build context to Docker daemon  3.584kB     //先打包上傳上下文目錄內容
Step 1/3 : FROM nginx:1.15.6
1.15.6: Pulling from library/nginx          //下載nginx鏡像
a5a6f2f73cd8: Already exists 
67da5fbcb7a0: Pull complete 
e82455fa5628: Pull complete 
Digest: sha256:31b8e90a349d1fce7621f5a5a08e4fc519b634f7d3feb09d53fac9b12aa4d991
Status: Downloaded newer image for nginx:1.15.6
 ---> e81eb098537d      //下面開始執行咱們Dockerfile定義的命令
Step 2/3 : WORKDIR /usr/share/nginx/html
 ---> Running in 155da3e24b72
Removing intermediate container 155da3e24b72
 ---> 608d1e7fc6cd
Step 3/3 : COPY --chown=nginx:nginx . /usr/share/nginx/html
 ---> 894f22f15a7c
Successfully built 894f22f15a7c
Successfully tagged demo1:v1.0.0

//查看當前機器上的鏡像,這個時候就看到剛纔建立的demo1鏡像
$ docker image ls
REPOSITORY                                            TAG                  IMAGE ID            CREATED             SIZE
demo1                                                 v1.0.0               894f22f15a7c        11 minutes ago      109MB

 

5.1.3. 啓動容器/運行網站

啓動容器的過程是: 根據鏡像啓動容器 -> 容器根據Dockerfile配置CMD命令啓動咱們應用。
下面啓動demo1鏡像,docker會建立一個容器運行nginx。

//經過docker run命令運行一個鏡像
//命令格式: docker run -p 容器綁定的外部端口:容器內部端口 鏡像:鏡像版本
$ docker run -p 8080:80 demo1:v1.0.0

 

經過瀏覽器訪問:http://localhost:8080 便可訪問網站

5.2. Dockfile詳解

下面介紹Dockfile經常使用的指令

5.2.1. FROM

FROM是Dockerfile 文件的第一條指令,標示要繼承的鏡像。

格式:
FROM image[:tag]

參數說明:
image : 鏡像名或者鏡像地址。
tag : 可選參數,鏡像版本或者叫鏡像標籤。

例子:

#這裏使用的是官方nginx鏡像,標籤爲:1.15.6
FROM nginx:1.15.6

#這裏使用的是完整的鏡像地址,標籤爲:v1.3.0
FROM XXXX:v1.3.0

 

5.2.2. COPY

複製指令,主要用於複製目錄和文件。
格式:
COPY [--chown=user:group] src dest

參數說明:
src : 待複製的文件或者目錄
dest : 複製文件的目的地
--chown=user:group : 可選參數,設置複製文件或者目錄的用戶組

例子:

#複製./site/目錄下面的全部文件,到/usr/share/nginx/html目錄,同時設置文件的用戶和組爲nginx
COPY --chown=nginx:nginx ./site/* /usr/share/nginx/html

 

5.2.3. RUN

用於執行命令
格式:
RUN command

只有一個參數command, 就是咱們想要執行的命令。

5.2.4. CMD

用於設置容器默認執行的命令。
格式:
CMD ["executable","param1","param2"]

參數說明:
executable : 執行程序,能夠包含完整路徑
param1, param2 ...param N : 執行程序的任意參數

5.2.5. ENV

設置容器環境變量
格式:
ENV = ...

例子:

#用空格分隔鍵值對
ENV myName="John Doe" myCat="fluffy"

 

5.2.6. USER

用於設置執行RUN, CMD 和 ENTRYPOINT命令時候的以什麼用的身份運行。
格式:
USER user[:group]

參數說明:
user : 用戶名
group : 可選參數,用戶組

注意:若是設置的用戶不存在,則以root用戶身份運行。 例子:

#設置執行RUN、CMD、ENTRYPOINT的時候用戶和組爲www
USER www:www

 

5.2.7. WORKDIR

用於爲RUN、CMD、ENTRYPOINT、COPY命令設置工做目錄。若是設置的工做目錄不存在,則自動建立一個。

例子:

WORKDIR /path/to/workdir

 

5.3. .dockerignore文件

docker build命令在發送上下文目錄的文件到docker守護進程以前,能夠經過.dockerignore配置文件,設置須要過濾的文件或者目錄。

.dockerignore文件保存在上下文目錄的根目錄。

.dockerignore例子:

.dockerignore配置語法相似.gitignore

# comment
#忽略全部目錄下面的.log後綴的文件
**/*.log
#忽略data目錄
/data

#忽略全部的.md結尾的文件,除了README.md
*.md
!README.md

 

6. 鏡像管理

鏡像管理問題:

  • 若是咱們爲不一樣的項目建立了多個鏡像,每一個鏡像又有多個版本怎麼管理它們?
  • 在服務器集羣中要根據不一樣的鏡像建立容器,去哪裏找到鏡像呢?

爲管理鏡像咱們須要一個相似github的鏡像倉庫,Docker官方爲咱們提供了一個公開的鏡像倉庫。 官方倉庫地址:https://hub.docker.com/, 在上面能夠找到各類各樣的鏡像,相似nginx、redis、mysql這些知名的開源軟件官方也到這裏發佈了本身的官方鏡像。

提示:知名開源軟件提供的官方鏡像會有Official Image標記, 這些鏡像會比較可靠,其餘的鏡像都是網友上傳的。

下載鏡像不須要帳號密碼,若是要發佈本身的鏡像就須要註冊賬號。

對於我的使用公開鏡像固然沒問題,可是對於公司就不行,對於公司要麼本身搭建一個私有的鏡像倉庫,要麼使用阿里雲這種雲平臺提供的鏡像服務,建立一個私有倉庫。

不管是本身搭建的鏡像倉庫仍是使用公開的鏡像倉庫用法是同樣的。

鏡像倉庫的用法相似git, 下面是鏡像倉庫的基本用法:

#登錄倉庫命令:
#公開倉庫不須要登陸
$ docker login --username=賬號 倉庫地址

#例子:
#登錄阿里雲容器服務倉庫
#回車後輸入密碼便可
docker login --username=123456@qq.com XXX

#下載鏡像, 命令格式
docker pull 鏡像:[鏡像版本號]

#例子1:
#下載nginx最新鏡像
docker pull nginx

#下載阿里雲鏡像倉庫的鏡像,鏡像版本號爲:v1.0.0
docker pull XXX:v1.0.0

#上傳鏡像,命令格式
docker push 鏡像:[鏡像版本號]

#例子:
#推送鏡像,以前docker login命令登錄到什麼倉庫,就推送到那裏。
docker push XXX:v1.0.0

 

7. 容器數據管理

7.1. 鏡像分層技術

一個Docker鏡像是有多個layer(層)組成的;每一層都存儲一些文件數據,每一層都是在另外一層的基礎上進行CRUD操做,每一層只是記錄相對前面幾層的差別部分的文件數據,Docker鏡像分層技術,很像git代碼管理,git記錄每一次代碼文件的變化歷史,每一次提交的版本都是上一個版本的差別部分。

注意:鏡像每一層的文件數據都是隻讀的。

當建立一個容器的時候,會在頂層增長一層可寫的容器層,咱們容器的數據都是寫在這上面。

容器層的數據不是持久化的,刪除容器數據就丟了。

例子:
Dockerfile定義以下

FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py

 

dockerfile由4條指令構成,建立鏡像後,分層示意圖以下:

image

從底層往上看,最底下的一層保存dockerfile的FROM指令運行後產生的數據,倒數第二層對應COPY指令複製文件產生的數據,以此類推。

你們能夠看到每一層都一個ID,例如最底下一層的id爲: d3a1f33e8a5a, 很像git的提交記錄吧。

當根據鏡像建立容器的時候會在頂層增長一層容器層(Container layer), 保存容器運行的數據。

提示:docker能夠分層緩存數據,若是本地鏡像已經下載過對應層的數據,那麼docker不會重複下載對應的層;因此有些鏡像看起來幾百M,可是瞬間就下載完成,緣由是分層緩存對應的數據。

下圖是根據同一個鏡像建立多個容器的狀況:
每個容器都有本身單獨的容器層(Container layer),容器之間數據是獨立的;可是他們底層的鏡像數據是同樣的。
image

7.2. 容器文件數據管理

在Docker容器裏面寫文件數據,這些數據不是持久化的,隨着容器被刪除數據將會丟失,下面是Docker提供的容器文件數據管理方式。

主要支持下面三種方式管理文件數據:

  • Volumes
    共享服務器上的某個目錄,可是這個共享目錄限制在/var/lib/docker/volumes/目錄下面,由docker維護。
  • Bind mounts
    共享服務器上的文件或者目錄,相對於Volumes區別就是不限制目錄,能夠是系統任何位置。
  • tmpfs
    這種方式文件數據保存在服務器內存中。

提示:不管使用那種方式管理容器文件數據,對容器內部來講,看起來跟本地文件系統同樣。區別就是真實數據保存在什麼地方。

容器管理文件數據示意圖:

image

7.2.1. Volumes方式

使用Volumes方式管理文件數據須要先建立volume數據卷,而後掛載到容器裏面。

例子:

//建立數據卷 demo1-data
$ docker volume create demo1-data

//查看當前機器建立的數據卷
$ docker volume ls
#輸出
DRIVER              VOLUME NAME
local               demo1-data

//查看數據卷詳情, 能夠查看數據具體保存在什麼地方
$ docker volume inspect demo1-data
#輸出
[
    {
        "CreatedAt": "2019-04-18T15:59:58+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/demo1-data/_data",
        "Name": "demo1-data",
        "Options": {},
        "Scope": "local"
    }
]

//刪除數據卷
$ docker volume rm demo1-data

//將建立好的數據卷掛載到容器裏面
$ docker run -d --name demo1 -v demo1-data:/mnt/data demo1:v1.0.0
參數說明:
-d : 讓容器在後臺運行.
--name : 給容器起個名字.
-v  : 掛載數據卷, 格式: -v 數據卷名字:容器目錄
demo1:v1.0.0 : 鏡像的名字

 

7.2.2. Bind mounts方式

任意目錄或者文件掛載方式.
例子:

//通常咱們本地開發啓動nginx,配置文件都是在本地,咱們但願容器能夠讀取本地的nginx配置。
//這個例子是把主機上的nginx配置文件掛載到nginx容器的配置文件裏面, 這裏掛載的是文件。
$ docker run --name my-custom-nginx-container -v /host/path/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx
參數說明:
--name my-custom-nginx-container : 容器名字
-v /host/path/nginx.conf:/etc/nginx/nginx.conf:ro : -v 掛載參數,將主機上的/host/path/nginx.conf文件掛載到容器的/etc/nginx/nginx.conf文件,ro 參數只讀的意思。

//除了上面的掛載文件,咱們也能夠掛載目錄, 接着上面的例子
$ docker run --name my-custom-nginx-container -v /host/path/nginx/conf:/etc/nginx/:ro -d nginx
參數說明:
-v /host/path/nginx/conf:/etc/nginx/:ro : 這裏把主機的/host/path/nginx/conf目錄以只讀方式掛載到容器的/etc/nginx/目錄。

 

7.2.3. tmpfs方式

tmpfs主要是將數據存儲在主機內存中。
例子:

$ docker run -d --name demo1 --tmpfs /mnt/data demo1:v1.0.0
參數說明:
--tmpfs /mnt/data : 掛載主機內存空間到容器的/mnt/data目錄

 

8. 容器互相通訊

在實際應用的時候咱們每每會啓動多個容器,分別運行不一樣應用,又或者爲了負載均衡啓動大量的容器實例;那麼容器之間怎麼通訊?

容器之間的通訊方式跟服務器之間的通訊方式差很少,除此以外在同一臺主機的容器之間Docker支持直接互相鏈接的通訊方式。
容器之間的通訊方式大致上有兩種:

  • 基於tcp/ip的網絡通訊。
  • 同一臺主機上建立容器的時候,使用--link參數鏈接本地的其餘容器。

例子1: 同一臺主機上的容器之間經過link參數互聯.

//啓動一個redis server,容器名字叫: redis, redis其餘參數配置默認
$ docker run -d --name redis 

//另外啓動一個容器,鏈接redis容器, 這樣在demo1容器內部可使用redis名字當成redis服務器地址
$ docker run -d --name demo1 --link redis demo1:v1.0.0
參數說明:
--link :鏈接容器, 格式: --link 容器名 , 鏈接多個容器,可使用多個--link參數便可

 

例子2:主機怎麼訪問容器裏面的應用?
例如,本地開發測試,咱們使用容器運行應用,那麼容器外面的主機怎麼訪問?

容器須要暴露容器內部的端口給主機,主機才能訪問容器內部的應用。

//例如,咱們使用nginx容器部署了一個網站應用,nginx容器須要暴露80端口出來。
$ docker run --name myapp -p 8080:80 -d nginx
參數說明:
-p  : 綁定端口參數,格式 -p 主機端口:容器內部端口
 主機端口能夠任意選擇,只要沒被其餘程序佔用便可。 

主機端口不必定要跟容器內部端口一致, 只是咱們習慣用同樣的端口,便於理解。

9. Docker經常使用命令

//查看正在運行的容器
$ docker ps

//啓動容器  - 容器建立以後不須要再次使用docker run命令建立容器, 這個時候只要啓動容器便可。
//啓動容器名爲redis的容器
$ docker start redis

//關閉容器
$ docker stop redis

//刪除容器
$ docker rm redis

//容器的啓動、關閉、刪除除了使用容器名字以外,還可使用容器id
//例如:
$ docker stop f648f34beabb

//執行容器裏面的命令
$ docker exec -it redis bash
參數說明:
-it   : 打開一個交互的終端。
redis  : 容器的名字叫作redis
bash    : 執行容器裏面的bash命令
//上面這條命令的其實就是鏈接容器打開一個shell窗口,相似登陸服務器同樣。
// 命令格式:  docker exec 容器名 要執行的命令

//刪除鏡像
docker image rm 鏡像名

//給鏡像打標籤
docker tag 鏡像id  標籤
例子:
$ docker tag ImageId xxxx:v2.0.1

//清理未使用的鏡像
$ docker image prune

 

10. Docker實踐建議。

10.1. 儘可能減少鏡像大小和layer。

不安裝跟應用無關的軟件,清理掉沒有用的文件; 例如咱們在編譯安裝軟件的是最產生不少中間文件,安裝成功後能夠刪除這些數據。
由於Docker鏡像的層級是有限的,在Dockfile配置文件中FROM、COPY、RUN這些指令都會產生layer, 用的最多的主要是RUN指令,推薦把連續的多條RUN指令合併成一條

例子:

FROM centos

COPY . /app
WORKDIR /app
#安裝依賴包
#下面每一條RUN指令都會產生一個layer, 光是RUN指令就產生了6個layer
RUN yum -y install gcc automake autoconf libtool make gcc-c++
#編譯安裝
RUN ./configure
RUN make
RUN make install
#清理安裝文件
RUN rm -rf /app
#清理yum 緩存
RUN yum clean all

 

優化後的Dockfile

FROM centos

COPY . /app
WORKDIR /app

#經過&&把多條命令連成一條命令,每行末尾的反斜槓是爲了可讀性,一行書寫一條命令。
#這裏只會產生一個layer
RUN yum -y install gcc automake autoconf libtool make gcc-c++ \
    && ./configure \
    && make \
    && make install \
    && rm -rf /app \
    && yum clean all

 

10.2. 避免在容器裏面存儲數據

由於在實際生產環境中容器是常常被銷燬、重建的,多是由於應用異常容器退出了,也多是某臺服務器負載過高,容器被調度到別的服務器運行; 當容器被銷燬的時候存儲在容器的數據會丟失。

生產環境中存儲持久化數據通常有如下方案:

  • 經過數據卷存儲數據,通常能夠選擇把數據存儲在主機服務器上、也能夠存儲在分佈式文件系統上。
  • 經過外部數據庫、雲存儲、日誌服務等可持久化的存儲引擎或者第三方雲服務存儲數據。

10.3. 一個容器只跑一個應用

要保證容器的「輕」的特性,咱們通常不會把容器當成服務器來用,什麼東西都往裏面安裝。

例如:咱們常常會在一臺服務器上安裝nginx、php、mysql、redis,同時部署一堆應用系統。
若是在容器這麼幹,升級維護就變得複雜了,並且不容易擴展,若是要升級redis,或者升級某個應用系統就得你們一塊兒更新,並且還無法對裏面的單個應用進行擴容。

一個容器跑一個應用,能夠保證容器的輕量級特性,又相對獨立,便於單獨升級和擴容。

例如:一個PHP系統,須要nginx、 php fpm、 mysql、redis 那麼鏡像和容器能夠這麼設計。

鏡像製做狀況:

  • nginx + php-fpm + PHP系統源碼 製做一個鏡像。
  • redis 單獨一個鏡像。
  • mysql 單獨一個鏡像。

容器狀況:
根據上面的鏡像製做狀況,至少須要同時運行三個容器,上面的三個鏡像分別創一個容器。

10.4. 爲項目或者公司製做基礎鏡像

一個項目或者一個公司的基礎環境通常都是同樣的,沒有必要每次都從新制做基礎環境鏡像。
例如一個項目的基礎環境是: nginx + php-fpm + PHP擴展庫(curl、kafka、redis、swoole), 咱們只要先根據環境須要製做一個基礎鏡像,項目只要繼承這個基礎鏡像,而後設置項目本身的參數便可。

10.5. 容器應用主進程不能切換到後臺運行。

容器經過CMD設置應用的啓動命令,當容器啓動應用的主進程後,應用主進程的生命週期就跟容器捆綁了,主進程退出,容器就退出了。

這個地方跟咱們通常服務器部署應用不同, 通常在服務器部署應用都是把進程切換到後臺保持運行就能夠,容器部署應用不能切換到後臺運行,不然容器會任務應用結束了,容器本身就退出了。

建立容器的時候能夠經過-d參數把容器切換到後臺運行,例如: docker run -d --name redis redis

相關文章
相關標籤/搜索