TerraformをDigital Oceanで試してみる2 環境情報の外出し
前回では、do.tfファイルの中に全ての情報を書き込みました。
それではtoken情報やssh keyの値などはgitなどで管理しづらいので、ssh_keysとtokenを別のファイルで管理します。
ファイルを外出しするようになると、以下の様な構成になります。
. ├── do.tf ├── terraform.tfvars └── variables.tf
この構成になるとterraform.tfvarsにtoken情報やssh keyの値を書くことで、このファイルのみをコミット対象外するとこでgitなどで問題無く扱えるようになります。
variables.tf
terraform.tfvarsで宣言する変数を宣言するためのファイルです。
do.tfの中で宣言しても、問題無いですが別ファイルで管理します。ファイルの内容はInput Variablesにあるように、書いていきます。
variable digitalocean_token {} variable ssh_key_id {}
terraform.tfvars
このファイルでは、変数と値を定義します。
terraform.tfvarsファイルという名前にしておくと、上記のような構成にしておくと-var-fileオプションを付けなくても自動的にTerraformが読み込んでくれます。
dgitalocean_token = "前回作成したtoken" ssh_key_id = "512189"
do.tf
前回作成したdo.tfのtokenとssh_keysの値を、variables.tfで定義した変数に書き換えます。
provider "digitalocean" { token = "${var.digitalocean_token}" } resource "digitalocean_droplet" "node-1" { image = "ubuntu-14-04-x64" name = "node-1" region = "sgp1" size = "512mb" ssh_keys = [${var.ssh_key_id}] }
これで、terraform plan や terraform applyの引数は変更なく実行出来ます。
TerraformをDigital Oceanで試してみる1
Digital Oceanの情報取得
Terraform を Digital Ocean で触ってみた (初級編) を参考にDigital Ocean のtokenを作成します。
Degital OceanのAPI V2を利用して、curlからSSH鍵の情報を取得します。
参考:https://developers.digitalocean.com/documentation/v2/#ssh-keys
# curl -X GET -H 'Content-Type: application/json' -H 'Authorization: Bearer 先ほど作成したtokenを記載' "https://api.digitalocean.com/v2/account/keys" { "ssh_keys": [ { "id": 512189, "fingerprint": "3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa", "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example", "name": "My SSH Public Key" } ], "links": { }, "meta": { "total": 1 } }
ここで返ってきたidとDegital Oceanのtokenを次で作成する.tfファイルで利用します。
.tfファイルの作成
terraformでは、.tfファイルを作成しそこから環境を作成出来ます。
ここで、今まで行った作業を元にDegital Ocean用のdo.tfファイルを作成してみます。
provider "digitalocean" { token = "作成したtokenを記載" } resource "digitalocean_droplet" "node-1" { image = "ubuntu-14-04-x64" name = "node-1" region = "sgp1" size = "512mb" ssh_keys = [512189] }
dropletの作成
do.tfを利用して、dropletを作成します。 planのみでは実際にdropletは作成されず、applyまで実行することでdropletが作成されます。
$ terraform plan Refreshing Terraform state prior to plan... The Terraform execution plan has been generated and is shown below. Resources are shown in alphabetical order for quick scanning. Green resources will be created (or destroyed and then created if an existing resource exists), yellow resources are being changed in-place, and red resources will be destroyed. Note: You didn't specify an "-out" parameter to save this plan, so when "apply" is called, Terraform can't guarantee this is what will execute. + digitalocean_droplet.node-1 image: "" => "ubuntu-14-04-x64" ipv4_address: "" => "<computed>" ipv4_address_private: "" => "<computed>" ipv6_address: "" => "<computed>" ipv6_address_private: "" => "<computed>" locked: "" => "<computed>" name: "" => "node-1" region: "" => "sgp1" size: "" => "512mb" ssh_keys.#: "" => "1" ssh_keys.0: "" => "512189" status: "" => "<computed>" $ terraform apply digitalocean_droplet.node-1: Creating... image: "" => "ubuntu-14-04-x64" ipv4_address: "" => "<computed>" ipv4_address_private: "" => "<computed>" ipv6_address: "" => "<computed>" ipv6_address_private: "" => "<computed>" locked: "" => "<computed>" name: "" => "node-1" region: "" => "sgp1" size: "" => "512mb" ssh_keys.#: "" => "1" ssh_keys.0: "" => "512189" status: "" => "<computed>" digitalocean_droplet.node-1: Creation complete
status: "" => "
dropletにログイン
作成したdropletの情報を確認してログインします。
$ terraform show digitalocean_droplet.node-1: id = 5556133 image = ubuntu-14-04-x64 ipv4_address = 128.199.64.45 locked = false name = node-1 region = sgp1 size = 512mb ssh_keys.# = 1 ssh_keys.0 = 512189 status = active
IPアドレスがわかったので、実際にログインしてみます。
秘密鍵は~/.ssh/id_rsaにあるものとしています。
ssh -i ~/.ssh/id_rsa root@128.199.64.45
dropletの破棄
作成したdropletを破棄します。
$ terraform destroy Do you really want to destroy? Terraform will delete all your managed infrastructure. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes digitalocean_droplet.node-1: Refreshing state... (ID: 5556133) digitalocean_droplet.node-1: Destroying... digitalocean_droplet.node-1: Destruction complete Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
gorillaでAPIサーバを書く 1
gorillaを使ってAPIサーバを書く。
ディレクトリ構成としては、以下を想定。
.api │ ├── handlers │ ├── core.go │ └── router.go └── api.go
それぞれのファイルは大まかに以下のような役割になります。
- api.goはサーバの起動設定などを記載
- router.goはファイル名の通りrouterなので、パスに合わせたハンドラの設定
- core.goはハンドラーの実装
まずは必要なパッケージのインストール
go get github.com/gorilla/mux
api.goはgorilla特有の処理などは書かないので割愛。
router.go では、mux.Routerを使ったRouter関数を定義します。 エンドポイントを指定して、エンドポイントごとにHTTPメソッドに対応するHandler関数を設定します。
package handlers import ( "net/http" "github.com/gorilla/mux" ) const ( RegexMatchUserID = "[A-Za-z0-9]{3}" ) func Router() *mux.Router { r := mux.NewRouter() s := r.PathPrefix("/api/"). Subrouter().StrictSlash(false) usersPath := "/users/" subpath := usersPath + "{userId:" + RegexMatchUserID + "}" s.HandleFunc(usersPath, GetUsers).Methods("GET") s.HandleFunc(subpath, GetUserById).Methods("GET") s.HandleFunc(subpath, DeleteUserById).Methods("DELETE") s.HandleFunc(subpath, CreateUser).Methods("POST"). Headers("Content-Type", "application/json") return r }
PathPrefixでベースとなるURLを元にエンドポイントを組み立て行く方法の他に、下記のようにPathとPathPrefixを組み合わて同じように書くことが出来ます。
func Router() *mux.Router { r := mux.NewRouter() userPath := urlbase + "/users/" users := r.Path(userPath).Subrouter() users.Methods("GET").HandlerFunc(GetUsers) subpath := userPath + "{userId:" + RegexMatchUserID + "}" usr := r.PathPrefix(subpath).Subrouter() usr.Methods("GET").HandlerFunc(GetUserById) usr.Methods("DELETE").HandlerFunc(DeleteUserById) usr.Methods("POST").HandlerFunc(CreateUser) return r }
Handlerメソッドについては、次回記載します。
SICPを読む会 1 問題1.3
SICP Support for DrRacketのインストールを参考にDrRacketをインストールして実行環境を作成します。
DrRacketで言語宣言ペイン(上側のペイン)で、#lang planet neil/sicp と書いて実行しても調べたページとは違う挙動しましたが
Schemeが実行出来るようなで練習問題を実施。
今日は問題1.3を実施。
今回は考え方としては3値のうち一番小さい値は何かという観点で手続きを定義します。
下記の一番最初のifの評価としては、『xはyよりも大きい、またはxはzよりも大きいのどちらもFalseになる = xは最も小さい』という周りくどい評価を実施しています。
> (define (square x) (* x x)) > (define (sum-of-squares x y) (+ (square x) (square y))) > (define (f x y z) (if (or (>= x y) (>= x z)) (if (or (>= y z) (>= y x)) (sum-of-squares x y) (sum-of-squares z x)) (sum-of-squares y z))) > (f 3 4 5) 41 > (f 4 3 5) 41 > (f 4 5 3) 41
また、schemeではminという手続きが定義されており最小の値を求めることが出来るので、以下のように書き換えることが出来ます。
> (define (f x y z) (if (= (min x y z) x) (sum-of-squares y z) (if (= (min x y z) y) (sum-of-squares x z) (sum-of-squares x y)) ) ) > (f 3 4 5) 41 > (f 4 3 5) 41 > (f 5 4 3) 41
このような場合はifではなくcondを使ったほうが可読性が上がるので、condを使って書き換えます。
> (define (f x y z) (cond ((= (min x y z) x) (sum-of-squares y z)) ((= (min x y z) y) (sum-of-squares x z)) (else (sum-of-squares x y)))) > (f 3 4 5) 41 > (f 4 5 3) 41 > (f 5 3 4) 41
goのお勉強1
スコープ
変数や関数名で1文字目が小文字だとパッケージ内からのみ見える。大文字だとパッケージ外からのみ見える。
ポインタ
ポインタは、C言語と同様に利用できる。
package main import "fmt" func main() { a := 5 var pa *int = &a // *intはint型のpointer fmt.Println(pa) // アドレスが表示される fmt.Println(*pa) // 値(5)が表示される *pa = 10 fmt.Println(a) // 10が表示される }
ただし、C言語と違いポインタは演算は出来ない。 c言語だと配列とかはポインタで表現され、以下のようにできるがgoでは出来ないことを確認する。 C言語では以下のように記述できる。
#include <stdio.h> int main() { int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int* p = array; /* 配列の全要素の値を表示する */ for (int i = 0; i < 10; ++i) { printf("*p = %d\n", *p); p = p + 1; /* ポインタの値を 1 増やす */ } return 0; }
goで同じような処理をしようとしてみるとinvalid operationとコンパイルエラーになる。
スライスにしても同様にコンパイルエラーになる。
package main import "fmt" func main() { arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} var p *[10]int = &arr for i := 0; i < 10; i++ { fmt.Println(*p) // [1 2 3 4 5 6 7 8 9 10]が出力される // fmt.Println(p[i]) とすると配列のi番目の値を出力できる p = p + 1 // invalid operation: p + 1 (mismatched types *[10]int and int) となりコンパイルエラーになる。 } }