引言
學習Rust四個多月了,打算用Rust重寫我之前的web項目。Rust web中文資料非常少,學習之路很是坎坷,很多框架文檔少的可憐,甚至有的框架直接沒有文檔只能依靠源代碼里面的運行示例研究。但是不管多困難只要堅持下去就一定會有收獲。這篇博客是我這一周學習的收獲,它應該可以幫你解決以下幾個問題。
1.搭建一個簡單web服務運行程序。
2.使用cargo build --release 打包的時候速度很慢甚至出現網絡異常的問題。
3.使用cargo build --release 打包的時候配置文件和靜態資源找不到的問題。
4.使用debian發布Rust web服務。
5.使用alpine 發布Rust web服務。
搭建簡單的web服務程序
這里我使用的是warp框架,warp 是基於hyper框架開發的,是一款簡單靈活的web框架,不過有一點它沒有文檔。
1.使用 cargo new docker-rust-web 創建一個項目結構如下:
2.在控制台運行 cargo run 命令測試項目是否創建成功 正確結果如下圖
3.添加依賴 cargo項目的依賴在Cargo.toml 文件中管理,因為只是為了演示docker發布web,所以這里只添加最基本的兩個依賴分別是warp和tokio。完整配置如下:
# web 框架 warp = "0.3.1" tokio = { version = "1", features = ["full"] }
項目結構圖如下:
4.創建一個web服務。完整代碼如下:
use warp::{Filter, Rejection, Reply}; type Result<T> = std::result::Result<T, Rejection>; #[tokio::main] async fn main() { let user = warp::path!("user").and_then(user_handler); println!("Started server at localhost:8080"); warp::serve(user).run(([127, 0, 0, 1], 8080)).await; } async fn user_handler() -> Result<impl Reply> { Ok("ok") }
結構圖如下:
5.控制台執行cargo run 測試一下結果。注意運行的時候會下載依賴包速度很慢,網絡環境差的話很有可能會失敗。正確運行結果如下圖:
6.瀏覽器訪問地址 http://127.0.0.1:8080/user 查看結果 正確結果如下
到這里一個簡單的web應用就搭建完成了。
更換crates.io源
接下來我們來解決第二問題,編譯代碼的時候因為網絡的問題經常失敗。原因不用說了cargo默認的源是國外的,這個和npm、maven倉庫一樣的原因,更換成功國內的鏡像就好了。找到你cargo的安裝地址,我的電腦是mac 使用brew安裝的cargo 默認地是 /Users/fuping/.cargo 目錄下,如下圖:
如果cargo安裝路徑下有config配置文件那么只需要將這段代碼復制進去(注意如果你的機器默認帶有config文件 那么一定不要覆蓋掉 在最后面追加就可以了),如果沒有config那就自己創建一個。代碼如下:
[source.crates-io] replace-with = 'ustc' [source.ustc] registry = "https://mirrors.ustc.edu.cn/crates.io-index"
上面這個源是中科大的,還有一個是個人搭建的據說比較穩定,但是我沒試過,感興趣的可以試一下。代碼如下
[source.crates-io] replace-with = "rustcc" [source.rustcc] registry = "https://code.aliyun.com/rustcc/crates.io-index.git"
配置好之后直接就生效了,我們來編譯測試一下 控制台執行
CARGO_HTTP_MULTIPLEXING=false cargo fetch cargo build --release
如果配置的是第一個源會出現ustc的標記出現這個標記就說明配置成功了。
關於 cargo build 后配置文件和靜態資源找不到
web項目我已經習慣了使用yml配置文件,為此我特意自己模仿springboot的配置方式寫了一個切換配置文件的功能,運行起來沒有任何問題,但是運行編譯后的執行程序一直都是提示找不到文件。折騰很久發現把配置文件手動復制的到執行程序的同級目錄發現程序能正常運行了。所以我猜想cargo 編譯的時候不會把配置文件和靜態資源也編譯進去,所以我的建議是把配置文件和靜態資源放到和src同級目錄下這樣編譯后方便復制和讀取配置文件。
debian發布Rust web服務
1.創建debian文件夾。debian文件夾和src同級目錄 如下圖:
2.創建Dockerfile文件。
FROM rust:1.55 as builder RUN USER=root cargo new --bin docker-rust-web WORKDIR ./docker-rust-web COPY ./Cargo.toml ./Cargo.toml COPY ./application.yml ./application.yml RUN cargo build --release \ && rm src/*.rs target/release/deps/docker-rust-web* ADD . ./ RUN cargo build --release FROM debian:buster-slim ARG APP=/usr/src/app RUN apt-get update \ && apt-get install -y ca-certificates tzdata \ && rm -rf /var/lib/apt/lists/* EXPOSE 8080 ENV TZ=Etc/UTC \ APP_USER=appuser RUN groupadd $APP_USER \ && useradd -g $APP_USER $APP_USER \ && mkdir -p ${APP} COPY --from=builder /docker-rust-web/target/release/docker-rust-web ${APP}/docker-rust-web COPY --from=builder /docker-rust-web/application.yml ${APP}/application.yml RUN chown -R $APP_USER:$APP_USER ${APP} USER $APP_USER WORKDIR ${APP} CMD ["./docker-rust-web"]
項目結構圖:
注意 application.yml 是我特意添加的配置文件用來演示docker發布帶有配置文件的項目 這里配置文件沒有用到
3.創建.dockerignore文件。注意前面的點.dockerignore文件是用來標記忽略文件夾或文件的。如下圖:
3.在控制台運行docker構建命令 如下:
sudo docker build -t rust-debian -f ./debian/Dockerfile .
構建失敗了,看上面這張圖使用的源還是默認的地址。rust和java不一樣,java一次編譯到處運行。我們可以把編譯后的jar文件直接打包成docker鏡像。而rust在不同的機器編譯結果不一樣所以我們需要把源代碼復制然后在容器里面重新編譯,容器默認安裝cargo使用的源是默認地址。怎么解決呢?針對這個問題我折騰了很久,想到到能不能在創建容器的時候重新創建或者覆蓋容器默認cargo安裝路徑下的配置文件,我創建一個只復制文件的容器然后登陸容器查看cargo的默認安裝路徑是 /usr/local/cargo。那么我們來嘗試一下能不能實現我們所想的。
1.在debian文件夾下創建config文件內容如下:
[source.crates-io] replace-with = 'ustc' [source.ustc] registry = "https://mirrors.ustc.edu.cn/crates.io-index"
項目結構圖如下:
2.更新Dockerfile 文件新增內容如下:
COPY ./debian/config /usr/local/cargo RUN CARGO_HTTP_MULTIPLEXING=false cargo fetch
項目結構圖如下:
3.再次執行docker構建命令 sudo docker build -t rust-debian -f ./debian/Dockerfile . 結果如下:
出現了ustc的標記 但是還是編譯失敗了 如下圖:
rust編譯后會把- 自動轉化為_ 所以失敗了 我們更新一下Dockerfile 將- 改成_ 如下圖:
再次執行構建命令 sudo docker build -t rust-debian -f ./debian/Dockerfile . 這次構建成功了結果如下圖:
4.啟動dockder鏡像 執行命令如下:
docker run -p 8080:8080 rust-debian
結果如下圖:
5.瀏覽器訪問測試 結果如下
瀏覽器訪問不到, 我登錄容器在容器內部使用curl命令訪問能訪問到 排查很久發現是rust代碼寫的有問題:
在docker容器中使用127.0.0.1就會導致只能容器內部訪問 。更新代碼如下:
warp::serve(user).run(([0, 0, 0, 0], 8080)).await;
項目結構如下:
再次重新構建鏡像然后啟動 查看瀏覽器已經可以了
alpine發布Rust Web服務
alpine和debian發布流程基本一致 只是配置上稍有不同
1.創建alpine文件夾.
2.創建Dockerfile文件 內容如下:
FROM ekidd/rust-musl-builder:stable as builder RUN USER=root cargo new --bin docker-rust-web WORKDIR ./docker-rust-web COPY ./application.yml ./application.yml COPY ./Cargo.toml ./Cargo.toml COPY ./alpine/config /opt/rust/cargo RUN CARGO_HTTP_MULTIPLEXING=false cargo fetch RUN cargo build --release RUN rm src/*.rs ADD . ./ RUN rm ./target/x86_64-unknown-linux-musl/release/deps/docker_rust_web* RUN cargo build --release FROM alpine:latest ARG APP=/usr/src/app EXPOSE 8080 ENV TZ=Etc/UTC \ APP_USER=appuser RUN addgroup -S $APP_USER \ && adduser -S -g $APP_USER $APP_USER RUN apk update \ && apk add --no-cache ca-certificates tzdata \ && rm -rf /var/cache/apk/* COPY --from=builder /home/rust/src/docker-rust-web/target/x86_64-unknown-linux-musl/release/docker-rust-web ${APP}/docker-rust-web COPY --from=builder /home/rust/src/docker-rust-web/application.yml ${APP}/application.yml RUN chown -R $APP_USER:$APP_USER ${APP} USER $APP_USER WORKDIR ${APP} CMD ["./docker-rust-web"]
注意 alpine 容器默認的cargo 安裝地址是 /opt/rust/cargo
3.創建config文件內容如下:
# more config [build] # Target musl-libc by default when running Cargo. target = "x86_64-unknown-linux-musl" [target.armv7-unknown-linux-musleabihf] linker = "arm-linux-gnueabihf-gcc" [source.crates-io] replace-with = 'ustc' [source.ustc] registry = "https://mirrors.ustc.edu.cn/crates.io-index"
注意 和debian的config相比多了兩個 配置這個兩個配置是alpine容器安裝的cargo默認的配置
4.執行構建docker鏡像命令
sudo docker build -t rust-alpine -f ./alpine/Dockerfile .
5.啟動鏡像。
docker run -p 8080:8080 rust-alpine
6.瀏覽器測試。
本文是在一位大佬的博客基礎上完成 原文地址: https://blog.logrocket.com/packaging-a-rust-web-service-using-docker/