Go と Elasticsearch の連携手法について
Tags: go, elasticsearch, docker
Published: 2019-06-19 19:31:39

# 注意
この記事は，2018年3月6日に書いたものです．

# Introduction
本記事では，全文検索エンジンである Elasticsearch を Go言語 から扱うための手法についてまとめます．Elasticsearch を Go とを連携するための手法は，検索してもあまり多くの情報が出てきません．その上，若干つまづきやすいポイントもありますので，この記事が，Elasticsearch と Go を連携しようと苦慮している人の助けになれば幸いです．

# Docker による準備
主題に入る前に，そもそも，Elasticsearch と Go を連携するための手法が必要になってきます．本記事では，Docker と docker-compose を併用した連携手法を提示します．

Go のために，下記のような Dockerfile を用意しましょう．

```Docker
FROM golang:1.9
ENV APPNAME TestApp
RUN mkdir -p /go/src/$APPNAME
WORKDIR /go/src/$APPNAME
ADD . /go/src/$APPNAME
RUN go get -v
```

これを，Elasticsearch と連携したい Go サーバのトップディレクトリに設置します．次に，下記のような `docker-compose.yml` を作成します．

```yaml
version: '3'
services:
  app:
    build: .
    command: ["./wait-for-it.sh", "elastic:9200", "-t", "30", "--", "go", "run", "main.go"]
    volumes:
      - .:/go/src/TestApp
    ports:
      - "8080:8080"
    depends_on:
      - elasticsearch
  elasticsearch:
    image: elasticsearch
    command: elasticsearch
    ports:
      - "9200:9200"
```

何てことのない普通の `docker-compose.yml` ですが，注意する点がいくつかあります．Elasticsearch サーバが起動しきっていない状態で Go サーバが起動してはいけないので， wait-for-it.sh というシェルスクリプトを用いて，Go サーバが起動するまで 30 秒の遅延を置くことにしています． なお，参考にした Medium の記事を文献 [1] に示します．

ちなみに， `wait-for-it.sh` は，下記のリンクから取得することができます．

https://github.com/vishnubob/wait-for-it/blob/master/wait-for-it.sh

これにて，Elasticsearch と Go を連携するための準備は終わりです．次節に，Elasticsearch と Go を連携するためのパッケージの紹介とその手法について述べます．


# Elasticsearch と Go

Elasticsearch を Go から呼出す方法はいくつかあります．例えば，Elastic 社公式のクライアントとして [go-elasticsearch](https://github.com/elastic/go-elasticsearch) がありますが，こちらは WIP です．[elastic](https://github.com/olivere/elastic) という著名なパッケージがありますので，通常はこちらを使用しましょう．以下に godoc を示します．

https://godoc.org/github.com/olivere/elastic

さてやるぞ，と思って，前節で構築した環境で， GoDoc の通り動かそうとすると失敗します．おそらく， `No Elasticsearch Node Available` と表示されるはずです．

このようなエラーが発生する理由ですが，Docker コンテナから返ってくる Elasticsearch サーバの IP アドレスは Private なものであるため，外部からアクセスできないということだそうです [2][3]．Sniffing をオフにすれば外部 IP からアクセスできますので，そのようにしましょう．下記の通りにすれば，正しく接続できるはずです．

```go
client, err := elastic.NewClient(elastic.SetURL("http://elasticsearch:9200"))
if err != nil {
  panic(err)
}
```

あとは，下記の通りにすれば Elasticsearch サーバに向けて REST リクエストなどを送ることができますし，Gin などと連携して HTTP Client を作ることもできます．

```go
package main
import (
   "github.com/olivere/elastic"
   "context"
)
func main() {
   client, err := elastic.NewClient(
      elastic.SetURL("http://elasticsearch:9200"),
      elastic.SetSniff(false),
   )
   if err != nil {
      panic(err)
   }
   defer client.Stop()
   resp, err := client.Get().
      Index("index").
      Type("Type").
      Id("Id").
      Do(context.Background())
   if err != nil {
      panic(err)
   }
}
```

# Conclusion

以上，Elasticsearch と Go を連携する上でつまづきやすいポイントのまとめでした．


# References

[1] [https://medium.com/@leo_hetsch/local-development-with-go-postgresql-and-elasticsearch-in-docker-61bc8a0d5e66](https://medium.com/@leo_hetsch/local-development-with-go-postgresql-and-elasticsearch-in-docker-61bc8a0d5e66)

[2] [https://github.com/olivere/elastic/issues/312](https://github.com/olivere/elastic/issues/312)

[3] [https://qiita.com/tanan/items/9593927c5b5ae0e811ab](https://qiita.com/tanan/items/9593927c5b5ae0e811ab)