ayumin.log

読みにくかったら脳内sedで整形してね

Oracle Cloud Infrastructure Container Engine for Kubernetes (OKE) にローカルからトークンベース認証で kubectl get pod するまでの道のり

最近無料枠の大きいOracle Cloud Infrastructure(以下OCI)でKubernetesを運用する機運が高まったので、とりあえず kubectl get podしようとしたら詰まった。

結論としては

  • kubeconfigに設定を追加するには、OCI CLIを認証が通った状態で叩ける必要がある
  • OCI CLIoci session authenticate するだけではダメ

という感じだったので、同じハマりをしないようメモっておく。

前提

必須条件

  • Oracle Cloud Infrastructureアカウントがある
  • ↑のアカウントに権限のあるユーザがいる (CLIで操作できるユーザが存在する)
  • アクセス可能なパブリックエンドポイントを持つOKEクラスタがある

トークンベース認証を行う

公式ドキュメントがあるので、その通りに認証を行う。

oci session authenticate

ブラウザがある場合は上コマンドを打つとCLIでリージョン選択後、ブラウザで認証画面が出てくる。

基本東京リージョンを使用するので、ap-tokyo-1の番号を選べば良いと思う。

ブラウザでログインしたあとは、CLIのprofile名を入力する。おそらく初回だと自動的にDEFAULTになるので、特に何も出ないで終わるケースもある。

うまくいくと ~/.oci/config に以下のような設定が追加されるはず。

[DEFAULT]
fingerprint=<keyのfingerprint>
key_file=/Users/ayu/.oci/sessions/DEFAULT/oci_api_key.pem
tenancy=<テナンシID>
region=<リージョン名(ap-tokyo-1等)>
security_token_file=/Users/ayu/.oci/sessions/DEFAULT/token

ない場合は公式ドキュメントにどうすれば良いか書いているので以下を参照。

docs.oracle.com

CLIが毎回トークンベースで認証を行うようにする

さて、ここからが本題。

OCI CLIは、トークンベースで認証を行っても、自動的にその認証情報を使ってくれない

なので、以下のどちらかをしないといけない。

  • コマンドのオプションとして、 --auth security_token をつける
  • 環境変数として OCI_CLI_AUTH=security_token を設定する

前者でも後者でも問題ないが、以下の理由から前者で対応することにする。

  • OCI CLIの設定は一括でまとめたい
  • 後者は環境変数になる都合上 ~/.bashrc などに追記する必要があり設定が増える

しかし毎回コマンドオプションをつけるのはだるいので、自動的にこのオプションがつくように設定する。

OCI CLIには、CLI構成ファイルというオプションやエイリアスの設定ができる機能があるので、これを使う。

まず、設定ファイルを作る。

oci setup oci-cli-rc

次に、 ~/.oci/oci_cli_rcに以下の設定を追加する。

compartment-idやtenancyというのは聞き馴染みがないかもしれないが、GCPのproject_idとproject_root_idに近い概念で、tenancyがプロジェクトのトップ、そこから木構造のようにコンパートメントを切って様々なプロジェクトを管理する。

OCIはコンパートメントの指定がないと基本的にリソースを絞れないので、ほとんどのコマンドでコンパートメントIDを要求してくる。コンソールでも大体左側のサイドバーで今何のコンパートメントを見ているか表示される。

[DEFAULT]
auth = security_token
compartment-id = <OKEクラスタがあるコンパートメントID。tenancyにある場合はテナンシID>

この設定をすることで、oci cliを使うコマンドが以下のように暗黙的に変換される。

# 未設定
oci foo bar

# 設定済み
oci foo bar --auth security_token --compartment-id <コンパートメントID>

これでOCI CLI周りの準備は完了。

OKEクラスタに接続する

OKEクラスタへの接続は別途設定が必要なので、OKEクラスタのアクセス情報を取得する。

OKEのクラスタを作成すると、左上側のボタンに クラスタへのアクセス ボタンがあるので、モーダルの ローカル・アクセス ボタンを押し、パブリックエンドポイントを使うときの設定のコマンドをコピペして実行すれば、kubectl が使用できる状態になる。

