全力で怠けたい

怠けるために全力を尽くしたいブログ。

Vim のキーボードマクロの記録と実行。

Vim のキーボードマクロが便利なのだがあまり使わないのでいざ使うときに「あれ、どうやってたっけ?」となるので Vim のキーボードマクロの基本的な記録、実行と閲覧方法をメモしておく。

キーボードマクロとは

テキストエディターの多くは入力した文字を保存しておいて保存しておいた文字を後から実行する機能があり、一般的にキーボードマクロとかキーボードレコーダーとか呼ばれることが多い。 この機能は Vim にも備わっていて、うまく使うと単調な繰り返し作業などを非常に効率よく処理することができる。

ちなみに Vim の同機能は記録と実行を合わせて complex repeats というようだ。詳しくは help q を参照。

キーボードマクロの記録

ノーマルモードq とタイプして続いてキーボードマクロの記録先のレジスターをタイプする。 キーボードマクロの記録先のレジスターは [0-9a-zA-Z"] から選べるので、たとえばキーボードマクロをレジスタa に記録するなら qa とタイプする。

キーボードマクロの記録が開始するとステータス行に recording @a のように表示されるので、実際の操作をしていく。

記録したい操作が終わったらノーマルモードに戻ってもう一度 q をタイプする。 するとステータス行の recording @a が消えてキーボードマクロの記録が終了したことが分かる。

キーボードマクロの実行

ノーマルモード@ とタイプして続けて実行したいキーボードマクロの記録先のレジスターをタイプする。 たとえばレジスタa に記録しておいたキーボードマクロを実行するときは @a とタイプする。 @ は他のコマンドと同じように実行回数を指定できるので、たとえば 10@a のようにタイプするとレジスタa に記録しておいたキーボードマクロを10回実行することができる。

キーボードマクロの閲覧

ノーマルモードreg とタイプするとレジスターに記録されているキーボードマクロが閲覧できる。

キーボードマクロの例

4の倍数のときだけアホになるキーボードマクロ。 数値が1行ごとに書かれているので qa でキーボードマクロの記録を開始して 4j0cwstupid!<esc> をタイプして q で記録を終了して、 10@a で記録しておいたキーボードマクロを実行している。

それぞれのコマンドは以下を参照。

  • 4j: カーソルを4行下に移動
  • 0: カーソルを行頭に移動
  • cw: 単語を削除してインサートモードを開始
  • stupid!: インサートモードで単純にタイプ
  • <esc>: ESC キーをタイプの意味。インサートモードを終了してノーマルモードへ戻る。

f:id:ebc_2in2crc:20190915221043g:plain

AWS で障害が発生したときにチェックしているページをまとめてみた。

はじめに

先週は AWS の東京リージョンで大規模な障害が発生した。

piyolog.hatenadiary.jp

AWS に関わるようになって初めて体験した AWS の大規模な障害だったのだが、オンプレミスのシステムが障害を起こしたときと同じように AWS というかクラウドで障害が発生したときも情報収集が非常に重要になる。

たとえば、障害が発生した AZ のリソースを止めて別の AZ にリソースを立ててトラフィックを新しく立てたリソースに回して業務継続する……などの対応は障害の原因と影響範囲が分からないとできないし、クラウド上で動いているシステムがお客様のもので自分たちがその運用会社であるなら、状況をお客様へ報告をするためにも障害の原因と影響範囲は分かっていなければならない。

AWS で障害が発生したときにチェックしているページ

前フリが長くなったが先週 AWS の障害が発生したときにチェックしていたページをまとめておく。

AWS Personal Health Dashboard

AWS Personal Health Dashboard は情報が見やすいし探しやすいのがよい。

ただ、先週 AWS の東京リージョンで発生した障害では AWS コンソールを表示できなくなることがしばしばあったので、AWS Personal Health Dashboard 以外のページもチェックしておくほうがよさそうだった。

AWS Service Health Dashboard

AWS Service Health DashboardAWS の全サービスのステータスを見ることができる。

自分が関係しそうなリージョンは東京リージョンくらいなので Asia Pacific のタブだけを見ていたが、Asia Pacific という名前のとおり東京リージョン以外にも香港リージョンとかソウルリージョンなどのサービスステータスが一緒に掲載されている。東京リージョンにしか関心がない自分にはやや冗長さは感じたがページの性格上仕方なさそう。

Twitter

AWS 公式以外からの情報収集手段としては Twitter をチェックしていた。

自分がチェックしているすべての情報収集手段のなかでも一番早く異常が検出できることが多い。外形監視として非常に優秀。

先週の東京リージョンの障害は AWS 公式の発表は「単一の AZ で発生」ということだったが「単一の AZ ってどこなの?」というのが分からなかったのだが、Twitter で「AZ ID が apne1-az4 の AZ で障害が発生してる (らしい)」という情報を得られたので「あ、自分のところも影響ありそう」ということが分かったのは大きかった。

あと、インフラ業務を担当している人との謎の連帯感を感じられるので Twitter はチェックしておいたほうがいいと思う。

AWS Service Health Dashboard] の RSS フィードを Slack に追加

AWS Service Health DashboardRSS フィードを Slack に追加すると AWS のステータスが更新されたときに Slack のチャンネルに post してくれるようになる。

AWS Service Health Dashboard をチェックするのは pull 型の情報収集手段だがこの方法は push 型の情報収集手段になる。 障害発生中といえどダッシュボードに張り付くわけにもいかないことが多いと思うので push 型の情報収集手段はかなり便利。

さいごに

障害は起きないに越したことはないがそんなことはありえないし、最低限これくらいはチェックしておいたほうがよさそうなページをまとめてみた。

iTerm2 のカレントのプロファイルは $ITERM_PROFILE で取得できる。

iTerm2 のカレントのプロファイルの取得方法のメモ。

iTerm2 のカレントのプロファイルの取得方法

iTerm2 のカレントのプロファイルは $ITERM_PROFILE で取得できる。

$ echo $ITERM_PROFILE
my-favorite-profile

カレントのプロファイルを取得できると何が嬉しいのか

iTerm2 のカレントのプロファイルを取得できると何が嬉しいのかというと用途に応じて iTerm2 のプロファイルを切り替えているのを元のプロファイルに戻したいときなどだ。

iTerm2 のプロファイルは echo "\033]1337;SetProfile=<プロファイル名>\a" で切り替えることができるので、SSH でログインするときにログイン先によってプロファイルを切り替えて、SSH 先からログアウトするときに元のプロファイルに戻すシェルスクリプトは以下のように書くことができる。

#!/bin/sh

CUR_PROFILE=$(echo ${ITERM_PROFILE})
echo "\033]1337;SetProfile=$1\a"
ssh "$@"
echo "\033]1337;SetProfile=${CUR_PROFILE}\a"

普段から iTerm2 の Default プロファイルを使っているとき

普段から iTerm2 の Default プロファイルを使っているときは元のプロファイルに戻すところは echo "\033]1337;SetProfile=Default\a" みたいに決め打ちでいいと思う。

Redash からダウンロードできる CSV ファイルの改行コードが CRLF だった。

Redash からダウンロードできる CSV ファイルの改行コードが CRLF なんだー、と思ったのでメモ。

書いた動機

Redash はクエリーの結果を CSV ファイルでダウンロードすることができるので CSV ファイルをいろいろ加工したりしてとても便利なのだが、Redash からダウンロードした CSV ファイルの改行コードが CRLF であることに気が付かず軽くハマった。

自分は忘れっぽいので時間がたつとまたハマりそうなので忘れないように書いておく。

Redash からダウンロードすることができる CSV の改行コードは CRLF

ダウンロードするものがないと始まらないので、おそらく日本の Redash ユーザーの7割くらいはお世話になっただろう kakakakakku さんの redash-hands-oncity テーブルからデータを引っ張ってみる。

こんな感じの雑な SQL で引っ張ってきたデータの CSV ファイルをダウンロードする。

select * from city limit 3;

で、おもむろに file コマンドで見てみると改行コードが CRLF であることが分かる。

$ file city.csv
city.csv: ASCII text, with CRLF line terminators

$ cat city.csv
ID,Name,CountryCode,District,Population
1,Kabul,AFG,Kabol,1780000
2,Qandahar,AFG,Qandahar,237500
3,Herat,AFG,Herat,186800

一見したところこのファイルは人畜無害なのだが、そのままだと CLI で軽く困ったりする。 たとえば、次のような単純なコマンドもおそらく意図しているとおりには動かない。

$ cat city.csv | xargs -I{} echo "**{}**"
**ID,Name,CountryCode,District,Population
**1,Kabul,AFG,Kabol,1780000
**2,Qandahar,AFG,Qandahar,237500
**3,Herat,AFG,Herat,186800

ではどうすればいいかというと、CR をとってしまうのが一番簡単 (たぶん)

$ cat city.csv | tr -d \\r  | xargs -I{} echo "**{}**"
**ID,Name,CountryCode,District,Population**
**1,Kabul,AFG,Kabol,1780000**
**2,Qandahar,AFG,Qandahar,237500**
**3,Herat,AFG,Herat,186800**

Redash からダウンロードする CSV ファイルの改行コードの設定とか Redash のどこかにありそうなものなんだけど、3分くらい探して見つからなかったので雑に CR を削除することにしたときのメモは以上。

どのアベイラビリティゾーン? のやりとりは AZ ID を使うべき理由。

昨日 (8/23) AWS の東京リージョンで大規模な障害が発生した。

piyolog.hatenadiary.jp

Twitter は結構な騒ぎになっていて、インフラと AWS をやり始めたばかりの自分も Service Health DashboardTwitter は結構頻繁にチェックしていたのだが「どうも障害が発生しているアベイラビリティゾーンは apne1-az4 らしい」というツイートを Twitter で見つけた。 apne1-az4 というのは AZ ID というものだということは分かったのだが「ap-northeast-1a みたいなやつとなにが違うのか?」と気になってちょっと調べてみたのをメモしておく。

メモ

とりあえず公式ドキュメント。

docs.aws.amazon.com

公式ドキュメントには以下のように書いてある。

アベイラビリティーゾーンは、リージョンコードとそれに続く文字識別子によって表されます (us-east-1a など)。リソースがリージョンの複数のアベイラビリティーゾーンに分散するように、アベイラビリティーゾーンは各 AWS アカウントの名前に個別にマップされます。たとえば、ご使用の AWS アカウントのアベイラビリティーゾーン us-east-1a は別の AWS アカウントのアベイラビリティーゾーン us-east-1a と同じ場所にはない可能性があります。

ようするにアカウント A の us-east-1a = アカウント B の us-east-1a が成り立つとは限らない、ということだ。

では、アカウント A とアカウント B との間で「障害が発生しているのはどのアベイラビリティゾーンか?」という情報を交換するにはどうすればよいかというと、ここで AZ ID の出番となる。 公式ドキュメントには以下のように書いてある。

アカウント間でアベイラビリティーゾーンを調整するには、アベイラビリティーゾーンの一意で一貫性のある識別子である AZ ID を使用する必要があります。たとえば、use1-az1 は、us-east-1 リージョンの AZ ID で、すべての AWS アカウントで同じ場所になります。

まとめると以下のようになる。

  • 単一アカウント内で情報をやりとりするときは us-east-1a のようなのでやり取りしていい。別に AZ ID を使ってもいい
  • 複数アカウント間で情報をやりとりするときは use1-az1 のような AZ ID でやりとりする必要がある

さいごに

有名サービスやゲームがバタバタと倒れていくなかうちのサービスはほとんど運営に支障はなかったようで大変幸運だった。

Redash のバックアップとリストアのメモ。

サービスの運営で Redash を使っていて運営メンバーは結構カジュアルにクエリとかいじってもらっているので、バックアップとリストアといった運用上必須なところをどうしているかをメモしておく。

はじめに

Redash は 公式の AMI を使って AWS 上に構築しているのでそれ前提で書く。 とはいっても 公式ドキュメント のバックアップ手順は sudo -u redash pg_dump redash | gzip > backup_filename.gz と書いてあるので、ようは Redash が内部で使っている PostgreSQL のバックアップを取得しておけばよい。 同じように、Redash をリストアするときは PostgresSQL のバックアップを Redash が内部で使っている PostgresSQL に流し込めばよい。

