Flutter×Firebaseでdocker環境の構築を行う

# はじめに
Flutterでアプリを作成するにあたって、Firebaseを使用しようと考えたのですが、
複数人で作成を行う場合にローカル環境を横展開できれば良いなと思い、環境構築を行いました。
(最終的にはローカルでFireStoreを使用できるようにする予定)
# 本題
### 前提
FlutterアプリにFirebaseを組み込む部分に関しては、本記事では割愛します。

1. Dockerfileの作成(Flutter)
2. Dockerfileの作成(Firebase)
3. imageの作成
4. docker-compose.yamlの作成
5. firebaseとの疎通確認

### 1. Dockerfileの作成(Flutter)
Flutter用のDockerfileをubuntuベースで作成。
AndroidSDK用に必要なものが多く、パッケージのインストール時にエラーが出た場合は適宜追加します。

```Dockerfile
FROM ubuntu:20.04

LABEL manintainer hogehoge

ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt-get update && apt-get install -y \
        autoconf \
        bash \
        bzip2 \
        curl \
        file \
        wget \
        unzip \
        gnupg \
        software-properties-common \
        vim \
        git \
        clang \
        cmake \
        ninja-build \
        pkg-config \
        libgtk-3-dev

#Flutter
RUN echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \
    && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
    && add-apt-repository ppa:maarten-fonville/android-studio
RUN apt-get update && \
    apt-get -y -q install \
    xz-utils libglu1-mesa openjdk-8-jdk google-chrome-stable android-studio \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*	

# Android
WORKDIR /usr/local
RUN mkdir -p Android/sdk
ENV ANDROID_SDK_ROOT /usr/local/Android/sdk
RUN mkdir -p .android && touch .android/repositories.cfg
RUN wget -O sdk-tools.zip https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip
RUN unzip sdk-tools.zip && rm sdk-tools.zip
RUN mv tools Android/sdk/tools
RUN cd Android/sdk/tools/bin && yes | ./sdkmanager --licenses
RUN cd Android/sdk/tools/bin && ./sdkmanager "build-tools;29.0.2" "patcher;v4" "platform-tools" "platforms;android-29" "sources;android-29"
ENV PATH "$PATH:/usr/local/Android/sdk/platform-tools"

# Dart
RUN sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
RUN apt-get update && \
    apt-get -y -q install \
    dart \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Download Flutter SDK
RUN git clone https://github.com/flutter/flutter.git
ENV PATH "$PATH:/usr/local/flutter/bin"

# Flutter Web
RUN flutter config --enable-web

RUN mkdir /flutter
WORKDIR /flutter
```

### 2. Dockerfileの作成(Firebase)
Firebase用のDockerfileをubuntuベースで作成。
今回はFirebase Local Emulator Suite をlocalで使用することを目的としています。
```Dockerfile
FROM ubuntu:20.04

RUN apt-get update -y

RUN apt-get install -y curl openjdk-11-jre-headless vim 

RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
    && apt-get install -y nodejs

RUN npm install -g firebase-tools

RUN mkdir /firebase
WORKDIR /firebase
```

### 3. imageの作成
各Dockerfileのあるディレクトリでそれぞれbuildします。
```
docker image build -t flutter:latest .
docker image build -t firebase:latest .
```

### 4. docker-compose.yamlの作成
それぞれimageの作成が完了したのち、docker-compose.yamlの作成をします。
プロセスが動いていないと、コンテナがすぐに落ちてしまうため、
`tty:true`の指定かそれでも解決しない場合はコマンドの指定をしておくとよいと思います。

