AWS App2Container で既存アプリケーションをコンテナ化しよう

## はじめに
2020/7/1 に 既存の.NET および Java アプリケーションをコンテナ化するための
AWS App2Container というツールがリリースされました。

**AWS App2Container の発表 - アプリケーションをコンテナ化して AWS クラウドに移行する**
https://aws.amazon.com/jp/about-aws/whats-new/2020/07/announcing-aws-app2container/

AWS App2Container は、起動中アプリケーションとその依存関係をコンテナイメージに
パッケージ化してECS タスクと Kubernetes ポッド定義を生成します。
またこれらをAWS 上にデプロイするための CloudForamtion テンプレートおよび、
CI/CD パイプライン用のテンプレートまで作成することもできます。

AWS App2Container 自体は無料で利用可能で、デプロイする AWS リソースに対し
通常の利用料金が発生します。
Javaアプリケーションのコンテナ化、ECS on Fargate と EKS のデプロイをそれぞれ実施しました。

## 前提条件
Java アプリケーションの場合の環境の前提条件は以下の通りです。

サポートされる Java アプリケーションフレームワーク

* Tomcat
* Spring Boot
* JBoss (standalone mode)
* Weblogic (standalone mode)
* Websphere (standalone mode)

サポートされる Linux ディストリビューション

* Ubuntu
* CentOS
* RHEL
* Amazon Linux 

また作業環境が以下の条件を満たしている必要があります。
これらのインストール手順や設定手順は割愛します。

* AWS CLI がインストール済みであること
* Docker Engine のインストール済みであること
* サーバーの root アクセス権があること
* tar コマンドが利用可能であること
* ストレージに 20GB 以上の空き容量があること

IAM ユーザー必要な権限は以下のドキュメントに記載されています。
コンテナ化したアプリケーションをデプロイする際には、AdministratorAccess が
前提となっているようです。

**App2ContainerでのIDおよびアクセス管理**
https://docs.aws.amazon.com/app2container/latest/UserGuide/iam-a2c.html

今回は Amazon Linux 2 上に Spring Boot アプリケーションを起動し、検証しました。 

## アプリケーションの準備
Spring Boot CLI を使用して Hello World アプリケーションを準備します。
手順は以下に記載のある通りです。
https://spring.pleiades.io/spring-boot/docs/current/reference/html/spring-boot-cli.html

```hello.groovy
@RestController
class WebApplication {

    @RequestMapping("/")
    String home() {
        "Hello World!"
    }

}
```

アプリケーションを起動し、

```shell-session
$ spring run hello.groovy
```

正常に応答することを確認しておきます。

```shell-session
$ curl localhost:8080
Hello World!
```

## App2Container のインストール
### インストール
インストールパッケージをダウンロードし、展開します。

```shell-session
$ curl -o AWSApp2Container-installer-linux.tar.gz https://app2container-release-us-east-1.s3.us-east-1.amazonaws.com/latest/linux/AWSApp2Container-installer-linux.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  104M  100  104M    0     0  8360k      0  0:00:12  0:00:12 --:--:-- 9605k

$ sudo tar xvf AWSApp2Container-installer-linux.tar.gz
install.sh
README
termsandconditions.txt
security/
security/app2container.sig
security/app2container.cert
AWSApp2Container.tar.gz
```

install.sh を実行します。
利用規約への同意が必要です。

```shell-session
$ sudo ./install.sh

Determining user ...
Determining Installer Path ...
The AWS App2Container tool is licensed as "AWS Content" under the terms and conditions of the AWS Customer Agreement, located at https://aws.amazon.com/agreement and the Service Terms, located at https://aws.amazon.com/service-terms. By installing, using or accessing the AWS App2Container tool, you agree to such terms and conditions. The term "AWS Content" does not include software and assets distributed under separate license terms (such as code licensed under an open source license).
Do you accept the terms and conditions above? (y/n): y
Installing AWS App2Container ...
~~以降省略~~
Installation of AWS App2Container completed successfully!
You are currently running version 1.0.0.
To get started, run 'sudo app2container init'
AWS App2Container was installed under /usr/local/app2container/AWSApp2Container.
```

