20200520のdockerに関する記事は10件です。

alpineでC言語依存モジュールを pip install した時の時間を計測してみた

Let's 計測

alpineでC言語依存モジュールを pip install すると激重になる話 のおまけ

素直にpip installした場合に、どれくらい時間がかかるか試してみた。
手元にあった機械学習用のrequirements.txtで実行してみると以下のようなログがでてきたので

Building wheels for collected packages: backcall, h5py, kiwisolver, matplotlib, numpy, pandas, pathspec, Pillow, PyYAML, pyzmq, regex, retrying, scikit-learn, scipy, tornado, typed-ast

開発時にも使用頻度高そうなモジュールだけピックアップして計測することにした。

  • 実行してみるリスト
    • numpy
    • pandas
    • Pillow
    • scipy
    • scikit-learn
    • h5py
    • matplotlib
    • regex

実験環境

手元で実行できる環境が二種類あったので両方とも試してみる。

  • case1: MacBookAir 2015 13inch intel-corei5 8GB DDR3 ・ docker desktop
  • case2: AMD Ryzen9 3900X, M.2 ASM2NE6500GTTD, 8x2GB DDR4 ・ ubuntu18.04(WSL2 integration)

計測方法

timeコマンドを頭につける事で実行時間を計測する。
どちらのケースも同一のdockerfileで実行しており、RUN命令文中にtime pip install hogeを埋め込む形を取っている。

FROM python:3.8-alpine3.11
RUN apk update \
  && apk add --virtual .build --no-cache openblas-dev lapack-dev freetype-dev \
  gfortran libxml2 g++ gcc zip unzip cmake make \
  libpng-dev openssl-dev musl libffi-dev python3-dev libxslt-dev \
  libxml2-dev jpeg-dev \
  && apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/testing hdf5-dev

RUN pip install --upgrade --no-cache-dir pip setuptools wheel && \
  time pip install --no-cache-dir Cython && \
...

こんな感じ。
docker build中に出力されるログをまとめたのが次の表となる。

結果

※ Cythonはalpineでもコンパイル不要のモジュールであり、Cython-0.29.16-py2.py3-none-any.whlのような.whl形式で降ってきます。非依存モジュールとの比較としてここに載せています。1

module / time case1 case2
real user sys real user sys
Cython 3.07s 2.33s 0.42s 1.52s 0.99s 0.07s
numpy 5m 28.27s 7m 6.90s 22.11s 2m 11.15s 3m 23.69s 6.05s
pandas 31m 46.58s 30m 36.25s 0m 53.83s 14m 8.24s 13m 53.10s 15.88s
Pillow 50.81s 44.09s 5.99s 24.79s 19.88s 1.69s
scipy 30m 45.99s 36m 29.33s 1m 45.89s 12m 52.81s 17m 54.87s 42.58s
scikit-learn 14m 38.63s 14m 3.37s 28.98s 6m 33.10s 6m 24.84s 10.11s
h5py 3m 45.58s 3m 34.79s 9.30s 1m 45.87s 1m 42.42s 4.18s
matplotlib 2m 51.50s 2m 35.53s 13.59s 1m 21.77s 1m 13.70s 6.75s
regex 30.52s 28.84s 1.24s 13.75s 13.06s 0.34s

おっっっっそい
ものっっっっっそい、おっっっっっっそい

case1だとnumpyだけでも5分かかるし、pandas・scipyに至っては30分を超えている。scikit-learnに至ってはnumpy・scipyに依存しているので実際のところ一時間は覚悟しないといけない。
挙げた全部合わせると一時間半超だ。
これがどういう事になるか。一日8時間作業すると仮定すると

一日たったの5回程度しかデプロイする事ができないことになる

気軽に開発環境に最新ブランチを充てたり、軽微なバグ修正の確認すら困難になる。作業時間も含めると更に減る。

一応、メモリが倍になると処理速度も倍になるので時間の短縮はできなくもない。
awsを使ったりしているのならメモリ量が多いインスタンスタイプを選択することで時短は可能だ。
しかし、性能的にcase1の二倍以上のcase2だと半分以下の時間で済むとはいえ、全部で40分ほどかかっているのは事実。
一日あたり最高で12回ほどのデプロイしかできない。


ちなみに、表の上から順番にpip installしていったので一部モジュールの実行時間には依存モジュールのインストール実行時間も含まれる。
kiwisolverもtar.gzで降ってきているようなのでmatplotlib単体はもう少し早いはず。

  Successfully built pandas
  Installing collected packages: six, python-dateutil, pytz, pandas
  Successfully installed pandas-0.25.3 python-dateutil-2.8.1 pytz-2019.3 six-1.14.0
------
  Successfully built scikit-learn
  Installing collected packages: joblib, scikit-learn
  Successfully installed joblib-0.14.1 scikit-learn-0.22.2.post1
-----
  Successfully built matplotlib kiwisolver
  Installing collected packages: cycler, kiwisolver, pyparsing, matplotlib
  Successfully installed cycler-0.10.0 kiwisolver-1.2.0 matplotlib-3.2.1 

まとめ

軽いものでも1個あたり1~2分かかるレベルなので、なるべくC言語依存モジュールはビルドしないで済む運用を考える必要がある。condaを使う、は今無しで

余談pip

pip install時はモジュールのwheel化が完了した直後に、再度whlを展開し直して必要な生成物をまるっとsite-package直下に移動する。pythonで書かれたコードからビルドされた共有ライブラリ郡*.soも含めて全てだ。
alpineのようにpython3.8のバイナリが/usr/local/bin直下にいるとすると/usr/local/lib/python3.8/site-packages/に移動する。

再展開は手間のように見えるがこれはPEPに則った安全なモジュールの導入法である。PEP491にもそう書かれている2


試しに、ビルド済みのwhlファイルをunzipして.soファイルを検索してみる。

./numpy/linalg/lapack_lite.cpython-38-x86_64-linux-gnu.so
./numpy/linalg/_umath_linalg.cpython-38-x86_64-linux-gnu.so
./numpy/core/_operand_flag_tests.cpython-38-x86_64-linux-gnu.so
...

それぞれのwhlファイルをpip installで導入後にライブラリパスに対して.soファイルを検索してみると、モジュール毎にビルド済みファイルが存在しているのを確認できる。

/usr/local/lib/python3.8/site-packages/numpy/linalg/lapack_lite.cpython-38-x86_64-linux-gnu.so
/usr/local/lib/python3.8/site-packages/numpy/linalg/_umath_linalg.cpython-38-x86_64-linux-gnu.so
/usr/local/lib/python3.8/site-packages/numpy/core/_operand_flag_tests.cpython-38-x86_64-linux-gnu.so
...
/usr/local/lib/python3.8/site-packages/pandas/io/sas/_sas.cpython-38-x86_64-linux-gnu.so
/usr/local/lib/python3.8/site-packages/pandas/_libs/hashtable.cpython-38-x86_64-linux-gnu.so
...

これでようやくpythonでimportできる訳だが、
裏を返せばpythonがモジュールを検索するパスに生成物さえ居れば使用できる。
なので元記事の回避法では、python関連以外は後に入れること前提で/usr/local/直下をdockerのマルチステージビルドを使ってえいやでコピペしている。

