続 カッコの付け方

AWSを始めとしたクラウドコンピューティング全般と、唯一神emacsにおける()の付け方についてだらだら書きます

適切な方法でBigQueryスケジュールドクエリを実行する

BigQuery(BQ)のスケジュールドクエリは、普通に設定すると設定したユーザー、つまり人の権限で実行されます。 これを読んでる人は何が問題かわかっているはずで、退職時にそのユーザーアカウントが消滅すると動かなくなる、だけでは済まずに設定自体が消滅します。それを避けたいのでサービスアカウント(SA)で実行したい、ただそれだけの話だけど間違った手順が広まってしまっている。

まず最初に、SAの鍵は不要です。SAでスケジュールドをやるには鍵が必須かのような認識は、今すぐ捨ててください。

参考にすべきところ

https://cloud.google.com/bigquery/docs/scheduling-queries?hl=ja#using_a_service_account

Googleの公式をしっかり読むのが結局の近道です。信頼に足る情報源としてはここが一番

コンソールから設定する

DevelopersConsole(DC)から設定する。これは時期はわからないが、比較的最近の機能のはずなので、古い記事だと直接APIを叩くしかないようにも見えますが、今は一応DCから設定はできます。

そのまえに、誰が、何をするかをちゃんと把握する

通常のスケジュールドクエリは、おおよそ下記の手順で作っているはずです。

  • AさんがSQLを作成する (実際に流して結果を確認する)
  • そのままスケジュールドクエリとして登録する
  • スケジュールドクエリはAさんの権限で実行される

これをSAで実行する場合

  • AさんがSQLを作成する
  • スケジュールドクエリとして登録するときに、実行ユーザーとしてSAを指定する
  • スケジュールドクエリはSAの権限で実行される

最後のスケジュールドクエリの実行はBQリソース=テーブルやデータセットに対するアクセス権限は、AさんとSAへの認可を同じものが必要。 もう一つ、SAで実行するために、スケジュールドクエリを設定するときに、SAを使う権限というものがAさんに必要となります。 また、なぜかGoogle管轄のプリセットSAに対して iam.serviceAccounts.getAccessToken が足らないとメッセージが出ることがあります。なぜ必要か説明できなし、100%発生するわけでもないです*1が、従いましょう。

  • SAに対してBQリソースへの認可
  • 設定を行う人に対して 対象SAを使う認可 (Web-UIから設定したいならば、プロジェクト内のSAを列挙する認可)
  • 原理は不明だが、プリセットのSAに対して iam.serviceAccounts.getAccessToken

DCでやってみる

まず、Aさんの権限で実行してみる。参考までに下記を付与したとする。ちなみに書き込み権限は下記にはないので、調整してください。

bigquery.jobs.create
bigquery.jobs.list
bigquery.routines.get
bigquery.routines.list
bigquery.savedqueries.create
bigquery.savedqueries.delete
bigquery.savedqueries.get
bigquery.savedqueries.list
bigquery.savedqueries.update
bigquery.transfers.get
bigquery.datasets.get
bigquery.tables.get
bigquery.tables.getData
bigquery.tables.list
bigquery.transfers.get
bigquery.transfers.update
resourcemanager.projects.get

この状態でAさん自身の実行でスケジュールドクエリを定義できることを確認しておく。

次にSAを使って同じスケジュールドクエリを定義する。SAを作成して、上記Aさんと同じBQリソースに対する権限を付与する。 そこから、AさんにはSAを列挙&使う権限が存在しないため、付与する必要がある。

cannot find sa

プロジェクトの規模にもよるが、一番安全に倒すならば、Aさんに与えるプロジェクトレベルの認可としては、SAの取得とリストのみに絞り、SAを使う認可はSA自体の権限(AWSでいうリソースポリシー)として付与するのがよい。具体的にはSAを列挙する専用ロール(SA列挙ロールと名付ける)をつくって、その中身を下記としプロジェクトでAさんに付与

iam.serviceAccounts.get
iam.serviceAccounts.list

さらにSA自身の認可(このサービス アカウントにアクセスできるプリンシパル)としてAさんにServce Account User: roles/iam.serviceAccountUser のロールを付与すればOK

これでスケジュールドクエリの設定は成功する。が、設定は成功しても実行時にまれに下記のメッセージでコケることがある。

P4 service account needs iam.serviceAccounts.getAccessToken permission. Running the following command may resolve this error: gcloud iam service-accounts add-iam-policy-binding <SA-user-email> --member='serviceAccount:service-<project-id>@gcp-sa-bigquerydatatransfer.iam.gserviceaccount.com' --role='roles/iam.serviceAccountTokenCreator'

これが発生したりしなかったりで、原因はわからないが、素直に SAのポリシーに service-<project-id>@gcp-sa-bigquerydatatransfer.iam.gserviceaccount.com に対して iam.serviceAccounts.getAccessToken を付与する

bqコマンドでやってみる

bq コマンドでやる場合ならば、SA列挙ロールは付与していなくてもOKで、SA自身の認可だけあればよい。

ServiceAccount Binding
あとは bq コマンドで設定する。下記はすでに定義済みのスケジュールドクエリ(転送設定)の実行ユーザーだけ差し替える方法。詳しくは公式より https://cloud.google.com/bigquery/docs/scheduling-queries?hl=ja#bq_3

