AWS

Linuxインスタンスの初期化方法4選

スポンサーリンク
CloudFormationで確認するインスタンスを初期化する4つの方法 AWS
スポンサーリンク
スポンサーリンク

Linuxインスタンスを初期化する4つの方法

EC2インスタンスを構築時に初期化する以下の4つの手法を取り上げます。

  1. ユーザーデータを使用する方法
  2. cfn-initを使用する方法
  3. SSM State ManagerでAnsible Playbookを実行する方法
  4. SSM State Managerでシェルスクリプトを実行する方法

構築する環境

Amazon.co.jp: AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版 : NRIネットコム株式会社, 佐々木 拓郎, 林 晋一郎, 金澤 圭: 本
Amazon.co.jp: AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版 : NRIネットコム株式会社, 佐々木 拓郎, 林 晋一郎, 金澤 圭: 本
Diagram of four ways to initialize Linux instance.

4つのEC2インスタンスに対して、上記の4つの方法で初期化処理を実行します。

実施する内容は、共通して以下を実施します。

  • yumをアップデートする。
  • yumでApacheをインストールし、起動・有効化する。
  • インスタンスIDをindex.htmlに書き込み、Apacheのルートページとする。

なお各インスタンスはSSHでアクセスできるように設定します。

環境構築用のCloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。

以下のURLにCloudFormationテンプレートを配置します。

awstut-fa/004 at main · awstut-an-r/awstut-fa
Contribute to awstut-an-r/awstut-fa development by creating an account on GitHub.

テンプレートファイルのポイント解説

今回の環境を構成するための、各テンプレートファイルのポイントを取り上げます。

ユーザーデータを使用して初期化処理を定義する

fa-004-ec2-01.yamlでインスタンス①を定義します。

Resources:
  Instance1:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: 0
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref InstanceSecurityGroup
      UserData: !Base64 |
        #!/bin/bash -xe
        yum update -y
        yum install -y httpd
        systemctl start httpd
        systemctl enable httpd
        ec2-metadata -i > /var/www/html/index.html
Code language: YAML (yaml)

ユーザーデータを使用することで、インスタンスの初期化処理を定義することができます。

Amazon EC2 でインスタンスを起動するとき、起動後にそのインスタンスにユーザーデータを渡し、一般的な自動設定タスクを実行したり、スクリプトを実行したりできます。

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

UserDataプロパティでユーザーデータを定義することができます。ユーザーデータに指定する内容はプレーンテキストではなく、Base64でエンコードされている必要があります。

ユーザーデータは、base64 でエンコードされている必要があります。Amazon EC2コンソールは、base64 エンコードを実行したり、base64 エンコード入力を受け入れたりできます。

インスタンスユーザーデータの使用

今回はCloudFormationの組み込み関数Fn::Base64を使用して、初期化処理をエンコードして渡します。

cfn-initを使用して初期化処理を定義する

fa-004-ec2-02.yamlでインスタンス②を定義します。

Resources:
  Instance2:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              httpd: []
          services:
            sysvinit:
              httpd:
                enabled: 'true'
                ensureRunning: 'true'
          commands:
            001make-index.html:
              command: ec2-metadata -i > /var/www/html/index.html
    Properties:
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: 0
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref InstanceSecurityGroup
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          yum install -y aws-cfn-bootstrap
          
          /opt/aws/bin/cfn-init -v \
          --stack ${AWS::StackName} \
          --resource Instance2 \
          --region ${AWS::Region}
Code language: YAML (yaml)

CloudFormationのヘルパースクリプトの一種であるcfn-initを使用することで、インスタンスの初期化処理を定義することができます。

cfn-init ヘルパースクリプトは、AWS::CloudFormation::Init キーからテンプレートメタデータを読み取り、それに応じて次のような操作を行います。

AWS CloudFormation のメタデータの取得と解析

パッケージのインストール

ディスクへのファイルの書き込み

サービスの有効化/無効化と開始/停止

cfn-init

ユーザーデータで同スクリプトの実行を定義します。メタデータとして同スクリプトで実行する処理を定義します。

AWS::CloudFormation::Init タイプを使用して、cfn-init ヘルパースクリプト用のメタデータを Amazon EC2 インスタンスに取り込みます。テンプレートが cfn-init スクリプトを呼び出す場合、スクリプトは AWS::CloudFormation::Init メタデータキーをルートとするリソースメタデータを検索します。

