production.log

ピクスタ株式会社で開発部の部長をやっている星直史のブログです。

AWS障害が発生した場合に確認するページやサイトまとめ

概要

2019年8月23日 13時頃からAWS EC2の接続ができなくなる障害が発生しました。
このような大規模障害は滅多にないので、障害の情報収拾する際に「どこみりゃいいんだ?」となるので、この機会にまとめることにしました。

この記事ではAWSで障害が発生した場合に確認するページやサイトをまとめます。

公式情報

公式の情報は正確性はあるものの、速報性には欠けます。
そのため、後述する非公式情報と並行して確認する必要があります。 公式情報からは、下記2つの情報が得られます。

  • 何がなぜ障害に繋がっているのか
  • いつ復旧する見込みなのか

この情報から、障害を回避するための方法や、自サービスの復旧見込みのアナウンス*1に役立てることができます

AWSサービス全体の障害情報

AWSサービス全体の障害情報はAWS Service Health Dashboardで確認することができます。

status.aws.amazon.com

このページは、大きく最新の情報(Recent Events)と過去7日間の障害履歴(Status History)が表示されます。 また、Recent Eventsには障害の状況も確認することができます。

f:id:watasihasitujidesu:20190823144855p:plain

自分のアカウントで影響を受けているAWSサービス

AWSサービス全体のうち、自分のアカウントで影響を受けている障害情報は、AWS Personal Health Dashboardで確認することができます。

aws.amazon.com

f:id:watasihasitujidesu:20190823150903p:plain

ここで表示される情報は、基本的にはAWS Service Health Dashboardと同一です。 また、AWS Personal Health Dashboardは、CloudWatch Eventsと統合することができるので、障害が発生した場合にいち早く通知を受け取ることも可能になります。

AWS CLI

先述した2つのダッシュボードで障害情報を確認することができますが、CLIでも確認することができます。

docs.aws.amazon.com

CLIでは下記オプションを指定することで、細かくフィルタリングすることができます。

  • describe-affected-entities
  • describe-entity-aggregates
  • describe-event-aggregates
  • describe-event-details
  • describe-event-types
  • describe-events

用途としては、下記の場合に使えるかと思います。

  • 7日以上前の障害状況と復旧までの時間をログとして残しておきたい
  • 特定の情報のみ操作したい

非公式情報

非公式の情報は正確性には欠けるものの、速報性があります。
AWS障害と思われる事象が観測された場合に、他のAWSユーザーも同様の状況に陥っているかを素早く確認することができます。 そのため、AWSの障害なのかを切り分けるための初手としては良い手だと思います

Twitter awsハッシュタグ

基本的には、Twitterを見れば誰かしらつぶやいていることが多いです。
この場合、最新タブに切り替えて、みんなの速報を確認します。

https://twitter.com/search?q=%23aws&src=typed_query&f=live

Twitter非公式アカウント

AWSの非公式アカウントで障害情報の速報を流しています。 今回の障害では見ることはありませんでしたが、「こういうのもあるよ」程度で知っておくと良いと思います。

まとめ

情報には、公式情報と非公式情報があります。 それぞれ、メリットとデメリットは下記の通りです。

  • 公式: 正確性はあるものの、速報性には欠ける
  • 非公式: 正確性には欠けるものの、速報性がある

そのため、障害が発生した場合、公式情報と非公式情報をうまく使い分けて、情報収拾をする必要があります。
また、公式の情報には、何がなぜ障害に繋がっているのか、いつ復旧する見込みなのかという情報が公開されます。
この情報から、障害を回避するための暫定対応策と、自サービスや社内メンバーへのアナウンスを考える事ができます。

*1:ユーザーさんや社内メンバーに向けて

SnapmartのブログサーバーのSSL証明書をLet's EncryptからAWS Certificate Manager (ACM)に移管しました

概要

先日、SnapmartのブログサーバーのSSL証明書が有効期限切れになってしまい、アクセスすることができなくなってしまいました。

これまでSnapmartのブログサーバーではLet's Encrypt という証明書を無料で発行しているサービスを使っていました。
しかし、このLet's Encryptで発行される証明書は有効期限が90日間で設定されているため、それに気づかず失効してしまいました。

Let's Encryptの使用による90日ごとに証明書の更新を行うのは今回のようなトラブルの元になることや、運用負荷が高まってしまうため、このタイミングでSnapmartのブログサーバーのSSL証明書をLet's EncryptからAWS Certificate Manager (ACM)に移管しました

当初の構成と運用

当初のシステム構成と、運用はこのような感じでした。

f:id:watasihasitujidesu:20190812155930p:plain

  • オリジンをEC2(WordPress)としたCloudFrontを使用
  • 証明書の発行はLet's Encryptが行う
  • CloudFrontに設定する証明書はACMで管理しているが、Let's Encryptで発行した証明書インポートしている