bq update \
--update_credentials \
--service_account_name=<SAのメアド> \
--transfer_config \
<スケジュールド・転送設定のリソース名>

転送設定のリソース名は

bq ls --transfer_config --transfer_location=us

とかで確認できる。ここで再度まとめておくと、AさんがSAで実行するように設定するので、AさんがSAを利用する権限が必要となる。 SA鍵を発行して、SA鍵で認証して、SA自身になりきり、SA自身をスケジュールドクエリ実行ユーザーとすることはできるが、そんなことしなくていいし、危険だから今すぐやめたほうがいい。AさんがSAを使える、もっと突っ込むと iam.serviceAccounts.actAs があればよい。で、このiam.serviceAccounts.actAs をプロジェクトレベルで振ってしまうと危ないので、SAのポリシーバインディングで付与する。

落ち穂拾い

DCから既存のスケジュールドをSA実行に切り替えたい

クエリのスケジューリング  |  BigQuery  |  Google Cloud

DCからはできないと書かれていますが、こことは別の方法でできます。BQコマンドが嫌な人はこちらで

from data transfer

select edit

you can find service-accounts

他のプロジェクトで作ったSAで実行したい

結論から言うと、できるが、おすすめしない。 まず最初に、SAを作成したプロジェクトと、リソースがあるプロジェクトは同一である必要はない。AWSはアカウントごとに認証装置があるからこそクロスアカウントとかいう仕組みが必要だが、GCPはプロジェクト単位で認証は発生していない。だからクロスプロジェクトしていても、認可さえ与えれば問題ない。
が、SAをプロジェクト跨ぎで特定プロジェクトのリソースに縛り付ける(e.g. GCEのSAや、今回のスケジュールドクエリのSA)に使うことは、GCPのIAMによる 組織ポリシーにより禁止されている、デフォルトで。これ、組織ポリシーと名前はついているが、プロジェクトレベルでのポリシーとして動いている constraints/iam.disableCrossProjectServiceAccountUsage 、詳しく知りたい人は自分で調べてください、スクリーンショットだけ貼っておく。

org policy

ここまで読んでいる人なら大丈夫だと思うが、念の為

  • SAと、スケジュールドを仕込むプロジェクトは同一
  • ジョブからアクセスするBQリソース(dataset, table ...) はプロジェクトまたいでいてOK

*1:筆者の環境では一度発生すると2度目以降は不要でした。プロジェクトによってはそもそも発生しない場合もあり。

AWS Lambdaが何故か何回もinvokeされてる、しかも成功してるのに

AWS Lambdaで久々にハマったので、理解を深めて残しておこうと思う。 もうGAからずいぶん経っているが、自分が初期のLambdaをよくわかってない、ということがわかった。

全部すっ飛ばして結論

  • Lambdaを同期実行 + AWS SDK/CLI などで単体実行するときは、Client側のタイムアウト設定を、Lambda側のタイムアウトよりも長くする
  • Boto3(Python)の場合は、1分以上Lambdaの実行にかかる場合は、これを守らないと3回ぐらいLambdaが勝手にInvokeされる

概論・今となっては特殊な動かし方の説明

Lambdaは基本なにか別サービスと連携して使うものです。しかし単体でも実行させることは可能です。(どこかのテックブログにはできないと書いてましたがこれは嘘です。)

今回の目的は、この完全に単体でLambdaを実行することです。結構レアケースだと思いますが、もはや世の中の情報が溢れかえっているのでこのレアケースを正確に示すためだけに外堀の解説を超雑にします。

同期・非同期実行

想像ですが、初期のLambdaは同期実行しかできなかったはず。同期とは、想像通り呼び出し後、Lambdaが完走するまで待ちます。結果もすぐに取り出せます。

対して非同期は文字取り、呼び出しが成功した時点で、一旦成功が呼び出し側に返ります。
アナタの言いたいことわかります、「じゃあ、全部非同期でよくね?」
しかし、おそらくアナタの考える非同期とちょっと違います。「Invokeすると Invoke-Idが帰ってくるんでしょ?それをポーリングすればいいんでしょ?」と思って調べるとそんなAPIないです
Lambdaの非同期実行の結果を取得する場合、別のサービスに投げる・失敗時向けとしてはDLQに投げる設定が可能で、その結果を能動的に 取得する必要があります。

サービス連携と同期実行

一番多いパターンは API Gateway + Lambda のパターンだと思いますが、この場合は基本、同期実行。非同期もできるはずですが、書きたいことから外れるので端折る。
API Gateway がリクエストを受けて、Lambdaを叩いてすぐに結果がほしいから、なんとなく同期で納得するとおもう。Httpリクエストで何分も待たせることないしね。
同期実行で動いているパターンでも、サービス連携している場合がほとんど。ここで言いたいのは、同期実行 ≠ 単体実行 ということ

単体・同期実行やりたい理由

「そもそもなんでお前、単体実行したいの?」については
権限をギンギンに絞りたい場所・リソースにアクセスする必要があるのだが、処理は超簡単でシュッと結果だけがほしい
という、今となってはレアケースとなってしまった、初期のLambdaの使い方にマッチすることがあったから。

「まあ、そのケースでも EventBridgeとかでCron実行するでしょ?」となるので、ホンマに単体実行なんてレアやとおもいますが、もっと正確に言うと、定期実行するジョブの一つの工程で、上記のケースが発生したため使った。

