Sionの技術ブログ

SREとして日々の学習を書いて行きます。twitterは@sion_cojp

AWS S3の特定パスにある大量のオブジェクトをGoで並列に別バケットに移動する

f:id:sion_cojp:20210628192147p:plain:w0

はじめに

チカクのまごちゃんねるというサービスでタイトルのような作業があったので、

そのとき使ったコードのサンプルの共有と背景を若干伏せて紹介してます。

記事を見て、何か良い知見があれば教えてください!

TL;DR

  • 数千万オブジェクト/数百万TBをGoで並列移動(コピー)した
  • DeepArchiveにして70%削減できたよ
  • 初期コストはかかったけど、すぐペイするよ

バケット構成

bucket/ に1 ~ Nの連番(id)があり、その配下には共通したディレクティブ(a ~ d)が入ってます。

aディレクトリにaa/bbというディレクトリがあり(もしくは片方ない)、その中にオブジェクトが入ってる状態です。

今回は「bucket/{id}/a/aa」「bucket/{id}/a/bb」の中身を全部他のバケットに移す作業です。

# bucket
bucket/
├── 1
│   ├── a
│   │   ├── aa
│   │   │   └── hoge.txt
│   │   └── bb
│   │       └── fuga.txt
│   ├── b
│   ├── c
│   └── d
├── 2
│   ├── a
│   │   └── bb
│   │       └── piyo.txt
│   ├── b
│   ├── c
│   └── d
.
.
.
└── 10000
│   ├── a
│   │   │   └── foo.txt
│   │   └── bb
│   │       └── bar.txt
│   ├── b
│   ├── c
│   └── d

Why

移動?

  • awsのコストが高いので、調査して色々と削減する動きをしていたら、s3がコストランキング2位だった
  • とあるバケットは、頻繁にアクセスしないけどSTANDARDで保存してた
  • さらにその中の特定パスのオブジェクト(数千万/数百TB)は1年間で使う事がほぼないけど保存する必要があった
  • 無限にオブジェクトが増えていくバケットだった

    • 一旦STANDARD_IAにしたが、それでもめちゃくちゃコストかかってたので、特定パスをDeepArchiveにすることでコストカットを提案
  • lifeycleで適用しようと考えたが、特定パスが相当な数があったためlifecycleの上限(1000)を超える。またlifecycleは正規表現が使えない

How

進め方

f:id:sion_cojp:20210625151840p:plain:w300

issueに切って頭出し。会議の内容や作業手順が全てissueに残していきました。

PdM/サーバサイド/その他関係するエンジニアに進捗、実行するタイミング、スポットでかかったコストや予想されるコストなど変化する事象を密にコミュニケーションしながら進めてました。

コストの洗い出しとGlacier or DeepArchive

まずトップ10あたりのidに対し、対象の容量の比率を出しました。

そして全体に照らし合わせ、どれくらいの容量とコストがあるのかを出しました。

次にGlaicer/DeepArchiveにしたときの、もし全オブジェクトを取り出した場合のコストを試算したところ、5.3ヶ月に1回取り出すのであれば、DeepArchiveの方が安いことがわかりました。

取り出しコスト =
標準データ取り出しリクエスト(オブジェクト数 / 1000件 * お値段)
+ 取り出し(オブジェクト数 * お値段)
+ GET(オブジェクト数  / 1000件 * お値段) 
+ COPY(STANDARD_IA等にコピー。オブジェクト数 / 1000件 * お値段)

※1000件ごとに値段がかかる。お値段はGlacier/DeepArchiveで違う
ref: https://aws.amazon.com/jp/s3/pricing/

取り出す頻度を加味してDeepArchiveにしたほうが圧倒的に安いという判断になりました。

あとで紹介するのですが、DeepArchiveにする初期コストを試算しておくと完璧だったと思います。

コード

github.com

社内ロジックを伏せてサンプルコードを載せてます。

最初はawscliでやろうとしたのですが、オブジェクトをGETするだけでも全然終わる気配がなかったので、Goで並列化するしかなかったです。

色々あって3日後にstg。5日後にprod実行だったので、1日くらいでベースは出来て、そこからいろんな要望(flag見るとわかるかと)が出て少しずつ修正していった感じです。

社内にGoに精通した人がそんなにいないので、2日目にmeetsでリアルタイムにロジックを説明してレビューしてもらいました。

今までsyncパッケージのWaitGroupで並列化してたのですが、今回はgolang.org/x/sync/ を使ってみてとてもわかりやすくて良かったです。

golang.org/x/sync/ を使ったGoの並列処理 - Sionの技術ブログ

