長生村本郷Engineers'Blog

千葉県長生村本郷育ちのエンジニアが書いているブログ

Rails に reCAPTCHA v3 導入して bot 対策

概要

Rails で構築した Webサービスbot 攻撃を定期的に受けた為、問い合わせフォームに reCAPTCHA v3 を導入しました。

何故 v2 でなく、reCAPTCHA v3 ?

v2 は I'm not a robot チェックボックスにチェックを入れた後に画像選択させる仕様があります。

例えば、看板が写ってるのはどれ?と選ばせる問いが出てきた場合、
「どこまでが看板としたらいいの?」と心理的負担も高く、ユーザが離脱する可能性もあります。

f:id:kenzo0107:20190216210737p:plain

v3 だと嬉しいことは何?

v3 *1 は設置したページのユーザ行動をスコア化し bot か判断します。

アクセスが増えるとより精度が高まってくる、という仕様です。

bot ユーザへの負担は全くなく、 bot を遮断できる様になるという、世の中進んでるなぁ感満載です。

gigazine.net

gem ある?

今回 gem は使用しませんでした。

というのも、 以下理由からでした。

  • gem 'recaptcha' が v3 非対応。
  • gem 'new_google_recaptcha' は v3 対応してますが、スコアが返ってこないのでテストし辛い。

その他に既にあるのかもわかりませんが、記事執筆時には探し出すことはできませんでした。

まず reCAPTCHA v3 発行

以下 reCAPTCHA コンソールにアクセスし発行してください。

https://g.co/recaptcha/v3

v3 を選択し、今回導入するドメインを登録します。*2

f:id:kenzo0107:20190216214351p:plain

発行されたサイトキー・シークレットキーを保存しておきます。

  • サイトキー

    • ユーザがサイトにアクセスした際にトークンを取得する際に必要なキーです。こちらはユーザ公開して問題ありません。
  • シークレットキー

    • トークンを元に Google に問い合わせする際に必要なキーです。こちらは秘密情報として扱います。

f:id:kenzo0107:20190216214558p:plain

Rails 側実装

Rails >= 5.2 を想定しています。

config/credentials.yml.enc

recaptcha:
  secret_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

シークレットを秘密情報に保存します。

app/controllers/application_controller.rb

require 'net/http'
require 'uri'

class ApplicationController < ActionController::Base
...
  RECAPTCHA_MINIMUM_SCORE = 0.5
  RECAPTCHA_ACTION = 'homepage'
...
  def verify_recaptcha?(token)
    secret_key = Rails.application.credentials.recaptcha[:secret_key]
    uri = URI.parse("https://www.google.com/recaptcha/api/siteverify?secret=#{secret_key}&response=#{token}")
    r = Net::HTTP.get_response(uri)
    j = JSON.parse(r.body)
    j['success'] && j['score'] > RECAPTCHA_MINIMUM_SCORE && j['action'] == RECAPTCHA_ACTION
  end
end

共通メソッドとして、recaptcha の認証メソッド verify_recaptcha? を設定しています。

ここで、bot となるスコアを 0.5 以下としています。

通常通り操作していれば、十分超える数値です。

config/locales/en.yml

en:
  recaptcha:
    errors:
      verification_failed: 'reCAPTCHA Authorization Failed. Please try again later.'

local en 設定です。

config/locales/ja.yml

ja:
  recaptcha:
    errors:
      verification_failed: 'reCAPTCHA 認証失敗しました。しばらくしてからもう一度お試しください。'

local ja 設定です。

app/controllers/hoges_controller.rb

class HogesController < ApplicationController
  def new; end

  def create
    unless verify_recaptcha?(params[:recaptcha_token])
      flash.now[:recaptcha_error] = I18n.t('recaptcha.errors.verification_failed')
      return render action: :new
    end

    # something to do

    redirect_to hoge_finish_path
  end

  def finish; end
end

new から create に post して reCAPTCHA で bot 判定して

  • OK → finish へ進む
  • NG → new に戻る

という設計です。

app/views/hoges/new.html.erb

<% if flash[:recaptcha_error] %>
<div class="text">
  <p><spacn class="error"><%= flash[:recaptcha_error] %></span></p>
</div>
<% end %>