参考文献


  1. ここに書いてあるように、none-anyは非OS・アーキテクチャ依存のモジュールでほとんどの場合pureなpythonで作成されています。公式PEP425も参照。 

  2. 余談中の余談だがanacondaは独自のモジュールインストール方法を採っているのでPEPに即していない。混ぜるな危険を引き起こしている一端。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails6について

前提

Rails6について学んだことを書いていきます。

macOS
Docker・Docker Composeインストール済み

本題

仮想環境を使う理由

Railsアプリケーションの開発をしようとするときに問題がある。
それは開発用マシン(作業マシン)と実運用を行うマシン(プロダクションマシン)の環境が異なること。
ここで言う環境とは、OSの種類やバージョン、あるいはOS上で動くソフトウェアやライブラリの種類やバージョンをさす。
Ruby on RailsはWindowsでもMacOSでもLinuxでも動作するように設計されている。
しかし、Railsを拡張するために導入するGemパッケージは必ずしもそうではない。
仮に動いたとしても、特定の環境では振る舞いが微妙に異なったり、不具合が出たりすることがある。

この問題の解決方法として、作業マシンとプロダクションマシンの環境を一致させること。
そこで現れるのが仮想環境と言うオプション。
Windows、MacOS、ubuntuなどのデスクトップOSの上に仮想環境を構築し、そこにLinuxベースのServerOSをインストールする。

開発中のRailsアプリケーションは仮想環境で動作させる。
デスクトップOS上のWebブラウザからRailsアプリケーションにアクセスして動作を確認。
他方、Railsアプリケーションのソースコードは共有フォルダの機能によって、デスクトップOS上で開いて編集する。
こうすれば、使い慣れたテキストエディタやIDEを使い続けながら、プロダクションマシンと同等の環境下でRailsアプリケーションの開発が行える。

Dockerとは

仮想環境を提供するオープンソース・ソフトウェア。
設定の容易さや起動の早さが人気。
個々の仮想環境をコンテナと呼ぶ。
コンテナの内容はDockerfileと呼ばれるテキストファイルで記述される。
このファイルがあれば様々なOS上でコンテナを復元できる。

Docker-composeとは

Dockerを用いて、Railsアプリケーションを開発したり、プロダクション環境で動かすとき、Railsアプリケーションとデータベースサーバーを別々のコンテナとして構築するのが一般的。
Docker-composeはこれらの複数のコンテナをまとめて起動・停止するためのツール。
docker-compose upというコマンドを実行するだけで、Webアプリケーションを構成する全てのコンテナ群が動き出す。

Rails開発環境の構築

DockerとDocker Composeを用いてRails開発用コンテナ群を構築。

terminal.
$ git clone https://github.com/oiax/rails6-compose.git
$ cd rails6-compose
$ ./setup.sh

コンテナ群の起動と停止

コンテナ群を全て起動するにはターミナルで下記のコマンドを実行。
オプション-dはコンテナをデーモン(バックグラウンドプロセス)として動かすためのもの。

terminal.
$ docker-compose up -d

コンテナ群を停止するにはターミナルで下記のコマンドを実行。

terminal.
$ docker-compose stop

webコンテナにログイン

terminal.
$ docker-compose exec web bash

Railsのバージョン確認

bash-4.4$ rails --version

Rails6.0.0と結果出力されればOK!

rails newコマンドで新規Railsアプリケーションの作成。

bash-4.4$ rails new アプリ名 -d postgresql --skip-test-unit

--skip-test-unitはTest::Unit関連のコードの生成を省略するためのオプション。

中身の確認

bash-4.4$ ls -a アプリ名/

Gemfileの編集

gem "bcrypt"         #パスワードの暗号化
gem "rails-i18n"     #railsが出力するエラーメッセージ、日付、時刻、通貨単位などの翻訳ファイルを集めたもの
gem "kaminari"       #ページネーション機能
gem "date_validator" #日付のバリデーションを行う
gem "valid_email2"   #メールアドレスへのバリデーションを行う
gem "nokogiri"       #XML/HTMLの解析・生成のため

※email_validatorはバージョンアップでバリデーションの基準が緩くなり、実用性が乏しくなった。
例えば、@を2個含むようなメールアドレスを有効と判定してしまう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Zabbix5.0とZabbix agent2をubuntu18.04とvagrantとdocker-composeで動かしてみたけど自分のDockerをまだ監視できない

はじめに

申し訳ないのですがこれは2020/05/20時点の私の技術力だとうまくいかなかったことを恥を忍んで報告する記事です。目的は私のこの徒労がほかの人のためになるかもしれないからです。この手順から何かすればうまくdocker監視できるかもしれないので、もしできた人はコメントください。

一応Zabbix5.0をubuntu18.04とvagrantとdocker-composeで一から環境をセットアップして動かすことはできたのでとりあえずそこまでやりたい人はこの記事を参考にしていただければ幸いです。

zabbix5.0

zabbix5.0はこの5月にリリースされました。

Zabbix 5.0LTSの新機能 https://www.zabbix.com/jp/whats_new_5_0

特にDockerも監視できるというZabbix agent2が使用できるのがDocker使いには魅力的かと思います。

5 What's new in Zabbix 5.0.0 https://www.zabbix.com/documentation/current/manual/introduction/whatsnew500#zabbix_agent_2

これの前にZabbix5.0とZabbix agent2をCentOS7のDockerで試したけどダメでした

私も興味があったので自分なりにCentOS7で試してみました。

しかしCentOS7だと、docker-composeによるzabbi5とZabbix agent2でDocker監視はできませんでした。正確に言うとDocker関連のアイテムのみ取得できませんでした。

早い話が、現時点では監視対象がRHEL8系だとDockerを監視することがそもそもできない構造になっています。

順を追って書きますと、まず公式のdocker-compose.ymlにはagent2を使用できるように記述されていませんでした。これは正直盲点でした。まさかDockerで動くのにdocker-compose.ymlに存在すらしないとは思わず......

そこで後述する方法でagent2を書いてもDockerのアイテムは取得できません。ほかのテンプレートを登録するとそれらはごく普通に値を取得できていたのではじめは自分の設定の問題かなあと考えておりました。

しかし調べてみると、Zabbix agent2はCentS7をサポートしてませんでした。RHEL8系からでした。

zabbix Agent 2 https://www.zabbix.com/documentation/current/manual/concepts/agent2

しかし、現在DockerはCentOS8をサポートしていません。

Docker公式 CentOSでのDockerインストール https://docs.docker.com/engine/install/centos/

試しに私も無理やり入れて動かしてみましたがimageのpullすらできませんでした。どうもRHEL系はpodmanというdocker互換のものがいるらしいです。これがzabbixのDocker監視機能で引っかかるかはまったくわかりません。多分ダメだと思いますが。

......

とこんなわけで、現時点では監視対象がRHEL8系だとDockerを監視することがそもそもできない構造になっています。

これらはただ表面のWhats Newとか読んでるだけだと気づけない落とし穴です。まんまと私ははまりました......

でもこのままDockerが監視できるのかどうかわからないのもどうかと思いますので、zabbix公式ページにおいて言及されているubuntu18.04にDockerをインストールし、docker-compose.ymlを編集して実際に動作させてみました。

ですがタイトル通りダメでした。

