AlpineでGoのバイナリ動かなくて焦った話(anaconda)

先に結論を言うとGoバイナリをAlpineで動かしたい時はルート証明書が必要になることがあって、ca-certificatesをインストールすりゃ解決するってこと。

「Goはさいきょーだから、バイナリファイル1つありゃうごくやろー!」って思ってたぼくはチンパンジーでした。

過程

Go言語ではanacondaっていうライブラリを使うとTwitterAPIを簡単に叩くことができます。最近Streaming APIにも対応したそう。

github.com

これを使って以下のようなコードを書きます。このプログラムは簡単に説明すると環境変数にセットされたTwitterAPIキーを使用してStreaming APIを叩いて取得したツイートを表示し続けるだけのものです。

ツイートが取得できない(APIキーが間違ってるとか)と「ERROR」と出力してプログラムが終了します。

package main

import(
  "log"
  "github.com/ChimeraCoder/anaconda"
  "os"
)

func main(){
  anaconda.SetConsumerKey(os.Getenv("CONSUMER_KEY"))
  anaconda.SetConsumerSecret(os.Getenv("CONSUMER_SECRET"))
  api := anaconda.NewTwitterApi(os.Getenv("ACCESS_TOKEN"), os.Getenv("ACCESS_SECRET"))
  stream := api.PublicStreamSample(nil)

  for {
    x := <-stream.C
    switch tweet := x.(type) {
    case anaconda.Tweet:
      log.Println("tweet.Text: ",  tweet.Text)
    case anaconda.StatusDeletionNotice:
      //pass
    default:
      log.Fatal("ERROR")
    }
  }
}

これをビルドしてバイナリファイルを作り、Dockerコンテナで動かそうと思いました。コンテナは公式のalpineのDockerイメージを使用します。Linuxの64bit環境で動かすのでGOOS=linux GOARCH=amd64 go buildというようにビルドします。

あとはこんな感じのDockerfileを書いてdocker build

FROM alpine
COPY test /
CMD ["/test"]
mbp2016:test shibujibu$ docker build -t hoge .
Sending build context to Docker daemon  5.962MB
Step 1/3 : FROM alpine
 ---> 76da55c8019d
Step 2/3 : COPY test /
 ---> 2193b80c19bb
Removing intermediate container 8098e29f2a77
Step 3/3 : CMD /test
 ---> Running in 20cb9a6ef9b5
 ---> ba8189720e34
Removing intermediate container 20cb9a6ef9b5
Successfully built ba8189720e34
Successfully tagged hoge:latest
mbp2016:test shibujibu$

コンテナを動かします。test.envにはTwitterAPIのキーの環境変数を記述してあります。

mbp2016:test shibujibu$ docker run --env-file test.env --rm hoge
2017/09/18 12:42:45 ERROR
mbp2016:test shibujibu$

は?エラー???

問題

test.envに記述したAPIキーは全部合っています。意味がわからん。

とりあえずGoバイナリを動かすコンテナに使用するイメージにちょっと変更を加えてみました。具体的にはDockerfileを書き換えてgolang:alpineからイメージを作ってそれを使うこととしました。

FROM golang:alpine
COPY test /
CMD ["/test"]

再びイメージをビルドしてからコンテナを動かします。

mbp2016:test shibujibu$ docker build -t hoge .

(省略)

mbp2016:test shibujibu$ docker run --env-file test.env --rm hoge
2017/09/18 12:48:50 tweet.Text:  RT @AndhraPradeshCM: అమెరికాలోని న్యూయార్క్<200c>లో జరుగనున్న ఐక్యరాజ్య సమితి ప్రత్యేక సమావేశానికి భారతదే
శం తరపున ఆంధ్రప్రదేశ్<200c>ను పంపించాలని కేం…
2017/09/18 12:48:50 tweet.Text:  RT @jyotigaurmba: #Prophecies_Of_Nostradamus #HappyBirthdayPM
सभी भविष्यवाणी के अनुसार तो दुनिया का मुक्तिदाता भारत में…
2017/09/18 12:48:50 tweet.Text:  松岡さん…ペテ…テルくん…

動いた!!!!

原因

alpine:latest(ここで使ったバージョンは3.6)とgolang:alpine(バージョンは1.9.0-alpine3.6)の違いを見てみます。なんとなくインストールされているパッケージが気になりました。apk infoでインストールされているパッケージのリストを出力することができます。

mbp2016:test shibujibu$ docker run --rm alpine apk info
WARNING: Ignoring APKINDEX.84815163.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.24d64ab1.tar.gz: No such file or directory
musl
busybox
alpine-baselayout
alpine-keys
libressl2.5-libcrypto
libressl2.5-libssl
zlib
apk-tools
scanelf
musl-utils
libc-utils
mbp2016:test shibujibu$ docker run --rm golang:alpine apk info
WARNING: Ignoring APKINDEX.84815163.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.24d64ab1.tar.gz: No such file or directory
busybox
alpine-baselayout
alpine-keys
libressl2.5-libcrypto
libressl2.5-libssl
zlib
apk-tools
scanelf
libc-utils
ca-certificates
musl
musl-utils
mbp2016:test shibujibu$

golang:alpineに入っていてalpineに入っていないのはca-certificatesだけですね。

TwitterAPI使う」→「OAuth使う」→「HTTPS通信でルート証明書が必要?」というわけで、ca-certificatesをインストールすりゃいけるんじゃないかと思いました。Dockerfileを書き換えます。

FROM alpine
RUN apk --update add ca-certificates
COPY test /
CMD ["/test"]
mbp2016:test shibujibu$ docker build -t hoge .
Sending build context to Docker daemon  5.962MB
Step 1/4 : FROM alpine
 ---> 76da55c8019d
(省略)
mbp2016:test shibujibu$ docker run --env-file test.env --rm hoge
2017/09/18 15:29:44 tweet.Text:  RT @NegritoNd: C'est simple. Il suffit d'un voyage en Asie dans la ville de Guangzhou tu trouve tes
fournisseurs dans le cosmétiques. Tissa…

動きました!