正常にインストールされたことを確認します。

```shell-session
$ sudo app2container --version
app2container version 1.0.0
```

### 初期設定
init コマンドで初期設定を行います。
内容は記載のとおりですが、コンテナ化に関連するアーティファクトの格納ディレクトリや
使用する AWS CLI のプロファイルなどが指定できます。
アーティファクトの格納先 S3 バケット(オプション) は App2Container で AWS 環境への
デプロイを行う場合は指定が必要です。

```shell-session
$ sudo app2container init
Workspace directory path for artifacts[default: /root/app2container]:
AWS Profile (configured using 'aws configure --profile')[default: default]:
Optional S3 bucket for application artifacts: <your_bucket_name>
Report usage metrics to AWS? (Y/N)[default: y]: N
Require images to be signed using Docker Content Trust (DCT)? (Y/N)[default: n]: N
Configuration saved
```

## アプリケーションのコンテナ化
### 既存アプリケーションの分析
inventory コマンドで実行中のアプリケーションを一覧表示します。
以下の例の場合、`java-generic-65424cc9` がアプリケーションIDとなり、
後続のコマンド実行時に指定が必要です。

```shell-session
$ sudo app2container inventory
{
                "java-generic-65424cc9": {
                                "processId": 2493,
                                "cmdline": "/usr/lib/jvm/java-11-openjdk-11.0.7.10-4.amzn2.0.1.x86_64/bin/java ... /usr/local/bin:/usr/local/lib/spring-boot-cli-2.3.1.RELEASE.jar org.springframework.boot.loader.JarLauncher run ./hello.groovy ",
                                "applicationType": "java-generic"
                }
}
```

analyze コマンドを実行すると、init 時に指定したアーティファクトディレクトリに
analysis.json が保存されます。アプリケーション実行のための依存関係が記述されており、
必要に応じて手動で値を書き換えることもできます。

```shell-session
$ sudo app2container analyze --application-id java-generic-65424cc9
✔ Created artifacts folder /root/app2container/java-generic-65424cc9
✔ Generated analysis data in /root/app2container/java-generic-65424cc9/analysis.json
👍 Analysis successful for application java-generic-65424cc9

💡 Next Steps:
1. View the application analysis file at /root/app2container/java-generic-65424cc9/analysis.json.
2. Edit the application analysis file as needed.
3. Start the containerization process using this command: app2container containerize --application-id java-generic-65424cc9
```

<details><summary>analysis.json の例 (クリックで展開)</summary>
<div>