どれくらいかかった?

30並列で、copyが2,3日。deleteが6時間かかりました。

障害が起こってないかの確認

実行時にはアラートチャンネルと顧客からの問い合わせチャンネルを見て、問題がないか確認してました。

終わったときのslack通知

$ vim run.bash

#!/bin/bash -
date > /tmp/s3.log
./bin/s3-move-other-bucket --src src_bucket --dest dest_bucket --parallel 30
echo $? >> /tmp/s3.log
date >> /tmp/s3.log

まず上記のbashを作り、nohup /bin/bash run.bash & でバックグラウンドで動かしました。

終わった時のslack通知には https://github.com/catatsuy/notify_slack を使いました。

$ vim hoge.toml
[slack]
url = "https://hooks.slack.com/services/xxxxxxxxxx"
channel = "#channel名"
interval = "1s"
$ vim watch.bash
#!/bin/bash -
while true
do
        ALIVE=`ps auxww | grep "/bin/bash hoge.bash"  | grep -v grep | wc -l`
        if [ ${ALIVE} = 0 ]; then
                echo "<@Uxxxxx> スクリプト終わったよ!" | /home/ec2-user/notify_slack -c /home/ec2-user/hoge.toml
                break
        fi
        sleep 10
done

2つファイルを作って nohup /bin/bash watch.bash & でバックグラウンドで動かしました。

こんな感じの通知が来てくれました。

f:id:sion_cojp:20210628185218p:plain

移動時と移動後の値段

Amazon S3 の AWS 請求および使用状況レポートを理解する - Amazon Simple Storage Service

移動時は$8200ほどかかりましたが、移動後は70%のコスト削減ができました。(具体的な数字は伏せてますが、すぐペイ出来る計算になります)

特にかかったのが、いつでもrollbackできるようにSTANDARD_IAのままコピーして、lifecycle適用で一気にDeepArchiveにしたので、そのときのAPN-EarlyDelete-SIAが$4000かかりました。

通信費に関しては、EC2上で動かしたので無料です。

初期コストはある程度かかるのは分かってたものの、プロジェクト進める前に具体的な数字を出した方が良かったですね。反省。

起こった問題について

1. インスタンス耐えれなかった問題

t2だとネットワーク。c5.largeだとメモリ数(OOM Killer)。

それぞれ並列(30)に耐えれなくてc4.4xlargeにしました。

2. オブジェクト自体のdiffは諦めた

src/destのオブジェクト自体の比較をしてより精度を高めたかったのですが、

https://github.com/google/go-cmp 使ってみたが、行毎で差分が出てほぼ全部差分になり思ったdiffにならず。

色々あって時間が足りなかったので、トータルオブジェクト数の一致性と、軽く目diffだけでOKと判断しました。

コードは若干diff実装の名残りが残ってるかもしれないです。

3. Aws::S3::Errors::SlowDownエラー

https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-resolve-503-slowdown-throttling/

実行者側でリクエスト制御するまで、aws側でslow downエラー(制限をかける)を返す事があるらしい。

雑に並列数(30)に下げて対応しました。

4. Go側の並列時にmapの取り扱い

fatal error: concurrent map read and map write

上記エラーが発生し、どうやらmapを並列で取り扱う場合はmutex.Lock / Unlockが必要だったということ。

まぁそれはそうだよねという感じで実装しました。

ref: mapの競合状態のはなし - 今川館

golang.org/x/sync/ を使ったGoの並列処理

はじめに

昔はsyncパッケージを使う必要があり、若干分かりづらいコードになってましたが、

golang.org/x/sync/ で実装すると分かりやすく改善されてたのでサンプルコードをおいてみます。

並列数は3に制限して実行するコードになってます。

syncパッケージのコード

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    num := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    wg := &sync.WaitGroup{}
    semaphore := make(chan struct{}, 3)

    for _, v := range num {
        wg.Add(1)
        semaphore <- struct{}{}
        go func(v int) {
            defer func() {
                <-semaphore
                defer wg.Done()
            }()
            Hoge(v)
        }(v)
    }
    wg.Wait()
}

func Hoge(i int) {
    fmt.Printf("start %v\n", i)
    time.Sleep(3 * time.Second)
    fmt.Printf("stop %v\n", i)
}

golang.org/x/sync/ のコード

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "golang.org/x/sync/errgroup"
    "golang.org/x/sync/semaphore"
)

