今天要使用 Docker 把先前的專案容器化啦,畫圖的時候畫出鯨魚 (海豚?) 螃蟹,有夠可愛哈哈哈
🏮 今天要部署的專案包含最終的 Dockerfile 都可以在 GitHub 找到。
在昨天的內容中,我們談到了以 ML 系統設計的角度怎麼處理要進入產品環境的機器學習模型。
而在實作先前的專案時,其實已經依照這個思路去考量了軟、硬體的需求,包含了如何挑選與使用量化模型 ([Day 15] - 鋼鐵草泥馬 🦙 LLM chatbot 🤖 (6/10)|GGML 量化 LLaMa)。
然而,在專案的最後 [Day 19] - 鋼鐵草泥馬 🦙 LLM chatbot 🤖 (10/10)|結論及展望 只有粗略的提到部署 Rust 應用程式的過程不會很複雜。
所以今天我們就要實際走過這個流程,讓鋼鐵草泥馬 🦙 真正的飛上雲端!
🚨 這裡不會介紹 Docker 是什麼以及該怎麼操作,只會走過必要的步驟並搭配相對應的解說。
如果想完整學習 Docker 的話,推薦 Learn Docker in a Month of Lunches
要將專案變成映像檔的關鍵角色是 Dockerfile
,但如果不是天天都在寫,我想八成的孩子會忘記語法。
真是拿你沒辦法,還好 Docker A 夢都幫我們準備好了 (請下 BGM~),docker init!
直接在專案資料夾中執行 docker init
就可以開始生成需要的檔案:
這裡的答案分別是
Rust
→1.75.0-nightly
→3000
注意,Docker Desktop 的版本要大於 4.21 才會支援 Rust。
可以看到只需要回答幾個問題 (大部分都是自動偵測),不到 10 秒就能為專案初始化以下檔案:
雖然自動生成 Dockerfile 很快很方便,但我們還是需要做一些修改讓它適用於我們的專案。
以下是修改後的 Dockerfile,每個指令的說明已經註解在上面了:
# App 名稱
ARG APP_NAME=iron_llama
# 要複製進最終映像檔的模型名稱
ARG MODEL_NAME=Taiwan-LLaMa-13b-1.0.ggmlv3.q2_K.bin
# 建置階段,使用 Rust 每夜版
FROM rustlang/rust:nightly-bookworm-slim as build
ARG APP_NAME
# 指定工作位址並將所有東西複製到容器中
WORKDIR /app
COPY . .
# 安裝需要的工具
RUN apt-get update
RUN apt-get install -y pkg-config openssl libssl-dev curl
# 加上 WASM target
RUN rustup target add wasm32-unknown-unknown
# 安裝 cargo-binstall, 這可以使安裝其它 cargo 擴充套件如 cargo-leptos 更容易
RUN wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz
RUN tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz
RUN cp cargo-binstall /usr/local/cargo/bin
# 安裝 cargo-leptos
RUN cargo binstall cargo-leptos -y
# 建置 app
RUN cargo leptos build --release -vv
# 最終階段
FROM rustlang/rust:nightly-bookworm-slim as final
ARG APP_NAME
ARG MODEL_NAME
RUN apt-get update && apt-get install -y openssl
WORKDIR /app
# 把模型複製到 model
COPY --from=build /app/$MODEL_NAME model
# 把伺服器的二進制檔複製到 server
COPY --from=build /app/target/server/release/$APP_NAME server
# 把 包含了 JS/WASM/CSS, etc. 的 /target/site 複製過來
COPY --from=build /app/target/site target/site
# 設定環境變數
ENV MODEL_PATH="/app/model"
ENV LEPTOS_SITE_ADDR="0.0.0.0:3000"
# 設定使用者
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
# 更改權限
RUN chown -R appuser:appuser /app
RUN chmod -R 755 /app
# 切換使用者
USER appuser
# 暴露端口
EXPOSE 3000
# 跑起來!
CMD ["/app/server"]
由於需要經歷多個階段的命令才能完成映像檔的製作,所以上面的 Dockerfile 又被稱為 多階段 Dockerfile。
其中最重要的指令如下:
FROM
命令指定基底映像檔,並使用 AS
參數為階段命名。COPY
命令搭配 --from
參數複製先前階段的檔案和目錄。而這個 Dockerfile 的輸出為 最終階段 (final) 所產出的 Docker 映像檔!
使用多階段 Dockerfile 具有以下優點:
而且再複雜的應用程式都可以使用這個流程用 Dockerfile 建置,各階段的目的整理如下:
這個 Dockerfile 唯一需要注意的就是基礎映像檔使用的是每夜版,它位於 docker hub 的 rustlang/rust,而非 docker init
所生成指向 rust 官方儲存庫 的 rust:1.75.0-nightly-slim-bullseye
(這個 tag 不存在)!
建立映像檔就很簡單了,只需要執行:
docker compose up --build
就能讓應用程式跑起來!
由於雲端平台並非這次系列文的主軸,所以這裡用最單純 (傻瓜) 的方法,把所有東西都帶上 AWS EC2 後直接 docker compose up
但這樣也可以透過 Public IPv4 DNS 的 3000 端口存取到我們的應用程式 (因為用的是算力很低的 instance 所以就不等模型回答了😴,但也代表如果使用更好的硬體也能得到更好的體驗🥳):
而我們也可以把映像檔傳到 AWS ECR,來將容器部署到 AWS ECS 等服務,重點在於使用 Docker 我們可以很輕鬆的部署由 Rust 建構的應用程式,而我們可以得到所有 Rust 提供的好處。
好啦今天就到這裡囉,鐵人賽接近尾聲了,請大家欣賞一下今天畫出來的圖,明天見~
今天其實畫出不少覺得不錯的圖,精選了四張放在這裡以資尊重: