» Rust:使用ElasticSearch构建全文检索API » 4. 部署 » 4.1 Dockerfile

Dockerfile

Docker 允许开发人员将他们的应用程序以及所有依赖项打包成一个称为容器的单个单元。这确保了在不同环境(如开发、测试和生产)之间的一致性,减少了“但是它在我的机器上运行正常”的问题。

安装 Docker: https://docs.docker.com/engine/install/

调整优化代码

使用端口配置值 main.rs

更新 src/main.rs:

@@ -10,6 +10,8 @@ async fn main() {
     let c = infrastructure::parse_config(CONFIG_FILE);
     let wire_helper = application::WireHelper::new(&c).expect("Failed to create WireHelper");
     let app = adapter::make_router(&wire_helper);
-    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
+    let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", c.app.port))
+        .await
+        .unwrap();
     axum::serve(listener, app).await.unwrap();
 }

移除多余的问号

修改 src/application/executor/book_operator.rs:

@@ -14,10 +14,10 @@ impl BookOperator {
     }
 
     pub async fn create_book(&self, b: model::Book) -> Result<String, Box<dyn Error>> {
-        Ok(self.book_manager.index_book(&b).await?)
+        self.book_manager.index_book(&b).await
     }
 
     pub async fn search_books(&self, q: &str) -> Result<Vec<model::Book>, Box<dyn Error>> {
-        Ok(self.book_manager.search_books(q).await?)
+        self.book_manager.search_books(q).await
     }
 }

使用 let Some

修改 src/infrastructure/search/es.rs:

@@ -57,10 +57,12 @@ impl BookManager for ElasticSearchEngine {
             .await?;
         let response_body = response.json::<Value>().await?;
         let mut books: Vec<model::Book> = vec![];
-        for hit in response_body["hits"]["hits"].as_array().unwrap() {
-            let source = hit["_source"].clone();
-            let book: model::Book = serde_json::from_value(source).unwrap();
-            books.push(book);
+        if let Some(hits) = response_body["hits"]["hits"].as_array() {
+            for hit in hits {
+                let source = hit["_source"].clone();
+                let book: model::Book = serde_json::from_value(source).unwrap();
+                books.push(book);
+            }
         }
         Ok(books)
     }

添加 Makefile

Makefile 是在软件开发项目中使用的一种特殊文件(特别是在类 Unix 操作系统中),用于自动化从源代码编译和构建可执行程序或库。

添加 Makefile:

# Binary name
BINARY_NAME=lr_ft_books

.PHONY: lint

lint:
	@echo "Linting..."
	cargo clippy

build:
	@echo "Building $(BINARY_NAME)..."
	cargo build --release --bin $(BINARY_NAME)

Clippy 是 Rust 生态中知名的 lint 工具。

更新 Cargo.toml 以指定 bin:

@@ -3,6 +3,10 @@ name = "lr_fulltext_search_rust"
 version = "0.1.0"
 edition = "2021"
 
+[[bin]]
+name = "lr_ft_books"
+path = "src/main.rs"
+
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]

执行 make build 以构建二进制文件:

make build

这等同于 cargo build --release --bin lr_ft_books。它将在项目 target/release 目录中创建一个名为 lr_ft_books 的二进制文件。

然后,你可以直接运行它:

./target/release/lr_ft_books

服务的 Dockerfile

添加 Dockerfile:

# Use a Rust Docker image as the base
FROM rust:1.77-alpine3.19 as builder

# Set the working directory inside the container
WORKDIR /app

# Install necessary packages
RUN apk update && \
    apk add --no-cache musl-dev pkgconfig openssl-dev

# Copy the Rust project files to the container
COPY Cargo.toml .
COPY src/ /app/src

# Define a build argument with a default value
ARG BINARY_NAME=lr_ft_books

# Build the Rust project
# See: https://github.com/rust-lang/rust/issues/115430
RUN RUSTFLAGS="-Ctarget-feature=-crt-static" cargo build --release --bin ${BINARY_NAME}

# Start a new stage from Alpine Linux
FROM alpine:3.19

# Install required packages
RUN apk update && \
    apk add --no-cache libgcc

# Define an environment variable from the build argument
ENV BINARY_NAME=lr_ft_books

# Set the working directory inside the container
WORKDIR /app

# Copy the built binary from the previous stage to the current stage
COPY --from=builder /app/target/release/${BINARY_NAME} .

# Command to run the binary when the container starts
CMD ./${BINARY_NAME}

Alpine Linux 是一个轻量级的安全的 Linux 发行版,特别适用于容器化环境、嵌入式系统和资源受限环境。这些环境下效率和安全性至关重要。

准备好 Dockerfile 之后,就可以在 Docker Compose 里配合 Elasticsearch 一起启动了。