本題・問題発生パターン

単体・同期実行で発生しうる問題は
呼び出しがタイムアウトした場合、boto(core)の機能によりリトライされて何回もinvokeされる
詳しく説明

同期実行の場合、Lambdaのinvokeは、Lambdaが実行完了 or 異常終了するまで処理まちします。この処理まちというのが、一般的なhttpsリクエストと同じです。 Lambda側で最大実行時間(タイムアウト)は設定できます、が一般的なhttpsリクエストと同じなので、client側がちゃんとLambda側のタイムアウトまで待ってくれるように仕込まないと、ちょっと重い処理ならclientが早々にあきらめて、永遠に成功しない。

awscli / boto3 で lambda invoke を叩いた場合、このclient側のタイムアウトは、botoの標準設定となります。ちなみにbotoは60秒です。ちなみにbotoは自動リトライ機能もついています。デフォルト3回だったはず

実験

labmda関数は timeoutと名付けて、下記コード。サンプルにsleep足しただけ。
これで、Lambda側の timeoutは 2分にしておく。

import json
import time

def lambda_handler(event, context):
    time.sleep(90)
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

awscliで叩いてみる。全部垂れ流すように --debug 付きにしといたほうがいい。

$ aws lambda invoke --function-name timeout result.txt --debug

2022-04-23 17:33:41,590 - MainThread - botocore.retries.standard - DEBUG - Max attempts of 3 reached.
2022-04-23 17:33:41,590 - MainThread - botocore.retries.standard - DEBUG - Not retrying request.
2022-04-23 17:33:41,590 - MainThread - awscli.clidriver - DEBUG - Exception caught in main()
Traceback (most recent call last):
  ...
socket.timeout: The read operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
 ...
urllib3.exceptions.ReadTimeoutError: AWSHTTPSConnectionPool(host='lambda.us-west-2.amazonaws.com', port=443): Read timed out. (read timeout=60)

ご覧の通り、read timeout = 60で、client側がLambdaをまちきれずに諦めている、それにリトライも3回やってる。
Lambda側のメトリックスだけみると、「成功してるのになんで3回も叩いてるの?アホなの?」となる。
awscliでも timeoutを指定できるので、今度は2分 client側も待つようにする。

あとから考えると aws cliを使うのはやめて、認証情報をつけることさえできれば、素直にcurlなり普通のhttp clientでやったほうがええです。

$ aws lambda invoke --function-name timeout result.txt --cli-read-timeout 120 --debug
 ...
2022-04-23 17:44:23,495 - MainThread - awscli.formatter - DEBUG - RequestId: ....
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

今度は成功する。

対策boto3の場合

import boto3
from botocore.config import Config

conf = config(read_timeout=120, retries={'max_attempts': 0})
client = boto3.client("lambda", config=conf)
client.invoke(...)

retryも黙らせたほうがいい。

令和4年の Desktop Linux

Linuxをクライアントとして使い始めて2年ぐらいたった。ubuntu 22.04 LTSが出る前だが、日記的に書いとく。

結局どやねん?

ubuntu 20.04 LTS @ ThinkPad X1 6gen を使っているが、生活はできる。
できるが、いろいろ面倒なのは違いない。

じゃあ、Macなら面倒無いのか?と言われるとそうでもないかもしれないが、当たり前だが情報量が圧倒的に少ないので、それなりに自己解決していけないと苦しいな。
自分が結構困ったときのメモ、(解決方法は書かないので、期待しない)

ハイバネーション

解決方法はググれば出てくるので自分で調べてください。だが、「未だにハイバネーションシュッとできないのかよ!」
注意は、Deep Sleepという完全にディスクに逃がしてバッテリー消費を抑える、普通の人が知ってるハイバネーションを実現するには、ハードウェア側でも対応が必要なようで、ワイのハードでもファームの更新が必要だった。ファームの最新情報も Ubuntu Software (アップデートのランチャーみたいなの) に出てくるので、ファーム更新自体は楽だが。

wayland or x.org

これも未だに決着がついてないみたいだ。何年やってんだよ
いろいろ改善が進んでそうだが、GPU積んでるならドライバの関係でx.orgらしい。私は Fractional Scaling (高解像度ディスプレイを、老眼仕様にするやつ) で謎のノイズが出たのでwaylandにしたが、別の問題がいろいろ出てきた。

wayland とかなんやねん?

長い話になるから、端折って書くと、Linux等はCLI至上主義だから、GUIなんておまけで超ファットな仕組みでGUI(ウインドウマネジャー)を実現してた。サーバーはそれでいいが、デスクトップでいつまでもCLI至上主義ってどうなん?いい加減古いやり方辞めへん?となって waylandが出たが、X 想定で書かれているアプリケーションは当然動かないよね。「めんどくせー wayland対応なんてやらねーよ」というアプリ開発者も結構いる、いたようで、主要なアプリはwaylandでも動くが、マイナーなのは、、、という状態。

キーボードリマップツール

人によるが、俺はこれがないと生きていけない。x.orgで動いているものが waylandで動くとは限らない。おれは xremap を使ってる、ありがたや。

スクリーンショット