```analysis.json
{
       "a2CTemplateVersion": "1.0",
       "createdTime": "2020-07-04 09:31:254",
       "containerParameters": {
              "_comment1": "*** EDITABLE: The below section can be edited according to the application requirements. Please see the analysisInfo section below for details discovered regarding the application. ***",
              "imageRepository": "java-generic-65424cc9",
              "imageTag": "latest",
              "containerBaseImage": "amazonlinux:2",
              "appExcludedFiles": [],
              "appSpecificFiles": [],
              "applicationMode": false,
              "logLocations": [],
              "enableDynamicLogging": false,
              "dependencies": []
       },
       "analysisInfo": {
              "_comment2": "*** NON-EDITABLE: Analysis Results ***",
              "processId": 2493,
              "appId": "java-generic-65424cc9",
              "userId": "1001",
              "groupId": "1001",
              "cmdline": [
                     "/usr/lib/jvm/java-11-openjdk-11.0.7.10-4.amzn2.0.1.x86_64/bin/java",
                     "-cp",
                     "/usr/local/bin:/usr/local/lib/spring-boot-cli-2.3.1.RELEASE.jar",
                     "org.springframework.boot.loader.JarLauncher",
                     "run",
                     "./hello.groovy"
              ],
              "osData": {
                     "ANSI_COLOR": "0;33",
                     "CPE_NAME": "cpe:2.3:o:amazon:amazon_linux:2",
                     "HOME_URL": "https://amazonlinux.com/",
                     "ID": "amzn",
                     "ID_LIKE": "centos rhel fedora",
                     "NAME": "Amazon Linux",
                     "PRETTY_NAME": "Amazon Linux 2",
                     "VERSION": "2",
                     "VERSION_ID": "2"
              },
              "osName": "amzn",
              "ports": [
                     {
                            "localPort": 8080,
                            "protocol": "tcp6"
                     }
              ],
              "Properties": {
                     "classpath": "/usr/local/bin:/usr/local/lib/spring-boot-cli-2.3.1.RELEASE.jar",
                     "jdkVersion": "11.0.7"
              },
              "AdvancedAppInfo": null,
              "env": {
                     "HOME": "/home/ssm-user",
                     "JAVA_HOME": "/usr/lib/jvm/java-11-openjdk-11.0.7.10-4.amzn2.0.1.x86_64",
                     "LANG": "C.UTF-8",
                     "OLDPWD": "/usr/local",
                     "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin",
                     "PWD": "/home/ssm-user",
                     "SHLVL": "1",
                     "SPRING_HOME": "/usr/local",
                     "TERM": "xterm-256color"
              },
              "cwd": "/home/ssm-user",
              "procUID": {
                     "euid": "1001",
                     "suid": "1001",
                     "fsuid": "1001",
                     "ruid": "1001"
              },
              "procGID": {
                     "egid": "1001",
                     "sgid": "1001",
                     "fsgid": "1001",
                     "rgid": "1001"
              },
              "userNames": {
                     "1001": "ssm-user"
              },
              "groupNames": {
                     "1001": "ssm-user"
              },
              "fileDescriptors": [
                     "/dev/pts/8",
                     "/dev/pts/8",
                     "/dev/pts/8",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter/2.3.1.RELEASE/spring-boot-starter-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot/2.3.1.RELEASE/spring-boot-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-context/5.2.7.RELEASE/spring-context-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.3.1.RELEASE/spring-boot-autoconfigure-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.3.1.RELEASE/spring-boot-starter-logging-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar",
                     "/usr/lib/jvm/java-11-openjdk-11.0.7.10-4.amzn2.0.1.x86_64/lib/modules",
                     "/home/ssm-user/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar",
                     "/home/ssm-user/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar",
                     "/home/ssm-user/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar",
                     "/home/ssm-user/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar",
                     "/home/ssm-user/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar",
                     "/home/ssm-user/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-core/5.2.7.RELEASE/spring-core-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-jcl/5.2.7.RELEASE/spring-jcl-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/yaml/snakeyaml/1.26/snakeyaml-1.26.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.3.1.RELEASE/spring-boot-starter-web-2.3.1.RELEASE.jar",
                     "/usr/local/lib/spring-boot-cli-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.3.1.RELEASE/spring-boot-starter-json-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.0/jackson-databind-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.0/jackson-annotations-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.0/jackson-core-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.0/jackson-datatype-jdk8-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.0/jackson-datatype-jsr310-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.0/jackson-module-parameter-names-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.3.1.RELEASE/spring-boot-starter-tomcat-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.36/tomcat-embed-core-9.0.36.jar",
                     "/home/ssm-user/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar",
                     "/home/ssm-user/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.36/tomcat-embed-websocket-9.0.36.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-web/5.2.7.RELEASE/spring-web-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-beans/5.2.7.RELEASE/spring-beans-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-webmvc/5.2.7.RELEASE/spring-webmvc-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-aop/5.2.7.RELEASE/spring-aop-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-expression/5.2.7.RELEASE/spring-expression-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/codehaus/groovy/groovy-templates/2.5.12/groovy-templates-2.5.12.jar",
                     "/home/ssm-user/.m2/repository/org/codehaus/groovy/groovy/2.5.12/groovy-2.5.12.jar",
                     "/home/ssm-user/.m2/repository/org/codehaus/groovy/groovy/2.5.12/groovy-2.5.12.jar",
                     "/home/ssm-user/.m2/repository/org/codehaus/groovy/groovy-xml/2.5.12/groovy-xml-2.5.12.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-context/5.2.7.RELEASE/spring-context-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot/2.3.1.RELEASE/spring-boot-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.3.1.RELEASE/spring-boot-autoconfigure-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-beans/5.2.7.RELEASE/spring-beans-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-web/5.2.7.RELEASE/spring-web-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter/2.3.1.RELEASE/spring-boot-starter-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.3.1.RELEASE/spring-boot-starter-logging-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar",
                     "/home/ssm-user/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar",
                     "/home/ssm-user/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar",
                     "/home/ssm-user/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar",
                     "/home/ssm-user/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar",
                     "/home/ssm-user/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-core/5.2.7.RELEASE/spring-core-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-jcl/5.2.7.RELEASE/spring-jcl-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/yaml/snakeyaml/1.26/snakeyaml-1.26.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.3.1.RELEASE/spring-boot-starter-web-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.3.1.RELEASE/spring-boot-starter-json-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.0/jackson-databind-2.11.0.jar",
                     "/usr/local/lib/spring-boot-cli-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.0/jackson-annotations-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.0/jackson-core-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.0/jackson-datatype-jdk8-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.0/jackson-datatype-jsr310-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.0/jackson-module-parameter-names-2.11.0.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.3.1.RELEASE/spring-boot-starter-tomcat-2.3.1.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.36/tomcat-embed-core-9.0.36.jar",
                     "/home/ssm-user/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar",
                     "/home/ssm-user/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.36/tomcat-embed-websocket-9.0.36.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-webmvc/5.2.7.RELEASE/spring-webmvc-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-aop/5.2.7.RELEASE/spring-aop-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/springframework/spring-expression/5.2.7.RELEASE/spring-expression-5.2.7.RELEASE.jar",
                     "/home/ssm-user/.m2/repository/org/codehaus/groovy/groovy-templates/2.5.12/groovy-templates-2.5.12.jar",
                     "/home/ssm-user/.m2/repository/org/codehaus/groovy/groovy-xml/2.5.12/groovy-xml-2.5.12.jar"
              ],
              "dependencies": {}
       }
```

