TL;DR
前回、AWSでSSI(include virtual)が記述されたHTMLを、S3の特定バケットに格納しLambdaでインクルード内のソースを結合した上で別のバケットに格納するような仕組みを作りましたが、それのAlibaba Cloud版です。
やりたかったこと
OSS(Object Storage Service)は静的サイトホスティングサービスのため、サーバーサイド側で動的に処理をするということが原則できません。
もちろんその後キャッシュする先であるCDN側(Alibaba Cloud CDN)もできません。
その場合、今であればローカル開発環境を構築しローカル上では別々のファイルにしておき、コンパイルする際に結合する方法がスタンダードだと思いますが、元々SSIが使われていた既存サイトをOSS+CDNへ移管する場合などを含め残念ながらすべての案件でそのフローが導入できるわけでもありません。
なので、元々SSIが使われていた場合、そのまま置換など行わずそのままの形で使用できるようにAlibaba Cloud側で調整を試みました。
やったこと
(前提としてAlibaba Cloud版のIAMであるRAMやKMSの設定が終わった状態です)
基本的に前回の記事を元に、Alibaba Cloud用にカスタマイズしています。
【AWS】S3 / LambdaでSSIっぽいことをやる
Alibaba Cloudの設定についてはSBクラウドさんのこちらの記事が役に立つ思います。
イベント駆動サービス FucntionComputeでオブジェクトストレージを操る
構成
前回と構成は同じです。
なので使用するサービスはOSSとFunction ComputeのみでAlibaba Cloud CDNは必要であれば。
設定方法
OSS
Alibaba Cloud版ではファイルアップするバケットは1つにしています。
その中でtemp用のディレクトリと公開用のディレクトリの2つに分けて処理を行います。
公開/temp用バケット
名前は何でも良いです。
今回は oss-ssi-include
としています。
各設定
アクセス許可はそれぞれ適宜設定されているものとします。
公開用ディレクトリとtemp用ディレクトリの作成
公開用ディレクトリ dist
とtemp用ディレクトリ src
を「ディレクトリの作成」ボタンからそれぞれバケット内に作成してください。
Function Compute
Function ComputeではPUTイベントを検知して、 src/
ディレクトリ内にアップされたHTMLファイル内にSSI(サーバーサイドインクルード)が記述されていれば、Function Computeでインクルード処理を行い、 dist/
ディレクトリに格納するような関数を作成します。
関数コード
import json
import os
import logging
import oss2
import re
import urllib.parse
logger = logging.getLogger()
logger.setLevel(logging.INFO)
OSS_ENDPOINT = "oss-ap-northeast-1.aliyuncs.com"
DEST_BUCKET_NAME = "oss-ssi-include"
def handler(event, context):
logger.info('## ENVIRONMENT VARIABLES')
logger.info(os.environ)
logger.info('## EVENT')
logger.info(event)
creds = context.credentials
auth = oss2.StsAuth(creds.accessKeyId, creds.accessKeySecret, creds.securityToken)
evt = json.loads(event)
# 下記書き換える
input_bucket = oss2.Bucket(auth, OSS_ENDPOINT, DEST_BUCKET_NAME)
logger.info('## INPUT BUKET')
logger.info(input_bucket)
input_key = urllib.parse.unquote_plus(evt['events'][0]['oss']['object']['key'])
logger.info('## INPUT KEY')
logger.info(input_key)
try:
# 入力ファイルの取得
response = input_bucket.get_object(input_key)
logger.info(response)
# ファイル出力
output_key = re.sub('^src/','dist/',input_key)
logger.info('## OUTPUT KEY')
logger.info(output_key)
if not input_key.endswith('.html'):
logger.info(response)
input_bucket.put_object(output_key, response)
else:
input_html = response.read().decode('utf-8')
logger.info('## input_html')
logger.info(input_html)
output_html = input_html
# SSI記述を取得
include_path_base = re.findall(r'<!--#include virtual="/(.*?)" -->.*?\n', input_html, flags=re.DOTALL)
logger.info('## PATH BASE')
logger.info(include_path_base)
if len(include_path_base) > 0:
for path in include_path_base:
include_path = path
logger.info('## PATH')
logger.info(include_path)
# SSIファイルの取得
try:
include = input_bucket.get_object('src/' + include_path)
include_html = include.read().decode('utf-8')
# SSIを実行
output_html = output_html.replace('<!--#include virtual="/' + include_path + '" -->', include_html)
except ClientError:
pass
input_bucket.put_object(output_key, output_html)
except Exception as e:
logger.info(e)
raise e
その他の設定
トリガー設定
トリガータイプ:OSS トリガー
トリガー名:put
イベントソース:
acs:oss:ap-northeast-1:xxxxxxxxxxxxxxxxx:ossname
イベント: oss:ObjectCreated:PutObject
, oss:ObjectCreated:PostObject
トリガールール:接頭辞 src/
ロール操作:既存のロールを選択
既存のロール:
ロールはRAMで設定した以下のポリシーでOKです。
{
"Version": "1",
"Statement": [
{
"Action": [
"fc:InvokeFunction"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
おわりに
前回、AWSでSSI(include virtual)が記述されたHTMLを、S3の特定バケットに格納しLambdaでインクルード内のソースを結合した上で別のバケットに格納するような仕組みを作りましたが、諸事情でAlibaba Cloudでも作る必要があったため用意してみました。
AWS版と違ってtemp用ディレクトリにファイルアップ→CloudComputeが処理→公開用ディレクトリにファイルが格納されるため、使い良い形にするのであれば公開用ディレクトリに格納されたファイルはCDNへ配信した方が良いと思います。
あと、日本でAlibaba Cloudの情報を得ようとする時はSBクラウドさんのエンジニアブログがおすすめです。
今回のものも大分参考にさせていただきました。
ただこれって重要あるのかな。。
現場からは以上です。