起動時に Linux インスタンスでコマンドを実行する - Amazon Elastic Compute Cloud

起動時に Linux インスタンスでコマンドを実行する

Amazon EC2 でインスタンスを起動するとき、起動後にそのインスタンスにユーザーデータを渡し、一般的な自動設定タスクを実行したり、スクリプトを実行したりできます。2 つのタイプのユーザーデータを Amazon EC2 に渡すことができます。シェルスクリプトと cloud-init ディレクティブです。また、このデータは、プレーンテキスト、ファイル (コマンドラインツールを使用してインスタンスを起動する場合に便利です)、または base64 でエンコードされたテキスト (API コール向け) として、インスタンス起動ウィザードに渡すこともできます。

より複雑なオートメーションのシナリオに興味がある場合、AWS CloudFormation や AWS OpsWorks のご利用を検討してください。詳細については、次を参照してください:

Windows インスタンスの起動時にコマンドを実行する方法については、Windows インスタンスの Amazon EC2 ユーザーガイドの「Windows インスタンスでの起動時のコマンドの実行」および「Windows インスタンス設定の管理」を参照してください。

次の例では、「Amazon Linux 2 に LAMP ウェブサーバーをインストールする」のコマンドが、シェルスクリプトと、インスタンスの起動時に実行される一連の cloud-init ディレクティブに変換されています。各例では、次のタスクがユーザーデータにより実行されます。

  • ディストリビューションソフトウェアパッケージが更新されます。

  • 必要なウェブサーバー、phpmariadb パッケージがインストールされます。

  • systemctl を介して httpd サービスが開始され、オンになります。

  • ec2-user が apache グループに追加されます。

  • ウェブディレクトリとその中に含まれるファイルに対して、適切な所有権とファイル権限が設定されます。

  • ウェブサーバーと PHP エンジンをテストするために、シンプルなウェブページが作成されます。

前提条件

このトピックの例では、

  • インスタンスには、インターネットからアクセス可能なパブリック DNS 名が設定されていることを前提にしています。詳細については、ネットワーク設定 セクションおよび セキュリティグループの作成[Auto-assign Public IP](パブリック IP の自動割り当て) を参照してください。

  • インスタンスに関連付けられたセキュリティグループは、SSH (ポート 22) トラフィックを許可するように設定されているため、インスタンスに接続して出力ログファイルを表示できます。詳細については、「セキュリティグループの作成」を参照してください。

  • インスタンスは Amazon Linux 2 AMI を使用して起動します。この説明は Amazon Linux 2 での使用を意図したものです。他の Linux ディストリビューションの場合、コマンドとディレクティブが動作しないことがあります。cloud-init のサポートなど、その他のディストリビューションについての詳細は、該当するディストリビューションの文書を参照してください。

ユーザーデータとシェルスクリプト

シェルスクリプトに慣れている場合は、この方法が最も簡単で完全に起動時に指示を送信する方法です。起動時にこれらのタスクを追加すると、インスタンスの起動にかかる時間が増えます。タスクが完了するまでさらに数分待ち、それからユーザースクリプトが正常に完了したことをテストしてください。

重要

デフォルトでは、ユーザーデータスクリプトと cloud-init ディレクティブは、インスタンスの最初の起動サイクル中にのみ実行されます。インスタンスを再起動するたびにユーザーデータスクリプトと cloud-init ディレクティブが実行されるように設定を更新できます。詳細については、AWS ナレッジセンターの「Amazon EC2 Linux インスタンスを再起動する度にユーザーデータを実行して自動的にファイルを作成するにはどうすればよいですか?」を参照してください。

ユーザーデータシェルスクリプトは、#! 文字と、スクリプトの読み取り先であるインタープリタのパス (通常は /bin/bash)) で開始する必要があります。シェルスクリプティングに関する有用な紹介文は、Linux ドキュメントプロジェクト (tldp.org) の「BASHプログラミングのハウツー」で入手できます。

ユーザーデータとして入力されたスクリプトはルートユーザーとして実行されます。そのため、スクリプトでは sudo コマンドを使用しないでください。作成したファイルはすべてルートユーザーの所有になることを忘れないでください。ルート以外のユーザーにファイルアクセスを与える場合、スクリプトで許可を適宜変更する必要があります。また、スクリプトはインタラクティブに実行されないため、ユーザーフィードバックを必要とするコマンド (-y フラグのない yum update など) を含めることはできません。

ユーザーデータスクリプトで AWS API (AWS CLI など) を使用する場合は、インスタンスを起動するときにインスタンスプロファイルを使用する必要があります。インスタンスプロファイルは、API 呼び出しを発行するためにユーザーデータスクリプトが必要とする適切な AWS 認証情報を提供します。詳細については、IAM ユーザーガイド の「インスタンスプロファイルの使用」を参照してください。IAM ロールに割り当てるアクセス許可は、API で呼び出すサービスによって異なります。詳細については、「Amazon EC2 の IAM ロール」を参照してください。