バックアップ

バックアップは以下のようなスクリプトを Docker ホスト (= Redash の AMI の EC2 インスタンス) の cron で動かしていて、gzip 圧縮した PostgreSQL のバックアップファイルは S3 へ保管している。

#!/bin/sh

# PostgreSQL のコンテナ ID を取得
CID=$(sudo docker container ls | grep redash_postgres | awk '{print $1}')

# PostgreSQL をバックアップ
sudo docker container exec ${CID} /bin/bash -c 'pg_dump -U postgres postgres | gzip > /usr/local/redash-backup.gz'

# PostgreSQL のバックアップファイルを PostgreSQL コンテナからホスト (EC2) へ持ってくる
sudo docker container cp ${CID}:/usr/local/redash-backup.gz redash-backup.gz

# S3 へ保管
aws s3 cp redash-backup.gz s3://<バケット名>

リストア

リストアは以下のようなスクリプトredash-restore.sh redash-backup.gz のように実行して、スクリプトがエラーなく終わればブラウザから Redash に接続して動作を確認する。

リストアの手順自体は難しいところはないけどそれなりにコマンドを打つし自分は絶対間違えるのでスクリプト化している。

#!/bin/sh

set -x

if [ $# -lt 1 ]; then
    echo 'usage: redash-restore.sh <backup>'
    exit 1
fi
BACKUP_PATH=$(readlink --canonicalize $1)
if [ ! -f ${BACKUP_PATH} ]; then
    echo "${BACKUP_PATH} not exists."
    exit 1
fi

# Redash 公式 AMI は設定ファイルやボリュームは /opt/redash/ にある
DOCKER_COMPOSE_YML=/opt/redash/docker-compose.yml

# Redash 絡みのコンテナを一旦止める
sudo docker-compose --file ${DOCKER_COMPOSE_YML} down --remove-orphans

# PostgreSQL のバックアップを流し込む先の PostgreSQL コンテナを動かす
sudo docker container run -d -v /opt/redash/postgres-data:/var/lib/postgresql/data -p 5432:5432 postgres:9.5.6-alpine

# PostgreSQL のバックアップファイルをホスト (EC2) から PostgreSQL コンテナへ持っていく
CID=$(sudo docker container ls | grep postgres | awk '{print $1}')
sudo docker container cp ${BACKUP_PATH} ${CID}:/usr/local/redash-backup.gz

# Redash のデータベースを削除 & 再作成
sudo docker container exec ${CID} /bin/bash -c 'psql -c "drop database if exists postgres" -U postgres template1'
sudo docker container exec ${CID} /bin/bash -c 'psql -c "create database postgres" -U postgres template1'

# バックアップを PostgreSQL へ流し込む
sudo docker container exec ${CID} /bin/bash -c 'zcat /usr/local/redash-backup.gz | psql -U postgres -d postgres'

# バックアップの流し込みに使った PostgreSQL のコンテナはもう使わないので止める
sudo docker container stop ${CID}
sudo docker container rm ${CID}

# Redash 絡みのコンテナを動かす
sudo docker-compose --file ${DOCKER_COMPOSE_YML} up --detach

参考ページ

まとめ

いまのところは Redash のクエリやダッシュボードのバックアップは PostgreSQL のバックアップで十分に事足りているが、バックアップとは別に Redash 特にクエリの変更履歴は管理したいなーと思っているので、そっちも調べて運用に取り込んでいきたい。

AWS Lambda を使って Github リポジトリのクローン数の草を Pixela に生やすメモ。

注意: この記事の中で利用している pixela-client-go はアクティブな開発は止まっています。 pixela-client-go の後継ライブラリの pixela4go を利用する記事 AWS Lambda と pixela4go を使って Github リポジトリのクローン数の草を Pixela に生やすメモ。を参照してください。


AWS Lambda を使って Github リポジトリのクローン数の草を Pixela に生やすメモ。

Github は Web UI や API を使ってリポジトリのクローン数を取得することができるのだが、どちらの方法を使っても過去2週間分のクローン数しか見られない。そこで、AWS Lambda を使って定期的に Github のクローン数を取得して Pixela に記録することにした。Pixela は操作が簡単な上に草を生やすことができるのでビジュアル面でも Github のクローン数を記録するという用途にぴったりだ。

実際に pixela-client-go のクローン数を Pixela に記録してみるとこんな感じになる。

AWS Lambda を使って Github リポジトリのクローン数の草を Pixela に生やすまでにしたことをメモしておく。

はじめに

使うもの

前提条件

もし AWS のアカウントや Github のアカウントがないとか API トークンがないなら事前に取得しておく。 このあたりの情報は公式サイトはもちろんネット上にたくさんあるので困ることはないと思う。

やること

  • Pixela アカウントとグラフを作成
  • Lambda 関数の作成
    • トリガーを追加
    • 関数パッケージの作成
    • 関数パッケージのアップロード
    • 環境変数の設定
    • Lambda 関数の動作確認

Pixela アカウントとグラフを作成

Github のクローン数を Pixela に記録するために Pixela アカウントの作成と Pixela グラフを作成する。 公式ブログを見ながら作っていくと迷うことなく作れると思う。

blog.a-know.me

Lambda 関数の作成

関数の作成

Github のクローン数を取得して Pixela に記録する関数を作成していく。

AWS コンソール を表示して Lambda > 関数 > 関数の作成 ボタンを押す。

f:id:ebc_2in2crc:20190813155338p:plain:w600

一から作成 を選んで関数名を入力する。 ランタイムは Go 1.x を選んで実行ロールは 基本的な Lambda アクセス権限で新しいロールを作成 を選ぶ。

f:id:ebc_2in2crc:20190813155717p:plain:w600

トリガーを追加

作成した関数を定期的に動かすためにトリガーを設定していく。

Designer の トリガーを追加 を押してトリガーの設定画面を表示する。

f:id:ebc_2in2crc:20190813162049p:plain:w600

トリガーの設定画面は以下のように設定する。

  • トリガーの選択は CloudWatch Events を選択
  • ルールは 新規ルールの作成 を選択
  • ルール名は 0100-utc-everyday を入力
  • ルールタイプは スケジュール式 を選択
  • スケジュール式は cron(0 1 * * ? *) を入力

f:id:ebc_2in2crc:20190813162204p:plain:w600

トリガーの有効化 にチェックを付けて 追加 ボタンを押す。

f:id:ebc_2in2crc:20190813162826p:plain:w600

トリガーの設定はこれで完了。この設定をしておくと Lambda 関数が毎日1時 (UTC) に自動的に実行するようになる。

関数パッケージの作成

Lambda で動かす関数パッケージを作っていく。

以下のコードを main.go というファイル名で保存する。

package main

import (
    "context"
    "errors"
    "os"
    "strconv"
    "time"

    "github.com/aws/aws-lambda-go/lambda"
    "github.com/ebc-2in2crc/pixela-client-go"
    "github.com/google/go-github/github"
    "golang.org/x/oauth2"
)

type MyEvent struct {
    Time time.Time `json:"time"`
}

const timeFormat = "20060102"

var (
    githubToken = os.Getenv("GITHUB_TOKEN")
    githubUser  = os.Getenv("GITHUB_USER")
    githubRepo  = os.Getenv("GITHUB_REPO")

    pixelaToken = os.Getenv("PIXELA_USER_TOKEN")
    userName    = os.Getenv("PIXELA_USER_NAME")
    graphID     = os.Getenv("PIXELA_GRAPH_ID")
)

func main() {
    lambda.Start(HandleRequest)
}

func HandleRequest(event MyEvent) (string, error) {
    yesterday := event.Time.Truncate(time.Hour * 24).Add(-time.Hour * 24)
    cloneCount, err := retrieveCloneCount(yesterday)
    if err != nil {
        return "failed to retrieve clone count", err
    }

    if err := registerCloneCount(yesterday, cloneCount); err != nil {
        return "failed to register clone count", err
    }

    return "success", nil
}

func retrieveCloneCount(t time.Time) (int, error) {
    client := newGithubClient(githubToken)
    clones, resp, err := client.Repositories.ListTrafficClones(
        context.Background(),
        githubUser,
        githubRepo,
        &github.TrafficBreakdownOptions{Per: "day"},
    )
    if err != nil {
        return 0, err
    }
    _ = resp.Body.Close()

    d := t.Format(timeFormat)
    for _, c := range clones.Clones {
        if c.GetTimestamp().Format(timeFormat) == d {
            return c.GetCount(), nil
        }
    }
    return 0, nil
}

func newGithubClient(token string) *github.Client {
    oauthCli := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(&oauth2.Token{
        AccessToken: token,
    }))
    return github.NewClient(oauthCli)
}