AWS::CloudFormation::Init

configプロパティ以下で、実行する処理を該当するセクションに分けて定義します。

設定は、いくつかのセクションに分かれています。(中略) cfn-init ヘルパースクリプトは、次の手順でこれらの設定セクションを処理します。パッケージ、グループ、ユーザー、ソース、ファイル、コマンド、そしてサービスの順です。

AWS::CloudFormation::Init

今回は以下の通り定義します。

  • packagesセクション:Apacheをインストールする。
  • servicesセクション:Apacheを起動・有効化する。
  • commandsセクション:インスタンスIDをindex.htmlに書き込み、Apacheのルートページとする。

SSM State ManagerでAnsible Playbookを実行して初期化する

fa-004-ec2-03.yamlでインスタンス③を定義します。

Resources:
  ApplyAnsiblePlaybooksAssociation:
    Type: AWS::SSM::Association
    Properties:
      AssociationName: !Sub ${Prefix}-playbook-association
      Name: AWS-ApplyAnsiblePlaybooks
      OutputLocation:
        S3Location:
          OutputS3BucketName: !Ref PlaybookBucketName
          OutputS3KeyPrefix: !Sub "${Prefix}/playbook-association-log"
      Parameters:
        Check:
          - "False"
        ExtraVariables:
          - SSM=True
        InstallDependencies:
          - "True"
        PlaybookFile:
          - !Ref PlaybookFileName
        SourceInfo:
          - !Sub '{"path": "https://${PlaybookBucketName}.s3.${AWS::Region}.amazonaws.com/${Prefix}/${PlaybookPackageName}"}'
        SourceType:
          - S3
        Verbose:
          - -v
      Targets:
        - Key: InstanceIds
          Values:
            - !Ref Instance3
      WaitForSuccessTimeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
Code language: YAML (yaml)

SSM State Managerを使用することで、インスタンスの初期化処理を定義することができます。

AWS Systems Manager の一機能であるステートマネージャーは、Amazon Elastic Compute Cloud (Amazon EC2) とハイブリッドインフラストラクチャを定義された状態に保つプロセスを自動化する、安全かつスケーラブルな設定管理サービスです。

AWS Systems Manager ステートマネージャー

AWSとしては、CloudFormationのcfn-initよりも、SSM State Managerによる初期化を推奨しています。

CloudFormationとState Managerの組み合わせには多くのパターンがありますが、我々が推奨するのは、CloudFormationでAWSリソースを定義し、Systems Managerを使ってOS上の設定管理を実施する方法です。(中略) cfn-initの代わりにState Managerを使用することを検討してください。

CloudFormation で cfn-init に代えて State Manager を利用する方法とその利点

SSM State Managerによってインスタンスを初期化するためには、State Manager関連付けを定義する必要があります。関連付けを作成する上でポイントとなるパラメータは、SSMドキュメントです。

AWS Systems Manager ドキュメント (SSM ドキュメント) は、Systems Manager がマネージドインスタンスで実行するアクションを定義します。Systems Manager には、実行時にパラメータを指定して使用できる事前設定済みのドキュメントが 100 件以上含まれています。

AWS Systems Manager ドキュメント

インスタンス③では、Ansible Playbookを使って初期化を実行します。そのためNameプロパティに「AWS-ApplyAnsiblePlaybooks」を指定します。

またOutputLocationプロパティで処理時のログの保存先を指定します。今回は後述のPlaybookファイルと同じ位置にログを出力させるように設定します。

Targetsプロパティで関連付ける対象のインスタンスを指定します。今回はインスタンスIDを使って指定します。

Parametersプロパティで、Playbookファイルのファイル名や、同ファイルの設置場所等を指定します。今回は「Ansible プレイブックを実行する関連付けを作成する (CLI)」を参考に設定しました。

インスタンス用のIAMロールを定義します。

Resources:
  InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
                - ssm.amazonaws.com
      Policies:
        - PolicyName: SSMStateManagerPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:PutObjectAcl
                  - s3:ListBucket
                Resource:
                  - !Sub "arn:aws:s3:::${PlaybookBucketName}"
                  - !Sub "arn:aws:s3:::${PlaybookBucketName}/*"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Code language: YAML (yaml)