func main() {
    num := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    sem := semaphore.NewWeighted(3)
    eg := errgroup.Group{}

    for _, v := range num {
        if err := sem.Acquire(context.Background(), 1); err != nil {
            fmt.Printf("failed to acquire semaphore: %v\n", err)
            break
        }
        v := v
        eg.Go(func() error {
            defer sem.Release(1)
            Hoge(v)
            return nil
        })
    }

    if err := eg.Wait(); err != nil {
        log.Fatal(err)
    }
}

func Hoge(i int) {
    fmt.Printf("start %v\n", i)
    time.Sleep(3 * time.Second)
    fmt.Printf("stop %v\n", i)
}

RDS MySQLをAuroraに移行したお話

f:id:sion_cojp:20210618154937p:plain:w0

はじめに

チカクのまごちゃんねるというサービスでMySQLをAuroraに移行したお話です

中間のpendingタイムを除けば、仕様把握含め2ヶ月くらいですね。

TL;DR

  • メンテナンスモードを作った
  • 無駄に分割していたRDSも1つに統合した
  • MySQLからAuroraに移行した

Why

Aurora?

その他の改善項目

  • stgとprodで仕様が違うので統一したい
    • prodだけなぜかRDS分割して運用してる -> 1つに統一したい
    • DBの名前が違う
  • ユーザのパスワードの強化、接続元の制限をしたい
  • RDS接続用のprivateドメインを作ってインフラ側で制御できるようにしたい

過去に1回チャレンジして失敗している

一度prodのaurora移行にチャレンジしたとき、AWS DMS を使ってデータを移行しようとしたら、インデックスが正常に移行できずに失敗したという背景がありました。

また同じことする不安を持つより、今回は下記のような判断にしました

  • MySQLからAuroraリードレプリカを作成 -> 昇格させる
  • 他RDSからのDB移行は、昇格させたAuroraにmysqldumpで移行

How

進め方

f:id:sion_cojp:20210616162917p:plain

github issueをベースに進めました。会議で話した内容、手順など全てがこのissueに載ってます。

移行中のデータ欠損対策

メンテナンスモードがないため、移行中にリクエストがきてデータ欠損する懸念がありました。

またサービスダウンが発生するので、その時の表示がユーザに不親切な状態になります。

対策としては、s3hosting / ALBを介したapi の2つメンテナンスモードを用意して、slackからON/OFF出来るようにしました。

f:id:sion_cojp:20210616171759p:plain:w300

f:id:sion_cojp:20210616171812p:plain:w300

peeringの確認

VPCと新VPCが存在する環境なので、そこのpeering状況の確認が必要です。

動作チェックとして、どちらのVPCからもAuroraリードレプリカに対してアクセスできるかを確認しました。

RDS接続用のprivateドメインを作ってインフラ側で制御する

VPC内のみ利用するprivateドメインを作成しました。

これをすることで、RDSを切り替える際のdeployを省き、

またTTL=0(接続するたびにドメイン解決)にすることで、インフラ側で接続先を切り替えることができます。

その分速度は若干遅くなりますが、運用メリットのほうが大きいです。

terraformで書くとこんな感じですが、terraformだとTTL=0にセットできないので、コンソールで設定してterraformに反映します。

/* private ドメイン作成 */
resource "aws_route53_zone" "private" {
  name = "private"

  vpc {
    vpc_id = data.aws_vpc.xxxx.id
  }
}


/* DBのwriterの例 */
resource "aws_route53_record" "rds-xxx-writer" {
  zone_id = aws_route53_zone.private.zone_id
  name    = "rds-xxx-writer"
  type    = "CNAME"
  ttl     = "0" # providerが対応してないため、1に設定 -> 手動で0に設定し、terraformも0にする対応をしてる
  records = [data.aws_rds_cluster.xxx.endpoint]
}

dumpの手順

コマンド自体は簡単で、気をつけるのは --single-transaction でロックかけないようにdumpしましょう。

大きなDBに対し事前にdumpがどれくらいかかるか検証したほうが良いでしょう。

-- DB全体の容量確認
SELECT 
    table_schema, sum(data_length) /1024/1024 AS mb 
FROM 
    information_schema.tables  
GROUP BY 
    table_schema 
ORDER BY       
    sum(data_length+index_length) DESC;

-- 今回の移行対象 + 一番大きなDBでdumpして時間を測る
-- UserName / HostName / xxx は適宜変えてください
mysqldump -uUserName -hHostName -p --quote-names --opt --single-transaction --skip-column-statistics --hex-blob -R --order-by-primary xxx > xxx.sql

-- ちなみに流し込む時のコマンド
-- UserName / HostName / Password / DBName / xxx は適宜変えてください
mysql -uUserName -hHostName -p Password DBName < xxx.sql

DB名を変更する手順

