bauer's diary

凡人の凡人による凡人のための備忘録

Redisでttlを指定していないキーを探し、削除する

1. はじめに

Redisを長く運用していると、いつの間にかSwap Usageが高騰したり、Freeable Memoryが枯渇してきて問題になることがあります。
そんなとき様々な原因が考えられますが、その一つにTTLを指定していないキーが容量を逼迫していたときの対処についてご紹介します。

2. Redis TTL とは

ttl コマンドでキーの有効期間を確認します。
有効期間を設定する場合は expire コマンドで指定します。

有効期間が設定されていないキーからは -1 が返ります。
存在しないキーからは -2 が返ります。
※Redis 2.6以前の場合は存在しないキーも -1 が返ります。
下記は使用例です。

> set key "hage"
OK
> get fuga
"hage"

// キーにTTL(単位はseconds)をセット
> expire key 100
(integer) 1

// TTLを確認
> ttl key
(integer) 98
> ttl key
(integer) 95

3. TTLが指定されていないキーを探す

ざっくりとTTLが指定されていないキーの数を確認する場合は、 info コマンドが有効です。
ここでは2つのキー全てがTTL未指定ということになります。

% redis-cli -h localhost info Keyspace
# Keyspace
db0:keys=2,expires=0,avg_ttl=0

それぞれどのようなキーなのか、Luaスクリプトを作成して探します。
ファイル名は find.lua とします。

local t = {}
local i = 1
for _, v in ipairs(redis.call('keys', 'prefix*')) 
do
  if redis.call('TTL',v) == -1 then
    t[i] = v
    i = i + 1
  end
end
return t
実行

db指定は必要であれば。

% redis-cli -h [redis-host] -n 0 --eval find.lua
結果

標準出力にキーが羅列していきます。

1) "hage"
2) "hagee"

4. TTLが指定されていないキーを削除する

色々やり方はあるかと思いますが、TTLが指定されていないキーに対してdelコマンドを羅列するシェルを生成するスクリプトにしました。
ファイル名は del_output.lua とします。

local t = {}
t[1] = "#!/bin/sh"
local i = 2
local cmd = "redis-cli -h [redis-host] del "
for _, v in ipairs(redis.call('keys', 'prefix*')) 
do
  if redis.call('TTL',v) == -1 then
    -- redis-cliのコマンドのターゲットをダブルクォーテーションで囲むため、keyの中のダブルクォーテーションをエスケープ
    local _v = string.gsub(v, '\"', '\\"')
    t[i] = cmd.."\"".._v.."\""
    i = i + 1
  end
end
return t
実行

シェルを生成します。

% redis-cli -h [redis-host] --eval del_output.lua > del_exec.sh

% chmod 755 del_exec.sh
% ./del_exec.sh
結果

削除が成功していくとこのように出力されるかと思います。

(integer) 1
(integer) 1
(integer) 1
(integer) 1

これでお掃除は完了です。

5. 最後に

運用期間が長いサービスだと、こういった作業が必要になることも出てくると思うので、キー毎に適切な有効期間を設定しておくことをオススメします。

certコマンドはSSL証明書の更新作業に便利だった

サーバー・インフラエンジニアをしていると定期的に必要な作業があります。
その一つがサーバー証明書の更新作業です。

これまでの更新確認作業

証明書の更新作業自体は、各プロジェクトで利用されているサーバ証明書の発行会社の手順に従って適用するかと思いますが、その際、ブラウザにアクセスして確認するのが一般的かと思います。
例えばChromeにてfacebookサイトの証明書を確認する場合、
デベロッパーツール

Security

Secure Originsから "https://www.facebook.com" を選ぶ

"Open full certificate details" をクリック
のような流れとなります。
f:id:kitakitabauer:20180512171605p:plain

すると、クライアントに取得したSSL証明書の情報が表示されるので、証明書の期限が有効か、また更新した場合はそれが延長されているかなどが確認できるかと思います。
f:id:kitakitabauer:20180512171434p:plain

とはいえ、大量に更新する必要がある場合、毎回アクセスしてこの作業を行うのは大変なので、今回 cert というコマンドを使って確認してみます。

cert インストール

github.com

Cert is the Go tool to get TLS certificate information.