</div>
</details>

### コンテナ化
containerize コマンドでアプリケーションをコンテナ化できます。
この際、AWS CLI に適切なクレデンシャル情報が設定されている必要があります。
(sudo で実行する場合、root ユーザーに設定されたプロファイル)
現在のところ、EC2 でインスタンスプロファイルを設定している場合でも、IAM ロールの
一時クレデンシャルは使用されないようですので、アクセスキーを別途設定しています。

```shell-session
$ sudo app2container containerize --application-id java-generic-65424cc9
✔ AWS prerequisite check succeeded
✔ Docker prerequisite check succeeded
✔ Extracted container artifacts for application
✔ Entry file generated
✔ Dockerfile generated under /root/app2container/java-generic-65424cc9/Artifacts
✔ Generated dockerfile.update under /root/app2container/java-generic-65424cc9/Artifacts
✔ Generated deployment file at /root/app2container/java-generic-65424cc9/deployment.json
👍 Containerization successful. Generated docker image java-generic-65424cc9

💡 You're all set to test and deploy your container image.

Next Steps:
1. View the container image with "docker images" and test the application.
2. When you're ready to deploy to AWS, please edit the deployment file as needed at /root/app2container/java-generic-65424cc9/deployment.json.
3. Generate deployment artifacts using "app2container generate app-deployment --application-id java-generic-65424cc9"
```

containerize コマンドにより generate app-deployment コマンド実行時に必要な
deployment.json や Dockerfile、コンテナイメージなどが生成されます。

```shell
$ sudo tree /root/app2container/java-generic-65424cc9/
/root/app2container/java-generic-65424cc9/
├── Artifacts
│   ├── ContainerFiles.tar ★アプリケーション実行の依存関係をまとめた tar
│   ├── Dockerfile 
│   ├── Dockerfile.update ★ 作成されたイメージをベースイメージとした Dockerfile のサンプル
│   ├── entryfile ★ コンテナ起動時の CMD 
│   └── excludedFiles ★ コンテナ化時に除外されたファイルのリスト
├── analysis.json
└── deployment.json
```

<details><summary>Dockerfile の例  (クリックで展開)</summary>
<div>