DB名を変更するためには、移行先のDBを用意し、RENAMEコマンドで対応しました。

RENAMEはcopyが走らないのですぐ終わります。

-- NewDBName / OldDBName は適宜変えてください
create NewDBName

RENAME TABLE OldDBName.TableName TO NewDBName.TableName

ユーザ通知や社外調整

こちらは然るべき方からよしなにやりました。

手順書

実際の手順書は下記となりました。(具体的な内容と、題目も若干伏せます)

# 事前作業
- ユーザ作成
- auroraリードレプリカを作成(2日目)

# 当日作業1日目
- 1. s3 hostingをメンテにする
- 2. 各batch止める
- 3. Lamdaのトリガーを無効化する
- 4. ALBをメンテにする
- 5. 動画のキューが0なことを確認
- 6. スナップショットを取る
- 7. 5.6 -> 5.7にmysqlをあげる
- 8. 確認作業
- 9. Lamdaのトリガーを有効化する
- 10. メンテ解除

# 当日作業2日目
- 1. s3 hostingをメンテにする
- 2. 各batch止める
- 3. Lamdaのトリガーを無効化する
- 4. ALBをメンテにする
- 5. 動画のキューが0なことを確認
- 6. mysqldump
- 7. auroraをmasterに昇格
- 8. dumpファイルをRDSにDBを流し込む
- 9. DB名変更
- 10. Route53で一気にドメイン切り替える
- 11. DB名、ユーザ、パスワード、ホスト名変更デプロイ
- 12. 確認作業
- 13. Lamdaのトリガーを有効化する
- 14. メンテ解除

想定外のトラブル集

5.6だとAuroraリードレプリカが作成できない

5.7から対応してるので、一旦5.6 -> 5.7にmysqlを上げる必要がありました。

スナップショットに時間がかかった

stgだけスナップショットに1時間かかりました。

これはインスタンスサイズが低すぎて時間がかかった可能性がありますね。

prodが5.7になるまで時間がかかった

2時間経っても終わらなかったです。

こちらはawsに問い合わせたところ、aws側のトラブルだと判明し、そこから迅速に対応していただけました。

default parameterだと設定値が変更できない

一時的にアップデートした5.7のときにdefaultパラメータを利用したのですが、

max_connections値が少なく2日目に行くまでに若干エラーが発生しました。

defaultなので変更できず、上限値と通常のリクエストを見て1日は許容という判断になりました。

最後に

状況把握をするのにかなり時間を費やしたと思います。

terraformに残ってなかったので、awsのコンソールとgithubを見ながら現状を把握するのはとても大変でした。

そこからissueを立て、流れや手順を書いた後、関係者に的確にレビューしていただいたので、スムーズに終わることが出来たのがとても感謝です!

株式会社チカクに入社しました

2021/3月に株式会社チカクに転職しました。

あまり入社エントリーは書かないのですが、1ヶ月経った節目として書いてみます。

自己紹介

元プロゲーマーでアジアチャンピオンです。

会社歴としては、下記の通りです。

会社名 職務
ビットアイル ハードウェア周り、ネットワークエンジニア
リブセンス インフラ / サーバサイド
エウレカ SRE
fluct(VOYAGE GROUP) SRE
FOLIO SRE
タイミー SRE/DRE/コーポレートエンジニア
チカク SRE

チカクってどんな会社?

f:id:sion_cojp:20210319140450p:plain:w500

まごちゃんねる というサービスを提供してます。

"撮れたてのまご動画を スマホから実家のテレビへ"

Wi-Fi機能付きのテレビで見れるフォトフレーム を(今は)提供してる感じです。

なぜチカク?

@yoshihiro_1992と前職のタイミーで知り合い、副業でチカクを手伝ってた時期がありました。

去年の12月からtwitterで転職先探してたところに改めて声をかけてもらいました。

そこから色々な社員の方と話す機会をいただいて「とても素敵だな〜」と思う反面、「今後の戦略どうなってるの?」といったところが正直不安でした。

が、 @yoshihiro_1992がいるということは、どんどん良いサービスに発展していく道筋があるんだろうなと思って、海外事例とか色々と調べて働きたいなと思いました。

調べたお話を少しだけ

https://coralcap.co/wp-content/uploads/2019/11/agetech.png

ref: https://coralcap.co/2019/11/agetech-startups/

高齢者向けのスタートアップはこんな感じ。

高齢者が多い日本ではあまりテック化されてないので、まだブルーオーシャンかと思います。

また、まごチャンネルの技術は高齢者以外にも適用できるので、戦略次第でさらに幅が広くなるのが強みだと思います。