cert はGo製のツールで、指定したドメインサーバー証明書情報を出力してくれます。
homebrew または go get でインストールします。

$ brew tap genkiroid/homebrew-cert
$ brew install cert
$ go get github.com/genkiroid/cert/...

インストールを確認します。

$ cert --version

これで準備完了です。

確認作業

更新したサイトのドメインを指定します。
一つでも複数でも指定が可能です。

$ cert facebook.com
cert facebook.com                                                                                                                                      17:56:07 [0]
DomainName: facebook.com
IP:         2a03:2880:f10c:83:face:b00c:0:25de
Issuer:     DigiCert SHA2 High Assurance Server CA
NotBefore:  2017-12-15 09:00:00 +0900 JST
NotAfter:   2019-03-22 21:00:00 +0900 JST
CommonName: *.facebook.com
SANs:       [*.facebook.com *.xx.fbcdn.net *.fbsbx.com *.xz.fbcdn.net *.facebook.net *.xy.fbcdn.net *.messenger.com fb.com *.fbcdn.net *.fb.com *.m.facebook.com messenger.com facebook.com]
Error:

Not After が、先程ブラウザで確認した日付と一致していることを確認できます。

LBやAWSのCloudFront、サーバーを直接指定する場合は "-k" オプションを使います。
結果は "-f json" オプションでJSONでも出力できますが、マークダウンで出力してくれる機能が便利で、GitHubのIssueでチケットに作業状況を更新しているので愛用しています。

$ cert -f md facebook.com
DomainName | IP | Issuer | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | --- | ---
facebook.com | 2a03:2880:f126:83:face:b00c:0:25de | DigiCert SHA2 High Assurance Server CA | 2017-12-15 09:00:00 +0900 JST | 2019-03-22 21:00:00 +0900 JST | *.facebook.com | \*.facebook.com<br/>\*.xx.fbcdn.net<br/>\*.fbsbx.com<br/>\*.xz.fbcdn.net<br/>\*.facebook.net<br/>\*.xy.fbcdn.net<br/>\*.messenger.com<br/>fb.com<br/>\*.fbcdn.net<br/>\*.fb.com<br/>\*.m.facebook.com<br/>messenger.com<br/>facebook.com<br/> |

opensslでも同じようなことはできますが、より手軽に使える印象なのでオススメします。

サクッと立てたAWS EC2でサクッとgoを実行したい

さくっと一時的にバッチ実行するインスタンスを立てたい。
そんなときのために構築手順をまとめておきます。

インスタンス起動

AWSコンソールから起動
必要に応じてEBSのボリューム増やしたり、カスタマイズします。

基本設定

インスタンスが起動したらsshします。

ssh ec2-user@xx.xx.xxx.xxx

yumリポジトリをupdateします。

sudo yum update -y

go インストール

yum installでもいいのですが、最新バージョンの追従が遅れるケースが多いので、
ここではwgetでバイナリを落とす形で進めます。

$ mkdir ~/tmp; cd ~/tmp
$ wget https://storage.googleapis.com/golang/go1.9.1.linux-amd64.tar.gz
$ tar zxvf go1.9.1.linux-amd64.tar.gz

wgetしたらPATHが通っている箇所にシンボリックリンクを貼ります。
例では1.9.1で固定していますが、通常は公式のダウンロードページから最新を探してURLを確認します。

$ sudo mv go /usr/local/
$ sudo ln -s /usr/local/go/bin/go /usr/bin/go
$ sudo ln -s /usr/local/go/bin/go /usr/local/bin/go 
$ sudo ln -s /usr/local/go/bin/godoc /usr/local/bin/godoc 
$ sudo ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt 

.bashrcにビルドの場所を指定します。
現時点ではディレクトリが存在していなくても問題ありません。

export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

// 保存してから読み込み
source ~/.bashrc

git インストール、作業リポジトリのclone

gitをインストールして、鍵設定をします。

$ sudo yum install -y git

$ cd ~

$ vi .ssh/config
StrictHostKeyChecking no
ServerAliveInterval 60
Host github.com
 User git
 Hostname github.com
 PreferredAuthentications publickey
 IdentityFile ~/.ssh/id_rsa