「CloudFrontを設置し、負荷対策と高速化はできているが、証明書の発行と運用まで手が回っていなかった」という状態です。

あるべき姿

「Let's Encryptからの脱却し、運用負荷(考えるべきこと)を減らす」を目的に、代替手段を考えます。

メリット デメリット
証明書を代理店から購入し、失効までの期間を伸ばす 失効までの期間が延長される 数年に一度は更新作業をしなければならない
Let's Encryptの証明書更新、ACMインポート、CloudFrontの設定を自動化する 目の前の課題は自動化により解決される プラットフォームの変化に弱い
証明書の管理にACMを使う 証明書の更新や設定はAWSが管理してくれる EC2に証明書をもたせることができない

これらを比較検討した結果、「証明書の管理にACMを使う」が有力だろうと考えました。
しかし、デメリットを挙げた通り、EC2はACMが発行した証明書を持つことができないため、CloudFrontとEC2インスタンスの間にELBをかます必要があります。

移行手順

大まかな手順は下記の通りです。

  • ACMでSSL証明書を発行
  • ALBを設置し、ターゲットをEC2インスタンス(既存のブログサーバー)に設定
  • CloudFrontのオリジンにALBを設定
  • CloudFrontのSSLをACMで発行したSSL証明書に設定

ACMでSSL証明書を発行

何はともあれ、SSL証明書が必要です。
今回は、faq.snapmart.jpとinfo.snapmart.jpの2つのドメインが必要であったため、ワイルドカードを使った証明書を作る必要があります。

発行自体は非常に簡単で、 AWS ACMのマネコンにアクセス => Request a certificate => Request a public certificateと画面を進み、Domain nameにワイルドカードを使ったドメイン名(今回の例だと*.snapmart.jp)を設定するだけです。

f:id:watasihasitujidesu:20190812201302p:plain

ALBを設置し、ターゲットをEC2インスタンス(既存のブログサーバー)に設定

ALBの設定は特筆する点がありませんので、公式のドキュメントを参考に作成します。

docs.aws.amazon.com

CloudFrontのオリジンにALBを設定

対象のオリジンを選択 => Origins and Origin Groups => Editから「Origin Domain Name」に上記で作成したALB名を指定します。

CloudFrontのSSLをACMで発行したSSL証明書に設定

最後に、CloudFrontのSSL証明書を変更します。
対象のオリジンを選択 => General => Editから、
Custom SSL Certificateを編集します。
テキストボックスに、Focus Inすると、ACMで作成した証明書名が表示されるので、それを選択します。

f:id:watasihasitujidesu:20190814105211p:plain

以上で設定は終わりです。

まとめ

最終的には下記の構成図となりました。

f:id:watasihasitujidesu:20190814110944p:plain

Let's Encryptは手軽にSSL証明書を取得できる便利なサービスです。
しかし、証明書の期限が90日であるため、こまめに更新を行わなければならず、運用が煩わしくなったり、更新漏れが発生しWebサイトにアクセスができなくなってしまいます。

今回は、SSL証明書の運用から解放されるために、証明書の管理をAWS Certificate Manager (ACM)に移管しました。
また、EC2をオリジンにしたCloudFrontを使用している場合は、EC2は直接ACMの証明書を使えないため、CloudFrontとEC2の間にALBを置く必要があります。

【React Native】Expo SDK v33.0.0 へのアップグレード手順

概要

今年の1月にGoogleが「今後Androidアプリは64bit対応してないとダメだよ!8月1日以降32bitのアプリはリリースできないからね!」というアナウンスをしました。 android-developers.googleblog.com

SnapmartアプリはReactNativeで実装しており、開発ツールとしてExpoを使っています。ExpoはSDKのバージョン33から64bitのApp Bundleをビルドできるようになりました。 blog.expo.io

この記事ではExpo SDK v32.0.0からv33.0.0へアップグレードする手順を紹介します。

手順

app.jsonの修正

app.jsonのsdkVersionを"33.0.0"に変更します。

package.jsonの修正

package.json内に記述している、react-native, expo, reactをそれぞれ下記の通り変更します。

{
  "react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz",
  "expo": "^33.0.0",
  "react": "16.8.3"
}

import文の修正

これまでexpoからのimportしていた各種APIが、SDK33からはexpoから分離し、別パッケージとして開発が行われるようになりました。 それに伴い、expoからのimportしていたコードは、分離されたパッケージからimportするように変更しなくてはなりません。

具体的にはこんな感じです。

  • Before
    • import { FileSystem } from 'expo';
  • After
    • expo install expo-file-systemを実行
    • import * as FileSystem from 'expo-file-system';

上記のような修正を全て手作業を行うのはとても骨が折れます。
Expoはこの処理を自動で修正するツールも開発しています。今回はそのツールを使用します。 www.npmjs.com

npx expo-codemod

使い方は非常に簡単です。

