Amazon S3とCloudFrontで独自ドメインをSSL対応し静的ファイルを配信する

VPSで単純な静的ファイルによって構築されているサイトを公開しているのですが、収益につなげていないので月額500円以上は高いなと思いました。

単純な静的ファイルならS3のウェブサイトホスト機能を使う方がコスト的には圧倒的に良いが、SSL対応ができないため考えていなかったのだが、CloudFrontを通すとAmazonの無料SSL証明書を使ってSSL経由でコンテンツを配信できることを知りました。

しかも単純にS3から直接配信するよりコストも削減できるらしい。理由としてはユーザーにより近い地域からデータを配信するようになり結果的にコストが削減できるからだそうだ。これは大変良い。

というわけで、今回は上記のケースでAWSを使って格安で静的ファイルをSSL対応して配信するのにチャレンジしてみます。AWSのGUIはよく変更されるので、スクリーンショットは使用せずにテキストで解説を行いたいと思います。

独自ドメインで静的ウェブサイトをホストする

まず前提のS3でコンテンツを公開しておきます。

余談ですが、S3単体で独自ドメインから公開したい場合はAmazonのヘルプを参考にしながらポリシーを設定します。CloudFrontを経由する場合は独自ドメイン経由でS3から配信する必要はないのでこの設定は不要です。