skitch と同じでええんやが、これがなかなかない。
shutter が機能的に最強だが、waylandで動かない。 flameshotはかなり近い操作感だが、日本語打てない。 ksnipも日本語打てない(直接ね、コピペはできる)。結局普通の標準機能でスクリーンショット撮って、shutter で編集が俺にはあってる。

Fraction Scalingしてもブラウザ画面汚い

他のOSみたいにHiDPIなディスプレイを150%,200%とかにスケールしても、キレイに表示されない。gnomeで出来ているであろう設定画面自体はパッキパキのフォントで描画されてるので、やり方次第なんだろうけど、これは悲しい。俺はしょぼいPCにLinux入れて「軽いぜ〜ウェーイ」やりたいのとちゃうねん!ゴリゴリの最強スペックをLinuxで使いたいんや!4Kモニタを200%で使って、えもいわれぬ美しさを求めてるんや!おれの 4K 200%のスクリーンショットがこんな感じ

正直よーわからんが、これでもwaylandのほうが x.orgよりはキレイらしいで。時間が解決するのかな?

snap..

詳しくはしらんが、xxxenvみたいな特定アプリごとにランタイムを使い分けるような類のもの、Dockerよりももうちょっとゆるいのかな。俺はインフラ屋だったから、この手のシステム汚さない系は「これ、ええやん!」と最初は思ったが、それこそスクリーンショットやら、クリップボードツールやらのように、単体解決するようなアプリならそれでええんですが、firefox拡張機能とか、emacsとか他のアプリとプロセス間通信するやつは結構難儀になる。 firefoxは 22.04(21ぐらいから?) snapになってしまったらしい、うーん。

いいところ

それなりに軽い・余計な心配ない

ほとんどの人が、結局クリーンインストールだと気に入らないはずなので、いろいろいじる。だから変なプロセスがリソース食ってるということはまずない。初期構築さえ乗り越えれば、そんなに不満なくやりたいとに集中できる。

Docker

Linuxは今の所 Docker Desktopがないので、税金払わないという点はええのだが、rootless dockerとか、user mappingずらしとかいろいろいじってしまうと野生のdocker-compose拾ってきて upで動かないというので何度もハマってる。Dockerは余計なことしないで、サーバーと同じ動かし方でええと思います/それができるのがLinuxの良さ。

結論、どのOSもたいして進化してないね

デバイスドライバ問題は20年前から比べてだいぶマシにはなっているはずですが、テンション上がるような進化はなんにも起こってない。特にここ10年は
じゃあ、WinやMacはどやねん?やけど、どっちもここ10年は全然進化してないね。あえていうなら仮想化ぐらい? スマホ・小型デバイス向けが忙しいからもう、世界レベルでDesktop OSなんかはもうどうでもいいということか。
例外はvistaLonghornとか言われてたときぐらいやわ、ワイの記憶によると。

Log Volume (GCP Cloud Logging) の料金が高杉なんだが

f:id:iga-ninja:20220416152634j:plain

Google Cloud Logging が異常な請求額になったため、その原因と対策を説明する。請求レポートの Log Volume (Cloud Logging) がなんか高いなーと調べてる人は正解!ここです。

Loggingは結構使われる重要なコンポーネントにもかかわらず、優れたエントリが非常に少ない。そんな中でも見るに値するものは下記しっかり解説されていると思います。

blog.g-gen.co.jp

上記で大部分の知識は得られるとおもうので、金額が高くなった理由を書く。

原因と対策

  • GCSのAPIアクセスログがデフォルトで_Defaultにも Sink(シンクは日本人には意味かぶりするのでこう記載する)される
  • GCSをバリバリ触るなど、極端にAPIアクセスが多いプロジェクトの場合、30daysの保持期間はどうでもいいので、Sink対象を_Defaultから除外する
  • GCSのログを別途保持したいのならば、ログバケット(_Default)ではなく、GCSバケットなどにSinkする

仕組み

予備知識何もなしでは意味がわからないので、上記の参考エントリを見てもらうことを推奨しますが、玄人向けに雑に書く

ログバケット/ログストレージ

まずこれ、GCSのバケットとは違います。GCSの一覧には出てこない、Googleが管理管轄するやつなので、GCSバケツのようにユーザーには直接は見えない。 _Default_Required の2つがデフォルトで存在し、お金かかるのは_Defaultの方。

ログエクスプローラで見えているのは、ログストレージの中だけ

まずログルーターを理解する。

https://cloud.google.com/logging/docs/images/routing-overview-17-09-21.png?hl=ja

ありとあらゆるログは、一旦 Cloud Logging APIに入る。ここまで金はかからないはず
次にログバケットにSinkされる。ログエクスプローラでは、このログバケツの中のものだけが見えている。ここがダダじゃない、盲点
Sink先は、GCS,Pub/Sub,BigQueryなど選べるが、これらへSinkしたものはログエクスプローラは関係ないし、Sinkすること自体には金かからず、GCSなどのストレージコストがかかる。

ログエクスプローラ_Defualt, _Required どっち見てるの?

両方見てる。自分はこれが理解できてなかった。そもそもLoggingは30日タダという昔の知識があった。それなのに「あれ、30日よりも前に辿れるけど、どういうこと?どこかにSink(ログバケットではなく、GCSとか)してたら勝手にそっちみてくれる?」と深く考えてなかったが、これは _Required 分が見えていたのだった。 コンソールいうとこんな感じ