実際zabbixがどんなことができるかを調べるためにはdocker-composeで試したほうが早いとは思います。ただ今回のように自分のできなさにすぐたどり着いてしまうのがつらいですが......

では本題です。今回は

  • VagrantでUbuntu18.04を実行する
  • Dockerとdocker-composeをインストールする
  • 公式のzabbix用docker-composeをpullして動かす
  • zabbix-agent2 imageをdocker-composeで動かす
  • Dockerを監視項目に追加して結果を確認する

という感じでやっていきます。

VagrantでUbuntu18.04を実行する

Docker for Desktopはかなり便利ですが少し特殊な挙動をしてコンテナの外の話になると途端に扱いづらくなります。たとえばDockerホストそのものにLinuxのコマンドを実行したいときなどは方法がほぼありません。

そこでVirtualboxとVagrantを使います。vagrantでOSインストールとか設定とかの手間を省きたいためです。

これらを事前にインストールしておきます。Hyper-Vを使っている場合だとVirtualboxを使えない場合があるので共存できるかできないかは適宜調査をお願いします。

virtualbox https://www.virtualbox.org/

vagrant https://www.vagrantup.com/

準備が完了したらCLIの所定のディレクトリでディレクトリを用意し、そのディレクトリに入ってからvagrant initします。

$ mkdir ubuntu18
$ cd ubuntu18
$ vagrant init

作成されたvagrantfileを以下のようにします。これでubuntu18.04がインストールされ、zabbix完成後はwebブラウザで192.168.33.10にアクセスすればzabbixのweb画面を見ることができます。

Vagrant.configure("2") do |config|
  config.vm.box = "bento/ubuntu-18.04"
  config.vm.network "private_network", ip: "192.168.33.10"
end

完了したらvagrant upでubuntuのインストールと起動を行います。だいぶ時間がかかります。

$ vagrant up

完了したらvagrant sshでubuntu内に入り、root権限に移行します。

$ vagrant ssh
$ sudo su -
# 

Dockerとdocker-composeをインストールする

楽をするため、今回はdockerの公式が用意しているというshスクリプトを使います。これもしばらく待つとdockerのバージョンが表示され完了します。

参考:最も簡単なDockerの公式セットアップ方法(CentOS/Ubuntu) https://qiita.com/zembutsu/items/bedb18e1061303e217b8

# curl -fsSL get.docker.com -o get-docker.sh
# sudo sh ./get-docker.sh

実行結果は以下のようになります。

# Executing docker install script, commit: 
[中略]
+ sh -c docker version
Client: Docker Engine - Community
 Version:           19.03.9
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        9d988398e7
 Built:             Fri May 15 00:25:18 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.9
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       9d988398e7
  Built:            Fri May 15 00:23:50 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
If you would like to use Docker as a non-root user, you should
now consider
adding your user to the "docker" group with something like:

  sudo usermod -aG docker your-user

Remember that you will have to log out and back in for this to
take effect!

WARNING: Adding a user to the "docker" group will grant the ability to run
         containers which can be used to obtain root privileges on the
         docker host.
         Refer to https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
         for more information.

次にdocker-composeをcurlでインストールします。

# sudo curl -L https://github.com/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

実行権限を付与しておきます。

# sudo chmod +x /usr/local/bin/docker-compose

docker-comoposeのバージョンを確認します。1.25.5となっていればよいです。

docker-compose --version
# docker-compose version 1.25.5, build 8a1c60f6

公式のzabbix用docker-composeをpullして動かす

では本題のzabbix公式docker-composeのファイル群をpullします。

github zabbix-docker https://github.com/zabbix/zabbix-docker

# git clone https://github.com/zabbix/zabbix-docker.git

cloneしてできたディレクトリに移動してdocker-compose.yamlを指定して起動します。今回はalpine版postgresDBのものを実行します。latestにしているのはzabbix公式がpushしているイメージをそのまま使えるようにするためです。

# cd zabbix-docker
# docker-compose -f docker-compose_v3_alpine_pgsql_latest.yaml up -d

docker-compose psでコンテナが全部起動しているか確認します。db_dataはexit0してますがこれで問題ないそうです。

# docker-compose -f docker-compose_v3_alpine_pgsql_latest.yaml ps
WARNING: Some services (zabbix-agent, zabbix-java-gateway, zabbix-proxy-mysql, zabbix-proxy-sqlite3, zabbix-server, zabbix-snmptraps, zabbix-web-apache-pgsql, zabbix-web-nginx-pgsql) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.
     Name           Command          State           Ports     
---------------------------------------------------------------
zabbix-docker_   sh               Exit 0                       
db_data_mysql_                                                 
1                                                              
zabbix-docker_   sh               Exit 0                       
db_data_pgsql_                                                 
1                                                              
zabbix-          docker-          Up                           
docker_mysql-    entrypoint.sh                                 
server_1         mysql ...                                     
zabbix-docker_   docker-          Up                           
postgres-        entrypoint.sh                                 
server_1         postgres                                      
zabbix-          /sbin/tini --    Up                           
docker_zabbix-   /usr/bin/doc                                  
agent_1          ...                                           
zabbix-          docker-          Up                           
docker_zabbix-   entrypoint.sh                                 
java-gateway_1   /usr/ ...                                     
zabbix-          /sbin/tini --    Up             0.0.0.0:10071-
docker_zabbix-   /usr/bin/doc                    >10051/tcp    
proxy-mysql_1    ...                                           
zabbix-          /sbin/tini --    Up             0.0.0.0:10061-
docker_zabbix-   /usr/bin/doc                    >10051/tcp    
proxy-           ...                                           
sqlite3_1                                                      
zabbix-          /sbin/tini --    Up             0.0.0.0:10051-
docker_zabbix-   /usr/bin/doc                    >10051/tcp    
server_1         ...                                           
zabbix-          /usr/bin/super   Up             0.0.0.0:162->1
docker_zabbix-   visord -c /e                    162/udp       
snmptraps_1      ...                                           
zabbix-          docker-          Up (healthy)   0.0.0.0:80->80
docker_zabbix-   entrypoint.sh                   80/tcp, 0.0.0.
web-apache-      /usr/ ...                       0:443->8443/tc
pgsql_1                                          p             
zabbix-          docker-          Up (healthy)   0.0.0.0:8081->
docker_zabbix-   entrypoint.sh                   8080/tcp, 0.0.
web-nginx-                                       0.0:8443->8443
pgsql_1                                          /tcp          
root@vagrant:~/zabbix-docker# 

webブラウザにアクセスしてみます。

http://192.168.33.10/

1login.png

web画面が表示できたようです。以下の初期パスワードでログインします。

Username:Admin
Password:zabbix

完了すると以下の画面にたどり着きます。

2top.png

Zabbix agent is not available (for 3m)とかでているのでこれだけ解消します。

左側のリボンでConfigrationを押して、hostsを選択します。

2secehost.png

Zabbix serverのリンクをクリックします。

4hosts.png

DNS nameをzabbix-agentと入力し、Connect toをDNSでボタンを押しておきます。終わったらUpdateボタンを押します。

5dnssettingchange.png

終わったら2分くらい放置した後で左側リボンのMonitoringのLatest dataを押します。するとデータが入ってきていることが確認できます。これでzabbix agentから情報を取得できるようになりました。

6latestdata.png

top画面からもエラーは消えました。これで大丈夫です。

7topafter.png