```Dockerfile
FROM amazonlinux:2
MAINTAINER AWS
WORKDIR /
# Copying Entryfile
COPY entryfile /entryfile
# Pre install packages
RUN yum -y install tar && yum -y install gzip && yum -y install shadow-utils.x86_64
# Adding and unpacking Tar file
COPY ContainerFiles.tar /
RUN tar xvfP /ContainerFiles.tar --directory / --skip-old-files --same-owner --ignore-failed-read && rm -rf /ContainerFiles.tar
# Follow the below example to update files# COPY ["generic_config_file", "/root/app2container/java-generic-65424cc9/Artifacts/generic_config_file"]# Environment Variables
ENV HOME /home/ssm-user
ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-11.0.7.10-4.amzn2.0.1.x86_64
ENV PWD /home/ssm-user
ENV TERM xterm-256color
ENV LANG C.UTF-8
ENV OLDPWD /usr/local
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
ENV SHLVL 1
ENV SPRING_HOME /usr/local
# Exposing ports
EXPOSE 8080
# User and user group
RUN groupadd -f -r -g 1001 ssm-user
RUN id -u ssm-user > /dev/null 2>&1 || useradd -r -g 1001 -u 1001 ssm-user
USER ssm-user:ssm-user
WORKDIR /home/ssm-user
# Image Entrypoint
CMD /entryfile
```

</div>
</details>

deployment.json についても 手動で編集することが可能です。
ECS のパラメーターについて、デフォルトで cpu:2, memory:4096 が設定されており、
Fargate タスク実行時のリソースとして確保されるようですので、必要に応じて変更すべきかと思います。


<details><summary>deployment.json の例  (クリックで展開)</summary>
<div>

```deployment.json
{
       "a2CTemplateVersion": "1.0",
       "applicationId": "java-generic-65424cc9",
       "imageName": "java-generic-65424cc9",
       "exposedPorts": [
              {
                     "localPort": 8080,
                     "protocol": "tcp6"
              }
       ],
       "environment": [],
       "ecrParameters": {
              "ecrRepoTag": "latest"
       },
       "ecsParameters": {
              "createEcsArtifacts": true,
              "ecsFamily": "java-generic-65424cc9",
              "cpu": 2,
              "memory": 4096,
              "dockerSecurityOption": "",
              "enableCloudwatchLogging": false,
              "publicApp": true,
              "stackName": "a2c-java-generic-65424cc9-ECS",
              "reuseResources": {
                     "vpcId": "",
                     "cfnStackName": "",
                     "sshKeyPairName": ""
              },
              "gMSAParameters": {
                     "domainSecretsArn": "",
                     "domainDNSName": "",
                     "domainNetBIOSName": "",
                     "createGMSA": false,
                     "gMSAName": ""
              }
       },
       "eksParameters": {
              "createEksArtifacts": false,
              "stackName": "a2c-java-generic-65424cc9-EKS",
              "reuseResources": {
                     "vpcId": "",
                     "cfnStackName": "",
                     "sshKeyPairName": ""
              }
       }
}[
```

</div>
</details>

この時点で コンテナイメージが作成されているため、ローカルで実行して検証することもできます。
現在稼働中の環境を再現するためだとはおもいますが、なかなかのイメージサイズになってます。

```shell-session
$ sudo docker image ls
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
java-generic-65424cc9   latest              d7843b85b3b9        About an hour ago   3.95GB
amazonlinux             2                   fa0a6a710ca7        3 days ago          163MB

$ sudo docker run --rm -d -p 8080:8080 java-generic-65424cc9:latest
34e6266424e130f0b6fcb6213547cc7b744123b24805db37bea3e4da9edc094c

$ sudo docker ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS              PORTS                    NAMES
34e6266424e1        java-generic-65424cc9:latest   "/bin/sh -c /entryfi…"   6 seconds ago       Up 5 seconds        0.0.0.0:8080->8080/tcp   vigilant_lederberg

$ curl http://localhost:8080
Hello World!
```

