- 投稿日:2020-01-04T22:23:55+09:00
Docker コンテナ内で立ち上げた godoc にホスト側からアクセスする
この記事について
Go言語の勉強用の環境として立ち上げた Docker コンテナ内で godoc サーバを立ち上げ、ホストOS側のブラウザからアクセスしてみたところ、アクセスできなかったので調べてみたメモ。
用意した Dockerfile
DockerfileFROM golang:latest RUN mkdir /go/src/app WORKDIR /go/src/app RUN go get golang.org/x/tools/cmd/godoc EXPOSE 6060/tcp実行したコマンド1(godocがインストールされたコンテナのビルドと、そのコンテナでのgodocサーバの起動)
$ docker build -t hoge . $ docker run --rm -p 6060:6060 hoge godoc実行したコマンド2(ホストからgodocサーバにアクセス)
$ curl http://localhost:6060 curl: (52) Empty reply from server上記のようにサーバから正常な応答が返ってこない。
上記のURLにアクセスした場合の期待する応答は、以下のようなリダイレクトのレスポンス。
$ curl -v http://localhost:6060 * Rebuilt URL to: http://localhost:6060/ * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 6060 (#0) > GET / HTTP/1.1 > Host: localhost:6060 > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 302 Found < Content-Type: text/html; charset=utf-8 < Location: /pkg/ < Date: Sat, 04 Jan 2020 12:58:12 GMT < Content-Length: 28 < <a href="/pkg/">Found</a>. * Connection #0 to host localhost left intact対処
godoc サーバを起動するコマンドを以下のように変更
$ docker run --rm -p 127.0.0.1:6060:6060 hoge godoc -http 0.0.0.0:6060
- godoc の起動オプション
-http 0.0.0.0:6060
を付与し、コンテナのすべてのネットワークインターフェースからの接続を受け付けるようにする。- docker run でコンテナ起動時に
-p 172.0.0.1:6060:6060
のようにホスト側のネットワークインターフェースをローカル・ループバック・アドレスに限定する。
- 別に
-p 6060:6060
でもホストからアクセス可能だが、ホストと同じネットワークに存在する別の端末からもアクセス可能になってしまう。(参考)- それでも構わない、あるいは、むしろ別端末アクセスさせたいというケースでは
172.0.0.1:
の部分は省略しても構わない。
上記のようにサーバを起動したあと、ホスト側のブラウザから
http://localhost:6060
にアクセスすると、無事 GoDoc のページが表示できた。参考
以下の記事がより詳しい
- 投稿日:2020-01-04T22:23:55+09:00
Docker コンテナ内で立ち上げた godoc にホスト側からアクセスできなかった
この記事について
Go言語の勉強用の環境として立ち上げた Docker コンテナ内で godoc サーバを立ち上げたが、ホストOS側のブラウザからアクセスできなかったので調べてみたメモ。
環境
- Windows10 Pro Insider Preview (バージョン: 2004, OSビルド: 19536.1000)
- WSL2 / Ubuntu-18.04 ( .wslconfig で localhost フォワーディング設定済み )
- Docker
- 2.1.7.0(41536)
- Engine 19.03.5
用意した Dockerfile
DockerfileFROM golang:latest RUN mkdir /go/src/app WORKDIR /go/src/app RUN go get golang.org/x/tools/cmd/godoc EXPOSE 6060/tcp実行したコマンド1(godocがインストールされたコンテナのビルドと、そのコンテナでのgodocサーバの起動)
$ docker build -t hoge . $ docker run --rm -p 6060:6060 hoge godoc実行したコマンド2(ホストからgodocサーバにアクセス)
$ curl http://localhost:6060 curl: (52) Empty reply from server上記のようにサーバから正常な応答が返ってこない。
上記のURLにアクセスした場合の期待する応答は、以下のようなリダイレクトのレスポンス。
$ curl -v http://localhost:6060 * Rebuilt URL to: http://localhost:6060/ * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 6060 (#0) > GET / HTTP/1.1 > Host: localhost:6060 > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 302 Found < Content-Type: text/html; charset=utf-8 < Location: /pkg/ < Date: Sat, 04 Jan 2020 12:58:12 GMT < Content-Length: 28 < <a href="/pkg/">Found</a>. * Connection #0 to host localhost left intact対処
godoc サーバを起動するコマンドを以下のように変更
$ docker run --rm -p 127.0.0.1:6060:6060 hoge godoc -http 0.0.0.0:6060
- godoc の起動オプション
-http 0.0.0.0:6060
を付与し、コンテナのすべてのネットワークインターフェースからの接続を受け付けるようにする。- docker run でコンテナ起動時に
-p 172.0.0.1:6060:6060
のようにホスト側のネットワークインターフェースをローカル・ループバック・アドレスに限定する。
- 別に
-p 6060:6060
でもホストからアクセス可能だが、ホストと同じネットワークに存在する別の端末からもアクセス可能になってしまう。(参考)- それでも構わない、あるいは、むしろ別端末アクセスさせたいというケースでは
172.0.0.1:
の部分は省略しても構わない。
上記のようにサーバを起動したあと、ホスト側のブラウザから
http://localhost:6060
にアクセスすると、無事 GoDoc のページが表示できた。参考
以下の記事がより詳しい
- 投稿日:2020-01-04T16:30:25+09:00
Go1.13とDockerで開発環境構築(ホットリロード有)
はじめに(背景)
筆者は今までGoでは簡単なツール(Slack用Botとか)しか作ってこなかったが、
Goを使ってWEBアプリを開発してみることにした。ところが、開発環境を構築するのに、検索でヒットする情報が微妙だった
- Goのバージョンが古くてGo Modulesを使っていなかったり、
- 使っているライブラリの最終更新が3年前だったり情報を整理しながら開発環境を構築する必要が有ったので、
せっかくだからアウトプットしておこうと思った。前提
- Goのversionは、執筆時点で最新の1.13系を使う
- docker-composeで立ち上げたい
- せっかくなので、ホットリロードが欲しい
以下には触れません
- GoでのWEBサーバーの立ち上げ方
- Go Modulesについて
- Docker、docker-composeの使い方
本題
Goの設定
Go Modulesを使う
Go Modulesの導入や使い方などは割愛
↓のコマンドでinitialize
go mod init github.com/username/project
ホットリロードの設定
色々調べたら、Realizeが良さそう(適当)
https://github.com/oxequa/realize設定ファイル(後述)を用意して、
realize start
コマンドを実行するだけDocker環境用意
Dockerfile
FROM golang:1.13-alpine WORKDIR /app COPY . . RUN apk add --no-cache git \ && go get gopkg.in/urfave/cli.v2@master \ && go get github.com/oxequa/realize
.realize.yml (ホットリロードの設定)settings: legacy: force: false interval: 0s schema: - name: hoge path: path/to/server commands: install: status: true run: status: true watcher: extensions: - go paths: - path/from/server ignore_paths: - .git
docker-compose.ymlversion: "3" services: app: build: context: . dockerfile: Dockerfile ports: - 8080:8080 volumes: - ./:/app command: realize start
※realizeの設定でエラーが出た。(↑のファイルには反映済み)
パッケージを取得する時
解決方法は以下のissueから
https://github.com/oxequa/realize/issues/253
↓の箇所RUN apk add --no-cache git \ && go get gopkg.in/urfave/cli.v2@master \ && go get github.com/oxequa/realize
Go Modules環境起因でもエラー
以下のissueから
https://github.com/oxequa/realize/issues/217
↓の箇所commands: install: status: trueおわりに
以上で、DockerでGoを開発する環境ができました。
あとはdocker-composeにDBなり何なりを追加すれば進められそうです。
- 投稿日:2020-01-04T16:23:46+09:00
database/sql で INSERT したレコードのIDを取得する
概要
Go の
database/sql
を利用して、 INSERT したレコードのIDを取得する参考
問題
- 使用しようとした
lib/pq
ではLastInsertId() (int64, error)
が使えないdatabase/sql
はint64
で ID を返す Interface しか存在しない使用しようとした
lib/pq
ではLastInsertId() (int64, error)
が使えない
database/sql
では import する Driver を差し替えることで、異なる SQL Database を利用する場合でも、共通の Interface を利用して SQL を扱える。今回利用しようとしたのは、 PostgreSQL 用の Driver の一つである
lib/pq
しかしながら、
Result
Interface のコメントを読むと下記が書かれており、全てのデータベースでのサポートではないことがわかる。また実際にlib/pq
でこのメソッドを呼び出そうとしてもエラーが返り、lib/pq
では INSERT したレコードの ID を取得できなかった。// LastInsertId returns the integer generated by the database
// in response to a command. Typically this will be from an
// "auto increment" column when inserting a new row. Not all
// databases support this feature, and the syntax of such
// statements varies.
database/sql
はint64
で ID を返す Interface しか存在しないまた
LastInsertId()
は auto increment されたint64
型の ID を取得する Interface であり、 ID をUUID
型に指定した場合などは、利用できないことがわかる。解決策
Instead, we get around this by making a few changes. First, we are going to update our SQL statement by adding RETURNING id to the end of the statement. This will tell our SQL database that we want it to return the id of the newly inserted record so that we can do something with it.
その為、まず SQL 側の解決策として、 Result Interface では LastInsertID を利用するのではなく、 SQLを変更して、 Insert した後の レコードの ID を返却するように変更する。
2つ目に Go のコード側も変更する。
database/sql
では UPDATE・INSERT は基本的に ~.Exec を利用して、Result, error
を取得し、 SELECT の場合は、 ~.Query を利用する。下記は元のコード。
stmt, err := prepare(repo, "INSERT INTO users(username, password, email) VALUES($1, $2, $3);") defer stmt.Close() if err != nil { return nil, fmt.Errorf("query prepare faild: %w", err) } _, err = stmt.Exec(user.Personal.Name, user.Password, user.Personal.Email) if err != nil { return nil, fmt.Errorf("exec faild: %w", err) }これを下記に変更する。
QueryRow を使用することで、 Row が返り値として戻ってくるようになり、~ RETURNING user_id;
の部分を実行結果として、受け取れるようになる。あとは Row.Scan() して、レコードの値を読み取ることで、 INSERT したレコードの ID を受け取ることができる。stmt, err := prepare(repo, "INSERT INTO users(username, password, email) VALUES($1, $2, $3) RETURNING user_id;") defer stmt.Close() if err != nil { return nil, fmt.Errorf("query prepare faild: %w", err) } err = stmt.QueryRow(user.Personal.Name, user.Password, user.Personal.Email).Scan(&user.Id) if err != nil { return nil, fmt.Errorf("query faild: %w", err) }これで、
LastInsertId()
介さないので、 Driver が対応していなくてもよく、また ID の形式も int でなくてもよくなった。以上、慣れないのでどうするのかわからなかった内容を備忘録として。
誰かの参考になれば幸いです。
- 投稿日:2020-01-04T12:19:47+09:00
Echo+GORMでRESTfulなAPIサーバをつくる
Overview
小規模なプロダクトにおいてPythonでAPIサーバはしんどい(できるけどソースが美しくない)ので, 全然使ったことないけどGoでAPIサーバを立ててみたい
Description
普段お仕事ではDjango REST Framework, 趣味ではFlaskを使っているんですが, PythonでORマッパー使おうとするとSQLAlchemyとか使うハメになってしんどいです.
かといってDjangoを使うのはウェイトが重い...
peeweeとかいういい感じのライブラリもあるっぽいですがということでせっかくなのでGoで作ってみます
今回は, 要素を一意に特定できる
id
と文字列text
を要素に持つPostを定義して, API経由でCRUD操作を行うようなアプリを作りますGET: localhost:1323/posts -> 全postの取得 GET: localhost:1323/posts/:id -> id指定でpost取得 PUT: localhost:1323/posts/:id -> id指定でpost更新 POST: localhost:1323/posts/:id -> id指定でpost作成 DELETE: localhost:1323/posts/:id -> id指定でpost削除Settings
DockerでGoの開発環境を整えます
とりあえず動かしたいという方は後述のソースをまるっとコピりましょう
環境構築について, こちらの記事がわかりやすいです.
Golang(Echo) x docker-composeでホットリロード用いた開発
ディレクトリ構造
. ├── Dockerfile ├── docker-compose.yml └── src ├── main.go ├── go.mod ├── go.sum ├── controller │ └── sqlite.go └── view └── post.goプログラム本体
ディレクトリ構造に合わせてコピる
main.go
GET, POST, PUT, DELETEなど各メソッドを使える
package main import ( "github.com/labstack/echo" view "app/view" ) func main() { e := echo.New() initRouting(e) e.Logger.Fatal(e.Start(":1323")) } func initRouting(e *echo.Echo) { e.GET("/posts", view.GetAllPosts) e.GET("/posts/:id", view.GetPost) e.POST("/posts", view.CreatePost) e.PUT("/posts/:id", view.UpdatePost) e.DELETE("/posts/:id", view.DeletePost) }post.go
json:id
でレスポンスのjsonのキー名を指定できる. デフォだとPascalCaseになっちゃうのがウザいので設定
gorm:hoge
で各属性を指定できるこれDB接続のところDecoratorみたいな感じで綺麗に共通化できないんでしょうか...
詳しい方教えて下さいpackage view import ( "net/http" "github.com/labstack/echo" . "app/controller" ) type Post struct { Id int `json:"id" gorm:"primary_key;AUTO_INCREMENT"` Text string `json:"text"` } func GetAllPosts(c echo.Context) error { db := OpenSQLiteConnection() defer db.Close() db.AutoMigrate(&Post{}) var posts []Post db.Find(&posts) return c.JSON(http.StatusOK, posts) } func GetPost(c echo.Context) error { db := OpenSQLiteConnection() defer db.Close() db.AutoMigrate(&Post{}) if id := c.Param("id"); id != "" { var post Post db.First(&post, id) return c.JSON(http.StatusOK, post) } else { return c.JSON(http.StatusNotFound, nil) } } func CreatePost(c echo.Context) error { db := OpenSQLiteConnection() defer db.Close() db.AutoMigrate(&Post{}) post := new(Post) if err := c.Bind(post); err != nil { return err } db.Create(&post) return c.JSON(http.StatusOK, post) } func UpdatePost(c echo.Context) error { db := OpenSQLiteConnection() defer db.Close() newPost := new(Post) if err := c.Bind(newPost); err != nil { return err } if id := c.Param("id"); id != "" { var post Post db.First(&post, id).Update(newPost) return c.JSON(http.StatusOK, post) } else { return c.JSON(http.StatusNotFound, nil) } } func DeletePost(c echo.Context) error { db := OpenSQLiteConnection() defer db.Close() if id := c.Param("id"); id != "" { var post Post db.First(&post, id) db.Delete(post) return c.JSON(http.StatusOK, post) } else { return c.JSON(http.StatusNotFound, nil) } }sqlite.go
hoge.sqlite3はDBを保存したいパスを指定
:memory:
の方を使うとインメモリで使えるpackage controller import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/sqlite" ) func OpenSQLiteConnection() *gorm.DB { //db, err := gorm.Open("sqlite3", ":memory:") db, err := gorm.Open("sqlite3", "hoge.sqlite3") if err != nil { panic("failed to connect database.") } db.LogMode(true) return db }Docker関連ファイル
libc-dev, gccがないとrealize(ホットリロード用ライブラリ)がインストールできないのでインスコ
Dockerfile
FROM golang:1.13.5-alpine as build WORKDIR /go/app COPY src . RUN apk add --no-cache git gcc libc-dev \ && go build -o app \ && go get gopkg.in/urfave/cli.v2@master \ && go get github.com/oxequa/realize FROM alpine WORKDIR /app COPY --from=build /go/app/app . RUN addgroup go \ && adduser -D -G go go \ && chown -R go:go /app/app CMD ["./app"]docker-compose.yml
version: '3.5' services: app: build: context: . target: build volumes: - ./src:/go/app command: realize start --run --no-config ports: - 1323:1323Run
モジュール管理用ファイルの
go.mod
を生成$ docker run --rm -v `pwd`:/go/app -w /go/app golang:1.13.5-alpine go mod init app各ファイルを作り終わったら次のコマンドでイメージのビルド&実行
$ docker-compose build $ docker-compose up ~ app_1 | [02:18:27][APP] : Install started app_1 | [02:18:28][APP] : Install completed in 0.446 s app_1 | [02:18:28][APP] : Running.. app_1 | [02:18:28][APP] : ____ __ app_1 | [02:18:28][APP] : / __/___/ / ___ app_1 | [02:18:28][APP] : / _// __/ _ \/ _ \ app_1 | [02:18:28][APP] : /___/\__/_//_/\___/ v3.3.10-dev app_1 | [02:18:28][APP] : High performance, minimalist Go web framework app_1 | [02:18:28][APP] : https://echo.labstack.com app_1 | [02:18:28][APP] : ____________________________________O/_______ app_1 | [02:18:28][APP] : O\ app_1 | [02:18:28][APP] : ⇨ http server started on [::]:1323コンテナができたらプログラムが実行されます.
ソースコードを変更すると自動で更新されるのがいい感じです!各メソッドが使えるかも試しておきましょう
curlでもいいですが, 直感的に使えるhttpieが便利です# 全件取得(まだ0件) $ http GET localhost:1323/posts # 新規作成(idは自動インクリメント) $ http POST localhost:1323/posts text=hoge $ http GET localhost:1323/posts # id=1のtextを更新 $ http PUT localhost:1323/posts/1 text=fuga $ http GET localhost:1323/posts/1 # id=1を削除 $ http DELETE localhost:1323/posts/1 $ http GET localhost:1323/postsTips
go get
などコマンドを打ちたいときは$ docker psで実行中のコンテナIDを調べて,
$ docker exec -it {CONTAINER_ID} shでコンテナ内に入れます
References
https://github.com/labstack/echo
https://github.com/jinzhu/gorm
Golang(Echo) x docker-composeでホットリロード用いた開発