<%= form_tag({action: :create}, {method: :post}) do %>
...
  <input id="recaptcha_token" name="recaptcha_token" type="hidden"/>
  <%= submit_tag "送信する", :class => "submit-recaptcha btn", :disabled => true %>
<% end %>

<script src="https://www.google.com/recaptcha/api.js?render=<%= Settings.recaptcha.site_key %>&ver=3.0"></script>
<script>
grecaptcha.ready(function() {
  grecaptcha.execute('<%= Settings.recaptcha.site_key %>', {action: 'homepage'}).then(function(token) {
    $('#recaptcha_token').val(token);
    $('.submit-recaptcha').prop('disabled', false);
  });
});
</script>
エラーメッセージ表示
<% if flash[:recaptcha_error] %>
<div class="text">
  <p><spacn class="error"><%= flash[:recaptcha_error] %></span></p>
</div>
<% end %>
<form> ~ </form> 内に以下 name=recaptcha_token input タグを追加します。
<input id="recaptcha_token" name="recaptcha_token" type="hidden"/>
ページアクセス時に reCAPTCHA の token を取得すべく、スクリプトを仕込みます。
<script src="https://www.google.com/recaptcha/api.js?render=<%= Settings.recaptcha.site_key %>&ver=3.0"></script>
<script>
grecaptcha.ready(function() {
  grecaptcha.execute('<%= Settings.recaptcha.site_key %>', {action: 'homepage'}).then(function(token) {
    $('#recaptcha_token').val(token);
    $('.submit-recaptcha').prop('disabled', false);
  });
});
</script>

reCAPTCHA トークン取得が成功した場合に以下実行します。

  • id="recaptcha_token" input タグの valueトークンを設定
  • submit ボタンの有効化

<%= Settings.recaptcha.site_key %> について
gem 'settingslogic' をインストールしている前提で設定しています。

導入していない場合は、簡易的に処理を試す程度であれば、 <%= Settings.recaptcha.site_key %> を取得したサイトキーに置き換えて下さい。*3

以上で設定は完了です。

ページにアクセスしてみる

ページ右下に reCAPTCHA マークが常に表示される様になります。

f:id:kenzo0107:20190216234742p:plain

集計情報を見る

reCAPTCHA コンソールを見ると、以下の様な表示が出ていてすぐには集計情報が反映されていないと思います。

f:id:kenzo0107:20190216235044p:plain

しばらく経つと以下の様なグラフが表示される様になります。

f:id:kenzo0107:20190216235204p:plain

注意

例えば、社内 IP 等固定された IP からテストで頻繁にアクセスすると、 bot 扱いされます。

reCAPTCHA 側で IP のホワイトリストはないので、その場合、 Rails 側で許可 IP リストを作る必要があります。

以上
参考になれば幸いです。

*1:2019年2月現在最新バージョン

*2:ドメインは複数登録可能です。ドメイン毎に集計や、 bot 対策の傾向を変えたい場合は、個々に発行します。 また、 RAILS_ENV = production とそれ以外で発行する方が本番への影響がないので推奨されます。

*3:前にもお伝えしましたが、サイトキーの管理は直指定でなく、何かしら管理が推奨です。

AWS ECS トラブルシューティング

f:id:kenzo0107:20190208225212j:plain

ECS を利用していて幾つかはまったポイントがあったのでまとめました。

started 1 task が複数回実行されるが、コンテナが起動しない

$ ecs-cli compose service up ...

level=info msg="(service hogehoge) has started 1 tasks ..."
level=info msg="(service hogehoge) has started 1 tasks ..."
level=info msg="(service hogehoge) has started 1 tasks ..."

ecs-cli compose service up でデプロイ時にタスク起動を実行するものの、起動が正しくできていない状態です。
こちらはコンテナ起動時の処理に問題がある場合があります。

  • コンテナログを確認して、コンテナ起動失敗時刻付近のログを確認してください。
  • 例えば、Nginx の設定ファイル, Rails のコードに typo, syntax error がある等です。

already using a port required by your task

service hogehoge was unable to place a task because no container instance met all of its requirements. 
The closest matching container-instance a1b2c3d4-e5f6-g7h8-j9k0-l1m2n3o4p5q6 is already using a port required by your task

port mapping を以下の様に設定していた。