cloud-init 出力ログファイルでコンソール出力がキャプチャされるため、インスタンスが意図したように動作しない場合でも、起動後、簡単にスクリプトをデバッグすることができます。ログファイルを表示するには、インスタンスに接続し、/var/log/cloud-init-output.log を開きます。

ユーザーデータスクリプトを処理すると、/var/lib/cloud/instances/instance-id/ にコピーされ、実行されます。実行後にスクリプトを削除することはできません。必ず /var/lib/cloud/instances/instance-id/ のユーザーデータスクリプトを削除してから、インスタンスに AMI を作成してください。それ以外の場合、スクリプトはこの AMI から起動されたインスタンスのこのディレクトリに存在します。

ユーザーデータおよびコンソール

インスタンスの起動時のインスタンスユーザーデータを指定できます。インスタンスのルートボリュームが EBS ボリュームの場合は、インスタンスを停止してユーザーデータを更新することもできます。

起動時にインスタンスユーザーデータを指定する

インスタンスを起動するための手順に従います。[User data] (ユーザーデータ) フィールドは、インスタンス起動ウィザードの 高度な詳細 セクションにあります。シェルスクリプトを [User data] (ユーザーデータ) フィールドに入力してから、インスタンスの起動手順を完了します。

下のスクリプト例では、スクリプトがウェブサーバーを作成し、設定します。

#!/bin/bash yum update -y amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2 yum install -y httpd mariadb-server systemctl start httpd systemctl enable httpd usermod -a -G apache ec2-user chown -R ec2-user:apache /var/www chmod 2775 /var/www find /var/www -type d -exec chmod 2775 {} \; find /var/www -type f -exec chmod 0664 {} \; echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php

インスタンスが起動し、スクリプトのコマンドを実行するまで十分待ち、それからスクリプトが意図したタスクを完了したことを確認します。

例では、ウェブブラウザにスクリプトが作成した PHP テストファイルの URL を入力します。この URL は、インスタンスのパブリック DNS アドレスにスラッシュとファイル名を追加したものです。

http://my.public.dns.amazonaws.com/phpinfo.php

PHP 情報ページが表示されるはずです。PHP 情報ページが表示されない場合、使用しているセキュリティグループに HTTP (ポート 80) トラフィックを許可するルールが含まれていることを確認します。詳細については、「セキュリティグループへのルールの追加」を参照してください。

(オプション) スクリプトが期待したタスクを達成しなかった場合、あるいは単にスクリプトがエラーなしで完了したことを確認する場合は、インスタンスに接続し、cloud-init 出力ログファイル (/var/log/cloud-init-output.log) を調べ、出力にエラーメッセージがないか探します。

デバッグの詳細情報を取得するには、次のディレクティブを指定して cloud-init データセクションを含む Mime マルチパートアーカイブを作成します。

output : { all : '| tee -a /var/log/cloud-init-output.log' }

このディレクティブにより、スクリプトから /var/log/cloud-init-output.log にコマンド出力が送信されます。cloud-init データ形式と MIME マルチパートアーカイブの作成方法の詳細については、「cloud-init Formats」を参照してください。

インスタンスユーザーデータの表示と更新

インスタンスのユーザーデータを更新するには、まずインスタンスを停止する必要があります。インスタンスが実行されている場合は、ユーザーデータを表示できますが、変更することはできません。

警告

インスタンスを停止すると、インスタンスストアボリューム上のデータは消去されます。インスタンスストアボリュームのデータを保持するには、データを永続的ストレージに必ずバックアップします。

