Rust + Rocket (0.5.0-rc.1) + rocket_sync_db_pools + DIESEL + MySQLでAPIサーバーを作る
タイトル通りの言語、ライブラリ、フレームワークでAPIサーバーを作成する話です。
似たような記事はたくさんあるんですがrocket_sync_db_poolsを使ったやり方があんまりなくて困ったので書き残しておきます。
ちなみに基本的にはrocket_sync_db_poolsのexampleと(DIESEL)https://diesel.rs/のGetting Startedを進めて繋ぎ合わせただけです。
ただサンプルがPostgreSQLだったりmain.rsじゃなくてlib.rsだったり個人的には読み替えが大変でした。
作り方
local環境のversionは以下の通りです。
- rustc: 1.60.0 (7737e0b5c 2022-04-04)
- cargo: 1.60.0 (d1fd9fe2c 2022-03-01)
まずはRocketのリファレンスのGetting Started は済んでいる前提で進めます。
また、MySQLもどこかの環境に使えるものがある状態とします。
rocket_sync_db_poolsを追加
2022/05/05時点でRocketのlatest versionはv0.4.10 です。 また、v0.5.0-rc.1がPre-releaseされていて、Rocketのリファレンスでは初めて訪問するとv0.5のリファレンスが表示されます。
v0.4 まではrocket_contrib::databases
というlibraryがdatabase周りの設定に使われていたのですが、こちらがdeprecatedになります。
代わりにrocket_sync_db_pools
を使います。
Rocket/CHANGELOG.md at v0.5.0-rc.1 · SergioBenitez/Rocket · GitHub
なのでCargo.toml
にrocket_sync_db_pools
を追加します。
rocket_sync_db_pools = { version = "0.1.0-rc.1",features = ["diesel_mysql_pool"] }
今回はfeatureにdiesel_mysql_poolを指定しましたがSQLiteとPostgreSQLを指定することも可能です。
その場合は以下のadapterのリストから適切なものを選択してください。
DBのURLを設定
次にdbのurlをアプリケーションに設定します。
Rocket.tomlを作成し以下のように記載します。
[default.databases] mysql = { url = "mysql://user1:root@127.0.0.1/testdb" }
urlの記法は
{dbの種類}://{dbに接続する際のusername}:{usernameに対応するpassword}@{dbのhost}/{利用するdatabaseの名前}
です。
- mysqlを利用していて
- アプリケーションがmysqlに接続するときのユーザー名が
user1
で - user1のpasswordが
root
で - dbのhostが
127.0.0.1
で - アプリケーションが利用するdatabaseの名前が
testdb
の場合 mysql://user1:root@127.0.0.1/testdb
になります。
main.rsを編集
src/main.rsを以下のように編集します。
#[macro_use] extern crate rocket; use rocket_sync_db_pools::{database, diesel}; #[database("mysql")] struct LogsDbConn(diesel::MysqlConnection); #[launch] fn rocket() -> _ { rocket::build() .mount("/", routes![posts]) .attach(LogsDbConn::fairing()) }
databaseとdieselをscope内に入れてStructを作成、rocket::build()にattachしたらOKです。
この状態でcargo runを実行してサーバーが立ち上がればアプリケーションとMySQLの接続は完了です。
DIESELを追加
次にDIESELを設定していきます。
ちなみにDIESELはRust用のORMです。
Diesel is a Safe, Extensible ORM and Query Builder for Rust
Cargo.toml
に追加します。
[dependencies] ... ... diesel = { version = "1.4.4", features = ["mysql"] }
mysqlを使うのでfeaturesはmysqlを指定します。
cargo install diesel_cli
Rocket.tomlに書いたdbのURLを DATABASE_URL
というkey名で環境変数に設定します。
export DATABASE_URL=mysql://user1:root@127.0.0.1/testdb
setupコマンドを実行します。
diesel setup
おそらくcargo runでサーバーが起動できている場合はsetupも問題なくいけます。
次にmigrationファイルを用意します。ここではGetting Started通りpostsテーブルを作ります。
diesel migration generate create_posts
migrations/{日付}_create_posts/ 以下にup.sqlとdown.sqlがそれぞれ作成されるので、up.sqlにcreate tableのSQLを、down.sqlにdrop tabelのSQLを書きます。
-- up.sql CREATE TABLE posts ( id integer AUTO_INCREMENT PRIMARY KEY, title varchar(255) NOT NULL, body text NOT NULL, published bool NOT NULL DEFAULT false )
-- down.sql DROP TABLE posts
migrationを実行し、posts テーブルが作成されたことを確認します。
diesel migration run
次にmodels.rsを作成します。
ここにはPost struct (structはjavascriptで言うところのObject的なやつ)を定義します。
use rocket::serde::Serialize; #[derive(Queryable, Serialize)] #[serde(crate = "rocket::serde")] pub struct Post { pub id: i32, pub title: String, pub body: String, pub published: bool, }
Queryable はSQL内でPost structを読めるようにしてくれる便利なやつ、だそうです。まだあんまりわかってない。
SerializeはPost structをAPIのresponseの型として利用するために使っています。
次はmain.rsを書いていきます。
まずdieselをextern crate (global環境にlibrary importする的なやつ)としてscopeに入れます。
そうなるとrocket_sync_db_pools::dieselと名前がかち合うのでrocket_sync_db_poolsもextern crateとしてscopeに入れました。
これはglobalを汚染してしまうので本来良くないと思います。
asを使って別の名前をつければ全部scope内に入れなくても良いはず。
#[macro_use] extern crate rocket_sync_db_pools; #[macro_use] extern crate diesel;
次に先ほど作成したmodel.rsとmigration実行時に自動作成されたschema.rsをmain.rs内で利用するためにmodで指定します;
mod schema; mod models;
filterメソッドなどを利用するためにdiesel::prelude::*を、Post structを利用するためにmodels::Postをuseでscope内に入れます。
use diesel::prelude::*; use models::Post;
最後にendpointにアクセスが来たときの関数を定義します
use rocket::serde::json::Json; #[get("/posts")] async fn index_posts(conn: LogsDbConn) -> Json<Vec<Post>> { use schema::posts::dsl::*; conn.run(|c| { let result = posts.filter(published.eq(true)) .limit(5) .load::<Post>(c) .expect("Error Loading"); Json(result); }).await }
最終的なmain.rsは以下のようになります。
#[macro_use] extern crate rocket; #[macro_use] extern crate rocket_sync_db_pools; #[macro_use] extern crate diesel; use rocket::serde::json::Json; mod schema; mod models; use diesel::prelude::*; use self::models::Post; #[database("mysql")] struct LogsDbConn(diesel::MysqlConnection); #[launch] fn rocket() -> _ { rocket::build() .mount("/", routes![posts]) .attach(LogsDbConn::fairing()) } #[get("/posts")] async fn index_posts(conn: LogsDbConn) -> Json<Vec<Post>> { use schema::posts::dsl::*; conn.run(|c| { let result = posts.filter(published.eq(true)) .limit(5) .load::<Post>(c) .expect("Error Loading"); Json(result); }).await }
dbからのresponseをresultに入れてJsonでserializeしてresponseとして返しています。
あとはdbにデータを挿入。
insert into posts(title, body, published) values ("ワイワイ", "ワイワイしています。", false), ("ガヤガヤ", "ガヤガヤしています。", true), ("オヤオヤ", "オヤオヤしています。", true) ;
curlで確認。
$ curl localhost:8000/posts [{"id":2,"title":"ガヤガヤ","body":"ガヤガヤしています。","published":true},{"id":3,"title":"オヤオヤ","body":"オヤオヤしています。","published":true}]
ちゃんとpublishedがtrueのものだけ返却されてればOKです!
「プログラムはなぜ動くのか」の読書感想文
この間会社の図書館で「プログラムはなぜ動くのか」を見つけたので読んでみました
https://www.amazon.co.jp/dp/B00HRN7VK0
なぜこのチョイス?
以前どなたかのブログで
「最近はコードはかけるが裏側で何が起こってるのかわかってないやつが多すぎる」
みたいなことを言ってる人がいて、そこで紹介されてた覚えがありました
この主張に関しては思うところもあるんですが低レイヤーを知っていること自体はエンジニアにとってプラスだと思ったので
全体感想
内容としては応用情報で勉強したのとほぼ一緒
CPUとは、レジスタとは、2進数とは、メモリとディスクとはといった話がコンピュータ上でダブルクリックをしてから実際にプログラムが動作するまで実際には何が起こっているのかという流れで進んでいく
一つ一つの章は細かく丁寧に解説されてると思う
話が進んでいくのも個人的にはわかりやすかった
ただ、10章アセンブリ言語を読んでプログラムの動きを理解しようという部分で
筆者は「アセンブリ言語を経験していないプログラマは、自動車の運転方法だけを知っていて、その仕組みを知らない運転手に相当します。」と書いていたが
それはいろいろと違うだろ、とツッコミたくなった
概要
章ごとに短くまとめていきます
第1章 プログラマにとってCPUとは何か
CPUとはマシン語になったプログラムを実行する装置
CPUはレジスタ、制御装置、演算装置、クロックの4つの要素から構成される
レジスタはデータを格納する領域
制御装置は命令に応じてコンピュータ全体は制御する
演算装置はレジスタのデータを演算する
クロックはCPUが動作するタイミングとなるクロック信号を発生させる
第2章 データを2進数でイメージしよう
コンピュータはIC (集積回路)と呼ばれる電子部品で構成される
ICの中には数本から数百本のピンが並んでいるが、一つ一つのピンは0Vか +5Vの二つの状態しか持てない
なのでコンピュータで情報を取り扱うには1と0だけで表現する2進数で取り扱う必要がある
第3章 コンピュータが小数点数の計算を間違える理由
コンピュータはデータを2進数で表すので、数値を表現する場合には「2のN乗」を足して表現する
これは小数点数を表す場合も同じ
ただ、2のN乗の組み合わせは整数は全て表現できるが小数はうまくいかないことがある
具体的には2の-N乗をどれだけ足しても0.1を表現することはできず、循環小数になってしまう
なのでコンピュータ上で0.1となっている数は正確には0.1ではなく、10倍しても1にならない場合がある (10.000002とか)
これを回避するためには計算時には整数として扱い、計算が終わった後で割るという方法がある
第4章 四角いメモリーを丸く使う
メモリーとはディスクから読み出したデータを置いておく領域
CPUがディスクから直接データを読み出すのには時間がかかるがメモリからは高速に読み出すことができる
メモリは論理的なイメージはビルのような構造で、各フロアに1byteのデータを保存するようにして使う
第5章 メモリーとディスクの親密な関係
メモリーとディスクはどちらもデータを保存しておく記憶装置
それぞれに異なった性質があり、お互いの特徴を活かす形でコンピュータは動いている
- ディスクキャッシュ
メモリの一部にはディスクキャッシュと呼ばれる領域がある
ここには一度ディスクから読み出されたデータを保存しておく
一度読み出されたデータは何度か必要となることが多いので、それをメモリに保存しておくことで毎回ディスクから読み出すことによる時間のロスを減らしている
- 仮想記憶
仮想記憶とはディスクの一部を仮想的な記憶装置として利用する方法
ただ、実際にはメモリが増えているわけではない
ディスク内に仮想メモリー用の領域を用意しておき、実行されるプログラムを一定の大きさのページに分割しておく
そして必要に応じてメモリーと仮想メモリーのデータの入れ替えを行う
第6章 自分でデータを圧縮してみよう
データの圧縮を行う方法は以下のようなものがある
- ランレングス法
同じデータを表すときに繰り返す回数をデータとしてもつことで容量を減らす方法
ex. AAABCCCCDD => A3B1C4D2
ただ実際の文書ファイルなどでは同じ文字が繰り返し出現することはそこまで多くなく、1文字を表現するのに文字 + 繰り返し数のセットが必要なランレングス法ではデータが圧縮前より大きくなってしまうこともある
- ハフマン法
データを出現頻度順に並べて、出現頻度の多いものを少ないbit数で表すようにする方法
ハフマン法で圧縮するとハフマン符号と呼ばれる対応表を圧縮後データが作成される
第7章 プログラムはどんな環境で動くのか
Wisdowsでは自分で書いたプログラムでハードウェアを直接操作することは基本的にはしない
OSを介してハードウェアを動かすことになる
OS + ハードウェアのセットのことを動作環境と呼ぶ
第8章 ソース・ファイルから実行可能ファイルができるまで
なんらかのプログラミング言語で記述したプログラムをソースコードと呼び、そのファイルをソースファイルと呼ぶ
CPUは機械語 = ネイティブコードしか理解できないので、翻訳をする必要がある
その翻訳をするのがコンパイラと呼ばれるプログラム
CPUごとに扱うネイティブコードが違うため、コンパイラはCPUによって異なる
コンパイルを行うとオブジェクトファイルが得られる
ライブラリなどを使用しているとオブジェクトファイル単体では処理が揃わず実行することができない
複数のオブジェクトファイルを結合することでexeファイルができる
第9章 OSとアプリケーションの関係
OSは制御プログラム、言語プロセッサ、ユーティリティなど複数のプログラムの集合体
OSが存在する以上ハードウェアを直接操作するようなプログラムを書くことはなく、OSの機能を利用しているということを意識しながらプログラムを書くことが大切
OSのハードウェア制御機能は小さな関数の集合体として提供されているのが一般的
これらの関数、及び関数を呼び出す行為をシステムコールと呼ぶ
Cなどの高水準言語は特定のOSに依存しない
高水準言語では独自の関数名を使い、それをコンパイル時に該当するOSのシステムコールに変換する
このようにOSと高水準言語によりハードウェアを抽象化することができる
第10章 アセンブリ言語からプログラムの本当の姿を知る
アセンブリ言語とはネイティブコードと一対一に対応する言語
なのでネイティブコード => アセンブリ言語への変換を行うことができ、アセンブリ言語を読むことができるとプログラムの実行される様子がわかるようになる
この章では実際にアセンブリ言語を見ながらプログラムが実行される順番などを解説していた
第11章 ハードウェアを制御する方法
Windowsを例にとり、OSがハードウェアを制御する方法を解説する章
ハードウェアの制御にはIN命令とOUT命令を使う
IN命令では指定した番号のポートからデータを入力しレジスタに格納する
OUT命令ではレジスタに格納されているデータをしてしたポート番号に出力する
また、コンピューターでは文字入力などリアルタイムに処理を行うことが求められる
これらは割り込み処理を使って実現されている
割り込み処理とは名前の通り現在実行中のプログラムの処理を途中で止め、他のプログラムの実行に処理を移す仕組み
第12章 コンピュータに考えさせるには
人の思考をプログラムで表すには処理として記述する方法がある
この章ではジャンケンのプログラムを作って、さらに過去の対戦結果を記憶させておいてプログラムに思考させるというのをやっていた
「エンジニアリング組織論への招待」の読書感想文
かなーり有名な本だと思ってるんですがエンジニアリング組織論への招待を読みました
なぜこのチョイス?
マネージャーとの 1 on 1 でマネジメント系でおすすめの本ないですかって聞いたら
「不確実性についての話ならこの本がいい」と勧められたので
全体感想
「そもそもエンジニアリングってなんだ」と問いかけて、
「エンジニアリングとは何かを実現することだ」ときちんと定義してから始まるのはよかった
その中で今ないものを実現するのだからその計画には不確実性が伴うという感じに話が進んでく
自分の思考、他人、チーム、組織全体と対象を大きくしながらそれぞれ不確実性のある部分とそれにどう対応すべきかが書いてある
特に他人の不確実性の章ではメンタリングと呼ばれる指導、支援の方法が丁寧に説明されてた
なのでいわゆるプログラマーと呼ばれるコードを書く人以外にも割と当てはまる話が多いと思う
個人的には、
「未来と他人は本質的に完全に理解することは不可能」というのと
「問題には観測できる / できない、コントロールできる / できないの4パターンがあり、観測できないものはそもそもコントロールできない(他人がどう思っているのかは見ることはできないしコントロールすることもできない)。観測できるがコントロールできないものは、観測できてコントロールできることを使って間接的に制御していくしかない (他人の行動は観測できるがコントロールはできないので、自分の行動によって働きかけて制御していくしかない)」
という部分がかなり納得感があった
概要
章ごとに短くまとめて行きます
Chapter 1 思考のリファクタリング
エンジニアリングとは「実現」していくための科学分野
企業において何かを「実現」するときには最初のアイディアの状態から最終的に具体的な仕事になるまでだんだんと不確実性が減っていく
不確実性には「未来の不確実性 = 環境不確実性」と「他人の不確実性 = 通信不確実性」が存在する
不確実なものをだんだんと確実なものにするためには情報が必要になる
情報を生み出すためにはやってみないとわからないという「経験主義」と経験から得た少ない情報からもしかしてこういう法則があるんじゃないかと大胆に考える「仮説思考」が必要
Chapter 2 メンタリングの技術
最近よく聞くメンターとはビジネスや教育の現場で若い社員 (メンティ)を良い導き手としてサポートする人間のことで、その手法をメンタリングと呼ぶ
メンターとメンティの関係性は
お互いに弱さを見せることができる
お互いに敬意を持っている
お互いにメンティの成長期待を持っている
であると効果的
答えを教えるのではなく、メンティが答えに辿り着く手助けをする
メンターとメンティで次のアクションを決めるときは「SMART」というフレームワークを使うと良い
Specific: 具体的
Measureable: 計測可能
Achevable: 到達可能
Related: メンティの課題に関連している
Time-Bound: 時間制限
Chapeter 3 アジャイルなチームの原理
アジャイル開発とは開発初期に綿密な計画を立てることは不可能という事実を受け入れ、小さなリリースとフィードバックを繰り返しながらプロダクトを開発していく手法
アジャイルとは何を作るべきかがわからない、マーケットの不確実性が高い領域において効果的
アジャイルとは不確実に向き合う、少人数の対話と重視する、役割を分けない、意思決定を遅延すると行った方法をとる
意思決定の遅延はネガティブに聞こえるが、計画初期のまだなんの経験もない状態のときに大きな意思決定をするのではなく、小さなリリースをしてフィードバックを受け、経験と情報が増えるまで大きな意思決定を先送りにするという意味
Chapter 4 学習するチームと不確実性マネジメント
不確実性は3つに大別される
方法不確実性
目的不確実性
通信不確実性
方法不確実性とはスケジュール予測と見積もりの手法に対するもの
リリーススケジュールは「間に合うか / 間に合わないか」ではなく「スケジュール予測が収束していくか」を管理しなくてないけない
不安量の大きいタスクを細かく解体し、Tシャツサイズ見積もりやストーリーポイント見積もりで相対見積もりを行う
目的不確実性はマーケットに対するもの
大きなプロダクトを実装し、いざリリースしたときにマーケットの求めるものと違っていた場合には損失は大きくなる
リリースポイントを細かく設定し、リリースのたびにマーケットの反応をみながら次のリリースの計画を立てると良い
通信不確実性は他者とのやりとりに対するもの
スクラムでは短いタイムボックスの中で見積もり、日々の進捗共有、成果物の確認、プロセスの振り返りを行う
基本的には対話を重視したフレームワークなので誰が何をしているかがチーム全体で共有しやすくなる
Chapter 5 技術組織の力学とアーキテクチャ
組織の人数が増えていったとき、理想としては比例して生産性も上がっていって欲しい
しかし実際には人が増えるとコミュニケーションのコストが大きくなり、だんだん生産性は下がっていく
コミュニケーションが無駄に多くならないよう、権限と責任を部下に移譲する必要がある
terraformでAWSのIAMを定義する方法
ほぼほぼリファレンスそのまま
terraformでAWS IAMを定義する
terraformで定義できるAWS IAM関連のリソースは2020/05/05時点で23個ある
そもそも全部を使ったことがないので、この記事ではアプリエンジニアの自分がAWSリソースのIAMを設定するときに、名前が似ててややこしい、どう使い分けるんだっけとなる6つについて用途を整理
管理ポリシーとインラインポリシーとかAWS IAMについては下に書きました
resource: aws_iam_policy
名前の通りIAMポリシーを作成する
resourceを定義しているので、ここで作成したIAMポリシーはAWSリソースとして作成される (管理ポリシー)
AWS: aws_iam_policy - Terraform by HashiCorp
data source: aws_iam_policy_document
こちらはdata sourceとしてのIAMポリシーを作成する
実際にAWSリソースとしては作成されないので、aws_iam_policy_documentを他のIAMアイデンティティに埋め込む形で使用する (インラインポリシー)
AWS: aws_iam_policy_document - Terraform by HashiCorp
resource: aws_iam_policy_attachment
管理ポリシーをIAMユーザー、ロールなどと紐づけるために使う
AWS: aws_iam_policy_attachment - Terraform by HashiCorp
resource: aws_iam_role
IAMロールを作成する
AWS: aws_iam_role - Terraform by HashiCorp
resource: aws_iam_role_policy
IAMロールとそのロールに紐付けたいIAMポリシーをインラインポリシーとして記述して作成する
AWS: aws_iam_role_policy - Terraform by HashiCorp
resource: aws_iam_role_policy_attachment
IAMロールに管理ポリシーを紐づける
aws_iam_policy_attachmentでも同じことができるが、aws_iam_policy_attachmentでは一つのロールに一つの管理ポリシーしか紐づくことを許可しない
aws_iam_role_policy_attachmentでは複数の管理ポリシーをIAMロールに紐づけることができる
AWS: aws_iam_role_policy_attachment - Terraform by HashiCorp
以下そもそもIAMってなんだっけ用
AWS IAM
AWS Identity and Access Management (IAM) enables you to manage access to AWS services and resources securely
AWS Identity & Access Management - Amazon Web Services
IAMはAWSサービスやリソースへのアクセスのための権限周りを管理するための仕組み
IAMユーザー
AWSアカウントにログインできるユーザーを作成することができる
初めてAWSにサインアップした状態では全権限をもつrootユーザーだけがいる
各IAMユーザーにはどのリソースにアクセスできるか権限を絞ることができるので、IAMユーザーを作成することでrootユーザーを共有しなくても安全に複数人で一つのAWSアカウントを共有できる
ユーザーはAWSアカウントにログインする際にはIAMユーザーを使ってログインするため、認証の側面ももつ
ID 管理の概要: ユーザー - AWS Identity and Access Management
IAMロール
特定のアクセス権限を持ち、アカウントで作成できる IAMアイデンティティ
IAMユーザーと似ているが、IAMユーザーが特定の一人に紐づくのに対して、IAMロールは複数のユーザーに紐づけることができる
また、IAMロールにはID、パスワードを持たせることはできない
IAM ロール - AWS Identity and Access Management
IAMポリシー
IAMポリシーとは実際にどのサービスへのアクセス、どのようなアクションを許可するかが記述されたポリシー
IAMユーザーやIAMロールとIAMポリシーをアタッチすることで、ユーザーやロールのアクセスを管理することができるようになる
IAMポリシーはJSONで記述され、複数のstatementから構成される
以下はterraformのiam_policy_documentでIAMポリシーを定義した例
data "aws_iam_policy_document" "example" { statement { sid = "1" actions = [ "s3:ListAllMyBuckets", "s3:GetBucketLocation", ] resources = [ "arn:aws:s3:::*", ] } statement { actions = [ "s3:ListBucket", ] resources = [ "arn:aws:s3:::${var.s3_bucket_name}", ] condition { test = "StringLike" variable = "s3:prefix" values = [ "", "home/", "home/&{aws:username}/", ] } } statement { actions = [ "s3:*", ] resources = [ "arn:aws:s3:::${var.s3_bucket_name}/home/&{aws:username}", "arn:aws:s3:::${var.s3_bucket_name}/home/&{aws:username}/*", ] } } resource "aws_iam_policy" "example" { name = "example_policy" path = "/" policy = "${data.aws_iam_policy_document.example.json}" }
statement: ポリシーのメイン要素、1つのポリシーは1つ以上のstatementをもつ
以下はsatementの中の要素
sid: statementを区別するためのid
effect: statement内のactionを許可するのか(Allow) / 禁止するのか(Deny)を表す
principals: リソースへのアクセスを許可または禁止するユーザー、アカウント、サービス等を指定する
actions: ポリシーで許可または禁止するアクションのリスト
resources: アクションが適用されるリソースのリスト
condition: ポリシーでアクセス許可を付与する状況を指定する
ややこしいが、「誰」が「何」を「どうする」ことを「許可/ 禁止」するに当てはめると
一つのstatementは「principal」が「resource」を「actions」することを「effect」するを記載したものになる
ポリシーとアクセス許可 - AWS Identity and Access Management
管理ポリシーとインラインポリシー
IAMポリシーには管理ポリシーとインラインポリシーがある
管理ポリシー: AWSリソースとして存在するIAMポリシー
インラインポリシー: IAM アイデンティティ (ユーザー、グループ、またはロール) に埋め込まれたポリシー。AWSリソースとしては存在していない
AWS的には再利用しやすかったり一元管理ができるので管理ポリシーの方がおすすめ
おれのためのterraformの概念まとめ
仕事で時々terraformを書くんですが割と雰囲気で触ってるので自分の中で怪しい概念とかを勉強しなおしました
基本的には公式ドキュメントの翻訳メモです
英語のドキュメント読みながら書いたけどもしかしたらおれが探してないだけで日本語対応してるのかも
Terraformについて
Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions.
Introduction - Terraform by HashiCorp
terraformはインフラをコード管理するためのツール
コードで管理できるのでgitによるversion管理やレビューも行えるし、同じ構成のインフラを作りたい時は既存のものを使い回すのも簡単
AWS、Azure、GCPはもちろん、多くのサービスに対応している
terraformではそれらのサービスをproviderと呼ぶ
ざっと数えた感じ使用可能なproviderの数は100は余裕で超えてそう
以下は全部AWSについて書いていく (AWSしか使ったことない)
resourceとdata source
resource
resourceは名前の通り、VPCやEC2インスタンスなどのAWSリソースを定義する
以下のtfファイルを作成しterraform applyすると、実際にAWS上にEC2インスタンスが作成される
resource "aws_instance" "web" { ami = "ami-a1b2c3d4" instance_type = "t2.micro" }
data source
data sourceはterraform内で利用可能なデータを定義する
実際にAWS上にあるデータをfetchしてきて使うことも可能
リソース自体はterraform管理したくないけど、そのリソースを参照して新たなAWSリソースをterraformで定義したい時などに使う
以下のtfファイルを作成しterraform applyすると、terraform内で使えるamiのデータが作成されるがAWSリソースは作成されない
その後このdata resourceのidを使ってEC2インスタンスを立てるなどして利用する
data "aws_ami" "example" { most_recent = true owners = ["self"] tags = { Name = "app-server" Tested = "true" } }
terraformはdata sourceのREADのみ可能
そもそもAWSリソースとして存在しないため、resourceのようにCREATE、UPDATE、DELETEを行うことはできない
module
A module is a container for multiple resources that are used together.
Modules - Configuration Language - Terraform by HashiCorp
moduleは一緒に使われるリソースをまとめたコンテナ
全てのterraform configurationはroot moduleというmoduleを持っていて、その中にはmain working directory (おそらくterraform initをしたdirectory)にあるtfファイルに定義されたリソースが含まれている
moduleの呼び出しは、moduleの中身のリソースをconfigurationに追加することを意味する
moduleを呼び出す際には、呼び出すmoduleのpathを指定するsource
と、呼び出しの際に使用するinput value
を宣言する
以下のtfファイルは、./app-cluster配下のterraformリソースをservers
という変数と共に呼び出す
module "servers" { source = "./app-cluster" servers = 5 }
module内のリソースはカプセルかされているため、リソースの要素 (id
, name
など)に直接アクセスすることはできない
アクセスするためには、module内でoutput value
を宣言しておく必要がある
以下のtfファイルでは/app-cluster/ec2.tfで定義したEC2インスタンスのidをinstance_id
という名前でoutput value
として宣言している
メインのworking directoryにあるelb.tfファイルでは、module.servers.instance_id
という呼び方でoutputしたinstance_idにアクセスしている
#./app-cluster/ec2.tf resource "aws_instance" "web" { ami = "ami-a1b2c3d4" instance_type = "t2.micro" } output "instanse_id" { value = aws_instance.web.id } #./elb.tf resource "aws_elb" "example" { # ... instances = module.servers.instance_id }
remote state
terraformでは、localにterraform.tfstate
というファイルを作成し、そこに実際のprovider上のリソースの状態を記録している
tfstateとworking directoryを比較することで、新たに作成されたリソースや変更の加わったリソースを検知し、providerのAPIをcallしてその差分を反映している
一人で開発している分にはいいが、チーム開発していると誰かが新たにリソースを作ったらちゃんと自分のtfstateにも反映しないと誤って消してしまうことがあるのでなかなか面倒
remote stateは、AWSであればS3などのremote datastoreにtfstateを置いておくことができる
そのため自分のローカルのtfstateを最新にしなくてもterraform plan
やterraform apply
の際にremote stateを参照するだけで意図しない変更を防ぐことができる
また、remote stateでは変数のoutputをすることができるため、workspaceをまたいだ変数の参照を行うことができる
例えば社内のインフラチームがremote stateを利用した上でVPCやSubnetの設定を行い、VPC ID等をoutputしておく
社内の別チームが、会社のVPC内に新たにリソースを作成したい場合はインフラチームのworkspaceのremote stateからVPC IDを参照して開発を行うことができる
data "terraform_remote_state" "vpc" { backend = "remote" config = { organization = "hashicorp" workspaces = { name = "vpc-prod" } } } # Terraform >= 0.12 resource "aws_instance" "foo" { # ... subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id }
ちなみにremote state側でのoutputと、resource側での参照を一度にapplyすることはできないらしい
まとめ
基本的には作りたいリソース、行いたい変更を宣言的に記述していくだけなので難しくはない (はず)
ハマりポイントとしては変数の渡し方や参照方法でつまづくことが個人的には多かったのできちんと仕組みを理解してコードを書けばいける (はず)
tfstateがわかればremote stateも怖くない (はず)
WORK RULES! の読書感想文 ~ googleの仕組み ~
ちょっと古い (5年くらい前)本ですが「WORK RULES! ~君の生き方とリーダーシップを変える~」を読んだのでいつものように感想と概要を書いていきます
どんな本?
Googleの人事担当上級副社長 (なんだそれ)を勤めている人がgoogleがより成長していくために、またグーグラー (googleの社員)がより働きやすい環境を作るためにどのような工夫を行っているのかが書いてある
googleの提供しているサービスだけじゃなくて、人事評価制度や福利厚生の導入なども全てデータに基づいて決められてることを知ることができた
1章 創業者になろう
従業員のマインドについての章
従業員が自分のことを、あくまで会社に雇われている従業員としての考え方をするか、それとも全員が創業者 (オーナー)であるかのように考えるかで会社の環境は変わってくる
全員が創業者のように考えるようになると、自分の給与に関係ある会社の業績だけでなく、自分たちが働いている会社がどんな環境かというところまで意識が届くようになり、会社の環境がよくなる
読者も自分が創業者だと考えるところからまずは始めよう
第2章 「文化が戦略を食う」
会社のミッションとカルチャーについての章
会社のミッションは非常に重要。ミッションが個人の仕事に意味を与える
また、会社の文化として情報はできるだけオープンにする方が良い
社員を信じて情報をオープンにし、裁量を与える方が仕事のパフォーマンスが上がるというデータも出ている
もしメンバーに与えた裁量に不安を感じていないのであればそれはまだ不十分だという証拠、自分が仕事に裁量を与えられたいと思うのであれば自分のして欲しいことを相手にするようにしよう
第3章 レイク・ウォビゴンの幻想
優秀なチームを作るための採用についての章
優秀なチームを作る方法は2つ
- 優秀な人間を外部から引っ張ってくる
- 優秀な人間を内部で育てる
2つの方法のうち、前者の方が確実に優秀なチームを作ることができる
ちなみに章のタイトルのレイク・ウォビゴンとは「レイク・ウォビゴンの人々」という本の中に出てくる全ての子供のできが平均以上という架空の町
https://www.amazon.co.jp/dp/4487761328
筆者は採用に取り組む様子はレイク・ウォビゴンを思い起こさせると言っている
面接官は自分が採用が得意だと思いこみ自分に似た人間を好意的に思いがちなのでバイアスのかかった採用になる
そうして、優秀な人間だけを集めたつもりが採用活動は平凡な結果に終わる
大切なのは時間をかけて人を探し、自分より優秀な人間だけを採用すること
また、マネージャーに面接を任せると自分の好きな人間を採用して権力が集中してしまうし、採用活動が長引くと妥協をしてしまう (現場の人間だから)ので別の人間が面接を行う方が望ましい
第4章 最高の人材を探す方法
採用候補者の探し方についての章
社員からの紹介はかなり強力な方法だが限界がある
まずは自分の求める人物像を具体的に描く
外部の会社に委託するのではなく、社内で採用部門を持っておき常にマーケットを見張る
今はその人物を必要とするポストがなくても将来的に必要になるかもしれない
時にはチームの人材全員採用するなど突拍子もないことも恐れずにやることが必要
第5章 直感を信じてはいけない
面接の話
直感で面接をすると、25分のうち24分50秒は最初の10秒で得た印象を確認するための時間になる
採用する人物のペルソナを定め、面接のフォーマットを定めておくと面接官によるブレが小さくなる
googleでは採用候補者の部下になる人間 (上司ではない)が面接を行うことで、この人の下で働きたいかという公平な視点を入れることができる
第6章 避難所の運営は避難者に任せる
社内での意思決定の話
基本的にはマネージャーの意見ではなく、データに基づいた意思決定を行う
しかし、意思決定のヒエラルキー自体は重要で、データがない場合はマネージャーが行う (より良い意思決定を行えるからマネージャーというポジションにその人はいるはず)
マネージャーがチームのメンバーに権限を委譲することは良いことで、その度にチームにステップアップのチャンスが訪れる
第7章 誰もが嫌う業績管理とgoogleがやろうと決めたこと
業績管理の話
社内の人事評価制度はやはりgoogleと言えども不満は出やすい
業績管理 (本人の成長)と給与の改定は分けて考えた方がいい
また、一人のマネージャーに業績を決定する全権を与えるのではなく、複数のマネージャーが集まってその社員に対する評価をすり合わせる (キャリブレーション)する必要がある
不満がある場合は手を上げる機会を提供してあげる必要もある
第8章 2本のテール
社員の業績の話
業績が最高の社員と最低の社員からこそ学ぶべきものがある
最高の社員をじっくり観察することにより業績をあげるための方法を学ぼう
また、マネジメントというのは人間の処理能力を超える複雑さであるため、チェックリストを用いると良い
調査やチェックリストを使って現状を正しく捉え、最低の社員に改善するように突っつくと良い
第9章 学習する組織を築こう
成長する方法についての話
パフォーマンスの向上には動作を細かく分解し、反復練習することが重要
外部講師を招くより社内の最も優秀な人を教師にする方が、教わる人は実際の現場にいる人間から教わることができるし、教える人も自分の仕事を言語化して伝えることで理解が深まり会社としては2重のメリットがある
トレーニングの受けた人の振る舞いを変えるようなプログラムに投資すると良い
第10章 報酬は不公平でいい
給与の話
ずば抜けて優秀な社員には、社内の摩擦を恐れず不公平な報酬を払う
ただ不満感が出ないように社員同士で称え合う文化があると良い
報酬は現金でなく経験を与える方が満足度が高いというデータがある
また大きなチャレンジをして失敗した場合、それに報いる
失敗しても攻めたことを称える
第11章 タダ (ほぼタダ)ほどすてきなものはない
福利厚生の話
福利厚生はお金がかかると思われがちだが、ランチに移動販売に来てもらったり講演会を開いたりはほぼ無料で行うことができる
社員の負担を減らすことは会社としてプラスに働く
新たな福利厚生を提案されるとNoという理由を探しがちだがYesという利用を見つけるようにしよう
第12章 ナッジ / 選択の背中を押す
ナッジとは行動経済学の用語で、人々が強制的ではなく自発的により良い選択ができるようにする工夫のこと
ナッジを使うことでチームの行動をより良いものに変えることができる
ただし、選択肢を奪うことは不満を産むので、選択肢としては残した上でより良い方を選ぶように工夫する必要がある
ナッジは強制ではないということに気を付ける
第13章 人生は最高の時ばかりではない
失敗の話
今までのGoogleの制度は最初からうまくいったものではなく、多くの失敗を繰り返して出来上がったもの
失敗したときは間違いを認め隠そうとしない
あらゆる方向に助言を求め、間違いから教訓を学びそれを伝えると良い
感想
googleの人事制度についてデータを交えながらどのような施策を実践し、現在どのように運用されているかを知ることができた
細かな施策は多いが根元となる考え方としては、「データに基づいて判断する」「社員は会社にとって最も大切な資産」ってのがあるのかなと思った
本の主旨とはずれるけど福利厚生の部分では、会社の制度としてあればもちろんいいが、ないとしても自分の健康とパフォーマンス向上のために時間とお金をかけることは大切だと感じた
無料版はてなブログでGoogleアドセンスに通った話
現在無料プランではてなブログをつかってるんですが
2/22にGoogleアドセンスの審査に通りました
やったことをさらす系の記事はごまんとあるとぉうんですが、僕の場合はマジでなにもしてません
気合いで申し込みつづけたら受かることもあるよってことで記事に残します
1回目 12/4 不合格 お客様のサイトが見つかりません
Googleアナリティクスを使ってたらGoogleアドセンスなるものを発見
ブログで稼げたらいいなーくらいのノリで申し込む
翌日結果が返ってきました
あれ、落ちた
ログインして理由を確認してみると
お客様のサイトが見つかりません
そもそも申し込みの時点でheadタグにコードを貼り付けて、「お客様のコードが見つかりました」って出てたのにそんなことあるのか?と思って調べるとはてブあるあるらしいですね
ググると
- wwwから始まるurlでリダイレクトされるようにする
- 登録時のurlの末尾に/をつける
- 独自ドメインを取得する
などいろいろ対策が出てきました
はてブProにするかドメイン取得しないといけないみたいなんで「PV増えたらやるかー」とおもって放置してました
2回目 2/18 不合格 お客様のサイトが見つかりません
はてブのホットトピックにアドセンス合格しました!みたいな記事を見つけて、もう1回やってみるかと思い申し込みました
読んだ記事に「割と運かも?」とあったのでとりあえずなんの対策もせず申し込んでみる
また翌日には結果が返ってきました
お客様のサイトが見つかりません
(-_-)ウーム、なぜだ
3回目 2/20 不合格 お客様のサイトが見つかりません
まあ運ゲーならこれくらいでへこたれちゃダメだ!とすぐにもう一度申し込む
お客様のサイトが見つかりません
うん、まあ知ってた
4回目 2/22 なぜか合格!!!!
おれは諦めないぞ、と若干ヤケになりつつ申し込む
翌日、、、
え、合格した、、、
やったー!!!というより、なんで?という気持ちの方がでかかったですね
一応書いとくとこの時点で記事は80くらい
PVは一日10くらいです(悲しい)
結論
おそらくはてブとGoogleアドセンスはシンプルに相性がわるい!
結果が「お客様のサイトが見つかりません」なら申し込みつづけると通ることがある!
諦めずに頑張りましょう