有償コンテンツのHLS配信に、さくらのクラウド ウェブアクセラレータ(CDN)+オブジェクトストレージ を組み合わせて使う

これは さくらインターネット Advent Calendar 2021  の21日目の記事です。

 

本記事では、

「オブジェクトストレージ」とCDNサービス「ウェブアクセラレータ」を組み合わせて利用し、

ウェブアクセラレータ「ワンタイムURL機能」を利用して、有償コンテンツ想定のHLS配信を試してみた内容を書きました。 

【オブジェクトストレージ】

【ウェブアクセラレータ(CDN)】

 

目次

 

ウェブアクセラレータ「ワンタイムURL機能」とは?

ウェブアクセラレータのワンタイムURLは、有償コンテンツ配布用のダウンロード用ページやイベントで期間限定の動画や画像の公開ページ作成などといった、サイト内のコンテンツに指定時間のみアクセス可能にしたい時にご利用いただけます。

( サイト全体ワンタイムURL機能の利用 | ウェブアクセラレータ | さくらのクラウド ドキュメント )

 

配信イメージ

本記事では、下記の利用用途を想定した配信を試してみます。

 

【通知イメージ】

f:id:nozomi1773:20211221055830p:plain

アーカイブ動画の視聴など、有償コンテンツ購入者に、

  • メール

  • ログインアカウント毎の購入ページ(Webページで、ログインアカウントごとのコンテンツ表示)

などで、「HLS配信の閲覧用URL(ワンタイムURL)」を通知します。

 

【配信イメージ】

f:id:nozomi1773:20211221063445p:plain

  • この配信用URLを把握している人(有償コンテンツ購入者)のみが閲覧できる。
  • この配信用URLから規定の期間内のみ閲覧可能で、期間を過ぎると閲覧NGになる。
  • オブジェクトストレージへの直接アクセスも閲覧NGとなる。

 

設定

STEP1. オブジェクトストレージに、バケットを作る

マニュアル:オブジェクトストレージ サービス基本情報 | さくらのクラウド ドキュメント

 

STEP1-① 【さくらのクラウド ホーム】 から 【オブジェクトストレージ】を選択

f:id:nozomi1773:20211220190725p:plain

STEP1-② 【サイト 】 → 【石狩第1サイト】 を選択

f:id:nozomi1773:20211220191051p:plain

 

STEP1-③ バケットの追加

バケット】→ 【バケットの追加】 を選択

f:id:nozomi1773:20211220192011p:plain

バケットの名前を入力して、【追加】する

※ 今回の記事では、バケット名「video」

f:id:nozomi1773:20211220192346p:plain

STEP1-④ パーミッションの追加(ウェブアクセラレータ連携用 READ(読み込み)権限)

パーミッション】→ 【パーミッションの追加】を選択

f:id:nozomi1773:20211220192711p:plain

ウェブアクセラレータ連携用READ(読み込み)権限パーミッションを【追加】します。

※ 今回の記事では、「video」バケットのみのREAD(読み込み)権限のパーミッションを「video-read-only」の名前で追加します。

f:id:nozomi1773:20211220222818p:plain

表示されたシークレットアクセスキーをメモします。(再度表示されない為、必ずメモしておきます。)

f:id:nozomi1773:20211220223354p:plain

 

STEP1-⑤ パーミッションの追加(コンテンツアップロード用 READ/WRITE(読み込み/書き込み)権限)

パーミッション】→ 【パーミッションの追加】を選択

f:id:nozomi1773:20211220192711p:plain

コンテンツアプロード用READ/WRITE(読み込み/書き込み)権限パーミッションを【追加】します。

※ 今回の記事では、「video」バケットのみのREAD/WRITE(読み込み/書き込み)権限のパーミッションを「video-read-write」の名前で追加します。

f:id:nozomi1773:20211220223657p:plain

表示されたシークレットアクセスキーをメモします。(再度表示されない為、必ずメモしておきます。)

f:id:nozomi1773:20211220223354p:plain

 

STEP2. オブジェクトストレージに、htmlファイルをアップロード(配信確認用)

※ 本手順では、Ubuntu 18.04 の環境にて、awscliで準備しました。

STEP2-① awscli の インストール

$ sudo apt update 
$ sudo apt install awscli

 

STEP2-② awscli の 設定

※ 今回の記事では、「video」バケットのコンテンツアアプロード用に、「video」のREAD/WRITE(読み込み/書き込み)権限のパーミッション「video-read-write」シークレットアクセスキーをつかう。

$ sudo aws configure --profile video
AWS Access Key ID [None]: 「video-read-write」のアクセスキーを入力
AWS Secret Access Key [None]: 「video-read-write」のシークレットアクセスキーを入力
Default region name [None]: "jp-north-1" を入力
Default output format [None]: "json" を入力

$ tail -3 ~/.aws/config
[profile video]
region = jp-north-1
output = json

$ aws --profile video --endpoint-url=https://s3.isk01.sakurastorage.jp s3 ls s3://video/
$
→ 「video」バケットにアクセス可能なことを確認。まだ空の状態。

 

STEP2-③ テスト用のhtmlファイルを作成し、アップロードします。

今回は ローカルにもvideoディレクトリを準備し、awscli の sync で同期します。

$ mkdir video
$ cd video

~/video$ vim index.html
~/video$ cat index.html
<html>
<head>
<title>配信テストページ</title>
</head>
<body>
配信テストページです。<br/>
</body>
</html>

$ aws --profile video --endpoint-url=https://s3.isk01.sakurastorage.jp s3 sync ~/video s3://video/
upload: ./index.html to s3://video/index.html

アップロードできたことを確認

$ aws --profile video --endpoint-url=https://s3.isk01.sakurastorage.jp s3 ls s3://video/
2021-12-XX XX:XX:XX        124 index.html

コントロールパネルから確認すると下記の状態

f:id:nozomi1773:20211220232309p:plain

 

デフォルトのオブジェクトACLは private の為、API経由でのみアクセス可能で、

直接 httpsアクセスを試した場合、下記のようにエラーとなります。

$ curl -v https://video.s3.isk01.sakurastorage.jp/index.html

