diff --git a/README.md b/README.md index 7601445..fc23283 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ ISUCONは3人チームで取り組むことを基準に課題が作られてい ## 問題詳細 * マニュアル: [ISHOCON1マニュアル](https://github.com/showwin/ISHOCON1/blob/master/doc/manual.md) -* AMI: `ami-06cda439fc5c0da1b` -* インスタンスタイプ: `c5.xlarge` +* AMI: `ami-0df9d6920cdfce51b` +* インスタンスタイプ: `c7i.xlarge` * 参考実装言語: Ruby, Go, Python * メンテナンス外: Node.js(TypeScript), Crystal(by [@Goryudyuma](https://github.com/Goryudyuma)), Scala(by [@Goryudyuma](https://github.com/Goryudyuma)) * 推奨実施時間: 1人で8時間 diff --git a/admin/ami/scripts/init.sh b/admin/ami/scripts/init.sh index 13d71af..4ee0f15 100644 --- a/admin/ami/scripts/init.sh +++ b/admin/ami/scripts/init.sh @@ -20,4 +20,4 @@ echo 'ishocon ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/ishocon sudo mkdir -m 775 /home/ishocon/webapp sudo mkdir -m 777 /home/ishocon/data sudo chown -R ishocon:ishocon /home/ishocon -sudo chmod 777 /home/ishocon +sudo chmod 750 /home/ishocon diff --git a/contest/terraform/.gitignore b/contest/terraform/.gitignore index 3fa8c86..4dd7ebf 100644 --- a/contest/terraform/.gitignore +++ b/contest/terraform/.gitignore @@ -1 +1,2 @@ .terraform +terraform.tfstate* diff --git a/contest/terraform/.terraform-version b/contest/terraform/.terraform-version index a551051..fee0a27 100644 --- a/contest/terraform/.terraform-version +++ b/contest/terraform/.terraform-version @@ -1 +1 @@ -0.15.0 +1.9.7 diff --git a/contest/terraform/.tool-versions b/contest/terraform/.tool-versions new file mode 100644 index 0000000..ae2c800 --- /dev/null +++ b/contest/terraform/.tool-versions @@ -0,0 +1 @@ +terraform 1.9.7 diff --git a/contest/terraform/README.md b/contest/terraform/README.md index eb2a854..231196c 100644 --- a/contest/terraform/README.md +++ b/contest/terraform/README.md @@ -2,9 +2,9 @@ ## 概要 -terraformでISHOCON1の環境が構築できます。 -社内ISUCON等のコンテストの開催準備として使うと便利です。 -開催者(以下admins)と参加者(以下players)のロールに分けて、サーバーを複数台準備できます。 +terraformでISHOCON1の環境が構築できます。 +社内ISUCON等のコンテストの開催準備として使うと便利です。 +開催者(以下admins)と参加者(以下teams)のロールに分けて、サーバーを複数台準備できます。 ## 必要なもの @@ -16,41 +16,42 @@ terraformでISHOCON1の環境が構築できます。 ### 1. AWSアカウントの登録 ```shell -$ aws configure --profile ishocon1 +$ aws configure ``` ### 2. adminsとplayersに、GitHubにて秘密鍵を登録してもらう -playerはコンテストで使用するインスタンスにログインするために、GitHubに登録されている秘密鍵を使用する。 +playerはコンテストで使用するインスタンスにログインするために、GitHubに登録されている秘密鍵を使用する。 GitHubにて秘密鍵を登録後、各自のPCで以下のコマンドを実行し自身のGitHubのアカウントIDが表示されることを確認する。 ```shell $ ssh -T git@github.com ``` -### 3. stateファイルを入れるS3バケットを作成 -tfstateの管理をローカルで行う場合には、作成不要。 +### 3. main/main.tf を編集 -### 4. FIXMEを直す - -- [ ] terraform.tfの中のbucketに、先ほど作成したS3のbucket nameを入れる - - ローカルでtfstateを管理し、S3を使わない場合にはterraform.tfを削除する -- [ ] users.tfにadmins, playersのGitHubアカウントIDを入れる +- [ ] (必須) `admins`と`teams`(チーム名と参加者名)に各々のGitHubアカウントIDを記述 - 詳細は[ドキュメント](https://docs.github.com/ja/github/authenticating-to-github/connecting-to-github-with-ssh)を参照のこと - adminsは全インスタンスに入ることができる - -### 5. terraform apply してリソースの作成 +- [ ] (任意) module/variables.tf を参考に変更したいパラメーターを main/main.tf に定義 + +### 4. terraform apply してリソースの作成 ```shell +$ cd main $ terraform apply ``` -outputに競技で使用するインスタンスのIPアドレスが入っているので、playerの人に渡す。 +outputに競技で使用するインスタンスのIPアドレスが出力されるので、参加者に共有する。 + +``` +$ ssh ishocon@ +``` + +でインスタンスにSSHできる。 ## 注意点 -- spot instanceで立てるようにしてコストの削減を図っています - - コンテスト中に絶対に落ちて欲しくないなどの理由により spot instanceを使わない場合、main.tfの `aws_spot_instance_request` を `aws_instance` に変えてください -- デフォルトでは、ISHOCON1の推奨インスタンスタイプである `c5.xlarge` でインスタンスが起動します +- デフォルトでは、ISHOCON1の推奨インスタンスタイプである `c7i.xlarge` でインスタンスが起動します - このterraformを使用することで発生した問題に対して責任は一切取りません - コードをよく読んだ上で、自己責任でご使用ください diff --git a/contest/terraform/main.tf b/contest/terraform/main.tf deleted file mode 100644 index 62d6d24..0000000 --- a/contest/terraform/main.tf +++ /dev/null @@ -1,73 +0,0 @@ -resource "aws_security_group" "player" { - name = "player_instance" - vpc_id = aws_vpc.vpc.id - - ingress { - description = "HTTP" - from_port = 80 - to_port = 80 - protocol = "tcp" - cidr_blocks = [ - "0.0.0.0/0" - ] - } - - ingress { - description = "SSH" - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = [ - "0.0.0.0/0" - ] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = [ - "0.0.0.0/0" - ] - } -} - -resource "aws_spot_instance_request" "player" { - for_each = local.players - - ami = "ami-06cda439fc5c0da1b" - instance_type = "c5.xlarge" - - subnet_id = aws_subnet.player_subnet.id - - vpc_security_group_ids = [ - aws_security_group.player.id - ] - - user_data = <<-EOF -#!/bin/bash -mkdir /home/ishocon/.ssh -curl https://github.com/${each.value}.keys > /home/ishocon/.ssh/authorized_keys -${join("\n", [for admin in local.admins : "curl https://github.com/${admin}.keys >> /home/ishocon/.ssh/authorized_keys"])} -chown -R ishocon:ishocon /home/ishocon/.ssh -useradd -u 1001 -g 1001 -o -N -d /home/ishocon -s /bin/bash ${each.value} - -EOF - - root_block_device { - volume_size = 30 - } - - wait_for_fulfillment = true - - tags = { - player_name = each.value - } -} - -resource "aws_ec2_tag" "spot_instance" { - for_each = aws_spot_instance_request.player - resource_id = each.value.spot_instance_id - key = "player_name" - value = each.key -} diff --git a/contest/terraform/main/.terraform.lock.hcl b/contest/terraform/main/.terraform.lock.hcl new file mode 100644 index 0000000..773a224 --- /dev/null +++ b/contest/terraform/main/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.69.0" + constraints = ">= 5.46.0, 5.69.0, ~> 5.69" + hashes = [ + "h1:unGIj/eLOrl42LQm7u0fjtjQHp+FHKinpSxR1ZuWsfI=", + "zh:123af8815a80abfd62eab5f9fc3d9226735cfea3627e834a1b48321cd8d391a6", + "zh:1298f312e239768c1846541e89b4fbec7eb21769c4a488c87181909049219fbe", + "zh:4edc950b39f3653beb8cd3e0b86a7dc9b6a77e90e543ed7be72639107bbc48a9", + "zh:5f24c916d6d2ce51e18210628b3b1aca8b85b383982a920b2a6adc259bdbd4e9", + "zh:66f0b2f5869a4dfed7154444c272022c6d9350dc4dfa0fc6d87ccbfc983ec560", + "zh:67e3be60863cf1c51c5be866d8646d433cc31e07514b9121611f812e73f2400d", + "zh:884672345a1d0362644a4d1588085fd4c4f56d3ca61b10c0d25cd1940d828fec", + "zh:8ab0f92da124171c80a2361beb79822fb0f074ffab74e506f58e953a69b283ce", + "zh:908d879139f2246024b5510a38f00f61489eeee6f3f72be10acc5b424c8fc723", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9db6331398d648d9f2f4aa4db1eb9081e9bff584dcfe8f5350e04e6c5d339899", + "zh:a809bbd43bc392e91485b72bd9693874972bc5697b4f24fbcd61b461618ebb6d", + "zh:b9e9464458e7beb9fbf59f8db02f56138f398aaa6173b58a8bfa76aca82106d9", + "zh:cd7f041edaeeb1c4b06152ac8f3ce7b31c39a80a949083255f8fc81bbb11aeac", + "zh:eb71c9b2071ab2caa7aba577902df41c25ded1251c28560f0ac45f5e0f47360e", + ] +} diff --git a/contest/terraform/main/main.tf b/contest/terraform/main/main.tf new file mode 100644 index 0000000..0a40d0f --- /dev/null +++ b/contest/terraform/main/main.tf @@ -0,0 +1,27 @@ +// FIXME +locals { + admins = [ + "admin_github_id", + ] + + teams = { + "team1" = [ + "user1_github_id", + "user2", + "user3", + ], + "team2" = [ + "user4", + "user5", + ], + } +} + +module "main" { + source = "../module" + + admins = local.admins + teams = local.teams + + use_spot_instance = false +} diff --git a/contest/terraform/main/output.tf b/contest/terraform/main/output.tf new file mode 100644 index 0000000..11e1d4c --- /dev/null +++ b/contest/terraform/main/output.tf @@ -0,0 +1,3 @@ +output "ip_addr" { + value = module.main.ip_addr +} diff --git a/contest/terraform/main/provider.tf b/contest/terraform/main/provider.tf new file mode 100644 index 0000000..669918a --- /dev/null +++ b/contest/terraform/main/provider.tf @@ -0,0 +1,21 @@ +terraform { + required_version = "1.9.7" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "5.69.0" + } + } +} + +provider "aws" { + region = "ap-northeast-1" + + default_tags { + tags = { + Service = "ISHOCON1" + ManagedBy = "Terraform" + } + } +} diff --git a/contest/terraform/module/ec2.tf b/contest/terraform/module/ec2.tf new file mode 100644 index 0000000..5607fa8 --- /dev/null +++ b/contest/terraform/module/ec2.tf @@ -0,0 +1,51 @@ +resource "aws_instance" "main" { + for_each = var.use_spot_instance ? {} : { for idx, team in local.team_list : team => team } + + ami = var.ami_id + instance_type = var.instance_type + + subnet_id = module.vpc.public_subnets[0] + associate_public_ip_address = true + + vpc_security_group_ids = [ + aws_security_group.main.id + ] + + user_data = <<-EOF +#!/bin/bash +mkdir /home/ishocon/.ssh +${join("\n", [for player_in_team in var.teams[each.value] : "curl https://github.com/${player_in_team}.keys >> /home/ishocon/.ssh/authorized_keys"])} +${join("\n", [for admin in var.admins : "curl https://github.com/${admin}.keys >> /home/ishocon/.ssh/authorized_keys"])} +chown -R ishocon:ishocon /home/ishocon/.ssh +useradd -u 1001 -g 1001 -o -N -d /home/ishocon -s /bin/bash ${each.value} + +EOF + + root_block_device { + volume_size = 8 + } +} + +resource "aws_ec2_tag" "instance_name" { + for_each = aws_instance.main + + resource_id = each.value.id + key = "Name" + value = "ISHOCON1 - ${each.key}" +} + +resource "aws_ec2_tag" "instance_team_name" { + for_each = aws_instance.main + + resource_id = each.value.id + key = "team_name" + value = each.key +} + +resource "aws_ec2_tag" "instance_players" { + for_each = aws_instance.main + + resource_id = each.value.id + key = "players" + value = join(",", var.teams[each.key]) +} diff --git a/contest/terraform/module/ec2_spot.tf b/contest/terraform/module/ec2_spot.tf new file mode 100644 index 0000000..75c1086 --- /dev/null +++ b/contest/terraform/module/ec2_spot.tf @@ -0,0 +1,53 @@ +resource "aws_spot_instance_request" "main" { + for_each = var.use_spot_instance ? { for idx, team in local.team_list : team => team } : {} + + ami = var.ami_id + instance_type = var.instance_type + + subnet_id = module.vpc.public_subnets[0] + associate_public_ip_address = true + + vpc_security_group_ids = [ + aws_security_group.main.id + ] + + user_data = <<-EOF +#!/bin/bash +mkdir /home/ishocon/.ssh +${join("\n", [for player_in_team in var.teams[each.value] : "curl https://github.com/${player_in_team}.keys >> /home/ishocon/.ssh/authorized_keys"])} +${join("\n", [for admin in var.admins : "curl https://github.com/${admin}.keys >> /home/ishocon/.ssh/authorized_keys"])} +chown -R ishocon:ishocon /home/ishocon/.ssh +useradd -u 1001 -g 1001 -o -N -d /home/ishocon -s /bin/bash ${each.value} + +EOF + + root_block_device { + volume_size = 8 + } + + wait_for_fulfillment = true +} + +resource "aws_ec2_tag" "spot_instance_name" { + for_each = aws_spot_instance_request.main + + resource_id = each.value.spot_instance_id + key = "Name" + value = "ISHOCON1 - ${each.key}" +} + +resource "aws_ec2_tag" "spot_instance_team_name" { + for_each = aws_spot_instance_request.main + + resource_id = each.value.spot_instance_id + key = "team_name" + value = each.key +} + +resource "aws_ec2_tag" "spot_instance_players" { + for_each = aws_spot_instance_request.main + + resource_id = each.value.spot_instance_id + key = "players" + value = join(",", var.teams[each.key]) +} diff --git a/contest/terraform/module/locals.tf b/contest/terraform/module/locals.tf new file mode 100644 index 0000000..e8690a6 --- /dev/null +++ b/contest/terraform/module/locals.tf @@ -0,0 +1,3 @@ +locals { + team_list = keys(var.teams) +} diff --git a/contest/terraform/module/output.tf b/contest/terraform/module/output.tf new file mode 100644 index 0000000..928d2b1 --- /dev/null +++ b/contest/terraform/module/output.tf @@ -0,0 +1,6 @@ +output "ip_addr" { + value = { + for team in local.team_list : + team => try(aws_instance.main[team]["public_ip"] , aws_spot_instance_request.main[team]["public_ip"]) + } +} diff --git a/contest/terraform/provider.tf b/contest/terraform/module/provider.tf similarity index 71% rename from contest/terraform/provider.tf rename to contest/terraform/module/provider.tf index 3076555..3882ec8 100644 --- a/contest/terraform/provider.tf +++ b/contest/terraform/module/provider.tf @@ -1,13 +1,14 @@ terraform { + required_version = "1.9.7" + required_providers { aws = { source = "hashicorp/aws" - version = "3.37.0" + version = "~> 5.69" } } } provider "aws" { - profile = "ishocon1" region = "ap-northeast-1" } diff --git a/contest/terraform/module/sg.tf b/contest/terraform/module/sg.tf new file mode 100644 index 0000000..47c0b54 --- /dev/null +++ b/contest/terraform/module/sg.tf @@ -0,0 +1,33 @@ +resource "aws_security_group" "main" { + name_prefix = "${var.name}_player_instance" + vpc_id = module.vpc.vpc_id + + ingress { + description = "HTTP" + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = [ + "0.0.0.0/0" + ] + } + + ingress { + description = "SSH" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = [ + "0.0.0.0/0" + ] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = [ + "0.0.0.0/0" + ] + } +} diff --git a/contest/terraform/module/variables.tf b/contest/terraform/module/variables.tf new file mode 100644 index 0000000..5eb4a9f --- /dev/null +++ b/contest/terraform/module/variables.tf @@ -0,0 +1,70 @@ +## Required variables + +variable "admins" { + description = "Admins' GitHub IDs" + type = set(string) +} + +variable "teams" { + description = "List of teams and their members' GitHub IDs" + type = map(set(string)) + default = { + "team1" = [ + "user1", + "user2", + "user3", + ], + "team2" = [ + "user4", + "user5", + ], + } +} +# Example: +# teams = { +# "team1" = [ +# "user1", +# "user2", +# "user3", +# ], +# "team2" = [ +# "user4", +# "user5", +# ], +# } + +## Optional variables + +variable "name" { + description = "Name for the main resources. Useful for creating test resources avoiding name conflicts" + type = string + default = "ishocon1" +} + +variable "ami_id" { + description = "AMI ID for the ISHOCON EC2 instances" + type = string + default = "ami-0df9d6920cdfce51b" + +} + +variable "instance_type" { + description = "Instance type for the ISHOCON EC2 instances" + type = string + default = "c7i.xlarge" + +} + +variable "use_spot_instance" { + description = "Use spot EC2 instance to save cost" + type = bool + default = false +} + +variable "vpc_cidr_block" { + description = "VPC CIDR block for the ISHOCON VPC" + type = string + default = "172.16.0.0/16" +} + + diff --git a/contest/terraform/module/vpc.tf b/contest/terraform/module/vpc.tf new file mode 100644 index 0000000..ffc3a64 --- /dev/null +++ b/contest/terraform/module/vpc.tf @@ -0,0 +1,19 @@ +data "aws_availability_zones" "available" { + state = "available" +} + +locals { + # AZによって差が出ないように1つだけを使う + first_az = slice(data.aws_availability_zones.available.names, 0, 1) +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "5.13.0" + + name = var.name + cidr = var.vpc_cidr_block + + azs = local.first_az + public_subnets = [for k, v in local.first_az : cidrsubnet(var.vpc_cidr_block, 8, k + 48)] +} diff --git a/contest/terraform/network.tf b/contest/terraform/network.tf deleted file mode 100644 index 2da3807..0000000 --- a/contest/terraform/network.tf +++ /dev/null @@ -1,29 +0,0 @@ -resource "aws_vpc" "vpc" { - cidr_block = "172.16.0.0/16" -} - -resource "aws_subnet" "player_subnet" { - vpc_id = aws_vpc.vpc.id - cidr_block = "172.16.10.0/24" - availability_zone = "ap-northeast-1a" - - map_public_ip_on_launch = true -} - -resource "aws_main_route_table_association" "main_route_table" { - vpc_id = aws_vpc.vpc.id - route_table_id = aws_route_table.route_table.id -} - -resource "aws_route_table" "route_table" { - vpc_id = aws_vpc.vpc.id - - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.gw.id - } -} - -resource "aws_internet_gateway" "gw" { - vpc_id = aws_vpc.vpc.id -} diff --git a/contest/terraform/output.tf b/contest/terraform/output.tf deleted file mode 100644 index 7a978e0..0000000 --- a/contest/terraform/output.tf +++ /dev/null @@ -1,6 +0,0 @@ -output "ip_addr" { - value = { - for player in local.players : - player => aws_spot_instance_request.player[player]["public_ip"] - } -} diff --git a/contest/terraform/terraform.tf b/contest/terraform/terraform.tf deleted file mode 100644 index 4fe2f83..0000000 --- a/contest/terraform/terraform.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - backend "s3" { - region = "ap-northeast-1" - bucket = "" // FIXME - key = "default.tfstate" - encrypt = true - } -} diff --git a/contest/terraform/users.tf b/contest/terraform/users.tf deleted file mode 100644 index 163796b..0000000 --- a/contest/terraform/users.tf +++ /dev/null @@ -1,11 +0,0 @@ -// FIXME -locals { - admins = toset([ - "FIXME_github_id_for_admin", - ]) - - players = toset([ - "FIXME_github_id_for_player1", - "FIXME_github_id_for_player2", - ]) -} diff --git a/doc/manual.md b/doc/manual.md index c71698d..7020252 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -4,7 +4,7 @@ ## インスタンスの作成 AWSのイメージのみ作成しました。 -* AMI: ami-06cda439fc5c0da1b +* AMI: ami-0df9d6920cdfce51b * Instance Type: c7i.xlarge * EBS-optimized: Yes * Root Volume: 8GB, General Purpose SSD (gp3)