GCPのロードバランサの仕組みの復習

あいまいな理解でなんとなく使っているので復習がてらメモ。

下記を参考にまとめる。

www.ianlewis.org

リンク先から引用するが、2枚目のダイアグラムの画像がすごくわかりやすい。

f:id:road288:20180717190925p:plain

  • まずはヘルスチェックを作る

    • ヘルスチェックは他のリソースに依存しない
    • バックエンドサービスにアタッチすると動作を始める。それまでは作っただけでは何もしない
    • Compute Engineリソースのカテゴリに存在する
  • バックエンドサービス

    • 実体はインスタンスグループへのリンクだが、ポート番号とロードバランシングモードを設定できる。そこで設定したポート番号はnamed portとして設定する
  • バックエンド

    • バックエンドサービスとは別。バックエンドサービスはいくつかのバックエンドを束ねる。バックエンドサービスにヘルスチェックがひもづき、バックエンドにインスタンスグループがひもづく。(上掲のダイアグラムを参照)
  • URLマップ(Url Maps)

    • ホスト(host rules)とURLのマッピング(path matchers)で構成。
    • デフォルトでつなぐバックエンドサービスを設定する。他にマッチするものが無ければこれが利用される。1つしか作らないのであればこれだけで十分。
  • ターゲットプロキシ

    • フォワーディングルールとURLマップの間に挟まるもの
    • ここでユーザーの接続はterminateする
    • 主にSSL証明書を設定する
  • フォワーディングルール

    • IPアドレスをロードバランサ・ターゲットプロキシとひもづける
    • 実際に課金されるのはここ
    • IPアドレスは事前に別で作っておく

Goならわかるシステムプログラミング:第3章

  • 標準入力
package main

import (
    "os"
    "io"
    "fmt"
)

func main() {
    for {
        buffer := make([]byte, 5)
        size, err := os.Stdin.Read(buffer)
        if err == io.EOF {
            fmt.Println("EOF")
            break
        }
        fmt.Printf("size=%d input='%s'\n", size, string(buffer))
    }
}
  • ファイル入力
package main

import (
    "os"
    "io"
)

func main() {
    file, err := os.Open("main.go")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    io.Copy(os.Stdout, file)
}
  • ネットワーク通信の読み込み
package main

import (
    "net"
    "net/http"
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {
    conn, err := net.Dial("tcp", "ascii.jp:80")
    if err != nil {
        panic(err)
    }
    conn.Write([]byte("GET / HTTP/1.0\r\nHost: ascii.jp\r\n\r\n"))
    res, err := http.ReadResponse(bufio.NewReader(conn), nil)
    fmt.Println(res.Header)
    defer res.Body.Close()
    io.Copy(os.Stdout, res.Body)

}
package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
)

func main() {
    data := []byte{0x0, 0x0, 0x27, 0x10}
    var i int32
    binary.Read(bytes.NewReader(data), binary.BigEndian, &i)
    fmt.Printf("data: %d\n", i)
}
  • PNGファイルを分析してみる
package main

import (
    "io"
    "encoding/binary"
    "fmt"
    "os"
)

func dumpChunk(chunk io.Reader) {
    var length int32
    binary.Read(chunk, binary.BigEndian, &length)
    buffer := make([]byte, 4)
    chunk.Read(buffer)
    fmt.Printf("chunk, '%v' (%d bytes)\n", string(buffer), length)
}

func readChunks(file *os.File) []io.Reader {
    var chunks []io.Reader
    file.Seek(8, 0)
    var offset int64 = 8

    for {
        var length int32
        err := binary.Read(file, binary.BigEndian, &length)
        if err == io.EOF {
            break
        }
        chunks = append(chunks, io.NewSectionReader(file, offset, int64(length)+12))
        offset, _ = file.Seek(int64(length+8), 1)
    }
    return chunks
}

func main() {
    file, err := os.Open("Lenna.png")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    chunks := readChunks(file)

    for _, chunk := range chunks {
        dumpChunk(chunk)
    }
}

実行結果

chunk, 'IHDR' (13 bytes)
chunk, 'sRGB' (1 bytes)
chunk, 'IDAT' (473761 bytes)
chunk, 'IEND' (0 bytes)

Process finished with exit code 0
  • PNG画像に秘密のテキストを入れてみる
package main

import (
    "io"
    "bytes"
    "encoding/binary"
    "hash/crc32"
    "os"
    "fmt"
)

func textChunk(text string) io.Reader {
    byteData := []byte(text)
    var buffer bytes.Buffer
    binary.Write(&buffer, binary.BigEndian, int32(len(byteData)))
    buffer.WriteString("tExt")
    buffer.Write(byteData)
    crc := crc32.NewIEEE()
    io.WriteString(crc, "tExt")
    binary.Write(&buffer, binary.BigEndian, crc.Sum32())
    return &buffer
}
func readChunks(file *os.File) []io.Reader {
    var chunks []io.Reader
    file.Seek(8, 0)
    var offset int64 = 8

    for {
        var length int32
        err := binary.Read(file, binary.BigEndian, &length)
        if err != nil {
            break
        }
        chunks = append(chunks, io.NewSectionReader(file, offset, int64(length)+12))
        offset , _ = file.Seek(int64(length+8), 1)
    }
    return chunks
}

