Amazon-Elastic-Container-Service

ECS構築してみた!

はじめに

簡単なECR、ECS(Fargate)の構築方法を記載して、自らの理解とどなたかの手助けになればと思い記事にしました。ビギナー向けの内容となっております。
※検証環境で作成し、既に存在しないリソースですので細部まで記載しております。

構成

パブリックサブネットに配置したALBから、ECS(ECRはプライベートリポジトリを使用)にブラウザからアクセスし、簡単なアプリと連携してRDSにデータを格納するといったコンテナ主体の構成となっております。

image.png

前提

知識を整理しておきます。
Elastic Container Registry(ECR)とは?・・・

Amazon Elastic Container Registry (Amazon ECR) は、セキュリティ、スケーラビリティ、信頼性を備えた AWS マネージドコンテナイメージレジストリサービスです。

ECRについての公式ドキュメントです。
AWS公式:Amazon Elastic Container Registry とは

Amazon Elastic Container Service(ECS)とは?・・・

Amazon Elastic Container Service (Amazon ECS) は、コンテナ化されたアプリケーションを簡単にデプロイ、管理、スケーリングできる、完全マネージド型のコンテナオーケストレーションサービスです。

ECSについての公式ドキュメントです。
AWS公式:Amazon Elastic Container Service とは

Fargateとは?・・・

AWS Fargate はAmazon ECSで使用できるテクノロジーであり、サーバーやAmazon EC2インスタンスの クラスターを管理することなくコンテナを実行できます。AWS Fargate を使用すると、コンテナを実行するために仮想マシンのクラスターをプロビジョニング、設定、スケールする必要がありません。

Fargateについての公式ドキュメントです。
AWS公式:Amazon ECS の AWS Fargate

用途等

コンテナをEC2上又はFargate上に設置する場合の利点不利点について理解しきれていない部分がありました。また、開発者からすると喜ばれるサービスでバージョンなどの環境を気にすることなく作業できるので、優れモノだなというイメージもありました。
実際構築するとミドルウェアの設定起動など工夫が必要で、サイズなどの制限があるなどEKSについても知る必要があるなと思いました。

Fargateはアプリケーションの部分のみの責任範囲(後述)となっております。
つまり、ミドルウェアやその他アプリケーション以外の部分のカスタマイズ性を考慮するとともに、規模の大小によっても判断が必要であると考えられます。

責任共有モデル

ECSを構築するにあたって、責任共有モデルについて一度確認しておきます。下表は特徴について概要を理解するために記載しております。

項目 EC2 (IaaS) Fargate (サーバーレス/PaaS)
カスタマイズ性 高い 低い
OS/ミドルウェア管理 ユーザーが担当 AWSがOSを管理、ミドルウェアはユーザーが管理
ユーザーの責任 広い 狭い
適したユースケース 高度なカスタマイズや特殊要件 小中規模ワークロード

細部はリンクをご覧ください。
AWS公式:責任共有モデル

AWS公式:Infrastructure as a Service (IaaS) とは何ですか?(IaaS,PaaS,SaaSの違い)

コスト

ECSを導入するにあたって気になるのがコストですね。一度確認しておきます。
※中略しているので、細部はリンクから確認が必要です。

Fargate が EC2 と比べて87%のコスト削減を実現できていることがわかります。一方、 EC2 のリソースが完全に使用されている場合、 EC2 起動タイプは 20% 以上ものコスト削減を実現

常時稼働させるサービスにおいてはEC2のコストパフォーマンスはそもそも高い。Fargateは、EC2と同じスペックであっても、常に稼働しているわけではないので数十%のコストを削減できる。

AWS公式:Infrastructure as a Service (IaaS) とは何ですか?(IaaS,PaaS,SaaSの違い)

構築

VPC

手順 詳細
VPCを選択 「VPCなど」を選択し、 VPCと関連リソースを一気に作成します。
パブリックサブネット 2つのパブリックサブネットを作成
プライベートサブネット 2つのプライベートサブネットを作成
インターネットゲートウェイ インターネットゲートウェイを作成
NATゲートウェイ パブリックサブネットにNATゲートウェイを配置し、ルート設定を行います。
その他 デフォルト設定を使用

EC2

この手順通りに構築する必要はありません。一例として記載しております。改良しちゃいましょう!

項目 詳細
OS Amazon Linux 2023
配置サブネット パブリックサブネット
用途 イメージ作成用のローカルPCとして構築
セキュリティグループ SSH (ポート 22, 自分のIP)
- HTTP (ポート 80, 0.0.0.0)
- カスタム TCP (ポート 5000, 0.0.0.0)
キーペア 指定
その他 デフォルト設定で問題なし

アプリの作成に移ります。
Pythonがインストールされているか確認します。

python3 --version

出力例:

Python 3.9.20

pipがインストールされているか確認します。

pip3 --version

初期ではインストールされていないので、インストールしていきます。

sudo dnf install python3-pip -y