とはいえ後述するtemplateの追加でdockerを追加してもこんな感じで何も情報が入ってきません。だからagent2を用意する必要があります。

8agent1docker.png

確認ができたのでいったんコンテナをdownします。downするときもこんな感じでファイル指定がいります。少々面倒ですね。

# docker-compose -f docker-compose_v3_alpine_pgsql_latest.yaml down

docker-compose up -dで動かせるようにする

upとかdownとかのコマンドが大変面倒なことになっているので先ほど使用したファイルをコピーしてそれの名前をdocker-compose.yamlにします。これでだいぶ使いやすくなります。

# cp docker-compose_v3_alpine_pgsql_latest.yaml docker-compose.yaml

ちゃんとファイルがいることが確認できます。

# ls | grep docker-compose.yaml
docker-compose.yaml

zabbix-agent2 imageをdocker-composeで動かす

出来上がったdocker-compose.yamlに追記します。

vimでもいいと思いますが私はvscodeのremote developmentを使って直に編集できるようにしています。

追記することは非常に単純で、zabbix-agentの部分をコピペします。

そのあとzabbix-agentの情報のimage:部分をzabbix/zabbix-agent2:alpine-5.0-latest、つまりdocker imageのzabbix agent2に変え、最後にpostsのホスト側を10050から10060にするだけです。

 zabbix-agent2:
  image: zabbix/zabbix-agent2:alpine-5.0-latest
  ports:
   - "10060:10050"
  volumes:
   - /etc/localtime:/etc/localtime:ro
   - /etc/timezone:/etc/timezone:ro
   - ./zbx_env/etc/zabbix/zabbix_agentd.d:/etc/zabbix/zabbix_agentd.d:ro
   - ./zbx_env/var/lib/zabbix/modules:/var/lib/zabbix/modules:ro
   - ./zbx_env/var/lib/zabbix/enc:/var/lib/zabbix/enc:ro
   - ./zbx_env/var/lib/zabbix/ssh_keys:/var/lib/zabbix/ssh_keys:ro
  links:
   - zabbix-server:zabbix-server
  deploy:
   resources:
    limits:
      cpus: '0.2'
      memory: 128M
    reservations:
      cpus: '0.1'
      memory: 64M
   mode: global
  env_file:
   - .env_agent
  privileged: true
  pid: "host"
  networks:
   zbx_net_backend:
    aliases:
     - zabbix-agent
     - zabbix-agent-passive
     - zabbix-agent-alpine
  stop_grace_period: 5s
  labels:
   com.zabbix.description: "Zabbix agent"
   com.zabbix.company: "Zabbix LLC"
   com.zabbix.component: "zabbix-agentd"
   com.zabbix.os: "alpine"

そのあとでagent1にあたるものは全部コメントアウトしておいてください。とはいえ一度agent1とagent2をコンテナで動かしていても結果に変化はありませんでした。

#  zabbix-agent:
#   image: zabbix/zabbix-agent:alpine-5.0-latest
#   ports:
#    - "10050:10050"

[中略]

#   labels:
#    com.zabbix.description: "Zabbix agent"
#    com.zabbix.company: "Zabbix LLC"
#    com.zabbix.component: "zabbix-agentd"
#    com.zabbix.os: "alpine"

終わったらdocker-compose up -dをします。

# docker-compose up -d

Dockerを監視項目に追加して結果を確認する

ここからがうまくいかなかった話です。

再びconfigrationのhostsに移動し、create hostをクリックします。

9createhost.png

hostnameは適当でよいです。DNS nameをzabbix-agent2と入力し、Connect toをDNSでボタンを押しておきます。

10hostadd.png

さらにtemplateタブを押して移動してLink new templates
行のselectを押し、Applications → Dockerと選びます。

11templateadd.png

終わったらUpdateボタンを押します。

そうすればうまくいくと思ったのですが、結局ダメでした。こんな感じで待てど暮らせどデータが入ってきません。

12dockernothing.png

topの画面でもこんな感じで表示されます。

13topareat.png

にもかかわらずagent2のstatusはenabledになっています。zabbix初心者なのでこんな事象ははじめてで皆目見当がつかないです。

14confighosts.png

ここからいくらか粘ったのですがどこから情報をあさればいいのかもわからず結局終了しました。つらい。

おわりに

本当はできましたって記事にしたかったのですが結局うまくいきませんでした。申し訳ありません。

冒頭でも書きましたが一応Zabbix5.0を一から環境セットアップしてagent2そのものを動かすことはできたのでそこまでやりたい人はこの記事でお試しください。

この手順から何かすればうまくdocker監視できるかもしれないので、もしできた人がいたらコメントください。

私のこの徒労がほかの人のためになることを願ってます......

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでMySQLを簡易的に立ててデータインポート

環境

  • Windows 10
  • MySQL Workbench 8.0
  • Docker Toolbox
    • MySQL8.0.16

工程

  • docker に MySQL を入れる
  • MySQL Workbench で接続できるようにする
  • SQLをインポートする

Docker machine の起動

Docker Quickstart Terminal のアプリケーションを起動する

MySQL のイメージを取得

$ docker pull mysql:8.0.16

MySQLコンテナを作成して起動

# docker ps -a で全てのコンテナ一覧
# docker ps で起動しているコンテナ一覧
# docker stop [CONTAINER ID] でコンテナ停止
# docker rm [CONTAINER ID] でコンテナ削除

$ docker run -it --name temp-mysql -e BIND-ADDRESS=0.0.0.0 -e MYSQL_ROOT_PASSWORD=mysql -p 3306:3306 -d mysql:8.0.16

# 一度作成したら docker start temp-mysql で起動できる

MySQLコンテナに接続

$ docker exec -it temp-mysql bash

MySQLに接続

$ mysql -u root -p -h 127.0.0.1

※終わったら MySQL を exit、コンテナも exit

外部IPを確認

$ docker-machine ls
NAME                ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
default             *        virtualbox   Running   tcp://192.168.99.100:2376           v19.03.5
local-by-flywheel   -        virtualbox   Stopped                                       Unknown

MySQL Workbench から MySQL に接続

※MySQL Workbench は新しいバージョンをインストールしておくこと

接続情報

HOST ACCOUNT PASS PORT
192.168.99.100 root mysql 3306

SQLをインポートするデータベースを作成

CREATE DATABASE `{database_name}`;

SQLをインポート

  • MySQL Workbench を起動して MySQL に接続する
  • Navigator(左メニュー) のタブを Administration から Schemas に切り替える
  • 作成したデータベースを選択
  • 上メニューの ServerData Import を選択
    • Import from Self-Contained File のラジオボタンを選択
    • インポートしたいファイルを選択
    • Default Traget Schema でインポート先の Shema を選択
    • Start Import ボタンをクリック
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

actix-webのホットリロードされるDocker開発環境を作る

はじめに

本記事では表題の通り、actix-webの開発環境について検討します。
actix-webはRust製のweb frameworkで、LL製の有名フレームワークより高速に動作することで知られています。
https://www.techempower.com/benchmarks/

一方で、修正を確認するためには都度コンパイルをする必要があり、RubyやPythonのような軽量言語に比べると開発効率が下がるのでは、という懸念があるかと思います。
(実際には型安全でコンパイラのチェックが優秀なRustは結果として開発効率が良い可能性もありますが。)