$ vi .ssh/id_rsa
// 自身の秘密鍵を貼るなり、scpで置くなり、生成してGitHubに登録するなり

パーミッションを600にします。

chmod 600 .ssh/config .ssh/id_rsa

実行したいgoリポジトリをgit cloneします。

git clone git@github.com:[owner_name]/[repository_name].git ./go/src/github.com/[owner_name]/

(必要なら) ag インストール

ここでは何かと必要になる ag をインストールしてみます。
MacはHomebrewですぐですが、Amazon Linuxでは yum が使えるのでCentOSと同じ手順で進めます。

ディレクトリ作成・ソースダウンロード
$ mkdir -p $HOME/local/source
$ cd $HOME/local/source
$ git clone https://github.com/ggreer/the_silver_searcher
ソースをmakeするための準備

コンパイラなどがないためインストールします。

$ sudo su -

yum -y groupinstall "Development Tools"
yum -y install pcre-devel xz-devel

exit
コンパイル・インストール
$ cd $HOME/local/source/the_silver_searcher

// このまま次の工程に進むとエラーが起きるため、zlib-develをインストールします。
$ sudo yum -y install zlib-devel

$ ./build.sh
$ sudo make install

インストール完了です。

$ which ag
/usr/local/bin/ag

(必要なら) aws cli インストール

こちらの手順もAWSを操作したい場合のみでかまいません。
pip のインストールスクリプトをダウンロードします。

curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
sudo python get-pip.py

pip を使用して AWS CLI をインストールします。

sudo pip install awscli

自身の ~/.aws/credentials や ~/.aws/config を見て設定します。

aws configure
AWS Access Key ID [None]: 
AWS Secret Access Key [None]:
Default region name [None]:
Default output format [None]:

実行

$ cd ~/go/src/github.com/[owner_name]/[repository_name]

// 依存パッケージがあれば、 dep ensure とか glide install でインストール

$ go run main.go rw.go


以上です。ご自身の環境に応じてカスタマイズして頂けたら幸いです。

Jenkinsで重いジョブと一緒に動かしたくない&特定の日時でしか実行したくないとき

小ネタですが、どなたかのかゆいところに手が届けばと思い、投稿します。

前提

  1. Jenkinsでサービスのジョブ実行を制御している
  2. バッチサーバーのリソースは諸事情により簡単に増やせない

といった方々にご一読頂けたら嬉しい内容です。

要件

  1. 特定の重いジョブ("Heavy Job" とします)とは一緒に動いてほしくない
  2. 1日1回、特定の時間帯しか動かしたくない

この2つの要件を満たすにはどうすればいいのかを考えた場合、
まず単純にHeavy Jobの下流として指定する方法が思い浮かぶのですが、
Heavy Jobが1日に複数回動くケースでは、2つ目の要件(1日1回、特定の時間帯しか動かしたくない)を満たすことができません。

ではクーロンで日時を指定して動かせば・・・となりますが、Heavy Jobはその名の通り重いジョブなので、実行時間が想定よりも延びてしまう可能性があります。

その場合、処理内容によっては一緒に動いてしまうと、バッチサーバのマシンリソースを食い合うことによって、OOMなど深刻な問題に陥いる可能性が生まれ、結果お互いのジョブ及び他の実行中ジョブにも影響を与えてしまいます。

どうすればよいか?

やり方は色々あるかと思いますが、自分が対応したのはこういったやり方です。

  1. Heavy Jobの下流で動くように設定 → 要件1を満たす
  2. 特定の時間帯のみ実行されるように制御する(下記例では0〜3時) → 要件2を満たす
#!/bin/sh -e
hour=`date -d "9hours" "+%k"`
hour=`echo $hour | tr -d " "`
echo "現在" $hour ""

# 0〜3時のみ実行
if [ $hour -ge 4 ]; then
  echo "AM0〜3時以外は実行しない"
  exit 0
fi# ここからジョブで実現したいことを書く

解説

Build → Execute shell にて時間帯制御のロジックを書きます。

hour=`date -d "9hours" "+%k"`

-d "9hours" オプションで9時間後の日付を取得することができます。
UTC環境で動く前提ですが、JSTとの差は+9時間なのでこのようにしています。