f:id:iga-ninja:20220416151454p:plain

請求の詳細にはなんと出る?

Log Volue (Cloud Logging) と出ている、SKU-ID: 143F-A1B0-E0BE 「あれ?Loggingは30日でしょ?なんでこんなに請求されるの?」 これを見ている人と同じ原因で俺も調べ始めた。

「もうちょっといい表現あるだろ」とは思うが、まあ間違っていないし、一言で説明するのは無理か。 30日の保持期間はこれとはほぼ関係なく、ログバケットに取り込んだ量を指します。課金対象の _Default

で、悪いことにこのログバケットへのSinkが超高い $0.5/1GB

監視できるの?

結構丁寧にUIにも出ている、気づかんかったけど。

f:id:iga-ninja:20220416151458p:plain

Monitoringに飛ぶリンクもついていて、それで加算でないかはわかる。このときのメトリックスはxxx Ingest とか書いてある。ユーザーから見たら ingestとは逆の感覚だな。

対策 _Default に特定イベントが飛ぶことをやめる

俺の場合は、GCSのイベントだった。まあ、AWSの感覚からしたら、GCSのイベントがデフォルトで全部取り込まれるのは 頭おかしい 文句は後でまとめて書くが。

_Default については、どれをSinkの対象とするかの設定ができるので、 GCSのイベントを除外する設定を入れたら料金は収まった。繰り返すけど、30日を縮めてもいみないからね。

f:id:iga-ninja:20220416153133p:plain

NOTに追加するか、除外に指定するかはお好きなように

f:id:iga-ninja:20220416152901p:plain

_Default への Sinkやめたら、 GCSへのSinkとかも失敗しない?

大丈夫、失敗しない。上で示した概要図みて。GCSなどログバケット以外へのSinkは _DefaultからのSinkではないから。

ダメ出し・文句

Cloud Logging は AWS CloudWatch Logsと大体同じで、AWSの感覚からすると(かつて、今は違う)「30日タダは太っ腹」に見えたが、GCSのログを全部デフォルトで取得する判断はないわ、ありえへん。だってAWSですら、ただ単にCloudTrailでS3にファイル吐き出すだけでも、デフォルト Offにしてるのに、さらに10倍以上も単価の高いCloudWatch Logsに全部打ち込むとか、正気の沙汰じゃない。

もちろん、ログストレージが高いのはわかる、ログエクスプローラやログからメトリックス作れるので高機能だ。だが、デフォルトで全部突っ組むのはありがた迷惑。
思惑としては、ログエクスプローラでなんもしなくてもGCSのログ見れまっせ!監査もバッチリアピールしたいんだろうし、アプリ開発者は自分の撒いたログがシュッと見れればええんやろ、だから邪魔くさいインフラ部分の説明は覆い隠したいんだろう。でもそこでユーザーへの説明端折っても、ユーザーの学習をスポイルしてもあんまいいことだとは思えんな、現にクレーム来るからわざわざ料金アラームのリンクつけてるんでしょ?

気づいたのは GCSだけだが、同じようにサービスAPIが大量にログを作るサービスは他にもあるだろうきっと。

俺が選挙に行く理由

選挙直前だが、なんとなく書きたくなったので書く。俺が選挙に行く理由、それは 祭りに参加したいから!

俺の今まで

俺と選挙

俺はいわゆるロスジェネ世代。俺が若者のときももちろん、「最近の若者の投票率ガー」と言われてたが、そんなの構わず、1回を除いてすべての選挙権は行使している。唯一投票できなかったのは、2週間ぐらい風邪で寝込んだことがあり、そのときだけは無理だった。皆勤賞の俺でもそういうときはある。ちなみに俺はどこかの党員でもないし、活動家でもない。

俺と親父

親父がよく言ってた。「誰に投票しても同じや!」「こいつは当選させないという投票なら行くわ!」。俺の知る分では、親父は一度も投票しなかったはず、もしも当選させない選挙があっても行かなかっただろう。俺が選挙に行くのはこれの反動かもしれない、だって俺のからだの70%は反骨精神でできているから。

俺の20代

20代は独身で、リア充ではなかった。別に政治に興味があったわけでもなかったが、暇つぶしに選挙に行ってたのかもしれない。もしも20代の俺がリア充だったらどうだろう?それでも多分選挙は行ってたと思う、だって俺はひねくれものだから。

俺と氷河期

バキバキの就職氷河期です。今ほど「政治がわるい」「あの人が戦犯」とは言われてなかったなあとおもう。俺自身は運良く生きのこれている、そのせいか政治を恨んだりとかの感情はなかった。ただ、同世代の苦しんでいる人に「根性がないだけ!自己責任だ」とは絶対に言えない、逆にそういうの聞くとまじでムカつく。

俺と妻と家庭

妻も「投票いかなきゃ」「どうせならこの人に入れる」と選挙には前のめりの人だ。結婚すると、子供ができると選挙どころじゃないこともあるだろうが、幸い俺は選挙に行きやすい環境にいる。期日前投票したこともあったな。

自分語りの理由

誰もが俺と同じような環境にいたわけではない。どうしても行けないことだってあるだろう。政治に興味ない人もいるだろう。一票なんて無駄と考えるひともいるだろう。それは構わないし尊重すべきだ、みんな事情があるし、俺と同じ環境でもない。外に晒す文書なんでこういうやつがこういうふうに感じた補足。

