---
title: 'Docker 安裝、映像檔使用'
disqus: kyleAlien
---
Docker 安裝、映像檔使用
===
## OverView of Content
如有引用參考請詳註出處,感謝 :cat:
[TOC]
## Docker 概述
### Docker vs. 虛擬機
| 特性 | 容器 | 虛擬機 |
| -------- | -------- | -------- |
| 啟動速度 | 秒 | 分鐘 |
| 性能 | 接近原生 | 較弱 |
| 記憶體花費 | 少 | 多 |
| 硬碟耗費 | MB | GB |
| 運行數量 | 單個 PC 就可以運作上千個 | 單個 PC 大概幾十個 |
| 隔離姓 | 安全隔離 | 完全隔離 |
| 遷移性 | 佳 | 一般 |
* Docker 與虛擬機的一大差異就是 **Docker 不需要 VMM (Virtual Machine Manager)、Hypervisor 支援**,Docker 是作業系統內核層級的虛擬化,**性能更高**
### Docker 虛擬化
* 虛擬化是一種資源管理技術,會將硬體的特性隔離出來,並透過抽象、Adapter 來完成實作的呼叫 (打破硬體差異的屏障)
* 虛擬化有兩個大方向
1. 硬體虛擬化
2. 軟體虛擬化 (Docker),在軟體虛擬化的範疇中又有在細分為
| 虛擬化策略 | 說明 | 舉例 |
| - | - | - |
| 完全虛擬化 | 虛擬機完整模擬底層硬體環境、特權指令,使用者作業系統無須進行修改 | IBM p 和 z 系列虛擬化、VMware、Workstation、VirtualBox、QEMU |
| 硬體補助虛擬化 | 利用硬體 (主要是 CPU) 支援控制敏感指令,來實現完全虛擬化,**使用者也不需要修改** | VMware Workstation、Xen、KVM |
| 部分虛擬化 | 只針對部分硬體資源進行虛擬化,**使用者作業系統需要修改** | |
| 準虛擬化 | 部分硬體介面以軟體的方式提供給客戶作業系統,**使用者作業系統需要修改** | 早起 Xen |
| 作業系統虛擬化 | 核心透過建立多個虛擬的作業系統 (建立後會包含該系統所需的 Library),用來隔離不同的程序 | Docker、容器技術 |
Docker 的虛擬化就是 **作業系統虛擬化** (透過核心建立,不需要 VMM、Hypervisor 效率更高)
## Docker 基礎
### Docker 安裝 - Ubuntu
* 這裡示範如何在 Ubuntu 安裝 Docker,但主要的安裝過程還是請參考 [**Docker 官方文檔**](https://docs.docker.com/engine/install/ubuntu/) 最準確
* **基礎工具、準備**
```shell=
# 如果有舊的安裝,請先移除
sudo apt-get remove docker docker-engine docker.io containerd runc
```
> 
1. 透過 apt 安裝必要 library
```shell=
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
```
2. 下載 && 設定 GPG Key
```shell=
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```
3. 設定 stable repository (這樣才能安裝 Docker 引擎)
```shell=
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
* **安裝 Docker Engine**
```shell=
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
```
:::danger
* **Window 子系統 Ubuntu `Network Unreachable`** ?
Window cmd 中關閉 wsl,重新啟動
```shell=
wsl --shutdown
```
:::
* **在 Ubuntu 中啟動 Docker 服務**
1. 首先將 Docker 加入自身群組,並重新登入
```shell=
# 查看自身
whoami
# sudo usermode -aG docker <自身>
sudo usermode -aG docker alien
# 檢查群組
cat /etc/group | grep docker
```
2. 啟動 docker 服務
```shell=
service docker status
sudo service docker start
```
> 
:::info
* 查看 系統所有服務
```shell=
ls -laF /etc/init.d
```
> 
:::
### Docker 測試 hello-world
* **測試 Docker 運行是否正常**
1. Docker version
```shell=
docker version
```
> 
2. 嘗試跑 docker run hello-world
```shell=
docker run hello-world
```
> 
:::warning
* **Window WSL 子系統 Ubuntu 安裝 Docker,無法 run Docker 容器 !?**
> docker: Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "process_linux.go:319: getting the final child's pid from pipe caused \"EOF\"": unknown.
> ERRO[0012] error waiting for container: context cancele
最好的方法是升級 Window 的 WSL 版本到 2,[**參考**](https://www.omgubuntu.co.uk/how-to-install-wsl2-on-windows-10)
:::
* **Docker 運作 Log**
```shell=
tail /var/log/docker.logtail /var/log/docker.log
```
> 
### Docker 核心概念
* Docker 大部分操作都圍繞著**三個核心**
1. **Docker 映像檔**
Docker 映像 (Image) 類似於虛擬機,可以理解為一個 **唯讀 (read only) 模板**,一個映像檔可以包含一個基礎作業環境 + 一個應用程式
> Apache 映像檔 = 基礎作業系統 + Apache
2. **Docker 容器**
容器就是一個沙箱 (看不見彼此),用來 **運行、隔離** 不同應用,容器也是 Docker 運行映像檔後的實體 (Instance)
:::info
* 映像檔本身是唯獨,容器從映像檔啟動後,Docker 會在映像檔的上層建立一個可寫入層
:::
3. **Docker 倉庫**
Docker 有提供一個 映像檔 Hub,裡面有很多映像檔;可以依據需求下載區要的映像檔
## 映像檔操作
以下來學習使用指令操作映像檔
### 下載 Docker 映像檔
* 下載映像檔關鍵指令
| 指令關鍵 | 說明 | 其他 |
| -------- | -------- | -------- |
| docker search <關鍵字\> | 透過關鍵字,搜尋到倉庫中相關映像檔 | 可以指定搜尋倉庫 |
| docker pull NAME[:TAG] | 下載映像檔 | 如果沒有設定 tag 就會使用最新的映像檔 |
| docker images | 查看本地已經有的應向檔 | 同 image list |
| docker tag <NAME\> <TAG\> | 為已有的映象檔創建添加一個 TAG 標籤 | 但仍相同 image id |
| docker inspect <映像檔\> | 檢查映像檔資訊 | |
| docker history <映像檔\> | 檢查映像檔的建立紀錄 | 應該是該 image 的 commit 紀錄 |
| docker rmi <映像檔 or id\> | 刪除映像檔 | 有另外一個很像的 `docker rm` 是拿來移除容器 |
```shell=
# 搜尋 ubuntu
docker search ubuntu
# 下載最新 ubuntu
docker pull ubuntu
# 透過 tag 下載指定版本 ubuntu
docker pull ubuntu:14.04
```
> 
* 可以透過指定 Registry url 來選定要搜索、pull 的倉庫,假設我要搜尋 Google 的 Registry (請先安裝 [**Google gcloud**](https://cloud.google.com/sdk/docs/install#deb))
```shell=
docker pull gcr.io/google-samples/hello-app:1.0
docker images
```
> 
* 對已有的 image 檔創建自己的 TAG (觀察一下,兩者是有相同的 IMAGE ID)
```shell=
# 給 'python:latest 添加 py tag'
docker tag python:latest py
docker images
```
> 
### 建立 Docker 映像檔
| 指令關鍵 | 說明 | 其他 |
| -------- | -------- | -------- |
| docker commit | 依據已有的容器創建 | 可以記錄你對 container 的操作紀錄,方便以後重複使用 |
| docker import | 透過下載、現有檔案創建 | |
* 建立映像檔的方法有三種
1. 基於 **==現有映像檔產生的 ++容器++==** 建立:使用 **commit** 指令對現有的 container 建立
:::info
* 格式:`docker commit [OPTIONS] <Container id | name> [Repository[:TAG]]`
:::
使用 commit 指令 (有點類似 git commit),對現有 container 進行提交,**提交後 container 就會產生新 sha id**
> Respository 取名必須小寫
>
> 
```shell=
# 查看所有 container
docker ps -a
# 透過 指定 container id e35 來提交
docker commit -m "First commit for container by id" -a "alien" e35 alien_image:0.1
# 透過 指定 container name determined_hellman 來提交
docker commit -m "First commit for container by name" -a "alien" determined_hellman alien:0.2
```
> 
2. 基於 Linux 容器 (LXC) 範例匯入:使用 **import** 指令,直接從一個作業系統範本匯入一個映像檔
:::info
* 格式:docker import [OPTIONS] <Url | file | -\>-[Repository[:TAG]]
:::
這裡示範下載 [**ubuntu**](http://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/)
```shell=
# import by url
docker import http://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/ubuntu-base-22.04-base-amd64.tar.gz ubuntu_import:22.04
```
> 
```shell=
# 先下載到 download 資料夾
wget -P download/ http://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/ubuntu-base-22.04-base-amd64.tar.gz
# import by file
cat download/ubuntu-base-22.04-base-amd64.tar.gz | docker import - ubuntu_image_2:22.04
```
> 
3. 基於 Darkfile 建立 (這先跳過,以後再說)
### 儲存 or 載入 印象檔
| 指令關鍵 | 說明 | 其他 |
| -------- | -------- | -------- |
| docker save -o | 儲存現有 image | - |
| docker load -input | 加載本地 image | - |
1. save:儲存 hello-world image 到 `~/images`,壓縮成 `hello-world.tar`
```shell=
mkdir images
docker save -o ~/images/hello-world.tar hello-world
```
> 
2. load:加載本地 tar
```shell=
docker load --input ~/images/hello-world.tar
```
> 
## 容器操作
有點類似虛擬機的創建,但是 Docker 所創建的容器需要的資源很少、創建速度快
* Container 的創建基於 Image,所以要先確保有 Image 再創建
```shell=
docker images
```
> 
### 創建運行 Container
| 指令關鍵 | 說明 | 其他 |
| -------- | -------- | -------- |
| docker create | 依據 image 創建 Container | 創建出的 Container 狀態是 **==Created== (尚未運行),可透過 start 指令運行 container** |
| docker start | 運行特定 container | 透過 id 指定 container,**啟動後狀態為 ==UP==** |
| docker run | 創建 + 運行 container,可以看做 create + start 指令的結合 | 如果沒有 image 也會從倉庫自動下載 |
1. create:依照 `ubuntu:22.04` 映像檔創建 Container
```shell=
# docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
# 創建 container
docker create -it ubuntu:22.04
# 查看所有 container
docker ps -a
```
> 
由於 create 指令的 option 相當多,這裡不會都提及 (有用到才會說明),以需要可以透過 `docker create --help` 查詢
2. start:運行指定 container
```shell=
# 查看所有 container
docker ps -a
# 啟動 id 38 (id 不用全部輸入) 的 container
docker start 38
docker ps -s
```
> 
3. run:建立 & 運行 container
```shell=
# 依照 ubuntu:20.04 image 創建 container
# 並運行 bin/echo 'Hello World'
docker run ubuntu:20.04 /bin/echo 'Hello World'
```
> 
docker run 常見的 OPTION 指令,完整請查看 `docker run --help`
| Run's OPTIONS | 說明 | 其他 |
| -------- | -------- | -------- |
| -d | 背景運行 | 可透過 `docker attach <container id>` 進入前景 (id 透過 pa -a 查詢) |
| -i | 可以使用者戶交 (可輸入指令) | - |
| -t | 分配一個虛擬終端機 (pseudo-tty) | - |
```shell=
# 啟動 run
docker run -it -d ubuntu:20.04
docker ps -s
docker attach acb
```
> 
### 停止、重啟 Container
| 指令關鍵 | 說明 | 其他 |
| -------- | -------- | -------- |
| docker kill <id\> | 直接關閉容器 | 暴力 |
| docker stop <id\> | 會對容器發送 SIGTERM 信號,約略 10s 後發送 SIGKILL 信號 | |
| docker restart | 重新啟動容器 | |
```shell=
# 先查看有哪些容器
docker ps -a
# 啟動指定 容器 (ac 是 id)
docker start ac
# 查看正在運行的容器
docker ps -s
```
> 
### 進入已運行 Container
| 指令關鍵 | 說明 | 其他 |
| -------- | -------- | -------- |
| docker attach <id\> | 將 Container 回到前景 | 當多個視窗訪問同一個容器時,所有視窗都會一起顯示 |
| docker exec [OPTIONS] <id\> <COMMAND\> | 在已經啟動的容器中,執行任意命令 | 就像是對該容器重新創建了一個 tty 終端機 |
:::warning
這兩個命令只能用在已啟動的部分
:::
* attach:將已啟動的 container 回到前景時,若有多個終端機訪問同一個 Container 則會 **++同步++ 顯示**
> 
* exec:執行一個新的命令,兩個不同視窗的命令不會相互影響
```shell=
# 啟動兩個視窗
# 以下的「ac」是 container id 的簡稱
docker exec -it ac /bin/bash
```
### 刪除 Container
| 指令關鍵 | 說明 | 其他 |
| -------- | -------- | -------- |
| docker rm <id\> | 移除容器 | 當容器正在運行中則無法刪除 (可用 -f) |
```java=
# 查看當前有的容器
docker ps -a
# 移除 id 為 33 的容器
docker rm 33
# 再次檢查容器是否已被刪除
docker ps -a
```
> 
:::warning
* 當容器正在運行時,則無法刪除,可以使用 `-f` 來強制關閉 (但不太好)
> 
:::
### 匯出、匯入容器
| 指令關鍵 | 說明 | 其他 |
| -------- | -------- | -------- |
| export -o <輸出檔案> <id\>| 將容器輸出到本地 | 提醒:映像檔是 save `-o` |
| import file \| Url - [Respository[:TAG]] | 用法同 **IMAGE 匯入** | 也就是匯入 image,container 要手動啟動 |
1. 輸出指定 container
```shell=
# 創建一個 container 資料夾
mkdir -p container
# 輸出 container 到 ~/container 資料夾中
docker export -o ~/container/myContainer.tar e3
```
> 
2. 輸入指定 tar 檔案,再查看 image 檔
```shell=
cat ~/container/myContainer.tar | docker import - my_container:0.3
# 查看 images
docker images
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `Docker`