"+%k" はフォーマット指定です。0〜23の時間の数値で、1桁の場合はスペースが入ります。

これによって hour には JST での0〜23の数値が入ります。

hour=`echo $hour | tr -d " "`

次に、先程取得したhourの整形です。
trコマンドで半角スペースを削除します。

if [ $hour -ge 4 ]; then
  echo "AM0〜3時以外は実行しない"
  exit 0
fi

最後に、greater than or equal(>=)の意味を持つ -ge を条件に、実行したくない時間だったら exit 0 で抜けるようにしています。


解説は以上です。どなたかのご参考になれば幸いです。

AWS EC2インスタンスのIPアドレスを即座に確認する

はじめに

サービスを運用しているエンジニアなら障害検知システムを導入して、日々の障害に備えているかと思います。

自身が所属するプロジェクトではAWSをメインに使っていて、監視サービスが異常を検知した場合、slackにインスタンスのホスト名を表示しています。
すぐにsshして詳細を確認したいけど、DNS設定していないインスタンスはIPでなければsshできません。

一刻も早く確認したいのに、毎回AWSコンソールに入ってEC2ダッシュボードからパブリックIP か プライベートIPを確認しなければいけないのは効率が悪い…。

これを機に インスタンス名やIPアドレスを一覧するコマンド を準備しようと思います。
※今回のやり方以外にも色々やりようはあるかと思いますが、自己流のやり方を記事に残しておきます。

前提
  1. aws-cli インストール・アクセスキー設定済
  2. EC2の各インスタンスタグにホスト名と環境名をつけて運用している

自身のプロジェクトでそれらのタグをつけていない場合は、今回のコマンドから不要な部分のコマンドを削除すればよいです。

1. とりあえず全て表示してみる

ベースとなるコマンドは aws ec2 describe-instancesです。
AWSで運用しているインスタンスの数によりますが、このまま叩くとEC2インスタンス情報が膨大なJSONにて表示されます。

2. 表示要素を絞り込む

JSONにはEC2インスタンスの様々な情報が含まれているため、必要最低限の要素だけを表示するようにします。

--query オプションを使うことでそれを指定できます。
検索パイプで渡して jq コマンドを実行している例もありますが、今は --query だけで事足りるようです。

$ aws ec2 describe-instances --query 'Reservations[].Instances[].{Env:Tags[?Key==`Environment`]|[0].Value,InstanceId:InstanceId,Name:Tags[?Key==`Name`]|[0].Value,PrivateIp:PrivateIpAddress,PublicIp:PublicIpAddress}'

[
    {
        "InstanceId": "i-XXXXXXX",
        "PublicIp": null,
        "Name": "host1",
        "Env": "Development",
        "PrivateIp": "10.XX.XXX.XXX"
    },
    {
        "InstanceId": "i-XXXXXXX",
        "PublicIp": "13.XXX.XX.XX",
        "Name": "host2",
        "Env": "Production",
        "PrivateIp": "10.XX.XXX.XXX"
    },
    {
        "InstanceId": "i-XXXXXXX",
        "PublicIp": null,
        "Name": "host3",
        "Env": "Staging",
        "PrivateIp": "10.XX.XXX.XXX"
    },
…
]

自分のプロジェクトでは、Environmentタグに環境名を、Nameタグにインスタンス名を設定しています。
"[?~]" で条件による絞り込みが可能です。値はバッククォートで囲います。
デフォルトだと配列形式の出力となり、Valueに対する項目名がわかりにくいので、MultiSelect Hash連想配列にしています。
尚、Reservation とは インスタンスを起動するアクション のことです。

3. テーブル形式で出力する

JSON形式よりもヒューマンリーダブルなテーブル形式にします。
こちらはコマンドに --output table を追加するだけです。

$ aws ec2 describe-instances --query 'Reservations[].Instances[].{Env:Tags[?Key==`Environment`]|[0].Value,InstanceId:InstanceId,Name:Tags[?Key==`Name`]|[0].Value,PrivateIp:PrivateIpAddress,PublicIp:PublicIpAddress}' --output table