そこで、本記事ではLL製の有名フレームワークと同等の効率でRust製のAPI Serverを開発するための環境を構築する方法を検討します。

理想の開発環境

現代のWebアプリ開発はコンテナベースで行われることが多いかと思います。
そこで、本記事では

  • docker-compose upとか打ったら開発環境が動く
  • 開発用と本番用のDockerfileが共通になっており、本番用のイメージが小さい
  • 修正がすぐに確認できる

のような条件を満たす開発環境を目指します。

想定する構成

本記事では、Vue.jsで作られたSPAのフロントエンドからactix-webで作られたAPI Serverへリクエストする構成を考えます。
本記事の主な対象ではないのですが、DBにはPostgreSQLを想定しています。

  • フロントエンド:Vue.js
  • バックエンド:actix-web
  • DB:PostgreSQL

image.png

本題

本記事ではRustに関係する部分のみを紹介します。
上記構成を作る全体像が必要であれば、成果物のリポジトリをご参照ください。

Dockerfile

Dockerfileの作成にはDockerfileを改善するためのBest Practice 2019年版を非常に参考にしています。

まず、マルチステージビルド機能を使用して開発用のステージとビルド用、本番用のステージを分けています。本番用には開発用に比べて軽量なベースイメージを使用しており、例えばFargateなどにデプロイする際にかかる時間を減らしています。

なお、今回は本番用のベースイメージにrust:1.43.1-slim-stretchを使用していますが、開発環境は不要なのでもっと小さなイメージを使用することが可能です。
ベースイメージについては以下のリンクが参考になります。
参考:RustのLinux muslターゲット (その2:極小Dockerイメージを実現)

Dockerfile
# 開発環境
FROM rust:1.43.1 as develop-stage
WORKDIR /app
RUN cargo install cargo-watch
COPY . .

# ビルド環境
FROM develop-stage as build-stage
RUN cargo build --release

# 本番環境
FROM rust:1.43.1-slim-stretch
COPY --from=build-stage /app/target/release/api .
EXPOSE 8088
CMD ["/usr/local/bin/api"]

ちなみに、このDockerfileと同じパスに

.dockerignore
target

というファイルを置いています。
これは本番用のイメージをビルドする際に、COPY . .で時間をかけないようにする意図があります。

docker-compose.yml

以下のyamlはRust部分のみを抽出したdocker-compose.ymlです。
buildには先ほどのDockerfileを指定し、targetには開発用のステージを指定します。

また、中間ファイルのディレクトリをキャッシュすることでdocker-compose upするたびに不要なビルドが実行されることを回避しています。

commandにはcargo watch -x runを指定しており、これによってホットリローディングを実現しています。
なお、actix-webのドキュメントにはAuto-Reloading Development Serverという項目が存在するのですが、ここで紹介されているsystemfdsystemdを必要としており、docker上で動かすのが難しいと思い断念しています。

cargo watchでは修正するたびにプロセスを再実行するのだと思いますが、ステートレスなAPI Serverを開発する分には影響が少ないと判断しています。

docker-compose.yml
version: '3.7'

services:
  backend:
    build:
      context: ./backend
      target: 'develop-stage'
    ports:
      - "8088:8088"
    depends_on:
      - db
    volumes:
      - ./backend:/app
      - cargo-cache:/usr/local/cargo/registry
      - target-cache:/app/target
    command: /bin/sh -c "cargo watch -x run"

volumes:
  cargo-cache:
  target-cache:

成果物

上記構成で作られた開発環境のサンプルは以下の通りです。
本記事では紹介しなかったフロントエンドのDockerfileなども含まれます。

https://gitlab.com/yamamoto_kazumasa/docker-actix-web

まとめ

API Serverの開発には十分なactix-webの開発環境を構築することが出来ました。
修正するたびにコンパイルする状況に比べれば良い環境ではないかと思います。

今回は開発時の構成にのみ着目しましたが、実際の開発ではユニットテストの実行やCI/CDの考慮が必要だと思います。
その場合には、本記事で紹介したDockerfileに適切にステージを追加することで必要なイメージを効率よく作ることができると思います。

将来的には開発の全体をカバーしたサンプルプロジェクトを作れたらと考えています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-machineコマンドをインストール

ちょいメモです:wink:

悩み

PhpStormを使っていてdocker-machineコマンドへのパスを設定しようと思ったら、
肝心の docker-machine コマンドがどこにも無い!

原因

最近のバージョンである Docker Desktop インストーラには付属しなくなったそうです。

解決方法

docker-machineコマンドを単体インストールすればよいそうです。
https://github.com/docker/machine/releases

Windowsの場合

一発インストールコマンドが掲載されていますが、環境によって動かない場合もあるので、手動インストールする方法をば。

exeをダウンロードする。

https://github.com/docker/machine/releases/download/v0.16.2/docker-machine-Windows-x86_64.exe
(URL中の /v0.16.2/ は執筆当時のものです。適宜読み替えてください。)

exeをDocker Desktop の bin フォルダにコピーする。

デフォルトだと↓かと思います。

C:\Program Files\Docker\Docker\resources\bin

違ったら探してね。

exeの名前を docker-machine.exe に変える。

以上!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows10 に PHP7+Laravel環境をDockerで構築

概要

掲題のとおり「Windows10 に PHP7+Laravel環境をDockerで構築」を、
↓こちらの神記事を参考に、初心者向けに解説していきます。

最強のLaravel開発環境をDockerを使って構築する【新編集版】

「どうしてそういう設定を施すのか?」といったことは元記事が丁寧に説明して下さっているので、ここではとにかくローカルPC内にLaravelサーバを立ち上げるまでの手続きを駆け足でご紹介すると共に、Windows10でやると行きづまる所を補足しています。

私が試した環境

  • Windows10 Pro 64bit版
  • Docker Desktop for Windows - 2.3.0.2 (今回の手順中でインストールします)
  • Git for Windows - 2.25.0.windows.1 (今回の手順中でインストールします)
  • Make for Windows - 3.81 (今回の手順中でインストールします)

下準備編

いくつかのソフトウェアが必要なので、ダウンロード&インストールしましょう。

Docker, docker-compose(Docker Desktop for Windows)

↓こちらをご参考に。

Windows 10 に Docker Desktop for Windows をインストールする

※ ↑の記事内に記載していますが、Windows10のエディションやパソコンのCPUによる条件がありますので、ご注意下さい。

Git, Git-Bash(Git for Windows)

↓こちらからダウンロードしてインストールしましょう。
https://gitforwindows.org/

Git for Windows を入れれば Git-Bash も一緒に入ります。

make for Windows

元記事ではmakeコマンドを使用されているので必要です。
(複雑なコマンドをショートカットのようにまとめるのに使用されています。)

↓こちらからダウンロードしてインストールしましょう。
http://gnuwin32.sourceforge.net/packages/make.htm

makeコマンドがあるフォルダを環境変数PATHに追加しておく。

どこからでもmakeコマンドを打てるようにしておきましょう。
手順は割愛しますが、代わりに↓こちらの記事を紹介させていただきます。

Windows で環境変数 PATH をいじる方法のまとめ

makeコマンドがあるフォルダの場所は、デフォルトのインストール設定では↓です。

C:\Program Files (x86)\GnuWin32\bin

