Goを少し理解していく

プルリクを貰った

ありがたいことに、サンプル用プロジェクトなのにsecondrykeyさんにプルリクを貰った!

https://github.com/nyo3q1/golang_simple_api/pull/1

コードを見ると全体的にGoらしい書き方に修正してくれていた。

Goが分かっていないというのもあるけど。スクリプト言語に甘えていたツケが回ってきた感が凄いので、分からないなりに噛み砕いて理解していく。

構造体の配列はポインタで返す

DBから値を取得、それを元に構造体の配列を生成し返却する関数の戻り値はポインタで返す。

理由

大きな構造体や配列は値のまま返すとコピーの負荷が高いから

わかりやすく書いてある記事

https://qiita.com/knsh14/items/8b73b31822c109d4c497#receiver-type

コードはこんな感じ

type People struct {
    id   int64
    name string
}

type Peoples []People

func GetPeoples() (*Peoples, error) {
    // 略
    return &peoples, nil
}

log.Fatal(funhttp.ListenAndServec())の動き

http.ListenAndServe は異常が起きた時のみ error を返すようになっている https://stackoverflow.com/questions/25920963/in-what-situation-will-http-listenandserve-return https://golang.org/doc/articles/wiki/#tmp_3

このQiitaが簡単にまとまっている

https://qiita.com/_kyamasan/items/f27978906c23fc3c49c3

ありがとう Shizuoka.go

このイベントに参加していなければプルリクは貰えませんでした。

本当に...ありがとう...

GoとRedisの環境をDocker Composeで構築する

とりあえず一番シンプルな構成はこんな感じかな?っていうのを作った https://github.com/nyo3q1/redis-de-go

Goのコードはこんな感じ

ホスト名 redis とポート番号書いて、パスワードとか設定するだけ。

