---
# System prepended metadata

title: Docker and Container 003 - image 映像檔
tags: [學習筆記, 教學, Docker]

---

-[![hackmd-github-sync-badge](https://hackmd.io/pJNDY4QbQH-6qwIrI-jXwg/badge)](https://hackmd.io/pJNDY4QbQH-6qwIrI-jXwg)
###### tags: `教學` `Docker` `學習筆記`

# Docker and Container 003 - image 映像檔


[TOC]



---
## 0. Docker Image
- 映像檔的概念（裡面放什麼）
> 映像檔是 Docker 的三大組件之一。（image, container, registry）。
Docker 在執行容器前需要本地端存在對應的映像檔，如果映像檔不存在本地端，Docker 會從映像檔倉庫下載（預設是 Docker Hub 公共註冊伺服器中的倉庫）。 
- Docker如何使用映像檔
>- dockerfile → image → container
>- image ←→ registry
- 一種簡易的成品，可食用，但通常可擴充/客製
  - 一鍋醃肉，但一頓飯不會只吃醃肉
  - 一鍋咖哩，比醃肉更完整些，但也可擴充

---

以下就常用功能一一做介紹：

---

## 1. PULL 取用映像檔

- 從docker hub拉取：`$ docker pull nginx:latest`
  - latest是tag/標籤，一般來說可當作版本來理解。
  - 沒有指定版本的話，預設都為最新版（latest）
- 從docker hub 尋找(search)：`$ docker search nginx`
  - 說明找到的結果，官方認證、星星數
- 查看映像檔資料：把image放在容器執行起來，查看裡面的內容：`$ docker run -it --name [container] [image] /bin/sh`
  - --name [container]：替容器取名
  - [image]：容器基底的映像檔


---

## 2. PUSH 推送映像檔
> 把映像檔 Push到公開網路的 Docker Hub 上，所有的人都可以把這個映像檔 Pull 下來使用
1.  註冊 [Docker Hub](https://hub.docker.com/) 帳號
2.  把 Docker Image Push 到 Docker Hub 上

```linux=
1. 查看現在有什麼映像檔
$ docker images

2. 選擇一個叫alpine，放在test_alpine專案裡
$ docker tag alpine kihifung/test_alpine

3. 推到遠端（hub）
$ docker push kihifung/test_alpine
沒登入，所以失敗
Using default tag: latest
The push refers to repository [docker.io/kihifung/test_alpine]
bc276c40b172: Preparing 
denied: requested access to the resource is denied

4. 登入registry
$ docker login
Username: kihifung
Password: 
Login Succeeded

5. 重新推一次（成功的訊息如下）
$ docker push kihifung/test_alpine
Using default tag: latest
The push refers to repository [docker.io/kihifung/test_alpine]
bc276c40b172: Mounted from library/alpine 
latest: digest: sha256:be9bdc0ef8e96dbc428dc189b31e2e3b05523d96d12ed627c37aa2936653258c size: 528
```

- 實例圖如下：
![](https://i.imgur.com/yzdwsQz.jpg)
![](https://i.imgur.com/HbQl9ilm.jpg)


3.  測試是否成功的把 Docker Image Push 到 Docker Hub 上
```linux=
1. 查看現有的映像檔，選擇刪除kihifung/test_alpine
$ docker images
$ docker rmi kihifung/test_alpine
Untagged: kihifung/test_alpine:latest
Untagged: kihifung/test_alpine@sha256:be9bdc0ef8e96dbc428dc189b31e2e3b05523d96d12ed627c37aa2936653258c

2. 確認已刪除後，將hub上的拉下來，
$ docker images
$ docker pull kihifung/test_alpine:latest
latest: Pulling from kihifung/test_alpine
Digest: sha256:be9bdc0ef8e96dbc428dc189b31e2e3b05523d96d12ed627c37aa2936653258c
Status: Downloaded newer image for kihifung/test_alpine:latest
docker.io/kihifung/test_alpine:latest

3. 確認有拉下來
$ docker images
```


- 實例圖如下：
![](https://i.imgur.com/ZPmej6p.jpg)
![](https://i.imgur.com/2OWNRim.jpg)

---

## 3. SAVE 儲存/匯出
> 原本用來存成串流，寫入指定的檔案，常用來存成壓縮檔
* `$ docker images`，查看有什麼映像檔
* `$ docker save alpine:latest > alpine.tar`，選擇alpine:latest這個映像檔存成壓縮檔（或是`$ docker save -o mytomcat.tar mytomcat`，先寫結果名稱，再來原始image，-o: 輸出檔案）
* `$ ls`，查看這個資料夾有什麼檔案，確認alpine.tar有沒有產生

---

## 4. LOAD 加載
* 接續前面的範例
* `$ docker images`
* `$ docker rmi alpine:latest`
* `$ docker load -i [路徑]/ai-engine.tar`
* 過程中可仔細觀看log

### 實例（小i）
* `$ docker load -i /xiaoi_app/ai-engine/images/ai-engine.tar`， 將/xiaoi_app/ai-engine/images這個資料夾裡面的ai-engine.tar這個檔案，加載上去。

---

## 5. Build 建構
- [Image-building best practices](https://docs.docker.com/get-started/09_image_best/)

---

## 6. Commit
> 將conatainer重新包成image 

### [修改已有映像檔](https://philipzheng.gitbook.io/docker_practice/image/create#xiu-gai-yi-you-ying-xiang-dang)
* 先使用下載的映像檔啟動容器。
```
$ sudo docker run -t -i training/sinatra /bin/bash
root@0b2616b0e5a8:/#
```
* 注意：記住容器的 ID，稍後還會用到。
* 在容器中加入 json 的 gem 套件。
root@0b2616b0e5a8:/# gem install json
* 當結束後，我們使用 exit 來退出，現在我們的容器已經被改變了，使用 docker commit 命令來提交更新後的副本。
```
$ sudo docker commit -m "Added json gem" -a "Docker Newbee" 0b2616b0e5a8 ouruser/sinatra:v2
4f177bd27a9ff0f6dc2a830403925b5360bfe0b93d476f7fc3231110e7f71b1c
```
* 其中，-m 指定提交的說明信息，跟我們使用的版本控制工具一樣；-a 可以指定更新的使用者信息；之後是用來建立映像檔的容器的 ID；最後指定新映像檔的名稱和 tag 。建立成功後會印出新映像檔的 ID。


#### 常見docker image
* `alpine`：輕量化的Linux作業系統
* `nginx`：輕量的網站伺服器
* `apache`：重量的網站伺服器


---

## 7. 刪除image
### 1. 還有容器在使用
- `$ docker rmi AAA`，砍掉AAA這個image
- 常見錯誤訊息：這個image還放在container中運行
![](https://i.imgur.com/x2fIQTm.jpg)
![](https://i.imgur.com/L51dPba.jpg)



* `$ docker rm 4caa6d8ca27c`，刪除container
* `docker rmi kihifung/course-image-build`，刪除特定image
* `docker rmi -f kihifung/course-image-build`，強制刪除特定image
![](https://i.imgur.com/lVT9GjC.jpg)

::: info
刪除image指令
```linux=
兩種皆可
$ docker rmi [image_name]
$ docker rmi [image_id]
```
:::
#### 刪不掉？
- 因為已經有容器在使用，要先把容器停止
- [指令連結](https://hackmd.io/CHWfratfRWGz0Ykb1ShC6A?both#%E5%8 8%AA%E9%99%A4)


### 2. 與多個專案連結
:::warning
root@docker_machine docker]# docker rmi b1d9e36f9546

Error response from daemon: conflict: unable to delete b1d9e36f9546 (must be forced) - **image is referenced in multiple repositories**
:::
- b1d9e36f9546 這個映像檔被兩個專案給使用：kihifung/t0802_2、kihifung/test0802  
- 刪除時可以用repository和tag的方式來刪除：
  - `$ docker rmi kihifung/t0802_2:latest `和 
  - `$ docker rmi kihifung/test0802:1.0 `

![](https://i.imgur.com/jDLwbYr.jpg)



### 3. 孤兒 dangling=true
:::warning
[root@docker_machine docker]# docker rmi 021b3423115f

Error response from daemon: conflict: unable to delete 021b3423115f (cannot be forced) - image has dependent child images
:::


解法：
1. 先刪掉孤懸的映像檔
* `$ docker rmi $(docker images --filter "dangling=true" -q )`
2. 再刪除欲刪除的映像檔
`$ docker rmi 021b3423115f`
- 這個主要是拿來清理一些，同一個 tag 但已經被覆蓋過的 image。有些 tag 被 build 了兩次，那第一次的 build 所建立出來的非共用部份的 image 就等於是孤兒了，用這個指令可以清除。


:::info
**dangling** 是表示那些沒有 TAG (標記)的 Image，常發生於自行建立的 Image 上，若是從 Docker Hub 或其他地方 pull 下來的幾乎都不會有這問題。

**系統判定這些未被 TAG 的 Image，不被用於任何 Images Layer 中，同時也占用了磁碟空間**，因此註記為 dangling，提醒開發人員此 Image 的狀況。

開發人員可以做的動作是將其 TAG、保持不變或刪除。

---
- [參考資料](https://hackmd.io/@KaiChen/Skt8SLZV8#%E6%9F%A5%E8%A9%A2%E9%80%8F%E9%81%8E-Filter-%E7%AF%A9%E9%81%B8)
:::
- 相關指令
  * `$ docker image prune`，移除沒有標籤並且沒有被容器引用的映象，這種映象稱為 **dangling(搖晃的)** 映象

##### 延伸閱讀
- [DOCKER 移除 CONTAINERS、IMAGES、VOLUMES 與 NETWORKS 的方法](https://blog.clarence.tw/2019/09/10/docker-removing-containers-images-volumes-and-networks/)
- [docker 移除，裁剪，刪除(prune)不使用的映象、容器、卷、網路](https://iter01.com/572355.html)
- [解決刪除映象時 image is referenced in multiple repositories](https://www.itread01.com/content/1545240006.html)


---

## 8. Tag 更名
- （範例圖，顯示映像檔名稱與image id）
- image的名稱其實只是一個貼紙，本體是以image id來編號，在顯示上會顯示為分配到哪一個專案(repository)
- 所以第一行表示：xxxx(image id)現在被OOOO(repository)使用，有著QQQ(tag)的標籤
- 因此，有些複製或刪除的指令看起來就像是貼上貼紙或撕掉貼紙（可在log裡看到tagged/untagged）。同一個image也可以有多個貼紙（被多個repo同時使用）
- `＄ docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]`


---

## 9. 其他
### docker image放在哪？
- `$ docker info`，查看docker相關資訊，其中`Docker Root Dir: /var/lib/docker`顯示出docker image的存放路徑
- ref:[修改 Docker image 預設存放位置](https://marcus116.blogspot.com/2019/03/change-download-docker-image-path.html)

### 其他不使用docker的方法
- [Kaniko — Image Builder 試玩](https://www.geminiopencloud.com/zh-tw/blog/kaniko/)



### 其他指令
* `$ docker images --no-trunc` 列出 Images 完整資訊
* `$ docker inspect --format='{{.Id}} {{.Parent}}' $(docker images --filter since=<image_id> -q)`，列出與<特定映像檔>有關的內容
![](https://i.imgur.com/arX8BDO.jpg)


##### 建構docker應用程式的小技巧
1. （不包含函式庫和相依套件的話）image盡量小，這樣啟動快載入也快
2. 如應用程式需要擴展，應考慮使用dockerswarn、kubernetes等工具
3. 為了最大效率，請將Docker與CI/CD結合，可以從雲端自動建立映像檔，並推送到docker倉庫。
    - 資料來源：[quicks tips for building applications with Docker](https://medium.com/hackernoon/docker-commands-the-ultimate-cheat-sheet-994ac78e2888)

---



---
## 回顧
### Image
* [ ] 什麼是image
* [ ] image可以幹嘛
* [ ] 這麼多不同的指令代表image的什麼用途



---

- [回到目錄](https://hackmd.io/@Hualiteq/r1lye3M3d)