---------------------------------------------------------------------------------------------------------
|                                           DescribeInstances                                           |
+-------------+----------------------+------------------------------+----------------+------------------+
|     Env     |     InstanceId       |            Name              |   PrivateIp    |    PublicIp      |
+-------------+----------------------+------------------------------+----------------+------------------+
|  Production |  i-XXXXX1            |  host1                       |  10.XXX.X.XXX  |  13.XXX.XXX.XXX  |
|  Staging    |  i-XXXXX3            |  host3                       |  10.XXX.X.XXX  |  None            |
|  Staging    |  i-XXXXX4            |  host4                       |  10.XXX.X.XXX  |  13.XXX.XXX.XXX  |
|  Development|  i-XXXXX6            |  host6                       |  10.XXX.X.XXX  |  None            |
|  Production |  i-XXXXX2            |  host2                       |  10.XXX.X.XXX  |  None            |
|  Staging    |  i-XXXXX5            |  host5                       |  10.XXX.X.XXX  |  None            |
…
4. 環境でソートする(最終形)

バラバラなまま表示することは運用上少ないので、環境名でソートします。
3. のコマンドの結果をパイプでつないで sort_by というソート関数を噛ませます。

今回ここが割りと肝だったのですが、Environmentタグを定義していないインスタンスがある場合、ソート関数を単純に追加すると下記エラーが起きます。

In function sort_by(), invalid type for value: None, expected one of: ['string'], received: "null"

なので、 not_null という関数で、Environmentタグの値がnullのインスタンスを事前に除外します。


上記2点を踏まえて変更した最終形のコマンドがこちらです。

$ aws ec2 describe-instances --query 'Reservations[].Instances[?not_null(Tags[?Key==`Environment`].Value)][].{Env:Tags[?Key==`Environment`]|[0].Value,InstanceId:InstanceId,Name:Tags[?Key==`Name`]|[0].Value,PrivateIp:PrivateIpAddress,PublicIp:PublicIpAddress}|sort_by(@,&Env)[]' --output table

---------------------------------------------------------------------------------------------------------
|                                           DescribeInstances                                           |
+-------------+----------------------+------------------------------+----------------+------------------+
|     Env     |     InstanceId       |            Name              |   PrivateIp    |    PublicIp      |
+-------------+----------------------+------------------------------+----------------+------------------+
|  Development|  i-XXXXX1            |  host1                       |  10.XXX.X.XXX  |  13.XXX.XXX.XXX  |
|  Development|  i-XXXXX2            |  host2                       |  10.XXX.X.XXX  |  None            |
|  Development|  i-XXXXX3            |  host3                       |  10.XXX.X.XXX  |  None            |
|  Development|  i-XXXXX4            |  host4                       |  10.XXX.X.XXX  |  None            |
|  Development|  i-XXXXX5            |  host5                       |  10.XXX.X.XXX  |  None            |

…

|  Staging    |  i-XXXXX10           |  host10                      |  10.XXX.X.XXX  |  None            |
|  Staging    |  i-XXXXX11           |  host11                      |  10.XXX.X.XXX  |  13.XXX.XXX.XXX  |
|  Staging    |  i-XXXXX12           |  host12                      |  10.XXX.X.XXX  |  None            |
+-------------+----------------------+------------------------------+----------------+------------------+

これで必要最低限の全EC2インスタンス情報が表示されるようになりました。

A. ホスト名で絞りたい

特定のホスト名で絞りこんで表示したい場合は、3. のコマンドに対して --filter で絞り込むだけです。

$ aws ec2 describe-instances --filter 'Name=tag:Name,Values=host10' 
 --query 'Reservations[].Instances[?not_null(Tags[?Key==`Environment`].Value)][].{Env:Tags[?Key==`Environment`]|[0].Value,InstanceId:InstanceId,Name:Tags[?Key==`Name`]|[0].Value,PrivateIp:PrivateIpAddress,PublicIp:PublicIpAddress}|sort_by(@,&Env)[]' --output table

---------------------------------------------------------------------------------------------------------
|                                           DescribeInstances                                           |
+-------------+----------------------+------------------------------+----------------+------------------+
|     Env     |     InstanceId       |            Name              |   PrivateIp    |    PublicIp      |
+-------------+----------------------+------------------------------+----------------+------------------+
|  Staging    |  i-XXXXX10           |  host10                      |  10.XXX.X.XXX  |  None            |
+-------------+----------------------+------------------------------+----------------+------------------+