...(略)...
< HTTP/1.1 403 Forbidden
< Server: openresty
< Date: Xxx, XX Dec 2021 XX:XX:XX GMT
< Content-Type: application/xml
< Content-Length: 174
< Connection: keep-alive
< x-amz-id-2: XXXXXXXXXXXXXXXXXXXX
< x-amz-request-id: XXXXXXXXXXXXXXXXXXXX
<
* Connection #0 to host video.s3.isk01.sakurastorage.jp left intact
<?xml version="1.0" encoding="UTF-8"?><Error><Code>AccessDenied</Code><Message>Access Denied</Message><Resource></Resource><RequestId>XXXXXXXXXXXXXXXXXXXX</RequestId></Error>

 

STEP3. ウェブアクセラレータに、オリジン「オブジェクトストレージ」で配信設定/htmlファイル配信確認

本記事内では、独自ドメイン利用にて試しています。サブドメイン(xxxx.user.webacccel.jp)利用の場合は、手順内のDNSの設定等は不要です。

 

マニュアル : 初期設定(オブジェクトストレージ・独自ドメイン利用) | ウェブアクセラレータ | さくらのクラウド ドキュメント

に沿って設定を実施します。

 

STEP3-① 【さくらのクラウド ホーム】 から 【ウェブアクセラレータ(CDN)】を選択

f:id:nozomi1773:20211220234517p:plain

 

STEP3-② 【サイト追加】 から 「サイト新規新規追加」に必要情報を入力して、【保存】で追加します。

オリジン種別:オブジェクトストレージ を選択し、必要情報を入力します。

※ 今回の記事では、STEP1で用意した、「video」バケット、「video」バケットのみのREAD(読み込み)権限のパーミッション「video-read-only」のアークレットアクセスキーを指定します。

f:id:nozomi1773:20211220235054p:plain

STEP3-③ サイト有効化の準備として、独自ドメインの場合、利用しているDNSサービスにて、対象のTXTレコードまたはCNAMEレコードを追加する。

サイト追加時の確認画面の「CNAME先|サブドメイン」の表示

f:id:nozomi1773:20211221000412p:plain

または、サイト一覧→「対象サイト」→「設定」から確認

f:id:nozomi1773:20211221000636p:plain

 

確認したレコードを、利用しているDNSサービスにてTXTレコードを設定します。

TXTレコードが設定されたことを、digで確認する場合、

$ dig *****.1773.work -t txt +short
"webaccel=xxxxxxxxxx.user.webaccel.jp"

のように確認。

 

STEP3-③ サイト有効化

サイト一覧→「対象サイト」→「設定」から、「有効にする」を選択し、サイトを有効化します。

f:id:nozomi1773:20211221001252p:plain

 

STEP3-④ CNAMEの設定

STEP3-③で確認したCNAME先を、利用しているDNSサービスにて設定します。

(サイト有効化済みで、TXTレコードは不要の為、この時点で除去)

CNAMEが設定されたことを、digで確認する場合、

$ dig *****.1773.work 
...(略)...
;; ANSWER SECTION:
*****.1773.work.         360     IN      CNAME   xxxxxxxx.user.webaccel.jp.
xxxxxxxx.user.webaccel.jp. 3600 IN      CNAME   site-XXXXXXXX.gslb1.sakura.ne.jp.
site-XXXXXXXX.gslb1.sakura.ne.jp. 9 IN A    XXX.XXX.X.X
...(略)...

のように確認。

 

STEP3-⑤ SSL証明書の設定

※ 本記事では、「Let's Encrypt 自動更新証明書」を使います。持ち込みの証明書の設定もできます。

f:id:nozomi1773:20211221001542p:plain

 

STEP3-⑥ 配信確認

 