SSM State Managerを使用して初期化するためには、インスタンスに2つの権限を付与する必要があります。

1つ目はAnsible Playbookおよびログを設置するS3バケットへのアクセス権です。インラインポリシーにて必要な権限を定義します。

2つ目はSSMマネージドインスタンスの条件を満たすための権限です。SSM State Managerの対象とできるインスタンスは、SSMマネージドインスタンスです。

マネージドインスタンスは、AWS Systems Manager 用に設定されたマシンです。Amazon Elastic Compute Cloud (Amazon EC2) インスタンスまたはハイブリッド環境内のオンプレミスのマシンを、マネージドインスタンスとして設定できます。

マネージドインスタンス

Amazon Linux 2ベースのインスタンスの場合、AWS管理ポリシーAmazonSSMManagedInstanceCoreの権限を付与することによって、SSMマネージドインスタンスとして扱うことができます。

SSM State Managerでシェルスクリプトを実行して初期化する

fa-004-ec2-04.yamlでインスタンス④を定義します。

Resources:
  RunShellScriptAssociation:
    Type: AWS::SSM::Association
    Properties:
      AssociationName: !Sub ${Prefix}-shellscript-association
      Name: AWS-RunShellScript
      OutputLocation:
        S3Location:
          OutputS3BucketName: !Ref LogBucketName
          OutputS3KeyPrefix: !Sub "${Prefix}/shellscript-association-log"
      Parameters:
        commands:
          - "sudo yum update -y"
          - "sudo yum install -y httpd"
          - "sudo systemctl start httpd"
          - "sudo systemctl enabled httpd"
          - "ec2-metadata -i > /var/www/html/index.html"
      Targets:
        - Key: InstanceIds
          Values:
            - !Ref Instance4
      WaitForSuccessTimeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
Code language: YAML (yaml)

基本的には、先ほどのAnsible Playbookの場合と同様です。

今回はシンプルにシェルスクリプトの実行を目的とした「AWS-RunShellScript」をSSMドキュメントに指定します。

Parametersプロパティ内のcommandsプロパティで実行するコマンドを定義することができます。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

PlaybookファイルをS3バケットに設置する

まずインスタンス③の初期化時に実行するAnsible Playbookファイルを、S3バケットに設置します。

今回はファイルをzip化した後に、AWS CLIを使って実行します。

$ zip -r playbook.zip playbook.yml

$ aws s3 cp playbook.zip s3://[bucket-name]/
Code language: Bash (bash)

AWS CLIを使用してCloudFormationスタックを作成する

次にCloudFormationスタックを作成します。

AWS CLIからCloudFormationスタックを作成する方法については、以下のページをご確認ください。

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • インスタンス1のID:i-0a9b66cd7cedd7c32
  • インスタンス2のID:i-0e809df5ce3202e36
  • インスタンス3のID:i-0972c3d05d89efeba
  • インスタンス4のID:i-02fb072859880b2d8

また各インスタンスの詳細を以下のコマンドで確認することができます。

$ aws ec2 describe-instances \
--instance-ids i-0a9b66cd7cedd7c32 i-0e809df5ce3202e36 i-0972c3d05d89efeba i-02fb072859880b2d8
Code language: Bash (bash)

上記より各インスタンスのパブリックDNS名を確認したところ、今回は以下となりました。

  • インスタンス1のパブリックDNS名:ec2-18-183-148-192.ap-northeast-1.compute.amazonaws.com
  • インスタンス2のパブリックDNS名:ec2-46-51-227-221.ap-northeast-1.compute.amazonaws.com
  • インスタンス3のパブリックDNS名:ec2-35-72-10-175.ap-northeast-1.compute.amazonaws.com
  • インスタンス4のパブリックDNS名:ec2-35-72-10-35.ap-northeast-1.compute.amazonaws.com

挙動確認1:ユーザーデータを使用してインスタンスを初期化する

準備が整いましたので、実際に挙動を確認します。

まずインスタンス1が正常に初期化されていることを確認します。

$ curl http://ec2-18-183-148-192.ap-northeast-1.compute.amazonaws.com
instance-id: i-0a9b66cd7cedd7c32
Code language: Bash (bash)

次にSSHでアクセスし、初期化時のログを確認します。

$ ssh -i MyKeyPair.pem ec2-18-183-148-192.ap-northeast-1.compute.amazonaws.com

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-10-0-1-253 ~]$


