Shogo's Blog

Jul 22, 2019 - 3 minute read - go golang

Goで指数的バックオフをやってくれるgo-retryを書いた

完全に車輪の再発明なんですが、他の実装には色々と思うところがあり書いてみました。 shogo82148/go-retry MOTIVATION カッコいいインターフェースが欲しい インターフェースは lestrrat さんのこの資料を参考にしています。 GoらしいAPIを求める旅路 (Go Conference 2018 Spring) from lestrrat 「これ、Loop Condition だ」のあたりで、なるほど!と思ってインターフェースを真似てみました。 このインターフェースに沿って、lestrratさん自身が実装した lestrrat-go/backoff があります。 しかし、個人的にちょっと実装が複雑だなと感じたので、もうちょっとシンプルに書けないかとやってみました。 Context サポート 先行実装たちは Context がGoに取り込まれる前からあるので、 Contextに対応したインターフェースが後付だったり、 そもそもContextに対応していなかったりします。 Context未対応の Go 1.5 はすでにサポート対象外なので、もう Context が存在しない実行環境は考えなくてよいはずです。 SYNOPSIS Loop Condition Interface 使い方は lestrrat-go/backoff と大体一緒。 指数的バックオフに必要な各種パラメーターをポリシーとして与え、リトライのためのループを回します。 // 指数的バックオフの各種パラメーターをポリシーとして定義 var policy = retry.Policy{ // 初回待ち時間 MinDelay: 100 * time.Millisecond, // 最大待ち時間 MaxDelay: time.Second, // 最大試行回数 MaxCount: 10, } func DoSomethingWithRetry(ctx context.Context) (Result, error) { retrier := policy.

Jul 16, 2019 - 2 minute read - perl aws lambda

AWS SDK for Perl Lambda Layerを公開しました

ハイラルからこんにちは。 AWS LambdaでCGIを蘇らせる で作成した Perl Custom Runtime 用の AWS Lambda Layer ですが、 中でイベントのハンドリングをしているモジュールを AWS::Lambda として CPAN で公開したところ、 AWS SDKを入れて欲しい との要望が来ました。 完全にネタとして作成したモジュールですが、いるんですね使う人。 というわけで AWS SDK を含んだ AWS Lambda Layer を公開しました。 使い方 公開レイヤーを使う AWS公式ではPerl用のSDKは提供していないので、Pawsという非公式SDKを使いました。 何も考えずにテキトウにインストールしてみたらSDKだけで121MBありました。 Perl本体が85MBなのでSDKのほうがでかい。 AWS Lambdaで作成できる関数は250MBが上限なので、流石に半分SDKに持っていかれるのはつらかろうと、Perl本体とは別のレイヤーに分けてあります。 レイヤーは最大5つまで登録できるので、Perl本体(例: arn:aws:lambda:ap-northeast-1:445285296882:layer:perl-5-30-runtime:2 )に加えて 以下のレイヤーを追加することで、Paws を呼び出すことができるようになります。 arn:aws:lambda:ap-east-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:ap-northeast-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:ap-northeast-2:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:ap-south-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:ap-southeast-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:ap-southeast-2:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:ca-central-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:eu-central-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:eu-west-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:eu-west-2:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:eu-west-3:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:sa-east-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:us-east-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:us-east-2:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:us-west-1:445285296882:layer:perl-5-30-paws:1 arn:aws:lambda:us-west-2:445285296882:layer:perl-5-30-paws:1 いつの間にかマネージドコンソールから編集ができるようになっていたので、開発がはかどりますね。 カスタムランタイムでもAWS Lambdaのマネージドコンソールから内容の編集ができる・・・? Perl も編集できるぞ・・・ pic.twitter.com/4228rG0hca — Ichinose Shogo (@shogo82148) July 16, 2019 ZIP アーカイブを使う ビルド済みのZIPアーカイブも公開しています。 以下のURLを指定して新規レイヤーを作成することで利用できます。

Jul 2, 2019 - 1 minute read - polyglot go golang perl php ruby

GoのバイナリをRubyスクリプトとしても扱う

