【文法系】【go】基本20:パッケージ
本noteの概要
golang(以下、go)の基本的な文法と出力内容について確認する。
本noteの対象者
・goをインストール済みの方
※ 筆者は仮想環境上でgoを実行していますが、ローカル環境でも基本的に挙動は変わらないと思います、goがインストールされていれば問題ないかと。
▽ 仮想環境上でgoを動かしたい方は以下参考までに ▽
【手順系】【go】仮想環境上でのWebアプリケーション開発①:go環境構築 - This is My note
本noteの環境
PC環境(ホスト)
# OSのバージョン (base) $ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G2022 # Virtualboxのバージョン (base) $ VBoxManage -v 6.1.2r135662 # vagrantのバージョン (base) $ vagrant -v Vagrant 2.2.7
仮想環境(ゲスト)
# Linuxのバージョンが記載されているファイルを検索 vagrant@:~$ ls /etc/*-release /etc/lsb-release /etc/os-release # ゲストOSのバージョンを出力 vagrant@:~$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
仮想環境上のディレクトリ構成
workspace - src - test - lesson.go
現在のディレクトリ
vagrant@vagrant-ubuntu-trusty-64:~/workspace/src/myapp$ pwd /home/vagrant/workspace/src/myapp
【注】
以降、特段の記述がない限り、コマンドの実行は現在のディレクトリ(test)で行われるものとし、文字数削減のため、表記を以下に省略して記述する。
省略前:vagrant@vagrant-ubuntu-trusty-64:~/workspace/src/myapp$
↓
省略後:$
Today's Thema:パッケージ
1.
⑴
使用例
ディレクトリ構成
. ├── main.go └── mylib └── math.go
<main.go>
package main import ( "./mylib" "fmt" ) func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(mylib.Average(s)) }
<mylib/math.go>
package mylib func Average(s []int)int{ total := 0 for _, i := range s{ total += i } return int(total/len(s)) }
▼ 実行
$ go run lesson.go 3
《解説》
⑵
使用例
ディレクトリ構成
. ├── main.go └── mylib └── math.go └── human.go
<mylib/human.go>
package mylib import "fmt" func Say(){ fmt.Println("Human!") }
▼ 実行
$ go run lesson.go 3 Human!
⑶ さらに下に階層をつくる
使用例
ディレクトリ構成
. ├── main.go └── mylib └── math.go └── human.go └── under └── sub.go
<under/sub.go>
package main import ( "fmt" "./mylib" "./mylib/under" ) func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(mylib.Average(s)) mylib.Say() under.Hello() }
▼ 実行
$ go run lesson.go 3 Human! Hello!
⑷ structをmainから呼び出す
使用例
ディレクトリ構成
. ├── main.go └── mylib └── math.go └── human.go └── under └── sub.go
<mylib/human.go>
package main import ( "fmt" "./mylib" "./mylib/under" ) func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(mylib.Average(s)) mylib.Say() under.Hello() person := mylib.Person{Name: "Mike", Age: 20} fmt.Println(person) }
<main.go>
package main import ( "fmt" "./mylib" "./mylib/under" ) func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(mylib.Average(s)) mylib.Say() under.Hello() person := mylib.Person{Name: "Mike", Age: 20} fmt.Println(person) }
▼ 実行
$ go run lesson.go 3 Human! Hello! {Mike 20}
《解説》
structの変数を小文字で書くと、同一パッケージからはアクセスできるが、別のパッケージ(今回であればmain)からはアクセスできなくなってしまうため注意。
<mylib/human.go>
package mylib import "fmt" type person struct{ // 小文字に name string // 小文字に age int // 小文字に } func Say(){ fmt.Println("Human!") }
<main.go>
package main import ( "fmt" "./mylib" "./mylib/under" ) func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(mylib.Average(s)) mylib.Say() under.Hello() person := mylib.person{name: "Mike", age: 20} // 小文字に fmt.Println(person) }
▼ 出力
# command-line-arguments ./main.go:16:13: cannot refer to unexported name mylib.person ./main.go:16:13: undefined: mylib.person
⑸ testing
単体テスト
テストをしたいプログラム(goファイル)と同階層に「テストをしたいgoファイルの名前_test.go」というファイルを作成する
ディレクトリ構成
. ├── main.go └── mylib └── math.go └── math_test.go // 追加 └── human.go └── under └── sub.go
<mylib/math_test.go>
package mylib import "testing" func TestAverage(t *testing.T) { v := Average([]int{1, 2, 3, 4, 5}) if v != 3{ t.Error("Expected 3, got", v) } }
▼ 実行
$ go test ./... ? _/home/vagrant/workspace/src/myapp [no test files] ok _/home/vagrant/workspace/src/myapp/mylib 0.003s ? _/home/vagrant/workspace/src/myapp/mylib/under [no test files] $ go test -v ./... ? _/home/vagrant/workspace/src/myapp [no test files] === RUN TestAverage --- PASS: TestAverage (0.00s) PASS ok _/home/vagrant/workspace/src/myapp/mylib 0.003s ? _/home/vagrant/workspace/src/myapp/mylib/under [no test files]
《解説》
go test ./...
コマンドでカレントディレクトリ下にあるテストファイルを探して実行。テストファイルがない場合には「[no test files]」を返す。
go test -v ./...
コマンドはテスト内容の詳細表示。
goには基本的なテストしかないため、しっかりとしたテストを実行したい場合などは、Ginkgoなどがおすすめ。
https://qiita.com/SYZ/items/373b1150f3f060103730
⑹ gofmt(goフォーマット)
gofmtを使用すると、goの書き方に合わせてコードを修正してくれる。
①:gofmt goファイル名
指定したファイルの中身をgoの書き方に修正してターミナルに出力。
例
<mylib/math.go>
package mylib func Average(s []int)int { total := 0 for _, i := range s{ total += i } return int(total /len(s )) }
▼ 実行
mylib$ gofmt math.go package mylib func Average(s []int) int { total := 0 for _, i := range s { total += i } return int(total / len(s)) }
《解説》
②:gofmt -w goファイル名
指定したファイルの中身をgoの書き方に合わせて修正し、ファイルの中身自体を書き換えてくれる。
例
<mylib/math.go>
package mylib func Average(s []int)int { total := 0 for _, i := range s{ total += i } return int(total /len(s )) }
▼ 実行
mylib$ gofmt -w math.go
<mylib/math.go>
package mylib func Average(s []int) int { total := 0 for _, i := range s { total += i } return int(total / len(s)) }
《解説》
⑺ サードパーティーのパッケージのインストール
$ go get インストールするパッケージ
今回インストールするパッケージ
GitHub - markcheno/go-talib: A pure Go port of TA-Lib (http://ta-lib.org)
パッケージのインストール
$ go get github.com/markcheno/go-talib
goのパッケージがインストールされている場所の確認
$ go env | grep GOPATH GOPATH="/home/vagrant/go"
インストールしたパッケージの確認
$ cd /home/vagrant/go go$ ls pkg src go$ cd src/ go/src$ ls github.com go/src$ cd github.com go/src/github.com$ ls markcheno
パッケージのインストール
$ go get github.com/markcheno/go-quote
コードの記載
インストールしたパッケージのGithubにあるExampleをそのまま記載。
<main.go>
package main import ( "fmt" "github.com/markcheno/go-quote" "github.com/markcheno/go-talib" ) func main() { spy, _ := quote.NewQuoteFromYahoo("spy", "2016-01-01", "2016-04-01", quote.Daily, true) fmt.Print(spy.CSV()) rsi2 := talib.Rsi(spy.Close, 2) fmt.Println(rsi2) }
main.goがあるディレクトリに戻って以下を実行
▼ 実行
$ go run main.go datetime,open,high,low,close,volume 2016-01-04 00:00,200.49,201.03,198.59,185.92,222353500.00 2016-01-05 00:00,201.40,201.90,200.05,186.24,110845800.00 2016-01-06 00:00,198.34,200.06,197.60,183.89,152112600.00 2016-01-07 00:00,195.33,197.44,193.59,179.48,213436100.00 2016-01-08 00:00,195.19,195.85,191.58,177.51,209817200.00 2016-01-11 00:00,193.01,193.41,189.82,177.68,187941300.00 2016-01-12 00:00,193.82,194.55,191.14,179.11,172330500.00 2016-01-13 00:00,194.45,194.86,188.38,174.65,221168900.00 2016-01-14 00:00,189.55,193.26,187.66,177.51,240795600.00 2016-01-15 00:00,186.77,188.76,185.52,173.70,324846400.00 2016-01-19 00:00,189.96,190.11,186.20,173.94,195244400.00 2016-01-20 00:00,185.03,187.50,181.02,171.71,286547800.00 2016-01-21 00:00,186.21,188.87,184.64,172.67,195772900.00 2016-01-22 00:00,189.78,190.76,188.88,176.21,168319600.00 2016-01-25 00:00,189.92,190.15,187.41,173.55,130371700.00 2016-01-26 00:00,188.42,190.53,188.02,175.91,141036800.00 2016-01-27 00:00,189.58,191.56,187.06,174.00,185681700.00 2016-01-28 00:00,189.96,190.20,187.16,174.91,143798800.00 2016-01-29 00:00,190.02,193.88,189.88,179.17,210529300.00 2016-02-01 00:00,192.53,194.58,191.84,179.11,136061600.00 2016-02-02 00:00,191.96,191.97,189.54,175.88,182564900.00 〜中略〜 [0 0 11.805138424204099 2.737417921772375 1.6236355292365552 8.280483916125652 56.41181226441782 13.210013791344194 56.23373812144925 24.262958081960942 29.151499083088687 12.98848538074747 41.15032016304844 82.60831803149993 40.11137682547193 68.71920147811743 38.769619274868674 56.65640833450013 88.43649560302671 86.50927232770997 27.27345009263249 49.75170865128946 56.781626138262695 12.891615447548046 6.223179254179022 6.606276618939291 5.844427039641772 1.3065838466637951 71.21984520208864 86.82078611920139 93.62385848231581 74.12207060089115 70.76443355887687 92.30467860170151 40.06362037915057 57.33776517520462 83.15578831123342 67.45567533286915 29.534847273534464 83.77699194472726 87.53180346410304 91.13000160360744 94.0107627029488 94.83644854861915 19.866664331860793 53.070147719517315 58.662246515518866 92.86882220427496 81.92004797782417 63.05407838123072 85.96912267237236 94.073858999178 96.55488432539273 97.35709326625738 82.74114996190607 17.661516166180068 15.984625049680359 32.956170574310484 90.85195137929757 94.99230492260554 63.207998184389005]
《解説》
【補足】
以下のようにすることで、importしたパッケージの名前を変更することもできる。
package main import ( "fmt" "github.com/markcheno/go-quote" a"github.com/markcheno/go-talib" // パッケージ名の前に任意の名前を設定(今回はa) ) func main() { spy, _ := quote.NewQuoteFromYahoo("spy", "2016-01-01", "2016-04-01", quote.Daily, true) fmt.Print(spy.CSV()) rsi2 := a.Rsi(spy.Close, 2) // デフォルトのtalibを今回設定したパッケージ名(a)に変更 fmt.Println(rsi2) }
また、コード内で使用しないパッケージは以下のようにアンダースコアをつけることで、エラーが発生しないようにすることもできる。
package main import ( "fmt" "github.com/markcheno/go-quote" _ "github.com/markcheno/go-talib" ) func main() { spy, _ := quote.NewQuoteFromYahoo("spy", "2016-01-01", "2016-04-01", quote.Daily, true) fmt.Print(spy.CSV()) // rsi2 := talib.Rsi(spy.Close, 2) fmt.Println(rsi2) }
今後はgo getではなく、vgoを使用することが多くなるかも、、?
vgo - GoDoc
【文法系】【go】基本19:並列処理(Goroutine)
本noteの概要
golang(以下、go)の基本的な文法と出力内容について確認する。
本noteの対象者
・goをインストール済みの方
※ 筆者は仮想環境上でgoを実行していますが、ローカル環境でも基本的に挙動は変わらないと思います、goがインストールされていれば問題ないかと。
▽ 仮想環境上でgoを動かしたい方は以下参考までに ▽
【手順系】【go】仮想環境上でのWebアプリケーション開発①:go環境構築 - This is My note
本noteの環境
PC環境(ホスト)
# OSのバージョン (base) $ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G2022 # Virtualboxのバージョン (base) $ VBoxManage -v 6.1.2r135662 # vagrantのバージョン (base) $ vagrant -v Vagrant 2.2.7
仮想環境(ゲスト)
# Linuxのバージョンが記載されているファイルを検索 vagrant@:~$ ls /etc/*-release /etc/lsb-release /etc/os-release # ゲストOSのバージョンを出力 vagrant@:~$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
仮想環境上のディレクトリ構成
workspace - src - test - lesson.go
現在のディレクトリ
vagrant@vagrant-ubuntu-trusty-64:~/workspace/src/test$ pwd /home/vagrant/workspace/src/test
【注】
以降、特段の記述がない限り、コマンドの実行は現在のディレクトリ(test)で行われるものとし、文字数削減のため、表記を以下に省略して記述する。
省略前:vagrant@vagrant-ubuntu-trusty-64:~/workspace/src/test$
↓
省略後:$
Today's Thema:並列処理
1.並列処理(Goroutine)
⑴ 基本的な使い方
使用例
package main import ( "fmt" "time" ) func goroutine(s string){ for i := 0; i < 5; i++{ time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func normal(s string){ for i := 0; i < 5; i++{ time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go goroutine("world") normal("hello") }
▼ 実行
$ go run lesson.go world hello hello world world hello hello world world hello
《解説》
並列処理を使わなかった場合
package main import ( "fmt" "time" ) func goroutine(s string){ for i := 0; i < 5; i++{ time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func normal(s string){ for i := 0; i < 5; i++{ time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { goroutine("world") normal("hello") }
▼ 実行
$ go run lesson.go world world world world world hello hello hello hello hello
《解説》
【補足】
以下のように、time.Sleep()をコメントアウトすると、goroutine()のスレッドは生成されるものの、goroutine()の処理が始まる前に、normal()の処理が完了し、プログラムが終了してしまうためgoroutine()の中身は出力されない。
package main import ( "fmt" // "time" ) func goroutine(s string){ for i := 0; i < 5; i++{ // time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func normal(s string){ for i := 0; i < 5; i++{ // time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go goroutine("world") normal("hello") }
▼ 出力
hello hello hello hello hello
そのため、main関数でtime.Sleep()を使用し、プログラムが終了するまでの時間を指定すると、goroutineも出力はされる。
package main import ( "fmt" "time" ) func goroutine(s string){ for i := 0; i < 5; i++{ // time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func normal(s string){ for i := 0; i < 5; i++{ // time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go goroutine("world") normal("hello") time.Sleep(2000 * time.Millisecond) }
▼ 出力
hello hello hello hello hello world world world world world
2.sync.WaitGroup
並列処理が実行完了するまでプログラムを終了しないようにする。
⑴ 基本的な使い方
使用例
package main import ( "fmt" "sync" "time" ) func goroutine(s string, wg *sync.WaitGroup){ for i := 0; i < 5; i++{ time.Sleep(100 * time.Millisecond) fmt.Println(s) } wg.Done() } func normal(s string){ for i := 0; i < 5; i++{ time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { var wg sync.WaitGroup wg.Add(1) go goroutine("world", &wg) normal("hello") wg.Wait() }
▼ 実行
$ go run lesson.go world hello world hello hello world world hello hello world
《解説》
並列処理が完了するまで、プログラムが終了しないため、1のときとは異なり、time.Sleep()をコメントアウトしても並列処理の内容は実行される。
package main import ( "fmt" "sync" // "time" ) func goroutine(s string, wg *sync.WaitGroup){ for i := 0; i < 5; i++{ // time.Sleep(100 * time.Millisecond) fmt.Println(s) } wg.Done() } func normal(s string){ for i := 0; i < 5; i++{ // time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { var wg sync.WaitGroup wg.Add(1) go goroutine("world", &wg) normal("hello") wg.Wait() }
▼ 出力
hello hello hello hello hello world world world world world
3.channel
並列処理を行う関数同士はそれぞれ独立しており、そのままではデータのやりとりができないため、channelを使用してデータのやりとりができるようにする。
⑴ main関数とgoroutine
使用例
package main import "fmt" func goroutine(s []int, c chan int){ // ④ sum := 0 for _, v := range s{ sum += v } c <- sum // c(channel)にsumを入れる } func main() { s := []int{1, 2, 3, 4, 5} // ① c := make(chan int) // ② go goroutine(s, c) // ③ x := <-c // ⑤ c(channel)に入ったsumを受け取りxに代入 fmt.Println(x) // ⑥ }
▼ 実行
$ go run lesson.go 15
《解説》
⑵ main関数とgoroutine1、goroutine2
使用例
package main import "fmt" func goroutine1(s []int, c chan int){ sum := 0 for _, v := range s{ sum += v } c <- sum } func goroutine2(s []int, c chan int){ sum := 5 for _, v := range s{ sum += v } c <- sum } func main() { s := []int{1, 2, 3, 4, 5} c := make(chan int) go goroutine1(s, c) go goroutine2(s, c) x := <-c fmt.Println(x) y := <-c fmt.Println(y) }
▼ 実行
$ go run lesson.go 20 15
《解説》
3.Buffered Channels
⑴
使用例
package main import "fmt" func main() { ch := make(chan int, 2) // make(chan データ型, バッファの数) ch <- 100 fmt.Println(len(ch)) ch <- 200 fmt.Println(len(ch)) x:= <-ch fmt.Println(x) fmt.Println(len(ch)) ch <- 300 fmt.Println(len(ch)) }
▼ 実行
$ go run lesson.go 1 2 100 1 2
《解説》
以下のように、x:= <-chでchannelを取り出さず、バッファー(今回は2)を超えるデータを入れようとするとエラーになる。
func main() { ch := make(chan int, 2) ch <- 100 fmt.Println(len(ch)) ch <- 200 fmt.Println(len(ch)) ch <- 300 fmt.Println(len(ch)) }
▼ 出力
1 2 fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main() /home/vagrant/workspace/src/myapp/lesson.go:12 +0x187 exit status 2
⑵ rangeとclose(ch)
channelとセットでrangeを使用する場合には、close(ch)でchannelの終了を明示する。
使用場面
package main import "fmt" func goroutine(s []int, c chan int){ sum := 0 for _, v := range s{ sum += v c <- sum } close(c) // channelの終了を明示 } func main() { s := []int{1, 2, 3, 4, 5} c := make(chan int, len(s)) // len(s)でchannelのバッファを指定 go goroutine(s, c) for i := range c{ fmt.Println(i) } }
▼ 実行
$ go run lesson.go 1 3 6 10 15
使い方
package main import "fmt" func main() { ch := make(chan int, 2) ch <- 100 fmt.Println(len(ch)) ch <- 200 fmt.Println(len(ch)) close(ch) // channelの終了を明示 for c := range ch{ fmt.Println(c) } }
▼ 実行
$ go run lesson.go 1 2 100 200
《解説》
close(ch)がないと、バッファで指定した数を超えるchannelを取りにいこうとしてしまうため、以下のようにエラーになる。
func main() { ch := make(chan int, 2) ch <- 100 fmt.Println(len(ch)) ch <- 200 fmt.Println(len(ch)) for c := range ch{ fmt.Println(c) } }
▼ 出力
1 2 100 200 fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan receive]: main.main() /home/vagrant/workspace/src/myapp/lesson.go:12 +0x200 exit status 2
4.producerとconsumer
ログを集めて、解析をする場面などで使用
⑴
使用例
package main import ( "fmt" "sync" "time" ) func producer(ch chan int, i int) { ch <- i * 2 } func consumer(ch chan int, wg *sync.WaitGroup) { for i := range ch { func() { defer wg.Done() fmt.Println("process", i*1000) }() } fmt.Println("###################") } func main() { var wg sync.WaitGroup ch := make(chan int) // Producer for i := 0; i < 10; i++ { wg.Add(1) go producer(ch, i) } // Consumer go consumer(ch, &wg) wg.Wait() close(ch) time.Sleep(2 * time.Second) fmt.Println("Done") }
▼ 実行
$ go run lesson.go process 0 process 2000 process 4000 process 6000 process 8000 process 10000 process 12000 process 14000 process 16000 process 18000 ################### Done
《解説》
5.fan-out fan-in
⑴
使用例
package main import "fmt" func producer(first chan int) { defer close(first) for i := 0; i < 10; i++ { first <- i } } func multi2(first <-chan int, second chan<- int) { // 「first <-chan」は送信用のchannel、「second chan<-」は受信用のchannelであることを表す。「<-」はなくてもOK。 defer close(second) for i := range first { second <- i * 2 } } func multi4(second chan int, third chan int) { defer close(third) for i := range second { third <- i * 4 } } func main() { first := make(chan int) second := make(chan int) third := make(chan int) go producer(first) go multi2(first, second) go multi4(second, third) for result := range third { fmt.Println(result) } }
▼ 実行
$ go run lesson.go 0 8 16 24 32 40 48 56 64 72
《解説》
producerのfor文でfirst channelが0を受け取り、multi2のfor文でfirst channelが受け取った0×2を行う。さらに、multi2のfor文の結果を受け取ったsecond channelの0を用いて、multi4で0×4を行い、main関数のresultとして0を出力する。その後も同様の流れで、producerのfor文の条件を満たすまで出力を繰り返す。
6.selectを用いたchannelの受信
複数のchannelをお互いにブロッキングしないようにしながら実行する
⑴
使用例
package main import ( "fmt" "time" ) func goroutine1(ch chan string) { for { ch <- "packet from 1" time.Sleep(3 * time.Second) } } func goroutine2(ch chan string) { for { ch <- "packet from 2" time.Sleep(1 * time.Second) } } func main() { c1 := make(chan string) c2 := make(chan string) go goroutine1(c1) go goroutine2(c2) for { select { case msg1 := <-c1: fmt.Println(msg1) case msg2 := <-c2: fmt.Println(msg2) } } }
▼ 実行
$ go run lesson.go packet from 2 packet from 1 packet from 2 packet from 1 packet from 2 packet from 1 packet from 2 packet from 1 packet from 2 packet from 1 packet from 2 packet from 1 : ※ 強制的に終了させるまで繰り返す
《解説》
以下のようにデータ型が異なるものや出力までの待ち時間が異なる場合でもOK。
package main import ( "fmt" "time" ) func goroutine1(ch chan string) { for { ch <- "packet from 1" time.Sleep(3 * time.Second) } } func goroutine2(ch chan int) { for { ch <- 100 time.Sleep(1 * time.Second) } } func main() { c1 := make(chan string) c2 := make(chan int) go goroutine1(c1) go goroutine2(c2) for { select { case msg1 := <-c1: fmt.Println(msg1) case msg2 := <-c2: fmt.Println(msg2) } } }
▼ 出力
100 packet from 1 100 100 packet from 1 100 100 100 packet from 1 100 : ※ 強制的に終了させるまで繰り返す
7.Default Selection と for break
⑴
使用例
package main import ( "fmt" "time" ) func main() { tick := time.Tick(100 * time.Millisecond) // 設定した時間ごとに実行 boom := time.After(500 * time.Millisecond) // 設定した時間経過後に実行 for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } }
▼ 実行
$ go run lesson.go . . tick. . . tick. . . tick. . . tick. . . BOOM!
《解説》
package main import ( "fmt" "time" ) func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case t := <-tick: // tを設定した場合 fmt.Println("tick.", t) case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } }
▼ 出力
. . tick. 2020-02-25 17:36:31.852435353 +0000 UTC m=+0.100577828 . . tick. 2020-02-25 17:36:31.953306907 +0000 UTC m=+0.201449365 . . tick. 2020-02-25 17:36:32.053527741 +0000 UTC m=+0.301670329 . . tick. 2020-02-25 17:36:32.153991047 +0000 UTC m=+0.402133577 . . tick. 2020-02-25 17:36:32.252393581 +0000 UTC m=+0.500536048 BOOM!
《解説》
⑵ for break
for文を抜ける
使用例
package main import ( "fmt" "time" ) func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) OuterLoop: // OuterLoopの名称は任意のものでOK for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") break OuterLoop default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } fmt.Println("##############") }
▼ 実行
$ go run lesson.go . . tick. . . tick. . . tick. . . tick. . . tick. BOOM! ##############
8.sync.Mutex
⑴
使用例
package main import ( "fmt" "sync" "time" ) type Counter struct { v map[string]int mux sync.Mutex } func (c *Counter) Inc(key string) { c.mux.Lock() defer c.mux.Unlock() c.v[key]++ } func (c *Counter) Value(key string) int { c.mux.Lock() defer c.mux.Unlock() return c.v[key] } func main() { c := Counter{v: make(map[string]int)} go func() { for i := 0; i < 10; i++ { c.Inc("Key") } }() go func() { for i := 0; i < 10; i++ { c.Inc("Key") } }() time.Sleep(1 * time.Second) fmt.Println(c, c.Value("Key")) }
▼ 実行
$ go run lesson.go {map[Key:20] {0 0}} 20
《解説》
【文法系】【go】基本15:ロギングとエラーハンドリング
1.log(ロギング)
以下のように、該当するファイルがない場合、エラー文を表示して、終了する際などエラーハンドリングに使用。
主な使用場面
package main import ( "io" "log" "os" ) func loggingSettings(logFile string){ logfile, _ := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) multiLogFile := io.MultiWriter(os.Stdout, logfile) log.SetFlags(log.Ldate | log.Ltime | log.Llongfile) log.SetOutput(multiLogFile) } func main() { loggingSettings("test.log") // test.logに以下のエラーハンドリングの結果を出力 _, err := os.Open("xxxxxx") if err != nil{ log.Fatalln("Exit", err) } }
▼ 出力
2020/02/24 06:53:21 /home/vagrant/workspace/src/myapp/lesson.go:20: Exit open xxxxxx: no such file or directory exit status 1
<test.log>
2020/02/24 06:53:21 /home/vagrant/workspace/src/myapp/lesson.go:20: Exit open xxxxxx: no such file or directory
⑴ 基本的な書き方
例①
package main import ( "log" "fmt" ) func main() { log.Println("logging!") log.Printf("%T %v", "test", "test") log.Fatalln("error!") fmt.Println("OK!") // 実行されない }
▼ 実行
$ go run lesson.go 2020/02/24 06:27:29 logging! 2020/02/24 06:27:29 string test 2020/02/24 06:27:29 error! exit status 1
《解説》
「log.Fatalln()」と同様に、「log.Fatalf()」もそこでコードは終了する。
func main() { log.Println("logging!") log.Printf("%T %v", "test", "test") log.Fatalf("%T %v", "test2", "test2") fmt.Println("OK!") // 実行されない }
▼ 出力
2020/02/24 06:38:26 logging! 2020/02/24 06:38:26 string test 2020/02/24 06:38:26 string test2 exit status 1
2.エラーハンドリング
説明
⑴ 基本的な書き方
例①
package main import ( "fmt" "log" "os" ) func main() { file, err := os.Open("./lesson.go") if err != nil{ log.Fatalln("Error!") } defer file.Close() data := make([]byte, 100) count, err := file.Read(data) if err != nil{ log.Fatalln("Error") } fmt.Println(count, string(data)) err = os.Chdir("test") // Chdirはchangeディレクトリの意 if err != nil{ log.Fatalln("Error") } }
▼ 実行
$ go run lesson.go 100 package main import ( "fmt" "log" "os" ) func main() { file, err := os.Open("./les 2020/02/24 07:52:42 Error exit status 1
《解説》
:=(イニシャライズのショートデクレアレーションは)が最低一つイニシャライズする変数があれば良いため、今回のケースでも使用できる。
つまり、「count, err := file.Read(data)」のcountはイニシャライズされ、errはすでに定義されているため、override(上書き)される。
なお、os.Chdir()については、エラーかどうかのみ返すため、変数は一つとなり、errはすでに定義済みのため、=でoverrideする。
2.panicとrecover
panic:自分で例外を投げられるため、そこでプログラムは強制終了される。
⑴ panic
例①
package main import "fmt" func thirdPartyConnectDB(){ panic("Unable to connect database!") } func save(){ thirdPartyConnectDB() } func main() { save() fmt.Println("OK?") }
▼ 実行
$ go run lesson.go panic: Unable to connect database! goroutine 1 [running]: main.thirdPartyConnectDB() /home/vagrant/workspace/src/myapp/lesson.go:6 +0x39 main.save() /home/vagrant/workspace/src/myapp/lesson.go:10 +0x20 main.main() /home/vagrant/workspace/src/myapp/lesson.go:14 +0x22 exit status 2
《解説》
save関数でthirdPartyConnectDB関数を呼び出し、panicが実行されるため、そこでプログラムが終了し、main関数にあるfmt.Println("OK?")は実行されない。
⑵ recover
例①
package main import "fmt" func thirdPartyConnectDB(){ panic("Unable to connect database!") } func save(){ defer func(){ s := recover() fmt.Println(s) }() thirdPartyConnectDB() } func main() { save() fmt.Println("OK?") }
▼ 実行
$ go run lesson.go Unable to connect database! OK?
《解説》
save関数でthirdPartyConnectDB関数を呼び出し、panicが実行されるが、save関数では、thirdPartyConnectDB関数よりも先にdefer func()が記述されているため、panicで発生した例外の内容のみrecover()が受け取り、Printlnで出力する(プログラムの終了はさせない)。プログラムは終了しないため、main関数のfmt.Println("OK?")も実行される。
【補足】
以下のように、panicを呼び出すthirdPartyConnectDB関数をdefer func()よりも先に書くと、panicでプログラムが終了してしまうため注意。
func save(){ thirdPartyConnectDB() defer func(){ s := recover() fmt.Println(s) }() }
▼ 出力
panic: Unable to connect database! goroutine 1 [running]: main.thirdPartyConnectDB() /home/vagrant/workspace/src/myapp/lesson.go:6 +0x39 main.save() /home/vagrant/workspace/src/myapp/lesson.go:10 +0x22 main.main() /home/vagrant/workspace/src/myapp/lesson.go:18 +0x22 exit status 2
学習メモ
今日学んだこと、調べた事項のURLをここに記します。
2020/5/23
Reactディレクトリ構成試行記 - hokan公式アカウント - Medium
React.js ライブラリ「react-toastify」を使用してアラート機能を実装する | mebee
2020/5/22
[webpackレシピ] その1: TypeScriptに対応する - sansaisoba's tech blog
2020/5/20
最新版TypeScript+webpack 4の環境構築まとめ(React, Vue.js, Three.jsのサンプル付き) - ICS MEDIA
webpack-dev-serverの導入、設定 - Qiita
webpack-dev-serverの基本的な使い方と設定方法の詳しい解説 | オリジナルゲーム.com
Babelとwebpackを使ってES6でReactを動かすまでのチュートリアル - Qiita
webpack - ERROR in Entry module not found: Error: Can't resolve './src' が解決できない|teratail
2020/5/19
【Python】絶対値の計算方法について(abs, math.fabs, numpy.abs) | Hbk project
interface{} な変数を型が決まっている関数の引数にする - Qiita
2020/5/18
React HooksのuseCallbackを正しく理解する - Qiita
JavaScriptの「コールバック関数」とは一体なんなのか
JavaScript中級者への道【5. コールバック関数】 - Qiita
React Hooksのルールをよく理解しないとハマるエラー対処法 - Qiita
TypeScriptのEnumのループはObject.entries()で実現可能 - Qiita
TypeScriptに於けるArray.reduceの型推論の種類 - Qiita
2020/5/12
RailsでAPI用のアプリを作成(POST処理編) - 親バカエンジニアのナレッジ帳
CORSがよくわからないので解説してみた&Rails APIでのCORS設定 - Qiita
2020/5/11
React開発において便利なTypeScriptの型まとめ - Qiita
JavaScriptで書く「let,var,const」の違い・使い分け | TechAcademyマガジン
イマドキのJavaScriptの書き方2018 - Qiita
React.js - React.FC型の使い所|teratail
Promiseの使い方、それに代わるasync/awaitの使い方 - Qiita
TypeScriptでReactをやるときは、小さいアプリでもReduxを最初から使ってもいいかもねというお話 | フューチャー技術ブログ
コードで理解するRedux(React使用) - Qiita
クラス · JavaScript Primer #jsprimer
ReactとFirebaseを使ってログインフォームを実装する② | Harkerblog
2020/5/10
【React | Redux】connect()をざっくりと解説してみる | Qrunch(クランチ)
TypeScript+Reduxで全ステートの型を解決するには - Qiita
2020/5/8
TypeScriptで始めるReactプロジェクトのボイラープレート作ってみた | Qrunch(クランチ)
Awesome Go : 素晴らしい Go のフレームワーク・ライブラリ・ソフトウェアの数々 - Qiita
TypeScript で書く React コンポーネントを基礎から理解する - Qiita
React (TypeScript): ベストプラクティス - Qiita
TypeScript+React+Reduxチュートリアル · nametake.info
2020/5/7
【INNER JOIN, LEFT JOIN , RIGHT JOIN】テーブル結合の挙動をまとめてみた【SQL】 - Qiita
Golang x Beego x Docker x CircleCI x npmで開発環境をサクッと作ってみよう - Qiita
自動テストのスタブ・スパイ・モックの違い | gotohayato.com
Go言語でテスト作成 testifyの基本的な使い方 | RE:ENGINES
go-sqlmockを使ってGORMで書いたコードをテストする - Qiita
Goでデータベースを簡単にモック化する【sqlmock】 - Qiita
2020/5/6
pythonでそのまま数値を出力すると小数点以下も表示される。 小数点以下を使用しない場合は、出力時にint()を使用する
def games(n): num = (n * (n-1))/2 print(int(num)) # int()を使用しない場合は小数点以下も表示される if __name__ == "__main__": number = int(input()) games(number)
Pythonで小数点以下を切り捨て・切り上げ: math.floor(), math.ceil() | note.nkmk.me
2020/5/5
【Go】string型からint64型・uint64型へ変換する方法 - Qiita
Go-gorm: JOINS make life easier! · Kaviraj
INNER JOINでエラーメッセージ「Column 'カラム名' in field list is ambiguous」が出た時 - Qiita
2020/5/4
《図解》 SaaS、PaaS、IaaSってどういう意味?そしてその違いとは?
Docker Compose restart の挙動 - 技術備忘記
.gitignoreしたファイルをレポジトリから削除する方法 - Qiita
git addを取り消す方法 - Reasonable Code
基本的なシステム構成図を理解するためのAWS基礎をまとめてみた - Qiita
2020/5/3
create-react-appで作った雛形のコードがService Workerで何をしているのか - Qiita
https://note.com/npaka/n/n6d0e8cd4ebe0?scrollpos=comment
なぜReact+TypeScriptでコンポーネント作成が早くなるのか - Qiita
TypeScriptでredux-formを使ってみる - かずきのBlog@hatena
TypeScriptの型におけるJSXサポートが100%分かる記事 - Qiita
Redux Form V6 typescript definition · GitHub
2020/5/1
軽量プログラミング言語とは?おすすめ言語5選と便利な使い方を解説!軽量言語を使うメリットは?強みを活かした使用例も紹介 | A-STAR(エースター)
GinでBindingが物珍しかったので他のフレームワークも調べてみた - Qiita
2020/4/30
アサーション(アサート)とは - IT用語辞典 e-Words
Goでメソッドを簡単にモック化する【gomock】 - Qiita
GitHub - golang/mock: GoMock is a mocking framework for the Go programming language.
モジュールバンドラーはなぜモダンなフロントエンドの開発に必要なのか?|Kosukeee|note
フロントエンド知らない私のwebpack入門 その1 - Qiita
Testifyでmockを作ってテストを記述してみたメモ - Qiita
Goテストの綺麗な書き方 - Eureka Engineering - Medium
Golangでtestingことはじめ(1)〜testingパッケージを使ったユニットテスト〜 - DeNA Testing Blog
DI・DIコンテナ、ちゃんと理解出来てる・・? - Qiita
0.デザインパターンの基本 1 | TECHSCORE(テックスコア)
2020/4/29
【JavaScript】Spread構文とRestパラメーターを使いこなそう - Qiita
【JavaScript入門】初心者でも分かるreduce()の使い方とサンプル例まとめ | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
2020/4/28
Getting Started with React - An Overview and Walkthrough Tutorial – Tania Rascia
TypeScriptでジェネリクス(Generics)を理解するための簡単なチュートリアル | I am mitsuruog
ユーティリティって何ですか? - jQueryを勉強中です。今、こち... - Yahoo!知恵袋
typescriptのuniontypesについて - ウェブエンジニア珍道中
【考えたこと、感じたこと】
・その日学んだことをその場限りにせず、次の学びにつなげるアプリをつくりたい
→その日調べたURLを保存できる。その日にやったことを振り返り、次のアクションを立てられるなど
→学習は反復により定着する。どうやって簡単に反復できるようにするかが課題。
→日次の学習記録には貼り付けたURLが記載されるが、学習記録概要?には、URLのトップページのみが保存される?(URLの/でアクセスできる部分)
【Go】本noteの基本的な環境について
本noteの環境
PC環境(ホスト)
# OSのバージョン (base) $ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G2022 # Virtualboxのバージョン (base) $ VBoxManage -v 6.1.2r135662 # vagrantのバージョン (base) $ vagrant -v Vagrant 2.2.7
仮想環境(ゲスト)
# Linuxのバージョンが記載されているファイルを検索 vagrant@:~$ ls /etc/*-release /etc/lsb-release /etc/os-release # ゲストOSのバージョンを出力 vagrant@:~$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
仮想環境上のディレクトリ構成
workspace - src - test - lesson.go
現在のディレクトリ
vagrant@vagrant-ubuntu-trusty-64:~/workspace/src/test$ pwd /home/vagrant/workspace/src/test
【注】
以降、特段の記述がない限り、コマンドの実行は現在のディレクトリ(test)で行われるものとし、文字数削減のため、表記を以下に省略して記述する。
省略前:vagrant@vagrant-ubuntu-trusty-64:~/workspace/src/test$
↓
省略後:$
【手順系】【Python】vagrant+dockerを使ったWebアプリケーション開発②:Flaskによる「Hello World!」
本noteの概要
仮想環境上でFlaskを使用したWebアプリケーションを開発する。
本noteの対象者
・VirtualBoxおよびVagrantをインストールしている方
・docker hubのアカウントを開設している方
・dockerおよびdocker-composeをインストールしている方
▽ dockerのインストール確認
vagrant@ubuntu-xenial:~$ docker version Client: Docker Engine - Community Version: 19.03.6 API version: 1.40 Go version: go1.12.16 Git commit: 369ce74a3c Built: Thu Feb 13 01:28:06 2020 OS/Arch: linux/amd64 Experimental: false Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/version: dial unix /var/run/docker.sock: connect: permission denied
▽ docker-composeのインストール確認
vagrant@ubuntu-xenial:~$ docker-compose version docker-compose version 1.23.1, build b02f1306 docker-py version: 3.5.0 CPython version: 3.6.7 OpenSSL version: OpenSSL 1.1.0f 25 May 2017
※ 筆者は仮想環境上でdockerを実行しているため、ローカルでの操作と少々異なる部分があるかもしれません。仮想環境上でdockerを動かしたい方は以下を参考にしてみてください。
【手順系】vagrant+dockerを使ったWebアプリケーション開発①:開発環境構築と「Hello World!」 - This is My note
本noteの環境
PC環境(ホスト)
# OSのバージョン (base) $ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G2022 # Virtualboxのバージョン (base) $ VBoxManage -v 6.1.2r135662 # vagrantのバージョン (base) $ vagrant -v Vagrant 2.2.7
仮想環境(ゲスト)
# Linuxのバージョンが記載されているファイルを検索 vagrant@:~$ ls /etc/*-release /etc/lsb-release /etc/os-release # ゲストOSのバージョンを出力 vagrant@:~$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
Today's Thema:Flaskを使ったブラウザでの「Hello World!」
完成図
<最終的なディレクトリ構成>
▼ ホスト(ローカル)
- ops - testapp - src - app.py - Vagrantfile
※ ローカルのops
ディレクトリ下を仮想環境と同期させているため、ローカル上もしくは仮想環境上でディレクトリやファイルを作成した場合には、仮想環境上もしくはローカル上にも同じものが表示されます。
▼ ゲスト(仮想環境)
- srv - ops - testapp - src - app.py
Today's Thema:Flaskアプリの作成
1.Dockerイメージの作成〜コンテナの起動
⑴ Dockerfileの作成
testapp$ touch Dockerfile
⑵ Dockerfileの編集
FROM ubuntu:latest RUN apt-get update RUN apt-get install python3 python3-pip -y RUN pip3 install flask
⑶ Dockerイメージのビルド
testapp$ sudo docker build . -t flask/app:1.0 Sending build context to Docker daemon 3.584kB Step 1/4 : FROM ubuntu:latest latest: Pulling from library/ubuntu Digest: sha256:8d31dad0c58f552e890d68bbfb735588b6b820a46e459672d96e585871acc110 Status: Downloaded newer image for ubuntu:latest ---> ccc6e87d482b 〜中略〜 Installing collected packages: itsdangerous, MarkupSafe, Jinja2, click, Werkzeug, flask Successfully installed Jinja2-2.11.1 MarkupSafe-1.1.1 Werkzeug-1.0.0 click-7.0 flask-1.1.1 itsdangerous-1.1.0 Removing intermediate container e0bcc95b9688 ---> 9d8501c135c1 Successfully built 9d8501c135c1 Successfully tagged flask/app:1.0
⑷ 作成されたイメージの確認
testapp$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE flask/app 1.0 9d8501c135c1 29 seconds ago 474MB ubuntu latest ccc6e87d482b 5 weeks ago 64.2MB
⑸ Dockerコンテナの起動
testapp$ sudo docker run -it flask/app:1.0 /bin/bash root@726c66c1dc24:/#
【補足】コンテナ内の環境確認
Dockerコンテナが起動したら、以下のように「python3」と入力し、pythonの対話モードが表示されたらOK。
なお、対話モードを終了するにはexit()と入力するか、control+dを押す。
root@726c66c1dc24:/# python3 Python 3.6.9 (default, Nov 7 2019, 10:44:02) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
2.Flaskの作成・起動
⑴ pythonファイルの作成
testapp$ mkdir src && cd src
※ 上記のディレクトリ名は何でもOK
testapp/src$ touch app.py
<app.py>
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "Hello World!" if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)
. # 現在のディレクトリの意味 ├── Dockerfile └── src └── app.py
⑵ コンテナの起動
cd ..
で一階層上に上がって、testappディレクトリに戻った上で、以下を実行。
testapp$ sudo docker run -it -p 80:5000 -v $(pwd)/src:/home flask/app:1.0 /bin/bash root@67b6da95b1df:/#
※ 仮想環境を使用していない場合は、sudo docker run -it -p 5000:5000 -v $(pwd)/src:/home flask/app:1.0 /bin/bash
でOK。
仮想環境上でコンテナを起動する場合は、仮想環境のポート番号に合わせて80
の箇所を変更。今回は、Vagrantfileでconfig.vm.network "forwarded_port", guest: 80, host: 8080
と設定しているため、仮想環境のポートは80
に設定し、ブラウザにアクセスする際は8080
を使用する。
⑶ pythonファイルのマウント確認
上記コンテナ起動時に、src
ディレクトリにあるファイルをコンテナ内のhome
ディレクトリにマウントしたため、コンテナのhome
ディレクトリにapp.pyがあるかどうかを確認。
root@67b6da95b1df:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var root@67b6da95b1df:/# cd home/ root@67b6da95b1df:/home# ls app.py
⑷ Flaskの起動
root@67b6da95b1df:/home# python3 app.py * Serving Flask app "app" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) 10.0.2.2 - - [21/Feb/2020 07:53:00] "GET / HTTP/1.1" 200 - 10.0.2.2 - - [21/Feb/2020 07:55:01] "GET / HTTP/1.1" 200 - 10.0.2.2 - - [21/Feb/2020 07:55:02] "GET / HTTP/1.1" 200 -
⑸ ブラウザ上での表示確認
http://localhost:8080/にアクセスし、「Hello World!」と表示されるかどうか確認。
なお、仮想環境を使用していない場合は、http://localhost:5000/にアクセス。
※ 仮想環境を使っている方で、8080
番以外のポートを指定した方はそれに合わせてアクセス。
【補足】
app.pyの内容を変更した場合には、control+cで一度Flaskを停止し、再度python3 app.py
でFlaskを起動すると内容が更新される。
【手順系】vagrant+dockerを使ったWebアプリケーション開発①:開発環境構築と「Hello World!」
本noteの概要
仮想環境上でのWebアプリケーション開発を行うため、Vagrantfileに必要なコードを記載し、vagrant upコマンドで仮想環境を構築する。
本noteの対象者
・VirtualBoxおよびVagrantをインストールしている方
・docker hubのアカウントを開設している方
本noteの環境
PC環境(ホスト)
# OSのバージョン (base) $ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G2022 # Virtualboxのバージョン (base) $ VBoxManage -v 6.1.2r135662 # vagrantのバージョン (base) $ vagrant -v Vagrant 2.2.7
Today's Thema:vagrant+dockerによる開発環境構築
完成図
<最終的なディレクトリ構成>
▼ ホスト(ローカル)
- ops # 今後のアプリケーション開発でソースを管理するための空のディレクトリ - Vagrantfile
▼ ゲスト(仮想環境)
- srv - ops
1.仮想環境情報の設定
vagrant init
コマンドでVagrantfileを作成し、Vagrantfileに環境構築のために必要な設定を記述します。
⑴ Vagrantfileの作成(vagrant 初期化)
(base) $ mkdir vagrant-docker && cd vagrant-docker # 適当なディレクトリを作成し、作成したディレクトリへ移動 (base) :vagrant-docker $ vagrant init ubuntu/xenial64 # vagrant 初期化 A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant.
⑵ Vagrantfileを使った仮想環境情報設定
<vagrantfile>
$install_docker = <<SCRIPT sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" sudo apt-get update sudo apt-get -y install docker-ce sudo systemctl start docker sudo curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose docker-compose --version SCRIPT Vagrant.configure("2") do |config| config.vm.box = "ubuntu/xenial64" config.vm.network "forwarded_port", guest: 80, host: 8080 # localhostと仮想環境をつなげるための設定 config.vm.synced_folder 'ops/', '/srv/ops' config.vm.provision 'shell', inline: $install_docker end
⑶ ソースコードを管理するディレクトリの作成
今後、アプリケーション開発を行う際のソースコードを管理するディレクトリを作成。
(base) :vagrant-docker $ mkdir ops
2.仮想環境の起動・接続
⑴ 仮想環境の起動
(base) :vagrant-docker $ vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Box 'centos/7' could not be found. Attempting to find and install... default: Box Provider: virtualbox default: Box Version: >= 0 〜中略〜 default: docker-compose version 1.23.1, build b02f1306
⑵ 仮想環境への接続
(base) :vagrant-docker $ vagrant ssh Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.4.0-173-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage 17 packages can be updated. 16 updates are security updates. New release '18.04.4 LTS' available. Run 'do-release-upgrade' to upgrade to it. vagrant@ubuntu-xenial:~$
3.仮想環境のセットアップ状況確認
⑴ ゲストOSのバージョン確認
# Linuxのバージョンが記載されているファイルを検索 vagrant@ubuntu-xenial:~$ ls /etc/*-release /etc/lsb-release /etc/os-release # ゲストOSのバージョンを出力 vagrant@ubuntu-xenial:~$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
⑵ dockerのインストール確認
vagrant@ubuntu-xenial:~$ docker version Client: Docker Engine - Community Version: 19.03.6 API version: 1.40 Go version: go1.12.16 Git commit: 369ce74a3c Built: Thu Feb 13 01:28:06 2020 OS/Arch: linux/amd64 Experimental: false Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/version: dial unix /var/run/docker.sock: connect: permission denied
⑶ docker-composeのインストール確認
vagrant@ubuntu-xenial:~$ docker-compose version docker-compose version 1.23.1, build b02f1306 docker-py version: 3.5.0 CPython version: 3.6.7 OpenSSL version: OpenSSL 1.1.0f 25 May 2017
4.Dockerコンテナを使った「Hello World!」
⑴ Webアプリケーション動作確認用コンテナ(training/webapp)の起動
vagrant@ubuntu-xenial:~$ sudo docker run -d -p 80:5000 training/webapp python app.py Unable to find image 'training/webapp:latest' locally latest: Pulling from training/webapp Image docker.io/training/webapp:latest uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/ e190868d63f8: Pull complete 909cd34c6fd7: Pull complete 0b9bfabab7c1: Pull complete a3ed95caeb02: Pull complete 10bbbc0fc0ff: Pull complete fca59b508e9f: Pull complete e7ae2541b15b: Pull complete 9dd97ef58ce9: Pull complete a4c1b0cb7af7: Pull complete Digest: sha256:06e9c1983bd6d5db5fba376ccd63bfa529e8d02f23d5079b8f74a616308fb11d Status: Downloaded newer image for training/webapp:latest 1eb795fd41625fb81974d2a54a66e39329f535fc9a664bcb5e21c2cb9ce4f4ce
⑵ ブラウザ上での表示確認
※起動したコンテナは後述のdocker stop
コマンドで停止できます。
5.仮想環境やdocker関連コマンド
○ 起動中コンテナの一覧表示
vagrant@ubuntu-xenial:~$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1eb795fd4162 training/webapp "python app.py" 19 minutes ago Up 19 minutes 0.0.0.0:80->5000/tcp gallant_murdock
※ 停止中のコンテナも含める場合はsudo docker ps -a
コマンドを使用
○ 起動中コンテナへのログイン
vagrant@ubuntu-xenial:~$ sudo docker exec -it 1eb795fd4162 /bin/bash root@1eb795fd4162:/opt/webapp#
【参考】今回使用した動作確認用コンテナの中身について
上記のコマンドで起動中コンテナにログインし、ls
コマンドを入力するとコンテナ内のコンテンツを確認することが可能。
root@1eb795fd4162:/opt/webapp# ls Procfile app.py requirements.txt tests.py
またvi app.py
コマンドでファイルの中身を確認すると、今回のアプリケーションはPython(Flask)で書かれていることがわかります。
<app.py>
import os from flask import Flask app = Flask(__name__) @app.route('/') def hello(): provider = str(os.environ.get('PROVIDER', 'world')) return 'Hello '+provider+'!' if __name__ == '__main__': # Bind to PORT if defined, otherwise default to 5000. port = int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port)
○ 起動しているDockerコンテナの停止
vagrant@ubuntu-xenial:~$ sudo docker stop 1c763df10ac5
○ 仮想環境からのログアウト
vagrant@ubuntu-xenial:~$ exit
○ 仮想環境の停止
(base) :vagrant-docker $ vagrant halt ==> default: Attempting graceful shutdown of VM...
○ 仮想環境の稼働状況確認
(base) $ vagrant global-status id name provider state directory ---------------------------------------------------------------------------------- 48dd523 default virtualbox running /Users/vm/infra 6126d9b main virtualbox running /Users/vm/ubuntu 2911deb minikube virtualbox poweroff /Users/vm/Linux/vagrant-minikube The above shows information about all known Vagrant environments on this machine. This data is cached and may not be completely up-to-date (use "vagrant global-status --prune" to prune invalid entries). To interact with any of the machines, you can go to that directory and run Vagrant, or you can use the ID directly with Vagrant commands from any directory. For example: "vagrant destroy 1a2b3c4d"
○ 仮想環境の削除
(base) $ vagrant destroy 48dd523
【参考】
・Vagrant+VirtualBoxによるDocker環境構築 - Qiita
・Vagrantの使い方 - Qiita
・Dockerインストールメモ - Qiita
・【まとめ】Vagrant コマンド一覧 - Qiita
・VagrantとDockerで「環境に縛られない」開発環境を構築しよう - RAKUS Developers Blog | ラクス エンジニアブログ