"portMappings": [
   {
     "hostPort": 0,
     "protocol": "tcp",
     "containerPort": 80
   }
 ],

新しいタスクでも 0:80 のポートを利用しようとする為、エラーとなります。 以下の様に設定することで回避できました。

"portMappings": [
   {
     "containerPort": 80
   }
 ],

insufficient memory available

INFO[0031] (service hogehoge) was unable to place a task because no container instance met all of its requirements. The closest matching (container-instance a1b2c3d4-e5f6-g7h8-j9k0-l1m2n3o4p5q6) has insufficient memory available. For more information, see the Troubleshooting section of the Amazon ECS Developer Guide.  timestamp=2018-03-09 15:45:24 +0000 UTC

タスク更新(ecs-cli compose service up)実行時、
上記の様なメモリ不足が出る場合はインスタンスタイプを上げる、また、他タスクを削除する等、メモリーリソースを増やす対応が必要です。

no space on device

no space on device で イメージを pull できない。

f:id:kenzo0107:20191003173809p:plain

df -hT コマンドで 容量の使用状況確認

未使用のコンテナ・ボリュームを強制削除しお掃除

docker system prune -af --volumes

msg="Couldn't run containers" reason="RESOURCE:CPU"

msg="Couldn't run containers" reason="RESOURCE:CPU"

タスクで指定している cpu (vCPU) が不足しています。 インスタンスタイプを上げる、もしくは、他タスクを削除する等、 CPU リソースを増やす対応が必要です。

Fargate - Port Mapping Error

level=error msg="Create task definition failed" error="ClientException: When networkMode=awsvpc, the host ports and container ports in port mappings must match.\n\tstatus code: 400, request id: a1b2c3d4-e5f6-g7h8-j9k0-l1m2n3o4p5q6"

起動タイプ Fargate で以下の様な設定だと、NG

    ports:
      - "80"

こちらだと OK。

    ports:
      - "80:80"

ホストポートとコンテナポートのマッピングが必要です。

Fargate volume_from は利用できない

volume_from は Fargate では使用できません。

level=error msg="Create task definition failed" error="ClientException: host.sourcePath should not be set for volumes in Fargate.\n\tstatus code: 400, request id: a1b2c3d4-e5f6-g7h8-j9k0-l1m2n3o4p5q6"

指定された IAM Role が適切なパーミッションを与えられていない

IAM Role に権限を適宜付与します。

level=info msg="(service hogehoge) failed to launch a task with (error ECS was unable to assume the role 'arn:aws:iam::123456789012:role/ecsTask
ExecutionRole' that was provided for this task. Please verify that the role being passed has the proper trust relationship and permissions and that your IAM user has permissions to pass this role.)." timestamp=2018-06-21 08:15:43 +0000 UTC

イメージ pull できないというエラーも権限を付与していないことに起因することが主です。

CannotPullContainerError: API error (500): Get https://123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/v2/: net/http: request canceled while waiting for connection"

現在稼働している ECS の IAM Role の権限を参考してください。変更される可能性があるのであくまで参考にし、適宜最新の情報を以ってご対応ください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "logs:PutLogEvents",
                "logs:CreateLogStream",
                "logs:CreateLogGroup",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:Describe*",
                "elasticloadbalancing:DeregisterTargets",
                "ecs:UpdateService",
                "ecs:Submit*",
                "ecs:StartTelemetrySession",
                "ecs:StartTask",
                "ecs:RunTask",
                "ecs:RegisterTaskDefinition",
                "ecs:RegisterContainerInstance",
                "ecs:Poll",
                "ecs:ListTasks",
                "ecs:DiscoverPollEndpoint",
                "ecs:DescribeTasks",
                "ecs:DescribeServices",
                "ecs:DescribeContainerInstances",
                "ecs:DeregisterContainerInstance",
                "ecs:CreateService",
                "ecr:UploadLayerPart",
                "ecr:PutImage",
                "ecr:InitiateLayerUpload",
                "ecr:GetDownloadUrlForLayer",
                "ecr:GetAuthorizationToken",
                "ecr:CompleteLayerUpload",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability",
                "ec2:Describe*"
            ],
            "Resource": "*"
        }
    ]
}

以上です。

また何か発生したら追記していきたいと思います。

Reference

ECS EC2 上で起動する Datadog Agent コンテナが unhealthy になる時の処方箋

