---
# System prepended metadata

title: CNTUG Infra Labs 使用心得 - Docker 學習筆記

---

# CNTUG Infra Labs 使用心得 - Docker 學習筆記

## 前言
在學習雲原生相關技術時，最缺乏的其實就是機器，感謝 CNTUG Infra Labs，讓我不用花錢就能體驗到基礎建設即服務(IaaS)，透過實際動手操作，能夠更深入地學習這些技術。

## 甚麼是 Docker

以往將軟體從 A 機器移植到 B 機器會遇到非常多的問題，如環境不一致，版本相容性，套件衝突等。但在 dotScale 2013 的其中一場演講 Solomon Hykes - Why we built Docker，演說後隨即引起很大的轟動，有了 Docker，就等於有了一個可靠的自動化移植的工具，開發團隊能夠更好的去協作，在進行部屬時，也不會因為前述的問題而煩惱，有興趣的話歡迎看看當初的演說。

{%youtube 3N3n9FzebAA %}


## Docker vs Virtual Machine

同樣都是虛擬化技術，那 Docker 跟 VM 的差別究竟是甚麼?

![image](https://hackmd.io/_uploads/H10jzJ-ia.png)


> source: [The 10 Most Common Questions IT Admins ask About Docker]([the-10-most-common-questions-it-admins-ask-about-docker](https://www.docker.com/blog/the-10-most-common-questions-it-admins-ask-about-docker/))


可以很明顯地看到上圖左側的容器化以及右側的虛擬化差別，Docker少了虛擬化 OS Kernel 層的部分，直接跟 Host 共用 Linux Kernal，透過Linux Kernel的資源隔離功能(例如 cgroups 和 namespace )允許獨立的`容器`在單個 Linux 實例中運行。(簡單說都是讓服務間不會互相干擾，但需要的資源比VM更少了)

:::success
想了解更多 Linux Kernel 知識的話可以去看 Jsev 的[「Linux 核心設計」系列講座](https://hackmd.io/@sysprog/linux-kernel-internal)
:::

這就讓 Docker 多了幾個優點:

* 簡化部屬流程
* 架設環境更便利
* 有效率的虛擬化
* 可擴展性

但也導致了某些問題:

* 由於跟 Linux 共用 Kernel 進而產生安全上的漏洞
* Docker Daemon 具有 root 權限 (Podman 逐漸成為明日之星!?)


## Docker 基本概念

Docker 主要有以下幾個基本概念:

* Image
* Container
* Volume
* Network

## Image

Image 就如同是應用程式的模板一樣，舉個現實情況的例子，假設你正在開發一個 Python 的 Web 應用程式，並使用 Flask 框架。

* 在沒有 Docker 的情況下: 你需要確保環境中已安裝正確版本的 Python，手動安裝 Flask 及其他相依套件等等。這導致了開發環境和生產環境之間的不一致，並增加部署的複雜性。
* 有了 Docker 之後: 你可以建立一個 Docker Image，裡面包含了正確版本的 Python、Flask，以及你的應用程式代碼。任何人只要有 Docker 都可以透過使用你的 Docker Image 建立出一模一樣的 Web 應用程式，而不用擔心相依性問題。

要建立自己的 docker image 需要先創建一個 Dockerfile，那我們就以上述例子實際建立看看。
```dockerfile!
FROM python:3.10.13-alpine3.18

WORKDIR /app
COPY . .

RUN pip install Flask==3.0.0

CMD ["python3", "main.py"]
```

* FROM: 指定基底要用的image，預設會從docker hub 上有的抓下來。
* WORKDIR: 指定容器要用的路徑。(路徑不存在時，Docker 會自動創建)
* ADD: 將本機端目前路徑下的所有檔案複製到容器的`/app`路徑下。(因為我們已經將容器的目前路徑設為`/app`)
* RUN: 創建 Image 時要執行的指令。
* CMD: 當 Container 啟動時，要執行的指令。

接著使用以下指令來 build Image:
```bash
docker build -t python-web-app:3.10.13 .
```

* `-t`: 指定 Image 的名稱以及標籤，格式為:`name:tag`。
* `.`: 告訴 Docker 從當前路徑下找 Dockerfile。


如果沒有出現任何 Error，就表示成功建立一個屬於你自己的 Docker Image了:

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

## Container

Build 好了 Image 之後，下一步當然就是啟動，也就是利用 Container 來啟動剛剛建立好的 Image。
```
docker run --rm -p 80:1234 -d python-web-app:3.10.13
```

* `-p`: 代表從本機端的 `80 port` forwarding 到 Container 的 `1234 port`，這樣才能夠從本機端看到你的網頁。
* `-d`: 代表 Container 在背景執行，也就不用占用你的 Terminal了。
* `--rm`: 代表當Container 執行完就直接移除。(因為 Container 執行完後會變成停止狀態，如果不需要的話就可以透過`--rm`移除。
    ![image](https://hackmd.io/_uploads/SkeWcrGsa.png)


接著你就可以開一個網頁輸入`localhost` 也就是本機端的 IP 來查看你的網頁了。

:::warning
由於我的本機目錄下並沒有 `main.py` 這個程式，所有執行起來會看不到網頁是正常的，實際看到 Container 的 log 也會跟你說沒有此檔案。

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


:::info
想實際看看網頁的話可以透過 nginx 執行以下指令，接著在網頁上輸入`localhost`就能看到網頁。
```
docker run -p 80:80 nginx:1.25.3
```
![image](https://hackmd.io/_uploads/r1YIoBzip.png)
:::



## Volume

由於 Container 內部就是一個隔離的環境，，所以當 Container 關閉之後資料也就沒了。
那如果我要執行像資料庫這種類型的 Container 該怎麼辦?
那就是要透過 Volume 綁定本機端的 path 到容器內的 path。

:::success
本次介紹主要講解最簡單的 `-v` 或是 `--volume`，如果想要了解更詳細的知識，像是 `--mount` 和 `--tmpfs`可以去 Docker Docs了解喔。

> https://docs.docker.com/storage/volumes/
:::

當我們在執行`docker run` 指令時，其實只要多設一個參數，就可以將本機端的路徑綁定Container 的路徑了。

```
docker run --rm -p 80:80 -v ./:/data alpine:3.19.1
```
例如我現在test目錄底下有一個 Dockerfile，內容為:
```dockerfile
FROM python:3.10.13-alpine3.18

WORKDIR /app
COPY . .

RUN pip install Flask==3.0.0

CMD ["python3", "main.py"]
```

接著在看容器內的`data`目錄下也有 Dockerfile，來看看是不是跟本機端的 Dockerfile 一模一樣。
![image](https://hackmd.io/_uploads/r1Aw0BMsT.png)

這樣就代表成功將容器內的資料綁定到主機端摟。


## 使用 Docker 架設幻獸帕魯專用伺服器

:::warning
CNTUG Infra labs 的平台原則上不允許用戶使用服務來架設遊戲伺服器喔。
:::
那接下來我們就來實際建立幻獸帕魯的專用伺服器。(本次所使用的 docker image 請參考下方連結)
> [thijsvanloef/palworld-server-docker](https://github.com/thijsvanloef/palworld-server-docker/tree/main)

首先先透過`git clone`將上面連結的repo複製一份到本機端。
```bash=
git clone https://github.com/thijsvanloef/palworld-server-docker.git
```

接著更改目錄內的`docker-compose.yml`，將`environment`替換成`env_file`，並指定檔案為`.env.example`。
:::success
Docker compose 就是可以一次啟動好幾個 Docker Container 的方法
:::

```
services:
  palworld:
    image: thijsvanloef/palworld-server-docker:latest
    restart: unless-stopped
    container_name: palworld-server
    stop_grace_period: 30s  # Set to however long you are willing to wait for the container to gracefully stop
    ports:
      - 8211:8211/udp
      - 27015:27015/udp  # Required if you want your server to show up in the community servers tab
    env_file:
      -  .env.example
    volumes:
      - ./palworld:/palworld/
```

接著就可以修改`.env.example`的內容，來設定伺服器內的設定，參數對應可參考Repo內的READ.md。
> https://github.com/thijsvanloef/palworld-server-docker?tab=readme-ov-file#environment-variables

接著輸入以下指令執行Server。
```
docker compose up -d
```

在進入遊戲內 -> 參加多人遊戲 (專用伺服器) -> 透過下方IP連線 (如果有新增密碼請記得把輸入密碼打勾)
![image](https://hackmd.io/_uploads/rykAFO8i6.png)

這樣你就建立好一個幻獸帕魯的專用伺服器摟!

:::warning
如果要讓其他人連線進來，需要將IP換成 Public IP，另外防火牆也需要將 Port 8211 開啟。
:::


## 感想

非常感謝這次成功申請到 CNTUG Infra，雖然第一次寫心得文可能文筆有點不好，但我很高興能在開源社群的活動中做出一點貢獻。各位如果使用 Docker 建置 Palworld 專用伺服器有任何問題都可以在下方 github repo 發 issue，或是到 Discord 問問題也行喔。

* **Github** : https://github.com/thijsvanloef/palworld-server-docker
* **Discord** : https://discord.gg/wcytChKQ