$ npx expo-codemod
$ npx expo-codemod sdk33-imports 'src/**/*.js' 'src/**/*.{ts,tsx}'
Transforming 13 TS files...
Processing 13 files... 
Spawning 11 workers...
Sending 2 files to free worker...
Sending 2 files to free worker...
Sending 2 files to free worker...
Sending 2 files to free worker...
Sending 2 files to free worker...
Sending 2 files to free worker...
Sending 1 files to free worker...
All done. 
Results: 
0 errors
12 unmodified
0 skipped
1 ok
Time elapsed: 0.471seconds 
Transforming 99 TSX files...
Processing 99 files... 
Spawning 11 workers...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
Sending 9 files to free worker...
All done. 
Results: 
0 errors
37 unmodified
0 skipped
62 ok
Time elapsed: 1.661seconds 
Added imports from 9 packages.
To install the correct versions of these packages you can run this expo-cli command:

expo install @expo/vector-icons expo-analytics-amplitude expo-camera expo-constants expo-contacts expo-font expo-image-picker expo-permissions expo-secure-store

このような感じで自動で変換してくれます。 また、処理が終わったあとのメッセージに、expo installすべきパッケージが羅列されます。 最後に下記コマンドを実行すれば変更は完了です。

expo install @expo/vector-icons expo-analytics-amplitude expo-camera expo-constants expo-contacts expo-font expo-image-picker expo-permissions expo-secure-store

node_moduleの削除と再インストール

package.jsonを書き換えたので、node_moduleディレクトリを削除します。 その後、yarn installで新しくパッケージのインストールを行います。

Breaking changesの確認と修正

最後にBreaking changesを確認し、変更すべきものがあったらアプリのコードを修正していきます。 github.com

まとめ

Expo SDKのバージョンアップは基本的にapp.jsonとpackage.jsonを修正してyarn installするだけで済みます。 SDK33においては、ExpoのAPIを独立させる動きがあっため、自動変換ツールを使用してパッケージのimport方法を変更しました。 自動変換ツールをExpoが提供してくれているので、特にハマることもなく変換できました。 最後のBreaking changesの確認と修正は、アプリの規模によって修正箇所が増減しそうです。 Snapmartアプリは影響があった修正は数カ所であったため、比較的スムーズにアップグレードが完了しました。

また、Expo SDKは下位互換性維持期間はリリースされてから半年であることや、半年をすぎるとbuildできなくなります。 そのため、Expoを使っている場合は、定期的にSDKのアップグレードを行なう必要があります。

ReactNative 0.59.8のCameraRoll.getPhotosでInvalid filter option: '(null)'. Expected one of 'photos','videos' or 'all'.が発生した場合の回避方法

概要

タイトルの通り、ReactNative 0.59.8において、CameraRoll.getPhotos({first:1234})がエラーになってしまいます。
また、これはiOSのみで発生します。
エラー文を見た感じ、optionとしてassetTypeに何かしら指定すれば良さそうです。

ただ、ドキュメントを見る限り、"Photos"がdefaultなので普通に不具合っぽいですね。

facebook.github.io

回避方法

先述の通りassetType: "Photos"を指定すれば良いかと思いきや、groupTypesも指定しなければうまく取得できませんでした。 最終的には下記のようにコードを書き換えるとうまく動作します。

CameraRoll.getPhotos({first: 1234, assetType: "Photos", groupTypes: "All"}) 

追記2019/07/21 19:23 JST

上記の修正を行なった場合、Android版ではgroupTypesが存在しない旨のエラーが出てしまうため、Platformによりコードを修正せざるを得ない。

AWS IAM ユーザー各自のMFA設定ポリシーの登録

概要

AWS IAMでは、特定のグループにポリシー(権限)を設定し、そのグループにユーザーを追加することで、権限管理が容易になります。
設定できるポリシーは、AWSが用意しているポリシーと、独自に設定できるポリシーの2パターンあります。
今回は、AWS IAM ユーザー各自のMFA設定ポリシーの登録の方法について書いていきます。

背景

AWSのユーザー登録を行う場合、基本的にはIAMユーザーを作成し、操作を行うことになります。
この場合、権限が高ければ高いほど、コア機能の変更ができる権限を付与する設計になることが多いです。
また、開発組織が少なければ少ないほど、エンジニア一人あたりの権限の範囲が広くなりやすいです。
そのため、もし認証情報が漏れた場合の影響が計り知れないため、セキュリティ対策としてMFAを設定してもらうことになります。

先述の通り、AWS IAMのポリシーは、AWSが用意しているポリシーと、独自に設定できるポリシーがあるのですが、MFA設定に限定されたポリシーは、AWSが用意していないため独自に作成する必要があります。
Snapmart(およびPIXTA)の開発でも、このMFA設定を行うのですが、毎度毎度「あれ、どう書くんだっけ?」となってしまうため、備忘録として残しておきたいと思い、書きました。

設定