インスタンスユーザーデータを変更するには
  1. Amazon EC2 コンソール (https://console.aws.amazon.com/ec2/) を開きます。

  2. ナビゲーションペインで、[インスタンス] を選択します。

  3. インスタンスを選択し、[Instance state (インスタンスの状態)]、[Stop instance (インスタンスの停止)] の順に選択します。このオプションが無効になっている場合は、インスタンスが既に停止しているか、またはルートボリュームがインスタンスストアボリュームです。

  4. 確認を求められたら、[Stop] を選択します。インスタンスが停止するまで、数分かかる場合があります。

  5. インスタンスが選択された状態のまま、[Actions (アクション)]、[Instance settings (インスタンス設定)]、[Edit user data (ユーザーデータの編集)] の順に選択します。

  6. 必要に応じてユーザーデータを変更し、[Save (保存)] を選択します。

  7. インスタンスを起動します。新しいユーザーデータは、再起動後にインスタンス上に表示されますが、ユーザーデータスクリプトは実行されません。

ユーザーデータと cloud-init ディレクティブ

cloud-init パッケージは、新しい Amazon Linux インスタンスが起動したときに、特定の側面を設定します。具体的には、お客様のプライベートキーでログインできるように、ec2-user の .ssh/authorized_keys ファイルを設定します。Amazon Linux インスタンスに対して cloud-init パッケージが実行する設定タスクの詳細については、「cloud-init」を参照してください。

構文は異なりますが、渡されたスクリプトと同じ方法で cloud-init ユーザーディレクティブを起動時のインスタンスに渡すことができます。cloud-init の詳細については、http://cloudinit.readthedocs.org/en/latest/index.html を参照してください。

重要

デフォルトでは、ユーザーデータスクリプトと cloud-init ディレクティブは、インスタンスの最初の起動サイクル中にのみ実行されます。インスタンスを再起動するたびにユーザーデータスクリプトと cloud-init ディレクティブが実行されるように設定を更新できます。詳細については、AWS ナレッジセンターの「Amazon EC2 Linux インスタンスを再起動する度にユーザーデータを実行して自動的にファイルを作成するにはどうすればよいですか?」を参照してください。

起動時にこれらのタスクを追加すると、インスタンスの起動にかかる時間が増えます。タスクが完了するまでさらに数分待ち、それからユーザーデータディレクティブが完了したことをテストしてください。

ユーザーデータで cloud-init ディレクティブをインスタンスに渡すには
  1. インスタンスを起動するための手順に従います。[User data] (ユーザーデータ) フィールドは、インスタンス起動ウィザードの 高度な詳細 セクションにあります。cloud-init ディレクティブテキストを[User data] (ユーザーデータ) フィールドに入力してから、インスタンスの起動手順を完了します。

    下の例では、ディレクティブが Amazon Linux 2 でウェブサーバーを作成し、設定します。一番上の #cloud-config 行は、cloud-init ディレクティブとしてコマンドを識別するために必要です。

    #cloud-config repo_update: true repo_upgrade: all packages: - httpd - mariadb-server runcmd: - [ sh, -c, "amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2" ] - systemctl start httpd - sudo systemctl enable httpd - [ sh, -c, "usermod -a -G apache ec2-user" ] - [ sh, -c, "chown -R ec2-user:apache /var/www" ] - chmod 2775 /var/www - [ find, /var/www, -type, d, -exec, chmod, 2775, {}, \; ] - [ find, /var/www, -type, f, -exec, chmod, 0664, {}, \; ] - [ sh, -c, 'echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php' ]
  2. インスタンスが起動し、ユーザーデータのディレクティブを実行するまで十分待ち、それから意図したタスクをディレクティブが完了したことを確認します。

    この例では、ウェブブラウザで、ディレクティブが作成した PHP テストファイルの URL を入力します。この URL は、インスタンスのパブリック DNS アドレスにスラッシュとファイル名を追加したものです。

    http://my.public.dns.amazonaws.com/phpinfo.php

    PHP 情報ページが表示されるはずです。PHP 情報ページが表示されない場合、使用しているセキュリティグループに HTTP (ポート 80) トラフィックを許可するルールが含まれていることを確認します。詳細については、「セキュリティグループへのルールの追加」を参照してください。

  3. (オプション) ディレクティブが期待したタスクを達成しなかった場合、あるいは単にディレクティブがエラーなしで完了したことを確認する場合は、インスタンスに接続し、出力ログファイル (/var/log/cloud-init-output.log) を調べ、出力にエラーメッセージがないか探します。デバッグの詳細情報を取得するには、ディレクティブに次の行を追加します:

    output : { all : '| tee -a /var/log/cloud-init-output.log' }

    このディレクティブにより、runcmd 出力が /var/log/cloud-init-output.log に送信されます。

ユーザーデータと AWS CLI

AWS CLI を使用して、インスタンスのユーザーデータを指定、変更、表示することができます。インスタンスのメタデータを使用して、インスタンスからユーザーデータを表示する方法については、「インスタンスからインスタンスユーザーデータを取得する」を参照してください。

Windows では、AWS CLI を使用する代わりに AWS Tools for Windows PowerShell を使用できます。詳細については、Windows インスタンスの Amazon EC2 ユーザーガイド の「ユーザーデータと Tools for Windows PowerShell」を参照してください。

例: ユーザーデータは、起動時に指定します。

インスタンスの起動時にユーザーデータを指定するには、run-instances コマンドと --user-data パラメータを使用します。run-instances で、AWS CLI はユーザーデータの base64 エンコードを実行します。

次の例は、コマンドラインで文字列としてスクリプトを指定する方法を示しています。

aws ec2 run-instances --image-id ami-abcd1234 --count 1 --instance-type m3.medium \ --key-name my-key-pair --subnet-id subnet-abcd1234 --security-group-ids sg-abcd1234 \ --user-data echo user data

次の例は、テキストファイルを使用してスクリプトを指定する方法を示しています。ファイルを指定するには、必ず file:// プレフィクスを使用してください。

aws ec2 run-instances --image-id ami-abcd1234 --count 1 --instance-type m3.medium \ --key-name my-key-pair --subnet-id subnet-abcd1234 --security-group-ids sg-abcd1234 \ --user-data file://my_script.txt

シェルスクリプトを使用したテキストファイルの例を次に示します。

#!/bin/bash yum update -y service httpd start chkconfig httpd on
例: 停止しているインスタンスのユーザーデータを変更する

停止したインスタンスのユーザーデータは、modify-instance-attribute コマンドを使用して変更できます。modify-instance-attribute では、AWS CLI はユーザーデータの base64 エンコードを実行しません。

  • Linux コンピュータでは、base64 コマンドを使用してユーザーデータをエンコードします。

    base64 my_script.txt >my_script_base64.txt
  • Windows コンピュータでは、certutil コマンドを使用してユーザーデータをエンコードします。このファイルを AWS CLI で使用する前に、最初の (証明書の開始) 行と最後の (証明書の終了) 行を削除する必要があります。

    certutil -encode my_script.txt my_script_base64.txt notepad my_script_base64.txt

--attribute および --value パラメータを使用して、エンコードされたテキストファイルを使用してユーザーデータを指定します。ファイルを指定するには、必ず file:// プレフィクスを使用してください。

aws ec2 modify-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData --value file://my_script_base64.txt
例: 停止しているインスタンスのユーザーデータをクリアする

既存のユーザーデータを削除するには、次の modify-instance-attribute コマンドを使用します。

aws ec2 modify-instance-attribute --instance-id i-1234567890abcdef0 --user-data Value=
例: ユーザーデータの表示

インスタンスのユーザーデータを取得するには、describe-instance-attribute コマンドを使用します。describe-instance-attribute では、AWS CLI はユーザーデータの base64 デコードを実行しません。

aws ec2 describe-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData

ユーザーデータが base64 でエンコードされた出力例を次に示します。

{ "UserData": { "Value": "IyEvYmluL2Jhc2gKeXVtIHVwZGF0ZSAteQpzZXJ2aWNlIGh0dHBkIHN0YXJ0CmNoa2NvbmZpZyBodHRwZCBvbg==" }, "InstanceId": "i-1234567890abcdef0" }
  • Linux コンピュータでは、--query オプションを使用してエンコードされたユーザーデータを取得し、base64 コマンドを使用してデコードします。

    aws ec2 describe-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData --output text --query "UserData.Value" | base64 --decode
  • Windows コンピュータでは、--query オプションを使用してコード化されたユーザーデータを取得し、certutil コマンドを使用してコードをデコードします。エンコードされた出力はファイルに保存され、デコードされた出力は別のファイルに保存されることに注意してください。

    aws ec2 describe-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData --output text --query "UserData.Value" >my_output.txt certutil -decode my_output.txt my_output_decoded.txt type my_output_decoded.txt

以下は出力例です。

#!/bin/bash yum update -y service httpd start chkconfig httpd on

シェルスクリプトと cloud-init ディレクティブを組み合わせる

デフォルトでは、ユーザーデータに含めることができるコンテンツタイプは一度に 1 つだけです。ただし、MIME マルチパートファイルの中で text/cloud-configtext/x-shellscript のコンテンツタイプを使用して、ユーザーデータにシェルスクリプトと cloud-init ディレクティブの両方を含めることは可能です。

以下に、MIME マルチパートの形式を示します。

Content-Type: multipart/mixed; boundary="//" MIME-Version: 1.0 --// Content-Type: text/cloud-config; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="cloud-config.txt" #cloud-config cloud-init directives --// Content-Type: text/x-shellscript; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="userdata.txt" #!/bin/bash shell script commands --//--

例えば、次のユーザーデータには cloud-init ディレクティブと bash シェルスクリプトが含まれています。cloud-init ディレクティブはファイル (/test-cloudinit/cloud-init.txt) を作成し、そのファイルに Created by cloud-init を書き込みます。bash シェルスクリプトはファイル (/test-userscript/userscript.txt) を作成し、そのファイルに Created by bash shell script を書き込みます。

Content-Type: multipart/mixed; boundary="//" MIME-Version: 1.0 --// Content-Type: text/cloud-config; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="cloud-config.txt" #cloud-config runcmd: - [ mkdir, /test-cloudinit ] write_files: - path: /test-cloudinit/cloud-init.txt content: Created by cloud-init --// Content-Type: text/x-shellscript; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="userdata.txt" #!/bin/bash mkdir test-userscript touch /test-userscript/userscript.txt echo "Created by bash shell script" >> /test-userscript/userscript.txt --//--