make for Windows をインストールするときにインストール先フォルダをカスタマイズした場合は、↑を参考に探してみてください。

一部コマンドをwinpty経由で実行するよう設定する。

Gitインストール時、オプションUse MinTTYをONにした場合、この設定作業が必要です。

  1. Windowsにログオンしているユーザーのホームフォルダ(例: C:\Users\山田太郎)の中に.bashrcという名前のテキストファイルを作ります。(すでに存在する場合は不要です。)
  2. .bashrcの中身に↓を追記して保存します。
alias docker='winpty docker'
alias docker-compose='winpty docker-compose'
alias make='winpty make'

Git-Bashに慣れている方は、vimコマンドなどで作業してももちろんOKです!

※ winptyは、Windowsへのコマンド指示をLinuxっぽいインターフェースでできるようにするラッパー的なソフトです。
ここでは、dockerdocker-composemakeコマンドだけ、自動的にwinpty経由で実行するよう設定をしています。
これをしないと、dockerコンテナの中に入って作業する系のコマンドが↓のようなエラーで失敗します。

$ docker-compose exec app composer create-project --prefer-dist laravel/laravel .
the input device is not a TTY.  If you are using mintty, try prefixing the command with 'winpty'

またmakeの中で実行されるコマンドもwinpty経由で実行させるにはmake自体をwinpty経由で実行しなければダメなので、3行目でそのように設定しています。

構築編

こちらは駆け足でザッと参ります。
一通り完了したら、ぜひ元記事も読んでみてください。

Git-Bashを起動する。

image.png

適当なフォルダを作って移動する。

ソースコードをダウンロードして色々やるので、万が一汚れても大丈夫なフォルダを作ってそこで作業しましょう。
ここでは例として、Cドライブ直下にtempというフォルダを作ってそこに移動してます。

cd /c
mkdir temp
cd temp

元記事で公開されているソースコード一式をダウンロードする。

githubに公開されているので、gitコマンドを使ってダウンロードします。

git clone git://github.com/ucan-lab/docker-laravel.git

githubアカウントを持ってなくてもダウンロードできるはず…
もし途中でkeyがうんたらと英語で質問が表示されたら、yesと入力してリターンキー押しておけばOK!

makeコマンド向け設定ファイルが存在するフォルダに移動する。

cd docker-laravel/infrastructure

LaravelサンプルWEBサイトを新規作成するコマンドを実行する。

make create-project

数分かかるかも。
ずらーっと流れるログが↓のようになって止まったらOK。

・
・
・
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
32 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

XXXXXX@XXXXX MINGW64 /c/temp/docker-laravel/infrastructure
$

LaravelサンプルWEBサイトにアクセスしてみる。

↓をWEBブラウザで開いてみてください。
http://127.0.0.1

↓こういうWEBページが表示されたら、成功!
image.png

これはLaravelフレームワークを使って作られているサンプルWEBページで、それを動かすためのWEBサーバが自分のPC内で起動している証です。

お疲れ様でした。

:thumbsup::thumbsup::thumbsup:

この記事では端折った部分もあるので、ぜひ元記事も一読してみてください。

最強のLaravel開発環境をDockerを使って構築する【新編集版】

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker ComposeでWordpressを起動してみた

docker-compose.ymlを作るだけだった。。。
ファイルリファレンス

docker-compose.yml
version: '3.8'

services:
  db:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    ports:
      - "8000:80"
    volumes:
      - ./html:/var/www/html
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
volumes:
  db_data: {}

docker-composeコマンド

docker-compose.ymlを作成したディレクトリで実行する。
コマンドリファレンス

作成と起動

コンテナの作成と起動。

$ docker-compose up -d

ブラウザでhttp://localhost:8000を開く

停止

コンテナの停止。(削除しない)

$ docker-compose stop

起動

停止しているdbとwordpressコンテナを起動。

$ docker-compose start db wordpress

ブラウザでhttp://localhost:8000/wp-adminを開く

停止と削除

コンテナの停止と削除。

$ docker-compose down --volumes

コンテナ一覧

コンテナ一覧を表示したいときに。

$ docker-compose ps

イメージ一覧

イメージ一覧を表示したいときに。

$ docker-compose images

コマンド実行

wordpressコンテナでls -lコマンドを実行。

$ docker-compose exec wordpress ls -l

Github

h23k/sandbox-wordpress

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker × swagger × Basic認証 をherokuにデプロイする

はじめに

関わっているスタートアップではAPIの調整をswaggerを用いて行なっているのですが、swagger uiに毎回ログインするのはめんどくさすぎるので、herokuにデプロイしてそちらを使うことにしました。
その際に認証なしはまずいのでbasic認証をかけています

ソースコード

まずは構成から、、、
とは言っても大したことはありません

.
├── Dockerfile
├── heroku.yml
└── swagger.yml
Dockerfile
FROM swaggerapi/swagger-ui

# ローカルで作成したswagger.ymlをdocker内のsample.yamlにコピー
COPY ./swagger.yml /usr/share/nginx/html/sample.yaml

# デフォルトで表示する内容をコピーしたものにするように環境変数を修正
ENV API_URL=sample.yaml

# Basic認証

# Basic認証を使うためのパッケージをインストール
RUN apk add apache2-utils

ARG USERNAME=username
ARG PASSWORD=password

# 暗号化して`/etc/nginx/.htpasswd`に書き出している
RUN htpasswd -c -b /etc/nginx/.htpasswd ${USERNAME} ${PASSWORD} && cat /etc/nginx/.htpasswd

# `/etc/nginx/nginx.conf` に

# auth_basic "Restricted Content";
# auth_basic_user_file /etc/nginx/.htpasswd;

# の2行を加筆する
RUN sed -i "s|location / {|location / {\n\tauth_basic \"Restricted Content\";\n\tauth_basic_user_file /etc/nginx/.htpasswd;\n|g" /etc/nginx/nginx.conf
heroku.yml
build:
  docker:
    web: Dockerfile

herokuでdockerを動かすためのファイル
herokuのためのdocker-composeみたいなイメージです
※参考: https://devcenter.heroku.com/articles/build-docker-images-heroku-yml

herokuの設定

ターミナル
heroku login

heroku container:login

heroku create 任意のアプリケーション名

heroku container:push web

heroku container:release web

heroku open

適切にデプロイされていることを確認
※参考:https://devcenter.heroku.com/articles/container-registry-and-runtime

CI/CD

gitにpushされた時に自動でデプロイさせる
1, herokuダッシュボードにいき該当のアプリケーションの詳細ページへ
2, 「deploy」を選択
スクリーンショット 2020-05-20 10.05.25.png

3, gitリポジトリを検索し「connect」を選択
スクリーンショット 2020-05-20 10.09.42.png

4, 自動デプロイするブランチを選択し「Enable Automatic Deploys」を選択
スクリーンショット 2020-05-20 10.12.51.png

5, masterにpush後herokuの「Activity」内で Build succeeded とかって文字があれば設定は完了です。

まとめ

個人的に一番辛かったのがどうやってswaggerにbasic認証をかけるか?
ってところです笑
まあ、蓋を開けてみれば別に大したことしなくても動いてくれたのでとりあえずいいかな〜と、、、
なんか本当はもうちょっといいやり方ありそうな気がするので他にやり方知っている方いれば教えてください汗

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】Firebaseでチャットアプリを開発(codelabs)