4. のコマンドでもいいですが、このケースではnullチェックやsortは不要かと思います。

B. 環境で絞りたい

A. と同じく --filter をかけます。

$ aws ec2 describe-instances --filter 'Name=tag:Environment,Values=Staging' --query 'Reservations[].Instances[?not_null(Tags[?Key==`Environment`].Value)][].{Env:Tags[?Key==`Environment`]|[0].Value,InstanceId:InstanceId,Name:Tags[?Key==`Name`]|[0].Value,PrivateIp:PrivateIpAddress,PublicIp:PublicIpAddress}' --output table

---------------------------------------------------------------------------------------------------------
|                                           DescribeInstances                                           |
+-------------+----------------------+------------------------------+----------------+------------------+
|     Env     |     InstanceId       |            Name              |   PrivateIp    |    PublicIp      |
+-------------+----------------------+------------------------------+----------------+------------------+
|  Staging    |  i-XXXXX10           |  host10                      |  10.XXX.X.XXX  |  None            |
|  Staging    |  i-XXXXX11           |  host11                      |  10.XXX.X.XXX  |  13.XXX.XXX.XXX  |
|  Staging    |  i-XXXXX12           |  host12                      |  10.XXX.X.XXX  |  None            |
+-------------+----------------------+------------------------------+----------------+------------------+

複数環境を指定したい場合は、バラバラに出力されないようにsortもかけたほうがよいです。


以上、どなたかのご参考になれば幸いです。

Stripeをシステム導入する際に知っておいたほうがよいこと 〜その2〜

前回の記事

kitakitabauer.hatenablog.com

今回はStripeを使った開発上で困ったことを紹介します。
ただし、こちらは2017年08月時点の情報なので、中には改善されていることがあるかもしれないのでご了承ください。

テストカードの発行元設定が全て日本以外

https://stripe.com/docs/testing#cards
このページに、テスト環境でのみ使えるクレジットカードがありますが、
サイト運用にあたり、リスク回避のため日本以外で発行されたカードは許可していないのですが、テストカードに日本のものがないため、テスト環境では日本以外のカードも使えるようにしなければなりませんでした。

テストカードに関してはその点以外、イレギュラーケースとして請求が拒否されるカードなど様々なカードが用意されていたので、不都合はありませんでした。

定期購読プランの料金は変更不可

定期購読のプランの領収書に税を表示するために、開発途中で税込みから税抜きの価格設定に変更し、サブスクリプションを作成する際に事前に計算した税価格を渡すようにしました。
作成済プランの料金を変更することはできないので、当たり前ですが、本番運用開始までに十分検証期間が必要です。
そういったこともあり、​税込み価格での設定を前提として、途中から税率を変更できたらうれしいなぁと思いました。
税抜き価格のプランのため、税率変更の場合はおそらく新しいプランを用意する必要がありそうなので…


以降のケースは全てStripeからリクエストされるWebhookに関する事柄です。

Webhookのパターン検証の術がない

Webhookは様々なタイプのリクエストがあります。

  1. 支払い期限を迎えたときの請求を作成するリクエス
  2. 支払いが成功したときのリクエス
  3. 支払いが失敗したことにより、サブスクリプションがキャンセルしたときのリクエスト…

これらリクエストのイベントタイプに対してエンドポイントを設定し、サービス固有の処理をフックすることができるのですが、どういったデータがWebhookに載ってくるか事前にわかればテストしやすかったなぁと感じました。
本番稼働後では遅いので、それらがテスト環境で簡単に検証できたらうれしいものです。

ダッシュボード:イベントログのタイプをフィルタリングする術が手打ちしかない

f:id:kitakitabauer:20170812172517p:plain
Stripe利用ユーザには売上や入金先設定やプラン、Webhookなどを管理できるダッシュボードが提供されているのですが、
f:id:kitakitabauer:20170812174215p:plain
Webhookのログをフィルタリングしたいときに、毎回タイプを手打ちする必要があるのはやや手間でした^^;

