20190710のdockerに関する記事は16件です。

socat でコンテナ内のコマンドを TCP ソケット経由で実行する

標準入出力を利用するコマンドを外部からネットワーク経由で実行したいことありませんか?
コンテナを使っているとまれにありますよね。例えばデータサイエンティストが実装したシンプルな Python スクリプトとか。
socat を使って TCP ソケット経由で外部からコマンドを実行できるようにしてみました。

課題

例えば 1マイクロ秒の誤差もないすごい date コマンドを持っているコンテナがあるとします。このコンテナは docker run date:latest を実行すると日時を標準出力に表示します。
この date コマンドをホスト側から実行するのは容易ですが、別のコンテナから実行したくても直接コマンドを実行する方法が見つかりませんでした。

socat.png

これを実現するために ssh や Docker on Docker で date コンテナを実行する方法もありますが、呼出元のコンテナに Docker をインストールしたり docker.sock を共有したり事前準備がやや面倒です。

この記事では上記のような「別コンテナのコマンドを実行して標準出力を取得したい」ケースを socat を使って解決します。

具体的な例

前の記事でヘッドレス Chrome を使用して HTML を標準出力にダンプするコンテナを作成しました。このコンテナを実行すると対象のウェブサイトの HTML が出力されます。

$ docker pull grohiro/headless-chrome
$ docker run grohiro/headless-chrome http://www.google.com/
#=> (HTMLが出力される)

コンテナの内部では gooogle-chrome コマンドを実行して HTML をダンプしています。このコンテナを socat を使って TCP ポート 9000 経由で外部から実行できるようにします。

コンテナイメージの作成

まずは上記の Chrome コンテナに socat を追加したイメージを作成します。そしてヘッドレス Chrome を起動するラッパースクリプトも追加します。

Dockerfile

FROM grohiro/headless-chrome
USER root
RUN apt-get install -y -qq socat
COPY ./chrome.sh /
ENTRYPOINT ["socat", "tcp4-listen:9000,fork,reuseaddr", "system:/chrome.sh"]

chrome.sh

#!/bin/sh
read URL
google-chrome --headless --no-sandbox --dump-dom --start-maximized --disable-gpu "$URL"

ファイルを作成したら Docker イメージをビルドします。名称は headless-chrome-socat です。

$ docker build -t headless-chrome-socat .

作成したコンテナを実行すると TCP ポート 9000 が socat にバインドされて LISTEN 状態になります。このポートに送信したデータは chrome.sh の標準入力に渡されます。

chrome.sh は標準入力の文字列を Chrome の引数として実行します。Chrome は指定された URL を読み込み HTML を標準出力にダンプします。

socat はこの標準出力をリクエスト元のコンテナにレスポンスとして返します。

実行元のコンテナはソケットの入力を読み込むことで Chrome がダンプした HTML を取得することができます。

socat2.png

動作確認

まずはホスト側から手動で URL を入力して HTML が出力されるか確認します。

以下のコマンドで作成したコンテナのポート 9000 を 127.0.0.1:9000 にマッピングして起動します。

$ docker run -p 9000:9000 headless-chrome-socat

ポート 9000 に接続して URL を送信してみましょう。

$ nc 127.0.0.1 9000
http://www.google.com
#=> (HTMLが出力される)

入力した http://www.google.comchrome.sh 経由でヘッドレス Chrome に渡されて HTML が出力されました。

docker-compose

ここまでできたら docker-compose.yml を定義します。

docker-compose.yml

services:
  crawler:
    # コマンド実行元のコンテナ
    image: php:7.5-slim
  chrome:
    # ヘッドレス Chrome コンテナ
    image: headless-chrome-socat

この docker-compose.ymlcrawler コンテナの中から tcp://chrome:9000 に URL を送信すると Chrome が実行されて HTML が取得できるようになりました。

例えば crawler コンテナ内の PHP からは fsockopen() を使ってデータを読み書きできます。

