「PHPフレームワーク Laravel Webアプリケーション開発」を読み終えた感想
2018/9/26に発売された、書籍「PHPフレームワーク Laravel Webアプリケーション開発」を読み終えましたので、感想を書いていきます。
「PHPフレームワーク Laravel Webアプリケーション開発」とは
https://www.amazon.co.jp/gp/product/4802611846
こちらは、PHP界隈ではとても有名な@ex_takezawaさん・@kurikazuさん・@shin1x1さん・@omoonさんが執筆し2018年9月26日に発売されたものになります。 書籍の概要については、@shin1x1さんのブログにてご紹介されている内容を参照させていただきます。
Laravel 5.5(LTS) に対応した本です。Laravel リファレンス のようにフレームワークの機能解説はもちろん含まれているのですが、機能を紹介するというより、実際にありそうなユースケースでどのように利用するかといった視点で書かれており、開発現場で Laravel をより活用したい向けの内容になっています。目次を見ると、実践的な内容が多く含まれていることが分かるでしょう。
読み始める前のモチベーション
今回の本を手にとったモチベーションとしては次のような物がありました。
- 普段、CakePHPを業務で触っているが、Laravel自体を改めてがっつり触りたい
- サービスコンテナがどのような実装になっているかなどLaravel内部の実装を知りたい
- レイヤードアーキテクチャなどアーキテクチャについての実践例を知りたい
読んだ結果
控えめに言って大変勉強になった内容でした。具体的には下記の点がとても良かったです。
Laravelの内部実装・構造から学ぶ
序盤の章にて、Laravelの内部実装を見ながら、サービスコンテナの仕組みやMVC・ADRといったアーキテクチャについて学べる内容になっていました。自分の中で言語化されていなった点についてLaravelのコードにて示される点がよかったです。
テスト駆動開発の実践例
最終章にて、顧客訪問記録アプリケーションを想定したバックエンドAPIをテスト駆動で実装してく章があります。
API実装といった業務で実践的な例でのテスト駆動開発についてやり方を見ることができたのは、普段自分が実践してみている方法と照らし合わせつつ見れたのでとても参考になりました。
読み方
この著書は、合計531ページありとてもボリューミーだったので、私は次の読み方で進めました。
- 第一部は、全部しっかり読み込み・写経する
- 第二部は、部分的に詳細の仕様の話になるので、全体感を抑えながら第一部からつながる部分のみ写経する
- 第三部は、テスト駆動開発での実践で第一部・第二部の知識の総集編として抑えられるのでガッツリ写経する
写経レポジトリ
今回写経していったレポジトリはgithub上にcommitしながらやっていました。chapter終了タイミングごとにcommitをしてrelease labelを付ける方法でやっています。
最後に
「PHPフレームワーク Laravel Webアプリケーション開発」は、普段Laravelを使っていない身でも理解しやすく、Laravelに限らずアプリケーション設計・アーキテクチャなど非常に汎用的な知識に関して実践できる内容になっていました。
まだ、手に取られていない方は、ぜひ一度手に取るといいと思います!
(サインほしい。。。)
Laravel本、来月のPHP勉強会までに読破してサイン懇願しにいく心づもり
— Khigashiguchi (@Khigashiguchi) September 28, 2018
ドメイン駆動設計についてpospomeさんの話を聞いた | Gopher道場課外学習Ⅰ参加ログ
Gopher道場卒業生向けのイベントとして、ドメイン駆動設計について@pospomeさんの話を聞かせていただきました。
その際に話されていた内容についてざっとメモを残させていただきます。
ドメイン駆動設計とは
- ドメイン
- = 問題領域
- ソフトウェアを適用する業務や関心事
- ドメイン = 業務
- 例:弥生会計
- 業務で設計を駆動すること = ドメイン駆動設計
merit of ddd
- 既存の業務駆動ではない設計手法
- DOA(データ中心アプローチ)
- テーブル構造がModelになるMVCフレームワークではDOAになりがち
- DBにデータを格納することを前提としている
- -> DBの特性・目的に合わせたデータ設計になる
- 特性:redis・RDBMS等でデータ設計が異なる
- 目的:頻度・データ量によって異なる
- DOAによる設計はプログラミングに対して最適かどうかはそうとは言えない、可読性の低下の可能性
- 業務上存在する概念をコード上で扱えない
- ex: UserAcountテーブルの構造体とプロフィール変更
- プロフィール変更についてどのカラムがプロフィールなの?ということに関して表現できていない
- UserProfile structを作って何がプロフィールかをコード上で表現する
- ex: Boss
- rankをもっている、big: 大ボス、middle: 中ボス
- 大ボスのみAttack()できるという仕様をどう表現するか、Attackはいつ発動するのか(中ボスからAttackを呼んだときになにもしないという事がわからない。)
- BigBoss / MiddleBossでそもそも分けてしまう。別々のモデルにすることでそれぞれ何ができるかが明確になる
- DOA(データ中心アプローチ)
- DOAで設計したデータ構造をそのままプログラミングで扱うのが問題
- 可読性が下がる根本原因 = 業務とコードの乖離
- 問題:業務の理解・コードの理解、両方を理解しないといけない
- ex: type, status, flg といった謎カラム
- 問題:業務の理解・コードの理解、両方を理解しないといけない
- DDDではデータ構造をそのままプログラミングで利用しない、プログラミングに適したデータ構造を利用する
- -> 業務とコードの乖離を可能な限り少なくし可読性を分ける
- -> 業務を理解 -> コードも理解できる
- -> 実装詳細に踏み込みやすいコードになる
- 業務を反映したデータ構造 + 振る舞い => 業務駆動開発(DDD(
何をすればDDDになるのか
- 実装がゴール、実装のための分析・設計
- 分析
- 業務をコードに落とし込めるように分析する
- 実装がゴール、そのゴールを達成するための分析
- 設計
- 分析結果をコードに落とし込むための設計
- 実装
- 実装する
- 各フェーズに対応したテクニックが存在する
- レイヤ構造は実装のための設計、分析・設計・実装のための一つの要素に過ぎない。
- リポジトリパターンは実装テクニック、同上でただの一つの要素に過ぎない
- 戦略(分析&設計)・戦術とDDDは分けている
- 戦略(分析&設計)が大事
- 戦術のみ適用する = 軽量DDD、アンチパターン
- ex. レイヤ構造・リポジトリパターンを適用したよ = 軽量DDD
- 分析をしていない、業務とコードに乖離が生じる
- 分析→設計→実装を通して初めてDDDになる
demerit of ddd
- 学習コスト
- 分析コスト
- 実際可読性が上がるかはわからない、分析結果によっては可読性が上がらない可能性がある
- モデルとDBのデータ構造が異なる、モデルとDBがほぼ同一である前提であるRailsのようなフレームワークを使っている場合の実装の手間
解決できないこと
- 業務の複雑さに伴うコードの複雑さは解決できない
- そもそも業務が複雑であれば、それをそのまま反映する
- 実装者の設計スキルが低いと可読性落ちる
necessity of DDD
DDD不要な例
- DBのデータ構造がプログラミングに適している場合
- ex: 単一概念のCRUD
- DDD不要
- 業務とコードのか入が問題にならない
- 保守しない、業務が単純
軽量DDD
- DDDではないが、単なる実装パターン詰合せとしては優秀
GoとDDDの相性
GoでDDDは相性
Q&A
- メルカリで使っているか
- 現状使っていない、ビジネススピードなどによりWeb系で導入していない
- microserviceとの相性
- 相性はいいっちゃいい、業界付けられたコンテキストのテクニックは相性がいい
- ただし、DDDとマイクロサービスの設計の親和性は要件による。
- データ構造がプログラミングに適しているかどうか。
- 密結合を分割 -> 分断されたモノリス
- マイクロサービス間で重複したコードを書く問題
- マイクロサービスをやるならコードの重複を受け入れるのが前提のスタンスと必要になる
- その上で共有したい場合は、ライブラリ化・サブモジュール化というアプローチもあるが、各サービスで必要なメソッド・フィールドを足していったりするとカオスになる。
- サービスごとに必要とすることが異なるケースが多い。
- コードが成果物 = コードを見ることで業務を知ることができる
- DDDの目的は可読性の高いコードを書くこと
- いい設計を行うためのアプローチ
- 作る対象の業務を知る必要がある
- 業務の理解度を上げることでコードの質が上がる
- ただし、設計の引き出しが多い人が勝つ、設計スキルが低いとそもそも可読性が下がる
- たくさん書いてたくさん失敗すること
- なれている自分の設計パターンと違うアプローチを取り入れてみる、いつものパターンは人から見たら良くないかもしれない。
最後に
ドメイン駆動設計について、何をやったら「ドメイン駆動設計」なの?とふわふわしていたのですが、「業務(ドメイン)で設計を駆動する」がどういうことなのか解説いただいたのがとても参考になりました。
次回はGCPについてのお話が聞けるということでとても楽しみです! mercari.connpass.com
OAuth 2.0 サーバの実装・テストについてgolangtokyoで発表してきました | golang.tokyo#18 参加レポート
2018年9月28日(金)に開催されたgolang.tokyo #18に参加して、LT枠(10分)で発表してきました。
golang.tokyoとは
プログラミング言語のGoの導入企業のメンバーが集まり、Goの普及を推進するコミュニティです トークイベント、ハンズオン、etcのイベントを開催していく予定です!
発表内容
今回は、APIを作成するに当たり面倒で避けられない「認証・認可」について、各種背景からモノリシックにリクエストハンドラーとして組み込む場合どう実装するかという発表でした。
フルスクラッチではなく、Red Hat OpenShiftがgithubに公開している、openshift/osinというOAuth2.0のサーバーライブラリを用いた実装・テストの例をまとめています。
LT応募方法
LT応募してみたいという方は、毎回のconnpassページ内で以下の感じでLT募集フォームがあると思うのでこちらで応募できます!
感想
約1年前からGo言語を趣味で触り始めてどこかでgolang.tokyoで発表してみたいと思っていたので、まだまだ精進が必要ですが一つ目標が達成できてよかったです。また、ネタを見つけて発表しに行きたいと思います。
TerraformでAWSのインフラ構成構築を自動化する(入門)
Terraformを用いてAWSのインフラ構成・構築を自動化するに当たり入門資料です。この記事では、Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版で構築する構成図をTerraformで構築していきます。
発表資料
Terraformとは
Terraformとは、HashiCorpが作っているコードからインフラリソースを作成・管理するためのツールです。
AWS, GCP, Azure, Heroku など多くのSaaSに幅広く対応しています。
今回作成するインフラ構成
Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版で構築する構成がベースになります。
具体的には、次のような構成要素が存在します。
- VPC
- CIDR:
10.1.0.0/16
- Internet gatewayがattachされている
- CIDR:
- public subnet
- CIDR:
10.1.1.0/24
- 外部InternetからのINBOUND接続が可能なsubnet
- CIDR:
- private subnet
- CIDR:
10.1.2.0/24
- 外部InternetからのINBOUND接続が遮断されたsubnet
- OUTBOUND接続は可能とするため、NAT Gatewayに対してRoutingが設定されている
- CIDR:
- EC2 instance: "web"
- subnet: public subnet
- private IP address:
10.1.1.10
- 外部Internetからの接続を受け付けるようなWebサーバーが想定される
- EC2 instance: "db"
- subnet: private subnet
- private IP address:
10.1.2.10
- 外部Internetからの接続を受け付けないDBサーバーが想定される
構築する
今回構築する際のサンプルコードは下記レポジトリにて公開しています。
準備
本資料を進めるに当たり、Terraformがinstallされている必要があるため公式リファレンスにそってインストールしておきましょう。
手順
- Asia/PacificにVPCを作成する
- public subnetを作成する
- Internet Gatewayを作成してpublic subnetのroute tableに設定する
- public subnetにEC2 instanceを作成する
- private subnetを作成しEC2 instanceを作成する
- NAT Gatewayを作成する
1. Asia/PacificにVPCを作成する
本章のコードは、GitHub - Khigashiguchi/terraform-basic-network at 0.1 にて公開しています。
Terraformでは、*.tf
拡張子のファイルでインフラ構成について定義していきます。まずは、VPCを作成するところからです。main.tf
というファイルを作ります。
provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "ap-northeast-1" }
まず、最初のprovider
では、どのSaaSを利用したインフラ構成について定義していくのかを書きます。ここが、Google Cloudであればgoogle
と定義することになります。
Provider: Google Cloud - Terraform by HashiCorp
今回は、AWSを利用するため、aws
をproviderに指定し、AWSのAPIに利用に必要なaccess_key
・secret_key
、そして対象regionを指定します。
ここでは、べた書きすることも可能なのですがgit管理外にアクセスキーを置きたいため、variables
という機能を用います。
別途、variable.tf
というファイルを作成して次のように定義します。
variable "aws_access_key" {} variable "aws_secret_key" {}
上記のを指定することで、main.tf
内では"${var.aws_access_key}"
といった形で使うことが出来ます。ここでvariableとした場合、実際にインフラ構築時のコマンドインターフェースにて値を入力するなどと行ったgit管理外の場所から注入することが可能になります。
この手順では、VPCを作成したいため先程定義したmain.tf
に対して以下追記します。
provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "ap-northeast-1" } // +以下追記 resource "aws_vpc" "main" { cidr_block = "10.1.0.0/16" tags { Name = "VPC領域:Terraform" } }
実際に実行してみましょう。まず、terraform init
というコマンドによって初期化します。
$ terraform init
その後、terraform apply
というコマンドによって記述したインフラ構成を実際に構築することが出来ます。
$ terraform apply var.aws_access_key / var.aws_secret_key An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: + aws_vpc.main id: <computed> arn: <computed> assign_generated_ipv6_cidr_block: "false" cidr_block: "10.1.0.0/16" default_network_acl_id: <computed> default_route_table_id: <computed> default_security_group_id: <computed> dhcp_options_id: <computed> enable_classiclink: <computed> enable_classiclink_dns_support: <computed> enable_dns_hostnames: <computed> enable_dns_support: "true" instance_tenancy: "default" ipv6_association_id: <computed> ipv6_cidr_block: <computed> main_route_table_id: <computed> tags.%: "1" tags.Name: "VPC領域:Terraform" Plan: 1 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value:
コマンドを実行した後、Enter a value
と聞かれて一度止まるので、ここでyes
と入力することで実際に動作します。
コマンド実行終了後、現状どうなっているかを確認するためには、terraform show
というコマンドを実行することで確認することが出来ます。
$ terraform show
ここでは、確認できたので一度作成したVPCを削除してみます。削除するには、terraform destroy
と実行することで構築したものを削除することが出来ます。
$ terraform destroy
次に、作成したVPC内でsubnetを切っていきます。
手順1の関連資料
- Terraform公式リファレンス:Configuration: Data Source Configuration
- Terraform公式リファレンス:Configuration: Resource Configuration
- Terraform公式リファレンス:Resource: AWS Provider
- Terraform公式リファレンス:Resource: aws_vpc
2. public subnetを作成する
本章のコードは、GitHub - Khigashiguchi/terraform-basic-network at 0.2 にて公開しています。
作成したVPC内にsubnetを切ります。手順1で作成したmain.tf
に次の記述を追加することでsubnetを作成することが出来ます。
provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "ap-northeast-1" } resource "aws_vpc" "main" { cidr_block = "10.1.0.0/16" tags { Name = "VPC領域:Terraform" } } // + 以下追記 resource "aws_subnet" "web" { vpc_id = "${aws_vpc.main.id}" cidr_block = "10.1.1.0/24" tags { Name = "Public Subnet by Terraform" } }
ここでは、CIDRが10.1.1.0/24
であるsubnetをVPC内に作成しています。次に作成したsubnetに対してroute tableを作成していきます。
手順2の関連資料
3. Internet Gatewayを作成してpublic subnetのroute tableに設定する
本章のコードは、GitHub - Khigashiguchi/terraform-basic-network at 0.3 にて公開しています。
手順2で作成したsubnetのroute table・外部internetからの接続を受け付けるためのinternet gatewayを作成します。main.tf
に次のように追記します。
provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "ap-northeast-1" } resource "aws_vpc" "main" { cidr_block = "10.1.0.0/16" tags { Name = "VPC領域:Terraform" } } // + 追記 resource "aws_internet_gateway" "gw" { vpc_id = "${aws_vpc.main.id}" tags { Name = "Internet Gateway by Terraform" } } // + 追記 resource "aws_route_table" "r" { vpc_id = "${aws_vpc.main.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.gw.id}" } tags{ Name = "Public route table by Terraform" } } resource "aws_subnet" "public" { vpc_id = "${aws_vpc.main.id}" cidr_block = "10.1.1.0/24" tags { Name = "Public Subnet by Terraform" } } // + 追記 resource "aws_route_table_association" "a" { subnet_id = "${aws_subnet.public.id}" route_table_id = "${aws_route_table.r.id}" }
ここでは、3つの要素が追記されています。
まず、AWS Internet GatewayをVPC内に作成しています。そして、作成したInternet Gatewayに接続を流すroute tableを作成しています。最後に、public subnetとroute tableを関連付けています。
これにより、"public subnet"は外部Internetからの接続を受け付けられるようになりました。
手順3の関連資料
- Terraform公式リファレンス:Resource: aws_internet_gateway
- Terraform公式リファレンス:Resource: aws_route_table
- Terraform公式リファレンス:Resource: aws_route_table_association
4. public subnetにEC2 instanceを作成する
本章のコードは、GitHub - Khigashiguchi/terraform-basic-network at 0.4 にて公開しています。
手順3までで作成したsubnet内にEC2 instanceを作ります。main.tf
に追記します。
provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "ap-northeast-1" } resource "aws_vpc" "main" { cidr_block = "10.1.0.0/16" enable_dns_support = true // +追記 enable_dns_hostnames = true // +追記 tags { Name = "VPC領域:Terraform" } } resource "aws_internet_gateway" "gw" { vpc_id = "${aws_vpc.main.id}" tags { Name = "Internet Gateway by Terraform" } } resource "aws_route_table" "r" { vpc_id = "${aws_vpc.main.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.gw.id}" } tags{ Name = "Public route table by Terraform" } } resource "aws_subnet" "public" { vpc_id = "${aws_vpc.main.id}" cidr_block = "10.1.1.0/24" tags { Name = "Public Subnet by Terraform" } } resource "aws_route_table_association" "a" { subnet_id = "${aws_subnet.public.id}" route_table_id = "${aws_route_table.r.id}" } // + 追記 resource "aws_security_group" "wsg" { name = "web-sg" description = "web-sg security group created by terraform" vpc_id = "${aws_vpc.main.id}" ingress{ from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress{ from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } tags{ Name = "web-sg" } } // + 追記 resource "aws_key_pair" "auth" { key_name = "${var.key_name}" public_key = "${file(var.public_key_path)}" } // + 追記 resource "aws_instance" "web" { ami = "ami-08847abae18baa040" // Amazon Linux 2 AMI (HVM), SSD Volume Type instance_type = "t2.micro" subnet_id = "${aws_subnet.public.id}" private_ip = "10.1.1.10" associate_public_ip_address = true security_groups = [ "${aws_security_group.wsg.id}" ] key_name = "${aws_key_pair.auth.id}" tags { Name = "Web Server by Terraform" } }
ここでは、3つの要素を追加しています。
まずは、Instanceに設定するSecurity Groupです。web-sg
という名前で作っています。このセキュリティグループは、22・80ポートを全IPから受け付けるという設定になります。
次に、aws_key_pair
という公開鍵認証でSSH接続する際の鍵です。ここでは次の実施する内容にてローカルで生成した公開鍵をkey pairに指定するという作業を進めます。
最後に、EC2 instanceを作成しています。大まかに下記のような内容のInstanceを作成します。
- 無料枠で使用できるAMI(Amazon Linux 2 AMI (HVM), SSD Volume Type_
- インタンスタイプ:t2.micro
- subnet:上で作成したpublic subnet
そして、SSH接続するためのキーを作成・設定します。まず、すでに定義したmain.tf
の記述内容に合わせてvariables.tf
に下記を追加します。
variable "aws_access_key" {} variable "aws_secret_key" {} // +追記 variable "key_name" { description = "the name of aws key pair" } // +追記 variable "public_key_path" { description = "path to the ssh public key" }
そして、これまでvariableの指定をコマンドインターフェースからやっていましたが、同一ディレクトリにtfvars
という拡張子のファイルを置くことによって、ファイルに指定することが出来ます。
実際に、terraform.tfvars
というファイルを作成して下記のように定義します。
aws_access_key = "YOUR-ACCESS-KEY" aws_secret_key = "YOUR-SECRET-KEY" key_name = "terraform-basic-key" public_key_path = "~/.ssh/terraform-basic-key.pub"
今まで、コマンドインターフェースで入力していた内容もこのように書くことにってファイルからの入力が可能になります。(このファイルはシークレット情報が含まれるためgit管理外に置くことが推奨されます。)
最後に、terraform-basic-key
という名前でsshキーを作成して~/.ssh
以下に設置しておきましょう。
上記により、EC2 Instanceの生成が可能になりました。
手順4の関連資料
- Terraform公式リファレンス:Resource: aws_instance
- Terraform公式リファレンス:Resource: aws_security_group
- Hashicorp/terraform:issues#2164 How to give a single security group for an aws instance ?
- Terraform でキーペア登録し起動した EC2 に SSH接続
5. private subnetを作成しEC2 instanceを作成する
本章のコードは、GitHub - Khigashiguchi/terraform-basic-network at 0.6 にて公開しています。
さらに、外部Internetからの接続を受け付けないprivate subnetを作成してその中にinstanceを作ります。main.tf
に追記していきます。
provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "ap-northeast-1" } resource "aws_vpc" "main" { cidr_block = "10.1.0.0/16" enable_dns_support = true enable_dns_hostnames = true tags { Name = "VPC領域:Terraform" } } resource "aws_internet_gateway" "gw" { vpc_id = "${aws_vpc.main.id}" tags { Name = "Internet Gateway by Terraform" } } resource "aws_route_table" "r" { vpc_id = "${aws_vpc.main.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.gw.id}" } tags{ Name = "Public route table by Terraform" } } resource "aws_subnet" "public" { vpc_id = "${aws_vpc.main.id}" cidr_block = "10.1.1.0/24" availability_zone = "ap-northeast-1d" tags { Name = "Public Subnet by Terraform" } } // +追記 // private subnetの作成、internet gatewayとの接続のないdefault route tableをそのまま使うことで外部Internetから遮断されている状態にする resource "aws_subnet" "private" { vpc_id = "${aws_vpc.main.id}" cidr_block = "10.1.2.0/24" availability_zone = "ap-northeast-1d" tags { Name = "Private Subnet by Terraform" } } resource "aws_route_table_association" "a" { subnet_id = "${aws_subnet.public.id}" route_table_id = "${aws_route_table.r.id}" } resource "aws_security_group" "wsg" { name = "web-sg" description = "web-sg security group created by terraform" vpc_id = "${aws_vpc.main.id}" ingress{ from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress{ from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } // +追記 // pingでの疎通確認ができるように設定 ingress{ from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] } tags{ Name = "web-sg" } } // +追記 // 22番・3306番ポート・ICMPプロトコル通信を許可する設定(DBサーバを想定しているため) resource "aws_security_group" "dsg" { name = "db-sg" description = "db-sg security group created by Terraform" vpc_id = "${aws_vpc.main.id}" ingress{ from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress{ from_port = 3306 to_port = 3306 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress{ from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_key_pair" "auth" { key_name = "${var.key_name}" public_key = "${file(var.public_key_path)}" } resource "aws_instance" "web" { ami = "ami-08847abae18baa040" // Amazon Linux 2 AMI (HVM), SSD Volume Type instance_type = "t2.micro" subnet_id = "${aws_subnet.public.id}" private_ip = "10.1.1.10" associate_public_ip_address = true security_groups = [ "${aws_security_group.wsg.id}" ] key_name = "${aws_key_pair.auth.id}" tags { Name = "Web Server by Terraform" } } // +追記 // private subnet内にinstanceを作成 resource "aws_instance" "db" { ami = "ami-08847abae18baa040" // Amazon Linux 2 AMI (HVM), SSD Volume Type instance_type = "t2.micro" subnet_id = "${aws_subnet.private.id}" associate_public_ip_address = false private_ip = "10.1.2.10" security_groups = [ "${aws_security_group.dsg.id}" ] key_name = "${aws_key_pair.auth.id}" tags { Name = "DB Server by Terraform" } }
行数が増えてきたので、main.tfのコメントにて詳細について記載しています。
6. NAT Gatewayを作成する
本章のコードは、GitHub - Khigashiguchi/terraform-basic-network at 0.7 にて公開しています。
private subnet内のInstanceから外部に対してのOUTBOUND通信ができるようにNAT Gatewayを設定します。main.tfに下記のように追記します。
provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "ap-northeast-1" } resource "aws_vpc" "main" { cidr_block = "10.1.0.0/16" enable_dns_support = true enable_dns_hostnames = true tags { Name = "VPC領域:Terraform" } } resource "aws_internet_gateway" "gw" { vpc_id = "${aws_vpc.main.id}" tags { Name = "Internet Gateway by Terraform" } } resource "aws_route_table" "public" { // - 修正 from "a" -> "public" vpc_id = "${aws_vpc.main.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.gw.id}" } tags{ Name = "Public route table by Terraform" } } // + 追記 // private subnetのroute tableを作成、以降で定義するNAT Gatewayを指定。 resource "aws_route_table" "private" { vpc_id = "${aws_vpc.main.id}" route { cidr_block = "0.0.0.0/0" nat_gateway_id = "${aws_nat_gateway.default.id}" } tags{ Name = "Private route table by Terraform" } } resource "aws_subnet" "public" { vpc_id = "${aws_vpc.main.id}" cidr_block = "10.1.1.0/24" availability_zone = "ap-northeast-1d" tags { Name = "Public Subnet by Terraform" } } resource "aws_subnet" "private" { vpc_id = "${aws_vpc.main.id}" cidr_block = "10.1.2.0/24" availability_zone = "ap-northeast-1d" tags { Name = "Private Subnet by Terraform" } } // + 追記 // NAT Gateway用のEIPを作っておく resource "aws_eip" "nat" { vpc = true } // + 追記 // NAT Gatewayを作成 resource "aws_nat_gateway" "default" { allocation_id = "${aws_eip.nat.id}" subnet_id = "${aws_subnet.public.id}" tags{ Name = "default Gateway by Terraform" } } resource "aws_route_table_association" "public" { // - 修正 from "a" -> "public" subnet_id = "${aws_subnet.public.id}" route_table_id = "${aws_route_table.public.id}" } // + 追記 // private subnetと追加作成したroute tableを関連付ける resource "aws_route_table_association" "private" { subnet_id = "${aws_subnet.private.id}" route_table_id = "${aws_route_table.private.id}" } resource "aws_security_group" "wsg" { name = "web-sg" description = "web-sg security group created by terraform" vpc_id = "${aws_vpc.main.id}" ingress{ from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress{ from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress{ from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] } tags{ Name = "web-sg" } } resource "aws_security_group" "dsg" { name = "db-sg" description = "db-sg security group created by Terraform" vpc_id = "${aws_vpc.main.id}" ingress{ from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress{ from_port = 3306 to_port = 3306 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress{ from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_key_pair" "auth" { key_name = "${var.key_name}" public_key = "${file(var.public_key_path)}" } resource "aws_instance" "web" { ami = "ami-08847abae18baa040" // Amazon Linux 2 AMI (HVM), SSD Volume Type instance_type = "t2.micro" subnet_id = "${aws_subnet.public.id}" private_ip = "10.1.1.10" associate_public_ip_address = true security_groups = [ "${aws_security_group.wsg.id}" ] key_name = "${aws_key_pair.auth.id}" tags { Name = "Web Server by Terraform" } } resource "aws_instance" "db" { ami = "ami-08847abae18baa040" // Amazon Linux 2 AMI (HVM), SSD Volume Type instance_type = "t2.micro" subnet_id = "${aws_subnet.private.id}" associate_public_ip_address = false private_ip = "10.1.2.10" security_groups = [ "${aws_security_group.dsg.id}" ] key_name = "${aws_key_pair.auth.id}" tags { Name = "DB Server by Terraform" } }
手順6の関連資料
上記設定追加により、NAT Gatewayが作成されました。以上にて、目標としていたインフラ構成の実現が完了しました。
まとめ
- Terraformを用いてAWSでのインフラ構築を行いました。インフラ構成をコード化しておくと再度作成する時や不要になった際の削除などが用意になるため、業務インフラ構成や個人でのインフラ構築などにも有用になるかと思います。
湯河原「おんやど恵」で足湯駆動開発 | 開発合宿レポート
2018年9月1日(土)〜2018年9月3日(月)の2泊3日で、友人と湯河原まで開発合宿してきましたので、雑にどんな感じの場所かを書いていきます。
宿
「おんやど恵」という旅館に2泊3日で泊まりました。
Googleで「開発合宿」と調べるとすぐ検索で出てくる有名な宿です。
部屋は和室のとても快適なお部屋です。
開発場所
専用の会議室を予約することができます。
また、足湯コーナーがあるので定期的に足湯駆動開発しました。
足湯駆動開発してる pic.twitter.com/u5pBkMTp7N
— Khigashiguchi (@Khigashiguchi) September 1, 2018
ご飯
旅館では、朝・夜ごはんを用意していただけます。
夜ご飯は、会席料理をいただきました。
朝ごはんもボリュームが有って美味しいです。
駅周辺グルメ
旅館付近にはあまりないですが、湯河原駅までいくと、「らーめん 麺の蔵」というラーメン屋があります。
旅館付近では、旅館のすぐ横にLAWSONがあったのでそこで昼ごはんなどを買っていました。
ちょっと気分転換したい時
少し歩けばとてもマイナスイオンが感じられる公園が出てきます。
歩いて10分くらいの距離にありました。
開発合宿中にマイナスイオンを感じに来た pic.twitter.com/99mA4mAOLe
— Khigashiguchi (@Khigashiguchi) September 2, 2018
また、独歩の湯という足湯施設に行くことも出来ます。
18:00閉店ですが、気分転換に遊び行ってもいいですね。
まとめ
9月ということもあり、人が多すぎることもなくとても快適に過ごせました。
ECS(Fargate)で動かすコンテナにSSMからクレデンシャル情報を渡す
発表時の資料
本記事に関する発表資料はこちらです。
※ プラットフォームバージョン1.3以上の場合
この記事はプラットフォーム1.3より前を使用する前提で記載しています。 1.3以上をお使いになる場合は、こちらの記事をご覧ください。
https://devblog.thebase.in/entry/2019/01/16/110000
目次
- クレデンシャル情報の扱い方
- 概要構成
- 具体的な手順
- 参考資料
クレデンシャル情報の扱い方
クレデンシャル情報の扱い方を考えるに当たり、Beyond the Twelve-Factor Appというクラウドネイティブアプリケーションの設計パターンについて説明した資料がよく参照されています。
上記の資料内の「05. CONFIGURATION, CREDENTIALS, AND CODE」という章で次のように記載されています。
「今すぐにでもコードをオープンソース化できるかどうか」というのがわかりやすい指標ですね。こちらで推奨されている環境変数への格納を今回進めていきます。
寄り道:環境変数への格納について検討
環境変数への格納について、「セキュリティ観点」・「管理観点」の両者で検討してみます。
セキュリティ観点
セキュリティ観点的には、必要以上に参照可能な状態を避ける必要があります。例えば、「/etc/environmentにクレデンシャル情報を書いてそれを使う」といったことをした場合は、アプリケーションの動作に必要なプロセス以外からもクレデンシャル情報を読むことができます。
今回のケースでは、コンテナプロセス上でアプリケーションを動かすケースのため、コンテナプロセスに対して環境変数を注入することになります。それであれば、必要なプロセス以外からのクレデンシャル情報へのアクセスはある程度避けることができそうです。
管理観点
YAMLやTOMLファイルで設定する際、次のように要素に応じて階層的に管理したいというニーズがあると思います。
[database1] user = "sample_user1" password = "sample_password1" host = "database1" port = 3306 name = "sample1" [database2] user = "sample_user2" password = "sample_password2" host = "database2" port = 3306 name = "sample2"
環境変数での注入の場合、単に環境変数に直接書くのであれば階層をもたせることは少し難しそうですが、AWSであればAWS Systems Manager パラメータストアを用いることで階層管理を実現できそうです。
AWS Systems Manager パラメータストア は、設定データ管理と機密管理のための安全な階層型ストレージを提供します。パスワード、データベース文字列、ライセンスコードなどのデータをパラメータ値として保存することができます。
概要構成
今回の概要構成は下図のものです。
大まかな構成要素は以下になります。
略語 | 正式名称 | 概要 |
---|---|---|
ECS | Amazon Elastic Container Service | Docker コンテナをサポートする拡張性とパフォーマンスに優れたコンテナオーケストレーションサービス |
ECR | Amazon Elastic Container Registry | 完全マネージド型の Docker コンテナレジストリ |
Fargate | AWS Fargate | ECS/EKS内のテクノロジー、サーバーやクラスターを管理することなくコンテナを実行できるようになる |
IAM | AWS Identity and Access Management | AWS リソースへのアクセスを安全に制御するためのウェブサービス |
Parameter Store | AWS Systems Manager Paramter Store | 設定データ管理と機密管理のための安全な階層型ストレージ |
KMS | AWS Key Management Service | データの暗号化に使用するキーの容易な作成および管理 |
ALB | Application Load Balancer | L4/L7で機能するロードバランサー |
具体的な手順
今回のサンプルは下記のgithubレポジトリに公開しています。
※ 今回のサンプルをそのまま試しに実行する場合は、なにかしらのMySQLサーバを準備する必要があります。サンプルを実行したい場合は、RDSの無料枠などを利用して接続可能なデータベースを用意してください。
手順一覧
- KMSで暗号化キーの作成
- Parameterの登録
- IAMロールの作成
- IAMポリシーの作成
- IAMロールにIAMポリシーをアタッチ
- Containerイメージの作成・プッシュ
- ECSのタスク定義
- タスク実行
KMSで暗号化キーの作成
まずは、クレデンシャル情報を暗号化するためのキーをKMSで作成します。作成に当たり、AWS CLIのcreate-keyコマンドを実行します。
$ aws kms create-key --description go-ecs-sample --region ap-northeast-1 KEYMETADATA < AWSAccountId > arn:aws:kms:ap-northeast-1:< AWSAccountId >:key/< KeyId > go-ecs-sample True < KeyId > CUSTOMER Enabled ENCRYPT_DECRYPT AWS_KMS
上記コマンドを実行すると、CMK(customer master key)というデータの暗号化に用いるキーが作られます。後続で実行する作業にて、< AWSAccountId >
・< KeyId >
を使っていきます。
Parameterの登録
次に、Parameter Storeにクレデンシャル情報を登録していきます。今回は、RDSへの接続情報を登録していきます。
$ aws ssm put-parameter --name /goecssample/database/sample/master/user --type "String" --value "user" --description "データベースのmasterユーザー名" --region ap-northeast-1 $ aws ssm put-parameter --name /goecssample/database/sample/master/password --type "SecureString" --value "password" --key-id "< KeyId >" --description "データベースのmasterユーザーパスワード" --region ap-northeast-1 $ aws ssm put-parameter --name /goecssample/database/sample/master/host --type "String" --value "host" --description "データベースのmasterホスト名" --region ap-northeast-1 $ aws ssm put-parameter --name /goecssample/database/sample/master/name --type "String" --value "sample" --description "データベースのmasterデータベース名" --region ap-northeast-1 $ aws ssm put-parameter --name /goecssample/database/sample/master/port --type "String" --value "3306" --description "データベースの接続ポート" --region ap-northeast-1
上記のコマンドを実行すると次のようなレスポンスが返ってきます。
1 1 1 1 1
このレスポンス結果は、各パラメータのバージョンを表しています。これは--overwrite
オプションを付けて上書きしたりすると、2・3と増えていきます。
また、環境変数を扱う上での階層管理ですが、Parameter Storeを使う場合は、/stage1/stage2/stage3
という形で階層を区切ることが出来るので、今回活用しています。
AWS System Manager: パラメータを階層に編成
IAMロールの作成
次に、ECSタスクのタスクロールとなるIAMロールを作成します。タスク用のIAMロールを作成し、タスク定義に当該IAMロールを定義することによって、IAMロールの認証情報にアクセスすることができるようになります。今回、Parameter Store・KMSにアクセス可能なIAMロールを作成して、ECSのタスク定義に定義することを目指します。
まず、IAMロールの定義をjsonで作成します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
作成したファイルをもとにIAMロールを作成します。
$ aws iam create-role --role-name go-ecs-sample --assume-role-policy-document file://ecs-tasks-trust-policy.json ROLE arn:aws:iam::< AWSAccountId >:role/go-ecs-sample 2018-08-24T02:10:14Z / HFJKHSYGHOSUHG... go-ecs-sample
IAMポリシーの作成
次に、IAMポリシーを作成します。まずは次のようなjsonファイルを作成します。ここでは、「Parameter Storeのgoecssample/
以下の階層のパラメータを取得する」・「今回作成したKMSキーで暗号化したものを複合する」ことを許可しています。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ssm:DescribeParameters" ], "Resource": "*" }, { "Sid": "Stmt1482841904000", "Effect": "Allow", "Action": [ "ssm:GetParameters" ], "Resource": [ "arn:aws:ssm:ap-northeast-1:< AWSAccountId >:parameter/goecssample/*" ] }, { "Sid": "Stmt1482841948000", "Effect": "Allow", "Action": [ "kms:Decrypt" ], "Resource": [ "arn:aws:kms:ap-northeast-1:< AWSAccountId >:key/< KeyId >" ] } ] }
作成したファイルをもとにIAMロールを作成します。
$ aws iam create-policy --policy-name go-ecs-sample --policy-document file://go-ecs-secret-access.json POLICY arn:aws:iam::< AWSAccountId >:policy/go-ecs-sample 0 2018-08-24T02:13:46Z v1 True / 0 HFJKHSYGHOSUHG... go-ecs-sample 2018-08-24T02:13:46Z
IAMロールにIAMポリシーをアタッチ
作成したIAMロールにIAMポリシーをアタッチします。
$ aws iam attach-role-policy --role-name go-ecs-sample --policy-arn "arn:aws:iam::< AWSAccountId >policy/go-ecs-sample"
Dockerイメージの作成・プッシュ
ここまででParameter Storeから必要な情報を取る準備はできたので、次にECSタスクで実行するためのDockerイメージを作成していきます。今回のサンプルは、MySQLサーバに接続可能であればサーバが立ち上がるAPIです。
https://github.com/Khigashiguchi/go-ecs-example/blob/master/main.go
func main() { var err error // Get configuration conf, err := config.NewConfig() if err != nil { fmt.Fprintf(os.Stderr, "failed to get configuration: %s", err) panic(err.Error()) } // Get database Handle db, err := NewDB(conf.DB) if err != nil { fmt.Fprintf(os.Stderr, "failed to get connection with database: %s", err) panic(err.Error()) } // Router r := mux.NewRouter() h := Handler{DB: db} r.Methods("GET").Path("/posts").HandlerFunc(h.GetPostsHandler) r.Methods("GET").Path("/.healthcheck").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) // Serve HTTP service fmt.Fprint(os.Stdout, ">> Start to listen http server post :80\n") if err = http.ListenAndServe(":80", r); err != nil { fmt.Fprintf(os.Stderr, "failed to start http server: %s", err) panic(err.Error()) } }
上記の簡単なAPIを動かすために次のようなDockerfileを書きます。ENTRYPOINTからaws-cliを叩くためにawscliをインストールしています。
https://github.com/Khigashiguchi/go-ecs-example/blob/master/Dockerfile
FROM golang:1.10-alpine3.7 WORKDIR /go/src/github.com/Khigashiguchi/go-ecs-example/ COPY . /go/src/github.com/Khigashiguchi/go-ecs-example/ RUN apk add --no-cache ca-certificates \ dpkg \ gcc \ git \ musl-dev \ openssh \ bash \ curl \ python # Install the AWS CLI # https://aws.amazon.com/jp/blogs/news/managing-secrets-for-amazon-ecs-applications-using-parameter-store-and-iam-roles-for-tasks/ RUN curl -O https://bootstrap.pypa.io/get-pip.py RUN python get-pip.py RUN pip install awscli RUN go get -u github.com/golang/dep/cmd/dep RUN dep ensure RUN go build -v -o server EXPOSE 80 ENTRYPOINT ["./docker-entrypoint.sh"] CMD ["./server"]
ENTRYPOINTで指定しているdocker-entrypoint.sh
は次のようなものです。ローカル環境では叩きに行かずAWS ECS上でのデプロイ時のみParameter Storeにアクセスしたいため、ECS上でのデプロイ時のみ事前付与する環境変数、PARAMETER_STORE_PREFIX
を用意しています。
#!/usr/bin/env bash set -e PARAMETER_STORE_PREFIX=${PARAMETER_STORE_PREFIX:-} if [ -n "$PARAMETER_STORE_PREFIX" ]; then export DB_USER=$(aws ssm get-parameters --name /${PARAMETER_STORE_PREFIX}/database/sample/master/user --query "Parameters[0].Value" --region ap-northeast-1 --output text) export DB_PASSWORD=$(aws ssm get-parameters --name /${PARAMETER_STORE_PREFIX}/database/sample/master/password --with-decryption --query "Parameters[0].Value" --region ap-northeast-1 --output text) export DB_HOST=$(aws ssm get-parameters --name /${PARAMETER_STORE_PREFIX}/database/sample/master/host --query "Parameters[0].Value" --region ap-northeast-1 --output text) export DB_NAME=$(aws ssm get-parameters --name /${PARAMETER_STORE_PREFIX}/database/sample/master/name --query "Parameters[0].Value" --region ap-northeast-1 --output text) export DB_PORT=$(aws ssm get-parameters --name /${PARAMETER_STORE_PREFIX}/database/sample/master/port --query "Parameters[0].Value" --region ap-northeast-1 --output text) fi exec "$@"
これを、ECRのレポジトリにイメージプッシュします。レポジトリの作成手順は下記をご参照ください。
$ $(aws ecr get-login --no-include-email --region ap-northeast-1) $ docker build -t go-ecs-sample . $ docker tag go-ecs-sample:latest < AWSAccountId >.dkr.ecr.ap-northeast-1.amazonaws.com/go-ecs-sample:latest $ docker push < AWSAccountId >.dkr.ecr.ap-northeast-1.amazonaws.com/go-ecs-sample:latest
ECSのタスク定義
ECSのタスク定義を作成します。タスク定義の詳細の説明については、下記の公式ドキュメントをご参照ください。
要点としては、下記2点です。
- タスクロールに今回作成したIAMロール
`go-ecs-example
を指定してください。
- タスクロールに今回作成したIAMロール
なお、タスク実行ロールはデフォルトのecsTaskExecutionRole
でOKです。
そして、コンテナ定義で設定する環境変数に次の内容をセットしてください。
環境変数 | 値 |
---|---|
PARAMETER_STORE_PREFIX | goecssample |
タスク実行
タスクを登録するとタスクが実行されます。
前回のステータス
がRunning
になっていれば、OKです。
参考資料
Goのテストに関する"もやもや"がgolang.tokyo#17でとても解消された | golang.tokyo#17参加レポート
2018年8月21日(火)に、freee株式会社さんのイベント会場で、golang.tokyo#17が開催されました。今回のテーマは、「今あらためてテストの話」ということだったのですが、普段テストを書いていてもやもやしていた部分が解消されるとてもタメになる内容ばかりでした。 本参加レポートでは、会場の雰囲気から内容までをレポートいたします。
golang.tokyoとは
プログラミング言語のGoの導入企業のメンバーが集まり、Goの普及を推進するコミュニティです トークイベント、ハンズオン、etcのイベントを開催していく予定です!
すでに、togetterをまとめていただいているので、当日の様子はこちらで見ることができます。 togetter.com
会場の雰囲気
今回の会場は、freee株式会社さんのイベントスペースで行われました。
発表内容
Tour of testing by budougumi0617
@budougumi0617さんによる発表です。
前段に、freeeさんの会社について紹介されていて、Railsがメインで動いているところに対して、マイクロサービス化を進めていて、実際4機能ほどGoで書かれたマイクロサービスが動いているようです。
今回の発表では、主に「go test
コマンド・testing.T
についてGo1.10までの復習」・「テストのベタープラクティス」についてはなされていました。
体系的にまとめてくださっているので、何回でも見返したい神資料でした。
個人的に「なるほど」となったのは、Table Driven Testingでループを回す際、tests
で回してtt
で受け取るという点でした。
標準パッケージのテストの書き方が基本的にこうなっているとのことで、cases
・c
でやっていた私は返ってすぐ書き換えました。
非公開な機能を使ったテスト by tenntenn
資料URL:https://docs.google.com/presentation/d/1LLEHlg2IEecaoXnUpKOkMSE1hk9lAWVNJz5VMwmbqm8/edit
Gopher道場で講師をされている、@tenntennさんの発表でした。
「テストはテスト対象と別のパッケージにする」事に関する標準パッケージの状況・利点・export_test.go
を利用したテスト手法についてご紹介されていました。
発表の際、デモをおこなわれていたのですが、デモで使用されていたコードは以下の記事にて公開されているようです。
今回紹介されている手法は、標準パッケージでも利用されているようなので、標準パッケージのソースを読むのが一番参考になるとのことでした。
また、質疑応答にて、「非公開な機能が多いと大変な書き方では」という意見が上がったのですが、その場合は、そもそも「非公開が多いのはよくない設計なのでは」という見直した上で、internalパッケージにするといった検討が必要になることもあるとのことでした。
LT1 外部環境への依存をテストする by duck8823
www.slideshare.net
@duck8823さんより、CIサーバーを作った際に行った、外部環境への依存をテストする方法についての発表でした。
今回発表されているCIサーバーはgithub上に公開していただいています。
LT2 Developer-friendly なテストを考える by izumin5210
@izumin5210さんよる発表です。
「Developer-friendly なテスト」を「落ちたときに直しやすいテスト」と定義した上で、どのようにテストを書いていくかという点について、Table Driven Testingやgoogle/go-cmpなどを使うやり方をご紹介されていました。
LT3 止めたいのに止められないテストの話 by knsh14
資料URL: https://talks.godoc.org/github.com/knsh14/go-slides/lt-go-test-failfast.slide#1
@knsh14さんによる発表です。
go test -failtest
を実際に試した際に意図しない挙動になったことをきっかけに、内部挙動を調査・実験していった内容でした。実際に、go test -failtest
はちゃんと動くのかという結論については資料でぜひご確認ください。
まとめ
「今あらためてテストの話」というテーマで開催された、golang.tokyo#17でしたが、普段テストを書いていて「これであってるのか???」ともやもやしていた点を話していただいてとても素晴らしい会でした。
golang.tokyoの運営スタッフの皆さん・会場を提供していただいたfreeeの皆さんありがとうございました!