その他の決め手の要因

知人が似たようにデバイスを提供する会社で働いてて、ユーザ(Customer)の声がストレートに聞ける環境はとても楽しそうだと思ったのが1つ。

バイスを持ってる会社は強いし、技術的に面白そうだと思ったのも1つ。

あと自分自身、親とは色々あってほぼ無縁だった人間なのですが、この年齢(33歳)になるにつれ、親も老いていき。

数年振りに会った時、寿命というワードが思い浮かぶくらい老けたのを見て、そこから親孝行を徐々にし始めた経緯もあります。

私ですら親孝行をし始めるなら、世の中はもっといる。から手助けになりたいなぁと思うことが増えた次第です。

入社後の話

環境

今はフルリモートで働いてます。

自粛明けたらある程度出社しようかなと思いますが、自宅のほうがスピード出そうなので悩み中です。

朝は10:00開始で今まで裁量労働だった身としてはキツいイメージでしたが、この歳(33)になると起きれてしまうので私は苦じゃないですね。

やってること

大きなポイントだと

  1. 既存アーキテクチャの洗い出し
  2. 新規アーキテクチャへの刷新(コンテナ化)
  3. terraformに落とす
  4. コスト削減

費用としてはEC2(オーバースペックに運用)が大きくかかっており、オートスケーリングやCI/CD周りなどを考慮すると、なるべくコンテナ化したい気持ちで、Fargateに移行するための検証やmodule作成を行ってます。

EKSは一旦Fargate挟んでから考えます。

deployも、slackからdeployする仕組みを用意しました。

コンテナ移行するための材料は揃ったので、今は既存と新規アーキテクチャの全体設計図を書いてます。

terraformをapplyしても差分が出てたり、そもそもterraform化されてない部分も多いので、落とし込みたいですね。

コストもめちゃくちゃかかってるな〜と思って、S3の料金を削減する案もreviewしてもらい、いつやるかまでのissueを切ったりしました。

datadogもコストがかかっており、また契約更新のタイミングなので、精査して手を打っているところです。

楽しいこと

既存アーキテクチャはよく出来てるので勉強になることが多いです。

あとユーザの指摘がわかりみが深いので、頑張らないと!という気持ちになります。

大変なこと

あまりドキュメントが無かったり、terraformでコード化されてなかったりリバースエンジニアリングが大変です。

awsアカウントとある程度のterraformをポンと渡されて「どうぞ!」みたいな状態だと私は思ってます笑

slackやgithubやnotionをかなり漁って理解することができてますが、それでも背景が見えづらい箇所はかなりきついですね。

あとはservice、cron、batch、job queue周りを安全に移行するため、中で何がどう動いてるか、railsやネイティブ、端末側のコードを読んで把握していく作業をしてます。

複雑ではありますが、今行ってる全体アーキテクチャ図に落とし込んでしまえば、後から入る人でもすんなり参画できると思います。

私自身、1ヶ月経った今はほぼ把握できてる(と思います。)

今後やっていきたいこと

コンテナ化はもちろんのこと、既存システムを全てterraformに落とし込んだり、SLOをさらによく定義したり。

スピード改善にも着手していきたい気持ちです。

データマネジメントも改善の余地が多くあるので、、DRE業務も合わせて行っていきたいですね。

AWS S3 Glacierの取り出しをRubyで検証してみる

f:id:sion_cojp:20210316152558p:plain

What

  • S3のGlacier, Glacier Deep Archiveがあるが、実際に取り出したことがあまりない
  • 取り出すにはリクエストが必要
  • 今働いてる chikakurailsをメインにしてるので、rubyで検証しました

Glacier周りの料金や仕様

f:id:sion_cojp:20210316145443p:plain:w400

ref: https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/restoring-objects.html

やったこと

  • s3にオブジェクト(動画)を保存
  • 動画は https://www.home-movie.biz/free_movie.html から拝借しております
  • 下記のように3種類ストレージクラスを用意し、ダウンロードしてみる
    • STANDARD
    • Glacier...今回は迅速取り出し(Expedited)をやります
    • Deep Archive

f:id:sion_cojp:20210316143845p:plain

今回使ったコード

Gemfile

source 'https://rubygems.org'

gem 'aws-sdk-s3'

main.rb

### 秘匿情報はxxxxxで隠してます
### awsにはsaml認証経由でアクセスしてます
require 'aws-sdk-s3'

ENV['AWS_REGION'] = "ap-northeast-1"