$ ruby --help Usage: ruby [switches] [--] [programfile] [arguments] (中略) -x[directory] strip off text before #!ruby line and perhaps cd to directory (後略) なんか Ruby にも -x あるらしいので。 package main import ( "fmt" "runtime" ) const script = ` #!ruby puts "Hello Ruby World!!\n" __END__ ` func init() { runtime.KeepAlive([]byte(script)) } func main() { fmt.Println("This is Go world!!") } はい。 $ go build -o main main.go $ ./main This is Go world!

May 27, 2019 - 1 minute read - go golang time

サーバーの時刻を伝える time wellknown uri を実装してみた

インターネットをさまよっていたら、 /.well-known/time の存在を知ったので、雑に実装してみました。 使い方 うまいこと共存できそうだったので、HTTP/WebSocketで時刻同期するWebNTPを書いた で作成した WebNTP の一部として実装してあります。 shogo82148/go-webntp $ go get github.com/shogo82148/go-webntp/cmd/webntp $ webntp -serve :8080 $ curl -I localhost:8080/.well-known/time HTTP/1.1 204 No Content X-Httpstime: 1558915632.285965 Date: Mon, 27 May 2019 00:07:12 GMT 仕様 HTTPには「予約済みのURI」というものが定義されています。(RFC5785)。 Well-Known URIs Let’s Encrypt でドメインの所有権確認に使用される ACMEプロトコル(RFC8555) や、 Mastodon のユーザーディスカバリーに使用する WebFinger(RFC7033)等々、 近年話題になったサービスの裏方で使われています。 /.well-known/acme-challenge ACMEプロトコル(RFC8555) /.well-known/webfinger WebFinger(RFC7033) Time over HTTPS も Well-Known URIs を利用するプロトコルのひとつです。 /.well-known/time Time over HTTPS specification 仕様としては非常に単純で、サーバー側は HTTP の HEAD に対して、 Date ヘッダーをつけたリクエストを返すだけ。 より高精度な時刻を得るために X-HTTPSTIME ヘッダーに秒未満の情報を入れた Unix タイムスタンプ を返すこともできます。

Apr 17, 2019 - 3 minute read - go golang aws cloudformation mackerelio

CloudFormationのMackerel用インテグレーションを作ってる話

Mackerel は mkr コマンドを用いて cli から操作ができます。 mkr コマンドを用いると 監視ルールを GitHub で管理 したり、 カスタムダッシュボードを管理したり、といったことができます。 しかし、個人的に以下のような不満があります。 サービス、ロール、ホスト、新ダッシュボード等々、監視設定以外のリソースに対応していない 旧ダッシュボードは対応しているんだけど、新ダッシュボード対応がまだ 新ダッシュボードのUIは使いやすくてすごくいいんだけど、コピペや一斉置換ができないので、テキストで管理したい 出力がJSONなのつらい JSON手で書くの難しくないですか? メトリックスの送信設定と監視設定の管理が別になってしまう カスタムメトリックス送っているのに監視設定を忘れた、みたいなことが起こる メトリックスの送信設定については、以前 サーバーレスでCloudWatchメトリクスをMackerelに転送する で CloudFormation上での管理を実現しました。 ここにさらに Mackerel の監視設定を追加できれば、最強なのでは?とやってみました。 例 あれこれ説明する前に例を見てもらったほうがわかりやすいと思うので、こんなことができますよ、という設定例から。 例1: レスポンスタイムの99%パーセンタイルを監視する Mackerel の AWSインテグレーション は ALB に対応していますが、 レスポンスタイムのメトリックスは平均レスポンスタイムだけです。 「平均」は代表的な統計値ですが、全体としては速いんだけど一部のリクエストだけ遅い、という状況を見逃してしまいます。 レスポンスタイムの大まかな分布をパーセンタイルで把握したい、ということはよくありますよね? (K社でZabbixを使って監視していたときによくお世話になった) 今回作ったインテグレーションを使えば、以下のように「Mackerelのサービス定義」「メトリックスの転送設定」「監視設定」が CloudFormation のテンプレートとして表現できます。 AWSTemplateFormatVersion: 2010-09-09 # Type: Mackerel::* を使うためのおまじない Transform: - AWS::Serverless-2016-10-31 - Mackerel - JSONString Resources: MackerelService: Type: Mackerel::Service Properties: Name: "awesome-service" # メトリックスを転送する Lambda 関数 MetricsForwarder: Type: AWS::Serverless::Application Properties: Location: ApplicationId: arn:aws:serverlessrepo:us-east-1:445285296882:applications/mackerel-cloudwatch-forwarder SemanticVersion: 0.

