Apache/Python(uWSGI)/RDS(Aurora)で3層アーキテクチャ
簡単なWebアプリを構築することを考えます。
アプリは以下の3層構成とします。
- Web層:ALB・Webサーバ(Auto Scaling)
- アプリ層:NLB・アプリサーバ(Auto Scaling)
- DB層:RDS(Aurora)
作成するアプリケーションは日時情報を保存・表示するアプリです。
クライアントからアクセスを受けた日時を保存するページと、保存されている日時の一覧を返すページを作成します。
構築する環境
プライベートサブネット内にWebサーバ用のAuto Scalingを作成します。
Auto Scalingグループ内に作成されるEC2インスタンスは、最新版のAmazon Linux 2とします。
グループ内のインスタンスは、Apacheをインストールすることで、Webサーバとして動作させます。
Auto ScalingグループにALBをアタッチします。
プライベートサブネット内にアプリサーバ用のAuto Scalingを作成します。
こちらのグループ内に作成されるインスタンスも、最新版のAmazon Linux 2とします。
インスタンスはPython(uWSGI)でアプリサーバとして動作させます。
Auto ScalingグループにNLBをアタッチします。
プライベートサブネットにRDS(Aurora)を作成します。
AuroraはDBサーバとして動作します。
AuroraはMySQLタイプとします。
CloudFormationカスタムリソースでAuroraの初期化処理を行います。
具体的にはカスタムリソースにLambda関数を関連付けて、DB内にテーブル作成を実行します。
パブリックサブネットにNATゲートウェイを配置します。
各種サーバの構築時に、インターネットにアクセスし、セットアップに必要なパッケージをダウンロードするために使用します。
クライアントと各種サーバ間の通信を整理します。
- クライアント -> ALB -> Webサーバ:HTTP(80/tcp)
- Webサーバ -> NLB -> アプリサーバ:UNIX Domain Socket(9090/tcp)
- アプリサーバ -> DBサーバ:MySQL(3306/tcp)
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-fa/tree/main/094
テンプレートファイルのポイント解説
ALB
今回の構成では、ALBをプライベートサブネット内のAuto Scalingグループにアタッチします。
こちらの詳細につきましては、以下のページをご確認ください。
セキュリティグループ
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-ALBSecurityGroup"
GroupDescription: Allow HTTP Only.
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref HTTPPort
ToPort: !Ref HTTPPort
CidrIp: 0.0.0.0/0
Code language: YAML (yaml)
クライアントからWebサーバへの通信は、不特定多数のグローバルアドレスからHTTP(80/tcp)で行われます。
ですからこれを許可するようにルールを定義します。
Webサーバ
Auto Scalingグループ
Resources:
LaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
IamInstanceProfile:
Arn: !Ref InstanceProfileArn
ImageId: !Ref InstanceImageId
InstanceType: !Ref InstanceType
SecurityGroupIds:
- !Ref WebSecurityGroup
TagSpecifications:
- ResourceType: instance
Tags:
- Key: !Ref InstanceTagKey
Value: !Ref InstanceTagValueWeb
LaunchTemplateName: !Sub "${Prefix}-LaunchTemplate-Web"
Code language: YAML (yaml)
Auto Scalingグループ内にWebサーバを配置します。
今回はスケーリングポリシーなしのAuto Scalingグループを構成します。
こちらに関する詳細は、以下のページをご確認ください。
Auto Scalingグループ用の起動テンプレートですが、ポイントはタグ設定です。
以下の通り、タグを設定します。
- キー:Server
- 値:ApacheWeb
これは後述するインスタンスの初期化処理時に、SSMドキュメントを使用するのですが、その際にタグ情報を使用して、対象インスタンスを区別するためです。
セキュリティグループ
Resources:
WebSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-WebSecurityGroup"
GroupDescription: Allow HTTP from ALBSecurityGroup.
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref HTTPPort
ToPort: !Ref HTTPPort
SourceSecurityGroupId: !Ref ALBSecurityGroup
Code language: YAML (yaml)
クライアントからALBに向けられたHTTPトラフィックは、Webサーバにルーティングされます。
トラフィックの送信元はALBからとなりますので、ALBのセキュリティグループを指定します。
SSM関連付け
Resources:
WebServerAssociation:
Type: AWS::SSM::Association
Properties:
AssociationName: !Sub "${Prefix}-run-shellscript-association"
Name: AWS-RunShellScript
OutputLocation:
S3Location:
OutputS3BucketName: !Ref PlaybookBucket
OutputS3KeyPrefix: !Sub "${Prefix}/shellscript-association-log"
Parameters:
commands:
- yum update -y
- yum install -y httpd
- !Sub "echo 'ProxyPass / uwsgi://${NLBDNSName}:${UWSGIPort}/' >> /etc/httpd/conf/httpd.conf"
- !Sub "echo 'ProxyPassReverse / uwsgi://${NLBDNSName}:${UWSGIPort}/' >> /etc/httpd/conf/httpd.conf"
- systemctl start httpd
- systemctl enable httpd
Targets:
- Key: !Sub "tag:${InstanceTagKey}"
Values:
- !Ref InstanceTagValueWeb
WaitForSuccessTimeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
Code language: YAML (yaml)
インスタンスをWebサーバとして動作させるための初期化処理を定義します。
今回はSSMドキュメントAWS-RunShellScriptを使用します。
SSMドキュメントを使用したインスタンスの初期化処理については、以下のページをご確認ください。
実行する内容は以下の通りです。
- Apacheをインストールする。
- 設定ファイル(httpd.conf)にNLB向けのプロキシ設定を追記する。
- Apacheを起動・有効化する。
Targetsプロパティで、この初期化処理を実行する対象を指定します。
先述のタグが付与されているインスタンスに対して、このSSM関連付けを適用します。
NLB
今回の構成では、Webサーバとアプリサーバを分離し、両サーバの間に内部NLBを配置します。
本構成に関する詳細は、以下のページをご確認ください。
アプリサーバ
Auto Scalingグループ
Resources:
LaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
IamInstanceProfile:
Arn: !Ref InstanceProfileArn
ImageId: !Ref InstanceImageId
InstanceType: !Ref InstanceType
SecurityGroupIds:
- !Ref AppSecurityGroup
TagSpecifications:
- ResourceType: instance
Tags:
- Key: !Ref InstanceTagKey
Value: !Ref InstanceTagValueApp
LaunchTemplateName: !Sub "${Prefix}-LaunchTemplate-App"
Code language: YAML (yaml)
Auto Scalingグループ内にWebサーバを配置します。
先述の通り、ポイントはタグ設定です。
以下の通り、タグを設定します。
- キー:Server
- 値:ApacheWeb
セキュリティグループ
Resources:
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-AppSecurityGroup"
GroupDescription: Allow uWSGI from NLB.
VpcId: !Ref VPC
Code language: YAML (yaml)
アプリサーバ用のセキュリティグループですが、CloudFormationテンプレート上では、許可する通信を指定しません。
アプリサーバ向けトラフィックの送信元はNLBですが、今回のNLBはクライアントIPの保存を無効化しているため、トラフィックの送信元はNLBのプライベートアドレスとなります。
そしてこのアドレスはNLBの作成後に動的に付与されるため、テンプレートでは指定することはできないものとなります。
上記への対応として、CloudFormationカスタムリソースを使用します。
詳細は以下のページご確認ください。
今回は上記のページの内容の内、Lambda関数を以下の通りに変更します。
Resources:
AppSecurityGroupFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import boto3
import cfnresponse
import os
nlb_loadbalancer_name = os.environ['NLB_LOADBALANCER_NAME']
security_group_id = os.environ['SECURITY_GROUP']
uwsgi_port = int(os.environ['UWSGI_PORT'])
filter_value = '*{nlb}*'.format(nlb=nlb_loadbalancer_name)
client = boto3.client('ec2')
CREATE = 'Create'
response_data = {}
def lambda_handler(event, context):
try:
if event['RequestType'] == CREATE:
describe_network_interfaces_response = client.describe_network_interfaces(
Filters=[
{
'Name':'description',
'Values':[
filter_value
]
}
]
)
for interface in describe_network_interfaces_response['NetworkInterfaces']:
private_address = interface['PrivateIpAddress']
authorize_security_group_ingress_response = client.authorize_security_group_ingress(
GroupId=security_group_id,
IpPermissions=[
{
'FromPort': uwsgi_port,
'IpProtocol': 'tcp',
'IpRanges': [
{
'CidrIp': '{address}/32'.format(address=private_address)
}
],
'ToPort': uwsgi_port
}
]
)
print(authorize_security_group_ingress_response)
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
Environment:
Variables:
NLB_LOADBALANCER_NAME: !Ref NLBLoadBalancerName
SECURITY_GROUP: !Ref AppSecurityGroup
UWSGI_PORT: !Ref UWSGIPort
FunctionName: !Sub "${Prefix}-AppSecurityGroupFunction"
Handler: !Ref FunctionHandler
Runtime: !Ref FunctionRuntime
Role: !GetAtt AppSecurityGroupFunctionRole.Arn
Code language: YAML (yaml)
NLBのプライベートアドレスを取得後、これを使用して、アプリサーバ用セキュリティグループのルールを追加します。
SSM関連付け
Resources:
AppServerAssociation:
Type: AWS::SSM::Association
Properties:
AssociationName: !Sub "${Prefix}-apply-ansible-playbook-association"
Name: AWS-ApplyAnsiblePlaybooks
OutputLocation:
S3Location:
OutputS3BucketName: !Ref PlaybookBucket
OutputS3KeyPrefix: !Sub "${Prefix}/playbook-association-log"
Parameters:
Check:
- "False"
ExtraVariables:
- !Sub >-
SSM=True
DB_NAME=${DBName}
DB_PASSWORD=${DBPassword}
DB_READ_ENDPOINT_ADDRESS=${DBReadEndpointAddress}
DB_TABLENAME=${DBTableName}
DB_USER=${DBUser}
DB_WRITE_ENDPOINT_ADDRESS=${DBWriteEndpointAddress}
MYSQL_PORT=${MySQLPort}
InstallDependencies:
- "True"
PlaybookFile:
- !Ref PlaybookFileName
SourceInfo:
- !Sub '{"path": "https://${PlaybookBucket}.s3.${AWS::Region}.amazonaws.com/${Prefix}/${PlaybookPackageName}"}'
SourceType:
- S3
Verbose:
- -v
Targets:
- Key: !Sub "tag:${InstanceTagKey}"
Values:
- !Ref InstanceTagValueApp
WaitForSuccessTimeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
Code language: YAML (yaml)
インスタンスをWebサーバとして動作させるための初期化処理を定義します。
今回はSSMドキュメントAWS-ApplyAnsiblePlaybooksを使用します。
後述のAnsible Playbookを実行することで、初期化を実行します。
ポイントはExtraVariablesプロパティです。
本プロパティで変数を定義することができますが、これらの変数はAnsibleのランタイム環境で使用することができます。
今回は本プロパティを使用して、DBのユーザ名・パスワード等の情報をAnsible環境に渡します。
記法は以下の通りです。
[変数名1]=[値1] [変数名2]=[値2] …
今回はコードの可読性を高めるために、変数を複数行に分けて記載します。
「>-」を使用することで、各行の改行をスペースに変換して結合しつつ、最終行の改行は削られます。
playbook.yml・uwsgi.ini・uwsgi.service
基本的には、以下のページでご紹介した内容と同様です。
playbook.yml
playbook.ymlに変更点があるため、これを取り上げます。
- hosts: all
gather_facts: no
become: yes
tasks:
- name: update yum.
yum: name=*
- name: install packages by yum.
yum:
name:
- python3-devel
- gcc
- name: install packages by pip3.
pip:
name:
- uwsgi
- flask
- mysql-connector-python
executable: pip3
- name: create app directory.
file:
path: /home/ec2-user/myapp
state: directory
- name: copy flask script.
copy:
src: ./run.py
dest: /home/ec2-user/myapp/run.py
- name: copy uWSGI ini.
copy:
src: ./uwsgi.ini
dest: /home/ec2-user/myapp/uwsgi.ini
- name: copy uWSGI Service.
copy:
src: ./uwsgi.service
dest: /etc/systemd/system/uwsgi.service
- name: create uWSGI environment variables file.
copy:
dest: "/etc/sysconfig/uwsgi"
content: |
DB_NAME={{DB_NAME}}
DB_PASSWORD={{DB_PASSWORD}}
DB_READ_ENDPOINT_ADDRESS={{DB_READ_ENDPOINT_ADDRESS}}
DB_TABLENAME={{DB_TABLENAME}}
DB_USER={{DB_USER}}
DB_WRITE_ENDPOINT_ADDRESS={{DB_WRITE_ENDPOINT_ADDRESS}}
MYSQL_PORT={{MYSQL_PORT}}
- name: reload daemon.
systemd:
daemon_reload: yes
- name: start and enable uWSGI.
systemd:
name: uwsgi
state: started
enabled: yes
Code language: YAML (yaml)
systemd用の変数を定義するタスクを追加しました。
/etc/sysconfig/uwsgiにファイルを作成し、これに環境変数を記述します。
作成する変数はDBのユーザ名・パスワード等の情報です。
これらはSSM関連付けのExtraVariablesで定義され、Ansibleに渡された値を使用しています。
uwsgi.service
uwsgi.serviceで、/etc/sysconfig/uwsgiを読み込むように設定します。
[Unit]
Description = uWSGI
After = syslog.target
[Service]
WorkingDirectory = /home/ec2-user/myapp/
EnvironmentFile=/etc/sysconfig/uwsgi
ExecStart = /usr/local/bin/uwsgi --ini /home/ec2-user/myapp/uwsgi.ini
Restart = on-failure
RestartSec = 3
KillSignal = SIGQUIT
Type = notify
StandardError = syslog
NotifyAccess = all
[Install]
WantedBy = multi-user.target
Code language: plaintext (plaintext)
run.py
import datetime
import json
import mysql.connector
import os
from flask import Flask
db_name = os.environ['DB_NAME']
db_password = os.environ['DB_PASSWORD']
db_read_endpoint_address = os.environ['DB_READ_ENDPOINT_ADDRESS']
db_tablename = os.environ['DB_TABLENAME']
db_user = os.environ['DB_USER']
db_write_endpoint_address = os.environ['DB_WRITE_ENDPOINT_ADDRESS']
mysql_port = int(os.environ['MYSQL_PORT'])
str_format = '%Y-%m-%d %H:%M:%S'
app = Flask(__name__)
@app.route('/')
@app.route('/read')
def read():
conn = mysql.connector.connect(
host=db_read_endpoint_address,
port=mysql_port,
user=db_user,
password=db_password,
database=db_name
)
cur = conn.cursor()
read_sql = 'select * from {table};'.format(table=db_tablename)
cur.execute(read_sql)
result = json.dumps(
[record[0].strftime(str_format) for record in cur],
indent=2
)
cur.close()
conn.close()
return result
@app.route('/write')
def write():
conn = mysql.connector.connect(
host=db_write_endpoint_address,
port=mysql_port,
user=db_user,
password=db_password,
database=db_name
)
cur = conn.cursor()
now = datetime.datetime.now()
now_str = now.strftime(str_format)
write_sql = 'insert into {table} values ("{now}");'.format(
table=db_tablename,
now=now_str
)
cur.execute(write_sql)
cur.close()
conn.commit()
conn.close()
return 'Saved: {now}'.format(now=now_str)
if __name__ == '__main__':
app.run()
Code language: Python (python)
Flaskを使用して、以下の2ページを定義します。
- readページ・トップページ:Auroraに接続し、保存されている全日時情報を取得して、クライアントに返す。
- writeページ:Auroraに接続し、現在の日時情報を保存し、クライアントに返す。
それぞれのページでAuroraに接続しますが、接続時に使用するエンドポイントが異なります。
readページの場合は、読み込み処理だけが発生しますので、リードレプリカ向けの読み込み用エンドポイントを使用します。
writeページの場合は、書き込み処理も発生しますので、プライマリサーバ向けの書き込み用エンドポイントを使用します。
DBサーバ
Aurora
Auroraクラスターにプライマリサーバとリードレプリカサーバを作成します。
基本的な構成は、以下のページと同様です。
AuroraのDBを初期化するために、CloudFormationカスタムリソースを使用します。
具体的には、カスタムリソースに紐づくLambda関数から、Auroraに接続し、初期化処理を実行します。
詳細につきましては、以下のページをご確認ください。
今回はSSMパラメータストアに保存された、以下のSQL文を実行することで、DBの初期化を実行します。
Resources:
SQLParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub "${Prefix}-SQLParameter"
Type: String
Value: !Sub |
USE ${DBName};
CREATE TABLE ${DBTableName} (dt datetime);
Code language: YAML (yaml)
具体的には、操作するDBを選択後、日時情報を保存するテーブルを作成する内容です。
セキュリティグループ
Resources:
DBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-DBSecurityGroup"
GroupDescription: Allow MySQL from AppSecurityGroup and FunctionSecurityGroup.
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref MySQLPort
ToPort: !Ref MySQLPort
SourceSecurityGroupId: !Ref AppSecurityGroup
- IpProtocol: tcp
FromPort: !Ref MySQLPort
ToPort: !Ref MySQLPort
SourceSecurityGroupId: !Ref FunctionSecurityGroup
Code language: YAML (yaml)
Auroraへの接続はアプリサーバとLambda関数から発生しますので、これらの通信を許可する内容です。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
Ansible Playbookの準備
CloudFormationスタックを作成する前に、Ansibleの準備を行います。
具体的にはPlaybook等をZip化し、S3バケットに設置します。具体的なコマンドは以下のページをご確認ください。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- ALB:fa-094-ALB
- ALBのDNS名:fa-094-alb-1763061004.ap-northeast-1.elb.amazonaws.com
- NLB:fa-094-NLB
- Webサーバ用Auto Scaling:fa-094-ALBAutoScalingGroup
- アプリサーバ用Auto Scaling:fa-094-NLBAutoScalingGroup
- Auroraクラスター:fa-094-dbcluster
ALB
作成されたリソースをAWS Management Consoleから確認します。
ALBを確認します。
ALBが正常に作成されていることがわかります。
またALBのDNS名もわかります。
ALBターゲットグループも確認します。
グループ内に2つのインスタンスが作成されていることがわかります。
つまりALBに関連づいているWeb用Auto Scalingグループによって、自動的にWebサーバインスタンスが2台起動したということです。
NLB
NLBを確認します。
NLBが正常に作成されていることがわかります。
NLBターゲットグループも確認します。
グループ内に2つのインスタンスが作成されていることがわかります。
つまりNLBに関連づいているアプリ用Auto Scalingグループによって、自動的にアプリサーバインスタンスが2台起動したということです。
Aurora
Auroraクラスターを確認します。
正常にAuroraクラスターが作成されていることがわかります。
確かに書き込み用・読み込み用のエンドポイントが作成されていることがわかります。
動作確認
準備が整いましたので、ALBのトップページにアクセスします。
レスポンスとして空のリストが返ってきました。
まだ日時情報が保存されていないためです。
何度か書き込みページにアクセスし、DBにデータを保存させます。
正常に日時データが保存されました。
改めて読み込みページにアクセスします。
保存されたデータが返ってきました。
今回の3層構造のアプリが正常に動作していることがわかります。
(参考)サーバのログ
参考までにWeb・アプリ層のサーバログを確認します。
まず2台のWebサーバのログの一部を取り上げます。
10.0.2.56 - - [06/Nov/2022:03:01:52 +0000] "GET /write HTTP/1.1" 200 26 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
10.0.2.56 - - [06/Nov/2022:03:02:03 +0000] "GET / HTTP/1.1" 200 77 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
Code language: plaintext (plaintext)
10.0.2.56 - - [06/Nov/2022:03:01:35 +0000] "GET / HTTP/1.1" 200 2 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
10.0.2.56 - - [06/Nov/2022:03:01:47 +0000] "GET /write HTTP/1.1" 200 26 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
10.0.2.56 - - [06/Nov/2022:03:01:57 +0000] "GET /write HTTP/1.1" 200 26 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
10.0.2.56 - - [06/Nov/2022:03:02:14 +0000] "GET /read HTTP/1.1" 200 77 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
Code language: plaintext (plaintext)
ALBによって、2つのWebサーバにトラフィックが振り分けられていることがわかります。
次にアプリサーバのログの一部を取り上げます。
[pid: 2178|app: 0|req: 70/70] 10.0.2.56 () {64 vars in 1335 bytes} [Sun Nov 6 03:01:47 2022] GET /write => generated 26 bytes in 48 msecs (HTTP/1.1 200) 2 headers in 79 bytes (1 switches on core 0)
[pid: 2178|app: 0|req: 71/71] 10.0.1.106 () {52 vars in 765 bytes} [Sun Nov 6 03:01:47 2022] GET / => generated 27 bytes in 26 msecs (HTTP/1.1 200) 2 headers in 79 bytes (1 switches on core 0)
[pid: 2178|app: 0|req: 73/73] 10.0.2.56 () {64 vars in 1332 bytes} [Sun Nov 6 03:02:14 2022] GET /read => generated 77 bytes in 30 msecs (HTTP/1.1 200) 2 headers in 79 bytes (1 switches on core 0)
Code language: plaintext (plaintext)
[pid: 2189|app: 0|req: 67/67] 10.0.1.106 () {52 vars in 762 bytes} [Sun Nov 6 03:01:47 2022] GET / => generated 27 bytes in 44 msecs (HTTP/1.1 200) 2 headers in 79 bytes (1 switches on core 0)
[pid: 2189|app: 0|req: 68/68] 10.0.2.56 () {66 vars in 1365 bytes} [Sun Nov 6 03:01:52 2022] GET /write => generated 26 bytes in 34 msecs (HTTP/1.1 200) 2 headers in 79 bytes (1 switches on core 0)
[pid: 2189|app: 0|req: 69/69] 10.0.2.56 () {66 vars in 1366 bytes} [Sun Nov 6 03:01:57 2022] GET /write => generated 26 bytes in 37 msecs (HTTP/1.1 200) 2 headers in 79 bytes (1 switches on core 0)
Code language: plaintext (plaintext)
こちらもNLBによって、2つのアプリサーバにトラフィックが振り分けられていることがわかります。
まとめ
Apache/Python(uWSGI)/RDS(Aurora)で3層アーキテクチャを作成しました。