func registerCloneCount(date time.Time, cloneCount int) error {
    client := pixela.NewClient(userName, pixelaToken)
    pixel := client.Pixel(graphID)
    result, err := pixel.Create(date.Format(timeFormat), strconv.Itoa(cloneCount), "")
    if err != nil {
        return err
    }
    if result.IsSuccess == false {
        return errors.New(result.Message)
    }

    return nil
}

AWS Lambda にアップロードする zip ファイルを作成する。

$ go build -o hello main.go && zip hello.zip hello
  adding: hello (deflated 53%)

MacWindowsコンパイルするときは GOOS=linux を付けて Linux 向けにクロスコンパイルする。

$ GOOS=linux go build -o hello main.go && zip hello.zip hello
  adding: hello (deflated 53%)

main.go がやっていること

ざっくり書くと「プログラムの実行日付の前日の Github リポジトリのクローン数を取得」して「プログラム実行日付の前日の (PIxela の) Pixel に記録」している。 当日のクローン数を当日の Pixel に記録したいときは yesterday := event.Time.Truncate(time.Hour * 24).Add(-time.Hour * 24) のあたりを適当にいじるとよい。

関数パッケージのアップロード

作成した zip ファイルを AWS にアップロードしていく。

コードエントリタイプは .zip ファイルをアップロード を選択して アップロード ボタンを押してさきほど作成した zip ファイルを AWS にアップロードする。

