Sionの技術ブログ

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

Djangoでコンテナ用にjsonログ出力を実装する

f:id:sion_cojp:20211209175650p:plain:w0:h0

TL;DR

  • djangoの標準ログは質素すぎて足りない部分が多かった
  • djangoのログ出力をjson形式にするformatterを作成
  • healthcheck pathなど、特定URIをログ出力させないfilterを作成

Why?

筆者はdjango初心者ですが、自社サービスで一部djangoを使っており、

「datadog logsにjsonログを流したい」という要件が出てきました。

標準のログ機能じゃ要件を満たせず。。またdjangoのloggingはclassが設定できるので、

「標準でダメだったら自分たちで作ってね」という仕様なのかなぁと思いました。

Why formatter?

formatterを作ろうと思ったのは2つ理由があります。

  1. ログを出力させても質素なログしかGETできず、http request/response周りのデータが欲しかった点です。 _
  2. 理由は、json型で出力させて、datadog logsにフィールド認識させたかった点です。

https://b0uh.github.io/django-json-logging.html のようにやるとjsonは出力され、2つ目は解決しそうですが、1つ目が解決しません。

https://docs.datadoghq.com/ja/logs/log_collection/python/?tab=jsonlogformatter をみると

https://github.com/namespace-ee/django-datadog-logger が推奨となってたので使ってみるとこのような出力になりました。

{
    "message": "HTTP 200 OK",
    "logger.name": "django_datadog_logger.middleware.request_log",
    "logger.thread_name": "Thread-2",
    "logger.method_name": "log_request",
    "syslog.timestamp": "2021-12-02T09:10:00.541469+00:00",
    "syslog.severity": "INFO",
    "network.client.ip": "172.23.0.1",
    "http.url": "/hoge",
    "http.url_details.host": "localhost",
    "http.url_details.port": 8000,
    "http.url_details.path": "/hoge",
    "http.url_details.queryString": {},
    "http.url_details.scheme": "http",
    "http.method": "GET",
    "http.referer": null,
    "http.useragent": "curl/7.77.0",
    "http.request_version": null,
    "http.request_id": "f44778b6-0e78-433a-a2ca-24d1ae1736a3",
    "duration": 1774787.9028320312,
    "http.status_code": 200
}

とても良さそうですが、自由にフィールドを追加したい/フィールド名を変えれたら便利

(その他サービスのログフィールドと一緒にすると検索しやすいから)

だと思って、一からformatterを作成することにしました。

Why filter

healthcheck pathなどはノイズになるため、リクエストが来てもログを出力しないようにしたかったです。

djangoにはfilter機能があったので、それも一から作ってみました。

実装したものを適用した後のログ出力

$ curl -X POST -H "Content-Type: application/json" \
-d '{"Name":"hogehoge", "fugarfuga": "fugaaaaaa"}' localhost:8000/hoge
{
    "middleware": "django_datadog_logger.middleware.request_log",
    "levelname": "WARNING",
    "filename": "request_log.py",
    "status": 404,
    "duration": 28986692.428588867,
    "error.message": "Not Found",
    "host": "localhost",
    "port": 8000,
    "protocol": "http",
    "method": "POST",
    "path": "/hoge",
    "query_string": {},
    "request_body": "{\"Name\":\"hogehoge\", \"fugarfuga\": \"fugaaaaaa\"}",
    "user-agent": "curl/7.77.0",
    "remote_ip": "172.23.0.1",
    "referer": null,
    "time": "2021-12-08T12:44:50.447804+00:00"
}

How

こちらにプログラムを置いてます。

github.com

設定方法

required

httpリクエストを解析するために下記が必要です。

# requirements.txt
django-datadog-logger==0.5.0

config/base.py

MIDDLEWARE = [
    "django_datadog_logger.middleware.request_id.RequestIdMiddleware",
    .
    .
    .
    "django_datadog_logger.middleware.error_log.ErrorLoggingMiddleware",
    "django_datadog_logger.middleware.request_log.RequestLoggingMiddleware",
]

のようにして、django-datadog-loggerをmiddlewareに登録させます。 これは、 logging_formatter.py にhttpの詳細ログを送るためです。

config/logging_formatter.py

format用のプログラムです。 django-datadog-loggerでも足りない必要なログを追加したり、

datadog logsに合わせてフィールド名を変更できます。

最後にjson型に変更して出力して終わり。

config/logging_filter.py

filter用です。

ログの出力をするorしないをジャッジします。主にヘルスチェックなど、有効なURIだがロギングしたくないものに対して行ってます

config/environemnt名.py

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    'filters': {
        'custom': {
            '()': 'config.logging_filter.Main',
        },
    },
    "formatters": {
        "json": {"()": "config.logging_formatter.Main"},
    },
    "handlers": {
        "console": {"level": "INFO", "class": "logging.StreamHandler", "formatter": "json", 'filters': ['custom']},
    },
    "loggers": {
        'django.server': {'level': 'ERROR'},
        "django_datadog_logger.middleware.request_log": {"handlers": ["console"], "level": "INFO", "propagate": False},
    },
}
DEBUG: False