func dumpChunk(chunk io.Reader) {
    var length int32
    binary.Read(chunk, binary.BigEndian, &length)
    buffer := make([]byte, 4)
    chunk.Read(buffer)
    fmt.Printf("chunk '%v' (%d bytes\n", string(buffer), length)
    if bytes.Equal(buffer, []byte("tExt")) {
        rawText := make([]byte, length)
        chunk.Read(rawText)
        fmt.Println(string(rawText))
    }
}

func writeFile() {
    file, err := os.Open("Lenna.png")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    newFile, err := os.Create("Lenna2.png")
    if err != nil {
        panic(err)
    }
    chunks := readChunks(file)
    io.WriteString(newFile, "\x89PNG\r\n\x1a\n")
    io.Copy(newFile, chunks[0])
    io.Copy(newFile, textChunk("ASCII PROGRAMMING++"))
    for _, chunk := range chunks[1:] {
        io.Copy(newFile, chunk)
    }
}

func readFile() {
    file, err := os.Open("Lenna2.png")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    chunks := readChunks(file)
    for _, chunk := range chunks {
        dumpChunk(chunk)
    }
}

func main() {
    writeFile()
    readFile()
}

  • テキスト解析 改行/単語で区切る
package main

import (
    "bufio"
    "strings"
    "fmt"
    "io"
)

var source = `1行目
2行目
3行目`

func main() {
    reader :=bufio.NewReader(strings.NewReader(source))
    for {
        line, err := reader.ReadString('\n')
        fmt.Printf("%#v\n", line)
        if err == io.EOF {
            break
        }
    }
}
  • bufio.Scannerを使う
package main

import (
    "bufio"
    "strings"
    "fmt"
)

var source = `1行目
2行目
3行目`

func main() {
    scanner := bufio.NewScanner(strings.NewReader(source))
    for scanner.Scan() {
        fmt.Printf("%#v\n", scanner.Text())
    }
}
  • データ型を指定して解析

Prometheusのドキュメントを読む

  • prometheusを起動
./prometheus --config.file=prometheus.yml

http://localhost:9090でステータスページが見られる http://localhost:9090/metricsで自身のmetricsのendpointが見られる(自分自身のmetricsも収集している)

  • 設定の再読込をするには?
    • PrometheusプロセスにSIGHUPを送る
    • /-/reloadにPOSTリクエストを送る

https://prometheus.io/docs/introduction/faq/#can-i-reload-prometheus-s-configuration

  • バッチジョブの監視はどうする?

    • Pushgatewayを使う
  • データ形式 <metric name>{<label name>=<label value>, ...}

    • 例:api_http_requests_total{method="POST", handler="/messages"}
  • prometheus.ymlの構成

    • global
      • prometheus自体の一般的な設定
    • rule_files
      • 追加で作った計算式を別ファイルに定義し、それを読み込む
    • scrape_configs
      • データを取得しにいくEndpoint(サーバー)などを記載
  • インストール方法

  • prometheus.rules.yml

    • recording_rulesとalerting_rulesがある
    • recording_rulesは集計したい値の追加
    • alerting_rulesはalertmanagerに送る条件の追加
    • prometheusはアラートの発火をシンプルに行うだけ。アラートの集約・通知条件・依存関係の定義などの細かい設定はalertmanagerで行う、というように役割分担する。
  • templateはGo言語のtext/templateパッケージをベースにしている

Goならわかるシステムプログラミング:第2章

第2章。

  • ファイルディスクリプタ・・・OSがカーネルのレイヤーで用意している抽象化の仕組み limits.confでいじるやつ。 0が標準入力 1が標準出力 2が標準エラー出力

  • 構造体が持つべきメソッドを表現するのがインタフェース

package main

import "fmt"

type Talker interface {
    Talk()
}

type Greeter struct {
    name string
}

func (g Greeter) Talk() {
    fmt.Printf("Hello, my name is %s\n", g.name)
}

func main() {
    var talker Talker

    talker = &Greeter{"wozozo"}
    talker.Talk()
}
  • ファイル出力の例
package main

import (
    "os"
)

func main() {
    file, err := os.Create("test.txt")
    if err != nil {
        panic(err)
    }
    file.Write([]byte("os.File example\n"))
    file.Close()
}
  • 画面出力
package main

import "os"

func main() {
    os.Stdout.Write([]byte("os.Stdout example\n"))
}
  • バッファ
package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer
    buffer.Write([]byte("bytes.Buffer example\n"))
        //buffer.WriteString("bytes.Buffer example\n") これでもOK
    fmt.Println(buffer.String())
}
  • インターネットアクセス