選挙は祭り?

投票所に行く、夜に選挙速報を見る、自分の入れた票とか支持政党とかの結果をみる。これだけで楽しい、祭りだエンタメだ。しかしただの祭りと違って、祭りのあとの日々にも影響する。

投票しないということ

投票は義務ではない、強制ではない。投票しないということは、世の中の他の人に委任したとも言えると思う。よくわからん自分が投票して、変な人が政治家になるぐらいなら他人に判断を委ねるというのもあるだろう。もしくは単にめんどくさいという人もいるだろうが、どちらによ悪いことだと思わない。

投票義務化という話もあるらしいが、あんまりいいことだとは思えない。一生政治に無関心という人生もあっていい、そういう人がいるほうがおもろい。

投票するということ

世の中に与える影響としては、誰かを支持したという1票になる。もしくは嫌いな候補者を当選させないための1票を対抗にいれたということになる。俺はこの意味よりも、自分自身に与える影響のほうが大事だとおもっている。

自分で投票のすること意味=決断を他人任せにしない

他人任せが嫌なら立候補しろやというのもあるが、まあ俺じゃ当選しないわ、ええ年した大人ならわかるやろ? ほぼ皆勤賞で投票してるが、政権が変わったときに自分はどこに入れたかとか、大体覚えてる。 「なんであのときあそこに入れたんだろう?」「俺の1票無駄だったな」「白票でも意味あるやろ」後悔することも、徒労感もいろいろあった。 でも俺は選挙に参加した。だから責任がある、成功も失敗も後悔も無力感も全部自分のことになる。だから次の選挙はどうしようと考えられる。

素人が口出すなだと?やだね

若いやつは政治に興味ないとか、自分のこととして考えられないだとか、ごちゃごちゃ言ってるのを見るが、若い頃の俺は大した考えもなく祭りだと思って選挙に行った。それで何がわるい? マスコミに影響されて投票した、ポピュリストを見抜けずに投票した、圧勝するのにわざわざ投票した、どんな結果であれ参加しないと自分の経験にはならない。投票しないことは委任でもあり、その考えは尊重されるべきだが、どうしても当事者意識は薄れる。「俺以外の世間のバカどもがしょうもないやつに騙されて当選させた」、と政治が悪くなってきたら、あとから文句いうことができる、だって自分は参加していないのだから、全部他人のせいにできるから。

若いうちは政治なんかわからん、そのとおりだ。俺だってそうだった、ジジイになった今でも政治なんかようわからん。誰かの思惑に騙されてるのかもしれない。でも、あのときの判断は良かった・悪かったははっきり自分の中に残っている。そして次はこうすると考えるようになれた。それはテキトーだろうが投票してきたからだ断言していい。

もしも政治わからん若者が、だれかに扇動されてヤバそーなやつが当選しそうになっても構わん。少なくとも俺は阻止するために対抗に票いれる、次の選挙が仮にそんなことになっても自分で納得できるように選挙に行く、いつでも。

で、ホントに選挙は祭り?でいいの?

ええやろ。自分の選挙権をどう使おうが他人にとやかく言われる筋合い無い。ノリで選んで全然OK、選挙よりもおもろいことあるなら、そっち優先でもいい。ただひとこというと「選挙、結構おもろいで!」

バケットポリシーとCloudTrail Logの組み合わせ

今回書くことは、もうすでに手垢がついているので、なるべくさらっと書きます。

2022/02/01今更ですが更新 ACLやめればすべて解決!

【アップデート】S3でACLを無効化できるようになりました #reinvent | DevelopersIO

テーマはバケットポリシーとクロスアカウントアクセスです。本エントリでも少しだけ触れますが、原理はこちら

dev.classmethod.jp

新機能サイコーな話なんですが、運用視点で書きます。

f:id:iga-ninja:20210323000614p:plain

バケットポリシー発動条件

バケットポリシーを設定したら発動とかそんなしょうもない話ではない、バケツの所有者と・オブジェクトの所有者が一致しているときのみ発動します
これは頭に叩き込む、ここがブレるとわけがわからなくなる

挙動の確認

バケツの所有者がクロスアカウントアクセスによって書き換えられることはありません。よって、クロスアカウントアクセスの書き込みによってバケツの所有者は絶対に変わりません。しかしオブジェクト所有者はアップロードしたAWSアカウントののものになります。SS貼っときます。
これが 普通のオブジェクト バケツ=オブジェクト
f:id:iga-ninja:20210323000141p:plain これが 問題のオブジェクト バケツ≠オブジェクト f:id:iga-ninja:20210323000052p:plain

上記に書きましたが、バケツに対するAPI ListBucket(ListObjectではない、ここが怪しいひともググって)は通ります。aws s3 ls でオブジェクトの有無は見える。しかしGetObjectは通りません。上記のSSは一つのバケツに、普通のオブジェクトと所有者違いのオブジェクトを混ぜた。こんな状態でも普通のオブジェクトに対しては GetObjectは通る

  • バケツ所有者とオブジェクト所有者が違うとバケットポリシーは効かない
  • バケツ所有者とオブジェクト所有者が違っても、(バケットポリシーでListObjectがついてたら) aws s3 ls は通る
  • 一つのバケツに、所有者同じ/所有者異なるオブジェクトが混在することはできる、その場合、所有者同じオブジェクトに関してはバケットポリシーは有効