$response = "";
$fp = fsockopen('chrome', '9000');
if ($fp) {
    fwrite($fp, "https://www.google.com\n");
    while (!@feof($fp)) {
        $response .= fgets($fp, 4096);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Redmineのローカル環境をDockerで簡単に構築する

Docker for Macをインストール

Docker for Macのダウンロードページからdmgをダウンロードします。
※Docker IDを作る必要があります。
スクリーンショット 2019-07-10 22.29.56.png
ドラッグ&ドロップでmacにインストールします。
スクリーンショット 2019-07-10 22.35.34.png

アプリケーションをダブルクリックで起動して画像のようなウインドウが出ればOKです。
スクリーンショット 2019-07-10 22.38.13.png

メニューバーにくじらのアイコンが出て、画像のように緑色の丸と一緒に「Docker Desktop is Running」と出ていればDockerが起動しています。
スクリーンショット 2019-07-10 22.41.18.png

RedmineをDocker経由で持ってくる

Docker Hubのredmine公式イメージからコマンドをコピーし、CUIで実行
スクリーンショット 2019-07-10 22.45.12.png

コピーしたコマンドを実行します

$ docker pull redmine

下記のようになればOKです。

Using default tag: latest
latest: Pulling from library/redmine
fc7181108d40: Pull complete 
d5e459c0e2e8: Pull complete 
9d72610672ed: Pull complete 
8021817cc2b5: Pull complete 
11df209a1902: Pull complete 
22d9ea80dda2: Pull complete 
44657032d49c: Pull complete 
52667b1ef3e5: Pull complete 
ecbc5377aa1e: Pull complete 
91c3a7d560ed: Pull complete 
7023fe92c4a0: Pull complete 
4b79f7046904: Pull complete 
443f0769b46f: Pull complete 
Digest: sha256:bbf8fa56934342c33e415ead52a75ca9e052818bd1bead59b0395c67e1fed626
Status: Downloaded newer image for redmine:latest

Redmineのローカル環境構築

redmineのDockerイメージがあるか、「docker images」で確認します。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
redmine             latest              3f1c9319f54c        6 days ago          579MB

Dockerイメージを確認できたら、「docker run」でコンテナを立ち上げます。

$ docker run -d --name redmine -p 8080:3000 redmine 
  • -d(--detach):バックグラウンドで起動する
  • -p:ポートフォワード ローカルのポート : Dockerコンテナのポートで指定する

http://localhost:8080/にアクセスするとredmineのローカル環境が構築されています。
スクリーンショット 2019-07-10 23.01.39.png

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

RailsチュートリアルのためにDockerコンテナの作成 for Windows

動機

これからRailsチュートリアルを始めることにしました。しかしながら、自宅デスクトップPCでもモバイルノートPCでも進めたいと思うのです。自宅メインPCは環境構築が面倒なことで有名なWindows。一方でモバイルノートPCのMacOSの環境も汚したくない。環境依存の問題が出るのは困る。というわけで、開発環境は仮想化します。

Docker for Windowsのインストール

私の場合は、Chocolatey経由でDocker for Windowsをインストールしました。Chocolateyを使ったアプリケーションのインストールを行う場合、Powershellに管理者権限が必要となります。

powershell
$ choco install docker-desktop

Docker for Windowsがインストールされているか確認

Windows Powershellで以下のコマンドを実行します。管理者権限は必要ありません。

powershell
$ docker -v

Docker for Windowsが正しくインストールされていれば、実行結果は以下のようになります。

powershell
Docker version 18.09.2, build 6247962

バージョンが表示されていますね。

Railsチュートリアル用のコンテナ作成

Dockerイメージの入手 - Ruby 2.5.1

Dockerコンテナを作成するために、Dockerイメージを入手しなければなりません。

Dockerイメージという言葉そのものがわからない初心者なので、Google検索をかけてみます。すると、Dockerイメージとは以下のようなものであると言及されていました。

Dockerコンテナを作成する以前の段階で必要になる、ということですね。

Windowsにおけるコンテナイメージの入手は、Windows Powershellでdocker pullコマンドを実行することにより行います。管理者権限は必要ありません。

ネットワークエラーの発生と解決

powershell
$ docker pull ruby:2.5.1
Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

Docker DesktopのSettingsウィンドウを開き、Network - DNS Serverの設定値をAutomaticからFixed:8.8.8.8に変更することにより解決しました。

なお、DNSサーバーの設定を変更すると、その時点でDocker Desktopの再起動がかかります。再起動の完了を示すバルーンが表示されるまできちんと待ちましょう。

powershell
$ docker pull ruby:2.5.1
error during connect: Post http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.39/images/create?fromImage=ruby&tag=2.5.1: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.

上記のエラーは、Docker Desktopの再起動が終わっていないのにdocker pullしようとした場合に発生するエラーです。

今度こそdocker pull

Dockerによる仮想環境の構築といっても、まず仮想環境のイメージを入手しないと話になりません。docker pullは、Docker Hubで用意されているイメージを入手するためのコマンドです。

powershell
$ docker pull ruby:2.5.1

Rubyのバージョンは、2.5.1決め打ちとします。

powershell
$ docker pull ruby:2.5.1
2.5.1: Pulling from library/ruby
...省略
Status: Downloaded newer image for ruby:2.5.1

今度こそイメージを入手することができました。

本当にDockerイメージが入手できているか確認

powershell
$ docker image ls
powershell
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ruby                2.5.1              ...略

以上のような動作結果になりました。Ruby 2.5.1のDockerイメージを正しく入手できたようです。

Dockerコンテナの作成

powershell
$ docker container run -it --name RailsTutorialTest -p 8080:3000 -v C:\mountpoint\rails_tutorial_test:/var/www ruby:2.5.1 /bin/bash

Windowsのパスワードの入力を要求されるので入力します。WindowsへのログオンにMicrosoftアカウントを使っている場合、Microsoftアカウントのパスワードを打ち込みます。

docker container runのオプション

  • -it…ホスト側の標準入出力とDockerコンテナの標準入出力を接続する
    • -i…ホスト側の標準入力とDockerコンテナの標準入力を接続する
    • -t…Dockerコンテナの標準出力とホスト側の標準出力を接続する
    • ホスト側からDockerコンテナ上のシェルを操作するために必要な設定
  • -p {ホスト側TCPポート}:{コンテナ側TCPポート}
    • ホスト側の指定TCPポートとコンテナ側の指定TCPポートの間で転送を行わせる
    • -p 8080:3000…ホスト側のTCP8080番ポートとコンテナ側のTCP3000番ポートの間で転送を行わせる
      • Ruby on Railsのdevelopmentは、初期設定でTCP3000番ポートを使用する
  • --name {コンテナの名前}
    • Dockerコンテナの識別に使う名前をつける
    • このオプションがない場合、ランダムな文字列から名前が生成される
    • コンテナを識別する方法としては、名前以外に以下の方法がある
      • 長いUUID…f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778
      • 短いUUID…f78375b1c487
  • -v {ホスト側ディレクトリ}:{コンテナ側ディレクトリ}
    • コンテナが実行されているホスト側ディレクトリを、コンテナ側ディレクトリとしてマウントする
    • ホスト側がWindows系のファイルシステムの場合、パスのディレクトリ区切り文字は\である
  • 使用するイメージ名
    • オプションの後に入力する
    • 今回はruby:2.5.1である
  • コンテナで最初に実行するコマンド
    • 使用するイメージ名の後に入力する
    • 今回は/bin/bashである

DockerのShared Driveがノートンのファイアウォールで遮断されないようにする

最初の実行では、以下のエラーが発生し、Dockerコンテナを作成できませんでした。どうやら、システムにインストールされているファイアーウォール機能によってDockerのShared Driveが遮断されている、ということのようです。

powershell
...略...docker.exe: Error response from daemon: Drive sharing seems blocked by a firewall.

私のWindows PCには、Norton Internet Securityがインストールされています。ということで、DockerのShared Driveがノートンのファイアウォールに引っかからないようにする - gfonius.netによれば、Windows上のファイルやフォルダーをDockerコンテナにマウントするためには、以下の設定内容を変更する必要があるようです。

  • Windows 10の[ローカル グループ ポリシー]
  • Nortonファイアウォールの[デバイスの信頼]

gfonius.net記載の情報を参考に、以下の設定を変更します。

  • Windows 10の[ローカル グループ ポリシー]から、[コンピューターの構成 - Windowsの設定 - セキュリティの設定 - ネットワーク リスト マネージャー ポリシー - 識別されていないネットワーク]を選択→[場所の種類]を[プライベート]に変更
  • Norton Internet Securityから、[設定 - ファイアウォール - デバイスの信頼… 設定する]→ともに適当な名前で構わないので、以下2つのIPアドレスの[信頼レベル]に[完全な信頼]を設定
    • 10.0.75.0
    • 10.0.75.1

以上で、Dockerコンテナを作成できるようになるはずです。

結果

改めてDockerコンテナの作成コマンドを実行すると、今度こそDockerコンテナを作成することができました。powershellのウィンドウ上で、コンテナのbashが動いています。

bash
root@...略...:/# 

関連リンク

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

raspberrypi3にdockerをインストール

***追記
下記の手順でインストールはできたが、
実行時に下記のエラーが発生して、
実行できなかった。

docker: Error response from daemon: unable to find "net_prio" in controller set: unknown.

docker側の対応待ちのよう

https://github.com/docker/for-linux/issues/545


まずは、下記のサイト通り、
下記のコマンドを実行。

curl -sSL https://get.docker.com | sh

エラーメッセージが表示されて、
インストールできなかった。

・エラーメッセージ
E: Repository 'http://raspbian.raspberrypi.org/raspbian buster InRelease' changed its 'Suite' value from 'testing' to 'stable'

下記のコマンドを実行したらいいとあったので、
実行する。

sudo apt-get update --allow-releaseinfo-change

やっぱりエラーが出力される。

・エラーメッセージ
E: リポジトリ https://download.docker.com/linux/raspbian 10 Release には Release ファイルがありません。
https://www.nekochango.com/entry/linux/trouble/apt-get_bionic_release_nothing

とりあえず、
dockerのインストールコマンドを実行してみる。

sudo apt-get install -y docker.io

正常にインストールされた。

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

Ubuntu16の公式Dockerイメージには/etc/servicesや/etc/protocolsが入っていない

Ubuntu16の公式Dockerイメージには/etc/servicesや/etc/protocolsが入っていない

初期状態では一部のネットワーク系API(getprotobynameとかgetaddrinfoとか)が失敗する。

ネットワーク系のアプリやライブラリを使う際にエラーが起きると、原因究明に難渋する場合がある。
(こんなファイルがないとか、なかなか想像しないですよね?)

対策

問題のファイル群はnetbaseパッケージに含まれている模様。入れましょう。

sudo apt install netbase

問題の実例

例えば、こんな問題が起きた:

メッセージキューのライブラリであるQpid Protonを使用。
通信先のポート番号を明示的に指定した場合は問題なく動くが、明示指定しないと処理が永久に進まなくなるという不具合が発生。

調べたところ、以下の機序で無限ループしていた。

  1. Qpid ProtonがAMQPのデフォルトポート番号をgetaddrinfo()で解決しようとする
  2. /etc/servicesがないので、APIが失敗
  3. Qpid Protonが(なぜか)失敗時に無限にリトライする実装になっているため、最初に戻る

その他

  • Ubuntu16のdockerイメージの他、それをベースにしたdockerイメージにも入っていない場合があるので注意。
    Ubuntu16ベースのNVIDIA cudaイメージとか。
    Dockerfileを書くときはパッケージを--no-install-recommendsで入れることが多いので、依存関係による自動導入もされにくい。

  • Ubuntuをミニマム構成でインストールした場合にも入らないかも?(未確認)。

ぐち

これくらい最初から入れといてくれてもいいのに…

(障害票の閉じられ方を見るに、by designなのか?)

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

(Failed to) Show docker load progress bar over SSH

SSH上でもdocker loadのプログレスバーを表示したかった(失敗)

Introduction

I often load large (several gigabytes) docker images over SSH. Locally I can see progress bar saying "Loading layer". However the it is gone when docker is loaded over SSH.

Method and Result

Understanding internal state

Firstly I analyzed docker.sock packet. Since unix domain socket is used to communicate with docker backend, usual wireshark does not work well. So, after sudo mv /var/run/docker.sock /var/run/docker.sock.temp, I used two proxies to utilize wireshark tcp.port == 10000.

sudo socat UNIX-LISTEN:/var/run/docker.sock,mode=777,reuseaddr,fork TCP-CONNECT:localhost:10000
socat -v TCP-LISTEN:10000,fork UNIX-CONNECT:/var/run/docker.sock.temp

Then, while I ssh localhost docker load < img.tar, in wireshark I see POST /v1.30/images/load?quiet=1 HTTP/1.1 packet. This indicates that quiet mode was specified even though I'm not specifying -q.

Understanding the call stack

[backend]
components/engine/image/tarexport/load.go Load()
components/engine/daemon/image_exporter.go LoadImage()
components/engine/api/server/router/image/image_routes.go postImagesLoad()
[frontend]
components/cli/cli/command/image/load.go runLoad()

Now I understand that runLoad()'s dockerCli.Out().IsTerminal() controls the state.

How is the terminal checked?

In components/engine/pkg/term/term.go IsTerminal(), I see return tcget(fd, &termios) == 0. If this condition becomes true, the console output should be recognized as terminal.

Conclusion

I found that tcget is the key to get the progress bar back. But I cannot find how to achieve it yet. This is not a personal project, so (unlike cTouch's safariextz signing date hack) custom LD_PRELOAD cannot be used. I also tried unbuffer as described in https://unix.stackexchange.com/questions/249723/how-to-trick-a-command-into-thinking-its-output-is-going-to-a-terminal , but it does not work well either. Suggestions are welcomed...

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

Show docker load progress bar over SSH

SSH上でもdocker loadのプログレスバーを表示する

Introduction

I often load large (several gigabytes) docker images over SSH. Locally I can see progress bar saying "Loading layer". However the it is gone when docker is loaded over SSH.

Method and Result

Understanding internal state

Firstly I analyzed docker.sock packet. Since unix domain socket is used to communicate with docker backend, usual wireshark does not work well. So, after sudo mv /var/run/docker.sock /var/run/docker.sock.temp, I used two proxies to utilize wireshark tcp.port == 10000.

sudo socat UNIX-LISTEN:/var/run/docker.sock,mode=777,reuseaddr,fork TCP-CONNECT:localhost:10000
socat -v TCP-LISTEN:10000,fork UNIX-CONNECT:/var/run/docker.sock.temp

Then, while I ssh localhost docker load < img.tar, in wireshark I see POST /v1.30/images/load?quiet=1 HTTP/1.1 packet. This indicates that quiet mode was specified even though I'm not specifying -q.

Understanding the call stack

[backend]
components/engine/image/tarexport/load.go Load()
components/engine/daemon/image_exporter.go LoadImage()
components/engine/api/server/router/image/image_routes.go postImagesLoad()
[frontend]
components/cli/cli/command/image/load.go runLoad()

Now I understand that runLoad()'s dockerCli.Out().IsTerminal() controls the state.

How is the terminal checked?

In components/engine/pkg/term/term.go IsTerminal(), I see return tcget(fd, &termios) == 0. If this condition becomes true, the console output should be recognized as terminal.

Conclusion

I found that tcget is the key to get the progress bar back. But I cannot find how to achieve it yet. This is not a personal project, so (unlike cTouch's safariextz signing date hack) custom LD_PRELOAD cannot be used. I also tried unbuffer as described in https://unix.stackexchange.com/questions/249723/how-to-trick-a-command-into-thinking-its-output-is-going-to-a-terminal , but it does not work well either. Suggestions are welcomed...

PS

angel_p_57's method worked like a charm!

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

kubernetesでpodのstatusがREADY 0/1になっててどうすればいいか分からなくて困った、というかログの確認方法が分からなかった

はじめに

kubernetes触り始めました!!
全然興味なかったんですけどやってみると楽しいですね。
結局どんなアプリケーションを動かすかが大事だと思ってたんでインフラ系は興味ナッシンだったのですが、環境つくるの楽しい。まだはじめの一歩どころか小指の爪切ったくらいのレベルなんですけども。

で、色々チュートリアルしたりしてたんですけど、PodがRunningにならないの。。

どうすればいいか分からなくて泣いた。

Errorログどこに出るの

私もね、一応SEなんでね、ログをみて原因を探ろうとしましたよ。
さすがにログも見ないで助けを求めちゃうようなゆとり社員じゃないですから。




どこでログみるの!!??




結局ログ見ないまま先輩に聞きました。

エラーログの出し方

ということで学んだ内容を共有します。
kubernetesでは、ログを出すとき以下2つのコマンドが使えます。

1個目: logs
コンテナが出力しているログを確認する方法。コンテナ内で起動してるアプリケーションに原因がある場合はこちら。

2個目: describe
podのイベントを出力する。メモリ足りない!とかマウント失敗!などのkubernetesの設定に問題がある場合はこちら。

流れとしては、問題のあるpodの名前を調べて、上記のコマンドを実行します。

まずは調べたいpodの名前を調べます。

$ kubectl get po 
NAME                        READY   STATUS    RESTARTS   AGE
guestbook-v1-544fbbc99b-d   0/1     Pending   0          23h

poはpodの短縮名称です。短縮名知ったら使いたくなる。
上記の例だと、guestbook-v1-544fbbc99b-d がpodの名前です。

podの名前が分かったので、describeないしlogsコマンドでログをGETできます。

$ kubectl describe pod guestbook-v1-544fbbc99b-d 
$ kubectl logs guestbook-v1-544fbbc99b-d47t5

describeの場合は、リソースタイプを指定する必要があるので、pod [pod名]と指定する必要があります。
pod以外でも、リソースタイプを指定すればログをとれます。

$ kubectl describe pv pv名
$ kubectl describe svc サービス名

などなど。

参考

エラーの原因がわかった後は、以下の記事を参考に解決すると良いかも。
Kubernetesのポッドが起動しない原因と対策

さいごに

kubernetes楽しい。名前変だし勝手に避けてたけど、だんだん名前もかっこよく思えてきた。
けどやっぱり、開発のが楽しいかな。。
どこで動かすかより、何を動かすかのが大事だよね。。
一生畑を耕して、野菜植えないみたいなことにはなりたくないので、私は耕し屋さんではなく、野菜を育てるところまで頑張って、立派な農家を目指したいと思います!!

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

PostgresのDBバックアップ・リストア簡易検証手順

docker-compose.ymlを使ってPostgresインスタンス起動。

  • mydb1: バックアップ対象のインスタンス
  • mydb2: リストア先のインスタンス
docker-compose.yml
version: '3.1'

services:
  mydb1:
    image: postgres:9.6
    restart: always
    environment:
      POSTGRES_DB: example1
      POSTGRES_USER: example
      POSTGRES_PASSWORD: example
    ports:
      - '15432:5432'
  mydb2:
    image: postgres:9.6
    environment:
      POSTGRES_DB: example2
      POSTGRES_USER: example
      POSTGRES_PASSWORD: example
    ports:
      - '25432:5432'

Postgresインスタンスの起動

docker-compose up

テスト用にデータを投入する。

psql -U example -h localhost -p 15432 -d example1

> create table users(id serial primary key, name varchar);
> insert into users (name) values ('hanako'), ('taro');

mydb1をバックアップする

pg_dump -U example -h localhost -p 15432 -d example1 > exmple1.dump

mydb2にリストアする

psql -U example -h localhost -p 25432 -d example2 -f exmple1.dump

リストア後の確認

psql -U example -h localhost -p 25432 -d example2

> select * from users;
|id|name  |
|--|------|
| 1|hanako|
| 2|taro  |
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS ECS向けCloudWatch Container Insightsのパブリックプレビューが開始したので、使用開始方法をまとめてみた

はじめに

こんにちわ。Wano株式会社エンジニアのnarikawaと申します。


ということで、以下の手順書を参考に試しに設定してみました。
Amazon ECS CloudWatch Container Insights - Amazon Elastic Container Service
ところどころわかりづらいところがあったり、日本語訳が提供されていないこともあったので、整理して発信しようと思い、この記事を書いています。
全てを訳せているわけではないので、詳細は大元の手順書を参考にしていただけると幸いです。

対象読者

  • CloudWatch Container Insightsの設定が、プレビュー時に使っていなくてよくわからない方
  • 手順書が英語しか対応していないため嫌厭してまだ試せていない方
  • CloudWatch Container Insightsでどんなことができるか知りたい方

導入手順

1.まずは設定いじる

GUI(console)で設定する

  • ecsのAccount Settingのタブに移動する   スクリーンショット 2019-07-10 11.06.32.png

※この際、ルートユーザーまたはコンテナーインスタンスのIAMロールを使用していることが前提

  • すると以下のような見慣れない設定チェックボックスがあるので、チェックして有効にする スクリーンショット 2019-07-10 11.06.17.png

※この際、実行しようとしているIAM usersとIAM role はecs:PutAccountSetting permissionがこのアクションに必要

CUIで設定する

  • 自分のアカウント全てのIAM usersとIAM roleのdefaultの設定をcontainer insightsをenabledにしたい

put-account-setting-default (AWS CLI)の場合

aws ecs put-account-setting-default --name containerInsights --value enabled --region us-east-1

Write-ECSAccountSettingDefault (AWS Tools for Windows PowerShell)の場合

Write-ECSAccountSettingDefault -Name containerInsights -Value enabled -Region us-east-1 -Force
  • 自分のアカウントのあるIAM usersとIAM roleのdefaultの設定をcontainer insightsをenabledにしたい(root userのみ)

put-account-setting (AWS CLI)の場合(特定のuser設定変更)

aws ecs put-account-setting --name containerInsights --value enabled --principal-arn arn:aws:iam::aws_account_id:user/userName --region us-east-1

Write-ECSAccountSetting (AWS Tools for Windows PowerShell)の場合(特定のuser設定変更)

Write-ECSAccountSetting -Name containerInsights -Value enabled -PrincipalArn arn:aws:iam::aws_account_id:user/userName -Region us-east-1 -Force

2.クラスターを作る

スクリーンショット 2019-07-10 11.05.33.png

  • その際、以下のような新しいチェックボックスがあるので、enableにチェックする スクリーンショット 2019-07-10 12.24.39.png

3.いつも通りサービスかタスクを立ち上げる

4.誘導に従って、cloudwatchのcontainer insightsを見る

  • クラスタのメトリクスのタブに移動すると、見慣れない青枠の誘導があるので、View Container Insightsのボタンをクリックする

スクリーンショット 2019-07-10 11.05.18.png

  • そうすると以下のような、docker stats相当の情報が観れる

スクリーンショット 2019-07-10 11.34.24.png

  • 以下のように時間でも絞れるし
    スクリーンショット 2019-07-10 11.34.41.png

  • 以下のように特定のクラスタ、サービス、タスクでもfilterをかけれるようになっている

スクリーンショット 2019-07-10 11.34.35.png

container insightsのいいところ

  • コンテナインスタンスにsshして、docker statsしなくてもコンテナの状態を監視できる

疑問点

  • まだterraformでは対応していない?
    • public previewだから多分対応していない
    • clusterのパラメタで早く切り替えれると嬉しい。。
  • 既存のクラスタをいじって設定を足せないのか
    • terraformで建ててから手動で切り替えようと思ったらできなかった(そもそもクラスターの設定修正画面がない)
    • CLIならいけるのか??
    • ecs — AWS CLI 1.16.195 Command Reference 見当たらない、、
    • わかる方ぜひ教えて欲しいです、、わりと切実
    • [追記 2019/7/10]以下の回答いただけました!、正式なリリースでの対応が待ち遠しい

終わりに

  • 週1投稿に関して、これは先週分ってことで許してください(遅れてすみません。。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Hello Docker するだけの Docker Image を作る

Docker 触ったことない人向け。
Dockerfile を書く心理的障壁を下げることを目的とする記事です。

docker コマンドがインストール済であることを前提としています。
インストールがまだの人はこちらをご参照の上、インストールお願いします。https://docs.docker.com/get-started/

Dockerfile

今回、実行環境は Ubuntu を使うことにします。
Version は xenial を使うことにします。

Ubuntu の Version およびサポート期間などは以下を参照。
https://www.ubuntulinux.jp/ubuntu

Dockerfile から参照する Ubuntu の Docker Image
https://hub.docker.com/_/ubuntu

Dockerfile
FROM ubuntu:xenial

RUN echo "Now building..."

CMD echo "Hello Docker"

Docker Image 作成

docker build <image name> <dir path> コマンドを実行。
<dir path> には Image に固める file が配置されているパスを指定します。

$ cd <Dockerfile が配置されているディレクトリ>

$ docker build -t helloworld .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM ubuntu:xenial
 ---> 7e87e2b3bf7a
Step 2/3 : RUN echo "Now building..."
 ---> Using cache
 ---> 5045cdd6bdcd
Step 3/3 : CMD echo "Hello Docker"
 ---> Using cache
 ---> 87c1c9ff264e
Successfully built 87c1c9ff264e
Successfully tagged helloworld:latest

Hello Docker

docker run <image name> コマンドを実行。

$ docker run helloworld
Hello Docker

以下のようにすると docker run 時に起動したコンテナの bash セッションを生成することができます。

$ docker run -it helloworld /bin/bash
root@b48d469b6a68:/# 

参考

https://qiita.com/nl0_blu/items/1de829288db2670276e8

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

DockerHubで公開されているコンテナが安全か確かめてみた結果【人気のコンテナ上位800個】

はじめに

Docker Hubに公開されているイメージはどの程度安全なのか、 Dockle starsTrivy stars を利用して検証しました。
検証結果は、 https://containers.goodwith.tech/ に公開しています。

screencast24.gif

結論

基本的にどのコンテナにも脆弱性はある!
人気が高いコンテナ/最近ビルドされているコンテナでも関係ない!

ただ、Docker公式が用意しているコンテナは今のところ大丈夫。
より詳しいことを知りたい人は 操作方法 を見て、 https://containers.goodwith.tech/ を操作してみてください。

操作方法

page.png

① ソートやフィルタが簡単にできます

header-2.png

※ Scoreは脆弱性のCVSSスコアなどを元にした参考値です。指標を一つに統一したかったので作りました。
ガチ勢の方々、怒らないでください & よりよい指標をつくるためのアドバイスをください。

② Dockle, Trivy というカラムを選択すると、JSON形式で詳細な情報が表示されます

clickable-2.png

③ JSONのロードが遅いときは「JSON Detail」のリンクからダウンロードできます

jsondetail-2.png

※ netlifyを利用してるんですが、特に大きいJSONファイルの取得が遅いので、解決方法を知りたいです

何がチェックできるの?

CISベンチマークに沿っているかチェックできます。CIS(The Center for Internet Security)のセキュリティ専門家たちが発行している資料です。

slide17

簡単に言うと、Dockleの列ではイメージの設計が正しくされているか、Trivyの列では脆弱性のあるパッケージが使われていないかをチェックできます。

その他にも、パスワードが設定されていないユーザのチェックなど、Linuxの基礎的なセキュリティもチェックします。
original-checkpoint-comparison.png

ただし、すべてのコンテナで警告が出てしまうので、「latestタグはやめよう」「Content Trustを有効にしよう」の2項目は無視しています。

データの作り方

過去に記事にした Dockle starsTrivy stars を利用しています。
対象のコンテナイメージに対してそれぞれスキャンしていき、結果を集計しました。なお800個のコンテナを平行処理して1時間掛かりませんでした。

なお、私はDockleの作者で、Trivyのメインコミッタの一人です。GitHub Starが増えると喜びます。

フロントエンドやホスト環境は?

reactnetlify.png

メインで使っているライブラリは以下のものです。
Docker Meetup Tokyo #31のLTに間に合わせるべく、画面のベース作成1日というギリギリのスケジュールだったためCreate React Appを利用しました。

├── public : JSONや画像など
├── src : ソースコード
└── yarn.lock

フォルダ構成は現在このようになっており、すべてnetlifyでホストされています。
ただ、ファイルサイズが大きくなるとnetlifyだと極端に遅くなります。
特に脆弱性の数が多いJSONデータのロードで顕著です。解決策があれば教えてください。

脆弱性対策はどうすればいい?

ここや、この記事の後半でお伝えしたとおり、どのように向き合うかは、そのサービスの用途や求めるレベルによります。

ただ、Trivyで検出された脆弱性については、新しいOSにして新しいバージョンのパッケージを入れたら脆弱性は減るので、公開されているDockerfileを元に自らの手で書き直すことをおすすめします。

脆弱性についてより詳しいことが気になった人は、私も参加しているプロジェクト Vulsのチームが主催する 「既知の脆弱性はこう捌け!」系の勉強会に参加すると、体系的に学ぶ事ができます。

最後に

最初に、Docker公式が用意しているイメージが今のところ大丈夫と伝えました。
しかし、いつ脆弱性が入るのかはわからないので、ビルドごとにイメージのチェックすることをおすすめします。実例として、今年の5月まで公式が用意したAlpine LinuxのイメージにRootユーザのパスワードが設定されていないという脆弱性がありました。

ローカルビルドの際に、すでにあるイメージを毎回参照していて、過去の脆弱性をそのまま使い続けている状況もありえます。ローカルでイメージを作成している人/コンテナのイメージをキャッシュから作成されている方は、特に一度スキャンすることをおすすめします。

なお、今後も https://containers.goodwith.tech/ に、ユーザーの入力したイメージ名からスキャンをするなど、機能追加していく予定です。 フッターにシェアボタン付けたので、シェアお願いします?

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

DockerHubで公開されているコンテナが安全か確かめてみた【人気のコンテナ上位800個!】

はじめに

Docker Hubに公開されているイメージはどの程度安全なのか、 Dockle starsTrivy stars を利用して検証しました。
検証結果は、 https://containers.goodwith.tech/ に公開しています。

page.png

結論

基本的にどのコンテナにも脆弱性はある!
人気が高いコンテナ/最近ビルドされているコンテナでも関係ない!

ただ、Docker公式が用意しているコンテナは今のところ大丈夫。
より詳しいことを知りたい人は 操作方法 を見て、 https://containers.goodwith.tech/ を操作してみてください。

操作方法

① ソートやフィルタが簡単にできます

header-2.png

※ Scoreは脆弱性のCVSSスコアなどを元にした参考値です。指標を一つに統一したかったので作りました。
ガチ勢の方々、怒らないでください & よりよい指標をつくるためのアドバイスをください。

② Dockle, Trivy というカラムを選択すると、JSON形式で詳細な情報が表示されます

clickable-2.png

③ JSONのロードが遅いときは「JSON Detail」のリンクからダウンロードできます

jsondetail-2.png

※ netlifyを利用してるんですが、特に大きいJSONファイルの取得が遅いので、解決方法を知りたいです

何がチェックできるの?

CISベンチマークに沿っているかチェックできます。CIS(The Center for Internet Security)のセキュリティ専門家たちが発行している資料です。

slide17

簡単に言うと、Dockleの列ではイメージの設計が正しくされているか、Trivyの列では脆弱性のあるパッケージが使われていないかをチェックできます。

その他にも、パスワードが設定されていないユーザのチェックなど、Linuxの基礎的なセキュリティもチェックします。
original-checkpoint-comparison.png

ただし、すべてのコンテナで警告が出てしまうので、「latestタグはやめよう」「Content Trustを有効にしよう」の2項目は無視しています。

データの作り方

過去に記事にした Dockle starsTrivy stars を利用しています。
対象のコンテナイメージに対してそれぞれスキャンしていき、結果を集計しました。なお800個のコンテナを平行処理して1時間掛かりませんでした。

なお、私はDockleの作者で、Trivyのメインコミッタの一人です。GitHub Starが増えると喜びます。

フロントエンドやホスト環境は?

reactnetlify.png

メインで使っているライブラリは以下のものです。
Docker Meetup Tokyo #31のLTに間に合わせるべく、画面のベース作成1日というギリギリのスケジュールだったためCreate React Appを利用しました。

├── public : JSONや画像など
├── src : ソースコード
└── yarn.lock

フォルダ構成は現在このようになっており、すべてnetlifyでホストされています。
ただ、ファイルサイズが大きくなるとnetlifyだと極端に遅くなります。
特に脆弱性の数が多いJSONデータのロードで顕著です。解決策があれば教えてください。

脆弱性対策はどうすればいい?

ここや、この記事の後半でお伝えしたとおり、どのように向き合うかは、そのサービスの用途や求めるレベルによります。

ただ、Trivyで検出された脆弱性については、新しいOSにして新しいバージョンのパッケージを入れたら脆弱性は減るので、公開されているDockerfileを元に自らの手で書き直すことをおすすめします。

脆弱性についてより詳しいことが気になった人は、私も参加しているプロジェクト Vulsのチームが主催する 「既知の脆弱性はこう捌け!」系の勉強会に参加すると、体系的に学ぶ事ができます。

最後に

最初に、Docker公式が用意しているイメージが今のところ大丈夫と伝えました。
しかし、いつ脆弱性が入るのかはわからないので、ビルドごとにイメージのチェックすることをおすすめします。実例として、今年の5月まで公式が用意したAlpine LinuxのイメージにRootユーザのパスワードが設定されていないという脆弱性がありました。

ローカルビルドの際に、すでにあるイメージを毎回参照していて、過去の脆弱性をそのまま使い続けている状況もありえます。ローカルでイメージを作成している人/コンテナのイメージをキャッシュから作成されている方は、特に一度スキャンすることをおすすめします。

なお、今後も https://containers.goodwith.tech/ に、ユーザーの入力したイメージ名からスキャンをするなど、機能追加していく予定です。 フッターにシェアボタン付けたので、シェアお願いします?

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

DockerHubで公開されているコンテナが安全か確かめてみた結果【人気のコンテナ上位800個!】

はじめに

Docker Hubに公開されているイメージはどの程度安全なのか、 Dockle starsTrivy stars を利用して検証しました。
検証結果は、 https://containers.goodwith.tech/ に公開しています。

screencast24.gif

結論

基本的にどのコンテナにも脆弱性はある!
人気が高いコンテナ/最近ビルドされているコンテナでも関係ない!

ただ、Docker公式が用意しているコンテナは今のところ大丈夫。
より詳しいことを知りたい人は 操作方法 を見て、 https://containers.goodwith.tech/ を操作してみてください。

操作方法

page.png

① ソートやフィルタが簡単にできます

header-2.png

※ Scoreは脆弱性のCVSSスコアなどを元にした参考値です。指標を一つに統一したかったので作りました。
ガチ勢の方々、怒らないでください & よりよい指標をつくるためのアドバイスをください。

② Dockle, Trivy というカラムを選択すると、JSON形式で詳細な情報が表示されます

clickable-2.png

③ JSONのロードが遅いときは「JSON Detail」のリンクからダウンロードできます

jsondetail-2.png

※ netlifyを利用してるんですが、特に大きいJSONファイルの取得が遅いので、解決方法を知りたいです

何がチェックできるの?

CISベンチマークに沿っているかチェックできます。CIS(The Center for Internet Security)のセキュリティ専門家たちが発行している資料です。

slide17

簡単に言うと、Dockleの列ではイメージの設計が正しくされているか、Trivyの列では脆弱性のあるパッケージが使われていないかをチェックできます。

その他にも、パスワードが設定されていないユーザのチェックなど、Linuxの基礎的なセキュリティもチェックします。
original-checkpoint-comparison.png

ただし、すべてのコンテナで警告が出てしまうので、「latestタグはやめよう」「Content Trustを有効にしよう」の2項目は無視しています。

データの作り方

過去に記事にした Dockle starsTrivy stars を利用しています。
対象のコンテナイメージに対してそれぞれスキャンしていき、結果を集計しました。なお800個のコンテナを平行処理して1時間掛かりませんでした。

なお、私はDockleの作者で、Trivyのメインコミッタの一人です。GitHub Starが増えると喜びます。

フロントエンドやホスト環境は?

reactnetlify.png

メインで使っているライブラリは以下のものです。
Docker Meetup Tokyo #31のLTに間に合わせるべく、画面のベース作成1日というギリギリのスケジュールだったためCreate React Appを利用しました。

├── public : JSONや画像など
├── src : ソースコード
└── yarn.lock

フォルダ構成は現在このようになっており、すべてnetlifyでホストされています。
ただ、ファイルサイズが大きくなるとnetlifyだと極端に遅くなります。
特に脆弱性の数が多いJSONデータのロードで顕著です。解決策があれば教えてください。

脆弱性対策はどうすればいい?

ここや、この記事の後半でお伝えしたとおり、どのように向き合うかは、そのサービスの用途や求めるレベルによります。

ただ、Trivyで検出された脆弱性については、新しいOSにして新しいバージョンのパッケージを入れたら脆弱性は減るので、公開されているDockerfileを元に自らの手で書き直すことをおすすめします。

脆弱性についてより詳しいことが気になった人は、私も参加しているプロジェクト Vulsのチームが主催する 「既知の脆弱性はこう捌け!」系の勉強会に参加すると、体系的に学ぶ事ができます。

最後に

最初に、Docker公式が用意しているイメージが今のところ大丈夫と伝えました。
しかし、いつ脆弱性が入るのかはわからないので、ビルドごとにイメージのチェックすることをおすすめします。実例として、今年の5月まで公式が用意したAlpine LinuxのイメージにRootユーザのパスワードが設定されていないという脆弱性がありました。

ローカルビルドの際に、すでにあるイメージを毎回参照していて、過去の脆弱性をそのまま使い続けている状況もありえます。ローカルでイメージを作成している人/コンテナのイメージをキャッシュから作成されている方は、特に一度スキャンすることをおすすめします。

なお、今後も https://containers.goodwith.tech/ に、ユーザーの入力したイメージ名からスキャンをするなど、機能追加していく予定です。 フッターにシェアボタン付けたので、シェアお願いします?

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

DockerでGitLabとDB2のコンテナを起動するコマンド覚書

GitLabを起動する

docker-compose.yml
web:
    image: 'gitlab/gitlab-ce:latest'
    restart: always
    hostname: 'localhost'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://localhost:9010'
        gitlab_rails['gitlab_shell_ssh_port'] = 2022
    ports:
      - '9010:9010'
      - '2022:22'
    volumes:
      - '/srv/gitlab/config:/etc/gitlab'
      - '/srv/gitlab/logs:/var/log/gitlab'
      - '/srv/gitlab/data:/var/opt/gitlab'

DB2を起動する

ディレクトリ構成

/docker-compose.yml
/db/Dockerfile

Dockerfile

FROM ibmcom/db2express-c:latest
docker-compose.yml
db2:
    container_name: mydb2
    ports:
        - '50000:50000'
    environment:
        - LICENSE=accept
        - DB2INST1_PASSWORD=password
        - DBNAME=testdb
    volumes:
        - '/db2:/database'
    image: ibmcom/db2

起動コマンド

docker-compose up

※微調整が必要な可能性あり

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

フロント「Vue + gRPC-Web」、サーバー「Python + gRPC」構成でタイマーアプリを作る。

概要

REST APIより良いAPIの通信方法はないものか調べていたら、gRPCというものが見つかりました。なかなか良さそうなので簡単なアプリを作りながら、使った感じをメモします。

結論を先に言うと、Pythonでサーバー作るのちょっときついかも。。でした。
期待していた、messageの補完が効かないからです。

gRPCってなに

gRPCはRPC実装の一つです。

RPCとはなにかといういうと、REST APIの弱点を克服する通信規格のひとつです。
RESTの辛みを解消した、代替候補の1つという認識です。

REST APIとの大きな違いとしては、引数や返り値のデータ型をしっかり決める必要があることかな、と。
これの何が嬉しいかというと、APIの引数、返り値を編集したり使用するときに補完が効く(はず)ということ。
APIの構造を決めればデータ型は自動生成する機能もついています。
なので、仕様書を見ながらこの項目は文字型だ、数字型だということを気にしなくてもよく、より実装=仕様な感じになるのではないかなと。

あと、サーバーとの双方向通信(プッシュ型)の処理もできるのは期待大です。
もうポーリングする必要ないんです!

RPC(Remote Procedure Call)の名前が示す通り、REST APIよりも、関数を呼ぶ感覚で使える感じですね。

やってみる

作るもの

タイマーを作ってみます。
フロントVue, サーバーPythonの構成でgRPC通信します。
別にサーバーなくても作れる内容なんですが、とにかくgRPCを無理やり使うんです。

それがサンプルプログラムってもんです。
仕様
  • タイマー機能はサーバーに持つ。
  • フロントからタイマー開始の命令をgRPC経由で送る。
  • サーバーからは1秒おきに残り時間をgRPC経由でフロントに送る。
  • フロントはサーバーから送られてきた残り時間を画面に表示する。

作ったものをGitHubに上げています。

試した環境(前提)

  • Windows 10
  • Python 3.6
  • Node.js 12.5.0
  • npm 6.9.0
  • yarn 1.9.4
  • Docker for Windows 2.0.0.3 (envoy用)

(余談)Docker初めて使いましたが、死ぬほど便利ですね。

環境構築

必要なツールを入れていきます。

Vueをインストール

フロントはVueで作るので、Vueのツールをインストールします。

npm install -g @vue/cli

ここを参考にさせていただきました。

Protocol Buffer のコンパイラの準備

gRPCでは、APIの仕様の記述をProtocol Buffersというインターフェース定義用言語を使います。
Protocol Buffersは、雑な例えをすると、SQLのCREATE TABLE構文だけ抜き出したもの+関数インターフェース定義ができる言語、みたいな印象です。
Protocol Buffersで書いたデータ定義や関数定義を各言語にコンパイルするため,言語別のコンパイラをインストールします。
今回はjavascript用とPython用です。

javascript用(gRPC-web)コンパイラのインストール
手動で入れます。

コンパイラ本体javascript用プラグインをダウンロードして、中に入っている実行ファイル(protoc.exeprotoc-gen-grpc-web.exe)にそれぞれパスを通す。

インストールされたか確認するには、

protoc --version
libprotoc 3.8.0

となればとりあえずOK。
javascript用ですが、Typescript定義ファイルも一緒に吐き出せます。やった。

ここの手順は公式チュートリアルのここらへんを参考にしています。

Python用コンパイラのインストール
pip install grpcio-tools

インストールされたか確認するには、

python -m grpc_tools.protoc --version
libprotoc 3.8.0

となればOK。

疑問だった点。

何でweb用とpython用でコンパイラの入れ方が違うのだろう。。
個人的には、python用もprotoc-gen-python.exe みたいなプラグインにして、コンパイラ本体は共通にしてほしかったです。
どうやらpipでインストールしたほうもprotocコンパイラの本体(をdllにしたもの)が別途ダウンロードされているようなので、クライアントとサーバーでバージョンが異なったりしないのかモヤモヤします。

APIの実装

Protocol Buffersを実装していきます。

timer.proto
syntax = "proto3";

package timer_with_grpc;

service Timer {
  rpc StartTimer(StartRequest) returns (stream TimerState);  // タイマー開始
  rpc StopTimer(Empty) returns (TimerState); // タイマー停止
}

message StartRequest {
  int32 time = 1;
}

message TimerState {
  bool isRunning = 1;
  int32 leftTime = 2;
}

// 引数空ができないようなので、空用の定義をしておく
message Empty {
}

シンプル。
基本的な定義はサンプルの書き換えで何とかなりそうな印象です。

serviceの中にAPIのインターフェースを定義し、messageで引数や返却値のデータ構造を定義しています。
ここではStartTimerStopTimerの2つのAPIを定義しています。
引数がいらないAPIもありそうですが、

rpc StopTimer() returns (TimerState);

としてみたらエラーになったので、何かしら引数はいるみたいです。
今回はEmptyという空のデータ型を定義してみました。
良い方法かどうかはわかりません。

また、returns に stream をつけるとサーバーからデータをプッシュ型でクライアントに送信できます。これがやりたかった。

Protocol Buffersのコンパイル

クライアント用コード生成(Javascript + Typescript定義ファイル)
protoc -I=. timer.proto --js_out=import_style=commonjs:. --grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext:.

timer_pb.js
timer_pb.d.ts
timer_grpc_web_pb.js
timer_grpc_web_pb.d.ts

という4つのファイルが出力されました。
オプション次第でちゃんとTypescriptの定義ファイルまで出してくれるのでありがたいですね。(+dtsをつける)
内容結構ごつくてちゃんと読めてませんが、timer_grpc_web_pb.d.tsにAPIのクライアントインターフェースらしきものがあることが確認できます。

サーバ用コード生成(Python)
python -m grpc_tools.protoc -I. timer.proto --python_out=. --grpc_python_out=.

timer_pb2.pytimer_pb2_grpc.pyという2つのファイルが出力されました。
timer_pb2_grpc.pyの方にAPI実装のひな型があるので、継承して機能を実装します。

サーバー側APIの実装

TimerApi.py
import timer_pb2
import timer_pb2_grpc

from concurrent import futures
import time
import grpc


# APIのロジック
class TimerServicer(timer_pb2_grpc.TimerServicer):
    leftTime = 0
    isRunning = False

    def StartTimer(self, request, context):
        self.leftTime = request.time
        self.isRunning = True

        while self.leftTime > 0:
            if self.isRunning: # 途中でStopTimerされてないかチェック
                yield self.makeTimerState()
                time.sleep(1)
                self.leftTime -= 1
            else:
                return

        self.isRunning = False
        yield self.makeTimerState()

    def StopTimer(self, request, context):
        self.isRunning = False
        return self.makeTimerState()

    def makeTimerState(self):
        return timer_pb2.TimerState(
            isRunning=self.isRunning,
            leftTime=self.leftTime
        )


# サーバーの実行
def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    timer_pb2_grpc.add_TimerServicer_to_server(TimerServicer(), server)
    server.add_insecure_port('0.0.0.0:8082')
    server.start()
    print("Server Start!!")
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        server.stop(0)


if __name__ == '__main__':
    serve()

TimerServicerがprotocコンパイラが出力したAPIインターフェースを継承して
ロジックを追加した部分です。

StartTimerは、引数でもらった秒数だけカウントダウンしていきます。
StopTimerで止めます。

クライアント側実装

Vueでプロジェクトを作成

まずはVueのclientとう名前のプロジェクトを作成します。

$ vue create client


Vue CLI v3.8.4
┌───────────────────────────┐
│  Update available: 3.9.2  │
└───────────────────────────┘
? Please pick a preset: Manually select features
? Check the features needed for your project: TS, CSS Pre-processors
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? No    
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

Babelを使用しないで、Typescriptとscssを使うようにした以外はデフォルトで作りました。

コンパイルしてみる。

クライアント用のプロジェクトができたので、いったんコンパイルしてみましょう。
コンパイルのコマンドはclientディレクトリに入ってから、

yarn serve

です

その際、Protocol Buffersのコンパイルした
timer_pb.js
timer_pb.d.ts
timer_grpc_web_pb.js
timer_grpc_web_pb.d.ts
も含めて(client/src以下に4ファイルを置いて)コンパイルしてみると、
以下のようなエラーがでてきました。

1:23 Cannot find module 'google-protobuf'.
  > 1 | import * as jspb from "google-protobuf"
      |                       ^
<<中略>>
10:26 Cannot find module 'grpc-web'.
  > 10 | import * as grpcWeb from 'grpc-web';
       |                          ^

google-protobufgrpc-webがないと怒られたので、素直にインストールします。

yarn add google-protobuf @types/google-protobuf grpc-web

あたらめてコンパイルすると、今度は通りました。

実装
App.vue
<template>
    <div id="app">
        <p>
           - TIMER -
        </p>
        <br>
        <p>
            {{isTimerRunning ? "実行中" : "停止中"}}
        </p>
        <p class="time">
            {{leftTime}}
        </p>

        <button @click="startTimer">start</button>
        <button @click="stopTimer">stop</button>
    </div>
</template>

<script lang="ts">
    import {Component, Vue} from 'vue-property-decorator';
    import {TimerClient} from "./timer_grpc_web_pb";
    import {Empty, StartRequest, TimerState} from "./timer_pb";
    import {ClientReadableStream} from "grpc-web";

    @Component({})
    export default class App extends Vue {
        private timerClient: TimerClient;
        private isTimerRunning: boolean = false;
        private leftTime: number = 0;

        constructor() {
            super();
            this.timerClient = new TimerClient('http://' + window.location.hostname + ':8081', null, null);
        }

        private startTimer(): void {
            const request = new StartRequest();
            request.setTime(10);  // 10秒をセット
            const stream: ClientReadableStream<TimerState> = this.timerClient.startTimer(request, {});
            stream.on('data', (response: TimerState) => {
                this.isTimerRunning = response.getIsrunning();
                this.leftTime = response.getLefttime();
            });
        }

        private stopTimer(): void {
            this.timerClient.stopTimer(new Empty(), {}, (err, response: TimerState) => {
                this.isTimerRunning = response.getIsrunning();
                this.leftTime = response.getLefttime();
            });
        }

    }
</script>

<style lang="scss">
    #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }
    .time{
        margin:0 0 40px 0;
        font-size:148px;
        color:#999;
    }
</style>

Typescript側は、コンストラクタでAPIクライアントをインスタンス化し、開始ボタン or 停止ボタンで対応するAPIをコールします。
タイマー開始の方はサーバー側から時間が変わるたびにデータがプッシュされてくるので、その値に応じて秒数を更新する仕組みです。

プロキシサーバーを用意

ここに関しては公式のサンプル通りです。
正直よくわかってないです。
envoyというプロキシサーバー(本来はロードバランサらしい?)を使います。
現状gRPCのリクエストをダイレクトにサーバーに伝えられないらしいです。(なぜだ。ポートの問題?)

公式のチュートリアルから、

envoy.Dockerfile
envoy.yaml
をひろってきて、以下のコマンドで

docker build -t envoy_for_timer -f ./envoy.Dockerfile .

実行する

サーバーアプリ起動

python TimerApi.py 

リバースプロキシ起動

docker run -d -p 8081:8081 envoy_for_timer

フロントアプリ起動

yarn serve

フロントアプリを起動後に

  App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.1.22:8080/

と表示されましたら、上記アドレスでアクセスできるはずです。

tello_yolo_camera.gif

感想

  • REST APIしか触ったことない勢としては、サーバーからのプッシュ型のAPIは触ってて面白かったです。
  • streamでつなぎ続けると再度実行されたときに前のものをキャンセルしたりしたいケースもありそうだなと思いました。
  • Python版は情報が少ない。DBとのつなぎをDjangoにすることをもくろんでPythonを選んだけれど、Goで書いた方がよいのかなと悩む。
  • Typescriptの方はmessageの変数名の補完が効いた。Pythonの方はほとんど効かなかった。Pythonだめなのかなー。。

こちらからは以上です。

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