環境変数やportの指定は適宜調整。
```docker
version: '3.4'

services:
  flutter: 
    container_name: "flutter"
    hostname: "flutter-base"
    image: "flutter:latest"
    volumes:
      - ../../hoge/:/flutter
    command: /bin/bash -c "/bin/bash"
    environment:
      - FIRESTORE_EMULATOR_HOST=localhost:8080
    tty: true

  firebase:
    container_name: "firebase"
    hostname: "firebase-tools"
    image: "firebase:latest"
    volumes:
      - ../../hoge/firebase/:/firebase
      - ../../hoge/firebase/bin/:/firebase
      - ../../hoge/firebase/config/:/firebase
    ports:
      - 8081:8081 # Emulator Suite UI
      - 5002:5002 # Firebase Hosting
      - 5001:5001 # Clound Functions
      - 9000:9000 # Realtime Database
      - 8080:8080 # Cloud Firestore
      - 8085:8085 # Cloud Pub/Sub
    working_dir: /firebase
    command: /bin/bash -c "/bin/bash"
    tty: true

networks:
  hogehoge:
    external: true
```
### 5. firebaseとの疎通確認
`docker-compose up -d`でコンテナの立ち上げを行います。
コンテナがそれぞれ立ち上がったら、firebaseのコンテナにアクセスし、
firebaseへのログインとプロジェクトの初期化を行います。
```
$ docker exec -it 1e4812964fba bash
root@firebase-tools:/firebase# firebase login --no-localhost
root@firebase-tools:/firebase# firebase init
```
その後、firebase.jsonのhost/portの指定を以下のように変更します。
```
  "emulators": {
    "auth": {
      "host": "0.0.0.0",
      "port": 9099
    },
    "functions": {
      "host": "0.0.0.0",
      "port": 5001
    },
    "firestore": {
      "host": "0.0.0.0",
      "port": 8080
    },
    "database": {
      "host": "0.0.0.0",
      "port": 9000
    },
    "hosting": {
      "host": "0.0.0.0",
      "port": 5002
    },
    "pubsub": {
      "host": "0.0.0.0",
      "port": 8085
    },
    "ui": {
      "enabled": true,
      "host": "0.0.0.0",
      "port": 8081
    },
    "storage": {
      "host": "0.0.0.0",
      "port": 9199
    },
    "singleProjectMode": true
  },
```

上記完了後、コンテナ内で`firebase emulators:start`を叩くと、以下のように
UIViewのURLが出るので、アクセスをします。
```
┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://127.0.0.1:8081/               │
└─────────────────────────────────────────────────────────────┘
┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ 0.0.0.0:9099   │ http://127.0.0.1:8081/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions      │ 0.0.0.0:5001   │ http://127.0.0.1:8081/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ 0.0.0.0:8080   │ http://127.0.0.1:8081/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Database       │ 0.0.0.0:9000   │ http://127.0.0.1:8081/database  │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Hosting        │ 0.0.0.0:5002   │ n/a                             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Pub/Sub        │ 0.0.0.0:8085   │ n/a                             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Storage        │ 0.0.0.0:9199   │ http://127.0.0.1:8081/storage   │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at 127.0.0.1:4400
  Other reserved ports: 4500, 9150

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
```
`http://localhost:8081/`で以下のように表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1295486/5fda755d-b3eb-dc29-fd8e-0ff57cb45d39.png)

疎通確認のため、適当なデータを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1295486/1c77537d-14d5-c3fc-7f6d-a3b74bc6e691.png)


下記サイトにあるcurlのコマンドをflutterコンテナから叩いて、データが削除されていれば
コンテナ間での通信ができていることを確認できます。
https://firebase.google.com/docs/emulator-suite/connect_firestore?hl=ja#:~:text=your%2Dproject%2Did%22-,%E3%83%86%E3%82%B9%E3%83%88%E9%96%93%E3%81%A7%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%82%92%E3%82%AF%E3%83%AA%E3%82%A2%E3%81%99%E3%82%8B,-Firestore%20%E3%81%AE%E6%9C%AC%E7%95%AA

networkを作成している場合は、`http://{コンテナ名}`で指定してあげます。

```
root@flutter-base:/flutter# curl -v -X DELETE "http://firebase:8080/emulator/v1/projects/hoge-project/databases/(default)/documents"
*   Trying 172.18.0.2:8080...
* TCP_NODELAY set
* Connected to firebase (172.18.0.2) port 8080 (#0)
> DELETE /emulator/v1/projects/hoge-project/databases/(default)/documents HTTP/1.1
> Host: firebase:8080
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/json
< content-length: 4
< 
{
}
* Connection #0 to host firebase left intact
```
これでEmulatorの立ち上げと疎通確認までが完了になります。

# 最後に
firestoreをローカルで使えるようになることは大きなメリットだと思っているので、
具体的なアプリとの実装含め、引き続き調べてみようと思います。