f:id:kenzo0107:20190110112818p:plain

概要

$ docker ps

CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS                    PORTS                NAMES
8baa0e2cff47        datadog/docker-dd-agent:latest   "/entrypoint.sh supe…"   31 hours ago        Up 31 hours (unhealthy)   8125/udp, 8126/tcp   ecs-dd-agent-task-1-dd-agent-f6d3d5eb9febcab9c601

ある日、ECS で起動させている Datadog Agent コンテナが unhealthy になってしまう事象が発生しました。 その原因と対応法をまとめました。

結論

Datadog Agent イメージを現時点の最新バージョン 6 系にすることで解決できました。

Datadog サポートに問い合わせた所、 今回のケースでは Datadog Agent イメージのバージョンが 5 系だったことに起因していました。

datadog/docker-dd-agent:latest は 5系の最新だった!

バージョン5が最新だった時には設定手続きは以下に沿って実施していました。 https://docs.datadoghq.com/integrations/faq/agent-5-amazon-ecs/

上記手順にて登場する datadog agent の ECS での起動用タスクが以下になります。 ここで指定しているイメージ (datadog/docker-dd-agent:latest) が 5系でした。 https://docs.datadoghq.com/json/dd-agent-ecs.json

datadog/docker-dd-agent:latest は 5系の最新だった!

datadog/agent:latest が 2019.01.10 時点最新の 6系 !

現最新バージョン 6系を扱うには以下設定手続きを参照します。 https://docs.datadoghq.com/integrations/amazon_ecs

手続きで変更点はタスク定義の変更くらいです。 https://docs.datadoghq.com/json/datadog-agent-ecs.json

今の所、datadog/agent:latest が6系の最新になっています。 7系になった際には是非とも互換維持してほしいです。

おまけ

サポートへの問い合わせ

サポートに問い合わせると、 caseID という問い合わせの ID をいただけます。 その後、caseID を設定し、起動時のログファイル (tar.gz) を取得し、サポート宛に添付しました。

ECS の管理下にある EC2 に ssh ログインし以下実行します。

$ docker run --rm -v /tmp:/tmp -e API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx datadog/docker-dd-agent:latest /etc/init.d/datadog-agent flare <caseID>

2019-01-03 12:27:44,472 | ERROR | dd.collector | utils.dockerutil(dockerutil.py:148) | Failed to initialize the docker client. Docker-related features will fail. Will retry 0 time(s). Error: Error while fetching server API version: ('Connection aborted.', error(2, 'No such file or directory'))
...
2019-01-03 12:27:45,807 | INFO | dd.collector | utils.flare(flare.py:161) | Saving all files to /tmp/datadog-agent-2019-01-03-12-27-44.tar.bz2
/tmp/datadog-agent-2019-01-03-12-27-44.tar.bz2 is going to be uploaded to Datadog.
...

EC2 ホスト上に /tmp/datadog-agent-2019-01-03-12-27-44.tar.bz2 ファイルが取得できるので、それをサポート宛にメール添付しました。

上記でログも含めサポートに連絡した所、API バージョンにより接続中止されている、という指摘を受け、バージョン上げて!という話になりました。

2019-01-03 12:27:44,472 | ERROR | dd.collector | utils.dockerutil(dockerutil.py:148) | Failed to initialize the docker client. Docker-related features will fail. Will retry 0 time(s). Error: Error while fetching server API version: ('Connection aborted.', error(2, 'No such file or directory'))

サポートさんありがとう♪ f:id:kenzo0107:20190110112453p:plain

以上です。 参考になれば幸いです。

ECS EC2 上で起動する Datadog Agent コンテナが unhealthy になる時の処方箋

f:id:kenzo0107:20190110112818p:plain

概要

$ docker ps

CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS                    PORTS                NAMES
8baa0e2cff47        datadog/docker-dd-agent:latest   "/entrypoint.sh supe…"   31 hours ago        Up 31 hours (unhealthy)   8125/udp, 8126/tcp   ecs-dd-agent-task-1-dd-agent-f6d3d5eb9febcab9c601

ある日、ECS で起動させている Datadog Agent コンテナが unhealthy になってしまう事象が発生しました。 その原因と対応法をまとめました。

結論