OKEコンソール

ローカル・アクセス

あとは kubectl get pod ができるはず。

エラーが出る場合、どこかの認証が誤っている(特にOCI CLIの構成ファイル ~/.oci/oci_cli_rc の設定が漏れている)可能性が高いので、再度確認してみる。

TerraformでAzureのVMを用意しようとしたらハマった話

2022/3/4 追記

以下の対応をした後、git push 時に名前解決ができない問題が発生した。

/etc/resolv.cof には問題なくnameserverの設定があるため原因は分からないが、DNS周りに問題が出る可能性がある。

きっかけ

ICTSC の勉強をしようと思い、せっかくなのでAzureの勉強ついでにTerraformで構成を管理しようと思った。

が、そこで結構辛いハマり方をしたので備忘録として残しておく。

環境

  • WSL2 on windows11
  • Terraform 1.1.5
  • azurevm provider 2.96.0

結論

結論から言うと、WSLのネットワーク特有の問題だった。

解決方法は以下とその後に続く議論を参照。

github.com

簡単に言えば、wslでのresolv.confの自動生成を止め、代わりにネームサーバを固定してやれば良いらしい。

自分の設定は以下。

/etc/wsl.conf

[network]
generateResolvConf = false

[boot]
command = printf 'nameserver 8.8.8.8\nnameserver 8.8.4.4\n' >> /etc/resolv.conf

この後PowershellでWSLをシャットダウンする。

powershell

wsl --shutdown