package main

import (
    "net"
    "io"
    "os"
)

func main() {
    conn, err := net.Dial("tcp", "ascii.jp:80")
    if err != nil {
        panic(err)
    }
    io.WriteString(conn, "GET / HTTP1.0\r\nHost: ascii.jp\r\n\r\n")
    io.Copy(os.Stdout, conn)
}
  • httpパッケージを使う方法
package main

import (
    "net"
    "io"
    "os"
    "net/http"
)

func main() {
    conn, err := net.Dial("tcp", "ascii.jp:80")
    if err != nil {
        panic(err)
    }
    req, err := http.NewRequest("GET", "http://ascii.jp", nil)
    if err != nil {
        panic(err)
    }
    req.Write(conn)
    io.Copy(os.Stdout, conn)
}
  • ウェブサーバーからブラウザに対して書き込む
package main

import (
    "net/http"
    "io"
)

func handler(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "http.ResponseWriter sample")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}
  • デコレータ・・・io.Writerを受け取り、書き込まれtデータを加工して別のio.Writerに書き出す 下記の例は1つのWriteでファイルと標準出力両方に書き込んでいる
package main

import (
    "os"
    "io"
)

func main() {
    file, err := os.Create("multiwriter.txt")
    if err != nil {
        panic(err)
    }
    writer := io.MultiWriter(file, os.Stdout)
    io.WriteString(writer, "io.MultiWriter example\n")
}

*gzip圧縮してos.Fileに中継する

package main

import (
    "os"
    "compress/gzip"
    "io"
)

func main() {
    file, err := os.Create("test.txt.gz")
    if err != nil {
        panic(err)
    }
    writer := gzip.NewWriter(file)
    writer.Header.Name = "test.txt"
    io.WriteString(writer, "gzip.Writer example")
    writer.Close()
}
  • バッファをためておいてFlush()で書き込む
package main

import (
    "bufio"
    "os"
)

func main() {
    buffer := bufio.NewWriter(os.Stdout)
    buffer.WriteString("bufio.Writer ")
    buffer.Flush()
    buffer.WriteString("example\n")
    buffer.Flush()
}
  • Fprintf()
package main

import (
    "fmt"
    "os"
    "time"
)

func main() {
    fmt.Fprintf(os.Stdout, "Write with os.Stdout at %v", time.Now())
}
Write with os.Stdout at 2018-07-05 23:07:33.224526589 +0900 JST m=+0.000422627
  • JSONを整形して標準出力に出す
package main

import (
    "encoding/json"
    "os"
)

func main() {
    encoder := json.NewEncoder(os.Stdout)
    encoder.SetIndent("", "   ")
    encoder.Encode(map[string]string {
        "example": "encoding/json",
        "hello": "world",
    })
}

問題

Q2.1: ファイルに対するフォーマット出力

package main

import (
    "os"
    "fmt"
)

func main() {
    file, err := os.Create("test.txt")
    if err != nil {
        panic(err)
    }
    d := 1
    s := "s"
    f := 0.2
    fmt.Fprintf(file, "%d, %s, %f", d, s, f)
    file.Close()
}

Q2.2: CSV出力

package main

import (
    "os"
    "encoding/csv"
)

func main() {
    file, err := os.Create("test.csv")
    if err != nil {
        panic(err)
    }
    writer := csv.NewWriter(file)
    writer.Write([]string{"apple", "orange", "lemon"})
    writer.Write([]string{"red", "orange", "yellow"})
    writer.Flush()
    file.Close()
}

Q2.3 gzipされたJSON出力をしながら、標準出力にログを出力

package main

import (
    "net/http"
    "encoding/json"
    "io"
    "compress/gzip"
    "os"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Encoding", "gzip")
    w.Header().Set("Content-Type", "application/json")
    source := map[string]string {
        "Hello": "World",
    }

    gzipWriter := gzip.NewWriter(w)
    encoder := json.NewEncoder(io.MultiWriter(gzipWriter, os.Stdout))
    encoder.SetIndent("", "   ")
    encoder.Encode(source)
    gzipWriter.Close()
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Goならわかるシステムプログラミング:第1章

Goならわかるシステムプログラミング

Goならわかるシステムプログラミング

ライブラリで借りた。 ちょうどGoと、インフラレイヤーの知識をあらためてつけたいという希望がいっぺんに満たせそうなので取り組んでいく。

第1章

GoとVisual Studio Codeの設定、delveを使ったデバッガーを使用してみるところまで。 Visual StudioのKeybindに慣れなかったが、プラグインIntelliJのKeybindというのがあってそれをいれたらいっきに楽になった。

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, World!") // ここにブレークポイント
}

魚は痛みを感じるか?

魚は痛みを感じるか?

魚は痛みを感じるか?

ライブラリにあったので手にとった。

図解・ベイズ統計「超」入門 あいまいなデータから未来を予測する技術

読んだ。 基礎がわかってないのでこういう本を他にも読んでいきたい