## アプリケーションのデプロイ
### デプロイ用リソース作成
generate app-deployment コマンドで、アプリケーションを AWS にデプロイするための
CloudFormation テンプレート等の各種リソースが生成されます。
`--deploy` オプションを付与することで、そのままデプロイまで実行できますが、
ここではまずはアーティファクトの生成のみを実行します。

```shell-session
$ sudo app2container generate app-deployment --application-id java-generic-65424cc9
✔ AWS prerequisite check succeeded
✔ Docker prerequisite check succeeded
✔ Created ECR Repository
✔ Registered ECS Task Definition with ECS
✔ Uploaded CloudFormation resources to S3 Bucket: <your_bucket_name>
✔ Generated CloudFormation Master template at: /root/app2container/java-generic-65424cc9/EcsDeployment/ecs-master.yml
👍 ECS CloudFormation templates and additional deployment artifacts generated successfully for application java-generic-65424cc9

💡 You're all set to use AWS CloudFormation to manage your application stack.

Next Steps:
1. Edit the CloudFormation template as necessary.
2. Create an application stack using the AWS CLI or the AWS Console. AWS CLI command:

        aws cloudformation deploy --template-file /root/app2container/java-generic-65424cc9/EcsDeployment/ecs-master.yml --capabilities CAPABILITY_NAMED_IAM --stack-name a2c-java-generic-65424cc9-ECS

3. Set up a pipeline for your application stack using "app2container:

        app2container generate pipeline --application-id java-generic-65424cc9"
```

実行が成功すると、アーティファクトディレクトリに ECS をデプロイするための
タスク定義、CloudFormation のマスターテンプレート、パイプライン用定義ファイルが配置されます。
EKS 用のリソースはデフォルトでは作成されませんので、記事後半を参照ください。

CloudFormation ではネストされたスタックが生成されるため、残りのテンプレートは init 時に
指定した S3 バケットにアップロードされています。

```shell-session
$ sudo tree /root/app2container/java-generic-65424cc9/
/root/app2container/java-generic-65424cc9/
├── Artifacts
│   ├── ContainerFiles.tar
│   ├── Dockerfile
│   ├── Dockerfile.update
│   ├── entryfile
│   └── excludedFiles
├── EcsDeployment
│   └── ecs-master.yml ★ CloudForamtion テンプレート
├── analysis.json
├── deployment.json
├── pipeline.json ★ パイプライン作成用定義ファイル
└── taskDef.json ★ ECS用タスク定義

$ aws s3 ls s3://<your_bucket_name>/a2c-<application-id>/ecs/subtemplates/
2020-07-04 11:58:23      12258 ecs-cluster.yml
2020-07-04 11:58:23       2327 ecs-dns.yml
2020-07-04 11:58:24      16521 ecs-gmsa-automation-doc.yml
2020-07-04 11:58:24       2545 ecs-gmsa-execute.yml
2020-07-04 11:58:24       7216 ecs-gmsa-iam-roles.yml
2020-07-04 11:58:24       6112 ecs-gmsa-lambda-functions.yml
2020-07-04 11:58:24       3713 ecs-gmsa.yml
2020-07-04 11:58:24      16909 ecs-lb-webapp.yml
2020-07-04 11:58:24      19955 ecs-master.yml
2020-07-04 11:58:24      10882 ecs-private-app.yml
2020-07-04 11:58:24       9586 ecs-public-load-balancer.yml
2020-07-04 11:58:24       3377 ecs-vpc.yml
```

また、この時点で ECS へのタスク定義登録および、ECR へのイメージ push が自動で行われています。

### ECS on Fargate へのデプロイ
generate app-deployment コマンドで生成された ecs-master.yml を使用してデプロイします。

```shell-session
$ sudo aws cloudformation deploy --template-file /root/app2container/java-generic-65424cc9/EcsDeployment/ecs-master.yml --capabilities CAPABILITY_NAMED_IAM --stack-name a2c-java-generic-65424cc9-ECS

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - a2c-java-generic-65424cc9-ECS
```

作成された ELB にリクエストを投げると、メッセージが返ってきました。

