From 4ef54ce23227778d69d0b3003dfa6c21290f788b Mon Sep 17 00:00:00 2001 From: showwin Date: Mon, 7 Oct 2024 00:16:51 +0900 Subject: [PATCH] Add Packer to build AMI --- README.md | 2 +- admin/.bashrc | 14 +++ {docker/app/base => admin}/nginx.conf | 0 ami/.gitignore | 3 + ami/README.md | 25 ++++ ami/_secret_vars.hcl | 11 ++ ami/ami.pkr.hcl | 168 ++++++++++++++++++++++++++ ami/scripts/base.sh | 41 +++++++ ami/scripts/benchmarker.sh | 9 ++ ami/scripts/init.sh | 23 ++++ ami/scripts/webapp.sh | 41 +++++++ ami/shared_vars.hcl | 2 + doc/manual.md | 32 +++-- docker/app/base/.bashrc | 14 --- docker/app/base/Dockerfile | 4 +- terraform/main.tf | 2 +- 16 files changed, 356 insertions(+), 35 deletions(-) create mode 100644 admin/.bashrc rename {docker/app/base => admin}/nginx.conf (100%) create mode 100644 ami/.gitignore create mode 100644 ami/README.md create mode 100644 ami/_secret_vars.hcl create mode 100644 ami/ami.pkr.hcl create mode 100644 ami/scripts/base.sh create mode 100644 ami/scripts/benchmarker.sh create mode 100644 ami/scripts/init.sh create mode 100644 ami/scripts/webapp.sh create mode 100644 ami/shared_vars.hcl delete mode 100644 docker/app/base/.bashrc diff --git a/README.md b/README.md index 6271009..7601445 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ISUCONは3人チームで取り組むことを基準に課題が作られてい ## 問題詳細 * マニュアル: [ISHOCON1マニュアル](https://github.com/showwin/ISHOCON1/blob/master/doc/manual.md) -* AMI: `ami-0d00b2a9a38084503` +* AMI: `ami-06cda439fc5c0da1b` * インスタンスタイプ: `c5.xlarge` * 参考実装言語: Ruby, Go, Python * メンテナンス外: Node.js(TypeScript), Crystal(by [@Goryudyuma](https://github.com/Goryudyuma)), Scala(by [@Goryudyuma](https://github.com/Goryudyuma)) diff --git a/admin/.bashrc b/admin/.bashrc new file mode 100644 index 0000000..e287e06 --- /dev/null +++ b/admin/.bashrc @@ -0,0 +1,14 @@ +export PATH=$PATH:/usr/local/go/bin +export GOROOT=/usr/local/go +export GOPATH=$HOME/.local/go +export PATH="$HOME/.rbenv/bin:$PATH" +command -v rbenv >/dev/null && eval "$(rbenv init -)" +export PYENV_ROOT="$HOME/.pyenv" +export PATH="$PYENV_ROOT/bin:$PATH" +command -v pyenv >/dev/null && eval "$(pyenv init -)" + +export ISHOCON1_DB_HOST="127.0.0.1" +export ISHOCON1_DB_PORT=3306 +export ISHOCON1_DB_USER="ishocon" +export ISHOCON1_DB_PASSWORD="ishocon" +export ISHOCON1_DB_NAME="ishocon1" diff --git a/docker/app/base/nginx.conf b/admin/nginx.conf similarity index 100% rename from docker/app/base/nginx.conf rename to admin/nginx.conf diff --git a/ami/.gitignore b/ami/.gitignore new file mode 100644 index 0000000..893a872 --- /dev/null +++ b/ami/.gitignore @@ -0,0 +1,3 @@ +secret_vars.hcl +webapp.tar.gz +benchmarker.tar.gz diff --git a/ami/README.md b/ami/README.md new file mode 100644 index 0000000..e5755d8 --- /dev/null +++ b/ami/README.md @@ -0,0 +1,25 @@ +[Packer](https://www.packer.io/)を使ってAMIの作成をする。 + +# How to use + +## Prerequisites +* [Session Manager plugin for the AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) + +## Steps + +``` +$ cd ishocon1/ami +$ cp _secret_vars.hcl secret_vars.hcl +# Update your values at `secret_vars.hcl` + +$ cd ../ && tar -zcvf ami/webapp.tar.gz ./webapp && cd ami +$ cd ../admin/ && tar -zcvf ../ami/benchmarker.tar.gz ./benchmarker && cd ../ami + +$ packer init . +$ packer validate -var-file=shared_vars.hcl -var-file=secret_vars.hcl . + +# This will take around 15 minutes to run +$ packer build -var-file=shared_vars.hcl -var-file=secret_vars.hcl ami.pkr.hcl + +# You can see the AMI ID from the output +``` diff --git a/ami/_secret_vars.hcl b/ami/_secret_vars.hcl new file mode 100644 index 0000000..03c3bd6 --- /dev/null +++ b/ami/_secret_vars.hcl @@ -0,0 +1,11 @@ +# Subnet where we build AMI +subnet_id = "subnet-xxxxxxxx" + +# Security Group ID of the builder EC2 instance +security_group_id = "sg-xxxxxxxx" + +# SSH Key Pair name of the builder EC2 instance +ssh_keypair_name = "keypair_name" + +# SSH Key file path to connect to the builder EC2 instance +ssh_private_key_file = "/path/to/your/private_key.pem" diff --git a/ami/ami.pkr.hcl b/ami/ami.pkr.hcl new file mode 100644 index 0000000..eceae3f --- /dev/null +++ b/ami/ami.pkr.hcl @@ -0,0 +1,168 @@ +packer { + required_plugins { + amazon = { + version = ">= 1.2.7" + source = "github.com/hashicorp/amazon" + } + } +} + +variable "source_ami" { + description = "The source AMI to use for the builder" + type = string +} + +variable "region" { + description = "The region to build the image in" + type = string + default = "ap-northeast-1" +} + +variable "security_group_id" { + description = "The ID of the security group Packer will associate with the builder to enable access" + type = string + default = null +} + +variable "ssh_keypair_name" { + description = "The name of the SSH keypair to associate with the builder instance" + type = string + default = null +} + +variable "ssh_private_key_file" { + description = "The path to the private key file to use for SSH connection to the builder instance" + type = string + default = null +} + +variable "subnet_id" { + description = "If using VPC, the ID of the subnet, such as subnet-12345def, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC" + type = string + default = null +} + +variable "instance_type" { + description = "The instance type Packer will use for the builder" + type = string + default = "c7i.xlarge" +} + +variable "iam_instance_profile" { + description = "The IAM instance profile to associate with the builder" + type = string + default = null +} + +source "amazon-ebs" "ishocon1" { + ami_name = "ishocon1-${formatdate("YYYYMMDD-hhmm", timestamp())}" + instance_type = var.instance_type + region = var.region + security_group_id = var.security_group_id + subnet_id = var.subnet_id + source_ami = var.source_ami + + associate_public_ip_address = true + ssh_username = "ubuntu" + ssh_interface = "public_ip" + communicator = "ssh" + ssh_keypair_name = var.ssh_keypair_name + ssh_private_key_file = var.ssh_private_key_file + + run_tags = { + Name = "ISHOCON1 AMI builder" + } + + tags = { + Name = "ISHOCON1" + base_ami = "{{ .SourceAMI }}" + built_by = "https://github.com/showwin/ISHOCON1/tree/master/ami" + } + + launch_block_device_mappings { + device_name = "/dev/sda1" + volume_size = 8 + volume_type = "gp3" + delete_on_termination = true + } +} + +build { + name = "ishocon1" + sources = [ + "source.amazon-ebs.ishocon1" + ] + + # Init + provisioner "file" { + source = "./scripts/init.sh" + destination = "/tmp/init.sh" + } + + provisioner "shell" { + inline = [ + "sudo bash /tmp/init.sh" + ] + } + + # Base + provisioner "file" { + source = "./scripts/base.sh" + destination = "/tmp/base.sh" + } + + provisioner "shell" { + inline = [ + "sudo -u ishocon -H sh -c 'bash /tmp/base.sh'" + ] + } + + # WebApp + provisioner "file" { + source = "../admin/.bashrc" + destination = "/tmp/.bashrc" + } + + provisioner "file" { + source = "../admin/nginx.conf" + destination = "/tmp/nginx.conf" + } + + provisioner "file" { + source = "../admin/ishocon1.dump.tar.gz" + destination = "/tmp/ishocon1.dump.tar.gz" + } + + provisioner "file" { + source = "./webapp.tar.gz" + destination = "/tmp/webapp.tar.gz" + } + + provisioner "file" { + source = "./scripts/webapp.sh" + destination = "/tmp/webapp.sh" + } + + provisioner "shell" { + inline = [ + "sudo -u ishocon -H sh -c 'bash /tmp/webapp.sh'" + ] + } + + # Benchmarker + provisioner "file" { + source = "./benchmarker.tar.gz" + destination = "/tmp/benchmarker.tar.gz" + } + + provisioner "file" { + source = "./scripts/benchmarker.sh" + destination = "/tmp/benchmarker.sh" + } + + provisioner "shell" { + inline = [ + "sudo -u ishocon -H sh -c 'bash /tmp/benchmarker.sh'" + ] + } +} diff --git a/ami/scripts/base.sh b/ami/scripts/base.sh new file mode 100644 index 0000000..b8e424b --- /dev/null +++ b/ami/scripts/base.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -ex + +cd /home/ishocon + +# Install MySQL +sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password password ishocon1' +sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password ishocon1' +sudo debconf-set-selections <<< 'mysql-service mysql-server/mysql-apt-config string 4' +sudo apt-get install -y mysql-server + +# Install Ruby +export RUBY_VERSION=2.7.1 +sudo apt-get install -y ruby-dev libmysqlclient-dev libffi-dev libyaml-dev bzip2 +sudo apt-get clean +git clone https://github.com/sstephenson/rbenv.git ~/.rbenv +export PATH="$HOME/.rbenv/bin:$PATH" +eval "$(rbenv init -)" +git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build +rbenv install $RUBY_VERSION && rbenv rehash && rbenv global $RUBY_VERSION + +# Install Python +export PYTHON_VERSION=3.8.5 +sudo apt-get install -y zlib1g-dev libbz2-dev libffi-dev libsqlite3-dev liblzma-dev libmariadb-dev pkgconf +sudo apt-get clean +git clone https://github.com/pyenv/pyenv.git ~/.pyenv +export PYENV_ROOT="$HOME/.pyenv" +export PATH="$PYENV_ROOT/bin:$PATH" +eval "$(pyenv init -)" +pyenv install $PYTHON_VERSION && pyenv global $PYTHON_VERSION + +# Install Go +export GO_VERSION=1.23.1 +export TARGETARCH=amd64 +sudo wget -q https://dl.google.com/go/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz +sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-${TARGETARCH}.tar.gz +sudo rm go${GO_VERSION}.linux-${TARGETARCH}.tar.gz +export PATH=$PATH:/usr/local/go/bin +export GOROOT=/usr/local/go +export GOPATH=/home/ishocon/.local/go +export PATH=$PATH:$GOROOT/bin diff --git a/ami/scripts/benchmarker.sh b/ami/scripts/benchmarker.sh new file mode 100644 index 0000000..65b3391 --- /dev/null +++ b/ami/scripts/benchmarker.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -ex +sudo su - ishocon +. /home/ishocon/.bashrc + +cd /tmp/ +tar -zxvf /tmp/benchmarker.tar.gz +cd /tmp/benchmarker +go build -x -o /home/ishocon/benchmark *.go diff --git a/ami/scripts/init.sh b/ami/scripts/init.sh new file mode 100644 index 0000000..13d71af --- /dev/null +++ b/ami/scripts/init.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -ex + +export LANG=en_US.UTF-8 +export LC_ALL=C.UTF-8 +export TZ=Asia/Tokyo +sudo ln -snf /usr/share/zoneinfo/$TZ /etc/localtime +echo $TZ | sudo tee /etc/timezone + +sudo apt-get update +sudo apt-get install -y sudo wget less vim tzdata nginx \ + curl git gcc make libssl-dev libreadline-dev +sudo apt-get clean + +# ishocon ユーザ作成 +sudo groupadd -g 1001 ishocon +sudo useradd -g ishocon -G sudo -m -s /bin/bash ishocon +# sudo echo 'ishocon:ishocon' | chpasswd +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 diff --git a/ami/scripts/webapp.sh b/ami/scripts/webapp.sh new file mode 100644 index 0000000..10bcd65 --- /dev/null +++ b/ami/scripts/webapp.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -ex + +# Move files +cp /tmp/.bashrc /home/ishocon/.bashrc +sudo cp /tmp/nginx.conf /etc/nginx/nginx.conf +cp /tmp/ishocon1.dump.tar.gz /home/ishocon/data/ishocon1.dump.tar.gz +cd /home/ishocon +tar -zxvf /tmp/webapp.tar.gz +chown -R ishocon:ishocon /home/ishocon/webapp + +# Load .bashrc +. /home/ishocon/.bashrc + +# # Install Ruby libraries +cd /home/ishocon/webapp/ruby +gem install bundler -v "1.16.1" +bundle install + +# # Install Python libraries +cd /home/ishocon/webapp/python +sudo apt-get install -y libmysqlclient-dev +pip install -r requirements.txt + +# Install Go libraries +ls /home/ishocon +ls /home/ishocon/webapp +cd /home/ishocon/webapp/go +go build -o webapp *.go + +# Load data into MySQL +sudo chown -R mysql:mysql /var/lib/mysql +sudo service mysql start +sudo mysql -u root -pishocon1 -e 'CREATE DATABASE IF NOT EXISTS ishocon1;' +sudo mysql -u root -pishocon1 -e "CREATE USER IF NOT EXISTS ishocon IDENTIFIED BY 'ishocon';" +sudo mysql -u root -pishocon1 -e 'GRANT ALL ON *.* TO ishocon;' +tar -zxvf ~/data/ishocon1.dump.tar.gz -C ~/data && sudo mysql -u root -pishocon1 ishocon1 < ~/data/ishocon1.dump + +# Nginx +sudo nginx -t +sudo service nginx start diff --git a/ami/shared_vars.hcl b/ami/shared_vars.hcl new file mode 100644 index 0000000..0299216 --- /dev/null +++ b/ami/shared_vars.hcl @@ -0,0 +1,2 @@ +# Ubuntu Server 24.04 LTS (HVM) +source_ami = "ami-0b20f552f63953f0e" diff --git a/doc/manual.md b/doc/manual.md index 2a22541..c71698d 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -4,12 +4,12 @@ ## インスタンスの作成 AWSのイメージのみ作成しました。 -* AMI: ami-0d00b2a9a38084503 -* Instance Type: c5.xlarge +* AMI: ami-06cda439fc5c0da1b +* Instance Type: c7i.xlarge * EBS-optimized: Yes -* Root Volume: 8GB, General Purpose SSD (gp2) +* Root Volume: 8GB, General Purpose SSD (gp3) -参考画像 +参考画像 * Security Groupの設定で `TCP 22 (SSH)` と `TCP 80 (HTTP)` を `Inbound 0.0.0.0/0` からアクセスできるようにしてください。 ![](https://raw.githubusercontent.com/showwin/ISHOCON1/master/doc/images/instance3.png) @@ -27,9 +27,9 @@ $ sudo su - ishocon ``` $ ls - benchmark #ベンチマーカー - db_init.sh #DBの初期化スクリプト(後で説明) - webapp #最適化するアプリケーション +benchmark # ベンチマーカー +data # DBの初期化データ (後で説明) +webapp # 最適化するアプリケーション ``` ### Web サーバーを立ち上げる @@ -48,19 +48,18 @@ $ gunicorn -c gunicorn_config.py app:app #### Go の場合 ``` $ cd ~/webapp/go -$ go get -t -d -v ./... $ go build -o webapp *.go $ ./webapp ``` -#### Scala の場合 +#### Scala の場合 (メンテナンス外) ``` $ cd ~/webapp/scala/ishocon1 $ sbt > ~;jetty:stop;jetty:start ``` -#### Node.js (TypeScript) の場合 +#### Node.js (TypeScript) の場合 (メンテナンス外) ``` $ cd ~/webapp/nodejs/ $ npm install @@ -88,16 +87,15 @@ index eb5e600..600ba5e 100644 ``` -#### Crystal の場合 +#### Crystal の場合 (メンテナンス外) ``` -# メンテナンスされておらず動きません $ cd ~/webapp/crystal $ shards install $ crystal build app.cr $ ./app ``` -これでブラウザからアプリケーションが見れるようになるので、IPアドレスにアクセスしてみましょう。 +これでブラウザからアプリケーションが見れるようになるので、 `http://` にアクセスしてみましょう。 **トップページ** ![トップページ](https://raw.githubusercontent.com/showwin/ISHOCON1/master/doc/images/top.png) @@ -118,7 +116,7 @@ $ ./benchmark --workload 3 ``` * ベンチマーカーは並列実行可能で、負荷量を指定することができます。 * 何も指定しない場合は3で実行されます。 -* 初期実装でスコアは100点前後になると思います。(workloadが3の場合) +* 初期実装でスコアは400点前後になると思います。(workloadが3の場合) * 並列度が高い場合は1分以上経っても終了しない場合がありますが、スコアには影響ありません。 ## MySQL @@ -126,13 +124,13 @@ $ ./benchmark --workload 3 * ユーザ名: ishocon, パスワード: ishocon * ユーザ名: root, パスワード: ishocon1 -別のバージョンのMySQLに変更することも可能です。 +別のバージョンのMySQLに変更することも可能です。 その場合、初期データの挿入は ``` $ cd -$ ./db_init.sh +$ sudo mysql -u root -pishocon1 ishocon1 < ~/data/ishocon1.dump ``` -で行うことができます。 +で行うことができます。 既存のMySQLを使う限りはこれを実行する必要はありません。 ## スコアについて diff --git a/docker/app/base/.bashrc b/docker/app/base/.bashrc deleted file mode 100644 index 2e0aa77..0000000 --- a/docker/app/base/.bashrc +++ /dev/null @@ -1,14 +0,0 @@ -export PATH=$PATH:/usr/local/go/bin -export GOROOT=/usr/local/go -export GOPATH=$HOME/gocode -# export PATH="$HOME/.rbenv/bin:$PATH" -# eval "$(rbenv init -)" -# export PYENV_ROOT="$HOME/.pyenv" -# export PATH="$PYENV_ROOT/bin:$PATH" -# eval "$(pyenv init -)" - -export ISHOCON1_DB_HOST="127.0.0.1" -export ISHOCON1_DB_PORT=3306 -export ISHOCON1_DB_USER="ishocon" -export ISHOCON1_DB_PASSWORD="ishocon" -export ISHOCON1_DB_NAME="ishocon1" diff --git a/docker/app/base/Dockerfile b/docker/app/base/Dockerfile index 24912f3..27d20a8 100644 --- a/docker/app/base/Dockerfile +++ b/docker/app/base/Dockerfile @@ -18,10 +18,10 @@ RUN groupadd -g 1001 ishocon && \ RUN echo 'ishocon ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers # *env 系の設定が入った .bashrc のコピー -COPY docker/app/base/.bashrc /home/ishocon/.bashrc +COPY admin/.bashrc /home/ishocon/.bashrc # Nginx の設定 -COPY docker/app/base/nginx.conf /etc/nginx/nginx.conf +COPY admin/nginx.conf /etc/nginx/nginx.conf # MySQL のインストールと dump, cnf の配置 RUN ["/bin/bash", "-c", "debconf-set-selections <<< 'mysql-server mysql-server/root_password password ishocon1'"] diff --git a/terraform/main.tf b/terraform/main.tf index c1749b4..62d6d24 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -35,7 +35,7 @@ resource "aws_security_group" "player" { resource "aws_spot_instance_request" "player" { for_each = local.players - ami = "ami-0d00b2a9a38084503" + ami = "ami-06cda439fc5c0da1b" instance_type = "c5.xlarge" subnet_id = aws_subnet.player_subnet.id