今更感もありますが、、サーバレスアプリ開発の勉強の為にFirebaseを用いて簡単なチャットアプリを作成しました。

Firebaseについて

Firebaseは、高品質のアプリを迅速に開発できるGoogle のmBaaSです。
今回は主要な機能である下記を使用します。

  • Firebase Authentication : アプリへのログイン機能を提供
  • Cloud Firestore : 構造化データを保存し、変更時に即座に通知を受け取ることが出来る
  • Cloud Storage : クラウドにファイルを保存する
  • Firebase Hosting : ホスティングする
  • Firebase Cloud Messaging :プッシュ通知を送信する
  • Firebase Performance Monitoring : アプリのパフォーマンスデータを収集する

開発環境

  • Windows10
  • Docker (DockerDesktop for windows(2.2.0.5))

完成イメージ

ソースコード(github)
完成イメージ

チャットアプリ開発

1. 環境構築

  1. codelabsのリポジトリから、ローカルへソースをコピーしてくる
  2. Firebaseのconsoleよりwebアプリを作成
    1. [プロジェクトの作成]を行う(プロジェクト名とGoogleアナリティクス有効は任意)
    2. </>(webアプリにFirebaseを追加)を行う(アプリ名は任意、FirebaseHostingの設定はONにする)
  3. 下記Dockerfileとdocker-compose.ymlをローカルソースのRootへ配置し、docker-compose up -d --buildコマンドで開発環境コンテナを作成する
Dockerfile
Dockerfile
FROM node:10.19-alpine

WORKDIR /app

RUN npm install -g firebase-tools
RUN apk add curl

ENV HOST 0.0.0.0
EXPOSE 5000
EXPOSE 9005

docker-compose.yml
version: '3'

services:
  node:
    container_name: node
    build: ./
    tty: true
    volumes:
      - ./:/app
    ports: 
      - 5000:5000
      - 9005:9005

2. FirebaseCLI設定

  1. 作成したコンテナ内にFirebaseCLIをインストールする
    1. docker exec -it node ashでコンテナ内へ
    2. npm -g install firebase-toolsよりインストール実施
    3. firebase --versionが返ってくればインストール成功
  2. FirebaseCLIへユーザー認証する
    1. firebase loginと打つとURL(ttps://accounts.google.com/o/oauth2/auth...)が表示されるので、ブラウザで表示されたURLへ移動
    2. Googleの認証画面が表示されるので、画面に従い認証を通す
    3. cd /app/web-start/でweb-startディレクトリへ移動
    4. firebase use --addにてアプリとFirebaseを関連付けする(Project IDは前手順のものを選択、aliasは無くてOK)
    5. firebase serve -o "0.0.0.0" --only hostingにてローカル上でのアプリ起動
    6. hosting: Local server: http://localhost:5000と表示されれば起動
    7. ブラウザより http://localhost:5000 を起動し、下記画面が表示されるか確認 チャットアプリ

3. 機能実装

  1. Firebaseのconsoleより、下記設定を行う
    1. 「開発>Authentication」、「sign-in method」からGoogleログインを有効にする
    2. 「開発>Database」、CloudFirestoreのCreate Databaseを行う([Start in test mode]を選択し、locationは任意選択)
    3. 「開発>Storage」、「始める」からCloud Storageを作成する(locationは任意選択)
  2. web-start/public/index.html内にFirebaseSDKとSDK構成用のscriptの記載があるか確認
  3. web-start/public/scripts/main.js内のfunctionを下記記載の通りに書き換える

参考)
https://firebase.google.com/docs/reference/js/firebase.auth.Auth
https://firebase.google.com/docs/reference/js/firebase.firestore
https://firebase.google.com/docs/reference/js/firebase.storage
#messagesのデータモデル図
データモデル

index.html
<script src="/__/firebase/X.X.X/firebase-app.js"></script>
<script src="/__/firebase/X.X.X/firebase-auth.js"></script>
<script src="/__/firebase/X.X.X/firebase-storage.js"></script>
<script src="/__/firebase/X.X.X/firebase-messaging.js"></script>
<script src="/__/firebase/X.X.X/firebase-firestore.js"></script>
<script src="/__/firebase/X.X.X/firebase-performance.js"></script>
<script src="/__/firebase/init.js"></script>

<script src="scripts/main.js"></script>
main.js
// Signs-in Friendly Chat.
function signIn() {
  var provider = new firebase.auth.GoogleAuthProvider();
  firebase.auth().signInWithPopup(provider);
}

// Signs-out of Friendly Chat.
function signOut() {
  firebase.auth().signOut();
}

// Initiate firebase auth.
function initFirebaseAuth() {
  firebase.auth().onAuthStateChanged(authStateObserver);
}

// Returns the signed-in user's profile Pic URL.
function getProfilePicUrl() {
  return firebase.auth().currentUser.photoURL || '/image/profile_placeholder.png';
}

// Returns the signed-in user's display name.
function getUserName() {
  return firebase.auth().currentUser.displayName;
}

// Returns true if a user is signed-in.
function isUserSignedIn() {
  return !!firebase.auth().currentUser;
}

// Saves a new message on the Firebase DB.
function saveMessage(messageText) {
  return firebase.firestore().collection('messages').add({
    name: getUserName(), 
    text: messageText, 
    profilePicUrl: getProfilePicUrl(), 
    timestamp: firebase.firestore.FieldValue.serverTimestamp()
  }).catch(function(error) {
    console.error('Error writing new message to database', error);
  });
}

// Loads chat messages history and listens for upcoming ones.
function loadMessages() {
  // create the query to load the last 12 messages and listen for new ones.
  var query = firebase.firestore()
                    .collection('messages')
                    .orderBy('timestamp', 'desc')
                    .limit(12);

  // start listening to the query.
  query.onSnapshot(function(snapshot) {
    snapshot.docChanges().forEach(function(change) {
      if (change.type === 'removed') {
        deleteMessage(change.doc.id);
      } else {
        var message = change.doc.data();
        displayMessage(change.doc.id, message.timestamp, message.name, message.text, message.profilePicUrl, message.imageUrl);
      }
    })
  })
}

// Saves a new message containing an image in Firebase.
// This first saves the image in Firebase storage.
function saveImageMessage(file) {
  // 1 - We add a message with a loading icon that will get updated with the shared image.
  firebase.firestore().collection('messages').add({
    name: getUserName(), 
    imageUrl: LOADING_IMAGE_URL, 
    profilePicUrl: getProfilePicUrl(), 
    timestamp: firebase.firestore.FieldValue.serverTimestamp()
  }).then(function(messageRef) {
    // 2 - Upload the image to Cloud Storage.
    var filePath = firebase.auth().currentUser.uid + '/' + messageRef.id + '/' + file.name;
    return firebase.storage().ref(filePath).put(file).then(function(fileSnapshot) {
      // 3 - Generate a public URL for the file.
      return fileSnapshot.ref.getDownloadURL().then((url) => {
        // 4 - Update the chat message placeholder with the image's URL.
        return messageRef.update({
          imageUrl: url,
          storageUri: fileSnapshot.metadata.fullPath
        });
      });
    });
  }).catch(function(error) {
    console.error('There was as error uploading a file to Cloud Storage:', error);
  });
}