インストール完了を確認します。

pip3 --version

出力例

pip 21.3.1 from /usr/lib/python3.9/site-packages/pip (python 3.9)

Flaskがインストールされているか確認します。

pip3 show Flask

初期ではインストールされていないので、インストールします。

pip3 install Flask

インストールされたか確認します。

pip3 show Flask

出力例

Name: Flask
Version: 3.1.0
Summary: A simple framework for building complex web applications.
Home-page:
Author:
Author-email:
License:
Location: /home/ec2-user/.local/lib/python3.9/site-packages
Requires: blinker, click, importlib-metadata, itsdangerous, Jinja2, Werkzeug
Required-by:

MySQLクライアントをセットアップします。
インストールされているか確認します。

mysql --version

出力例

-bash: mysql: command not found

リポジトリをインストールします。

sudo dnf -y localinstall  https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm

リポジトリが有効化されているか確認します。

sudo dnf repolist enabled | grep mysql

出力例

mysql-connectors-community     MySQL Connectors Community
mysql-tools-community          MySQL Tools Community
mysql80-community              MySQL 8.0 Community Server

MySQLにGPGキーをインストールします(古い場合は任意にカスタマイズしましょう。)。

sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023

MySQLクライアントをインストールします。

sudo dnf -y install mysql mysql-community-client

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

mysql --version

出力例

mysql  Ver 8.0.41 for Linux on x86_64 (MySQL Community Server - GPL)

クライアントのみ構築して進めます。

Dockerをインストールします。

sudo dnf install -y docker

デーモンを起動します。

sudo systemctl start docker

自動起動を有効にします。

sudo systemctl enable docker

出力例

Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.

ステータスを確認しておきます。

sudo systemctl status docker

active (running)を確認しておきます。

バージョンも確認しておきます。

docker --version

出力例

Docker version 25.0.5, build 5dc9bcc

RDS

Aurora(MySQL)の場合は、RDSに比べてバージョン指定が異なる点や事前に決めることが多いものの基本的には以降の手順から構築できます。

Auroraについてもっと知りたい場合はリンクをご覧ください。
Aurora (MySQL)使ってみた。

手順 詳細
標準作成 -
エンジンタイプ MySQLを選択
バージョン デフォルトのMySQL 8.0.40を選択
利用枠 無料利用枠
DBインスタンス識別子 fargate
パスワード 任意
インスタンスタイプ db.t3.micro
ストレージ gp3でストレージ20で進める
EC2接続 EC2コンピューティングリソースに接続しない
VPC 作成したVPCを使用
パブリックアクセス なし
セキュリティグループ デフォルトのセキュリティグループを外し、作成したセキュリティグループを適用
最初のデータベース名 fargate

EC2からRDSへ接続

接続してみます。

mysql -h <RDSインスタンスのエンドポイント> -P 3306 -u <ユーザー名> -p

アクセスできたことを確認し、テーブルとカラムを作成します。
データベースを選択します。

USE fargate;

test_tableというテーブル及びnameというカラム(文字列)を作成します。

CREATE TABLE test_table (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255)
);

Query OKの出力を確認します。
そしてテーブルが作成されたか確認しておきます。

SHOW TABLES;

出力例

+-------------------+
| Tables_in_fargate |
+-------------------+
| test_table        |
+-------------------+
1 row in set (0.00 sec)

次に、カラムが作成されたことを確認します。

DESCRIBE test_table;

出力例

+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int          | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

ターゲットグループ

手順 詳細
セキュリティグループ HTTP (ポート 80) 、 カスタム TCP (ポート 5000) を開ける
ターゲットタイプ 「IPアドレス」
プロトコル及びポート HTTP (ポート 5000)
HTTPのバージョン HTTP1
ヘルスチェック デフォルト
ターゲット登録画面 設定せずに進める(以降で自動的にコンテナを指すようになる)
ポート(ターゲットへのルーティング) 5000

ALB

手順 詳細
スキーム インターネット向け
ターゲット 先程作成したターゲットグループを設定
リスナー HTTPS及びHTTPを作成し、HTTPSへリダイレクトするように設定
ポート 5000番でターゲットにアクセスするように設定

ディレクトリ構成

作成するディレクトリ構成を確認しておきます。
Amazon Linux 2023内でイメージを作成してコンテナを作成していきます。
ざっと確認します。
/my-flask-app
├── app.py
├── Dockerfile
├── requirements.txt
└── templates
   └ index.html

HTML

簡単なフロントを作成します。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Submit Name</title>
</head>
<body>
    <h1>Submit Your Name</h1>
    <form action="/submit" method="post">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name" required>
        <button type="submit">Submit</button>
    </form>
</body>
</html>

Python

from flask import Flask, request, render_template, redirect, url_for
import pymysql

app = Flask(__name__)

