tail -f /dev/null

If you haven't had any obstacles lately, you're not challenging. be the worst.

AWS EC2 Image Builder (Windows) における Build component に癖がある

EC2 Image Builder とは

  • EC2 の Golden Image (AMI) を作成できるサービス
  • Image の更新 / 自動化 / テスト / スケジューリングが行える
  • Install する software や Test は YAML で独自に定義 (Build component)
  • SSM Agent により build, test が実施される
    • local system user NT AUTHORITY/Sysmte により
  • build, test は SSM Automation (30個以上の) document により成り立っている
    • 裏で AutoScaling が動作して build 用の EC2, test 用の EC2 が順次作成・破棄される
      • その instance に SSM session 経由で login して process や進捗を確かめることも可能
  • build, test が終わると sysprep が実行される
  • Administrator の user は存在するものの、profile が無い為 home directory は存在しない
    • login した時点で作成される
  • 今のところ Golden Image の配布までしか実施してくれない為、ASG / EC2 への反映は別途仕組みを作る必要がある

Windows OS の Image を作成する

例えば次のような SSM Agent, Chocolatey の install, Chocolatey 経由での awscli の install を行う step を記述したとする。

phases:
  - name: build
    steps
      - name: InstallLatestSSMAgent
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 2
        inputs:
          commands:
            - Invoke-WebRequest https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/windows_amd64/AmazonSSMAgentSetup.exe -OutFile C:\Windows\temp\SSMAgent_latest.exe
            - C:\Windows\temp\SSMAgent_latest.exe /S
            - Restart-Service AmazonSSMAgent

      - name: InstallChocolaty
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 2
        inputs:
          commands:
            - iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

      - name: InstallAWSCLI
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 2
        inputs:
          commands:
            - choco install -y awscli

Exit code was '1618' Another installation currently in progress. Try again later.. のような error を SSM Automation が出力する (※1.)。

Progress: 100% - Completed download of C:\Windows\TEMP\chocolatey\awscli\1.17.9\AWSCLI64PY3-1.17.9.msi (21.48 MB).
Download of AWSCLI64PY3-1.17.9.msi (21.48 MB) completed.
Hashes match.
Installing awscli...
WARNING: Another installation currently in progress. Try again later.
ERROR: Running ["C:\Windows\System32\msiexec.exe" /i "C:\Windows\TEMP\chocolatey\awscli\1.17.9\AWSCLI64PY3-1.17.9.msi" /qn /norestart /l*v "C:\Windows\TEMP\chocolatey\awscli.1.17.9.MsiInstall.log" ] was not successful. Exit code was '1618'. Exit code indicates the following: Another installation currently in progress. Try again later..
  awscli may be able to be automatically uninstalled.
The install of awscli was NOT successful.
Error while running 'C:\ProgramData\chocolatey\lib\awscli\tools\chocolateyinstall.ps1'.
 See log for details.
Chocolatey installed 0/1 packages. 1 packages failed.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

これは、別で何らかの installer や MSIExecute Mutex (Windows Installer) 等が動いている場合に出力される error である。個々の build step が独立した session では無いことを指し示している。

これを考慮して各々の step を process で独立・待機させる為 Start-Process -Wait を利用する。

追記: Packer でも同事象が発生し Chocolaty の issue 1616にある通り、Chocolaty での install が不安定なのは MSI installer に対し Choco が制御を渡し、 background で MSI が install されている時に MSI がすぐレスポンスを返すことで Choco が終了したと判断し、次の処理へ移ってしまっている模様。直接 MSI 経由で install を行うのが良さそう。

          commands:
            - Invoke-WebRequest https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/windows_amd64/AmazonSSMAgentSetup.exe -OutFile C:\Windows\temp\SSMAgent_latest.exe
            - Start-Process -FilePath C:\Windows\temp\SSMAgent_latest.exe -ArgumentList "/S" -NoNewWindow -Wait
            - Restart-Service AmazonSSMAgent

          commands:
            - Start-Process 'choco' -ArgumentList "install -y awscli" -NoNewWindow -Wait

この状態で This command cannot be run due to the error: The system cannot find the file specified. の error に遭遇する。

Debug Start executing command C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe [-InputFormat None -Noninteractive -NoProfile -ExecutionPolicy unrestricted -f C:\Windows\TEMP\AWSTOE\script-000.ps1  ; exit $LASTEXITCODE] 
Error [func1 @ exechandler.go.40] Start-Process : This command cannot be run due to the error: The system cannot find the file specified.
At C:\Windows\TEMP\AWSTOE\script-000.ps1:1 char:1
+ Start-Process 'choco' -ArgumentList "install -y awscli" -NoNewWindow  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Start-Process], InvalidOperationException
    + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand

フルパスで exe を実行する必要があるのと、このまま aws command を別 step (同 session) で利用しようとすると command not found となる為、PATH を再読み込みする必要がある(これは Image Builder 関係なく)。

※ step で refreshenv, exit 等を行っても読み込めない。

          commands:
            - Start-Process 'C:\ProgramData\chocolatey\bin\choco.exe' -ArgumentList "install -y awscli" -NoNewWindow -Wait
            - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")

以下のように Chef を S3 から落として install する step があったとする。

      - name: DownloadChefClient
        action: S3Download
        timeoutSeconds: 60
        onFailure: Abort
        maxAttempts: 2
        inputs:
          - source: xxx/chef-windows.msi
            destination: C:\Windows\temp\chef-windows.msi

      - name: InstallChefClient
        action: ExecuteBinary
        timeoutSeconds: 300
        onFailure: Abort
        maxAttempts: 2
        inputs:
          path: 'C:\Windows\System32\msiexec.exe'
          arguments:
            - "{{ build.DownloadChefClient.inputs[0].destination }}"
            - "/qb"
            - "/i"

この定義の仕方だと install 時に execution timed out してしまう (何故だ...)。

Debug Start executing command C:\Windows\System32\msiexec.exe [C:\Windows\temp\chef-windows.msi /quiet] 
Error [ExecuteCommands @ exechandler.go.53] command execution timed out

こちらも以下のように ExecuteBinary を諦め process を独立させて wait するようにした。

      - name: InstallChefClient
        action: ExecutePowerShell
        timeoutSeconds: 300
        onFailure: Abort
        maxAttempts: 2
        inputs:
          commands:
            - Start-Process "msiexec" -ArgumentList "/qb /i {{ build.DownloadChefClient.inputs[0].destination }}" -NoNewWindow -Wait

Chef の recipe 実行時は次のように ruby.exe が not recognized とのことで、再度パスの読み込みを行った。

Error [func1 @ exechandler.go.40] '"ruby.exe"' is not recognized as an internal or external command, operable program or batch file.
      - name: ApplyChefRecipes
        action: ExecutePowerShell
        timeoutSeconds: 300
        onFailure: Abort
        maxAttempts: 2
        inputs:
          commands:
            - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
            - Start-Process "C:\opscode\chef\bin\chef-client.bat" -ArgumentList "-r 'role[xxx]'" -NoNewWindow -Wait

Packer で上記を実施すると、 EC2 Image Builder で行っている上記のようなことを行わず素直に Build が成功する為、やはりまだ不安定さが拭えない Image Builder よりも現状は枯れた技術を採用した方が良さそうだった (Windows OS に限っては)。

※1. user_data の出力は C:\ProgramData\Amazon\EC2-Windows\Launch\Log\UserdataExecution.log で確認が可能。