4. 通知の表示

  1. web-start/public/manifest.jsonを下記記載の通りに書き換える
  2. web-start/public/firebase-messaging-sw.jsを作成し、下記内容を記載する
  3. web-start/public/scripts/main.jsのfunctionsaveMessagingDeviceTokenを下記内容で実装する
  4. ここまで出来たら、下記の手順にて通知が表示されることが確認できる
    1. コンテナ内でfirebase serve -o "0.0.0.0" --only hostingよりアプリ起動
    2. http://localhost:5000 へアクセスし、通知の許可を求められるので「許可」にする
    3. ブラウザからjavascriptのconsoleを開き、Got FCM device tokenの値を控える
    4. FirebaseのconsoleよりCloud Messagingを開きサーバーキーを控える
    5. 下記記載のcurlコマンドのYOUR_DEVICE_TOKENを上記3、YOUR_SERVER_KEYを上記4で控えた内容に置換る
    6. 作成したcurlコンテナ内で実行することでブラウザに表示中のチャットアプリから通知が表示される
manifest.json
{
  "name": "Friendly Chat",
  "short_name": "Friendly Chat",
  "start_url": "/index.html",
  "display": "standalone",
  "orientation": "portrait",
  "gcm_sender_id": "103953800507"
}
firebase-messaging-sw.js
importScripts('/__/firebase/6.0.4/firebase-app.js');
importScripts('/__/firebase/6.0.4/firebase-messaging.js');
importScripts('/__/firebase/init.js');

firebase.messaging();
main.js
// Saves the messaging device token to the datastore.
function saveMessagingDeviceToken() {
  firebase.messaging().getToken().then(function(currentToken) {
    if (currentToken) {
      console.log('Got FCM device token:', currentToken);
      // Saving the Device Token to the datastore.
      firebase.firestore().collection('fcmTokens').doc(currentToken)
          .set({uid: firebase.auth().currentUser.uid});
    } else {
      // Need to request permissions to show notifications.
      requestNotificationsPermissions();
    }
  }).catch(function(error){
    console.error('Unable to get messaging token.', error);
  });
}

// Requests permissions to show notifications.
function requestNotificationsPermissions() {
  console.log('Requesting notifications permission...');
  firebase.messaging().requestPermission().then(function() {
    // Notification permission granted.
    saveMessagingDeviceToken();
  }).catch(function(error) {
    console.error('Unable to get permission to notify.', error);
  });
}

参考)https://firebase.google.com/docs/reference/js/firebase.messaging

curl
curl -H "Content-Type: application/json" \
     -H "Authorization: key=YOUR_SERVER_KEY" \
     -d '{
           "notification": {
             "title": "New chat message!",
             "body": "There is a new message in FriendlyChat",
             "icon": "/images/profile_placeholder.png",
             "click_action": "http://localhost:5000"
           },
           "to": "YOUR_DEVICE_TOKEN"
         }' \
     https://fcm.googleapis.com/fcm/send

5. セキュリティルール

  1. web-start/firestore.rulesを作成し、下記内容を記載する
  2. web-start/storage.rulesを作成し、下記内容を記載する
  3. web-start/firebase.jsonへ、下記追記する
  4. コンテナ内でfirebase deploy --only storage,firestoreコマンドにより作成したセキュリティルールをデプロイする
firestore.rules
service cloud.firestore {
  match /databases/{database}/documents {
    // Messages:
    //   - Anyone can read.
    //   - Authenticated users can add and edit messages.
    //   - Validation: Check name is same as auth token and text length below 300 char or that imageUrl is a URL.
    //   - Deletes are not allowed.
    match /messages/{messageId} {
      allow read;
      allow create, update: if request.auth != null
                    && request.resource.data.name == request.auth.token.name
                    && (request.resource.data.text is string
                      && request.resource.data.text.size() <= 300
                      || request.resource.data.imageUrl is string
                      && request.resource.data.imageUrl.matches('https?://.*'));
      allow delete: if false;
    }
    // FCM Tokens:
    //   - Anyone can write their token.
    //   - Reading list of tokens is not allowed.
    match /fcmTokens/{token} {
      allow read: if false;
      allow write;
    }
  }
}

参考) https://firebase.google.com/docs/firestore/security/overview

storage.rules
// Returns true if the uploaded file is an image and its size is below the given number of MB.
function isImageBelowMaxSize(maxSizeMB) {
  return request.resource.size < maxSizeMB * 1024 * 1024
      && request.resource.contentType.matches('image/.*');
}

service firebase.storage {
  match /b/{bucket}/o {
    match /{userId}/{messageId}/{fileName} {
      allow write: if request.auth != null && request.auth.uid == userId && isImageBelowMaxSize(5);
      allow read;
    }
  }
}

参考)https://firebase.google.com/docs/storage/security/start

firebase.json
{
  //Add this
  "firestore": {
    "rules": "firestore.rules"
  },
  //Add this
  "storage": {
    "rules": "storage.rules"
  },
  "hosting": {
    ...
  }
}

6. パフォーマンス測定

  1. web-start/public/index.htmlに下記内容があるか確認
  2. web-start/public/scripts/main.jsに下記を追記
  3. FID計測する場合は、web-start/public/index.htmlへ下記追記
  4. Firebaseのconsoleからパフォーマンスを確認することが可能となる
index.html
<script src="/__/firebase/X.X.X/firebase-performance.js"></script>
<script src="/__/firebase/init.js"></script>
main.js
firebase.performance();
index.html(EnableFID)
<script type="text/javascript">!function(n,e){var t,o,i,c=[],f={passive:!0,capture:!0},r=new Date,a="pointerup",u="pointercancel";function p(n,c){t||(t=c,o=n,i=new Date,w(e),s())}function s(){o>=0&&o<i-r&&(c.forEach(function(n){n(o,t)}),c=[])}function l(t){if(t.cancelable){var o=(t.timeStamp>1e12?new Date:performance.now())-t.timeStamp;"pointerdown"==t.type?function(t,o){function i(){p(t,o),r()}function c(){r()}function r(){e(a,i,f),e(u,c,f)}n(a,i,f),n(u,c,f)}(o,t):p(o,t)}}function w(n){["click","mousedown","keydown","touchstart","pointerdown"].forEach(function(e){n(e,l,f)})}w(n),self.perfMetrics=self.perfMetrics||{},self.perfMetrics.onFirstInputDelay=function(n){c.push(n),s()}}(addEventListener,removeEventListener);</script>

7. デプロイ

  1. /web-start/firebase.jsonへdepoyするローカルファイルを指定する
  2. firebase deploy --except functionsコマンドにてFirebaseHostingへdeployする
  3. Deploy complete!との表示されれば完了(URLはHosting URL: https://xxxx)
firebase.json
{
  "firestore": {
    "rules": "firestore.rules"
  },
  "storage": {
    "rules": "storage.rules"
  },
  "hosting": {
    "public": "./public"
  }
}

最後に

上記はGoogleの提供するcodelabを元に構築しました。
取り敢えずチャットアプリを作成する部分のみを抜粋して記載しております為、より深くFirebaseについて知りたい方は参考元 (Firebase web codelab) をご覧いただければと思います。

また、色々と独学で進めている部分が多い為、上記記事でおかしな点あればやさしくコメント頂けると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む