Feb 28, 2019 - 1 minute read -

新元号の候補約4510万件が漏洩!!

平成の次の元号候補、4510万4656件の漏洩が確認された。 テキスト形式 (301MB) gz圧縮版 (108MB) 政府は「新元号、情報管理を徹底へ 漏洩なら差し替え」との方針を示しており、 早急な差し替え対応を行うと思われる。 2019-04-01 追記: $ curl -s https://t.co/OCaFAriJIt | cat -n | grep 令和 726041令和 無事漏洩してました!!! — Ichinose Shogo (@shogo82148) 2019年4月1日 追記ここまで と、まあ、二番煎じなわけですが。 新元号は漏洩すると変更されるということなので常用漢字2文字の全組み合わせ約228万通りをすべて記載したテキストファイルを作成しました。漏洩させていきましょう。https://t.co/G06utDbgka pic.twitter.com/8UcPDqNdXo — いんぐらむ (@kazuokiriyama) 2019年2月26日 ただ、このツイートのリプライのもあるとおり漏洩漏れがあるようですし、 新元号に使われる可能性のある漢字は常用漢字ではない可能性だってあると僕は考えいます。 だって、お国のやることですからね。下手したら改元に合わせて「常用漢字の見直しもやる」ということだって考えられます。 というわけで、僕は ShiftJIS, EUC-JP で表現可能な文字列まで範囲を広げることにしました。 Unicodeへの統一が進んでいるとはいえ、 ShiftJIS, ECU-JP で動いているレガシーなシステムもあるでしょうし、この範囲に収めるだろうなという予想です。 ShiftJISからUnicodeへの変換には規則性がないので、変換テーブルを使う必要があります。 Goのコードを漁った ら以下の変換表を参照していたので、これを利用しました。 https://encoding.spec.whatwg.org/index-jis0208.txt 非漢字も含まれているので、雑に漢字を絞ったあと、 curl https://encoding.spec.whatwg.org/index-jis0208.txt | grep CJK | cut -f3 | cut -d' ' -f1 | sort | uniq > kanji.txt 直積列挙スクリプトに突っ込めば出来上がり。

Feb 12, 2019 - 2 minute read - go golang aws lambda

外部サービスでもIAM Roleで認証がしたい!

背景 外部サービスのAPIやWebHookを叩くときには、多くの場合 API トークンが必要になります。 もちろん API トークン無しでも叩けるサービスはありますが、GitHub APIのようにトークン無しではリクエスト数が大きく制限されたり、 一部機能が制限されてしまう場合があります。 外部連携サービスが増えてくると、このAPIトークンをどうやって管理するかが問題になってきます。 プロダクションに投入されているサービスは普通複数のサーバーから構成されており、各サーバーにAPIトークンを配布するのはちょっと面倒です。 この問題に対して、以下のようなことが行われて来ました。 プライベートネットワークからのアクセスに限定した Proxy を立てる APIトークンの管理は Proxy に任せる DevOpsが注目され、Slackの利用が広まったころに、このような目的で書かれたProxyサーバーがよく登場しました。 社内IRCをSlackに移行した時にやったこと この記事で紹介されている kayac/nopaste Slackboard〜Slackプロキシサーバ in Go〜 Slackプロキシサーバ〜slackboard〜を利用したメルカリのSlack活用法 App::Ikachan - 様々なサーバのバッチ処理の結果等を IRC のチャンネルに通知するサーバ (IRCはHTTPで動いているわけではないし、大本の目的もコネクション維持だけど、認証も代理でやってくれる) しかし、これらのサーバーはSlack専用だったりIRC専用だったりします。 Slackだけじゃなくって、GitHubにコメント登録したり、Mackerelのグラフアノテーションを投稿したり、 他のサービスとも連携したい! 最近はどんなAPIもHTTPで提供されるようになったので(IRCは・・・ウッ・・・そんなのなかった)、もっと汎用的に書けるのではとやってみました。 実装 APIトークンの保管場所として AWS Systems Manager Parameter Store を採用しました。 Parameter Store からAPIトークンを取り出す部分と、実際にAPIを叩く部分は AWS Lambda を使用します。 各サーバーに Forward Proxy デーモンを立てておき、APIを使いたいアプリケーションはこのProxyを経由するようにします。 この図ではEC2インスタンスを例にしていますが、IAM Roleを付与できるAWSのサービスであれば何でも (ECS, Lambda, CodeBuild, etc.) APIにアクセスすることができます。 外部サービスのAPIを叩くのが Lambda 関数というのもポイントです。 APIトークンをヘッダーに設定するのか、URLの一部に含めるのか、クエリストリングに含めるのか・・・といった設定方法はサービスによってまちまちです。 Lambda 関数がこの辺の設定を肩代わりしてくれるので、APIトークンの扱いを気にする必要はありません。 また、API利用時にうっかりAPIトークンを漏らしてしまう心配もなくなります。 APIトークンの管理をしたいんじゃない!!ただ、APIを叩きたいだけなんだ!!!! という思いから、Proxy デーモンはシークレットに関しては何も関与しません。