◇【復習】オブジェクトストレージに直接httpsアクセス(403 Forbidden

デフォルトのオブジェクトACLは private の為、API経由でのみアクセス可能で、

オブジェクトストレージに直接 httpsアクセスを試した場合、下記のようにエラーとなります。

$ curl -v https://video.s3.isk01.sakurastorage.jp/index.html

...(略)...
< HTTP/1.1 403 Forbidden
< Server: openresty
< Date: Xxx, XX Dec 2021 XX:XX:XX GMT
< Content-Type: application/xml
< Content-Length: 174
< Connection: keep-alive
< x-amz-id-2: XXXXXXXXXXXXXXXXXXXX
< x-amz-request-id: XXXXXXXXXXXXXXXXXXXX
<
* Connection #0 to host video.s3.isk01.sakurastorage.jp left intact
<?xml version="1.0" encoding="UTF-8"?><Error><Code>AccessDenied</Code><Message>Access Denied</Message><Resource></Resource><RequestId>XXXXXXXXXXXXXXXXXXXX</RequestId></Error>

 

◇ ウェブアクセラレータ経由でhttpsアクセス(200 OK

STEP2で、アップロードしたコンテンツにウェブアクセラレータ経由でアクセスします。

$ curl -v https://*****.1773.work/index.html

...(略)...
< HTTP/2 200
< server: nginx
< date: Xxx, XX Dec 2021 XX:XX:XX GMT
< content-type: text/html
< content-length: 124
< accept-ranges: bytes
< etag: "XXXXXXXXXXXXXXXXXXXX"
< last-modified: Xxx, XX Dec 2021 XX:XX:XX GMT
< x-amz-id-2: XXXXXXXXXXXXXXXXXXXX
< x-amz-request-id: XXXXXXXXXXXXXXXXXXXX
< cache-control: s-maxage=3600
< age: 0
< via: XXXXXXXXXXXXXXXXXXXX
< x-webaccel-origin-status: 200
< x-cache: MISS
<
<html>
<head>
<title>配信テストページ</title>
</head>
<body>
配信テストページです。<br/>
</body>
</html>

→ ウェブアクセラレータ経由でアクセスできることを確認!

 

STEP4.「ワンタイムURL」対象のコンテンツの準備、オブジェクトストレージにワンタイムURL形式のHLSは配信用ファイルをアップロード

 

STEP4-① ワンタイムURLのシークレットを決める

8文字以上16文字以内(使用可能な文字種はカンマとスペースを除いた印字可能ASCII) の文字列を決めます。

※ 本記事では、ランダム文字列を生成します。

※ シークレット文字列は、他人に公開しないでくだい。

$ cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -1
****************

 

STEP4-② HLS配信用ファイルの作成

※ 本記事では、用意したmp4ファイルを ffmpeg を使って、m3u8ファイル・tsファイルを生成します。 

~/video$ ls -l
total 19648
-rw-r--r-- 1 xxxxxxxxx xxxxxxxxx      124 Dec XX XX:XX index.html
-rw-r--r-- 1 xxxxxxxxx xxxxxxxxx 20061144 Dec XX XX:XX test.mp4

 

用意したmp4ファイル「test.mp4」を、ffmegを使って、

「test-video.m3u8」と、10秒ごとに分割した「test-videoXXX(001~008).ts」のセグメントファイルを生成します。

~/video$ sudo apt install ffmpeg

~/video$ ffmpeg -i test.mp4 -c:v copy -c:a copy -f hls -hls_time 10 -hls_playlist_type vod -hls_segment_filename "test-video%3d.ts" test-video.m3u8
...(略)...

~/video$ ls test*
test-video.m3u8  test-video000.ts  test-video001.ts  test-video002.ts  test-video003.ts  test-video004.ts  test-video005.ts  test-video006.ts  test-video007.ts  test-video008.ts  test.mp4

→ ファイルが生成された状態です。

 

STEP4-③ m3u8ファイルを「ワンタイムURL」形式に書き換え

HLS配信にてワンタイムURLを利用する場合、

・m3u8ファイル内に設定するtsファイルの取得URLの記述を、ワンタイムURLとして記述 ←STEP4-③でこちらを設定

・HTMLに記述するm3u8ファイルの取得URLの記述を、ワンタイムURLとして記述

を設定する必要があります。

 

◇記述の変更前(ffmpegで生成したファイルそのままの状況)

~/video$ cat test-video.m3u8

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:13
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:12.659044,
test-video000.ts
#EXTINF:9.175833,
test-video001.ts
#EXTINF:8.708700,
test-video002.ts
#EXTINF:10.377033,
test-video003.ts
#EXTINF:12.012000,
test-video004.ts
#EXTINF:9.009000,
test-video005.ts
#EXTINF:8.241567,
test-video006.ts
#EXTINF:12.012000,
test-video007.ts
#EXTINF:1.635267,
test-video008.ts
#EXT-X-ENDLIST

→ この値を、「test-videoXXX.ts?webaccel_secure_hash="MD5の値"&webaccel_secure_time="HEX値"」という形式に修正します。

(ワンタイムURLの形式についての詳細 は、

サイト全体ワンタイムURL機能の利用 | ウェブアクセラレータ | さくらのクラウド ドキュメント

 

今回は、

・シークレット文字列が 【STEP4-① ワンタイムURLのシークレットを決める】 で生成した文字列

・ワンタイムURLの有効期限を「2021/12/31 23:59:59 まで有効」

として、設定します。

 

◇ 書き換え実行例

onetime-replace.sh
―――
#!/bin/bash

org_file="/home/***/video/test-video.m3u8.org"
new_file="/home/***/video/test-video.m3u8"

secret="*****"(設定したシークレット文字列)
limit_time=$(date -d '2021/12/31 23:59:59' +%s)

if [ ! -e $org_file ]; then
echo "org_file が存在しません。"
exit
fi

if [ -e $new_file ]; then
rm $new_file
fi
touch $new_file

while IFS= read -r line
do
if [ ${line::1} == "#" ]; then
echo $line >> $new_file
else
md5=$(echo -n "//${line}/${secret}/${limit_time}/" | md5sum)
echo "${line}?webaccel_secure_time=${limit_time}&webaccel_secure_hash=${md5%% *}" >> $new_file
fi
done < $org_file

 

~/video$ cp -ip test-video.m3u8 test-video.m3u8.org
~/video$ ~/tools/onetime-replace.sh

 

◇記述の変更後(書き換え後)

~/video$ cat test-video.m3u8

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:13
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:12.659044,
test-video000.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=2f5e1163b2829b323fd4c9b1be299f7e
#EXTINF:9.175833,
test-video001.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=90ac6521bfaee2d630e172cbfc2a1052
#EXTINF:8.708700,
test-video002.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=c0847397ff49c39d3850563b420b24f0
#EXTINF:10.377033,
test-video003.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=b917f094b32000decbb9e2fa6c7560d5
#EXTINF:12.012000,
test-video004.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=d3c841f358ffdaf0bd71b5d00edb0318
#EXTINF:9.009000,
test-video005.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=2b5327e5aee07a0148256f96453c9d28
#EXTINF:8.241567,
test-video006.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=cffdbaaf44d8c4afbf1a0e8827984458
#EXTINF:12.012000,
test-video007.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=57dcd721d0f0044de83799f70644f4c2
#EXTINF:1.635267,
test-video008.ts?webaccel_secure_time=1640962799&webaccel_secure_hash=b303a86c70e9740385afcd43ff3c8449
#EXT-X-ENDLIS

元ファイルは不要のため退避

~/video$ mv test-video.m3u8.org ~/backup/
~/video$ mv test.mp4 ~/backup/

 

※ 今回は、htmlとHLS配信用ファイルを直下の階層に置いています。

  別ディレクトリに配置する場合は、ワンタイムURL発行時のファイルパス指定に、ディレクトリを含めます。(詳細 は、

サイト全体ワンタイムURL機能の利用 | ウェブアクセラレータ | さくらのクラウド ドキュメント

 

STEP4-④ m3u8ファイルを「ワンタイムURL」形式で呼び出すhtmlファイルの作成

HLS配信にてワンタイムURLを利用する場合、

・m3u8ファイル内に設定するtsファイルの取得URLの記述を、ワンタイムURLとして記述

・HTMLに記述するm3u8ファイルの取得URLの記述を、ワンタイムURLとして記述 ←STEP4-④でこちらを設定

を設定する必要があります。

 

※ 本記事では、Video.js を使用します

 

◇ m3u8のワンタイムURLの発行

・シークレット文字列が 【STEP4-① ワンタイムURLのシークレットを決める】 で生成した文字列

・ワンタイムURLの有効期限を「2021/12/31 23:59:59 まで有効」

として、設定します。

$ SECRET="*****"(設定したシークレット文字列)
$ LIMIT_TIME=$(date -d '2021/12/31 23:59:59' +%s)
$ FILEPATH="/test-video.m3u8"
$ MD5=$(echo -n "/${FILEPATH}/${SECRET}/${LIMIT_TIME}/" | md5sum)
$ echo "${FILEPATH}?webaccel_secure_time=${LIMIT_TIME}&webaccel_secure_hash=${MD5%% *}"
/test-video.m3u8?webaccel_secure_time=1640962799&webaccel_secure_hash=5b7f44851593725b3d0d5a1fd9fed43c

※ 今回は、htmlとHLS配信用ファイルを直下の階層に置いています。

  別ディレクトリに配置する場合は、ワンタイムURL発行時のファイルパス指定に、ディレクトリを含めます。(詳細 は、

サイト全体ワンタイムURL機能の利用 | ウェブアクセラレータ | さくらのクラウド ドキュメント

 

◇ htmlファイル作成 (例)

~/video$ vim play.html

~/video$ cat play.html
 
<html>
  <head>
    <title>Test Play</title>
  <link href="https://vjs.zencdn.net/7.17.0/video-js.css" rel="stylesheet">
  </head>
  <body>
    <video-js id=example-video width=1280 height=720
              class="vjs-default-skin" controls>
      <source
         src="./test-video.m3u8?webaccel_secure_time=1640962799&webaccel_secure_hash=5b7f44851593725b3d0d5a1fd9fed43c"
         type="application/x-mpegURL">
    </video-js>
  <script src="https://vjs.zencdn.net/7.17.0/video.js"></script>
    <script>
      var player = videojs('example-video');
    </script>
  </body>
</html>

※ 今回は、htmlとHLS配信用ファイルを同一ドメイン内に配置しています。

 ドメインにリソース共有する場合(htmlファイルとHLS配信ファイルの配信ドメインが別の場合など)は、CORS機能を利用してください(詳細:CORS機能の利用 | ウェブアクセラレータ | さくらのクラウド ドキュメント

 

 

STEP4-⑤ コンテンツのアップロード

アップロード対象の確認

~/video$ ls
index.html  test-video.m3u8   test-video001.ts  test-video003.ts  test-video005.ts  test-video007.ts  test.mp4
play.html   test-video000.ts  test-video002.ts  test-video004.ts  test-video006.ts  test-video008.ts

STEP2と同様に、ローカルのvideoディレクトリを、awscli の sync で同期します。

$ aws --profile video --endpoint-url=https://s3.isk01.sakurastorage.jp s3 sync ~/video s3://video/
upload: ./test-video.m3u8 to s3://video/test-video.m3u8
upload: ./play.html to s3://video/play.html
upload: ./test-video001.ts to s3://video/test-video001.ts
upload: ./test-video008.ts to s3://video/test-video008.ts
upload: ./test-video002.ts to s3://video/test-video002.ts
upload: ./test-video004.ts to s3://video/test-video004.ts
upload: ./test-video003.ts to s3://video/test-video003.ts
upload: ./test-video005.ts to s3://video/test-video005.ts
upload: ./test-video000.ts to s3://video/test-video000.ts
upload: ./test-video006.ts to s3://video/test-video006.ts
upload: ./test-video007.ts to s3://video/test-video007.ts

アップロードできたことを確認

$ aws --profile video --endpoint-url=https://s3.isk01.sakurastorage.jp s3 ls s3://video/
2021-12-XX XX:XX:XX        124 index.html
2021-12-XX XX:XX:XX        688 play.html
2021-12-XX XX:XX:XX       1207 test-video.m3u8
2021-12-XX XX:XX:XX    2419184 test-video000.ts
2021-12-XX XX:XX:XX   1051672 test-video001.ts
2021-12-XX XX:XX:XX    2317664 test-video002.ts
2021-12-XX XX:XX:XX    2745364 test-video003.ts
2021-12-XX XX:XX:XX    2341540 test-video004.ts
2021-12-XX XX:XX:XX    2272168 test-video005.ts
2021-12-XX XX:XX:XX    2451708 test-video006.ts
2021-12-XX XX:XX:XX    4578552 test-video007.ts
2021-12-XX XX:XX:XX    597840 test-video008.ts

 

STEP5. ウェブアクセラレータに、「ワンタイムURL」の設定

サイト一覧→「対象サイト」→「設定」から、

「サイト全体ワンタイムURL設定」を「有効」にし、STEP4で用意したシークレット文字列を設定し、「保存」します。

f:id:nozomi1773:20211221025204p:plain

 

STEP6. 配信確認

 

◇ ウェブアクセラレータ経由で 通常URLで httpsアクセス(403 Forbidden

ワンタイムURL以外の方式で、アクセスした場合、アクセス拒否(403)となります。

$ curl -v https://*****.1773.work/index.html

...(略)...
< HTTP/2 403
< server: nginx
< date: Xxx, XX Dec 2021 XX:XX:XX GMT
< content-type: text/html
< content-length: 146
<
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>

 

◇ ウェブアクセラレータ経由で ワンタイムURLで httpsアクセス(200 OK

# index.html の '2021/12/31 23:59:59' までアクセス可能なURLを発行します
---
$ BASEURL="https://****.1773.work"
$ SECRET="*****"(設定したシークレット文字列)
$ LIMIT_TIME=$(date -d '2021/12/31 23:59:59' +%s)
$ FILEPATH="/index.html"
$ MD5=$(echo -n "/${FILEPATH}/${SECRET}/${LIMIT_TIME}/" | md5sum)
$ echo "${BASEURL}${FILEPATH}?webaccel_secure_time=${LIMIT_TIME}&webaccel_secure_hash=${MD5%% *}"
https://*****.1773.work/index.html?webaccel_secure_time=1640962799&webaccel_secure_hash=be29d92d067ab9a76982ddeae561eb88

$ curl -v "https://*****.1773.work/index.html?webaccel_secure_time=1640962799&webaccel_secure_hash=be29d92d067ab9a76982ddeae561eb88"
...(略)...
< HTTP/2 200
< server: nginx
< date: Xxx, XX Dec 2021 XX:XX:XX GMT
< content-type: text/html
< content-length: 124
< accept-ranges: bytes
< last-modified: Xxx, XX Dec 2021 XX:XX:XX GMT
< x-amz-id-2: XXXXXXXXXXXXXXXXX
< x-amz-request-id: XXXXXXXXXXXXXXXXX
< cache-control: s-maxage=3600
< age: 446
< via: XXXXXXXXXXXXXXXXX
< x-webaccel-origin-status: 200
< x-cache: HIT
<
<html>
<head>
<title>配信テストページ</title>
</head>
<body>
配信テストページです。<br/>
</body>
</html>

 

◇ 用意したHLS配信ページにアクセス

・配信ページの play.html のワンタイムURLを発行します

# play.html の '2021/12/31 23:59:59' までアクセス可能なURLを発行します
---
$ BASEURL="https://****.1773.work"
$ SECRET="*****"(設定したシークレット文字列)
$ LIMIT_TIME=$(date -d '2021/12/31 23:59:59' +%s)
$ FILEPATH="/play.html"
$ MD5=$(echo -n "/${FILEPATH}/${SECRET}/${LIMIT_TIME}/" | md5sum)
$ echo "${BASEURL}${FILEPATH}?webaccel_secure_time=${LIMIT_TIME}&webaccel_secure_hash=${MD5%% *}"
https://*****.1773.work/play.html?webaccel_secure_time=1640962799&webaccel_secure_hash=a65c56a11e222a6211eabe5b652662c3

 

・発行したワンタイムURL「https://*****.1773.work/play.html?webaccel_secure_time=1640962799&webaccel_secure_hash=a65c56a11e222a6211eabe5b652662c3」にブラウザからアクセスします(このURLを有償コンテンツ購入者にのみ通知する想定

◇ 設定した有効期限までの間、動画を閲覧できます。

f:id:nozomi1773:20211221040856p:plain

◇ 不正なアクセス(hash値が正しくない)や、閲覧期限の有効期限切れの場合は、403 Forbidden で閲覧できません。

f:id:nozomi1773:20211221033355p:plain

 

以上、今年リリースの新機能を使って、有償コンテンツ想定のHLS配信を試してみた紹介でした!

go-carbon・carbon-relay-ngのモニタリング項目メモ

 

go-carbon

carbon-relay-ng は Graphite/Carbon に相当するもので、違いなどの詳細は 

GitHub - go-graphite/go-carbon をみてください。

 

carbon-relay-ng

carbon-relay-ng は Graphite の carbon-relay-py  に相当するもので、違いなどの詳細は GitHub - grafana/carbon-relay-ng をみてください。

 

go-carbon・carbon-relay-ngを用いたデータ集計を日々運用していく上で、エラー等の状態を検知するために、個人的に「このあたりをモニタリングすると良さそう」と思っている項目のメモです。使い方次第では、他にもあるかもしれません。

 

go-carbon 項目

go-carbonモニタリングに使う項目は、起動後1分は値がnullになるものので、起動後はスキップにするのがおすすめ。(起動してから1分間隔で送信:このあたり

TCP受信数

carbon.agents.【go-carbonのインスタンス名】.tcp.metricsReceived

TCP接続エラー数

carbon.agents.【go-carbonのインスタンス名】.tcp.errors

③ キャッシュ個数

carbon.agents.【go-carbonのインスタンス名】.cache.metrics

④ キャッシュサイズ

carbon.agents.【go-carbonのインスタンス名】.cache.size

⑤ キャッシュオーバーフロー

carbon.agents.【go-carbonのインスタンス名】.cache.overflow

go-carbon は、キャッシュの許容範囲を超えた場合、ディスクに書き込まれない分が喪失する。

この時点より早く検知したい場合は、cache.maxSizeの●%のcahce.size(④キャッシュサイズ)を監視しておくと良さそう。

 

carbon-relay-ng 項目

① 送信数

service_is_carbon-relay-ng.instance_is_【carbon-relay-ngのインスタンス名】.mtype_is_counter.unit_is_Metric.direction_is_in

② メトリック欠落エラー系

carbon-relay-ng/conn.go at b0c8921812d428c3370f3de932f73c038ecf3e94 · grafana/carbon-relay-ng · GitHub のあたりの項目で必要なもの

例:
service_is_carbon-relay-ng.instance_is_【carbon-relay-ngのインスタンス名】.mtype_is_counter.dest_is_【送信先】.unit_is_Metric.action_is_drop.reason_is_bad_pickle

③ 送信エラー系

carbon-relay-ng/destination.go at b0c8921812d428c3370f3de932f73c038ecf3e94 · grafana/carbon-relay-ng · GitHub のあたりの項目で必要なもの

例:
service_is_carbon-relay-ng.instance_is_【carbon-relay-ngのインスタンス名】.mtype_is_counter.dest_is_【送信先】.unit_is_Metric.action_is_drop.reason_is_slow_spool

④ Aggregator 破棄数

Aggregator で、設定した wait時間を過ぎて投入されたデータは破棄されます。

service_is_carbon-relay-ng.instance_is_【carbon-relay-ngのインスタンス名】.mtype_is_counter.module_is_aggregator.unit_is_Metric.what_is_TooOld

破棄された個数を検知したいときはこれをモニタリングすると良さそうです。

個数は把握できるものの、取りこぼしたメトリックの内容が把握できないので、別途ログ出力するようにも試みました。こちらは下記の記事に書きました。

 

 

carbon-relay-ng の ログ出力変更、reload対応のメモ

carbon-relay-ng

carbon-relay-ng は Graphite の carbon-relay-py  に相当するもので、違いなどの詳細は GitHub - grafana/carbon-relay-ng をみてください。

 

去年にやっていた内容なのですが、忘れる前に書き残しておきます。

carbon-relay-ng 0.13.0 に、3点程ほど手を加えたのですが、こちらの都合な内容含むため、PRは出してません。

なので行方不明にならないように置き場と内容をメモしておきます。

 
変更内容

① ログをログファイルに出力する

元々は journalログへの出力のみなので、明示的に指定したファイルに個別出力するようにしました。GitHub - Songmu/replaceablewriter を使わせていただきました。

 設定ファイル側にも追記しておく。

# This setting sets where the log is written. The default setting is "" (journal log only).
log_file = "/var/log/carbon-relay-ng/carbon-relay-ng.log"

 

② Aggregator の TooOld の詳細をログ出力する

carbon-relay-ng の Aggregator で、設定した wait時間を過ぎて投入されたデータは破棄されます。その破棄されたデータの個数は、

service_is_carbon-relay-ng.instance_is_${relay_instance}.mtype_is_counter.module_is_aggregator.unit_is_Metric.what_is_TooOld

に記録されますが、個数のみではなく、破棄された内容を記録したかったので、その詳細をログに吐くようにしました。

【出力例】
2020-06-23 16:14:33.901 [WARNING] Aggregator is receiving too old. key is hoge.ab.ab01.agg.count.test01, ts is 1576629860, quantized is 1576629840, value is 3.

TooOld 以外のモニタリングしておいた方が良さそうな内容については、別途、後日書こうかと思います 。

 

③ reload 対応

ログをファイル出力するようにしたので、ログローテーション用に、reload(SIGHUP)でログファイルをリオープンするという変更。

serviceファイルにも ExecReload を、追記しておく。

ExecReload=/bin/kill -HUP $MAINPID

 

carbon-relay-ng の deb生成の話は、nfpmでdeb作成するメモ の方に、書きました。

 

WSL1 + Cmder で wslbridge error: failed to start backend process のエラー解消

発生バージョン

Cmder 1.3.10 → 1.3.16

Windows 10 version 1903

内容

Windows 10 環境で、WSL1 + Cmder を利用していて Windows Update 後に下記のエラーになりました。

wslbridge error: failed to start backend process
note: backend error output: -v: -c: line 0: unexpected EOF while looking for matching `''
-v: -c: line 1: syntax error: unexpected end of file

ConEmuC: Root process was alive less than 10 sec, ExitCode=0.
Press Enter or Esc to close console...

解消方法メモ

Cmder の バージョンアップ ( 参考記事 : Cmder のバージョンを上げる方法 | ラボラジアン ) では治らなかったので、

wslbridge error: failed to start backend process · Issue #2382 · cmderdev/cmder · GitHub

というIssueのコメントが恐らく WSL2 用の手順で書かれているのですが、同様の対応でWSL1でも解消しました。WSLの詳細に疎いので原因は理解できていませんが、解消方法だけ、書き残しておきます。

  • Cygwin Snapshots からダウンロードした cygwin1.dll の置き換え ( vendor\conemu-maximus5\ConEmu\wsl 配下 )

  • Releases · Biswa96/wslbridge2 · GitHub からダウンロードした v0.6 の wslbridge2 ・ wslbridge2-backend 新規配置 ( vendor\conemu-maximus5\ConEmu\wsl )

  • Cmder の WSL bash task の設定変更 ( conemu-cyg-64.exe --wsl → conemu-cyg-64.exe %ConEmuBaseDirShort%\wsl\wslbridge2.exe ) 
WSL bash task (例) : 
<変更前>
*set "PATH=%ConEmuBaseDirShort%\wsl;%PATH%" & %ConEmuBaseDirShort%\conemu-cyg-64.exe --wsl -cur_console:pm:/mnt
<変更後>
*set "PATH=%ConEmuBaseDirShort%\wsl;%PATH%" & %ConEmuBaseDirShort%\conemu-cyg-64.exe %ConEmuBaseDirShort%\wsl\wslbridge2.exe -cur_console:pm:/mnt

あとで wslbridge.exe とかに変更してもとの方だとエラー再現して起動しないので、wslbridge2.exe で起動する必要があるようでした。

  • 再度Cmderを起動して、wslbridge2.exe がセキュリティソフトにはじかれるようなら許可設定する
はじかれるとこんな感じに : 

{PID:264} failed to run shell (13): Permission denied
{PID:264} shell: `C:\Tool\cmder_mini\vendor\conemu-maximus5\ConEmu\wsl\wslbridge2.exe`
{PID:264} dir: `/cygdrive/c/Tool/cmder_mini`

ConEmuC: Root process was alive less than 10 sec, ExitCode=0.
Press Enter or Esc to close console...

 

これで無事 WSL1 接続のエラーが解消しました。

nfpmでdeb作成するメモ

rpmdeb をつくるのに nfpm が便利と教えていただいて、半年ほど前から nfpm を使うようになりました。手軽に使えて便利です。

 

詳細なインストール方法やconfigの内容はドキュメント(https://nfpm.goreleaser.com/)で確認してみてください。

 

使用例 : carbon-relay-ng の deb生成してみる

carbon-relay-ng を例に、私がdeb生成で使っている内容を、雑にメモしておきます。

nfpmインストール

# wget https://github.com/goreleaser/nfpm/releases/download/v1.4.1/nfpm_amd64.deb
# apt install ./nfpm_amd64.deb

 

梱包するものの準備

carbon-relay-ng
L nfpm.yaml
L files
L carbon-relay-ng <- 事前にバイナリをビルドしておく
L carbon-relay-ng.conf
L carbon-relay-ng.service
L tmpfile.conf
L scripts
L before_install.sh <- ディレクトリ作成・所有変更など
L after_install.sh <- daemon-reload など

 

nfpm.yaml 準備

# nfpm config file for carbon-relay-ng
name: "carbon-relay-ng"
arch: "amd64"
platform: "linux"
version: "0.13.0"
release: "1"
section: "default"
provides:
- carbon-relay-ng
maintainer: "Dieter Plaetinck"
description: |
Fast carbon relay+aggregator with admin interfaces for making changes online - production ready
vendor: "http://github.com/grafana/carbon-relay-ng"
homepage: "http://github.com/grafana/carbon-relay-ng"
license: "2-clause BSD license"
bindir: "/usr/bin"
empty_folders:
- /var/log/carbon-relay-ng
- /etc/carbon-relay-ng
config_files:
./files/carbon-relay-ng.conf: "/etc/carbon-relay-ng/carbon-relay-ng.conf"
files:
./files/carbon-relay-ng: "/usr/bin/carbon-relay-ng"
./files/carbon-relay-ng.service: "/lib/systemd/system/carbon-relay-ng.service"
./files/tmpfile.conf: "/etc/tmpfiles.d/carbon-relay-ng.conf"
scripts:
preinstall: "scripts/before_install.sh"
postinstall: "scripts/after_install.sh"

私がはじめに使っていた時ドキュメントあまり読んでなかったので失敗したのですが、アップグレードの際に config_files (上書きしないを選択できる) と files (問答無用で上書き) があるので適宜使い分けます。

 

nfpm実行 ( コマンド )

nfpm pkg --target carbon-relay-ng_0.13.0-1_amd64.deb

 

nfpm実行シェルの例 ( スクリプト内から抜粋 )

github-release.sh <- nfpm 実行とgithubにアップロード
L carbon-relay-ng <- 先ほどの準備物一式
L foo
L var


for APP_NAME in "carbon-relay-ng" "foo" "var"
do
PKG_VERSION=$(grep version ${APP_NAME}/nfpm.yaml | cut -d'"' -f 2)
RELEASE_VERSION=$(grep release ${APP_NAME}/nfpm.yaml | cut -d'"' -f 2)
( cd ${APP_NAME} && nfpm pkg --target "${APP_NAME}_${PKG_VERSION}-${RELEASE_VERSION}_amd64.deb" )



done

 

こんな感じでdeb作成してます。手軽に使えて便利です。

 

蛇足

前のバージョン( v1.1.8 ) を使っていたときに、release version が - ではなく ~ になってしまう挙動でしたが、新しいバージョン ( v.1.4.1 ) では - になるようになったようです。

もしも、同じ事象に遭遇されている方がいたらバージョン確認してみてください。

feat: version release/prerelease options by astorath · Pull Request #111 · goreleaser/nfpm · GitHub で変更になったそうです )

nfpm --version
1.1.8

nfpm pkg --target carbon-relay-ng_0.13.0-1_amd64.deb

dpkg -I carbon-relay-ng_0.13.0-1_amd64.deb | grep -i version: Version: 0.13.0~1 <- "~" になる

----------------------------
# nfpm --version 1.4.1

nfpm pkg --target carbon-relay-ng_0.13.0-1_amd64.deb

dpkg -I carbon-relay-ng_0.13.0-1_amd64.deb | grep -i version: Version: 0.13.0-1 <- "-" になる

 

carbon-relay-ng の v0.12.0以前をビルドするメモ

carbon-relay-ng

carbon-relay-ng は Graphite の carbon-relay-py  に相当するもので、違いなどの詳細は GitHub - grafana/carbon-relay-ng をみてください。

 

2019年12月中旬のタイミングで

carbon-relay-ng の owner が graphite-ng から grafana(Grafana Labs) に変更になっています。変更タイミングは Fix path to new grafana org by Dieterbe · Pull Request #384 · grafana/carbon-relay-ng · GitHub のあたりでした。

 

2020年6月19日現在の最新バージョンである v0.12.0 など、Fix path to new grafana org by Dieterbe · Pull Request #384 · grafana/carbon-relay-ng · GitHub  のcommit以前のバージョンをビルドする際には、こちらの差分を取り込むか、make実行時の配置パスを過去の graphite-ng に合わせるか、などで対応しておく必要があります。

なお、 graphite-ng は現在もう存在しません。

( 0.12.0 以前 )
graphite-ng/carbon-relay-ng

( 現在 )
grafana/carbon-relay-ng

 

うっかりこのことを忘れてビルドすると失敗します。

# make
cd ui/web && go-bindata -pkg web admin_http_assets/...
find . -name '*.go' | grep -v '^\.\/vendor' | xargs gofmt -w -s
CGO_ENABLED=0 go build -ldflags "-X main.Version=0.12.0" ./cmd/carbon-relay-ng
# github.com/grafana/carbon-relay-ng/cmd/carbon-relay-ng
cmd/carbon-relay-ng/carbon-relay-ng.go:85:18: cannot use formatter (type *logger.TextFormatter) as type "github.com/grafana/carbon-relay-ng/vendor/github.com/sirupsen/logrus".Formatter in argument to "github.com/grafana/carbon-relay-ng/vendor/github.com/sirupsen/logrus".SetFormatter:
*logger.TextFormatter does not implement "github.com/grafana/carbon-relay-ng/vendor/github.com/sirupsen/logrus".Formatter (wrong type for Format method)
have Format(*"github.com/graphite-ng/carbon-relay-ng/vendor/github.com/sirupsen/logrus".Entry) ([]byte, error)
want Format(*"github.com/grafana/carbon-relay-ng/vendor/github.com/sirupsen/logrus".Entry) ([]byte, error)
cmd/carbon-relay-ng/carbon-relay-ng.go:163:34: cannot use meta (type "github.com/grafana/carbon-relay-ng/vendor/github.com/BurntSushi/toml".MetaData) as type "github.com/graphite-ng/carbon-relay-ng/vendor/github.com/BurntSushi/toml".MetaData in argument to table.InitFromConfig
Makefile:five: recipe for target 'build' failed
make: *** [build] Error 2

ウェブアクセラレータの2019年にリリ-スした新機能の振り返りと機能活用レシピ

これは さくらインターネット Advent Calendar 2019  の10日目の記事です🎄

 

さくらインターネットCDNサービス「ウェブアクセラレータ」の開発・運用をしている稲波です。

CDNサービスって何?」という部分の説明については、こちらの記事内では省略させていただきますので、そのあたりについてを確認されたい方は、上記サービスサイトや下記のさくナレ連載をご覧ください。

ウェブアクセラレータは、2016年10月に正式提供開始したCDNサービスです。*1今年で、3周年を迎えました!

ウェブアクセラレータは随時機能追加を行っており、今年は、6つの新機能をリリースしました!*2

本記事では、2019年にリリースした新機能を振り返り、その機能活用のレシピを書きました。

 

2019年にリリースした6つの新機能

各々の詳しい設定方法については、マニュアル をご覧ください。

1 . コントロールパネルでキャッシュ期間が設定できる機能

こちらは、先週リリースしたばかりの機能です。

今まで、ウェブアクセラレータのキャッシュ期間設定は、オリジンサーバ(お客様のWebサーバ)側でキャッシュ制御用のレスポンスヘッダを付与する設定をしていただく必要がありました。

が、この機能が追加されたことで、コントロールパネルより一括でキャッシュ期間を設定できるようになりました。*3

 

2. リクエスプロトコルの選択機能

リクエスプロトコルについて、今までの「http・httpsの両方を許可する」のほかに、「httpsのみ許可する」「httpsにリダイレクト」が追加になり、選択可能になりました。

オリジンサーバ側で設定することなく、お手軽にリダイレクトの設定などができるようになりました。

 

3. オリジンガード機能

ウェブアクセラレータを経由しないオリジンサーバへの直接アクセスをガードできる機能です。ウェブアクセラレータで発行するヘッダ(X-WebAccel-Guard)のトークン値に一致する場合のみアクセス許可する設定を、オリジンサーバに設定することで、悪意のあるアクセスからオリジンサーバを守ることができます。

オリジンガードトークンの更新準備の機能も先日追加されましたので、トークン値を定期的に変更する際にも、解除することなく切り替え可能です。

 

4. ワンタイムURL機能

指定した有効期限内にのみコンテンツを公開するための機能です。

オリジンサーバにて、レスポンスヘッダ(X-WebAccel-Secret)を使ってシークレットキーと、URLのクエリパラメータをつかって有効期限を設定することで、ウェブアクセラレータ経由で配信した際に期間限定配信(有効期限内にのみアクセス可能)ができます。

※ こちらは、オリジンガードトークン機能と併せてご利用ください(オリジンサーバに直接アクセスできるとシークレットキーが確認できてしまうため、ウェブアクセラレータからの接続のみ許可する必要があります)。

 

5. 操作ログが記録されるようになった

さくらのクラウドのイベントログ機能で、ウェブアクセラレータの操作ログも記録されるようになりました。ウェブアクセラレータの操作について、過去の履歴を確認できます。

 

6. Rangeリクエストをキャッシュできるようになった

Rangeヘッダ付きのリクエストがあったときに、キャッシュ対象の場合は、バックグラウンドでコンテンツ全体を取得しキャッシュできるようになりました。

キャッシュ済みのコンテンツについて、Rangeの指定範囲が異なるリクエストがあった場合にも、キャッシュ済みのコンテンツより配信ができるので、オリジンサーバへのリクエスト負荷が削減できます。

 

機能活用のレシピを考える

機能単体の設定方法については、マニュアル に記載していますが、参考として、機能を組み合わせてつかったりなどで、どう活用することができるのかについて書いてみました。紹介するアプローチ方法は、ごく一部ですので、ウェブアクセラレータ自体は自由に使っていただけます。

※ あくまでこういう活用方法もできますよという紹介で、推奨しているわけではないので、ご承知ください。

 

1. コントロールパネルでキャッシュ期間設定 × オリジンレスポンスにキャッシュ無効の設定 をしてみる

オリジンサーバ(お客様のWebサーバ)側でキャッシュ制御用のレスポンスヘッダを付与する場合の、配信フローは下記になります。キャッシュ対象にCache-Control: s-maxageを設定します。

f:id:nozomi1773:20191205001203p:plain

オリジンのレスポンスヘッダでキャッシュ制御の例

オリジンのレスポンスヘッダにCache-Control: no-store などのキャッシュ無効の設定をしておくことで、その無効ヘッダが設定されたコンテンツは、コントロールパネルでのキャッシュ期間設定のキャッシュ対象外になります。キャッシュさせたくないパスや拡張子に無効設定しておくことで、その箇所はパススルー(ウェブアクセラレータをプロキシとして使う)させ、他は、キャッシュさせるという使い方も可能です。

※ この場合、キャッシュヒット率としては、あまり高くはならない可能性があるのでご注意ください。

f:id:nozomi1773:20191205001312p:plain

コントロールパネルでキャッシュ制御例

という風にキャッシュさせない設定をすることで、それ以外の機能適用条件に合うすべての対象が、キャッシュ期間設定で一括で適用されているといった活用方法もできるのではないかと思います。

キャッシュさせる設定を付与するか、キャッシュさせない設定を付与するか、運用するサイトの都合上便利な方で対応いただけると思います(合わせ技も可能です)。

 

補足 : 特定のヘッダがある場合にno-storeを付与する nginx の設定については、過去に、

という記事を書きました。

こちらの記事自体は、認証ヘッダについて書きましたが、コントロールパネルからキャッシュ設定した場合は機能仕様としてキャッシュ対象となりません。

違うヘッダ、例えば Set-Cookie で似たようなことがしたい場合などがありましたら、参考にしてみてください。

 

2. ワンタイムURL × Rangeリクエストキャッシュ

ワンタイムURLでの配信においても、Rangeリクエストキャッシュが可能です。1コンテンツあたり1GiB以内であればキャッシュ可能なため、サイズが大きめのコンテンツでリクエスト範囲(再生開始位置など)が違う場合でも、ワンタイムURLで限定公開コンテンツのキャッシュ配信をお手軽に実現できます。

f:id:nozomi1773:20191205001424p:plain

ワンタイムURLでもRangeリクエストキャッシュ可

 オリジンへの負荷が軽減できると思います。

※図では省略しましたが、上述の通りオリジンガードとの併用が必要です。

 

以上、新機能の振り返りと、機能活用のレシピでした。

最後まで読んでくださってありがとうございました!

*1:ベータ版は2016年7月28日から提供していました。

*2:勿論、新機能以外にも諸々の改善が実施されていたりします。

*3:適用条件を理解していただく必要があるため、実際に使う場合には、マニュアルを読んでいただきたいです!