def main(object_key)
  bucket_name = 'xxxxx'
  local_path = "./#{object_key}"
  role_arn = "arn:aws:iam::xxxxx:role/xxxxx"
  role_session_name = "xxxxx"

  sts_client = Aws::STS::Client.new(profile: 'saml')
  role_credentials = Aws::AssumeRoleCredentials.new(
    client: sts_client,
    role_arn: role_arn,
    role_session_name: role_session_name
  )

  client = Aws::S3::Client.new(credentials: role_credentials)

  s3_download(client, bucket_name, object_key, local_path)
end

# glacierはrestoreしてからダウンロードする
def s3_download(s3_client, bucket_name, object_key, local_path)
  # storageクラスによってはrestoreする必要があるので、取得する
  resp = s3_client.list_objects_v2(
    {
      bucket: bucket_name,
      prefix: object_key,
    }
  )

  case resp.contents[0].storage_class
  when "STANDARD"
  when "GLACIER"
    restore(s3_client, bucket_name, object_key, local_path, "Expedited")
  when "DEEP_ARCHIVE"
    # こいつだけrestoreが終わるまで下記エラーが出て実行できない
    # Error getting object: Object restore is already in progress
    restore(s3_client, bucket_name, object_key, local_path, "Standard")
  end
  download(s3_client, bucket_name, object_key, local_path)

rescue StandardError => e
  puts "Error getting object: #{e.message}"
end

# glacierはrestoreする
def restore(s3_client, bucket_name, object_key, local_path, tier)
  s3_client.restore_object(
    {
      bucket: bucket_name,
      key: object_key,
      restore_request: {
        days: 1,
        glacier_job_parameters: {
          # https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/restoring-objects.html
          # Standard...標準取り出し
          # Bulk...大容量取り出し
          # Expedited...迅速取り出し。deep archiveは対応してない
          tier: tier
        },
      },
    }
  )
end

# download
def download(s3_client, bucket_name, object_key, local_path)
  s3_client.get_object(
    response_target: local_path,
    bucket: bucket_name,
    key: object_key
  )
end

# 動作確認用
def list_bukets(s3_client)
  puts s3_client.list_buckets
end

# 動作確認用
def list_objects(s3_client, bucket_name)
  s3_client.list_objects(bucket: bucket_name).contents.each do |object|
    p object.key
  end
end

# STANDARD
main(mov_hts-samp001.mp4)

# Glaicer
main(mov_hts-samp002.mp4)

# Deep Archive
main(mov_hts-samp003.mp4)

実行

$ bundle install --path vendor/bundle

# 最初はglacier両方ともエラー
$ bundle exec ruby main.rb
Error getting object: Object restore is already in progress
Error getting object: Object restore is already in progress

# 1分後くらい
$ bundle exec ruby main.rb
Error getting object: Object restore is already in progress

# Glacierはダウンロードできたけど、Deep Archiveはだめだった
# 30分すぎても取り出せないので諦めた

結論

terraform 0.13でローカルproviderを利用する方法

はじめに

terraform 0.13からproviderが https://registry.terraform.io/ から配布され、下記のように記述すると自動取得するようになりました。

# ref: https://www.terraform.io/upgrade-guides/0-13.html#explicit-provider-source-locations
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 2.12"
    }
    newrelic = {
      source  = "newrelic/newrelic"
      version = "~> 2.1.1"
    }
  }
}

0.13でprovider開発などでローカルで利用する場合や、まだregistryに上がってないcommunity providerはどうするのか?

0.12では ~/.terraform.d/plugins に置いてました。

しかし、これをやっても読み込めずエラーが出てしまいます。それを私が実際にどうやって解決したかを書きます。

他に良い方法を知ってる方がいらっしゃれば別途記事を書いてもらえると有難いです。

今回は https://github.com/anasinnyk/terraform-provider-1password を例として紹介します。

1. providerのバイナリを配置します

### https://github.com/anasinnyk/terraform-provider-1password/releases/tag/1.1.0
### をダウンロードして下記のように配置します。このファイル名形式じゃないとダメです
/Users/sion_cojp/.terraform.d/plugins/registry.terraform.io/hashicorp/onepassword/1.1.0/darwin_amd64/terraform-provider-onepassword_v1.1.0


### hashicorp じゃないと下記エラーが出ます
# Error: Failed to query available provider packages
# 
# Could not retrieve the list of available versions for provider
# anasinnyk/onepassword: provider registry.terraform.io/anasinnyk/onepassword
# was not found in any of the search locations
# 
# - /Users/sion_cojp/.terraform.d/plugins

2. required_providerを書きます