クロスアカウントアクセスによる書き込みは、実はすべてのAWSアカウントで発生している

今回のテーマはこれです。クロスアカウントからの書き込みって、特殊なことに思えますが、実はほぼ全てのAWSアカウントで発生しています。
どこで? 確認できているところで、CloudTrailとELBのログ(他にもあるかも)

そもそもAWSが出すログってどう出してる?

CloudTrailのS3Exportをポチポチで作るとS3側にはバケットポリシーが書かれています。

CloudTrail の Amazon S3 バケットポリシー - AWS CloudTrail

自分のアカウントと異なる、AWS自身が直接管轄しているアカウントからの書き込みが発生します。つまりこれも クロスアカウントによる書き込みです なんにも考えずにクロスアカウントで書き込んでしまうと、そのバケツのオーナーも読めなくなってしまうので bucket-owner-full-control を必須にしています。ELBもバケットポリシーからみると、同じ挙動のはず。

何が困るの?

bucket-owner-full-control がついてるので、バケツ所有者(のアカウント)であれば問題ないです。が、ここでバケットポリシーによるクロスアカウントアクセスをしようとするとだめ。バケットポリシーはバケツ所有者とオブジェクト所有者が違うとバケットポリシーは効かない という鉄の掟があるから。

どうすればいい?

これからの人

aws.amazon.com

です、設定方法は最初にポイントしたところを見てください。正直なところ、この設定をして損はしないと思うので、今後作製するS3バケット一律この、オブジェクト所有者とバケツ所有者を一致させる設定をしておいて問題ないでしょう。

もうオブジェクトいっぱいある人

機能拡張により、ACLやめるですべて解決。ACL機能が削がれているので、オブジェクトのオーナーが誰とか一切関係ないので、既存オブジェクトも問題なしでした。

Linuxやったら chown すればいいですが、S3は所有者を変更するAPIは生えてません。ググるとこんなん出てきます。

aws.amazon.com

ざっと見てみると、s3 sync で新たなバケツにコピーです。つまり、同一オブジェクトの所有者だけ変える方法は存在しない。

まとめ

バケツ所有者とオブジェクト所有者が bucket-owner-full-control アクセス可能
同じ ついてなくてもOK バケットポリシー有効
違う ついている バケットポリシー無効+バケツ所有者は見える
違う ついてない バケットポリシー無効+オブジェクトオーナーのみアクセス可能
  • ACLやめてみんなハッピー
  • 今後作製するS3バケットは一律オブジェクト所有者とバケツ所有者を一致させる設定をOnでOK
  • AWSログ系の出力はクロスアカウントアクセスが結構発生しているのでクロスアカウントでの読み込みがある場合は注意
  • 所有者だけを変えるAPIは未だ(2021/03/22)存在しない

terraform で gcp iam管理に入門してみる

terraform で gcpのIAMを管理してみたときのメモ

f:id:iga-ninja:20210214015531p:plain
terraform with gcp

GCP/AWS 用語対応表

AWSからterraformを触った人が多数派と思うので、用語の意味違いが結構な落とし穴です。なので整理しときます。

AWS GCP memo
User Google アカウント GCPより広い世界
Group Google Group 同上
Role ServiceAccount 厳密には異なるがだいたい
Policy Role つらい、嫌がらせか?
PolicyAttachment IAM_member terraform上
該当なし? Policy terraform上?

まずUserとGroupについて、GCPのIAMは認証機能は持たず、基本的に認可だけやります。認証は、GCPに閉じた世界ではなく、Gooleアカウント/Groupで行っています。Gmailのフリーアカウントや、Google Workspaceのメアドがユーザーに該当します。

次にAWSのRoleに対応するものは、ServiceAccount(SA)というものです。GCPにもRoleがありますが、これはAWSのPolicyに相当します。まあ、英語の意味はあってますが、後発なので敢えて変えたんでしょうね。。AWSのRoleは主に人間以外のサーバやSAMLで使うものですが、GCPで同じことやる場合、人間以外のためにメールアドレス(メールは受信できないが)を払い出します、それがServiceAccountと見ていい。

AWSのPolicy=GCPのRoleは認可内容のセット・塊です。GCSの全権限を持つとか、GCE何でもできる権限とかを指します。AWSのIAMのようにどのサービスの何ができるということは個別で指定できます、が最初のうちは、プリセットのRoleが用意されているのでこれでほぼことが足りるはずです。プリセットのRole=AWSのAWSManagedのPolicyです。GCPのRoleは自作もできます。

注意喚起です、google_project_iam_policy というAWS触った人ならすぐにいじりたくなるものがありますが、基本いじっちゃだめです。本番でやると即死あり。

Terraform x GCP で、IAM権限を全削除してしまった - Qiita

https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_policy

最後にPolicyAttachment、これは厳密にはAWSではなく、terraformの話ですが、どのAWS User/RoleがどのPolicyと紐付いているかをつなぐものです。これがGCPでは google_project_iam_member に該当します。

terraformでの認可の振り方

2つやり方があります。google_project_iam_bindinggoogle_project_iam_member です。結論を先に書きますが、AWSのterraform猛者であれば google_project_iam_member 一択です。 google_project_iam_bindingをどうしても使いたい場合であっても、人間ユーザーが使うものだけに適応するのが望ましく、ServiceAccountで使うのは悪手です。その説明を書く。