ポリシーの設定は下記の通りです。
下記JSONのうち、AWS_ACCOUNT_NOは、ご自分のAWSアカウントの番号をハイフンを除外した数値となります。(012345678901234みたいな数値)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:ListUsers"
            ],
            "Resource": [
                "arn:aws:iam::AWS_ACCOUNT_NO:user/"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:ListVirtualMFADevices"
            ],
            "Resource": [
                "arn:aws:iam::AWS_ACCOUNT_NO:mfa/"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:EnableMFADevice",
                "iam:DeactivateMFADevice",
                "iam:ResyncMFADevice",
                "iam:ListMFADevices"
            ],
            "Resource": [
                "arn:aws:iam::AWS_ACCOUNT_NO:user/${aws:username}"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:DeleteVirtualMFADevice",
                "iam:CreateVirtualMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::AWS_ACCOUNT_NO:mfa/${aws:username}"
            ]
        }
    ]
}

SnapmartにPull Pandaを導入しました

概要

6月18日(火)にGitHubがPull Pandaを買収したと話題になったので、早速Snapmartにも導入しました。
今回は簡単にPull Pandaの紹介をします。

Pull Pandaとは

pullpanda.com

Pull Pandaとは、GitHubのPull Requestにおいて下記の機能を提供しています。

  • PRのリマインド
  • PRの分析
  • レビュアーの自動アサイン

PRのリマインド

PRのリマインドはSlackと連携し、特定のチャンネルや、DMで通知を行うことができます。
また、時間、曜日、フィルターの設定もできます。
設定は画面はこんな感じです。

特定のチャンネル宛てへのリマインド設定

DM設定

PRの分析

PRの分析は下記の画面が用意されています。

  • Review turnaround
  • Reviewer workload
  • Open PRs
  • PR merge time
  • PR throughput
  • PR size
  • Code churn

Code churn画面

これらは時間の経過と共に価値が上がる情報だと思うので、導入したタイミングの数値がどうこうというよりは、定期的に振り返って確認して整理するといった使い方になるのかと思います。

レビュアーの自動アサイン

レビュアーの自動アサインは、GitHub Appsで提供されます。
設定は、アプリをインストールし、自動アサインを有効化したいリポジトリを選択するだけです。

レビュアーの自動アサイン

所感

レビュアー自動アサインと、PRリマインドのReal-time messagesやDMの設定により、下記やりとりがbotで解消できそうです。人間による温かみのあるメッセージのやりとりがなくなるのは非常に残念です

  • レビューお願いします
  • レビューしました
  • lgtm!!

分析については、導入直後なので正直そこまで使用感がわからなかったのですが、少し時間を置いて見てみようと思います。

Linuxでduコマンドとdfコマンドを使って空き容量を増やす方法

概要

サービスを運用していると、サーバー内のディスク空き容量が枯渇してしまうことがしばしばあると思います。
以前AWS EC2のルートボリューム(EBS)をダウンタイム0で拡張する方法について書きましたが、対応の一つとして格納されているファイルを整理することで空き容量を増やすことも可能です。
今回は、Linuxでduコマンドとdfコマンドを使って空き容量を増やす方法について書きます。

コマンドの説明

dfコマンドとは

dfコマンドとは、サーバー内で使用中のディスクの量と使用可能量をファイルシステム毎に表示するコマンドです。
使用できるオプションは様々ですが、基本的にはdf -hで事足ります。

実行すると下記の通り、ファイルシステム、全体のサイズ、使用量、空き容量などが出力されます。

$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
devtmpfs         1.9G   56K  1.9G    1% /dev
tmpfs            1.9G     0  1.9G    0% /dev/shm
/dev/nvme0n1p1    32G   22G  9.7G   70% /

duコマンドとは

duコマンドとは、ディレクトリやファイル毎のディスク使用状況を出力するコマンドです。 実行すると下記の通り、ディレクトリやファイル毎にディスクの使用状況が出力されます。