Feb 7, 2019 - 3 minute read - acme python aws lambda

Let's Encrypt の証明書取得を AWS Lambda でやってみた

背景 ここ数年で暗号化されていないHTTPは減り、常時TLSが当たり前になってきました。 公開用のページはもちろん、開発段階のページでもTLSは必須です。 普段はAWS上で開発をしているので、AWS Certificate Managerを利用することが多いのですが、 ちょっとしたお遊びにELBやCloudFormationを使うのはオーバーキルです。 そこで EC2 にもインストールできて、無料で使える Let’s Encrypt を使って証明書を発行することを考えました。 Let’s Encrypt で発行できる証明書は期間が90日と短く、60日ごとの自動更新が推奨されています。 証明局とAPIとAPIクライアントの実装例は提供するから、あとの自動化部分は自前で頑張ってねという感じなので、自動化部分を頑張らないといけません。 今回は実行環境として AWS Labda、ACME(Automatic Certificate Management Environment)クライアントとして certbot、 認証方法に dns-01、認証に必要なDNSレコードの書き換えに AWS Route 53 を使用する、という構成にしました。 ソースコードをGitHubに挙げたのと、前回と同様に AWS Serverless Application Repository へ上げたので、ぜひご利用ください。 shogo82148/acme-cert-updater shogo82148/acme-cert-updater on AWS Serverless Application Repository 関連手法 Amazon Linux 2 に certbot をインストールして使う Amazon Linux 2 のドキュメントに TLS 対応のウェブサーバーを立てる例が載っています。 Let’s Encrypt で証明書を取る方法も紹介されているので、まずはこれを利用することを考えました。 付録: Amazon Linux 2 での Let’s Encrypt と Certbot の使用 - チュートリアル: Amazon Linux 2 で SSL/TLS を使用できるように Apache ウェブサーバーを設定する この方法は以下の理由から見送りました。

Jan 31, 2019 - 3 minute read - go golang aws mackerel

サーバーレスでCloudWatchメトリクスをMackerelに転送する