[ec2-user@ip-10-0-1-253 ~]$ sudo cat /var/log/cloud-init-output.log
...
+ yum update -y
...
Complete!
+ yum install -y httpd
...
Installed:
  httpd.x86_64 0:2.4.48-2.amzn2                                                 
...
Complete!
+ systemctl start httpd
+ systemctl enable httpd
...
+ ec2-metadata -i
Code language: Bash (bash)

/var/log/cloud-init-output.logで、初期化時のログを確認することができました。

このようにユーザーデータで初期化した場合は、別途設定しない限り、処理の対象となるインスタンスのローカルにログが設置されることになります。

挙動確認2:cfn-initを使用してインスタンスを初期化する

まずインスタンス2が正常に初期化されていることを確認します。

$ curl http://ec2-46-51-227-221.ap-northeast-1.compute.amazonaws.com
instance-id: i-0e809df5ce3202e36
Code language: Bash (bash)

次にSSHでアクセスし、初期化時のログを確認します。

$ ssh -i MyKeyPair.pem ec2-46-51-227-221.ap-northeast-1.compute.amazonaws.com

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-10-0-1-82 ~]$
Code language: Bash (bash)
[ec2-user@ip-10-0-1-82 ~]$ sudo cat /var/log/cfn-init.log
2021-10-03 05:02:50,379 [INFO] -----------------------Starting build-----------------------
2021-10-03 05:02:50,380 [DEBUG] Not setting a reboot trigger as scheduling support is not available
2021-10-03 05:02:50,381 [INFO] Running configSets: default
2021-10-03 05:02:50,382 [INFO] Running configSet default
2021-10-03 05:02:50,384 [INFO] Running config config
2021-10-03 05:02:53,990 [DEBUG] Installing/updating ['httpd'] via yum
2021-10-03 05:02:57,144 [INFO] Yum installed ['httpd']
2021-10-03 05:02:57,144 [DEBUG] No groups specified
2021-10-03 05:02:57,144 [DEBUG] No users specified
2021-10-03 05:02:57,144 [DEBUG] No sources specified
2021-10-03 05:02:57,144 [DEBUG] No files specified
2021-10-03 05:02:57,144 [DEBUG] Running command 001make-index.html
2021-10-03 05:02:57,144 [DEBUG] No test for command 001make-index.html
2021-10-03 05:02:57,182 [INFO] Command 001make-index.html succeeded
2021-10-03 05:02:57,182 [DEBUG] Command 001make-index.html output: 
2021-10-03 05:02:57,182 [DEBUG] Using service modifier: /sbin/chkconfig
2021-10-03 05:02:57,182 [DEBUG] Setting service httpd to enabled
2021-10-03 05:02:57,278 [INFO] enabled service httpd
2021-10-03 05:02:57,278 [DEBUG] Using service runner: /sbin/service
2021-10-03 05:02:57,297 [DEBUG] Starting service httpd as it is not running
2021-10-03 05:02:57,370 [INFO] Started httpd successfully
2021-10-03 05:02:57,371 [INFO] ConfigSets completed
2021-10-03 05:02:57,371 [DEBUG] Not clearing reboot trigger as scheduling support is not available
2021-10-03 05:02:57,371 [INFO] 
-----------------------Build complete-----------------------
Code language: Bash (bash)
[ec2-user@ip-10-0-1-82 ~]$ sudo cat /var/log/cfn-init-cmd.log
2021-10-03 05:02:50,382 P2445 [INFO] ************************************************************
2021-10-03 05:02:50,382 P2445 [INFO] ConfigSet default
2021-10-03 05:02:50,384 P2445 [INFO] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2021-10-03 05:02:50,384 P2445 [INFO] Config config
2021-10-03 05:02:53,991 P2445 [INFO] ============================================================
2021-10-03 05:02:53,991 P2445 [INFO] yum install httpd
2021-10-03 05:02:57,135 P2445 [INFO] -----------------------Command Output-----------------------
...
2021-10-03 05:02:57,142 P2445 [INFO]    Installed:
2021-10-03 05:02:57,142 P2445 [INFO]      httpd.x86_64 0:2.4.48-2.amzn2                                                 
...
2021-10-03 05:02:57,143 P2445 [INFO]    Complete!
2021-10-03 05:02:57,144 P2445 [INFO] ------------------------------------------------------------
2021-10-03 05:02:57,144 P2445 [INFO] Completed successfully.
2021-10-03 05:02:57,144 P2445 [INFO] ============================================================
2021-10-03 05:02:57,145 P2445 [INFO] Command 001make-index.html
2021-10-03 05:02:57,181 P2445 [INFO] Completed successfully.
Code language: Bash (bash)