func ExampleNewClient() {
    client := redis.NewClient(&redis.Options{
        Addr:     "redis:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    pong, err := client.Ping().Result()
    fmt.Println(pong, err)
    // Output: PONG <nil>
}

単純にredisに繋げに行くだけで、どの言語でも変わらないので書くことがない。

UptimeRobotはいいぞ

UptimeRobotはいいぞ

Webの死活監視は通常PingdomやらMackerelを使うと思います シンプルな監視だけで良ければ個人的にはUptimeRobotを推します。 https://uptimerobot.com/

uptimerobot.com

問題

以前勤めていた会社ではこんな問題がありました

Web制作の部署のレンタルサーバで運用しているWordPressなサイトが落ちている事に気づけない。

選定基準

監視ツール系の導入にあたり以下の課題がありました。 - Web制作部には専任のエンジニアがいない - Zabbixの導入も検討したが操作があまりにも複雑 - Web制作の人だけで完結できるのを探していた - 監視対象のURLが生きているかどうか確認するだけのシンプルな監視ツールが欲しい - 運用管理コストが低いツールが良いのでSaaSが望ましい - 試験導入なのでコストが低ければ低いほど良い

そうすると候補に上がるのが、Pingdomになりますが若干お値段高めかつ多機能なので除外。

そして Pingdom alternative でググってみつけたのが UptimeRobot です

機能

機能としてはこんな感じ - 機能はシンプルでWebの死活監視 - グラフで日々のフェッチ速度が閲覧可能 - 異常時の通知先設定も複数設定可能

しかも監視対象50件までは無料で使え、有料版に移行すると、SSL証明書の有効期限の監視などWeb製作者に嬉しい機能付き。

締め

導入したところ意外と評判が良く、簡単に使いこなせているようでした。

(そのあとすぐ退職したので今どうなっているのか分かりませんが)

ちなみに、OSS版だとこれがなかなか良さそう。 https://github.com/hunterlong/statping/tree/master/handlers

Docker-composeで環境変数の値をファイルで管理する

DBの設定をアプリケーション側で利用したい時よくやるやつ

とりあえずどんなふうに書くのかというとこんな感じ

services:
    db:
        image: mysql:8.0
        container_name: mysql_host
        environment:
            MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
            MYSQL_DATABASE: ${DATABASE}
            MYSQL_USER: ${DB_USER}
            MYSQL_PASSWORD: ${DB_USER_PASSWORD}
            TZ: '${TZ}'
   app:
        image: python
        container_name: app
        environment:
            MYSQL_DATABASE: ${DATABASE}
            MYSQL_USER: ${DB_USER}
            MYSQL_PASSWORD: ${DB_USER_PASSWORD}

で、これをどのように変数を共通化しているかというと、docker-compose.yamlと同階層に .env ファイルを配置しこんな感じに書く。

B_ROOT_PASSWORD=root
DATABASE=test_database
DB_USER=docker
DB_USER_PASSWORD=docker
TZ=Asia/Tokyo

実務でこれ運用した事ないから知らないけど、何故これが便利かと想像すると。

env.get()とかで値を取れば良いだけなので、アプリケーションにDBなどの情報を直接書かずにすむのが一番大きいのかな?

スケールのしやすさ、リポジトリに直接DBのパスワードとかあげなくてよくなったりとか...

一応このリポジトリで同じ事はやっている

https://github.com/nyo3q1/golang_simple_api

GoでMySQLのデータを取得する

用意するテーブルはこんな感じ

id name
1 Taro
1 Jiro

コード

一応ここに同じコードがある https://github.com/nyo3q1/golang_simple_api

package main

import (
    "log"
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

type People struct {
    id int64
    name string
}

type Peoples []People

func GetPeoples() Peoples{
    db, err := sql.Open("mysql", "docker:docker@tcp(db)/test_database")
    if err != nil {
        log.Fatal("db error.")
    }
 
    rows, err := db.Query("select * from people")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    var peoples Peoples
    for rows.Next() {
        people := People{}
        if err := rows.Scan(&people.id, &people.name); err != nil {
            log.Fatal(err)
        }

        peoples = append(peoples, people)
    }
    defer db.Close()

    return peoples
}

簡単な説明

説明する必要もないけど記事を書く上でコードだけというのも味気ないので...

この行で

  • MySQLに接続
  • ユーザ、パスワードが、docker:docker
  • 接続先がdbと言うホスト名(IPアドレスでもOK)
  • データベース名は test_database
sql.Open("mysql", "docker:docker@tcp(db)/test_database")

個人的に面白いなとおもったのが取得したデータを変数に格納するところ。

取得したカラムに沿っていかないとダメ。

取得するカラムを意識的に記述できて、レイテンシも向上しそうで好き。

rows.Scan(&people.id, &people.name)

ちなみにpeoplesをreturnするようにしているけど受け取り元ではこうやって書く

peoples := GetPeoples()

CircleCIのorbsを使うとメンテナンス性が簡単に向上する

CircleCIでGAEにデプロイする時に使ったので簡単に紹介

orbsの https://circleci.com/orbs/registry/orb/circleci/gcp-cli を利用する

まず、CircleCIの管理画面のBUILD SETTINGS > Environment Variables にこの3つを設定 - GCLOUD_SERVICE_KEY - GOOGLE_COMPUTE_ZONE - GOOGLE_PROJECT_ID

どのように設定するかは各自調べてください。

yamlは必要な箇所だけ見せるとこんな感じにシンプルにできる

version: 2.1
orbs:
  gcp-cli: circleci/gcp-cli@1.8.3

deploy:
    docker:
      - image: google/cloud-sdk:latest
    steps:
      - gcp-cli/initialize
      - run:
          name: Deploy to Google App Engine
          command: |
            gcloud --quiet app deploy app.yaml

PythonでGCSのオブジェクトをフォルダかどうか判定する

オブジェクトストレージってフォルダを作る概念はあるけど、実際はフォルダの概念は無いからAPIも用意されてないんですよね。

でもisdirって書きたい時あるよね? で、調べたら出てきて日本語の情報がなかったので書いておきます。

とりあえずこれがコード

def list_gcs_directories(bucket, prefix):
    # from https://github.com/GoogleCloudPlatform/google-cloud-python/issues/920
    iterator = bucket.list_blobs(prefix=prefix, delimiter='/')
    prefixes = set()
    for page in iterator.pages:
        print page, page.prefixes
        prefixes.update(page.prefixes)
    return prefixes

そしてこれが該当のリンク

List subdirectories · Issue #920 · googleapis/google-cloud-python · GitHub

ただ自分が実装した時こっちのコードでしか動作しなかった、あまりオススメはできませんが貼っておきます。

List subdirectories · Issue #920 · googleapis/google-cloud-python · GitHub

ちなみに、luigiのGCSの実装を真似ればもっと綺麗にできます。

難点としてライブラリを google.cloud.storage から googleapiclient に変更しないといけない。

luigi.contrib.gcs — Luigi 2.8.9 documentation