```shell-session
$ aws cloudformation describe-stacks --stack-name a2c-java-generic-65424cc9-ECS --query 'Stacks[].Outputs[2]'
[
    {
        "ExportName": "a2c-java-generic-65424cc9-ECS-PublicLoadBalancerDNSForCLIOutput",
        "OutputKey": "PublicLoadBalancerDNSName",
        "OutputValue": "a2c-j-Publi-XXXXXXXXXXXX-111111111.ap-northeast-1.elb.amazonaws.com"
    }
]

$ curl http://a2c-j-Publi-XXXXXXXXXXXX-111111111.ap-northeast-1.elb.amazonaws.com
Hello World!
```

コンテナを前提に作成されたアプリケーションと比較すると、事前の十分な検証が必要で
運用面等々の課題もあるかもしれませんが
数ステップでコンテナ化～ECS on Fargate へのデプロイまで行えるのは非常に魅力的です。

## CI/CD パイプラインの作成
### デプロイ用リソースの作成
generate pipeline を使用すると、AWS CodePipeline で CI/CD パイプラインを作成するために
必要なアーティファクトを生成することができます。
app-deployment と同様に `--deploy` オプションを使用することもできます。

generate pipline を実行するには、generate app-deployment で生成された pipeline.json の
releaseInfo に デプロイされた ECS のクラスタ名とサービス名を追記する必要があります。

```pipeline.json
{
        "a2CTemplateVersion": "1.0",
        "sourceInfo": {
                "CodeCommit": {
                        "repositoryName": "a2c-java-generic-65424cc9-ecs",
                        "branch": "master"
                }
        },
        "imageInfo": {
                "image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/java-generic-65424cc9:latest"
        },
        "releaseInfo": {
                "ECS": {
                        "beta": {
                                "clusterName": "",
                                "serviceName": "",
                                "enabled": false
                        },
                        "prod": {
                                "clusterName": "a2c-java-generic-65424cc9-ECS-Cluster",
                                "serviceName": "a2c-java-generic-65424cc9-ECS-LBWebAppStack-XXXXXXXXXXXX-Service-YYYYYYYYYYYY",
                                "enabled": true
                        }
                }
        }
}
```

ここでは prod に先ほどデプロイしたリソースを指定しています。
generate pipeline を実行すると、CodePipline をデプロイするための
CloudFormation と CodeDeploy 用の buildspec.ymlテンプレートが作成されます。


```shell-session
$ sudo app2container generate pipeline --application-id java-generic-65424cc9
✔ Created CodeCommit repository
✔ Generated buildspec file(s)
✔ Generated CloudFormation templates
✔ Committed files to CodeCommit repository
👍 Pipeline resource template generation successful for application java-generic-65424cc9

💡 You're all set to use AWS CloudFormation to manage your pipeline stack.

Next Steps:
1. Edit the CloudFormation template as necessary.
2. Create a pipeline stack using the AWS CLI or the AWS Console. AWS CLI command:

        aws cloudformation deploy --template-file /root/app2container/java-generic-65424cc9/Artifacts/Pipeline/CodePipeline/ecs-pipeline-master.yml --capabilities CAPABILITY_NAMED_IAM --stack-name a2c-java-generic-65424cc9-ecs-pipeline-stack

$ sudo tree /root/app2container/java-generic-65424cc9/
/root/app2container/java-generic-65424cc9/
├── Artifacts
│   ├── ContainerFiles.tar
│   ├── Dockerfile
│   ├── Dockerfile.update
│   ├── Pipeline
│   │   └── CodePipeline
│   │       ├── Dockerfile
│   │       ├── buildspec.yml
│   │       └── ecs-pipeline-master.yml
│   ├── entryfile
│   └── excludedFiles
├── EcsDeployment
│   └── ecs-master.yml
├── analysis.json
├── deployment.json
├── pipeline.json
└── taskDef.json
```

### パイプラインのデプロイ
ecs-pipeline-master.yml を使用してデプロイします。
こちらもネストされたスタックとしてデプロイされます。

```
$ sudo aws cloudformation deploy --template-file /root/app2container/java-generic-65424cc9/Artifacts/Pipeline/CodePipeline/ecs-pipeline-master.yml --capabilities CAPABILITY_NAMED_IAM --stack-name a2c-java-generic-65424cc9-ecs-pipeline-stack

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - a2c-java-generic-65424cc9-ecs-pipeline-stack
```