$ du -sch ./*
5.8G    ./OUTPUT_DATA
76K ./OUTPUT_TABLE
12K ./bin
4.0K    ./date.txt
4.0K    ./nohup.out
32K ./test.log
4.0K    ./tmp
5.8G    合計

ディスク容量の整理の仕方

dfコマンドとduコマンドを使い方としては

  • dfコマンド: サーバー内で使用しているファイルシステム全体の使用状況を把握
  • duコマンド: 個別のディレクトリ、ファイルの使用状況を把握

といったように、duコマンドで狙いを定めて、容量を圧迫しているファイルを特定していくという使い方になります。

先述したduコマンドの結果をみると、OUTPUT_DATAというファイルが(カレントディレクトリに おいては)巨大なファイルであることがあります。
また、圧迫しているものがディレクトリの場合は、さらにduコマンドで深掘りし、ファイルを特定していくことで整理をしていきます。

ReactNative + Expo製アプリをiOS / Android(Genymotion)エミュレーターで動作させる手順

概要

業務やプライベートでReactNative + Expo製アプリをpullしてきた場合、コードを読むだけではなく、サクッとエミュレーターで動かしたいとことが多くあると思います。
今回は、ReactNative + Expo製アプリをiOS / Android(Genymotion)エミュレーターで動作させる手順を紹介します。
iOSのエミュレーターはXcodeを使用し、AndroidはGenymotionを使用していきます。

XcodeとGenymotionとは

Xcodeとは

XcodeとはApple社が開発している、アプリ開発ツールです。
Xcodeではエディタ、エミュレータ、リリースまで行うことができます。
ReactNative + Expo製アプリにおいては、Xcodeが内包しているエミュレーターを使用するといった具合です。

Genymotionとは

Genymotionとは"爆速で動作するAndroidエミュレーター"です。
Android StudioのAVDと比較すると起動や動作が軽快です。 また、エミュレーターとして動作するデバイスのインストールと設定が非常にシンプルです。 ホストPCのリソース割り当ての設定もできます。

ツール類のインストール

iOS(Xcode)

XcodeはApp StoreからDLするだけです。

Android(Genymotion)

GenymotionはVirtualBoxGenymotionをインストールしていきます。 ダウンロードはそれぞれ下記サイトから行います。

デバイスのインストール

VirtualBoxGenymotionのインストールが完了したら、Genymotionを起動し、エミュレートするデバイスをインストールします。

Genymotionのデバイスインストール

サイドバーのForm factorとAndroid APIを選択し、任意のデバイスをInstallします。 割り当てるリソースについては、Detailsから設定を行います。

動作確認

ターミナルからexpo startを実行し、画面にQRコードが表示されたら下記キーを押下すると、それぞれエミューレーターが立ち上がります。
Androidの場合はエミュレーター起動後でなければ実行ができないことに注意が必要です。

  • a: Androidエミュレーターの起動
  • i: iOSエミュレーターの起動

SnapmartのWebサーバーにmonitを導入してunicornを不死鳥にした

概要

Webサービスを運営していると、夜中にWebサーバーのプロセスが突然死してしまい、朝に気づくということが多々あります。
流石に人間が24時間365日監視しているのはツラいので、ある特定のプロセスが落ちたら自動で検知と再起動という処理を行いたいです。
今回はSnapmartのWebサーバーにmonitを導入してunicornを不死鳥にしたので、その導入について書きます。

monitとは

冒頭でも述べましたが、ざっくりいうとmonitはサーバーのある特定のプロセスを監視し、プロセスがなくなったことを検知、自動再起動を行うOSSです。 monitが監視する条件に合致した場合に再起動を行うだけなので、原因の根本解決などは人間が行う必要があります。

導入手順

手順は大きく4つあります。

  1. monitをinstall
  2. unicornの起動スクリプトを書く
  3. unicorn用のmonitの設定を記述
  4. monitの再起動と動作確認

monitをinstall

monitはyumでinstallすることができます。

sudo yum install monit

unicornの起動スクリプトを書く

monitが自動で再起動させるためのスクリプトを書きます。 このスクリプトは/etc/init.d/unicornに配置します。 スクリプトの内容は下記の通りです。

#!/bin/sh
NAME="Unicorn"
ENV=production

RAILS_DIR="/awesome_your_app"
PID="${RAILS_DIR}/log/unicorn/unicorn.pid"
CONF="${RAILS_DIR}/config/unicorn.rb"

start()
{
    if [ -e $PID ]; then
        echo "already started"
        exit 1
    fi
    echo "start unicorn"
    su - ec2-user -c "cd ${RAILS_DIR} && bundle exec unicorn_rails -c ${CONF} -E ${ENV} -D"
}
stop()
{
    if [ ! -e $PID ]; then
        echo "${NAME} already stopped"
        exit 1
    fi
    echo "stop ${NAME}"
    kill -QUIT `cat ${PID}`
}

restart()
{
    stop
    sleep 3
    start
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    *)
    echo "Syntax Error"
    exit 2
    ;;
esac

上記のファイルを作成すると、/etc/init.d/unicorn start|stop|restartを行うことができます。 これを用いてmonitがプロセスの再起動を行うわけです。
※併せてchkconfigを設定するのも良いと思います。今回は割愛

unicorn用のmonitの設定を記述

monit.conf

まずmonit自体の設定を行います。設定ファイルは/etc/monit.confです。
こちらのファイルで重要なのは、Include /etc/monit.d/*.monitrcです。 これは、/etc/monit.d/配下にある*.monitrcをIncludeする設定です。この設定を行い、特定のモジュールに限定した設定ファイルを用意することで、管理が容易になります。

set daemon 60
set mailserver localhost
set eventqueue
    basedir /var/monit
    slots 100
set logfile syslog
Include /etc/monit.d/*.monitrc
Include /etc/monit.d/logging
set httpd port 2812 and use the address localhost
  allow localhost

/etc/monit.d/unicorn.monitrc

続いて、monitのunicorn監視用の設定を書いていきます。

check process unicorn with pidfile /awesome_your_app/log/unicorn/unicorn.pid
  group ec2-user
  start program = "/etc/init.d/unicorn restart"
  stop program = "/etc/init.d/unicorn stop"
  if 5 restarts within 5 cycles then unmonitor

設定はとてもシンプルです。

  • start, stopをどのgroupで、どのコマンドを実行するか
  • 何サイクルで何度失敗したら監視をやめるかを設定しています。

monitの再起動と動作確認

monitの再起動

これまでの設定を再読み込みしたいので、monitを再起動します。

sudo monit reload

動作確認

最後に動作確認をします。

  • monitがunicornプロセスを監視しているのか
  • unicornプロセスが落ちた場合に自動で再起動するか

上記2点が満たされれば良しとします。

monitがunicornプロセスを監視しているのか

monitの状況はsudo monit statusコマンドで確認できます。

Process 'unicorn'
  status                            running
  monitoring status                 monitored
  pid                               3941
  parent pid                        1
  uptime                            5h 0m 
  children                          5
  memory kilobytes                  204080
  memory kilobytes total            1670284
  memory percent                    5.3%
  memory percent total              44.0%
  cpu percent                       0.0%
  cpu percent total                 0.0%
  data collected                    Thu May 23 04:08:47 2019

バッチリですね!

unicornプロセスが落ちた場合に自動で再起動するか

monitのログを確認するために、sudo tail -f /var/log/monitでログを見守ります。 別のプロセスで/etc/init.d/unicorn stopを行います。

[UTC May 23 00:59:41] error    : 'unicorn' process is not running
[UTC May 23 00:59:41] info     : 'unicorn' trying to restart
[UTC May 23 00:59:41] info     : 'unicorn' start: /etc/init.d/unicorn
[UTC May 23 01:00:42] info     : 'unicorn' process is running with pid 20241

1分ほどすると上記のようにログが出てきます。

  • unicorn プロセスが実行されていないことを検知
  • unicorn プロセスを起動させようと試みる
  • /etc/init.d/unicornを実行
  • PID: 20241でunicornが起動

といった具合で自動で起動されました。バッチリですね!

まとめ

今回は、monitを導入してunicornプロセスが落ちた場合に自動で再起動されるようにしました。
unicornだけではなく、nginxやpostfixといったミドルウェアが謎の変死を遂げるのであれば、monitの導入を考えても良いでしょう。

また、monitを導入すると、導入の背景にあった人間が24時間365日監視を行うということから解放されます。
心配が一つ減るのは良いですね。

ただ、monitで再起動がかかるのは良いのですが、根本の原因は解決しなければなりません。
運用の負担はどんどん減らしていきましょう。

特定の名前のEC2のPublicIPを取得する方法

概要

TV放映などで、一時的にスケールアップさせたい時に、オートスケールの機構がなければ職人による技が光ります。
今回は、スポットリクエストで増やしたEC2インスタンスのIPをAWS ESS access policyに登録したかったので、IPを取得する必要がありました。

コマンド

aws ec2 describe-instances --filter "Name=tag:Name,Values=hogehoge" | grep ASSOCIATION | sort | uniq | awk '{print ""$4""}'

MySQL Staging環境のDBをdumpしてローカル環境のMySQLにimportする方法

概要

Snapmart社のstagingのDB環境は、AWS RDSを使用しています。

毎日24時にproduction環境のスナップショットからstaging用DBを作成し、update文で個人情報やメールアドレスなどをマスクした状態にしています。
また、stagingが配置されているVPCは社内ネットワークからしかアクセスできないようしています。
そのため、自宅からリモートで作業を行いたい場合、stagingのDBに接続できず思うように開発ができません。 *1

今回は、手っ取り早くローカルのmysqlにデータをdump/importするための手順を記します。

準備するもの・こと

AWS RDS staging用DBのデータ

AWS RDS staging用DBのデータをダンプします。 ダンプするデータは、特定のデータベースのテーブル定義データと、テーブルの中身のデータの二つに分けて取得します。

mysqldump -uhoge -phoge -hhoge.com -d -n > OUTPUT_TABLE 
mysqldump -uhoge -phoge -hhoge.com -t table_name > OUTPUT_DATA

そして、ダンプしたファイルをscpでローカルにもってきます。

元々のDBの削除と作成

めんどくさいのでRails経由で行います。

bundle exec rake db:drop RAILS_ENV=development
bundle exec rake db:create RAILS_ENV=development

import

データ部をimportする際に-fはエラーを無視してimportするぜ!というオプションです。 duplicate entyエラーが出てしまっていたのですが、細かいことは気にしないので、スルーすることにしました。

sql_local < ~/Downloads/OUTPUT_TABLE
sql_local -f < ~/Downloads/OUTPUT_DATA

追記 201903201343 メモ

DDLとデータで分けたりせずにdump実行すれば、dumpファイルに

DROP TABLE IF EXISTS XXXTable

がつくので、ローカルDBの初期化処理は必要ない。

*1:VPN使えば良いって話はありますが

vimでsnake_caseをCamelCaseに置換する

RailsとJavaScriptを同時に扱っていると*1、snake_caseからCamelCaseに置換したい時がしばしば訪れます。 一つずつ修正した方が、人間の温かみを感じることができるコードになると思うのですが、置換の方法も知った方が良いと思ったので、メモとして残しておきます。

:s/_\(.\)/\u\1/g

*1:Style Guideで記法が異なる言語を同時に扱っている場合

よちよちReactNative #1 を開催しました!

概要

ReactNativeを使い始めたものの、基本的に参考になる記事が少なくて詰みかけてしまったり、英語の記事がメイン(これはまだ良いけど)であったり、参考書が少ないので、初心者にはだいぶハードルが高いなぁと感じていました。 写真者同士で助け合ったり、気軽に質問できる場があったら良いなぁ〜と思い、よちよちReactNativeを開催することにしました!

どんな感じだったか

参加予定者が6名だったので結構集まった感あったのですが、土曜日の10時スタートというストイックな時間設定にしてしまったので、自分含めて4名での開催となりました!もくもく会は遊びじゃねぇんだよ!

イベント開催(connpassで人集め ~ 会場作り ~ 当日のアレコレ)が初めてだったので、良い体験になりました。 また、もくもく会にしたので、運営の手間とか難しさがほぼなく、ワンオペでもなんとかなることがわかりました!(寝坊で爆死しない限りなんとかなる!)

参加者のみなさんは、プログラムを書きながら実況もする訓練されたエンジニアが多かったので、Twitterを追ったりするのも楽しかったです。

togetter.com

振り返り

継続的に改善していくために、KPT(K: 良かったこと/続けていきたいこと、P: 改善点、T: 次に取り組むこと)で振り返りをしていきます!

Keep

  • connpass使って初めてイベント開催したので、使い方とか流れが完全に理解できた
  • 会社のエアコンにタイマー機能があることを知り、開始2時間前に起動するように設定したから、朝のんびりできた
  • 最大の難関である起床に成功した。
  • 意外と準備に手間がかからなかった(ワンオペでもそれなりにいけることがわかった)
  • 社内メンバーの助け舟を借りて、会社までの到着マニュアルを公開することができた
  • いつもカッチリキッチリ、台本用意したりして頑張るんだけど、ゆるっっと司会したりするのも良かった。
  • 楽しかった!

Problem

  • 会社の入り方というか、裏口がわかりにくい。
  • 裏口は鍵がかかっているので、開けに行かなければならない。
  • 土曜日の10時は気合いを入れすぎた?
  • 個人的に進捗が良くなかった...。(ディスプレイが無いなかで戦うのはキツかった)
  • 若干寒かったような気がする?
  • 会場の机の大きさが一人ひとつが限界っぽい?
  • イベントの目的(初心者の互助の場)が達成されたかは謎
  • ご飯行きましょう!って言うの忘れた...。

Try

  • 会社到着までのマニュアルを事前に公開しておく
  • 空調の設定温度が暖房25℃だったので、26℃に設定する。
  • ご飯行きましょう!って言う。もしくは、ご飯行く枠といかない枠を用意する
  • 自分は比較的経験者寄りなので、極力サポートしていく

まとめ

小規模だし、運営者として何をしたってわけではないのですが、人を集めてイベント開催することの楽しさがわかりました! 発表するようになったり、イベントで飲食したりするようになると、もっと楽しくなるんだろうなぁ〜と思いました!

ycyc-react-native.connpass.com

Expo + React Native + TypeScript環境を作る手順まとめ

概要

React Nativeによるアプリ開発では、Expoを使うと中々捗るのですが、*1
2018年11月3日にExpo SDKがv31.0.0にバージョンアップしました。 blog.expo.io

このリリースの大きなポイントとしては、2018年9月にリリースされたReact Native 0.57に対応したことです。 React Nativeはバージョン0.57でTypeScriptをサポートしました。 そのため、Expoを使ってもTypeScriptでの開発ができるようになります。

今回はExpo + React Native環境をTypeScript化するための手順をまとめます。

手順

プロジェクトを作る

$ expo init typescript-project
$ cd typescript-project

TypeScriptのインストールと設定

TypeScriptを使用するのでbabelは削除します。

$ rm babel.config.js
$ yarn remove babel-preset-expo

インストール

続いて、TypeScript関連のモジュールをインストールします。

yarn add typescript tslint @types/expo @types/react @types/react-native
yarn add -D tslint

tsconfig.json の設定

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es2015", "esnext.asynciterable"],
    "jsx": "react-native",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "types": []
  }
  "include": [                                                                                                            
    "./App.tsx"                                                                                                            
  ],                                                                                                                       
  "exclude": [                                                                                                             
    "node_modules"                                                                                                         
  ]
}

tslint.json の設定

{
  "extends": [
    "tslint:recommended"
  ],
  "rules": {
    "no-any": true,
    "prefer-array-literal": [true, { "allow-type-parameters": true }],
    "variable-name": [true, "ban-keywords", "check-format", "allow-pascal-case", "allow-leading-underscore"]
  }
}

以上でインストールと設定は終わりです

App.jsをTypeScript化

単純に拡張子を.tsxにするだけでTypeScript化ができます。簡単ですね。
jsxを含まない場合は、.tsとします。

$ mv App.js App.tsx

動作確認

TypeScriptの恩恵を受ける

App.jsをApp.tsxにTypeScript化したので、わざとコンパイルエラーとなるような修正を入れて、tslintやtscで怒られてみましょう。
App.tsxを開き、下記のコードを追加します。

...省略
const a: number = 1;                                                                                                       
const b: string = a  
...省略

定数bへの代入している行ではセミコロンが抜けているため、tslintで怒られるはずです。 文字列型の定数bに数値型の定数aを代入しようとしているため、コンパイルエラーになるはずです。

$ yarn tslint App.tsx

ERROR: App.tsx[5, 20]: Missing semicolon

予想通りの結果です!

$ yarn tsc -p ./ --noEmitOnError
App.tsx:5:7 - error TS2322: Type 'number' is not assignable to type 'string'.

5 const b: string = a
        ~

App.tsx:5:7 - error TS6133: 'b' is declared but its value is never read.

5 const b: string = a
        ~

Found 2 errors.

error Command failed with exit code 1.

こちらは、型が異なるも代入でfailしていることに加え、定数bを使用していないこともわかりました。 *2

シミュレーターの起動

最後にApp.tsxがこれまで通り動作するかを確認します。

$ yarn start

f:id:watasihasitujidesu:20181209150433p:plain

問題なく動作しましたね!

まとめ

今回はExpo + React Nativeの環境にTypeSCriptを導入する手順について書きました。 これまで通り(?)TypeScriptのinstallと設定を書き、.jsを.tsxに変更するだけでTypeScriptの恩恵を受けることができました。

TypeScriptの導入とは話が別ですが、Expo SDKがReact Nativeを追従する速さには驚きですね。 9月のリリースに対して11月に対応するのは素晴らしいです。 React Natibe のブログExpoのブログは要チェックですね。

*1:詳細は割愛

*2:--noEmitOnErrorオプションをつけることで、コンパイルエラーがあった場合に.jsファイルを生成しないようにしています。

Go言語でパッケージを作る方法

概要

Goでスクリプトを書いていると、巨大なmain関数ができてしまったり、1つのファイルに色々な関数がごちゃまぜになることはよくあります。 そこで、今回はGo言語のパッケージを作る方法について紹介します。

元々のスクリプト

下記のスクリプトをパッケージ化していくことを想定して説明します。

package main

import "fmt"

func foo(num int) int{
  return num * num
}

func main(){
  fmt.Println(foo(5))
}

main関数からfoo関数を呼んでいます。
foo関数は引数でint型を受け取り、引数を引数で除算した値を戻り値とします。

foo関数のパッケージ化

フォルダ構成の変更

まず、パッケージ化するために新たにフォルダを作成します。

mkdir sample

作成したフォルダにfoo.goを配置します。
この時点でのフォルダ構成は下記の通りです。

.
├── main.go
└── sample
    └── foo.go

foo.goの修正

配置したfoo.goの内容は下記の通りです。

package sample // 1

func Foo(num int) int{ // 2
  return num * num
}

パッケージ化するには、パッケージ化対象のファイルの先頭にpackage {パッケージ名}とします。
今回は、sampleフォルダを作っているので、パッケージ名をsampleにします。

また、関数名は大文字から始めます。
これはGo言語の仕様で、名前の先頭一文字が大文字になっていれば、パッケージの利用者側から参照可能となるためです。

main関数の修正

main関数は下記の通りに修正します。

package main

import (
  "fmt"
  "./sample" // 1
)

func main(){
  fmt.Println(sample.Foo(5)) // 2
}

sampleパッケージを作成したので、それを参照しなければなりません。
パッケージの参照はimport文の中で指定します。参照先は相対パスになります。

最後に、foo.goで定義した関数を呼び出します。
呼び出し方はパッケージ名.関数名となります。今回だとsample.Foo(5)です。

まとめ

今回はGo言語のパッケージについて紹介しました。

Go言語のパッケージ化は下記を行うだけです。

  • パッケージ化したい関数などを別フォルダの別ファイルとして定義し、関数名を大文字にする
  • パッケージ化したものをimportし、package名.関数名で呼び出す

パッケージ化することにより、あるまとまった機能単位に分割ができます。
何も考えずに書いてしまうと、ついmain関数が肥大化してしまうので注意が必要です。