背景 サーバーの監視にMackerelを使っているのですが、 用意されているメトリクスでは足りずカスタムメトリクスを追加することが多々あります。 Mackerel Agent Pluginsを利用すればメトリクスを増やすこと自体は簡単なのですが、 Agentを設置するインスタンスが増えるので、サーバー保守の手間が増えてしまいます。 僕のユースケースでは監視対象はたいていAWSのマネージド・サービスなので、 AWS CloudWatch に投稿されたメトリクスが Mackerel で見れれば十分なことが多いです。 そこで、以下の記事を参考に AWS Lambda と CloudWatch Events を組み合わせて、Mackerelへメトリクスを転送するスクリプトを書いてみました。 Amazon LambdaでCloudWatchのメトリクスをMackerelに監視させる デプロイしてみる 今回はなんと!皆さんの AWSマネジメントコンソールから、クリックひとつでデプロイできるようにしてみました! mackerel-cloudwatch-forwarder ・・・と、その前に下準備が必要です。 MackerelのダッシュボードからAPIキーをコピーしてきて、 AWS Systems Manager パラメータストアに Secure String として登録しておきます。 スクショでは Mackerel のものだと分かりやすいよう /development/api.mackerelio.com/headers/X-Api-Key という名前をつけました。 この名前を後で使うので覚えておきましょう。 次に AWS Lambda の画面を開き、「関数の作成」をクリックします。 「一から作る」「設計図」「AWS Serverless Application Repository」の3つの選択肢が表れるので、 「AWS Serverless Application Repository」 を選択します。 検索BOXに「Mackerel」と入れると、mackerel-cloudwatch-forwarderが 出てくるので、それを選択します。 なお、この選択肢はデフォルトでは表示ないので、「Show apps that create custom IAM roles or resource policies」にチェックを入れましょう。 アプリケーションの内容とパラメーターの設定画面に移ります。 「ParameterName」にパラメーターストアに登録したパラメーター名を入れましょう。 スクショの例では「/development/api.mackerelio.com/headers/X-Api-Key」を入力します。 「カスタムIAMロールを作成することに同意」のチェックボックスを選択したあと、デプロイ!

Jan 30, 2019 - 3 minute read - go golang mysql aws

CloudFormationでECSタスクのドレインをやる

やってみたはいいものの、1年後には仕組みを忘れていそうなのでメモ。 背景 昔はサービス毎にポコポコEC2インスタンスを立てていたのですが、 幸か不幸かインスタンスのリソースが余り気味でした。 そこで、最近流行りのコンテナ技術に乗っかって Amazon ECS (Amazon Elastic Container Service) を使って、 ひとつのインスタンスに複数のサービスを載せようと目論みました。 ちょうどその頃、Spot Fleetというものを使うと、 スポットインスタンスをお手軽に借りられるという話を聞いたので、 Spot Fleet + ECS で格安クラスターを作ってみよう!と手を出してみました。 (・・・もちろん、Fargateが東京リージョンで使えるようになったことは知っているけれど、スポットインスタンスの価格に負けてしまった・・・) AWS Fargate 東京リージョン サービス開始のお知らせ AWS Fargate で最大 50% の値下げを発表 ECS最適化インスタンスの更新問題 クラスターを作るだけなら、そう難しくはなく、インスタンス起動時にAmazon ECS-Optimized Amazon Linux AMIを使うだけです。 問題はこのイメージは定期的に更新される、ということです。 更新情報を流しているSNSトピックがあるので、これをサブスクライブしておくと、時たま更新通知が来ます。 Amazon ECS-Optimized Amazon Linux AMI の更新の通知のサブスクライブ この更新には機能追加はもちろん、セキュリティーフィックスも含まれているので、 なるべく早く新しいイメージに移行する必要があります。 移行は大まかに以下の手順で進めます。 新しいAMIイメージに更新した Spot Fleet を作成する 古いインスタンスに残っているタスクをいい感じに終了する(ドレイン) 突然殺すとユーザーにエラーが見えてしまうので、受付中のリクエストを捌き切ってから終了しないといけない ドレインが始まるとECSがタスク数を調整するために、新しいインスタンスにタスクをお引越ししてくれる ドレインが終了したら、古いインスタンスをシャットダウンする ここで問題になってくるのが「古いインスタンスに残っているタスクをいい感じに終了する(ドレイン)」の部分。 コンソールからポチポチするのも面倒なので、自動化したいところ。 しかし、いろいろとドキュメントをあさってみたのですが、「APIかawscliを叩く」「SNSとAWS Lambda をうまいこと組み合わせて頑張る」みたいな方法しか見当たらない・・・ しかもAWSの公式ブログ コンテナインスタンスのドレイン How to Automate Container Instance Draining in Amazon ECS Amazon ECS におけるコンテナ インスタンス ドレイニングの自動化方法 ・・・みんなどうやってるの・・・?