後は通常通りに使ってOK。正しく設定がされているかは、`cat /etc/resolv.conf' を確認してやると良い。

原因究明

状況把握

結構ざっと書いていたので、どこまで再現性があるかコメントアウトしながら確認したところ、

  • Provider のみの環境であればplanが通る
  • resource group を作ろうとするとplanがハングする

ということが確認できた。

ログ確認

次にログを見てみた。

$ terraform init
$ TF_LOG=TRACE terraform plan
...(省略)
# <subscription_id> はサブスクリプションIDで、環境によって変わるので適宜読み替え
2022-02-16T00:57:38.418+0900 [DEBUG] provider.terraform-provider-azurerm_v2.96.0_x5: AzureRM Response Error: Get "https://management.azure.com/subscriptions/<subscription_id>/providers?api-version=2016-02-01": dial tcp: lookup management.azure.com on 172.24.208.1:53: cannot unmarshal DNS message for https://management.azure.com/subscriptions/<subscription_id>/providers?api-version=2016-02-01: timestamp=2022-02-16T00:57:38.418+0900
...(省略)

この時点で、接続に失敗していそうなことは分かったものの、

という状態だったので、ネットワークの問題を疑いはしなかった。

アーキテクチャ確認

まさかないとは思ったものの、アーキテクチャ依存の問題を考え手元にあったm1 macで実行してみた。

するとなぜか成功した(!?!?!?!?) この時点でWSL固有の問題であることを疑い始める。

これがヒット、上で紹介したGitHub のissuesが見つかった。

GitHubのissueが立っていて助かった

もしissueが立ってなかったら確実に気づけなかった。providerのGitHubのissueぐらいは確認するが、WSLまでは気にしていなかった。

こんなこともあるんだなと思い急いでブログに備忘録を残している。

こういうのってterraformのprovider側にもissueたてておいた方が良いのだろうか。

備忘録: Goでバックエンド書いた時にハマったところ

久々にエンドポイント作った

echo+sqlxでバックエンド書いた時に出たミスをいくつか。

db変数をグローバルで宣言してない

var db *sqlx.DB
func main(){
    var err error
    db, err = db.Open("省略") 
}

SQL叩いてnil pointerとか言われた時はまずこれを疑おう。大抵の場合ハンドラのdbがスコープ外で宣言されてるせいで死んでる。

distrolessを使う時

静的リンクしたGoのバイナリを使う場合、alpineでも大して問題はないが、せっかくなのでdistrolessを使ったところハマった。 中でシェル使って動作確認~とか思って、その場合debugというtagを付けるとbusyboxのシェルが使えると聞いてたので、やってみた。

FROM gcr.io/distroless/static:debug

すると静的リンクしたGoのバイナリがなぜか開けなくなり、コンテナが起動できなくなるエラーが出た。ELFとかでエラー出てたので、ひょっとするとArmマシンだとうまくいかないのかもしれない。 debug使わなければ何も問題なかったので、少なくともArmマシンでシェルに入りたい時はalpineとか使うことにする。なおシェルに入らなければdebugいらないので問題ない。

DBに日本語入れる時

データ自体は問題ないが、シェルで直接SQL叩いて検証とかやりたくなるので、DBも日本語対応しておこう。 mariaDBをDockerで立てる場合、

[mysql]
default-character_set=utf8mb4

[mysqld]
character-set-server=utf8mb4

とか設定ファイル書いておき、/etc/mysql/conf.d ディレクトリに設定ファイルをコピーしてやればいける。

ハマってないけど後から見てハマりかけていたところ

複数行JOIN時

SQLで複数テーブルをカラムAでjoinしたテーブルに対してWHEREでまたカラムAを絞り込む時、

... WHERE tbl_name.columnA = ?

のようにすると分かりやすい。してなかったので、見返した時にJOIN後のテーブル構成がすぐに把握できなかった。

反省

ハッカソンみたいな短時間での開発は焦ってハマりがちなので気を付ける。

CUIのアニメ的なやつはどう作っているのか

プログレスバーを作りたい

きっかけはCUIツールを使っていた時、ふとVimプログレスバーのように、明らかにstdoutにprintするだけでは難しそうなものをどう作っているのか気になった。

また、CUIテトリスのようなものを作ろうとすると、ブロックが動くたびに下に新しく文字がprintされるのは明らかにおかしいので、どうやって同じ場所でアニメーションみたいに動きを表現しているのか気になっていた。

結論

結論から言うと、エスケープシーケンスでカーソルを移動し、再度同じところに出力し直している。

詳細はANCI Escape sequenceを見ると分かるが、その中に\033[nA というものがある。nには数字が入る。なお \033 の代わりに ^ と記載されている場合もあるが、

例えば \033[3A だとカーソルを3行上に移動する。

ここで注意すべき点は、エスケープシーケンスを出力している行も1行とカウントされるということ。 例えば2行の絵を表示させ続けたいからといって、\033[2A としてしまうとうまくいかない。

fmt.Println("1")
fmt.Println("2")
fmt.Println("\033[2A")

とすると、標準出力は

> go run main.go
1
> (入力待ち)

という状態になる。

なお、このエスケープシーケンスによって、標準出力が消えているわけではない。 というか標準出力はストリームであり、データの流れを今のカーソルの位置に出力し続けるに過ぎない。

アニメーションが同じような絵を同じ場所でパラパラとめくるのと理屈は全く同じで、データを消しているわけではなく、同じところに再度出力し直しているだけである。

感想

初歩的なことかもしれないが、標準出力にカーソル位置という概念があることを知らなかった。

カーソル位置という概念がなければ、printなどで命令を受け取った時に出力場所が定まらないので、言われてみればない方が不自然だった。

またエスケープシーケンスというと、\n 辺りはCを書いたことがある人なら誰しも書いたことあると思うが、画面制御で使う印象がなかった。

こういう見落としてること割とあるので気を付けなくては。

あと、これを調べる時に検索ワードが思いつかなったので、CUI アニメ とか C言語 テトリス とかで調べた。

使いそうな場面

やはりプログレスバーが一番あり得そう。あとはCUIゲーム作ったりするときにも使える。

プログレスバーはこの記事が分かりやすい。今度作ってみる。

t-keita.hatenadiary.jp

New Relic で始める監視

この記事は、HUITアドベントカレンダー 14日のものです。

qiita.com

前日はかりんとうさん

qiita.com

明日はkondoさんの記事です。

ブロックチェーンのパワー💪 - ああ's project

何をするの?

New Relic と Terraform で AWS 環境を監視も含めて自動構築する

監視の必要性

一般的にWEBアプリケーションは、開発する時間よりも運用する時間の方が長い。

特に最近ではDevOpsという単語に象徴されるように、開発と運用のサイクルを回し続けるスタイルが一般化されつつある。

ただサーバでアプリケーションを動かすだけでなく、アプリケーションのデータ(メトリクス)を収集分析し、それを元に更に開発を進めていくというスタイルで、それを実際に行うSREという業種もある。

まだすべての会社でとはいかないが、日本でも規模が大きいWEB系企業を中心にSREの導入が進んでいる。

New Relic とは?

簡単に言えば多機能監視SaaSである。詳しくは以下リンクを参照。

newrelic.com

特にアプリケーションのメトリクスを取るAPMが有名だが、今回は触れない。

内部監視から外形監視までできる上、NRQLというSQLライクなクエリ言語を備えており、メトリクスを取得するだけでなく、それを集計した見やすいダッシュボード作成や、アラートの作成も可能となっている。

アラートは勿論Slackなどにも投下可能。

Terraform とは?

インフラ構築自動化ツール。Infrastructure as Code の代表的なもので、普段はクラウド環境の構築自動化に使われる場合が多い。

様々な環境をproviderという形に抽象化し、HCLという一つの言語のみでそれら環境の構築を宣言的に記述できる。

勿論コードなので記述した環境はgit管理することもできるし、それを元にコマンド1つで環境の構築・破棄もできる。

そしてここからが本題だが、この環境というのはAWSGCPといったクラウドプロバイダだけでなく、ローカルのDockerコンテナ、主要クラウド事業者以外の管理など様々な場面で使える。

そして New Relic の provider も提供されており、Terraformによる自動構築が可能である。

つまり、Terraform 1つで、 AWSの環境を構築し、それに合う監視までも自動構築ができる。

これをしようというのが今回のテーマ。

今回構築するもの

今回はEC2を1インスタンス立てて、そのCPU使用率の監視を New Relic で行う。

実際にはEC2以外のRDSなどを使うケースもあるし、監視対象も外形監視を含め様々なものが必要だが、今回は簡易化のためそれらは省略している。

なおそれぞれのproviderのアカウント設定、Terraformのversion指定等は割愛する。その辺の細かい部分についてはそれぞれのdocを参照。

また、今回はデータ取得は Cloudwatch Metrics を使用する。その連携は

newrelic.com

の手順どおりに行う。

# EC2の構築
data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "nr_test" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
}


# New Relic の構築
resource "newrelic_alert_policy" "ec2" {
  name = "ec2"
}

resource "newrelic_nrql_alert_condition" "ec2_alert" {
  account_id                     = <NewRelic Account ID>
  policy_id                      = newrelic_alert_policy.ec2.id
  type                           = "static"
  name                           = "ec2"
  enabled                        = true
  violation_time_limit_seconds   = 360
  aggregation_window             = 60
  aggregation_method             = "event_flow"
  aggregation_delay              = 120
  expiration_duration            = 120
  open_violation_on_expiration   = true
  close_violations_on_expiration = true

  nrql {
    # EC2のインスタンスごとにCPU使用率の平均を取る
    query = "SELECT average(ec2. CPUUtilization) FROM Metrics FACET aws.instanceid
  }

  critical {
    operator              = "above"
    threshold             = 80 # CPU使用率80%以上でアラート
    threshold_duration    = 300
    threshold_occurrences = "ALL"
  }

  warning {
    operator              = "above"
    threshold             = 70
    threshold_duration    = 300
    threshold_occurrences = "ALL"
  }

まとめ

簡易的に、Terraformを使ってEC2とその監視基盤の自動構築をした。

全く関係ない雑記

最近作業中にBGMで色々聞いてるが、最近聞いた中だとマハラージャンの曲が良かった。

GCP の Cloud Functions は引数と戻り値をちゃんと書かないとハマる話

FaaSを使ってみる

タイトルが全て。

サークルの体験入部管理用に作ったBotバッチ処理をさせているが、Botのコードに無理やりgoroutineを入れてバッチ処理をさせている微妙なコードだった。 Oracle Cloudの無料枠に乗せているため、サーバの余力がなくコンテナを新たに立てるのが厳しかったので、そんな感じの無茶苦茶なコードになっていた。

サークルの先輩からIssuesが飛んできたりしてて、流石にそろそろ放置するのも忍びないのでBotの修正ついでに Cloud Functions に移行してみた。言語はGo。

6時間ぐらいハマった

色々困った部分はあったが、特にハマったのは以下の部分。

  • メインで動かす関数の引数、戻り値はイベントに応じてちゃんと設定する必要がある
  • package は main が使えない (ビルド時に main パッケージが自動追加されるらしい) ので、他の名前にする
  • メインで動かす関数は開始を大文字にする (Go で、package を分けることを考えれば妥当)

gcloud コマンドで行ったが、基本的にデプロイ時のエラーメッセージはあてにならないと思った方が良い。

エラーメッセージの内容で調べても情報は(英語であっても)基本出てこなかった。

Cloud Scheduler

バッチ処理の発火のためのスケジューラ。こっちは crontab とか書いたことあれば簡単で、ものの2, 3分あればできる。

Pub/Subで指定した時間にトピックを指定してやれば、ちゃんと Cloud Functions でトピックで発火する設定ができていれば動いてくれる。

感想

大体実行時間は3~4s/回ぐらい。一日一回のバッチ処理で、実行環境はメモリ128MBの最小サイズなので、1か月0.1円もしない。無料枠にも勿論収まるため超お手軽。

ちょうど良い FaaS 入門で面白かった。

通知Botとかも作れそうだし、最近 Discord も slash コマンドが使えるようになって、FaaS にコマンド処理を置けるようになったらしいので作ってみたい。

dev.to

ひたすら色んな言語のHello Worldをstraceしてみた話

サークルでHello Worldしまくってみた

サークルの人数は増えたものの、あまり会話が盛り上がらないという悲しい問題があるので、色々会話ネタ探しをしている中でやってみた。

1時間ぐらいわいわいやれる(少なくとも自分は)のでネタ的には面白かった。

やることはひたすらhello worldコードをもらって (もしくは書いて) strace -o していくだけ。

言語による行数の違い

簡易的な目安で厳密なものでは全くないが、strace -o の行数はある程度速度と相関があるので、この言語はどうなんだろうね、みたいな話ができた。

やってみたのは行数の少ない順に C, Rust, Go, JavaScript, Dart, Python, PHP

実行速度的にも大体そんな感じかなーという予想通りの結果となった。予想外だったのはPHP。思ったより行数が多く、Python以上という結果に。

出力のバッファリングについて

言語を色々使ったことがある身として、手軽に言語ごとの差を見せる例として真っ先に思いついたのがこれ。

ちょうどhelloworldを出力するコードが揃っていたので、これがwriteシステムコールによるものであるということと、言語によっては自動的にバッファリングが行われない場合もあるよ、という例を見せるために、helloworldを107回ほどループで出力するコードを書いて、writeシステムコールが何回呼ばれているか調べてみた。

この回数にしたのはあまり理由がないが、ちょうど Goの実行時間 < Pythonの実行時間 になるぐらいだったので分かりやすかった。ちなみに自分の環境だとGoが2.45s, Pythonが2.25sぐらい。Goは事前にコンパイル済み。

見たところPythonだと1000回、Goだと70000回ぐらいwriteシステムコールが呼ばれている結果に。Goもある程度は最適化がかかるっぽい?のか、107回呼び出しているところは見られなかった。

これだけwriteシステムコールは遅いんだよ、というところを見せつつ、これを解決するにはバッファリングが必要だよね、という感じでbufioを使いGoのコードを改善すると、2.45s が 0.48s と大幅改善することが示せた。

やってみた感想

実行時間がぱっと出てくるのでやっていて楽しいし、見た目上も分かりやすいのでおすすめ。

特にHUITにはPython使用者が多い一方、Go使用者は少なめなので、常に事前コンパイル型で低層を触りやすい言語が速いとは限らないという例を示せて収穫があったと思う。

自分も色々勉強になった。