---
# System prepended metadata

title: Docker 實戰 2 - 相關指令與 Dockerfile

---

# Docker 實戰 2 - 相關指令與Dockerfile

## 前言
前一篇和大家介紹了什麼是Docker，如果還沒看過的可以先去看一下 [Docker 實戰 1 - 五分鐘了解 Docker](/lo88XIBlTiGJeiUDs8JmFw)

本篇會告訴大家如何下載Docker及相關指令，最後教大家撰寫Dockerfile，就可以執行屬於自己的docker container了！

## 安裝
- Docker 有支援 Linux、Mac 跟 Windows，到 [Get Docker](https://docs.docker.com/get-docker/) 安裝即可，之後在cmd輸入docker version，有跑出版本就代表成功！
- 官方下載[Docker桌面版](https://www.docker.com/)：包含ＧＵＩ介面，管理方便，剛開始對於指令不熟的時候也可以從介面操作，會降低很多門檻！
    - 透過介面可以看到所有本地端的container，image和volumes
     ![](https://hackmd.io/_uploads/rkRGDaZR3.png)

## 常用指令
如果不使用GUI介面當然也可以查看相關內容


- 查看運行中的conatiner

        docker ps
    
    > 要查看所有的後面加上 -a ，會發現和上面GUI介面結果相同

    ![](https://hackmd.io/_uploads/ryNPOpZRn.png)

- 查看所有的image
    
        docker images
    
    ![](https://hackmd.io/_uploads/BJ4zOT-Rn.png)

- 下載image

        docker pull <image name>

- 建立 image

        docker build [OPTIONS] PATH


    - --build-arg=[] :设置镜像创建时的变量；
    - -f :指定要使用的Dockerfile路径；
    - --no-cache :创建镜像的过程不使用缓存；
    - --pull :尝试去更新镜像的新版本；
    - --quiet, -q :安静模式，成功后只输出镜像 ID；
    - --rm :设置镜像成功后删除中间容器；
    - --tag, -t: 镜像的名字及标签，通常 name:tag 或者 name 格式；可以在一次构建中为一个镜像设置多个标签。

- 執行 image

        docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

    - -d : 背景執行
    - -h : 自訂 host name
    - -i : 進入互動模式
    - -p: 指定端口映射，格式为：主机(宿主)端口:容器端口
    - -u : 設定 username
    - --name : 自訂 docker container 的名稱
    - -v : 設定從外部掛載資源到 container 中
    - --env-file=[]: 从指定文件读入环境变量；


## Dockerfile
Dockerfile 是一份以 YMAL 語言撰寫的 Docker 設定檔，是docker建立image的重要角色，會從檔名是「Dockerfile」的文字檔中，透過docker build創建 Image，在透過docker run 執行container。如下圖：
![](https://hackmd.io/_uploads/HJgI6T-A3.png)

### 基本語法
- FROM：哪個映像檔(通常會抓取應用程式的執行環境，例如前端通常都是寫 JavaScript 所以就會利用 Node 作為執行環境)
- WORKDIR：應用程式執行位置
- ADD/COPY：新增或複製檔案到應用程式執行位置
- RUN：執行應用程式

### 進階語法
- ARG：在 build 的過程中，可以從外部帶入一些參數進來作為後續的環境變數使用。
- ENV：在 build 的過程中，可以定義一些環境變數，讓後面指令在執行時候可以使用，但前提是這個環境變數必須要從 ARG 中先被定義。
- CMD：Docker 在啟動容器的時候會執行的指令，不過這個指令只能執行一次，也就是假如你的 Dockerfile 中寫了多個 CMD，最後只會執行最後寫的那個 CMD。
- ENTRYPOINT：Docker 在啟動容器的時候會優先執行的指令，所以比起 CMD 來說 ENTRYPOINT 會更優先執行。

### 實作 Docker container

1. 先用express簡單架一個伺服器
```javascript=
const express = require('express')
var cors = require('cors')

const app = express()
const port = 8000

app.use(cors())

app.get('/', (req, res) => {
  res.json({
    healthy: 'ok'
  })
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
```
2. 接著在資料夾的同一層建立Dckerfile的檔案
```dockerfile=
FROM node:18

# 建立目錄
WORKDIR /usr/src/app

# 安裝套件
# 將程式中的package和package-lock的檔案先複製到docker image目錄底下
COPY package*.json ./

# 在執行npm install 前面package有的套件就可以不用安裝，省下一些時間
RUN npm install

# Bundle app source
# 將自己程式文件中所有的檔案複製到 docker image裡面
COPY . .

# 指定 container 對外開放的 port
EXPOSE 8000
CMD [ "node", "app.js" ]
```

3. 建立透過dockerfile建立名為my-app的docker image
```dockerfile=
# my-app後面的 . 不可省略 代表路徑
# 就像git 指令中的git add . 一樣代表該資料夾
docker build -t my-app .
```
4. 等待建立image後就可以建立容器，執行image了
```dockerfile=
docker run -p 8000:8000 my-app
```
5. 打開瀏覽器 http://localhost:8000 就可以看到透過express建立的server了！

:::warning
:warning: 有一個常常會搞錯的地方就是 port的設定

docker run -p 本機端口:容器端口 [image name]

也就是說如果按照上面的例子，因為dockerfile中，設定EXPOSE 8000，所以後面的容器端口一定要是8000，如果把指令改成
> docker run -p 8787:8000 my-app

就會發現無法連上http://localhost:8000了，因為前面port改成8787所以連線的位置就會變成http://localhost:8787 !
    
:::

## 總結
恭喜大家距離docker又更進一步了，要熟悉一門新的技術真的要透過不斷的實戰練習才是最好的方法！下一篇要介紹的是同時執行很多image的魔法 docker-compose！ [Docker 實戰 3 - Docker-compose](https://hackmd.io/@emmmmmma/HyyMSNd1T)


![](https://hackmd.io/_uploads/BklBHMGAn.png)


## 參考資料
- [官方文件](https://docs.docker.com/)
- [Docker 基本觀念介紹與容器和虛擬機的比較](https://www.omniwaresoft.com.tw/product-news/docker-news/docker-introduction/)
- [Docker 學習筆記(三) — Docker指令講解](https://medium.com/%E4%B8%80%E5%80%8B%E5%B0%8F%E5%B0%8F%E5%B7%A5%E7%A8%8B%E5%B8%AB%E7%9A%84%E9%9A%A8%E6%89%8B%E7%AD%86%E8%A8%98/docker-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-docker%E6%8C%87%E4%BB%A4%E4%BD%BF%E7%94%A8%E8%AC%9B%E8%A7%A3-9040d9de4677)
- [docker run 命令](https://www.runoob.com/docker/docker-run-command.html)
- [Docker build 命令](https://www.runoob.com/docker/docker-build-command.html)