google_project_iam_binding

Roleを主体にして、それを使う人やServiceAccountを列挙します。だから members = [...] で指定します。AWSでterraformそこそこ昔からやってた人ならすぐにわかると思いますが、これを使ってる=死亡フラグです。

google_project_iam_member

Member=使う人やServiceAccountとRoleを1対1で紐づけます。aws provider (terraform) の IamPolicyAttachmentです。1対1紐づけの重要さを知っている人はこれから先は読まなくていいです。お疲れ様でした。

1対1紐づけでないと何がまずいのか?

terraformの習熟度次第では「そんなの俺には関係ないね」となるかもしれないですが、知ってて損はないと思うのでまあ、読んでください。 私が認識するterraformの良さの一つに、コードで管理する対象(バケツやGCE-VM GCPのリソースのこと)を選べる、を挙げます。例えば同じGCPプロジェクトでも、このチームはVPCだけ、このチームはGCSだけとかを、それぞれ異なるtfstate(実態としてはディレクトリとなる)で管理できる点です。
「インフラなので、1枚岩で1プロジェクト1ディレクトリで全部のリソースを管理しないとダメ」という管理ポリシーももちろん間違えではないですが、ユーザーが選べるという点が優れてる、嫌なら分割しなきゃいい。
で、google_project_iam_bindingを使ってしまうと、この管理対象を実質的に分割することができなくなってしまいます。(できなくはない、が運用が大変)まあ、ディレクトリ分割しなくてもtfファイルはかなり不格好になります。
もう一つは terraform workspaceを使っている場合も(知ってると思うけど)危ないです。回避方法は google_project_iam_member を使えです。

実証1 (神レビュアがいれば避けられるかも?)

四の五の言わずにさくっとtffileを書きます。まずは分割しないけどapply後に延々とdiffが出るパターンです

resource "google_service_account" "sa-one" {
  account_id = "sa-one"
  display_name = "sa-one"
  description = "テスト用1"
}

resource "google_project_iam_binding" "sa-one" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-one.email}"]
}

### 1ヶ月後に誰かがsa2を追加
resource "google_service_account" "sa-two" {
  account_id = "sa-two"
  display_name = "sa-two"
  description = "テスト用2"
}

resource "google_project_iam_binding" "sa-two" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-two.email}"]
}

Roleは間違って流してもいいように、普段使わんであろうやつを指してます。sa-one にこのロールを当ててその後、sa-twoにもそのロールを当てようとしてます。これは plan/applyともに通ります。しかしapply後に planを叩くと差分が出ます。というか何回applyしようが永遠に出ます。なぜか?
google_project_iam_bindingの書き方が間違っています。members = [] で2つのSAを列挙しないとだめです。正しい使い方は、Roleを主体で考えるので、それを使っている人・SAを全部列挙しろということになります。これ、とても恐ろしいことが起こってます、仮に人アカウントのRole割当は一切terraform管轄にしていない場合に、上記のRoleがProject/Editorだったりした場合、Applyしたら人アカウントからEditor権限全部剥奪されます。そのため、例ではどうでもいいRoleを採用してます。

terraformコード管理してます、Reviewを通さないとApplyできないという運用をしてます、sa-oneを参考にしてsa-twoをベースコピペでPR出しました、Planもグリーンです、さあレビューしてください!私は急いでます!、これでレビューでNGだせますか? シンプルに google_project_iam_bindinggrepして引っかかったら「これあかんで」ぐらいの運用しかできないんじゃないかな。

今回は、1ディレクトリに2つのSAを両方書きましたが、ディレクトリ分割し、tfstate分離してたら悲惨です。他のディレクトリのSAで、Roleの参照があるかないかを調べて忖度し続けなければならない。

実証2 (workspaceパターン)

workspaceでも試してみた、こちらはworkspace切り替え後のapplyで差分が出るので気づけるチャンスはある

resource "google_service_account" "sa-one" {
  account_id = "sa-one-${terraform.workspace}"
  display_name = "sa-one"
  description = "テスト用1"
}

resource "google_project_iam_binding" "sa-one" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-one.email}"]
}

defaultでapplyしたあと、別workspace(production)でapplyすると、差分が出ます。

Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # google_project_iam_binding.sa-one will be updated in-place
  ~ resource "google_project_iam_binding" "sa-one" {
        id      = "pj/roles/storage.hmacKeyAdmin"
      ~ members = [
          - "serviceAccount:sa-one-default@pj.iam.gserviceaccount.com",
          + "serviceAccount:sa-one-production@pj.gserviceaccount.com",
        ]
        # (3 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

差分が出る原理は同じですが、ちゃんと想定外のが出ている分だけましか。。

まとめ

  • GCPでも人・SAとRoleの紐づけは1対1 google_project_iam_member を推奨します
  • google_project_iam_binding をどうしても使いたい場合は、(ちょっと苦しいと思うが) SAに割り当てるRoleは全部スタムで、SAにしか割り当てない運用とする

エンジニアなので、コード短く書きたい、DRYに書きたい、変数でループしたい、よくわかります。しかしIAM設定はミスったときのダメージが大きすぎるので、リスクおよびその回避策とを天秤にかけて選択してください。