f:id:ebc_2in2crc:20190813163945p:plain:w600

環境変数の設定

関数パッケージが使う環境変数アップロード ボタンの下の 環境変数 に設定していく。

API トークンなど重要な情報を含んでいるので実際は暗号化するほうがよい。

環境変数 設定する値
GITHUB_TOKEN GithubAPI トーク
GITHUB_USER Github のユーザー名
GITHUB_REPO クローン数の草を生やしたいリポジトリ
PIXELA_USER_TOKEN Pixela の API トーク
PIXELA_USER_NAME Pixela のユーザー名
PIXELA_GRAPH_ID クローン数の草を生やす先のグラフの名前

これで Lambda 関数の作成は完了。 続いて Lambda 関数がちゃんと動くか確認していく。

Lambda 関数の動作確認

関数パッケージと環境変数が正しく設定されていて Lambda 関数がちゃんと動くかどうかを確認していく。

画面の上のほうにある テストイベントの設定 を押して テストイベントの設定 画面を表示する。

f:id:ebc_2in2crc:20190813164827p:plain:w600

テストイベントの設定 画面は以下のように設定する。

  • 新しいテストイベントの作成 を選択
  • イベントテンプレートは Hello World を選択
  • イベント名は test を入力

イベントの JSON は以下を設定する。 2019-08-13 のところはテストをしたい日付、たとえば Github リポジトリがクローンされたことが分かっている日、あるいは Github リポジトリがクローンされなかったことが分かっている日などを入力する。

{
  "time": "2019-08-13T01:00:00Z"
}

入力し終わったら 保存 ボタンを押してテストイベントを保存する。

次は作成したテストイベントを使って Lambda 関数の実際の動きを確認していく。

画面の上のほうにある テスト ボタンを押してテストイベント test を使って Lambda 関数を動かす。

f:id:ebc_2in2crc:20190813170555p:plain:w600

実行結果: 成功 が表示されれば Lambda 関数は問題なく動いている。

f:id:ebc_2in2crc:20190813170815p:plain:w600

以上。

参考ページ

blog.a-know.me

developer.github.com