$ vim backend.tf
terraform {
  backend "s3" {
    必要であれば書く
  }

  required_providers {
    onepassword = {
      source  = "hashicorp/onepassword"
      email = "sion_cojp@xxxxx"
      password = "xxxxxxx"
      secret_key = "xxxxxx"
      subdomain = "organization_name"
    }
  }
}

3. 1passwordにcliからsign inしてセッションキーをexportします

### signinしたらOP_SESSIONが出力されます
$ op signin organization_name.1password.com sion_cojp@xxxxx

export OP_SESSION_organization_name="xxxxxxxx"

### OP_SESSION_myに変えてexportする必要があります。理由はわかりませんが、やらないと下記エラーのどれかがでます
# 1. Error: email, password or secret_key is empty and environment variable OP_SESSION_my is not set
# 2. Output: [ERROR] 2020/10/13 17:07:29 You are not currently signed in. Please run `op signin --help` for instructions
export OP_SESSION_my="xxxxxxxx"

4. applyやshowをします

$ vim main.tf
data onepassword_user this {
  email = "sion_cojp@xxx"
}

### initでpluginの場所を指定するのがポイント。絶対パスじゃないとダメです
$ terraform init -plugin-dir=/Users/sion_cojp/.terraform.d/plugins
$ terraform show
# module.users.data.onepassword_user.this:
data "onepassword_user" "this" {
    email     = "sion_cojp@xxxxxx"
    firstname = "Shohei Koyama"
    id        = "xxxxx"
    state     = "A"
}

Reference

タイミーのEMとしてどのようにマネジメントしているか

はじめに

16歳からゲーマーとしてずっとチームマネジメントをやっており、

自身の強みなので本腰入れてやりたいという思いで1月にタイミーに入社しました。

結果、今はSREマネージャー(2人) + BI基盤チームマネージャー(3人) + コーポレートエンジニア(3人)として働いてます。

そんなにメンバーはいませんがマネージャーはやれてると思ってるので、色々と書いてみたいと思います。

そんなに掛け持ちして大丈夫なの?

スタートアップは仕方ないです。ぜひ強い方募集してます。

自分の脳内の長期計画は全てアウトプットしてチームと精査してるので出来る人がやればいい状態です。

私自身マネジメントしながら、業務でコードも書いてレビューもしてるので、自身の時間を作るために自走できるチームを作り上げるためのマネジメントでもあります。

マネージャーとしてどういう動きしてるの?

- ローテーション可能のものとする
- マネージャーを無くす意思って良いチームを
- 1on1でフィードバックをもらいましょう
- チーム内で最適な人がやりましょう

基本方針はこれです。(私がマネジメントしてるチームのオンボーディングに書いてます)

ローテーションは、それが出来れば理想的だなぁと思ってます。

なので、チームの権限は同じで、by nameで呼ばれた会議やメッセージはチームメンションに促してます。

もちろんベンダーとのメールのやりとりはslackに飛ばしてます。

あとは拾うべき役割の人がやってくれるでしょう。

私はマネージャーというのは偉い立場ではなく、あくまで役割だと思ってるのでチームメンバーと同じ扱いでいいと思ってます。

1on1

チームメンバーと1on1をまずは週1。徐々に週2くらいの間隔で30分行なってます。

質問は、その人のslackでの動きや発言の仕草、仕事ぶりを見て「その人が今どう悩んでるか」を分析し、それを踏まえて聞きたいことを考えてます。

もちろん1on1をやる前に項目を毎回伝えてます。またテンプレートで聞く部分もあります。

最近は

「もし10万円与えられたら、より効率的なリモートワークをするには何に使いますか?」

「変えたいと思ってる会社全体の文化はありますか?」

とか聞いたりしました。

ちなみに最初の1on1で送るメッセージはテンプレートでこれです。

### 最初なので1週間に1日/30分ほど作っていきたい気持ちです。
### そこから徐々に頻度を下げていきます
- キャリア
- 今後の休暇や働き方
- 目標とアクション決定
- 目標を阻害する問題に対して助けられること
- 時間があればその他

なぜ1on1をやるの?

# メンバー向け
- チームの意思を統一するためのコーチング
- チームワークのためのコーチング
- キャリア開発
- 可能性を広げていく

# 自身向け
- メンバーを知り、チームの可能性が広がるピースを考える(採用)
- メンバーがやりたいことを知り、その役割に当てはめる
- 自身のフィードバックをもらう
- 会社やチームがより成長するアドバイスをもらう

などを意識してます。

「良いチームを作るためにメンバーの変化に柔軟に対応して常にベストな状態にする」ために、個性やステータス(特に体調不良やモチベーション)を理解して、チームにどう適用するか戦略を立てていく。