# RDSの接続情報を入力します
DB_HOST = 'your-rds-endpoint'
DB_USER = 'your-username'
DB_PASSWORD = 'your-password'
DB_NAME = 'fargate'
TABLE_NAME = 'test_table'

def get_db_connection():
    try:
        return pymysql.connect(
            host=DB_HOST,
            user=DB_USER,
            password=DB_PASSWORD,
            db=DB_NAME,
            charset='utf8mb4',
            cursorclass=pymysql.cursors.DictCursor
        )
    except pymysql.MySQLError as e:
        logging.error(f"Error connecting to the database: {e}")
        return None

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/submit', methods=['POST'])
def submit():
    name = request.form['name']
    conn = get_db_connection()
    if conn is None:
        return "Failed to connect to the database.", 500
    try:
        with conn.cursor() as cursor:
            sql = f"INSERT INTO {TABLE_NAME} (name) VALUES (%s)"
            cursor.execute(sql, (name,))
            conn.commit()
    except pymysql.MySQLError as e:
        logging.error(f"Error executing query: {e}")
        return "An error occurred while inserting the data.", 500
    finally:
        conn.close()
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Dockerfile

FROM python:3.9.20-slim

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

RUN apt-get update && apt-get install -y default-mysql-client

COPY . .

CMD ["python3", "app.py"]

requirements.txt

Flask
pymysql

ECR

手順 詳細
リポジトリ プライベートリポジトリ
リポジトリ名 任意
プッシュコマンド コンソールに表示されているコマンド

認証トークンを取得し、レジストリに対してDockerクライアントを認証します。
AWS CLIの設定を確認しておきましょう。
また、以降はroot権限(sudo su)に移行してコマンドを実行していきます。

aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <your_ID>.dkr.ecr.us-east-1.amazonaws.com

Login Succeededを確認して進めます。
ビルドします。

docker build -t fargate .

イメージが作成できたか確認します。

docker images

出力例

REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
fargate      latest    b0c627697f77   13 seconds ago   137MB

ここでコンテナが意図するように起動するか確認してみます。

docker run -d -p 5000:5000 fargate

プロセスが存在するか確認します。

docker ps

htmlファイルが表示されているか確認します。

curl http://localhost:5000

出力例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Submit Name</title>
</head>
<body>
    <h1>Submit Your Name</h1>
    <form action="/submit" method="post">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name" required>
        <button type="submit">Submit</button>
    </form>
</body>

コンテナを停止させておきましょう。
IDを確認します。

docker ps

表示されたIDを使用して停止させます。数秒かかります。

docker stop <コンテナID>

停止したか確認します。

docker ps

プロセスが表示されていないことを確認します。

次に早速、プッシュする準備を行っていきます。
イメージをプッシュできるようにタグ付けを行います。

docker tag fargate:latest <your_ID>.dkr.ecr.us-east-1.amazonaws.com/fargate:latest

イメージをプッシュします。

docker push <your_ID>.dkr.ecr.us-east-1.amazonaws.com/fargate:latest

ECS

手順 詳細
セキュリティグループ カスタムTCP (ポート 5000, ソース:ALBのセキュリティグループ) を許可
- HTTP (ポート 80, ソース:ALBのセキュリティグループ) を許可
- MySQL(ポート3306,ソース:RDSのセキュリティグループ)を許可
手順 詳細
タスク定義 コンソールで作成
OS Linux/X86_64
スペック CPU、メモリは最小(任意)
タスクロール、実行ロール ecsTaskExecutionRole
コンテナ名 fargate
イメージURI ECRコンソールからコピーペースト
コンテナポート 5000番
プロトコル TCP
ポート名 空欄(任意)
アプリケーションプロトコル http
モニタリング メトリクス収集を推奨
その他 デフォルト設定を使用
手順 詳細
デプロイボタン 「サービスの作成」
クラスター作成 -
インフラストラクチャ AWS Fargate
モニタリング(オプション) Container Insights(任意)
暗号化(オプション) KMS暗号化なし(任意)
コンピューティングオプション 起動タイプを選択
タスク数 任意
AZの配置 自動配置
デプロイサーキットブレーカー 有効
ネットワーキング 該当のVPC
サブネット プライベートサブネット
パブリックIP 無効
ロードバランシング 既存のALB、リスナーを指定

動作確認

ブラウザから検索すると次のようにアプリが起動します。
適当な文字列を入力してSubmitします。
アプリ起動例:
image.png

Amazon Linux 2023(ローカル)からMySQLに接続してデータが格納されたか確認します。

mysql -h <RDS_endpoint> -P 3306 -u <user> -p

データベースを選択します。

USE fargate;

レコードを見てみます。

SELECT * FROM test_table;

出力例

+----+-------+
| id | name  |
+----+-------+
|  1 | keiji |
+----+-------+
1 row in set (0.00 sec)

おわりに

最後まで読んでいただきありがとうございます。
冒頭にも記載しましたが、ECSを学んだ後はEKSを学習することで理解が深まるなと思いました。
では!

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA