Docker で日本語形態素解析を Kagome の Web API で手軽に利用する（分かち書き解析）

> Docker で日本語を**手軽にワカチコ**したい。

- [わかち書きとは](#%E3%82%8F%E3%81%8B%E3%81%A1%E6%9B%B8%E3%81%8D%E3%81%A8%E3%81%AF)

## Kagome on Docker

あの日本語[形態素解析](https://ja.wikipedia.org/wiki/%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90)器「[Kagome](https://github.com/ikawaha/kagome)」が [v1.10.0](https://github.com/ikawaha/kagome/releases/tag/v1.10.0) から公式に Docker に対応しました。（2020/08/08 に [Kagome v2 がリリース](https://github.com/ikawaha/kagome/releases/tag/v2.0.1)されました）

つまり、 `Kagome` のコンテナに別のコンテナから Web API を叩いて（HTTP リクエストして）形態素解析機能が使えます。

また、従来どおり **[MeCab-IPADIC](http://taku910.github.io/mecab/) と [UniDic (unidic-mecab)](http://pj.ninjal.ac.jp/corpus_center/unidic/) の辞書も標準で同梱**されています。もちろんユーザー辞書も使えます。

```bash:DockerイメージのPULL
docker pull ikawaha/kagome:latest
```
```shell-session:sample（対話モード）
$ docker run --rm -it ikawaha/kagome
すもももももももものうち
すもも   名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも  名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも  名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち  名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
```

- Kagome Web API のリクエスト情報（仕様）
    - リクエスト・エンドポイント
        - Kagome v1 => `/a`
        - Kagome v2 => `/tokenize`
    - リクエスト・メソッド
        - `PUT`
    - リクエスト・ヘッダ（以下のいずれか）
        - `Content-type: application/json; charset=UTF-8`
        - `Content-type: application/x-www-form-urlencoded`
    - リクエスト・ボディ（`content`、本文データ）
        - JSON 配列の文字列データ
            - キー：`sentence`、値：分かち書きしたい文字列データ
            - キー：`mode`、値：`normal`
        - ヘッダが `application/x-www-form-urlencoded` の場合は、データは URL エンコードすること

```shell-session:KagomeWebAPIの動作サンプル
$ # Kagome サーバーの起動
$ # Kagome Web API コンテナの起動（80ポートを8888ポートにポートフォワードで外部公開）
$ docker run --rm -d -p 8888:80 --name kagomeAPI ikawaha/kagome server -http=":80"
...

$ # リクエスト例（RESTful, Kagome v1）
$ curl -s -X PUT localhost:8888/a -d'{"sentence":"お寿司食べたい", "mode":"normal"}' | jq .
...
$ # リクエスト例（RESTful, Kagome v2）
$ curl -s -X PUT localhost:8888/tokenize -d'{"sentence":"お寿司食べたい", "mode":"normal"}' | jq .
...
```

- [Bash の cURL や PHP などのリクエストのサンプル](#kagome-web-api-%E3%81%AE%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E6%83%85%E5%A0%B1)

[![](https://images.microbadger.com/badges/image/ikawaha/kagome.svg)](https://microbadger.com/images/ikawaha/kagome "View image info on microbadger.com") [![Docker Automated build](https://img.shields.io/docker/cloud/automated/ikawaha/kagome.svg)](https://hub.docker.com/r/ikawaha/kagome/ "View on Docker Hub") [![](https://img.shields.io/docker/cloud/build/ikawaha/kagome.svg)](https://hub.docker.com/r/ikawaha/kagome/builds "View builds on DockerHub")

- Base Image: Alpine Linux
    - ENTRYPOINT: `/usr/local/bin/kagome`
    - CMD: `なし`（`kagome` コマンドの引数の指定なし）
- Repoistories:
    - Image: https://hub.docker.com/r/ikawaha/kagome/ @ Docker Hub
    - Source: https://github.com/ikawaha/kagome @ GitHub
- Document: https://qiita.com/ikawaha/items/ff27ac03e22b7f36811b @ Qiita [or [more](https://www.google.com/search?q=site:qiita.com+%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90+kagome)]
- Author: @ikawaha 
- コンテナ間通信の API 利用サンプル
  - [`kagome` の WebAPI を PHP7 のコンテナから叩いてブラウザに表示するサンプル](https://github.com/KEINOS/Kagome-On-Docker-Sample) @ GitHub

この記事（TS;DR）では、`kagome` の Docker での使い方と WebAPI としての使い方を説明しています。

## TS;DR（詳細）

この記事では、**`kagome` を Docker で使えることによるメリットや使い方の一例**を紹介したいと思います。

また、以下に該当するかたを読者対象としています。

- 文書を「[わかち書き](https://ja.wikipedia.org/wiki/%E3%82%8F%E3%81%8B%E3%81%A1%E6%9B%B8%E3%81%8D)」にしたい
- プログラム言語を問わず、何かしらの RESTful な Web API を使ったことがある（`cURL` 含む）
- Docker は `docker build` `docker run` を [CLI シェル](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%A7%E3%83%AB#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB%E3%81%AE%E4%B8%80%E8%A6%A7)で使ったり Dockerfile を書いたことがある

### わかち書きとは

わかち書きとは「すもももももももものうち」を「すもも も もも も もも の うち」と要素ごとにスペースをわかちあう例のやつです。

この処理を行うために日本語の構文を解析するのですが、それを「形態素解析」と呼びます。

形態素解析は、機械学習に使われるだけでなく、読み上げソフトなどで間を空けるタイミングや、「は」を「`ha`」と読み上げるか「`wa`」と読み上げるかなどの解析にも使われます。

「`kagome` には興味あるけど、Docker よくわかんない」というかたは、以下の記事を参考にしてみてはいかがでしょう。

- 「[Dockerでプログラマが最低限知るべきことが、最速でわかるチュートリアル](https://qiita.com/woz/items/43462398765f8036f3a8)」@ Qiita
- [ここ１年以内に更新 or 作成された「`docker` `入門`」の Qiita 記事の検索結果](https://www.google.com/search?q=site:qiita.com+docker+入門&tbs=qdr:y) @ Google

### Why Docker?

基本的に `kagome` は Go 言語（以下 Golang）のパッケージとして Golang のプログラム内でライブラリもしくはモジュール的に使われることを主としています。

そのため「`kagome` 興味あるけど Golang だもんなぁ」と、<ruby>躊躇<rp>(</rp><rt>ちゅうちょ</rt><rp>)</rp></ruby>していた他のプログラム言語ユーザーも多いのではないでしょうか。

また、Golang と言えばクロス・コンパイルで有名なコンパイル型言語です。つまり、コンパイルしてバイナリ化したものは Golang 環境やランタイムなどがなくても動くアプリやコマンドが作れます。

しかし、`kagome` をコンパイルしたバイナリを外部コマンドとして使うにしても、「そのため（コンパイルのため）だけに Golang 入れたり、覚えたり、更新するのもコンパイるなぁ」といった人も多いかも知れません。[MeCab](https://ja.wikipedia.org/wiki/MeCab) にしようか悩んだあげく、形態素解析の Web サービスに投げたり、サービスが終わってしまったり。

安心してください。Golang 環境がなくても **Docker で気軽・手軽に `kagome` で分かち書き変換の機能が使える**ようになります。

これを機に `kagome` に触れてみてはいかがでしょうか。

### Docker での使い方

何はともあれ、まずは `kagome` の Docker イメージをダウンロードします。

```shell-session
$ docker pull ikawaha/kagome:latest
```

- <sup>🐒【注意】 上記は AMD64/Intel 用にビルドされたイメージです。ラズパイなどの ARM アーキテクチャの場合は、別途ビルドが必要になります。

#### 基本的な使い方

基本的な使い方は簡単。従来の `kagome` コマンドを `docker run ikawaha/kagome` に置き換えるだけです。

```shell-session:kagomeのヘルプ表示（-hオプション）
$ kagome -h
...
```
```shell-session:docker経由のkagomeのヘルプ表示（-hオプション）
$ docker run --rm ikawaha/kagome -h
...
```

#### 基本構文

```bash:基本構文
docker run [ run のオプション ] ikawaha/kagome [ kagome のオプション ]
```

上記の基本構文は２つのパートにわかれています。`docker` コマンドのパートと `kagome` コマンドのパートです。

```bash:基本構文
[    Docker コマンド パート    ] [       Kagome コマンド パート        ]
docker run [ run のオプション ] ikawaha/kagome [ kagome のオプション ]
```

`docker run [run のオプション]` までが `docker` のパートで、`ikawaha/kagome [kagome のオプション]` が `kagome` のパートです。

例えば `kagome` コマンドのバージョン情報や、サブ・コマンドのヘルプを知りたい場合は以下のようになります。

```shell-session:kagomeのバージョン確認（versionコマンド）
$ # 以下は `kagome version` と同等
$ docker run --rm ikawaha/kagome version
```

```shell-session:kagomeのtokenizeコマンドのヘルプ表示（-hオプション）
$ # 以下は `kagome tokenize -h` と同等
$ docker run --rm ikawaha/kagome tokenize -h
```

#### 実際に解析してみる

「すもももももももものうち」を形態素解析したい場合は以下のようになります。

```shell-session:対話モードで形態素解析を実行
$ # -it は --interactive --tty の略です
$ docker run --rm -i -t ikawaha/kagome
すもももももももものうち
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
うち	名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
^C$ # Ctl+C で対話モード終了
```

- 固有名詞など、**同梱の辞書に含まれない単語があると希望する結果にならない**場合は、記事の下部にある「ユーザー辞書の使い方」を参照してください。

## Kagome on Docker の真骨頂

さて、ここまでは Docker 内にある `kagome` バイナリを動かしているに過ぎません。

上記コマンドをシェル・スクリプトに記載して[パスを通して](https://www.google.com/search?q=site:qiita.com+%E3%83%91%E3%82%B9%E3%82%92%E9%80%9A%E3%81%99&tbs=qdr:y)おけば `kagome` コマンドとして使うこともできますし、最新バイナリを探したり、ビルドする必要がないだけでも楽だと思います。

しかし、やはり Docker で使うなら、もう少しコンパイるのいらないメリット感が欲しいところです。１つで２つおいしい感じの。

### Kagome Web API と Docker でもっと便利に

Docker を使うメリットは、アプリやサービスを動かすのに Docker 以外の**必要なランタイムやライブラリ類のインストールが不要になる**ことです。

しかし、もう１つのメリットは「１コンテナ１機能」として利用することで構造をシンプルにできるのでメンテナンスがしやすくなる点です。

例えば１台のサーバーに複数サービスを稼働させている場合、「この機能（例えばとある WebAPI）は安定しているので、これだけラズパイとかで隔離したいな」と思ったことがある人も多いと思います。せっかく安定してるのに、他のサービスが変なポートを開けないといけないものだから、気にしないといけないことが増えた、などです。

つまり「サーバーで実行中のプロセスをイメージに落として、別サーバーで動かせればいいのに」と。

Docker は `ps` コマンドで表示されるような**プロセスを抜き出してイメージ化するようなもの**です。そのため、特定のサービスや機能だけを抜き取って運用するようなケースには、まさにもってこいなのです。

つまり、「１コンテナ＝１サーバー＝１サービス」的に運用できるのも Docker を使うメリットです。

そこで、`kagome` 専用の Web API サーバーを建てて、他のサーバー（もしくはクライアント）から RESTful に（HTTP 経由で）アクセスできれば、各々のサーバーは１つの機能に専念できるというものです。

幸いなことに、**`kagome` にはビルトイン・Web サーバーと API が標準で備わっています**。

例えば、バックグラウンドで `kagome` の Web API サーバーを稼働させるには、下記コマンドを叩きます。

```shell-session:kagomeサーバーの起動（8888ポートで待機）
$ docker run --rm -d -p 8888:80 --name kagomeAPI ikawaha/kagome server -http=":80"
```

<details><summary>上記コマンドの内訳（説明）</summary><div><br>

- `docker run`
  - **`--rm` オプション**：コンテナを終了すると不要になったコンテナを削除します。
  - **`-d` オプション**：コンテナをバックグラウンドで起動させます。
  - **`-p 8888:80` オプション**：Docker 内のネットワークから見て WAN 側のポート 8888 番を LAN 側の `kagome` サーバー（`kagome` のコンテナ）の 80 番ポートにポートフォワード（ポート番号を変えながらデータを転送）します。
  - **`--name kagomeAPI`**：コンテナに名前を付けます。コンテナを終了させる時に `docker container kill kagomeAPI` と名前で指定できるようになります。

- `ikawaha/kagome`
    - `ikawaha/kagome` は、コンテナの元となる Docker のイメージ名です。
        - `Entrypoint`（デフォルトで呼び出されるコマンド）は `kagome` に指定されているので、続く引数が `kagome` のオプションになります。
    - **`server` コマンド**： `kagome` を Web サーバーモードで実行し、常駐させる `kagome` のサブコマンドです。`kadome server` と同等です。 
    - **`-http=":80"` オプション**： `kagome` サーバーを 80 番ポートで待機させる、`server` コマンドのオプションです。

</div></details>

すると、以降は `localhost:8888` にリクエストするだけで `kagome Web API` が利用できます。

### Kagome Web API のリクエスト情報

- エンドポイント
    - Kagome v1 => `/a`
    - Kagome v2 => `/tokenize`
- リクエストメソッド
    - `PUT`
- リクエストヘッダ
    - `Content-type: application/json; charset=UTF-8`
- リクエストボディ（`content`、本文データ）
    - JSON 配列の文字列データ
        - キー：`sentence`、値：分かち書きしたい文字列データ
        - キー：`mode`、値：`normal`

```bash:v1のエンドポイント
http://localhost:8888/a
```
```bash:v2のエンドポイント
http://localhost:8888/tokenize
```

<details><summary>Kagome Web API のリクエスト例（Bash cURL と PHP）</summary><div><br>

<h3>bash の cURL のサンプル</h3>

```shell-session:cURLでkagomeAPIを利用してみる
$ # Kagome v1 のリクエストサンプル
$ curl -s -XPUT localhost:8888/a -d'{"sentence":"お寿司食べたい", "mode":"normal"}'|jq .
{
  "status": true,
  "tokens": [
    {
      "id": 10618,
      "start": 0,
      "end": 1,
      "surface": "お",
      "class": "KNOWN",
      "features": [
        "接頭詞",
        "名詞接続",
        "*",
        "*",
        "*",
        "*",
        "お",
        "オ",
        "オ"
      ]
    },
    {
      "id": 190872,
      "start": 1,
      "end": 3,
      "surface": "寿司",
      "class": "KNOWN",
      "features": [
        "名詞",
        "一般",
        "*",
        "*",
        "*",
        "*",
        "寿司",
        "スシ",
        "スシ"
      ]
    },
    {
      "id": 381475,
      "start": 3,
      "end": 5,
      "surface": "食べ",
      "class": "KNOWN",
      "features": [
        "動詞",
        "自立",
        "*",
        "*",
        "一段",
        "連用形",
        "食べる",
        "タベ",
        "タベ"
      ]
    },
    {
      "id": 39236,
      "start": 5,
      "end": 7,
      "surface": "たい",
      "class": "KNOWN",
      "features": [
        "助動詞",
        "*",
        "*",
        "*",
        "特殊・タイ",
        "基本形",
        "たい",
        "タイ",
        "タイ"
      ]
    }
  ]
}
```

<h3>PHP のサンプル</h3>

<h4><code>file_get_contents</code> を使った例</h4>

```php
<?php

$url  = 'http://kagome/a';        // Kagome v1 の場合のエンドポイント
$url  = 'http://kagome/tokenize'; // Kagome v2 の場合のエンドポイント

$data = [
    'sentence' => 'お寿司食べたい',
    'mode'     => 'normal',
];

$context = stream_context_create([
    'http' => [
        'method'  => 'PUT',
        'header'  => 'Content-type: application/json; charset=UTF-8',
        'content' => json_encode($data),
    ]
]);

$json = file_get_contents($url_container, false, $context);

header('Content-Type: application/json');
echo json_encode(json_decode($json), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

```

<h4>HttpClient の Symfony コンポーネントを使った例</h4>

PHP の [composer](https://qiita.com/KEINOS/items/86a16b06af6e936a1841#composer-%E3%81%AE%E5%9F%BA%E7%A4%8E%E7%9F%A5%E8%AD%98) で [Symfony の <code>HTTP Client</code> コンポーネント](https://symfony.com/doc/current/http_client.html)の composer パッケージを導入して使うと便利です。この `HTTP Client` は PHP の `cURL` や PHP ストリームなどのラッパーで API のリクエストに必要な機能を備えています。`composer require symfony/http-client` コマンドで導入可能です。

```php
<?php

require_once('./vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$url  = 'http://kagome/a';        // Kagome v1 の場合のエンドポイント
$url  = 'http://kagome/tokenize'; // Kagome v2 の場合のエンドポイント

$data = [
    'sentence' => 'お寿司食べたい',
    'mode'     => 'normal',
];

$response = $this->client->request(
    'PUT',
    $url,
    [
        'json' => $data
    ]
);

var_dump($response->getStatusCode());
var_dump($response->getContent());

```

</div></details>


### docker-compose でさらに便利に

`docker-compose` に馴染みのあるかたは、ここまででピンと来たかもしれません。

「`docker-compose` でコンテナ間通信をさせればいいじゃない」と。

つまり、Docker 内に仮想的な LAN 環境を作り、フロントエンドの自分のコンテナとバックエンドとして `kagome` のコンテナを建てて、フロントからバックに HTTP 経由で `kagome` の `WebAPI` を利用して形態素解析させて、結果だけを WAN 側（エンドユーザー）に返す[プロキシ](https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7)的なシステムが簡単に作れます。

わかっちゃったかたは以下は不要だと思います。エンジョイ kagome ライフ！

#### docker-composeとは

**`docker-compose` とは** `init.d` `service` `systemctl` などと似た、**コンテナをサービス・アプリ的に管理するコマンド・ツール**です。

例えば、`myService1` というサービスがあった場合、各種 OS のプロセス管理ツールだと以下のようにして起動させます。

- `/etc/init.d/myService1 start`
- `service myService1 start`
- `systemctl start myService1`

似たような感覚で `docker-compose` でもコンテナを起動できます。起動方法は以下の通りです。

```shell-session:myService1を起動する
$ # myService1 はループしてカウントを表示するだけの常駐コンテナ
$ docker-compose up myService1
Starting myService1 ... done
Hello World! Welcome to myService1!
Sample looping. Count 1
Sample looping. Count 2
Sample looping. Count 3
（以下略）
```

ちなみにサービス名を指定しない場合、つまり `docker-compose up` とした場合は、全てのコンテナが一気に起動します。（`docker-compose` の設定ファイルの書き方は後述します）

サービス起動後に続けてコマンド操作が必要な場合、つまりサービスをバックグラウンドで実行させたい場合があると思います。その場合、`-d` オプションを付けるとバックグラウンドで起動できます。

バックグラウンドで起動したコンテナには `exec` コマンドで、そのコンテナにアクセスすることができます。

```shell-session:myService1をバックグラウンドで起動して別途アクセスする
$ docker-compose up -d myService1
Starting myService1 ... done
$
$ docker-compose exec myService1 /bin/sh
...
```

#### コンテナの再利用

コンテナの起動以外に `docker-compose` を使う、**もう一つのメリットはコンテナの再利用**です。

`docker run` などで起動に失敗したコンテナや [PID 1 が終了してしまったコンテナ](https://qiita.com/woz/items/43462398765f8036f3a8#attach-%E3%81%A8-exec-%E3%81%AE%E9%81%95%E3%81%84)（完全に処理が終了してしまったコンテナ）は再利用できないのですが、正常に終了したコンテナであれば基本的に再利用可能です。

問題は `docker` コマンドでコンテナを再利用しようとすると、コンテナの起動状況を確認して、コンテナ ID を指定しないといけないので、いささか面倒なのです。

例えば、以下のように `docker container ls` コマンドで起動中のコンテナを確認できますが `-a` オプションを付けると起動していないコンテナも含めて確認できます。

```shell-session:終了したコンテナを再起動する方法
$ # 起動しているコンテナを確認
$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
$ # 落ちているコンテナを確認
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                       PORTS               NAMES
acc84eb2b6f8        alpine:latest       "/bin/sh"           3 minutes ago       Exited (127) 3 minutes ago                       vigilant_mccarthy
30d08ed0beb5        ikawaha/kagome      "kagome"            9 minutes ago       Exited (2) 3 minutes ago                         jovial_kalam
$ # 落ちていた kagome のコンテナを -i (interactive) 付きで再起動
$ docker container start -i 30d08ed0beb5
ヌコもニャロメもねこのうち
ヌコ	名詞,一般,*,*,*,*,*
も	助詞,係助詞,*,*,*,*,も,モ,モ
ニャロメ	名詞,一般,*,*,*,*,*
も	助詞,係助詞,*,*,*,*,も,モ,モ
ねこ	名詞,一般,*,*,*,*,ねこ,ネコ,ネコ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
うち	名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
^C
$
```

> <sub>🐒 本記事上部で `docker run --rm` と **`--rm`（`remove`）オプションを付けているのは実行後にコンテナを削除してい**るためです。１回実行すれば終わり、つまり「常駐させておく必要のない使い捨ての実行」の場合は `--rm` オプションを付けて、起動と同時に終了後のコンテナ削除も指定しておくと便利です。<br>逆に、`--rm` オプションを付けた状態でコンテナが落ちると一緒に削除されてしまうので、再利用したくて `docker container ls -a` と打っても落ちたコンテナは表示されません。</sub>

`docker` コマンドの場合、ちょっと利用したい感覚でコンテナを再利用しようと思うと上記のようにいささか手間が必要になります。もちろんコンテナ名を指定しても良いのですが、以下の `docker-compose` と比較すると若干煩雑に感じます。

`docker-compose` コマンドでコンテナを起動させると `docker-compose up [サービス名]` でサービスを起動できます。（そもそも最初から内部エラーで起動しないコンテナは除く）

- `docker-compose up` → コンテナの作成と起動
- `docker-compose stop` → コンテナの一時停止（PC でいうスリープみたいなもの）
- `docker-compose start` →　コンテナの再開（PC でいうスリープ復帰みたいなもの）
- `docker-compose down` → コンテナの終了と削除（コンテナ内で作成されたデータは消えます）

この仕組みは**複数コンテナを手軽に管理したい場合に便利**です。

さて、肝心の `docker-compose` コマンドで使うサービス名やそのコンテナの定義ですが、`docker-compose.yml` の YAML ファイルに記述します。その設定ファイルと同じ階層で `docker-compose` コマンドを実行すると利用できます。

次項では、`docker-compose` の設定ファイルの作成とサンプルを使って `kagome` の使い方を説明します。

#### docker-compose で kagome Web API をワカチコしてみる

[ワカチコ](https://ja.wikipedia.org/wiki/%E3%82%86%E3%81%A3%E3%81%A6%E3%81%83)を知った時にはすでにブームが去っていたのですが、すみません。使いたくなったのです。

ええ、[わかち書き](https://ja.wikipedia.org/wiki/%E3%82%8F%E3%81%8B%E3%81%A1%E6%9B%B8%E3%81%8D)を。

そこで、下記 `docker-compose` の YAML ファイルを使って、`myapp1` コンテナ内から `kagome` コンテナに cURL で `kagome Web API` を「手動」で叩いてみたいと思います。

```yaml:docker-compose.ymlのサンプル
version: '3'

services:
  myapp1:
    container_name: myapp1
    image: alpine:latest
  kagome:
    container_name: kagome
    image: ikawaha/kagome:latest
    command: server -http=":80"

```

上記の YAML ファイルの意味は以下の通りです。

- `version`
  - 「[`docker-compose` の `v3` の書式](https://docs.docker.com/compose/compose-file/)を使う」と宣言しています。
- `services`
  -  Docker ネットワーク内で起動するコンテナを指定しています。ここでは `myapp1` と `kagome` の２つ。
      - `myapp1`
          - コンテナ名 `myapp1`とコンテナの元となるイメージに最新の `alpine linux` を指定しています。
      - `kagome`
          -  コンテナ名 `kagome`とコンテナの元となるイメージに最新の `ikawaha/kagome` を指定しています。<br>このコンテナは `entrypoint`、つまりデフォルトで実行するコマンドに `kagome` が指定されています。`command` で `kagome` コマンドの引数が渡せます。`command` がない場合は `$ kagome` と実行した時と同じ挙動をします。<br>引数の `server -http=":80"` を渡すことで、80 番ポートで待機する Web サーバーとして `kagome` を実行させています。<br>ここでのポイントは、このポートにアクセスできるのは**同じ Docker ネットワーク内のコンテナのみである**ことです。つまり、Docker を動かしているホストからもアクセスできない隔離されたネットワークです。<br>同じ Docker ネットワーク内の他のコンテナから、このコンテナに http アクセスするにはコンテナ名を指定します。つまり `http://kagome:80/` でアクセスできます。Docker をルーターとした仮想 LAN 環境を構築していると考えるといいでしょう。<br><br><sup>ちなみに、Docker を動かしているホスト側からコンテナにアクセスしたい場合は [`ports` を指定する](https://docs.docker.com/compose/compose-file/#ports)とアクセスできるようになります。つまり、**`ports` の指定があった場合は Docker がポート・フォワードしてくれる**ということです。</sup>

ここでの注意点は、`myapp1` は Alpine Linux の軽量イメージで、何も入っていません。`command` で指定もしていません。つまり、単純に起動しても動かすものがないので、そのまま処理が終了してしまい `myapp1` コンテナに接続できなくなります。

これは、まず先に `kagome` を起動させておいて、後から Alpine（`myapp1`）のコンテナ内部からシェルで操作をするための意図的な設定です。具体的には、`myapp1` コンテナを起動すると同時に sh シェルでログインしてコンテナ内部から操作します。

各々の操作の前にコメントを付けたので、それらを読みながらサンプル操作のログをご覧ください。

```shell-session
$ ls
docker-compose.yml
$
$ # サービス名 kagome のコンテナ「のみ」をバックグラウンド（-d）で起動
$ docker-compose up -d kagome
Starting kagome ... done
$
$ # サービス名 myapp1 のコンテナ起動と同時にコンテナ内部にshシェルでログイ
$ # ンする。
$ # これは普通に up で起動しても myapp1 に何も入っていないため、起動しても
$ # すぐに終了してしまい、後からログインできくなるためです。
$ docker-compose run myapp1 /bin/sh
/ # # 以下 myapp1 のコンテナ内操作
/ #
/ # # cURL, jq をインストール
/ # apk add curl jq
...
OK: 8 MiB in 21 packages
/ #
/ # # 同じネットワーク内のコンテナ名 kagome に 80 番ポートで cURL して、
/ # # 結果を jq コマンドにパイプ渡しする。
/ # curl -s -XPUT kagome:80/a -d'{"sentence":"お寿司食べたい", "mode":"normal"}' | jq .
{
  "status": true,
  "tokens": [
    {
      "id": 10618,
      "start": 0,
      "end": 1,
      "surface": "お",
      "class": "KNOWN",
      "features": [
        "接頭詞",
        "名詞接続",
        "*",
        "*",
        "*",
        "*",
        "お",
        "オ",
        "オ"
      ]
    },
    {
      "id": 190872,
      "start": 1,
      "end": 3,
      "surface": "寿司",
      "class": "KNOWN",
      "features": [
        "名詞",
        "一般",
        "*",
        "*",
        "*",
        "*",
        "寿司",
        "スシ",
        "スシ"
      ]
    },
    {
      "id": 381475,
      "start": 3,
      "end": 5,
      "surface": "食べ",
      "class": "KNOWN",
      "features": [
        "動詞",
        "自立",
        "*",
        "*",
        "一段",
        "連用形",
        "食べる",
        "タベ",
        "タベ"
      ]
    },
    {
      "id": 39236,
      "start": 5,
      "end": 7,
      "surface": "たい",
      "class": "KNOWN",
      "features": [
        "助動詞",
        "*",
        "*",
        "*",
        "特殊・タイ",
        "基本形",
        "たい",
        "タイ",
        "タイ"
      ]
    }
  ]
}
/ # # 🎉
/ #
/ # # myapp1 コンテナから抜ける
/ # exit
$
$ # 全てのコンテナを終了させる（同階層にある docker-compose.yml で定義されているコンテナ）
$ docker-compose down
Stopping kagome ... done
Removing kagome-api_myapp1_run_d44f74739b47 ... done
Removing kagome                             ... done
Removing network kagome-api_default
$
$ # 以上
```

<details><summary>動作確認環境</summary><div><br>

Docker 18.09 + docker-compose 1.23 + macOS Mojave（OSX 10.14）で動作確認しました。

```shell-session
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.4
BuildVersion:	18E226
$ docker version
Client: Docker Engine - Community
 Version:           18.09.0
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        4d60db4
 Built:             Wed Nov  7 00:47:43 2018
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       4d60db4
  Built:            Wed Nov  7 00:55:00 2018
  OS/Arch:          linux/amd64
  Experimental:     false
$ docker-compose version
docker-compose version 1.23.2, build 1110ad01
docker-py version: 3.6.0
CPython version: 3.6.6
OpenSSL version: OpenSSL 1.1.0h  27 Mar 2018
```

</div></details>

以上で HTTP でリクエストできるアプリであれば、どんなプログラム言語で書かれたコンテナからも kagome が気軽に使えます。便利。

もっと具体的に触ってみたいかた用に、`kagome` WebAPI を PHP のコンテナから叩いて（HTTP リクエストして）ブラウザに表示するサンプルを GitHub にあげました。参考にしてください。

- サンプル
  - [`kagome` の WebAPI コンテナを PHP7 のコンテナから叩いてブラウザに表示するサンプル](https://github.com/KEINOS/Kagome-On-Docker-Sample) @ GitHub


## ユーザー辞書の使い方

固有名詞など、**同梱の辞書に含まれない単語があると希望する結果にならない**場合があります。

```shell-session:１つの形態素で認識して欲しいが分割されてしまっている
$ docker run --rm ikawaha/kagome
朝青龍
朝	名詞,副詞可能,*,*,*,*,朝,アサ,アサ
青龍	名詞,固有名詞,地域,一般,*,*,青龍,セイリュウ,セイリュー
EOS
壇蜜
壇	名詞,固有名詞,人名,姓,*,*,壇,ダン,ダン
蜜	名詞,一般,*,*,*,*,蜜,ミツ,ミツ
EOS
全日本もう帰りたい協会
全日本	名詞,一般,*,*,*,*,全日本,ゼンニホン,ゼンニホン
もう	副詞,一般,*,*,*,*,もう,モウ,モー
帰り	動詞,自立,*,*,五段・ラ行,連用形,帰る,カエリ,カエリ
たい	助動詞,*,*,*,特殊・タイ,基本形,たい,タイ,タイ
協会	名詞,一般,*,*,*,*,協会,キョウカイ,キョーカイ
EOS
```

その場合は、**ユーザー辞書を作成し、辞書ファイルをオプションで指定**します。（指定の仕方は後述）

```shell_session:ローカルのmy_user.dicをコンテナのルートにマウントして利用する例
$ docker --rm -it -v $(pwd)/my_user.dic:/my_user.dic ikawaha/kagome -udic /my_user.dic
朝青龍
朝青龍	カスタム人名,朝青龍,アサショウリュウ
EOS
壇蜜
壇蜜	カスタム人名,壇/蜜,ダン/ミツ
EOS
全日本もう帰りたい協会
全日本もう帰りたい協会	カスタム名詞,全日本/もう/帰り/たい/協会,ゼンニホン/モウ/カエリ/タイ/キョウカイ
EOS
```

### ユーザー辞書のサンプル

上記で指定しているユーザー辞書は以下のようになります。

```csv:my_user.dic
朝青龍,朝青龍,アサショウリュウ,カスタム人名
壇蜜,壇 蜜,ダン ミツ,カスタム人名
全日本もう帰りたい協会,全日本 もう 帰り たい 協会,ゼンニホン モウ カエリ タイ キョウカイ,カスタム名詞
```

### ユーザー辞書の基本構文

辞書の構文（フォーマット）は、以下のように４パートに分かれています。UTF-8 の CSV 形式です。

```csv:ユーザー辞書の基本構文
[単語],[分割形式],[ヨミ],[品詞]
```

２列目の「分割形式」とは、単語が元々は分かれていた場合にスペース区切りで分割して記載します。例えば、「関西国際空港」の場合は「関西 国際 空港」になります。この場合、ヨミも対応するように「カンサイ コクサイ クウコウ」とスペース区切りにします。

### ユーザー辞書の詳細

- [形態素解析器 kagome のユーザー辞書の使い方](https://qiita.com/ikawaha/items/9ebe3e1104fb80706d99) @ Qiita

### ユーザー辞書のマウントの仕方

Kagome on Docker でローカルにあるユーザー辞書を使う場合は、**一旦ユーザー辞書をコンテナにマウントして、マウントした先のパスでユーザー辞書を指定**します。

```shell-session（同階層のmy_user.dicをコンテナのrootにマウントして、kagomeのオプションでそれを指定する）
$ ls
docker-compose.yml    my_user.dic
$
$ docker --rm -it -v $(pwd)/my_user.dic:/my_user.dic ikawaha/kagome -udic /my_user.dic
```

- `docker -v` オプション： `docker -v [ローカルのファイルの絶対パス]:[コンテナにマウントする先のパス]`
- `kagome -udic` オプション： `ikawaha/kagome -udic [コンテナ内のユーザー辞書のパス]`