# Docker入門
###### tags: `Container` `Docker`
## 簡介
Docker用比較熟知的東西來介紹的話,就是傳統虛擬機(VMware之類的)的概念。
跟傳統虛擬機的具體差別在於:
1. 虛擬機會基於原有的OS再安裝一個Guest OS,並且共同使用本機的硬體資源,所以使用會占用很多資源。
2. Docker是直接在原有的OS上形成一個「容器Container」,並將需要模擬的東西全都放在這裡面執行,不需要多加一個Guest OS吃硬體資源。
基於以上,同樣的裝置可能只跑10個虛擬機就會卡到往生,但可以跑數百甚至數千個Docker。
### Docker特點
1. 部署容易:
可以建立一個固定的映像檔重複使用,統一Docker Container中的內容,實現「標準化」。
2. 兼容性高:
Docker可以在各式各樣的平台上執行,所以很容易遷移到不同端點→可以在這邊開發,最後實用在那邊。
### Docker vs. Virtual Machine
| 特性 | Docker | Virtual Machine |
| -------- | -------- | -------- |
| 啟動時間 | 秒級 | 分鐘級 |
| 佔用空間 | MB | GB |
| 效能 | 接近原生 | 弱於原生 |
| 驅動數量 | 數千 | 數十 |
| 隔離級別 | 系統級 | 進程級 |
## Docker的組成
1. 映像檔(Image)
2. 容器(Container)
3. 倉庫(Repository)
> **倉庫註冊伺服器**中有若干**倉庫**,倉庫中又有若干**映像檔**。
> (Registry包著Repository然後包著Image)
### 映像檔 Image
* 用來**建立容器**
* 如同虛擬機要灌作業系統的iso檔
> 註:映像檔是<font color = "red">唯讀</font>的。
### 容器 Container
* 如同灌在虛擬機的**作業系統**的用途
* 彼此間**完全隔離,不互相干擾**
### 倉庫 Repository
* 存放映像檔的地方
* 分為<font color = "green">公有Public</font>跟<font color = "green">私有Private</font>
* 概念與Github相似,都是用push上傳、pull下載
* 最大的公有倉庫是[Docker Hub](https://hub.docker.com/)
## 映像檔 Image
映像檔是建立容器的必要條件。
需要用到的映像檔必須存在本機上;如果在本機端找不到,會從Repository中找尋並下載(預設是Docker Hub)。
### 取得映像檔
`docker pull 註冊伺服器位址/倉庫名稱:TAG`
若無特別指定註冊伺服器位址,則預設是從Docker Hub抓取映像檔。
### 列出已有映像檔
`docker images`可以列出本機現有的映像檔。

* REPOSITORY:倉庫名稱
* TAG:該映像檔的標籤
* IMAGE ID:該映像檔的ID(唯一)
* CREATED:下載映像檔的時間
* VIRTUAL SIZE:映像檔的大小
> 註:映像檔的ID是唯一的,如果看到重複的ID表示兩者是一樣的映像檔,彼此間以TAG區分。建立/下載映像檔時,若沒有特別寫明TAG,預設以lastest表示。
### 修改映像檔
先啟動,然後對它作想修改的事情後,輸入`exit`退出。
> [color=#FF0000] 啟動後,會出現root@ID,要記住這個ID才可以在之後commit時順利更新,不然就要用`docker image`自己找了。
接著用`docker commit`更新。
舉例:`docker commit -m "說明文字" -a "使用者資訊" ID 倉庫名稱:TAG`
> 前面有提到映像檔是<font color="red">唯讀</font>的,所以所謂的修改其實應該是指「增加功能」。
### 建立映像檔
要建立一個映像檔必須先有Dockerfile,因為裡面有建立時所需要用到的指令。
```bash
mkdir 資料夾名稱 #先創立一個資料夾
cd 資料夾名稱 #進入創立的資料夾
touch Dockerfile #建立Dockerfile
```
Dockerfile中,每一行都是一個指令。
建立好Dockerfile後,用編輯器打開來增修內容(`vi Dockerfile`或`vim Dockerfile`等)。
舉例:
```dockerfile
FROM 倉庫名稱:TAG #基底映像檔
MAINTAINER 維護者/開發者資訊
RUN apt-get -qq update #執行這個映像檔時要做的事情
RUN gem install sinatra
```
> 註:執行Dockerfile的期間,每一個指令都會建立一個容器,並做出對應的修改,等到全部執行完之後,會把過程中建立的容器清除。
好了之後用`docker build`建立映像檔。
舉例:`docker build -t="倉庫:TAG" 路徑`
* -t:增加TAG
* 路徑:用 .表示原本的路徑,也可以直接指定絕對路徑
### 上傳映像檔
`docker push 倉庫名稱`
如此可以把指定倉庫的映像檔全都推上註冊伺服器。
### 刪除映像檔
先用`docker rm`刪除依賴於欲刪除之映像檔的容器,再用`docker rmi 倉庫名稱`把本機端的指定倉庫刪除。
因為容器是基於映像檔建立的,所以有依賴性,不能直接刪掉映像檔。
### 小結
從修改、更新、上傳映像檔的功能來看,可以理解為何docker的擴充性很好。
## 容器 Container
### 啟動容器
`docker run -it ubuntu /bin/bash`
* -英文字母:有的沒的功能
-i:交互式操作(互動模式,反正就是可以用終端下指令)
-t:Docker分配一個虛擬終端至容器上
* ubuntu:映像檔名稱
* /bin/bash:命令。因為用-i需要有交互式Shell,因此用的是 /bin/bash
使用`docker run`的指令時,內部存在以下行為:
1. 檢查本地是否存在指定的映像檔,不存在就從公有倉庫下載
2. 利用映像檔建立並啟動一個容器
3. 分配一個檔案系統,並在唯讀的映像檔層外面掛載一層可讀寫層
4. 從宿主主機設定的網路橋界面中橋接一個虛擬埠到容器中去
5. 設定一個 ip 位址給容器執行使用者指定的應用程式
6. 執行完畢後容器被終止
(這一步也可以用`docker stop`自己來)
容器的核心為所執行的應用程式,所需要的資源都是應用程式執行所必需的。除此之外,並沒有其它的資源。可以在虛擬終端中利用 ps 或 top 來查看程式訊息。
### 後台執行容器
有時候容器不需要在前台一直運作,只要在後台就可以了,這時候只要加入`-d`參數就能達到。
`docker run -d 倉庫:TAG`
* -d:表示Daemonized(守護態),亦即在後台執行
如果需要查看相關資訊,可以用以下指令:
* `docker ps`列出容器資訊,包含<font color="brown">ID、基於什麼映像檔而成、命令、創建時間、現在狀態、Port、名稱</font>等。
* `docker log`可以看後臺執行的容器的輸出。
### 進入後台執行中的容器
有以下幾個方式:
* 命令
1. `docker exec`用法相當於`docker run`,不過是針對容器
如`docker exec -it 容器名稱 bash`就是以bash方式跟指定容器溝通。
2. `docker attach`是直接進入容器
可以多個窗口同時叫出同一個容器,但每個窗口會同步
(有一個窗口死掉的話,其他的也會同時死掉)
* 工具 - nsenter
具體使用方法[參考這裡](https://www.awaimai.com/737.html)。
### 終止容器
`docker stop 容器ID`可以停掉整個容器。
如果是只包含一個終端機的容器的話,輸入`exit`或按`ctrl+D`也會終止。
一般而言,被終止的容器用`docker ps`是看不到相關資訊的,如果還想看的話必須加上參數`-a`。
想要啟動停止的容器的話,可以使用`docker start`。
想要重新啟動正在運作的容器的話,可以使用`docker restart`。
### 匯出匯入
* 匯出
1. `docker export 容器ID > 指定檔名.附檔名`
這個指令會將<font color="red">容器**快照**</font>匯出,存成指定檔案。
> 快照:僅保存容器**當下**的狀態,不包含歷史訊息。
2. `docker save 指定檔名.附檔名 > 倉庫:TAG`
可以發現存的東西是映像檔的TAG,所以儲存最原始的、沒被改過的映像檔。
基本上這個對存容器沒什麼用,應該也不會用到...,畢竟映像檔可以直接從倉庫抓。
近年來幾乎不太用`save`跟`export`了,都直接用Dockerfile做。
* 匯入
1. 容器快照
`docker import 檔案 倉庫:TAG`
此指令會讓容器連著映像檔一起匯入。
2. 映像檔
`docker load 檔案`
單純匯入映像檔,跟上倉庫抓一模一樣。
### 刪除容器
1. 刪除終止的容器
`docker rm`
2. 刪除執行中的容器
`docker rm -f`
(-f是force的意思,Docker會發送`SIGKILL`信號給容器。)
## 倉庫 Repository
倉庫是存放映像檔的地方,容易混淆的概念是註冊伺服器。實際上註冊伺服器是管理倉庫的具體伺服器,每個伺服器上可以有多個倉庫,而每個倉庫下面有多個映像檔,所以可以將倉庫認為是一個具體的專案或目錄。
倉庫分為公有及私有,最大的公有倉庫是Docker Hub,基本上任何需求的映像檔都可以直接從上面抓取。
### 登入Docker Hub
`docker login 位址`
位址是選填的,可以不寫,預設為Docker Hub。
### 搜尋公有倉庫中的映像檔
先登入公有倉庫後,輸入`docker search 作者/映像檔名稱`
作者為選填,可以不寫,就會列出所有對應名稱的映像檔;如果寫了作者就只會列出該作者的對應映像檔。
Docker Hub就像Github一樣,可以給星星,所以如果想要找「幾顆星以上」的映像檔的話,可以加入參數`-s N`,N為星星數量。
### 建立私有倉庫
使用官方提供的docker-registry工具,建立私有的映像檔倉庫。
1. 安裝docker-registry工具
以Ubuntu為例:
```shell
apt-get install -y build-essential python-dev libevent-dev python-pip liblzma-dev swig
pip install docker-registry
```
或是直接clone Github上的對應專案:
```shell
apt-get install build-essential python-dev libevent-dev python-pip libssl-dev liblzma-dev libffi-dev
git clone https://github.com/docker/docker-registry.git
cd docker-registry
python setup.py install
```
直接clone的話要記得做[一些設定](https://philipzheng.gitbook.io/docker_practice/repository/config)。
2. 開始建立私有倉庫
`docker run -d -p 1234:5678 -v 路徑 --restart=always --name 指定倉庫名稱 registry`
* 參數說明:
* -d:在背景執行(=守護態模式)(非必要)
* -p:主機的port mapping到容器的port(必須,數字自訂)
* -v:指定私有倉庫的路徑(非必須,未指定則預設在容器的/tmp/registry下)
* --restart:當Docker重新啟動或停止時,這個本地倉庫是否要重新啟動。
* --name:想要給這個私有倉庫的名稱。
### 上傳映像檔至私有倉庫
1. 先使用`docker tag 欲推映像檔 私有倉庫位址:PORT/(USERNAME/)名稱:TAG`給欲上傳的映像檔標記。
2. 接著用`docker push 私有倉庫位址:PORT/(USERNAME/)名稱:TAG`,這樣就會把映像檔推到指定位址的私有倉庫上了。
3. 之後在別的機器上用`docker pull 私有倉庫位址:PORT/(USERNAME/)名稱:TAG`就可以把剛剛推上去的映像檔給拉下來。
> <font color="red">注意:推上倉庫的容器中的資料不會保存,只會存容器本身的設定(基底映像檔、有何app...),若要儲存執行中所經手的資料,就要用到別的工具,如**Volumes**。</font>
## 參考文件
1. [Docker Docs - Product Manual](https://docs.docker.com/engine/)
2. [Docker - 從入門到實踐](https://philipzheng.gitbook.io/docker_practice/)
3. [雲原生的基石,一文讀懂容器](https://blog.csdn.net/jdcdev_/article/details/105083933)
4. [全面易懂的Docker指令大全](https://joshhu.gitbooks.io/dockercommands/content/index.html)
5. [Docker教程](https://www.runoob.com/docker/docker-tutorial.html)
6. [比較save, export對於映象檔操作差異](https://blog.hinablue.me/docker-bi-jiao-save-export-dui-yu-ying-xiang-dang-cao-zuo-chai-yi/)
7. [用30天來介紹和使用 Docker](https://ithelp.ithome.com.tw/users/20103456/ironman/1320)
8. [Docker 基本教學 - 從無到有 Docker-Beginners-Guide](https://github.com/twtrubiks/docker-tutorial)