formatにlogging_formatter.pyを設定

filterはlogging_filter.pyを設定

handlerはconsole(コンソールにstdoutとして出力用)の1つだけ作成し、filter/formatterは上記を選択

loggersは2つです

  1. django.server
    • djangoが生成する標準ログ。質素で使えないため("GET / HTTP/1.1" 404 179 のようなログ)出力させないようにしてます
    • level: ERRORにすることで、 上記のログを出さないようにしています
    • handlerも設定不要
  2. django_datadog_logger.middleware.request_log
    • httpリクエストを解析したログも出力されるやつ。これをベースにログを生成します

またDEBUG: Trueにすることで、ルーティングしてないURIアクセス時の無駄なログ( Not Found: / )を消しています。

developmentだけTrue推奨です。

Fargateで異常終了したコンテナをLambda(Go)でslackに通知する仕組みをTerraformで管理する

f:id:sion_cojp:20211126184642p:plain:w0:h0

TL;DR

  • CloudWatch EventsでECS Cluster内のStatusをwatchする
  • Eventsトリガーで、Lambda(Go)を発火しSlackに通知する
  • それらをTerraformで管理する
  • (弊社はFargateオンリーなのでnamespaceにfargateってつけてます)

どんな感じに通知されるの?

f:id:sion_cojp:20211126165614p:plain

※検証時のexit 0でも通知させたときの画像です

Architecture

https://raw.githubusercontent.com/sioncojp/playground/master/terraform/fargate-non-exit-0-notify/docs/fargate-non-exit-0-notify_architecture.png

Code

https://github.com/sioncojp/playground/tree/master/terraform/fargate-non-exit-0-notify

実装時のポイント

事前にSlackのappを作る

  • https://api.slack.com/apps/
  • からアプリを作りましょう
  • おそらく下記権限が必要かと思います(OAuth & Permissions -> Scope -> Bot Token Scopes)
    • channels:read
    • chat:write

シークレット情報はLambdaのEnvironmentに設定できない

  • environmentに設定するとコンソールから丸見えになってしまうので、
  • コード側でParameterStoreから対象のデータを引っ張る形にしてます

terraformからGoで書かれたlambdaをdeployするのが難しい

  • applyする前に GOOS=linux GOARCH=amd64 のバイナリをbuildする必要があります。
    • もしバイナリがなかったらこけるだけ
    • なので一旦ドキュメントでカバーすることにしました。(いい方法あったら教えてください)

Goがインストールされてなくてもbuild, go get -uできるようにした

  • Makefileを見てもらうとわかるのですが、
    • makeを実行
    • .go-version のバージョンのGoをダウンロードし、bin/go として展開
    • それを元にbuild -これでどの環境でもbuildできるようにしました

exit 143と0

  • docker stopはコンテナにSIGTERM(exit 143)を送信する
  • なので基本的に143が返ってくることが多い
  • が、tiniやdumb_initでマッピングしてる場合があるのでexit 0も返ってくる可能性がある
  • ので両方正常系という扱いをした実装になってます

aws rootアカウントに二要素認証の必要性と有効化する手順

二要素認証の必要性

nullcon HackIM 2020 Writeup - Lateral Movement - こんとろーるしーこんとろーるぶい

こちらのCTF writeupを拝見したところ、下記のような内容でした。

1. script.jsを見て変なHTTPヘッダーを見つける
2. HTTPヘッダーに値をセットしてリクエストを送ると、「値 = EC2ホスト名」でホスト名にリクエストを転送することが判明
3. メタデータにアクセスする「169.254.169.25」にリダイレクトする踏み台サーバを用意
4. 踏み台のグローバルIPをセットしてリクエストを送り、メタデータ取得
5. IAMアクセスキー取得
6. 他のIAM Userに昇格してpassword書き換え
7. そのUserでawsコンソールログイン -> 権限がある部分は操作できる

フラグを盗まれる側の立場になってみると、

このような脆弱性を持ったヘッダーは開発時の設計&レビューで考慮するとしても、完璧に防げそうにはないなと。

「iamに UpdateLoginProfile を不必要にセットするな!」というのはおっしゃる通り。ですが、ログインされる手前の最終防御として二要素認証があれば防げそうだなぁと思いました。

「今時代、二要素認証が必要」ってのは理解しているものの、具体的になぜ必要なのか、というのをこのwriteupで理解できたことがとてもよかったです。

rootアカウントに二要素認証を有効化する手順

f:id:sion_cojp:20210811174730p:plain:w300 f:id:sion_cojp:20210811174331p:plain:w300

スマホやPC等の単一端末がないと難しいと思いがちですが、1passwordのアプリ(web版じゃないよ!)では二要素認証を有効化することができます。

アプリ側で有効化した後は、右画像の通りweb版でも利用可能になります。

あとはrootでログインし、こちらの手順を参考にしてみてください

仮想 Multi-Factor Authentication (MFA) デバイスの有効化 (コンソール) - AWS Identity and Access Management

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業務も合わせて行っていきたいですね。