はじめに
久しぶりにDockerさわったらboot2dockerも無くなってて浦島太郎状態でした。
再学習も兼ねて色々と触ってみたメモです。
何となくGoとAlpineLinux使ったらモテそうな気がしたんで、やってみました。
OSはMac前提でのお話しです。
ゴール
- タイトルの通りの開発用インフラ構成をローカルマシンに構築
- freshを使ってGoのファイルを更新したら自動でビルドが走る
- supervisorで
go run
してHello World!
が表示出来る
読むのが面倒な方 (Docker入ってる前提)
githubに一式アップしました。
https://github.com/s-noguchi/docker_alpine_go_nginx
-
①一式プルするなりダウンロードする
-
②イメージビルド
docker build --no-cache . -t alpine-go-nginx
-
③コンテナ起動
docker run -d -v /ローカルのファイルパス/hello:/home/go/hello -p 1000:80 -i -t alpine-go-nginx /bin/bash
-
④コンテナに入って
nginx
でNginx起動して、cd /home/go/hello && fresh
-
⑤ブラウザから
http://localhost:1000
をたたけば動くはずです。
何でAlpineLinuxを使うか?
驚くほど軽量です。
**OS単体だと5MBいかないです。**😵
Dockerで動かす前提なので、CentOSとかUbuntuのようなフルフルでのLinuxOSは必要無いので、まさにコンテナ時代に求められる軽量OSです!
オフィシャルDockerイメージのOSもAlpineLinuxに移行する計画もあるようです。
http://www.publickey1.jp/blog/16/docker_alpine_linux.html
Alpine Linuxの説明とかは他にも色々あるので詳細はここでは割愛します。
Docker for Macのインストール
何はともあれDockerが無いとはじまりません。
ここからダウンロードしてインストールしてください。
https://docs.docker.com/docker-for-mac/
便利な時代になりました。
dmgでインストールして起動すればDockerが使えるようになります。
各種ファイルの説明
ファイル構成
goのHello Worldのスクリプトとnginxの設定ファイルを事前に設定しておいて、Dockerのビルド時にコピーしたり、コンテナ起動時にボリュームマウントを行います。
github見てもらったほうが分かりやすいかもです。
Dockerfile
AlpineLinuxではパッケージ管理がapk
と呼ばれるパッケージ管理システムなのですが、今回は最新版が欲しかったのですがapk
だと無いので、GoとNginxはビルドしています。
# alpine Linux 3.4(latest)を指定
FROM alpine:3.4
# apk本体のupdate
RUN apk update
# 必要なパッケージのインストール
RUN apk add --no-cache wget bash git supervisor gcc build-base openssl-dev pcre-dev
# glibcのインストール
RUN wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://raw.githubusercontent.com/sgerrand/alpine-pkg-glibc/master/sgerrand.rsa.pub --no-check-certificate -P /tmp
RUN wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.23-r3/glibc-2.23-r3.apk --no-check-certificate -P /tmp
RUN apk add --no-cache /tmp/glibc-2.23-r3.apk
# Go1.7.1のインストール
RUN wget https://storage.googleapis.com/golang/go1.7.1.linux-amd64.tar.gz --no-check-certificate -P /tmp
RUN tar -C /usr/local -xzf /tmp/go1.7.1.linux-amd64.tar.gz
# Goのパス設定
ENV GOPATH /home/go
ENV PATH $PATH:/usr/local/go/bin:$GOPATH/bin
# Goの自動ビルドツールのインストール
# https://github.com/pilu/fresh
RUN go get github.com/pilu/fresh
# インストールした/tmpのファイルを削除
RUN rm /tmp/go1.7.1.linux-amd64.tar.gz
RUN rm /tmp/glibc-2.23-r3.apk
# TimeZoneをJSTに
RUN apk --update add tzdata && \
cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
apk del tzdata
## nginx
# パッケージのダウンロードと展開
WORKDIR /tmp
RUN wget https://nginx.org/download/nginx-1.11.3.tar.gz
RUN tar zxvf /tmp/nginx-1.11.3.tar.gz
# ビルドとインストール
WORKDIR /tmp/nginx-1.11.3
RUN ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module
RUN make
RUN make install
# nginxユーザーの追加
RUN adduser -D nginx
RUN passwd nginx -d nginx
RUN addgroup nginx nginx
# nginxのパスを通す
ENV PATH $PATH:/usr/local/nginx/sbin
# default.confの設定
WORKDIR /usr/local/nginx/conf
COPY ./nginx/nginx.conf nginx.conf
# virtual.confの設定
WORKDIR /usr/local/nginx
RUN mkdir conf.d
WORKDIR /usr/local/nginx/conf.d
COPY ./nginx/virtual.conf virtual.conf
# supervisor登録
RUN touch /etc/supervisord.conf
RUN echo '[supervisord]' >> /etc/supervisord.conf
RUN echo 'nodaemon=true' >> /etc/supervisord.conf
RUN echo '[program:nginx]' >> /etc/supervisord.conf
RUN echo 'command=/usr/local/nginx/sbin/nginx -g "daemon off;"' >> /etc/supervisord.conf
RUN echo '[program:hello]' >> /etc/supervisord.conf
RUN echo 'command=go run /home/go/hello/hello.go' >> /etc/supervisord.conf
# ポートの開放
EXPOSE 80 443 22
# supervisor 起動
CMD ["/usr/bin/supervisord"]
ハマったポイントとしては、Goのインストールは成功するものの一向に動かなくて困っていたら、glibc
が必要な事に気づき、# glibcのインストール
のコメントのところの処理をいれました。
ちゃんと http://golang-jp.org/doc/install にはLinux 2.6.23以降(glibcが必要)
と書いてありました😅ドキュメントはちゃんと読まないとダメですね。
nginx
ログすら吐かない最低限のヤバめの設定です。
使う時は必要に応じて設定をしてください。
80番で待ち受けて、9000番のfastcgiにフォワードする設定がしてあるだけです。
後述のGoのスクリプトでも9000番で待ってあげるようにしてあげます。
user nginx;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include /usr/local/nginx/conf.d/*.conf;
}
server {
listen 80;
server_name localhost;
location / {
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
}
}
GoのHello Worldスクリプト
package main
import (
"fmt"
"net"
"net/http"
"net/http/fcgi"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
}
func main() {
l, err := net.Listen("tcp", ":9000")
if err != nil {
return
}
http.HandleFunc("/", handler)
fcgi.Serve(l, nil)
}
実行します
ビルド
Dockerfile
のあるディレクトリで以下コマンドを実行します。
docker build --no-cache . -t alpine-go-nginx
-t
のパラメータは任意で設定できます。今回はalpine-go-nginx
としました。
コンテナ起動
-v でマウントするボリュームの設定をします。
-p でポートの指定をします。今回の場合、1000番で受け付けたリクエストを80番に転送します。
docker run -v /ローカルのパス/hello:/home/go/hello -p 1000:80 -i -t alpine-go-nginx /bin/bash
実行すると
bash-4.3#
のように表示され、コンテナにログイン出来るているはずです。
nginx 起動
ここで、nginx
とコマンドを打つとnginxがスタートします。
Dockerfileのnginxのビルドでは起動コマンドは/usr/local/nginx/sbin/nginx
なのですが、毎度打つのも面倒なので、Dockerfileで以下のようにパスを通してあります。
# nginxのパスを通す
ENV PATH $PATH:/usr/local/nginx/sbin
fresh 起動
いよいよGoの実行です。
freshはGoのコードの変更を検知して自動でビルドしてくれるツールです。
Dockerfileでは以下の箇所でgo get
しています。
# Goの自動ビルドツールのインストール
# https://github.com/pilu/fresh
RUN go get github.com/pilu/fresh
使い方ですが、まずhello.go
のファイルがあるディレクトリに移動します。
cd /home/go/hello
ここで
fresh
と実行するとGoの自動ビルドがはじまります。
変更があると検知して自動でビルドしてくれます。フロントエンド開発のgulp
とかGrunt
みたいなやつです。
freshの設定
freshは設定ファイルを渡して起動する事も出来ます。
デフォルトの設定だけでも十分動きますがご興味ある方はやってみてください。
今回は/home/go/hello
でvi hello_runner.conf
で新たにファイルを作ります。
中身はこんな感じです。
root: .
tmp_path: ./tmp
build_name: runner-build
build_log: runner-build-errors.log
valid_ext: .go, .tpl, .tmpl, .html
ignored: assets, tmp
build_delay: 600
colors: 1
log_color_main: cyan
log_color_build: yellow
log_color_runner: green
log_color_watcher: magenta
log_color_app:
log_color_
の色を変えたり、build_delay
の長さを変えたり、生成されるtmp_path
の名前を変えてみたりと色々とカスタマイズすることが出来ます。
設定したら、
fresh -c hello_runner.conf
で起動すると、独自に定義した設定で動作します。
詳細はこちらを見てみてください。
https://github.com/pilu/fresh
freshを使わないでgoを実行する場合
go run ./hello.go
すれば動きます。
Hello Worldの表示
ブラウザからhttp://localhost:1000
と入力すると以下のように表示されると思います。
ブラウザから1000番ポートで来たアクセスが80番に転送されて、さらにgoのアプリケーションが待っている9000番にNginxのfastcgiが転送しているイメージです。
freshがちゃんと自動ビルドしているか確認
Hello World!
をHello
に変更して保存してみます。
変更も変わってます
ビルド時にわざと怒られてみる
http.HandleFunc("/", handler)
を
http.HandleFunc("/", handler1234)
と存在しないfunc
にするとちゃんと怒ってくれました。
SupervisorでGoを実行してみたい
開発環境とはちょっと趣旨が変わりますが、Supervisorで実行してみたいと思いやってみました。
こちらの記事参考にさせて頂きました。
http://qiita.com/taka4sato/items/1f59371ead748d88635a
要はコンテナで起動しているサービスを永続化させたいので使います。
既にDockerfileには設定を書いているので、ビルドしたイメージで以下のようにコンテナ実行すればOKです。
docker run -v /ローカルのパス/hello:/home/go/hello -p 1000:80 -i -t alpine-nginx-go /usr/bin/supervisord
起動するとこんな感じでsuccessの表示がされているはずです。
INFO spawned: 'nginx' with pid 7
INFO spawned: 'hello' with pid 8
INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
INFO success: hello entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
Dockerfileの# supervisor登録
の設定が動作しています。
nginx
が起動されて、go run /home/go/hello/hello.go
が実行された状態です。
これまでと同様にhttp://localhost:1000
でアクセスするとHello World!
が表示されるはずです。
バックグラウンドで実行したい時
-d
オプションでバックグラウンド実行できます。
docker run -d -v /ローカルのパス/hello:/home/go/hello -p 1000:80 -i -t alpine-nginx-go /usr/bin/supervisord
注意点
ポートがかぶってるとコンテナ起動出来ないので、既に1000:80で作ったコンテナが残っている場合は破棄するか、ポート2000:80
とかにポートを変えてあげて実行してください。
参考までに
- 動いてるコンテナの確認
docker ps
- 全てのコンテナの確認
docker ps -a
- コンテナ削除
docker rm コンテナid
- 全部のコンテナ削除 docker rm -f
docker ps -a -q
おわりに
Goについて
Goをやりはじめたばかりなので、まだまだGoについては初心者レベルなので、皆様がどんな開発環境でやっているのかが知りたいので日々情報収集しております。良かったら教えてください。
AlpineLinuxについて
とにかく不要なものを削ぎ落とした硬派なLinuxで、僕は大好きになりました。
驚くほど軽量なので、色んなものが入っていないです。自分で必要なパッケージを見極めて作り上げていく楽しさがあるなと思います。