この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
コンサル部@大阪オフィスのYui(@MayForBlue)です。
最近初めてElastiCacheを触る機会があり、最初の一歩として公式のチュートリアルをやってみました。
より構築しやすいように手順を変えてみたり、構築後に簡単なテストをするなど少し工夫してやってみたので、手順をご紹介したいと思います。
ElastiCache とは
ElastiCache とは、インメモリデータストアやインメモリキャッシュとして機能するAWSのフルマネージドサービスです。
詳細は以下のブログをご参照ください。
チュートリアル内容
リンク
Amazon ElastiCache for Redis を使い、オンラインアプリケーション用の高速セッションストアを構築する
概要
PythonのマイクロフレームワークFlaskで簡易Webアプリケーションを作成し、セッション情報をElastiCache(Redis)に保存する
構成図
構成は以下のようになります。
プライベートサブネットにWebアプリケーション用のEC2を用意し、プライベートサブネットにElastiCacheのプライマリ/レプリカノードを配置します。
前準備
チュートリアルに使用するVPC、サブネット、インターネットゲートウェイはCloudFormationで構築しました。
テンプレートはこちら
AWSTemplateFormatVersion: "2010-09-09"
Description:
VPC and Subnet Create
Metadata:
"AWS::CloudFormation::Interface":
ParameterGroups:
- Label:
default: "Project Name Prefix"
Parameters:
- ProjectName
- Label:
default: "Network Configuration"
Parameters:
- VPCCIDR
- PublicSubnetACIDR
- PublicSubnetCCIDR
- PrivateSubnetACIDR
- PrivateSubnetCCIDR
ParameterLabels:
ProjectName:
defalut: "ProjectName"
VPCCIDR:
default: "VPC CIDR"
PublicSubnetACIDR:
default: "PublicSubnetA CIDR"
PublicSubnetCCIDR:
default: "PublicSubnetC CIDR"
PrivateSubnetACIDR:
default: "PrivateSubnetA CIDR"
PrivateSubnetCCIDR:
default: "PrivateSubnetC CIDR"
Parameters:
ProjectName:
Type: String
Default: "elc-tutorial"
VPCCIDR:
Type: String
Default: "10.1.0.0/16"
PublicSubnetACIDR:
Type: String
Default: "10.1.10.0/24"
PublicSubnetCCIDR:
Type: String
Default: "10.1.20.0/24"
PrivateSubnetACIDR:
Type: String
Default: "10.1.100.0/24"
PrivateSubnetCCIDR:
Type: String
Default: "10.1.200.0/24"
Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
# VPC
VPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub "${ProjectName}-vpc"
# InternetGateway
InternetGateway:
Type: "AWS::EC2::InternetGateway"
Properties:
Tags:
- Key: Name
Value: !Sub "${ProjectName}-igw"
InternetGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------#
# Public SubnetA
PublicSubnetA:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: "ap-northeast-1a"
CidrBlock: !Ref PublicSubnetACIDR
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-public-subnet-a"
# Public SubnetC
PublicSubnetC:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: "ap-northeast-1c"
CidrBlock: !Ref PublicSubnetCCIDR
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-public-subnet-c"
# Private SubnetA
PrivateSubnetA:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: "ap-northeast-1a"
CidrBlock: !Ref PrivateSubnetACIDR
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-private-subnet-a"
# Private SubnetC
PrivateSubnetC:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: "ap-northeast-1c"
CidrBlock: !Ref PrivateSubnetCCIDR
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-private-subnet-c"
# Public RouteTableA
PublicRouteTableA:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-public-route-a"
# Public RouteTableC
PublicRouteTableC:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-public-route-c"
# Private RouteTableA
PrivateRouteTableA:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-private-route-a"
# Private RouteTableC
PrivateRouteTableC:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ProjectName}-private-route-c"
# Routing
# PublicRouteA
PublicRouteA:
Type: "AWS::EC2::Route"
Properties:
RouteTableId: !Ref PublicRouteTableA
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
# PublicRouteC
PublicRouteC:
Type: "AWS::EC2::Route"
Properties:
RouteTableId: !Ref PublicRouteTableC
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
# RouteTable Associate
# PublicRouteTableA
PublicSubnetARouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PublicSubnetA
RouteTableId: !Ref PublicRouteTableA
# PublicRouteTableC
PublicSubnetCRouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PublicSubnetC
RouteTableId: !Ref PublicRouteTableC
# PrivateRouteTableA
PrivateSubnetARouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PrivateSubnetA
RouteTableId: !Ref PrivateRouteTableA
# PrivateRouteTableC
PrivateSubnetCRouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PrivateSubnetC
RouteTableId: !Ref PrivateRouteTableC
やってみる
サーバーの構築
まずはアプリケーションサーバーとなるEC2を構築します。
EC2のダッシュボードに移動し、「インスタンスの作成」をクリックします。
Amazon Linux 2 のAMIを選択します。
インスタンスタイプはt2.microを選択します。
インスタンス詳細の設定で以下を設定し、その他の値はデフォルトで「ストレージの追加」をクリックします。
1. ネットワーク:チュートリアル用に作成したものを選択(elc-tutorial-vpc
)
2. サブネット: elc-tutorial-public-subnet-a
3. 自動割り当てパブリックIP:有効
ストレージサイズはデフォルトのまま「タグの追加」をクリックします。
任意のタグを設定します。ここでは以下を設定しました。
Name:ecl-tutorial-server
セキュリティグループを設定します。
「新しいセキュリティグループを作成する」にチェックを入れ、以下を設定しました。
- セキュリティグループ名:
ecl-tutorial-server-sg
- 説明:
ecl-tutorial-server-sg
- インバウンドルール
タイプ | プロトコル | ポート範囲 | ソース | 説明 |
---|---|---|---|---|
SSH | TCP | 22 | マイIP | from my IP |
カスタムTCP | TCP | 5000 | 0.0.0.0/0 | from All |
インバウンドルールの1つ目はSSHログイン用、2つ目はWebアプリケーションへのアクセス用です。
内容を確認して問題なければ「起動」をクリックします。
ログイン用のキーペアを選択して「インスタンスの作成」をクリックします。
キーペアが未作成の場合は「新しいキーペアを作成する」を選択してください。
ElastiCacheの構築
次に、ElastiCacheを構築していきます。
インスタンスを構築する前に、構築に必要なセキュリティグループとサブネットグループを作成します。
まずはセキュリティグループからです。
EC2のダッシュボードの左側ペインより「セキュリティグループ」を選択し、「セキュリティグループの作成」をクリックします。
以下を設定し、「セキュリティグループの作成」をクリックします。
- セキュリティグループ名:
ecl-tutorial-redis-sg
- 説明:
ecl-tutorial-redis-sg
- インバウンドルール
タイプ | プロトコル | ポート範囲 | ソース | 説明 |
---|---|---|---|---|
カスタムTCP | TCP | 6379 | ecl-tutorial-server-sg | from server |
インバウンドルールでアプリケーションサーバーからポート番号6379の通信を許可する設定を入れています。
次にサブネットグループを作成します。
ElastiCacheのダッシュボードの左側ペインから「サブネットグループ」を選択します。
「サブネットグループの作成」をクリックします。
以下を設定し、「作成」をクリックします。
- 名前:
elc-tutorial-redis-subnet-group
- 説明:
elc-tutorial-redis-subnet-group
- VPC:
elc-tutorial-vpc
- サブネット
- elc-tutorial-private-subnet-a / ap-northeast-1a
- elc-tutorial-private-subnet-c / ap-northeast-1c
いよいよElastiCacheを構築していきます。
ElastiCacheのダッシュボードから「今すぐ始める」をクリックします。
以下を設定します。(その他の値はデフォルトのままで大丈夫です。)
- クラスターエンジン:Redis
- 名前:
elc-tutorial-redis
- 説明:
elc-tutorial-redis
- ノードのタイプ:
cache.t2.micro
- レプリケーション数:
1
- サブネットグループ:
elc-tutorial-redis-subnet-group
- セキュリティグループ:
ecl-tutorial-redis-sg
- 自動バックアップの有効化:無効
しばらくしてステータスがavailableになれば構築完了です。
アプリケーションの設定をする
サーバーにログインし、Webアプリケーションとしての設定、ElastiCacheとの接続を行います。
EC2にログインします。
$ ssh -i <key-pair>.pem <ユーザ名>@<パブリックIP or パブリックDNS名>
ログイン方法の詳細についてはこちらのブログをご参照ください。
ログインできたら、以下のコマンドを順番に実行します。
$ sudo yum install tmux
$ sudo amazon-linux-extras install redis4.0
$ sudo yum install git
$ sudo yum install python3
$ sudo pip3 install flask
$ sudo pip3 install virtualenv
$ git clone https://github.com/aws-samples/amazon-elasticache-samples/
$ cd amazon-elasticache-samples/session-store
$ virtualenv venv
$ source ./venv/bin/activate
$ pip3 install -r requirements.txt
$ export FLASK_APP=example-1.py
$ export SECRET_KEY=some_secret_string
$ flask run -h 0.0.0.0 -p 5000 --reload
チュートリアルに記載されていたものに加え、Flask、テストに使用するRedisクライアントツール、tmuxをインストールしています。
Flaskの起動が成功したら、ブラウザからアクセス確認してみます。
以下のようにhttp://【パブリックDNS】:5000/
にアクセスできれば成功です。
ElastiCache(Redis)と接続する
amazon-elasticache-samples/session-store/example-1.py
を編集し、既存のコードを削除して以下のコードを貼り付けます。
(コードの内容についてはチュートリアル内で解説されているため、ここでは割愛します)
チュートリアル内ではセッション情報保持期間が10秒間に設定されていますが、ここではテストのために180秒間に設定しました。
import os
import redis
from flask import Flask, session, redirect, escape, request
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', default=None)
# BEGIN NEW CODE - PART 1 #
REDIS_URL = os.environ.get('REDIS_URL')
store = redis.Redis.from_url(REDIS_URL)
# END NEW CODE - PART 1 #
@app.route('/')
def index():
if 'username' in session:
# BEGIN NEW CODE - PART 2 #
username = escape(session['username'])
visits = store.hincrby(username, 'visits', 1)
# BEGIN NEW CODE - EXPIRE SESSIONS #
store.expire(username, 180)
# END NEW CODE - EXPIRE SESSIONS #
return '''
Logged in as {0}.<br>
Visits: {1}
'''.format(username, visits)
# END NEW CODE - PART 2 #
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect('/')
return '''
<form method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>'''
@app.route('/logout')
def logout():
session.pop('username', None)
return redirect('/')
次に、環境変数にRedisのプライマリエンドポイントを設定します。
$ export REDIS_URL="redis://【プライマリエンドポイント】:6379"
設定できたら、tmuxを起動します。
$ tmux
一つ目のウィンドウで以下のコマンドを実行し、Redisに値が保存されていないことを確認します。
$redis-cli -h 【プライマリエンドポイント】
elc-tutorial-redis.xxxx.ng.0001.apne1.cache.amazonaws.com:6379> keys *
(empty list or set)
Ctrl+b
→c
をクリックし、新規ウィンドウを作成します。
以下のコードを実行し、Flaskサーバーを起動します。
$ flask run -h 0.0.0.0 -p 5000 --reload
* Serving Flask app "example-1.py" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
起動できたら、http://【パブリックDNS】:5000/login
にアクセスし、以下のような画面が表示されていることを確認します。
任意の文字列を入力してログインすると、Redisに値が保存されます。
a
という値で2回、b
という値で2回ログインしてみました。
tmuxの一つ目のウィンドウで値が保存されていることを確認してみました。
今回はアプリケーション側でハッシュ値を保存しているため、値参照のコマンドにはhgetall
を使用しています。
elc-tutorial-redis.xxxx.ng.0001.apne1.cache.amazonaws.com:6379>keys *
1) "b"
2) "a"
elc-tutorial-redis.xxxx.ng.0001.apne1.cache.amazonaws.com:6379> hgetall a
1) "visits"
2) "2"
elc-tutorial-redis.xxxx.ng.0001.apne1.cache.amazonaws.com:6379> hgetall b
1) "visits"
2) "1"
期待した値が保存されていることが確認できました!
後片付け
最後に、今回構築した以下を削除してチュートリアル完了です。
- ElastiCache
- Redisインスタンス
- サブネットグループ
- セキュリティグループ
- EC2
- EC2インスタンス
- セキュリティグループ
- ネットワーク
- CloudFormationテンプレート
最後に
ElastiCacheについて概要は知っていたものの、やはり実際に手を動かして作ってみるのが大事だなと思いました。
今回のチュートリアルでは構築に加え、簡易的なものではありますが、アプリケーションに組み込む方法も知れたのが良かったです。
また、今回のチュートリアルで学べるのは最初の構築の部分のみなので、ElastiCacheの詳細な挙動や性質については別途勉強していきたいと思います。
この記事がどなたかのお役に立てば幸いです。
以上、大阪オフィスのYui(@MayForBlue)でしたっ(`・ω・´)