Datadog Agent イメージを現時点の最新バージョン 6 系にすることで解決できました。

Datadog サポートに問い合わせた所、 今回のケースでは Datadog Agent イメージのバージョンが 5 系だったことに起因していました。

datadog/docker-dd-agent:latest は 5系の最新だった!

バージョン5が最新だった時には設定手続きは以下に沿って実施していました。 https://docs.datadoghq.com/integrations/faq/agent-5-amazon-ecs/

上記手順にて登場する datadog agent の ECS での起動用タスクが以下になります。 ここで指定しているイメージ (datadog/docker-dd-agent:latest) が 5系でした。 https://docs.datadoghq.com/json/dd-agent-ecs.json

datadog/docker-dd-agent:latest は 5系の最新だった!

datadog/agent:latest が 2019.01.10 時点最新の 6系 !

現最新バージョン 6系を扱うには以下設定手続きを参照します。 https://docs.datadoghq.com/integrations/amazon_ecs

手続きで変更点はタスク定義の変更くらいです。 https://docs.datadoghq.com/json/datadog-agent-ecs.json

今の所、datadog/agent:latest が6系の最新になっています。 7系になった際には是非とも互換維持してほしいです。

おまけ

サポートへの問い合わせ

サポートに問い合わせると、 caseID という問い合わせの ID をいただけます。 その後、caseID を設定し、起動時のログファイル (tar.gz) を取得し、サポート宛に添付しました。

ECS の管理下にある EC2 に ssh ログインし以下実行します。

$ docker run --rm -v /tmp:/tmp -e API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx datadog/docker-dd-agent:latest /etc/init.d/datadog-agent flare <caseID>

2019-01-03 12:27:44,472 | ERROR | dd.collector | utils.dockerutil(dockerutil.py:148) | Failed to initialize the docker client. Docker-related features will fail. Will retry 0 time(s). Error: Error while fetching server API version: ('Connection aborted.', error(2, 'No such file or directory'))
...
2019-01-03 12:27:45,807 | INFO | dd.collector | utils.flare(flare.py:161) | Saving all files to /tmp/datadog-agent-2019-01-03-12-27-44.tar.bz2
/tmp/datadog-agent-2019-01-03-12-27-44.tar.bz2 is going to be uploaded to Datadog.
...

EC2 ホスト上に /tmp/datadog-agent-2019-01-03-12-27-44.tar.bz2 ファイルが取得できるので、それをサポート宛にメール添付しました。

上記でログも含めサポートに連絡した所、API バージョンにより接続中止されている、という指摘を受け、バージョン上げて!という話になりました。

2019-01-03 12:27:44,472 | ERROR | dd.collector | utils.dockerutil(dockerutil.py:148) | Failed to initialize the docker client. Docker-related features will fail. Will retry 0 time(s). Error: Error while fetching server API version: ('Connection aborted.', error(2, 'No such file or directory'))

サポートさんありがとう♪ f:id:kenzo0107:20190110112453p:plain

以上です。 参考になれば幸いです。

boto3 の AssumeRole をしたアカウントスイッチ credentials 利用時の MFA 突破対応

f:id:kenzo0107:20181206121637p:plain

概要

備忘録です。

AssumeRole でのアカウントスイッチで credentials 情報を持っている場合に対応した boto3.Session での認証の仕方です。

MFA 設定してる場合も付けときました。

実装

# MFA 入力待ち
mfa_TOTP = raw_input("Enter the MFA code: ")

# sts クライアント
client=boto3.client( 'sts' )

# 認証
response = client.assume_role(
    RoleArn='arn:aws:iam::123456789:role/admin_full',
    RoleSessionName='mysession',
    DurationSeconds=3600,
    SerialNumber='arn:aws:iam::987654321:mfa/myaccount',
    TokenCode=mfa_TOTP,
)

# 認証情報
credentials = response['Credentials']

# session に 認証情報付加
session = boto3.Session(profile_name=session_name, 
    aws_access_key_id = credentials['AccessKeyId'],
    aws_secret_access_key = credentials['SecretAccessKey'],
    aws_session_token = credentials['SessionToken'],
)

ec2Client = session.client('ec2', region_name='ap-north-east1')
resources = ec2.describe_instances()

boto3.Session に sts で AssumeRole で得た credentials 情報を渡してます。

