ハイラルからこんにちは。
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を指定して新規レイヤーを作成することで利用できます。
$ 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!
インターネットをさまよっていたら、 /.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 タイムスタンプ を返すこともできます。
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.
平成の次の元号候補、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 直積列挙スクリプトに突っ込めば出来上がり。
背景 外部サービスの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 デーモンはシークレットに関しては何も関与しません。
背景 ここ数年で暗号化されていない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 ウェブサーバーを設定する この方法は以下の理由から見送りました。
背景 サーバーの監視に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ロールを作成することに同意」のチェックボックスを選択したあと、デプロイ!
やってみたはいいものの、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 におけるコンテナ インスタンス ドレイニングの自動化方法 ・・・みんなどうやってるの・・・?
AWS RDS には IAM 認証を使って接続する機能があります。
MySQL および PostgreSQL に対する IAM データベース認証 IAM 認証情報を使用して Amazon RDS への接続をユーザーに許可する方法を教えてください。 これを使って接続するGo言語のSQLドライバを書いてみました。
https://github.com/shogo82148/rdsmysql 使い方 IAMデータベース認証はデフォルトで無効になっているので、まずはこれを有効化します。 次に AWSAuthenticationPlugin を認証方式に指定して、新しいユーザーを作りましょう。
IAM データベース認証の有効化と無効化 データベースアカウントの作成 CREATE USER jane_doe IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS'; 他のSQLドライバはimportするだけで使えるのですが、 rdsmysqlではAWSへの権限情報を設定しなければならない都合上、 sql.Register を自前で呼び出す必要があります。 とは言っても、AWS SDKがいい感じに設定ファイルとか環境変数とか読んでくれるので、 rdsmysql.Driver にAWSセッションを渡すだけです。
c := aws.NewConfig().WithRegion("ap-northeast-1") s := session.Must(session.NewSession(c)) d := &Driver{ Session: s, } sql.Register("rdsmysql", d) db, err := sql.Open("rdsmysql", "jane_doe:@tcp(db-foobar.ap-northeast-1.rds.amazonaws.com:3306)/") if err != nil { log.Fatal(err) } defer db.Close() あとは通常のMySQLドライバとして呼び出すだけです。 go-sql-driver/mysql のラッパーになっているので、 DNS等の書き方はこれに準じます。 認証部分は rdsmysql がやってくれるので、パスワードは空でOKです。 パスワードの管理から開放されて楽ですね!