ダッシュボード:Webhookが失敗したことを一覧しづらい・検知する術がない

Webhookが失敗したら何らかのユーザ対応が必要なこともあるのですが、常にログを見続けているわけではないので、気付きとなるような仕組みがほしいところです。
テスト環境の話ではありますが、開発中にWebhookのリクエストが失敗を繰り返していて、Stripe本国のエンジニアから "大量のリクエストが送られていることと、それらが全て失敗している" という旨のメールを頂いて初めて失敗し続けていることがわかりました。
失敗の原因は実装ミスだったのですが、気づいた時点で既に Webhookへのリクエストが止められていて、その後しばらく再開しなかった ため、開発スケジュールに影響が出てしまいました。
ダッシュボードで失敗がサマリされているビューがあって、slackなどに通知できたら最高です。


慣れていないこともありましたが、総じてWebhook周りのトラブルが多かったです。。

以上、どなたかのご参考になれば幸いです。

Stripeをシステム導入する際に知っておいたほうがよいこと 〜その1〜

はじめに

Webサービス運営するにあたり、特にECサイト構築の中でも決済部分は重要かと思います。
自社システムでもそういったニーズがあり、どのオンライン決済サービスを選定すべきか慎重に判断しなければなりません。

まずはじめに、他のオンライン決済サービスとどう違うのか、既にまとめられている情報を確認します。
この他にも実に様々なサイトで比較が行われています。
f:id:kitakitabauer:20170811183540p:plain
(オンライン決済サービス15選。手軽な決済方法で、アパレルECはもっと身近になる | ファッションEC最前線 | ネットショップ担当者フォーラム より引用)

有名所は個人でも使ったことがある方も多いPayPalかと思います。
続いてSPIKEホリエモンが取り上げていたこともあり知ってる方もいるのではないでしょうか。

今回の選定ポイントは、

  • 定期購読(サブスクリプション型課金)が可能か
  • 開発効率(対応言語やAPI及びドキュメントの充実度)
  • ユーザーに課金操作をスムーズに行ってもらいたい

でした。

その中で、PayPalとSPIKEは

  • PayPalは、PayPalアカウントがないと決済ができない
  • PayPalはページ遷移をはさむ
  • SPIKEは定期購読に対応していない
  • SPIKEはビジネスプランでないとVISAにしか対応していない
  • SPIKEはAPIを利用する場合5,000円/月かかる

といったことがネックとなりました。(2017.08時点の情報)

そういったことも踏まえて、自社では Stripe を採用する運びとなりました。

Stripeは下記特徴があり、結果ほとんど困ることなく開発を進めることができ、リリースできました。

  • 2016年10月から日本でも導入できるようになり、日本法人も存在
  • 事前審査や開設の初期費用も不要なので初期導入が楽
  • 振込サイクルが早い
  • テスト環境での検証が簡単
  • 振込手数料もなし、返金手数料もなし

開発効率

言語対応

様々な言語のクライアントが用意されており、
その中で今回Golangのクライアントを使って開発しました。
github.com

ドキュメント

全て英語ですが、サンプルコードが充実しているので非常にわかりやすかったです。
開発ドキュメント:Documentation
APIリファレンス:Stripe API Reference

例えば、カード情報を取得するAPIを確認する場合、
curl
f:id:kitakitabauer:20170812131510p:plain
Go
f:id:kitakitabauer:20170812131520p:plain
といったサンプルリクエストが言語ごとに記述されています。

テスト環境

定期購読形式課金のテストのしやすさは、決済サービス選定の大きなポイントになると思います。
Stripeでの定期購読形式の特徴は下記の通りで、

  • 定期購読の支払い期限のタイミングでStripeがWebhookによる課金リクエストを行う
  • Webhookにより課金ステータス(成功か失敗か)が更新される
  • 特定のWebhookリクエストをイベントとして、自社サーバのエンドポイントに紐付けてユーザに対する個別の処理を行うことができる

更に テスト環境で利用できるクレジットカードが準備されていた のでそれを使って本番さながらのテストが可能でした。


今回はここまでです。

次回は、そんな便利なStripeの中でも開発上で困ったことを紹介したいと思います。

次回の記事はこちら。
kitakitabauer.hatenablog.com