{
"Version":"2012-10-17",
"Statement":[{
"Sid":"PublicReadGetObject",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::example-bucket/*"
]
}
]
}

Amazonの無料SSL証明書を作成する

AmazonのCertificate Managerにアクセスして証明書を作成します。事前にドメインの admin@example.com でメールを受信できるようにしておきます。Amazonではこのメールアドレスにメールを送信し、記載されている認証用URLにアクセスすることで証明書を発行できます。

他にwhoisの情報に記載されているメールアドレスにもメールが送られてくるらしいが、ドメインの情報に代理情報を掲載する場合が個人用途では多いと思うので上記の方法がよく使われるんじゃないでしょうか。

S3のコンテンツをCloudFrontからSSL経由で配信させる

CloudFrontの設定

CloudFrontにアクセスしてディストリビューションを作成します。今回私はこちらの記事を参考にしましたが、アクセスして指示に従えば簡単に作成することができます。

ディストリビューションを作成する際にはCNAMEに公開したいドメイン名を設定し、証明書は先程作成したものを選択、OriginsはS3のバケットexample.com.s3.amazonaws.com(セレクトボックスに自動で出てくるので深く考えなくて良い)を設定すれば完了です。

これでS3はファイル置き場、配信はCloudFrontが行うという構成になります。

S3に保存したファイルが反映されない・削除したファイルが削除されない

何度か試行錯誤しているとファイルが反映されなかったり、削除されなかったりという減少に遭遇して混乱すると思います。まず、ファイルを新しく作成したはずなのにCloudFrontにファイルが反映されないのはCloudFrontのネガティブキャッシュが効いている可能性があり、ファイルを削除してもCloudFrontから削除されないのはキャッシュが効いている可能性があります。

このキャッシュを再構築するには、ディストリビューションのページのInvalidationsタブのCreate Invalidationから削除したいファイルのパスを実行します。

DNSの設定

そしてDNSから対象ドメインのCNAMEをCloudFrontに向ければOKです。ディストリビューションの詳細に作成したディストリビューションに適用されたサブドメインが記載されているのでそこに向ければOKです。

wwwなしのドメインを向けたい場合はCNAMEが使えないので、簡単に設定するならAレコードで設定すれば、独自ドメインかつSSL経由でコンテンツが確認できます。しかしながら、AレコードだとIPアドレスが変わった時に対応できないのでAmazon Route53のAレコード設定にあるエイリアス機能を利用します。

Amazon Route53を使ってwww無しにCNAMEのような設定を行う

Amazon Route53は、AレコードにURLを指定することができる独自のエイリアス機能を持つらしく、wwwなしにCNAME的なことができるというわけです。Amazon Route53の設定はこちらを参考に進めればすぐに完了します。

私は最初Amazon Route53のエイリアス機能というのは、独自のエイリアスレコードがあるのかと思っていましたが、実際はAレコードの値設定の時にAliasをONにすることで、IPアドレスではなくアドレスで指定できるようです。

気になるRoute53の料金はPV5万弱で50円となるとのことで少々高めではありますが、それでも静的ファイルで構成されるコンテンツをVPSでホスティングしてSSL対応するのと比べれば圧倒的に安く済むかと思われます。

独自ドメインメールをGmailへ転送できるサービスまとめ

私はムームドメインでドメインを取得して、ムームーメールを使っています。ムームーメールは月額50円でおそらく最安です。もちろんマルチドメインも対応しています。

ただし、ムームーメールはムームドメインのネームサーバーを利用している必要があるので、Amazon Route53などの外部ネームサーバーを利用してしまうと使えないのが難点です。

そこで、外部のネームサーバーからでも利用できるメールサーバーを探してみました。

zohoメール

月額2$で利用できるようで、マルチドメインにも対応しているようです。おそらく転送もできるんじゃないかと思いますが、月額2$だとムームーメールの4倍ですから厳しいですね。

さくらのメールボックス

昔使っていたさくらのメールボックスは相手先の迷惑メールに入ってしまったような気がしましたが、SPFレコードの設定で問題なくいけますね。

さくらのメールボックスは年間契約で1029円、月額85円なのでムームーメールとほぼ変わらない金額ですね。こいつがムームーメールが使えなくなった時の代替案ですね!

Tagirl

今回の目的は「独自ドメインのメールを転送すること」なので、Tagirlが非常にマッチします。Tagirlは無料で使え、設定されたドメインのメールを指定したメールアドレスに単純に転送してくれるサービスです。

一つ難点があるとすると個人によって提供されているサービスなので突然の終了などの可能性があります。あと公式サイトにも

貧弱な運営基盤
一個人の趣味という形で運営されているので、非常に時間的・資金的制約が大きい状況下で運用されています。カンパ求む。

と書いてあるくらいなので、完璧を求める方には向かないかと思います。

Qiita API v2を利用して記事をローカルにエクスポート・バックアップする

APIを利用してQiitaの記事をエクスポート・バックアップしてみます。今日はPHPを使って書いてみることにします。

<?php
class Qiita {
const ITEM_LIMIT = 20;
private $token;
public function __construct($token) {
$this->token = $token;
}
public function getInfo() {
return $this->getRequest('https://qiita.com/api/v2/authenticated_user');
}
public function getItems($page) {
return $this->getRequest(
'https://qiita.com/api/v2/authenticated_user/items?' .
http_build_query(['page' => $page, 'per_page' => self::ITEM_LIMIT])
);
}
private function getRequest($url) {
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->token,
'Content-Type: application/json',
],
]);
$result = curl_exec($curl);
curl_close($curl);
return json_decode($result);
}
}
$token = 'xxxxxxxxxxx';
$workdir = '/tmp/qiita_' . $token;
$setouchi = new Qiita($token);
$max = ceil($setouchi->getInfo()->items_count / Qiita::ITEM_LIMIT);
if (!file_exists($workdir)) {
mkdir($workdir);
}
$created = 0;
$updated = 0;
$skip = 0;
for ($i = 1; $i <= $max; $i++) {
echo "リクエスト($i) -> ";
foreach ($setouchi->getItems($i) as $remoteItem) {
$filename = $workdir . '/' . preg_replace('/.*\//', '', $remoteItem->url);
if (file_exists($filename)) {
$localItem = file_get_contents($filename);
} else {
$localItem = false;
}
if ($localItem === false) {
$created ++;
file_put_contents($filename, json_encode($remoteItem));
echo '+';
} else if (strtotime($remoteItem->updated_at) > strtotime(json_decode($localItem)->updated_at)) {
$updated ++;
file_put_contents($filename, json_encode($remoteItem));
echo '^';
} else {
$skip ++;
echo '_';
}
}
echo PHP_EOL;
}
echo '---' . PHP_EOL;
echo '新規作成: ' . $created . PHP_EOL;
echo '更新あり: ' . $updated . PHP_EOL;
echo '更新なし: ' . $skip . PHP_EOL;

このファイルの$tokenのところと$workdirのところを好きなように変更してもらって、以下のように実行すると保存されます。

$ php qiita-copy.php
リクエスト(1) -> ++++++++++++++++++++
リクエスト(2) -> ++++++++++++++++++++
リクエスト(3) -> ++++++++++++++++++++
リクエスト(4) -> ++++++++++++++++++++
リクエスト(5) -> ++++++++++++++++++++
リクエスト(6) -> ++++++++++++++++++++
リクエスト(7) -> ++++++++++++++++++++
リクエスト(8) -> ++++++++++++++++++++
リクエスト(9) -> ++
---
新規作成: 162
更新あり: 0
更新なし: 0

この機能はバックアップだけでなく、ブログへの移行(別途インポート用フォーマットに変換する処理を書く必要はある)だったり、Evernoteに入れる(別途EvernoteのAPIを研究する必要あり)など用途はいろいろとあるかと思います。

Python3で家の家電をコントロールする

HomeBridgeで家をスマホからコントロールできるようにしているのだが、iPhoneを取りに行くのすら面倒くさくなった。せっかくHomeBridge用にconfig.jsonに自宅の家電をコントロールするための赤外線情報があるので、それをparseしてターミナルから操作することにした。Pythonで書いたのは勉強がてら。Groovyとで悩んだけど今日はIDE開きたくなかったから。。

使い方

そのまま叩くとアクセサリ一覧

$ python3 home.py
IRKit0002.local リビングの電気
IRKit0002.local リビングの冷房
IRKit0001.local 寝室の冷房
IRKit0001.local 寝室の電気
IRKit0002.local テレビ
IRKit0001.local ルンバ

こんな感じで使います

$ python3 home.py リビングの電気 on_form
$ python3 home.py リビングの電気 off_form

コード

家の中で使うのでかなり適当です。今日は早く寝ようと思ったけど簡単じゃなかった眠い。。Python慣れてないのでひとまずこれで。。

#!/usr/local/bin/python3
# coding: utf-8
import urllib.request, sys, json
f = open("/path/to/config.json")
config = json.load(f)
f.close()
accessories = {}
for it in config['accessories']:
accessories[it['name']] = it
if len(sys.argv) == 3:
name = sys.argv[1]
action = sys.argv[2]
accessory = accessories[name]
infrared_code = json.JSONEncoder().encode(accessory[action]).encode('utf-8')
target_url = 'http://' + accessory['irkit_host'] + '/messages'
method = "POST"
headers = {
"Content-Type": "application/json",
"X-Requested-With": "curl"
}
request = urllib.request.Request(
target_url,
data=infrared_code,
method=method,
headers=headers
)
with urllib.request.urlopen(request) as response:
sys.exit()
sys.exit()
for k, v in accessories.items():
print(v['irkit_host'], k)

まとめ

素直にiPhone取りに行けばよかった眠い。。が、代償にPythonを好きになった。

参考

MacへのPython3のインストール

Macにデフォルトで入っていたPythonのバージョンは2だった。Request関連でPython2よりPython3が良かったのでまずはPython3をBrewで入れる。

brew install python3
python3 -V

wcコマンドでファイルの全体の行数と指定したパターンにマッチした行数を数える

特定ディレクトリ内の全ファイルの行数を確認してSUMを得る

$ wc -l ./*
0 ./hoge2
0 ./hoge3
0 total

特定ファイルの行数だけを確認する

$ wc -l ./hoge2
0 ./hoge2

特定ファイルのパターンにマッチする行数を調べる

$ cat hoge1 | grep test | wc -l
2

特定ディレクトリ配下にある全ファイルの行からパターンにマッチする行数を調べる

cat ./* | grep test | wc -l
2

IntelliJを快適に使うための設定まとめ

以下の設定を行うことでかなり快適になる。全ての設定を網羅できず、いつも中途半端な状態で使っているのでまとめ。

  • vimプラグインの導入
  • .ideavimrcを作る
    • 昔は結構設定並べてたけど面倒くさくなって最近はこれだけ
  • フォントの設定
    • フォントの設定はIntelliJのメニューのPreferenceからfontと検索すると出てきます。EditorのFontになります。 スクリーンショット 2017-08-08 20.28.19.png
    • Line spasingの項目から行間を変更することができる。
    • デフォルトのフォントも十分見やすいけど最近自分がVimとかで使っているフォントをInnteliJに適用したら全然違う。
    • VimとかEmacsみたいなのはバリバリカスタマイズして使うのが楽しいしそれが醍醐味なんだけど、IntelliJは何もしなくても使えるからカスタマイズし忘れがちだけど、やっぱりカスタマイズするとどんなものでも違いますね。
  • 言語毎のインデントの設定
    • 全ての言語の設定をしておくと良いです。ついついJavaだけとかJSだけ。。。とやっていると後々面倒くさいので腹くくって全言語設定しておくのが良いです。
  • 特殊なファイル用のプラグインを追加
    • twig
    • Makefile (MacVimはデフォルトでMakefile対応してたかもだけどIntelliJは対応していなかったかも。プラグインして対応しました。)
    • ES7とかはデフォルトで対応していたような?TypeScriptは間違いなくデフォルト対応でした

次パソコン買い替えた時のために他にあればどんどん追記していきます。