はじめに
RustでHttps経由で外部のAPIを叩くエンドポイントを作成したところ、デプロイしたあとに500系のエラーが発生していたので対処をしました
その過程を忘れないようにまとめておきます
問題
以下のコードを実装しました
main.rs
#[tokio::main]
async fn main() {
let client = reqwest::Client::new();
let result = client.get("https://jsonplaceholder.typicode.com/users/1").send().await;
println!("{:?}", result);
}
そしてデプロイをして実行すると以下のエラーが発生しました
Err(reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("jsonplaceholder.typicode.com")), port: None, path: "/users/1", query: None, fragment: None }, source: hyper::Error(Connect, Ssl(Error { code: ErrorCode(1), cause: Some(Ssl(ErrorStack([Error { code: 337047686, library: "SSL routines", function: "tls_process_server_certificate", reason: "certificate verify failed", file: "../ssl/statem/statem_clnt.c", line: 1916 }]))) }, X509VerifyResult { code: 20, error: "unable to get local issuer certificate" })) })
検証
まずはローカルで実行します
$ cargo run
すると問題なくレスポンスが返ってきました
Ok(Response { url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("jsonplaceholder.typicode.com")), port: None, path: "/users/1", query: None, fragment: None }, status: 200, headers: {"date": "Thu, 29 Jun 2023 01:50:33 GMT", "content-type": "application/json; charset=utf-8", "content-length": "509", "connection": "keep-alive", "x-powered-by": "Express", "x-ratelimit-limit": "1000", "x-ratelimit-remaining": "999", "x-ratelimit-reset": "1686752728", "vary": "Origin, Accept-Encoding", "access-control-allow-credentials": "true", "cache-control": "max-age=43200", "pragma": "no-cache", "expires": "-1", "x-content-type-options": "nosniff", "etag": "W/\"1fd-+2Y3G3w049iSZtw5t1mzSnunngE\"", "via": "1.1 vegur", "cf-cache-status": "HIT", "age": "9476", "accept-ranges": "bytes", "report-to": "{\"endpoints\":[{\"url\":\"https:\/\/a.nel.cloudflare.com\/report\/v3?s=AZ%2B10legeLukYLv3vRkA5DlgJgxxM7z%2B3CcwUoKcPnAUGdLb6vjFMoDiXfE5sDcSSlD81xx1290OKGWXfPYi%2Bs6RTS5ikwTcgGvOdIdvmNy4lcNUm3QKB8qVa5lPP9sGmhOwE392Z5zpaFjI6azm\"}],\"group\":\"cf-nel\",\"max_age\":604800}", "nel": "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}", "server": "cloudflare", "cf-ray": "7dea84f18b2d25fe-NRT", "alt-svc": "h3=\":443\"; ma=86400"} })
次にデプロイしたK8sのポッドの中からcurlで確認します
$ kubectl --context=test -n api rust-reqwest -- bash
$ curl https://jsonplaceholder.typicode.com/users/1
こちらも問題なく返ってきました
このことからコンテナ自身に問題がありそうだと予測できました
まずはコンテナをローカルでビルドします
Dockerfile
FROM rust:1.69-slim-buster as build-stage
WORKDIR /build
RUN apt-get update && \
apt-get install -y curl libssl-dev pkg-config build-essential
COPY . .
RUN cargo test && \
cargo build --release
FROM debian:buster-slim
RUN apt-get update && apt-get install -y libssl-dev
COPY --from=build-stage /build/target/release/rust-reqwest /usr/bin
EXPOSE 10500
CMD ["/usr/bin/rust-reqwest"]
$ docker build . -t rust-reqwest
$ docker run rust-reqwest
すると同じエラーが発生しました
解決方法
Dockerfileにca-certificates
を追加することで解決しました
FROM rust:1.69-slim-buster as build-stage
WORKDIR /build
RUN apt-get update && \
apt-get install -y curl libssl-dev pkg-config build-essential
COPY . .
RUN cargo test && \
cargo build --release
FROM debian:buster-slim
RUN apt-get update && apt-get install -y libssl-dev ca-certificates
COPY --from=build-stage /build/target/release/rust-reqwest /usr/bin
EXPOSE 10500
CMD ["/usr/bin/rust-reqwest"]
$ docker build . -t rust-reqwest
$ docker run rust-reqwest
Ok(Response { url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("jsonplaceholder.typicode.com")), port: None, path: "/users/1", query: None, fragment: None }, status: 200, headers: {"date": "Thu, 29 Jun 2023 01:50:33 GMT", "content-type": "application/json; charset=utf-8", "content-length": "509", "connection": "keep-alive", "x-powered-by": "Express", "x-ratelimit-limit": "1000", "x-ratelimit-remaining": "999", "x-ratelimit-reset": "1686752728", "vary": "Origin, Accept-Encoding", "access-control-allow-credentials": "true", "cache-control": "max-age=43200", "pragma": "no-cache", "expires": "-1", "x-content-type-options": "nosniff", "etag": "W/\"1fd-+2Y3G3w049iSZtw5t1mzSnunngE\"", "via": "1.1 vegur", "cf-cache-status": "HIT", "age": "9476", "accept-ranges": "bytes", "report-to": "{\"endpoints\":[{\"url\":\"https:\/\/a.nel.cloudflare.com\/report\/v3?s=AZ%2B10legeLukYLv3vRkA5DlgJgxxM7z%2B3CcwUoKcPnAUGdLb6vjFMoDiXfE5sDcSSlD81xx1290OKGWXfPYi%2Bs6RTS5ikwTcgGvOdIdvmNy4lcNUm3QKB8qVa5lPP9sGmhOwE392Z5zpaFjI6azm\"}],\"group\":\"cf-nel\",\"max_age\":604800}", "nel": "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}", "server": "cloudflare", "cf-ray": "7dea84f18b2d25fe-NRT", "alt-svc": "h3=\":443\"; ma=86400"} })
おわりに
先輩の解決までの思考などを学びながら自分でもすばやく解決できるようになりたいなと思いました
参考