以上です。

No space left on device が発生して i-node 枯渇してた時の原因調査法

f:id:kenzo0107:20181015001102j:plain

Linux Server で No space left on device が発生した時の対処まとめです。

とりあえず df -h してみる

df -h しても 最大で 77% no space left on device が発生することでもなさそう

$ df -h 

Filesystem      Size  Used Avail Use% Mounted on
udev            1.9G     0  1.9G   0% /dev
tmpfs           385M   40M  346M  11% /run
/dev/nvme0n1p1   15G   11G  3.3G  77% /
tmpfs           1.9G     0  1.9G   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           1.9G     0  1.9G   0% /sys/fs/cgroup
tmpfs           385M     0  385M   0% /run/user/1022
tmpfs           385M     0  385M   0% /run/user/1128
tmpfs           385M     0  385M   0% /run/user/1098
tmpfs           385M     0  385M   0% /run/user/6096

-h = --human-readable 読みやすいサイズ表示をしてます。

df -i してみる

df -i で i-node 情報表示。最大 95%
これでした。

$ df -i

Filesystem     Inodes  IUsed  IFree IUse% Mounted on
udev           490419    351 490068    1% /dev
tmpfs          492742    521 492221    1% /run
/dev/nvme0n1p1 983040 927212  55828   95% /
tmpfs          492742      1 492741    1% /dev/shm
tmpfs          492742      3 492739    1% /run/lock
tmpfs          492742     16 492726    1% /sys/fs/cgroup
tmpfs          492742      4 492738    1% /run/user/1022
tmpfs          492742      4 492738    1% /run/user/1128
tmpfs          492742      4 492738    1% /run/user/1098
tmpfs          492742      4 492738    1% /run/user/1142

i-node とは?と思ったら、 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典 i-node編 辺りを見てみてください。

簡単に言うと、ファイルの属性情報を管理しているデータです。

要は、ファイル数が増えると、ファイルを管理するデータが増え、 i-node はどんどん増えていきます。

その調査法をまとめました。

どのディレクトリのファイル数が多いか調査

以下は「現ディレクトリでのファイル数多い順ランキング」です。

sudo find . -xdev -type f | cut -d "/" -f 2 | sort | uniq -c | sort -r

※ find の -xdev オプションはマウント先のファイルシステムを検索しない様にしてます。-type f はファイルのみ検索。

このワンライナーで原因となるファイル数の多いディレクトリを探索します。

当たりが付いている場合はそのディレクトリで実行

例えば、ユーザ毎にディレクトリが用意されている場合等に、個々人が home directory で git clone してるとか、
個々人が bundle install してて vendor ディレクトリ以下がファイル数が激増してたとか。

そういった事象があり得そうなら、 /home/ ディレクトリ以下でワンライナー実行して原因調査をするのが良いです。

各ユーザ毎が原因なら相談して消して良いかも確認できるし!

一番手っ取り早いのは、root path 「/」 で実行

どのディレクトリのファイル数が多いのかを探るのなら、一番上位階層の「/」(root) から実行した方が特定しやすいです。

但し、root から全てのディレクトリ内のファイルを検索するとなると非常に cpu を食います。
実行してしばらくレスポンスが返ってこなくてドキドキします。

top コマンド等で cpu 状況を監視しつつ、実行することをオススメします。

本番環境の web サーバで直ちにユーザ影響が出そうな場合は、LBから一旦外して、とか、ユーザアクセスの少ない時間に実行する様に影響範囲を最小限にしたい所。

状況見た上で進めましょう。

実際にあった i-node 枯渇原因

/usr ディレクトリ以下に linux-headers-*** ファイルが溜まっており、30% 近く食ってました。

以下記事に救われました。ありがとうございます。 古いカーネルの削除方法メモ

AWS EC2 t2 から t3 へ移行する為の step by step

f:id:kenzo0107:20180914130511j:plain

概要

AWS EC2 に t3 系インタスタンスが登場した為、サクッとできるかと思いきや、つまづいた箇所をまとめました。

今回対象のインスタンスは HVM で ubuntu 16.04.5 LTS を使用しました。

t2 と比べて t3 は何がいいの?

t2 から t3 へ移行する大まかな流れ