ゲームもそうですが、他スポーツもそうですね。

意思の統一?

意思の統一 = ベクトルが同じかどうかです。

無駄にだらだらコミュニケーション(意思疎通)をやるより、まず先に意思を統一すれば相互理解も早まってシンプルに解決しやすい思ってます。

そうすることで、必要なときに助けを求めやすくなるし、ダイバーシティーなチームでも回答も一致しやすくなります。そのためには相手が自身や相手がどんな人かを理解する必要があります。

- 自分がどういうポジションで動きたいか、動いてるか理解して働きましょう
- 相手がどういう役割なのか、相手がどういう思考をする人なのかを意識しましょう
- 仕事には感情入れずシンプルに会話しましょう

シンプルな会話はリモートワークにおいて非常に効果的なテクニックであり難しいと思ってます。

「会話の途中途中に絵文字はなるべく使わない」「できる限り短い文章にする」など色々あります。(ただし雑談は別

これを意識し、ベクトルが同じであれば意見はスピーディーに収束すると思ってます。

また「多部署に対して、同じ意見を言える」「役割を理解して連携し合う」ところも重視したいですね。

どんなチームを目指してるの?

# メンバー全員が
- 責任持って自走する
- 他部署に対して、同じ意見を言える
- お互いに役割を理解して連携し合う

みたいな感じですね。

SREチームとしてどう適用してるの?

- 未来予測しながら、仕事する
- 建設的でロジカルな議論をする
- 健康的である
- 成果させ出してれば自由。色々試していけばよい
- 意思疎通より意思の統一(ベクトルが同じかどうか)。意思疎通はPR。(深い、長くなりそうならchat or meets)
- HRT
    - 謙虚さを持って相手より勉学に励む(「おれについてこい」くらいの度量で)
    - 信頼を持ってあまり口を出さない。任せる
    - 尊敬して讃えあう(thumbsup)

というのを掲げてます。

HRTを意識することで、シンプルな会話になりやすくなります。

例えば会話の中で「人格否定してるわけではないですが」「非難してるわけではないですが」のような無駄な枕詞をなくした会話になりやすいです。

目指すは 誠実に言い合えるチーム です。

「自由を与えながら成果を出させる」というのは非常に環境が影響しやすいと思ってます。

なので我々は「失敗しても良い。何やってもいい。その代わり責任は持ちましょう。」という環境にしてます。

「迷ったり不安になったら、相談やレビューが絶対くる」とメンバーを信頼しましょう。来なかったらフィードバックし成長しあいましょう。

また働く時間も休みも自由にしてます。要所で会議や相談をセットすると思いますし、例えば新入社員が入ったばかりなら、相手が働いてる時間帯で働くメンバーだと信頼してます。

やりたい仕事をさせる

バックログにはいっぱいタスクが積んであります。

メンバーが自由にとってやらせれば良い仕組みにしており、なるべくslackや1on1で様子を伺いながら、成長したいorしてほしい分野をやらせます。

被ったら一緒にやらせれば良いのですが、問題はやりたくない仕事が余ることです。

そこはマネージャーである私がやってもよいし、成長のためにやらせてもよいし、そういうのが得意なメンバーを雇った良いチームを作り上げたいと思ってます。

会議はどれくらいやってるの?

  • 1週間何やったかを共有 + datadog dashboardを見る(15分。ドキュメントさえ見てれば参加自由)
  • 1on1(2週間に1回、30分)

各チーム会議はこれだけです。

これ以上必要ないと思いますし、全員時間を有意義に使うためです。

相手が何やってるか把握出来ないのでは?

まずマネージャーだから全部把握する必要はないかなぁと思ってます。

今何をやってるかに関しては、チーム全体で「相手に情報を出す」より「必要そうだったら出して、それを各々pull型で必要だったら拾う」を意識してます。

今後何やっていくかに関しては、基本github issueのissueやPRの通知や動きで把握出来ます。

その上でslackでは議論、issueやPRに5W1Hで決まったこととslackのURLだけ貼って簡潔にしてます。

なので、よくプレイヤーが疲弊しがちな、マネージャーのためにバックログにリアルタイム経緯を書いたりはしません。

物事の成果が出るタイミングにレビュー会を開いて、そこで「コメントに経緯を書いておくと、後からjoinした人がわかりやすいよね」というツッコミをすることが多いです。

最後に

多様な人材をうまく組み合わせることで、良いチームが出来上がります。

今でも良いチームですが、これからもっと高みを目指すために日々勤勉を積んで、爆速に成果を出せるチームにしたいと思います。