actix-webをコンテナで起動する

## はじめに

actix-webで作ったアプリケーションをコンテナ内で立ち上げてホストOSからアクセスするようにしたかったのですが、ホストOSからアクセスするところで躓いたので原因と解決法を自分用のメモもかねてご紹介します。


本記事に記載しているコードの完全版は以下で公開しています。

https://github.com/fcf-ebisawa/actix-web-docker-example

## まずは普通に実行する

今回は例として[公式サイト](https://actix.rs/)のHello Worldのコードを使う。

```rust:main.rs
use actix_web::{get, web, App, HttpServer, Responder};

#[get("/")]
async fn index() -> impl Responder {
    "Hello, World!"
}

#[get("/{name}")]
async fn hello(name: web::Path<String>) -> impl Responder {
    format!("Hello {}!", &name)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(index).service(hello))
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
}
```

`cargo run`でサーバーを起動した後、
http://127.0.0.1:8080/ にアクセスすると以下のような画面が表示されます。

![ホストOSでサーバーを起動](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3739073/d0a755a7-94d1-e2f9-2c63-eb505a5fb82c.png)

## コンテナを立ち上げる

### 準備

今回はdocker-composeを使うので、`Dokerfile`と`docker-compose.yml`を準備します。

```Dockerfile:Dockerfile
FROM rust:slim

# ビルドに必要なパッケージをインストール
RUN apt-get update && \
    apt-get install -y curl libssl-dev pkg-config build-essential

WORKDIR /app

COPY ./src ./src
COPY ./Cargo.toml ./Cargo.toml

RUN cargo build --release

CMD ["./target/release/actix-web-docker-example"]
```

```yml:docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./src:/app
    ports:
      - "8080:8080"
```

### 失敗

docker-composeコマンドでコンテナを立ち上げる。

```bash:bash
docker-compose up --build -d
```

コンテナが起動したら、ホストOSでhttp://127.0.0.1:8080/ にアクセスします。
すると、以下のように`ERR_EMPTY_RESPONSE`の画面が表示されます。

![chrome-error___chromewebdata_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3739073/a3403674-5444-279b-70cf-93c5367748cc.png)

### 原因

ポートフォワードの設定が反映されていないのかと思ったのですが、`docker ps`コマンドで状態を確認してみるとPORTSが表示されるので設定はできているようです。

```bash:docker ps
CONTAINER ID   IMAGE                          COMMAND                   CREATED         STATUS         PORTS                    NAMES
5c930489c9a3   actix_web_docker_example-app   "./target/release/ac窶ｦ"   4 minutes ago   Up 4 minutes   0.0.0.0:8080->8080/tcp   actix_web_docker_example-app-1
```

ここでPORTSの値が以下のようになっていることに注目してください。

```bash
PORTS 
0.0.0.0:8080->8080/tcp
```

公式のソースコードだと`127.0.0.1:8080`にバインドしていますが、`0.0.0.0:8080`にバインドしないといけないようです。
`127.0.0.1`と`0.0.0.0`の違いについては、検索してみるとたくさん情報がでてくるので調べてみてください。

### 成功

`0.0.0.0:8080`にバインドするようにソースを修正。
```rust:main.rs
// 省略

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(index).service(hello))
        // .bind(("127.0.0.1", 8080))?
        .bind(("0.0.0.0", 8080))?
        .run()
        .await
}
```

再度、docker-composeコマンドでコンテナを立ち上げる。

```bash:bash
docker-compose up --build -d
```

コンテナが立ち上がってからホストOSでhttp://127.0.0.1:8080/ にアクセスするとHello Worldが表示されました！

![コンテナでサーバーを起動](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3739073/4bdec6f4-c7c4-7c51-9123-eee6bda95a2c.png)

## おわりに

無事、actix-webのアプリケーションをコンテナ内で起動して、ホストOSからアクセスできるようになりました。今回はサンプルということでホスト名(0.0.0.0)やポート(8080)をハードコーディングしていますが、環境変数を使えば環境ごとに切り替えが容易になるのでそちらを推奨します。