要は、ena モジュールをインストールし、EC2 ENA サポートを有効化する必要がありました。

  1. t2 インスタンス停止
  2. AMI 作成
  3. t2 インスタンス起動
  4. t2 インスタンスカーネルモジュール(ena) のインストール
  5. ena モジュールインストール確認
  6. t2 インスタンス停止
  7. インスタンスタイプを t3 へ変更 (credit: unlimited もしたい場合はここで)
  8. t3 インスタンス起動

ENA って?

Elastic Network Adapter – Amazon EC2 向けの高性能パフォーマンスネットワークインターフェイス

プロセッサのワークロードを軽くし、ネットワークパケットと生成または処理を行う vCPU 間で短く効率的なパスを作成するために構築されています。

Linux インスタンスにおける Elastic Network Adapter (ENA) を使用した拡張ネットワーキングの有効化 には以下のように記載があります。

Amazon EC2 は、Elastic Network Adapter (ENA) を介して C5, C5d, F1, G3, H1, I3, m4.16xlarge, M5, M5d, P2, P3, R4, R5, R5d, X1, X1e, and z1d インスタンスに拡張されたネットワーキング機能を提供します。

拡張ネットワーキングは、Amazon EC2 コンソールから管理することはできません。

HVM インスタンスでのみサポート

まとめると、

  • Amazon EC2 向けの高性能パフォーマンスネットワークインターフェイス
  • HVM (Hardware-assited VM:完全仮想化) 環境でサポートされている。
  • PV (ParaVirtual:準仮想化) 環境ではサポートされない。
  • ENA というカーネルモジュールを介す事で、インスタンスに拡張されたネットワーキング機能が利用できる。

pv/hvm は AWS コンソール>EC2 説明の「仮想化」の項目で確認できます。
pv の場合は、 hvm の移行を検討する必要があります。

f:id:kenzo0107:20180914130817p:plain

以下から設定に進みます。

ENA 有効化設定手順

以下前提とします。

  • AMI を取る等のバックアップが済んでいる。
  • t2.small から t3.small に移行する。

Ubuntu での拡張ネットワーキングの有効化

ubuntu:~$ sudo apt-get update && sudo apt-get upgrade -y linux-aws

他OSの対応法も先ほどの Linux インスタンスにおける Elastic Network Adapter (ENA) を使用した拡張ネットワーキングの有効化 に記載されています。

W: mdadm: /etc/mdadm/mdadm.conf defines no arrays. エラーが発生した場合

/etc/mdadm/mdadm.conf ファイルに以下一文を追記します。

ARRAY <ignore> devices=<ルートデバイス>

自分の場合は以下の一文を一番下に追記して、もう一度 コマンド実行したら通りました。

ARRAY <ignore> devices=/dev/sda1

ena カーネルモジュールに関する情報表示

modinfo ena を実行し以下のように表示されれば OK です。

ubuntu:~$ modinfo ena

filename:       /lib/modules/4.4.0-81-generic/kernel/drivers/net/ethernet/amazon/ena/ena.ko
version:        1.1.2
license:        GPL
description:    Elastic Network Adapter (ENA)
author:         Amazon.com, Inc. or its affiliates
...

EC2 ENA サポート有効化

// インスタンス停止
macOS%$ aws ec2 stop-instances --instance-ids <instance id>

// ENA サポート設定
macOS%$ aws ec2 modify-instance-attribute --instance-id <instance id> --ena-support true

// EBS 最適化 (任意)
macOS%$ aws ec2 modify-instance-attribute --instance-id <instance id> --ebs-optimized

// credit unlimited 設定 (任意)
macOS%$ aws ec2 modify-instance-credit-specification --instance-credit-specification "InstanceId=i-<instance id>,CpuCredits=unlimited"

// インスタンスタイプ変更
macOS%$ aws ec2 modify-instance-attribute --instance-id <instance id> --instance-type t3.small

// インスタンス起動
macOS%$ aws ec2 start-instances --instance-ids <instance id>

これで t3 デビューを飾ることができました♪

参照

Amazon Web Servicesではじめる新米プログラマのためのクラウド超入門 (CodeZine BOOKS)

Amazon Web Servicesではじめる新米プログラマのためのクラウド超入門 (CodeZine BOOKS)

*1:本稿執筆時 2018-09-14