これにより、Dockerfile および buildspec.yml が CodeCommit で管理された状態となり、
レポジトリへの変更を検知してパイプラインが起動します。
CodeBuild で コンテナイメージの Build & Push および、タスク定義の編集を行い、
ECS クラスターへのデプロイを行なう、パイプラインを簡単に作成することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/66b87d68-e6bd-e1e9-ce29-a21732925e0a.png)

## EKS のデプロイ
### EKS デプロイ用アーティファクトの作成
EKS 関連のリソースを作成するには containerize コマンドで作成された deployment.json の 
eksParameters を編集し、createEksArtifacts を true に変更する必要があります。

```
       "eksParameters": {
              "createEksArtifacts": true,
              "stackName": "a2c-java-generic-65424cc9-EKS",
              "reuseResources": {
                     "vpcId": "",
                     "cfnStackName": "",
                     "sshKeyPairName": ""
              }
       }
```

再度、generate app-delopyment コマンドを実行します。
pipeline.json は上書きされてしまうので、ご注意ください。

```shell-session
$ sudo app2container generate app-deployment --application-id java-generic-65424cc9
✔ AWS prerequisite check succeeded
✔ Docker prerequisite check succeeded
✔ Created ECR Repository
✔ Uploaded CloudFormation resources to S3 Bucket: <your_bucket_name>
✔ Generated CloudFormation Master template at: /root/app2container/java-generic-65424cc9/EksDeployment/amazon-eks-master.template.yaml
👍 EKS CloudFormation templates and additional deployment artifacts generated successfully for application java-generic-65424cc9

💡 You're all set to use AWS CloudFormation to manage your application stack.You're all set to use AWS CloudFormation to manage your application stack.

Next Steps:
1. Edit the CloudFormation template as necessary.
2. Create an application stack using the AWS CLI or the AWS Console. AWS CLI command:

        aws cloudformation deploy --template-file /root/app2container/java-generic-65424cc9/EksDeployment/amazon-eks-master.template.yaml --capabilities CAPABILITY_NAMED_IAM --stack-name a2c-java-generic-65424cc9-EKS

3. Setup a pipeline for your application stack using app2container:

        app2container generate pipeline --application-id java-generic-65424cc9

```

EKS クラスターデプロイ用の CloudFormation テンプレートと deployment と service を作成する
ための マニフェストファイル が作成されます。namespace が default だったりするので
こちらも必要に応じてカスタマイズします。

```shell-session
$ sudo tree /root/app2container/java-generic-65424cc9/
/root/app2container/java-generic-65424cc9/
├── Artifacts
│   ├── ContainerFiles.tar
│   ├── Dockerfile
│   ├── Dockerfile.update
│   ├── Pipeline
│   │   └── CodePipeline
│   │       ├── Dockerfile
│   │       ├── buildspec.yml
│   │       └── ecs-pipeline-master.yml
│   ├── entryfile
│   └── excludedFiles
├── EcsDeployment
│   └── ecs-master.yml
├── EksDeployment ★
│   ├── amazon-eks-master.template.yaml
│   └── java-generic-65424cc9.pem
├── analysis.json
├── deployment.json
├── eks_deployment.yaml ★
├── eks_service.yaml ★
├── pipeline.json
└── taskDef.json
```

### EKS クラスターへのデプロイ
amazon-eks-master.template.yaml を使用して、EKS クラスターをデプロイします。
そこそこ時間がかかります。

```shell-session
$ sudo aws cloudformation deploy --template-file /root/app2container/java-generic-65424cc9/EksDeployment/amazon-eks-master.template.yaml --capabilities CAPABILITY_NAMED_IAM --stack-name a2c-java-generic-65424cc9-EKS

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - a2c-java-generic-65424cc9-EKS
````

EKS についても CI/CD パイプラインを作成することができますが、
手順は ECS と同様であるため割愛します。

## 参考
**App2Container Documenet**
https://docs.aws.amazon.com/app2container/latest/UserGuide/what-is-a2c.html


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