/var/log/cfn-init.logおよび/var/log/cfn-init-cmd.logで、初期化時のログを確認することができました。

このようにcfn-initで初期化した場合は、別途設定しない限り、処理の対象となるインスタンスのローカルにログが設置されることになります。

挙動確認3:SSM State ManagerでAnsible Playbookを実行してインスタンスを初期化する

まずインスタンス3が正常に初期化されていることを確認します。

$ curl http://ec2-35-72-10-175.ap-northeast-1.compute.amazonaws.com
instance-id: i-0972c3d05d89efeba
Code language: Bash (bash)

次にAWS Management ConsoleからSSM Run Commandの実行状況を確認します。

From the results of the execution of ApplyAnsiblePlaybooks in SSM Run Commands, we can see that it was successful.

Instance3に対してRun Commandが実行され、正常に終了したことがわかります。

実行結果の詳細を確認することもできます。

In the detail page of the result of running ApplyAnsiblePlaybooks, we can see that the process was divided into two steps.

AnsibleのPlaybookを実行するために2つのステップに分けてコマンドが実行されたことがわかります。

オプションを有効化した場合、S3バケットにログを出力することができます。

s3://[bucket-name]/[folder-name]/[command-id]/[instance-id]/awsrunShellScript/runShellScript/stdout

今回の実行ログの中身は以下の通りです。

$ cat stdout
...
Installed:
  ansible.noarch 0:2.9.25-1.el7                                                 
...
Running Ansible in /var/lib/amazon/ssm/i-079d3fa8b1503d6b2/document/orchestration/4649a395-5815-400d-bf21-596b1c098a20/downloads
Archive:  ./playbook.zip
  inflating: playbook.yml            
Using /etc/ansible/ansible.cfg as config file

PLAY [all] *********************************************************************

TASK [update yum] **************************************************************
...

TASK [install the latest version of Apache] ************************************
...

TASK [start and enable Apache] *************************************************
...

TASK [make index.html] ******************************************
...

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Code language: Bash (bash)

ログを確認すると、Ansibleのインストール後、Playbookをダウンロードし、Taskを順番に実行していることがわかります。

このようにSSM State Managerで初期化した場合は、S3バケットにログを出力することができます。

挙動確認4:SSM State Managerでシェルスクリプトを実行してインスタンスを初期化する

まずインスタンス4が正常に初期化されていることを確認します。

$ curl http://ec2-35-72-10-35.ap-northeast-1.compute.amazonaws.com
instance-id: i-02fb072859880b2d8
Code language: Bash (bash)

次にAWS Management ConsoleからSSM Run Commandの実行状況を確認します。

From the results of the execution of AWS-RunShellScript in SSM Run Commands, we can see that it was successful.

Instance4に対して実行され、成功していることがわかります。

詳細ページは以下の通りです。

On the AWS-RunShellScript execution result detail page, you can also check the log.

1ステップで実行されたことがわかります。

こちらも実行結果をS3バケットに保存するように設定しています。ログは以下の位置に設置されます。

s3://[bucket-name]/[folder-name]/[command-id]/[instance-id]/awsrunShellScript/0.awsrunShellScript/stdout

中身は以下の通りです。

$ cat stdout
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
...
Installed:
  httpd.x86_64 0:2.4.48-2.amzn2
Complete!
Code language: Bash (bash)

ログを確認すると、指定したコマンドの標準出力が書き込まれていることがわかります。

このようにSSM State Managerで初期化した場合は、S3バケットにログを出力することができます。

まとめ

EC2インスタンスを初期化する4つの方法を確認しました。

ユーザーデータおよびcfn-initを使用する方法では、処理の対象となるインスタンスのローカルにログが設置されることになります。

SSM State ManagerではAnsible Playbookやシェルスクリプトで初期化することができ、S3バケットにログを出力することができます。

タイトルとURLをコピーしました