ALB(Application Load Balancer)でWebサービスを冗長化する

概要

ALBを使ってアプリケーションを冗長化する手順です。

HTTPS接続でアプリケーションにアクセス出来るところまでをこの記事で紹介します。

前提条件

以下の事前条件が必要です。

  • VPCの作成を行っておく
  • 最低でも2台のWebサーバインスタンスを起動させておく事
  • ロードバランサー用サブネットの作成が行われている事(後で説明します。)

事前準備その1(ロードバランサー用サブネットの作成)

以下は公式サイトに書かれている内容です。

ロードバランサーのアベイラビリティーゾーンを指定します。ロードバランサーは、これらのアベイラビリティーゾーンにのみトラフィックをルーティングします。アベイラビリティーゾーンごとに 1 つだけサブネットを指定できます。ロードバランサーの可用性を高めるには、2 つ以上のアベイラビリティーゾーンからサブネットを指定する必要があります。

今回検証で利用している東京リージョンには ap-northeast-1aap-northeast-1c の2つのアベイラビリティーゾーンが存在するので、それぞれでサブネットの作成を行います。

サービス → VPC → サブネット → 「サブネットの作成」より作成を行います。

ap-northeast-1a で サブネットを作成します。
以下のように入力を行います。

  • ネームタグ

    • account_api_alb_1a
    • 開発環境アカウント用APIのALB用と分かる名前を付けています。分かりやすい名前であれば何でも構いません。
  • VPC

    • 利用対象となるVPCを選択します。
  • IPv4 CIRD block

    • 192.0.30.0/24
    • ネットワークの設計方針にもよりますが今回は 192.0.30.0/24 を割り当てます。

alb_subnet_step1.png

続いて ap-northeast-1c でも同じ要領でサブネットを作成します。
※先程とほとんど同じなので、入力内容に関しての詳細は省略します。

alb_subnet_step2.png

事前準備その2(SSLの証明書の用意)

SSLで接続を可能にするのでSSL証明書の用意が必要です。

今回は検証なので自己証明書を利用する事にします。

以前、LAMP 環境構築 PHP 7 MySQL 5.7(前編) という記事を書きました。

こちらに載っている手順を参考に自己証明書を用意します。

ALB(Application Load Balancer)の新規作成

ここからが本題になります。
サービス → EC2 → ロードバランサー → ロードバランサーの作成 を選択します。

alb_step1.png

Step1 ロードバランサーの設定

基本的な設定を行っていきます。
名前を入力します。(今回はaccount-api-alb)という名前を付けました。

インターネットに公開するサービスを想定しているので、スキーマは「インターネット向け」を選択します。

ロードバランサーのプロトコルにHTTPSを追加します。

alb_step2-1.png

アベイラビリティーゾーンに先程作成したサブネットを割り当てます。

alb_step2-2.png

Step2 セキュリティ設定の構成

SSL証明書の設定を行います。

alb_step2-3.png

証明書の名前は分かりやすい名前でOKです。

プライベートキーには事前準備で作成した、プライベートキーを入れます。
-----BEGIN RSA PRIVATE KEY----- から -----END RSA PRIVATE KEY----- までを全てコピーして下さい。

パブリックキー証明書には -----BEGIN CERTIFICATE----- から -----END CERTIFICATE----- までの内容を全てコピーして下さい。

セキュリティポリシーは ELBSecurityPolicy-2016-08 を選択します。

※2017-05-22 現在、この手順で問題なく証明書の追加が出来るハズなのですが Certificate not found というエラーが発生しロードバランサーの作成に失敗してしまいます。

証明書のアップロードを aws-cli を使って事前に実施するようにしたら上手く行きました。

証明書のアップロード
aws iam upload-server-certificate --server-certificate-name self-certificate --certificate-body file://crt.crt --private-key file://private.key

file:// を付けるのがポイントです。これがないと上手くアップロード出来ませんでした。

--server-certificate-name には任意の名前を入力して下さい。

上手く行くと下記のようなレスポンスが返ってきます。

証明書アップロードのレスポンス
{
    "ServerCertificateMetadata": {
        "ServerCertificateId": "XXXXXXXXXXXXXXXXXXXXX",
        "ServerCertificateName": "self-certificate",
        "Expiration": "2018-05-22T04:14:02Z",
        "Path": "/",
        "Arn": "arn:aws:iam::999999999999:server-certificate/self-certificate",
        "UploadDate": "2017-05-22T05:58:44.754Z"
    }
}

アップロード完了後に「AWS Identity and Access Management(IAM)から、既存の証明書を選択する」を選んで先程アップロードした証明書を選択して下さい。

alb_step2-3.1.png

この問題については 既存の ELB に SSL 証明書を追加しようとすると Server Certificate not found for the key というエラーになる件の解決方法 を参考にさせて頂きました。

Step3 セキュリティグループの設定

セキュリティグループの設定を行います。

alb_step2-4.png

Step4 ルーティングの設定

ターゲットグループの新規作成を行います。

alb_step2-5.png

名前、プロトコル、ヘルスチェック用のURLの設定等を行います。

Step5 ターゲットの登録

ロードバランサーの配下で起動するインスタンスを選択します。

alb_step2-6.png

作成に必要な情報入力は以上となります。

確認画面に進み作成を行いしばらくすると、ロードバランサーが作成され利用可能な状態となります。

※サービス → EC2 → ロードバランサー より確認が出来ます。

alb_step3.png

動作確認

サービス → EC2 → ロードバランサー よりDNSが確認出来るので、動作確認を行います。

curl -kv https://account-api-alb-000000000.ap-northeast-1.elb.amazonaws.com/
*   Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to account-api-alb-000000000.ap-northeast-1.elb.amazonaws.com (0.0.0.0) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: system
> GET / HTTP/1.1
> Host: account-api-alb-000000000.ap-northeast-1.elb.amazonaws.com
> User-Agent: curl/7.51.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Mon, 22 May 2017 07:26:02 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Server: nginx/1.12.0
< X-Request-Id: 76c7e41f-1a4e-4328-972c-b98055e84395
< Cache-Control: no-cache, private
<
* Curl_http_done: called premature == 0
* Connection #0 to host account-api-alb-000000000.ap-northeast-1.elb.amazonaws.com left intact
{"code":404,"message":"Not Found"}

各Webサーバのログを確認すると、処理が振り分けられているのが、確認出来ます。

本番環境での運用に向けて

ここまで簡単に作成が出来ましたが実環境で運用を行うにはまだまだ考慮が必要な点が多いです。

  • SSL証明書を正式な物にする(自己証明書で運用とかはさすがに厳しいと思います)
  • 独自ドメインでのアクセスを可能にする
  • 各EC2のログに記載されているIPがロードバランサーの物になっている

※これらの手順は順次行っていく予定ですので、準備が出来次第記事を書く予定です。

最後まで読んで頂きありがとうございました。

続きを読む

IPv6でアクセスすると"via IPv6"って出るやつ

IPv6でアクセスすると”via IPv6″って出る例のやつ作りました。
(HTMLタグ貼るだけのやつが見つからなかったので)

表示してみる

IPv6から繋ぐと
Screen Shot 2017-05-22 at 3.19.22.png
が表示されます。

IPv4から繋ぐと
Screen Shot 2017-05-22 at 3.19.41.png
が表示されます。

使い方

<span id="kibousoft-viav6"></span>
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://viav6.kibousoft.co.jp/', true);
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200){
   var dom = document.getElementById('kibousoft-viav6');
   dom.innerHTML = xhr.responseText;
 }
};
xhr.send(null);
</script>

ソースコード

汚いですが直書きです。大したことしてない。

index.php
<a href="https://github.com/kibousoft/viav6_web/" style="text-decoration: none; color: white;">
<?php
$ip = $_SERVER['REMOTE_ADDR'];
$headers = apache_request_headers();
if ($headers['X-Forwarded-For']) {
    $ip = $headers['X-Forwarded-For'];
}

if (preg_match('/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/', $ip)) {
    echo '<div style="background: linear-gradient(#FF0000, #FF99CC); padding: 5px; border: 1px solid #333333; border-radius: 3px; font-size: 13px; width: 50px; text-align: center; font-family: sans-serif;">via IPv4</div>';
} else {
    echo '<div style="background: linear-gradient(#0000FF, #99CCFF); padding: 5px; border: 1px solid #333333; border-radius: 3px; font-size: 13px; width: 50px; text-align: center; font-family: sans-serif;">via IPv6</div>';
}
?>
</a>

CORSの話

外部からXHRで取得される可能性のあるサイトでは、
Access-Control-Allow-Origin , Access-Control-Allow-Methods ヘッダーを返す必要があります。
.htaccessで以下を設定しました。

.htaccess
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET"

インフラの話

最初Amazon API Gatewayでやろうとしたんですが、API GatewayはIPv6対応していませんでした。
なので、OpsWorksでPHP App Serverを立てて動かしています。
OpsWorksにも以下の問題がありました。

  • Application Load Balancer(IPv6対応)には対応していない
  • EC2へのIPv6アドレスのアタッチには対応していない
  • セキュリティグループでIPv6のTCP 80番が許可されていない

そのため、上記の設定は手動で行いました。

備考

  • Happy Eyeballsの関係で、サイトにはIPv4で繋がって、XHRはIPv6で繋がるケースもあるよねとか細かい話はなしで。

続きを読む

aws周りのメモ2

postgresqlを使う

RDSへpostgresqlをいれて立ち上げ

認証と接続

import-key-pair — AWS CLI 1.11.87 Command Reference
http://docs.aws.amazon.com/cli/latest/reference/ec2/import-key-pair.html

cd $HOGE
openssl genrsa -out my-key.pem 2048
openssl rsa -in my-key.pem -pubout > my-key.pub
# IAMのコンパネで*.pubを入力
# 多分、権限があれば以下でもいける
# aws iam upload-ssh-public-key

【AWS 再入門】EC2 + RDS によるミニマム構成なサーバー環境を構築してみよう – NET BIZ DIV. TECH BLOG
https://tech.recruit-mp.co.jp/infrastructure/retry-aws-minimum-vpc-server-environment/

便利

無料枠

無料のクラウドサービス | AWS 無料利用枠
https://aws.amazon.com/jp/free/

AMI

AWS Marketplace: Search Results
https://aws.amazon.com/marketplace/search/results?x=14&y=18&searchTerms=&page=1&ref_=nav_search_box

CFテンプレート

サンプルコード & テンプレート – AWS CloudFormation | AWS
https://aws.amazon.com/jp/cloudformation/aws-cloudformation-templates/

ec2 ami tool & ec2 api tool

Mac で Amazon EC2 API Toolsを設定する – サーバーワークスエンジニアブログ
http://blog.serverworks.co.jp/tech/2013/01/31/mac-amazon-ec2-api-tools-setup/

ec2 api toolは若干心配。

VPCを使う

接続の際に、sshを経由したい。sslでもいいけどなんかsshがいいなと。
パスワードよりkeyのほうがセキュアだからかな。

0から始めるAWS入門①:VPC編 – Qiita
http://qiita.com/hiroshik1985/items/9de2dd02c9c2f6911f3b

導入

Amazon VPC とは? – Amazon Virtual Private Cloud
http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Introduction.html

公式のいろいろ

料金 – Amazon VPC | AWS
https://aws.amazon.com/jp/vpc/pricing/

基本は無料だけどNATとVPNは別課金。

【AWS 再入門】VPC 環境に踏み台サーバーを構築して SSH 接続してみよう – NET BIZ DIV. TECH BLOG
https://tech.recruit-mp.co.jp/infrastructure/retry-aws-bastion-host-vpc/#i-3

ec2(Bastion)を配置する必要がありそう。

【AWS 再入門】EC2 + RDS によるミニマム構成なサーバー環境を構築してみよう – NET BIZ DIV. TECH BLOG
https://tech.recruit-mp.co.jp/infrastructure/retry-aws-minimum-vpc-server-environment/

VPC に推奨されるネットワーク ACL ルール – Amazon Virtual Private Cloud
http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Appendix_NACLs.html

vpcでのネットワークのポリシーの例

Default VPC

AWSのDefault VPCを削除して困った話 – MikeTOKYO Developers
http://blog.miketokyo.com/post/49939300091/aws-default-vpc

デフォルトvpcは削除したらダメか。使い分けがわからん。

Amazon EC2 と Amazon Virtual Private Cloud – Amazon Elastic Compute Cloud
http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-vpc.html

基本の機能はデフォルトとそうじゃないvpcは同じだけど、
デフォルトvpcがないとちゃんと機能しない。
デフォルトの属性によって、指定がないとipを紐付けたりする。

VPCネットワーク設計

これだけ押さえておけば大丈夫!Webサービス向けVPCネットワークの設計指針 | eureka tech blog
https://developers.eure.jp/tech/vpc_networking/

ネットワークは一度稼働させると移行が大変なので、初期設計が非常に重要になります。

わかりやすい。図が特に。

  • Bastion
  • NAT
  • Security Group

ENI

インフラエンジニアに贈るAmazon VPC入門 | シリーズ | Developers.IO
http://dev.classmethod.jp/series/vpcfor-infra-engineer/

サブネットで指定したIPアドレスのうち、先頭4つと末尾の1つはVPCで予約されるため使用できません。

VPCでは常にDHCP有効とするのがポイントです。

また、DHCPサービスで伝えられる情報(DHCPオプション)は、変更することもできます。

仮想マシンにひもづくENIにより、DHCPサーバーから毎回同じMACアドレス、IPアドレスが付与されます。これは、仮想マシンの状態に依存しないため、仮想マシンを再起動しようと、一旦シャットダウンしてしばらくしてから起動した場合でも必ず同じアドレスが付与されます。

ENI(Elastic Network Interface)か。。なるほど。でも、使うことはなさそうだな。

NAT

IPマスカレードが使えないVPC
NATは、Static(静的・サーバー用途)とElastic(仮想・クライアント用途)がある。
個人的には、このNATインスタンスの実装は、あまり好きではありません。動きがややこしいですし、ユーザーが自分でNATインスタンスの管理をしなければならないのも煩雑な印象を受けます。VPCのネットワークサービスの一つとして提供される機能であれば、ユーザーからはなるべく抽象化され仮想マシンとして意識されないようにするべきと考えます。
ただ、ユーザーから仮想マシンとして見える分、機能・実装が具体的に把握できる点やカスタマイズ性が高い点は良いとも思っています。

NAT インスタンスと NAT ゲートウェイの比較 – Amazon Virtual Private Cloud
http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/vpc-nat-comparison.html

なるほど。。

Bastion

AWSで最低限セキュアな構成を組む – Qiita
http://qiita.com/ausuited/items/09b626fa5264f0c650fd

パブリックSubnetにEC2インスタンス(踏み台サーバーとして)
NATインスタンスを作成した要領で、パブリックSubnetにEC2インスタンスを作成する。Security groupは新規に作成してSSHをAnywhereに。Key pairは厳重に管理。尚、踏み台サーバーは、使用する時以外はStoppedにしておく事で、さらにセキュアな状態とする。このデザインパターンをOn Demand Bastionパターンと呼ぶらしい。

詳しい。「On Demand Bastionパターン」か。なるほど。

vpcへの踏み台サーバー
ポートフォワーディング、トンネルなどと同じ意味。

Network ACL

インスタンス単位じゃなくサブネット単位でより制限してセキュアにしたい場合に使うのかな。

安全なVPC設計 — Commerce Hack
http://tech.degica.com/ja/2016/01/07/designing-vpc-and-subnets/


結局どうするのか、、ひとまずNATはつかわずに、Bistionをつくってみる感じかな。

アベイラビリティーゾーン

リージョンごとでの、障害などで全部やられないように物理的にセグメントされた範囲の単位かな。
RDSではセグメントグループに2つ以上のゾーンを含める。でも、一つしか使わなくていい。ということか。s

RDSのVPC間の移動

サブネットグループの関連付けを変えればいいらしい。間違って設定したので移動した。

【小ネタ】知っていましたか?RDSを別のVPCに移動できることを | Developers.IO
http://dev.classmethod.jp/cloud/aws/rds_can_move_to_another_vpc/

Bastion作成作業をしてみる

主に下記を参考。

【AWS 再入門】EC2 + RDS によるミニマム構成なサーバー環境を構築してみよう – NET BIZ DIV. TECH BLOG
https://tech.recruit-mp.co.jp/infrastructure/retry-aws-minimum-vpc-server-environment/

  • サブネットってなんだっけとか復習。
  • ストレージはどうするのか。
    • とりあえずssdにしたけどマグネティックでよかったかなあ。

      • ssd:$0.12 : 1 か月にプロビジョニングされたストレージ 1 GB あたり
      • マグネティック: 0.05 USD/GB-月
  • public IPは設定必要だよね
  • market placeからamiを取得した方がいいの?
    • とりあえず公式のウィザードを使ったけど。
  • 認証にIAMが追加されていたので使ってみた
    • これとは別にキーペアは必要ってことかな。
  • CFnテンプレート(CloudFormationテンプレート)というのがあるらしい。。
    • これでつくりなおそうかな。。
  • サブネットとかいろいろネットワーク系の設定
    • なんだかんだいっていろいろあった
  • セキュリティグループ
    • エイリアスみたいなセキュリティグループにできたらいいのに。タグや名前で明示化かな。
    • bastionは22をあけて、rdsは5432をbastionからのみあける
  • ログイン
  • DNS
    • あれ、パブリックDNSがうまく割り振ってないな。。
      AWSでPublic DNS(パブリックDNS)が割り当てられない時の解決法 – Qiita
      http://qiita.com/sunadoridotnet/items/4ea689ce9f206e78a523
    • RDSのDNS
      • nslookupしたら内部ipがかえってくるのね。接続できないけどなんか気持ち悪いな。これかな。。
        外部からdnsを引けることを気にしている人は見かけなくて便利だからって話なのかね。
        【AWS】VPC内でPrivate DNSによる名前解決 – Qiita
        http://qiita.com/y_takeshita/items/2eb5e6abb5eb5516d1de

やってるうちはいいけど、しばらくやらないと設定の方法とか忘れそう。。こういうのは学習コストだけじゃないな。

PlantUMLで図にしておく

Kobito.hQIwJs.png

VPC内のRDSへLambdaから接続。。

しまった!アンチパターンだそうだ。。

Lambda+RDSはアンチパターン – Qiita
http://qiita.com/teradonburi/items/86400ea82a65699672ad

Lambda + RDS benchmark – Qiita
http://qiita.com/taruhachi/items/3f95ae3e84f56edb3787

新し目の記事でIAM認証でクリアできそうな。。

【全世界待望】Public AccessのRDSへIAM認証(+ SSL)で安全にLambda Pythonから接続する – サーバーワークスエンジニアブログ
https://blog.serverworks.co.jp/tech/2017/04/27/rds-iam-auth-lambda-python/


セキュアに接続するのと速度のトレードオフになっていたのが
IAM認証のおかげで両方可能になったということっぽい。
でも、ネットのスループット、コネクション数(料金・負荷)、など、、ほかにも気にすることが出て来そうで若干不安。
非同期でよければキューイングして一回投げっぱなしすればどうだろう。
もしくは、似てるけど、Lambdaから一回値を返してもらってからRDSへ投げ直す。
これでいっかなあ。。Lambdaの意味がなくなる?うーん。

今後

  • 疑問としてrdsなど内部向けのdnsを外から見れなくできないものか。
  • というか、rdsのエンドポイントって再起動したら変わったりしないかね。ipは固定されるのか。
    • たぶん、サブネット内でdhcpになるのでipは変動するけどエンドポイントは固定。。じゃないかしら。

posgresqlをつかうための情報

Amazon RDS 上の PostgreSQL – Amazon Relational Database Service
http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts.General.SSL

続きを読む

EC2 instance起動時にtagをつけるTagSpecifications

AWSCLIでEC2 instance起動時に同時にタグをつける方法としては、instance起動してinstance-idを取得しておいて、パイプでつないでtagをつけたり、スクリプトの中で後でタグ付けする方法があったと思います。
http://kurochan-note.hatenablog.jp/entry/2017/01/08/220155

AWSCLI EC2 Run-Instanceのなかに–tag-specificationsというoptionが入って、run-instancesの中でタグが作成できるようになりました。地味なアップデートかもしれませんが、結構うれしいです。

instanceの詳細はjsonに記述して、下記のように指定して実行します。

aws ec2 run-instances --cli-input-json file://instance.json

EC2は山ほど設定項目があるので、generate-cli-skeltonでフォーマットを出力して、必要な項目だけ入力して、不必要なものは消すとinstanceの詳細を記述したjsonの完成です。Gitにでも入れておきましょう。
http://docs.aws.amazon.com/cli/latest/userguide/generate-cli-skeleton.html

aws ec2 run-instances --generate-cli-skeleton

Instanceの設定詳細を記述したjsonサンプル

instance.json
{
    "ImageId": "<image-id>",
    "KeyName": "<my-key>",
    "SecurityGroupIds": [
        "<my-sgid>"
    ],
    "InstanceType": "<instance-type>",
    "BlockDeviceMappings": [
        {
            "VirtualName": "Root",
            "DeviceName": "/dev/sda1",
            "Ebs": {
                "VolumeSize": 100,
                "DeleteOnTermination": true,
                "VolumeType": "gp2"
            }
        }
    ],
    "Monitoring": {
        "Enabled": false
    },
    "SubnetId": "<subnet-id>",
    "DisableApiTermination": false,
    "IamInstanceProfile": {
        "Name": "<instance-iam-role>"
    },
    "TagSpecifications":[
        {
            "ResourceType": "instance",
            "Tags": [
              {
                "Key": "Name",
                "Value": "<server-name>"
              },
              {
                "Key": "ClusterName",
                "Value": "<cluster-name>"
              },
              {
                "Key": "Application",
                "Value": "<myapp>"
              },
              {
                "Key": "CostCenter",
                "Value": "<my-cost-center>"
              },
              {
                "Key": "Environment",
                "Value": "Test"
              },
              {
                "Key": "User",
                "Value": "<user-name>"
              }
            ]
        },
        {
          "ResourceType": "volume",
          "Tags": [
            {
              "Key": "Device",
              "Value": "<device-name>"
            },
{
              "Key": "CostCenter",
              "Value": "<my-cost-center>"
            },
            {
              "Key": "backup_key",
              "Value": "true"
            }
          ]
        }
    ]
}

続きを読む

[AWS] 積年の夢の終焉…?:リージョン間VPN接続 (powered by VyOS 1.1.7)

プロローグ

[AWS] 積年の夢の実現:リージョン間VPN接続 (powered by SoftEther)
[AWS] 積年の夢の続き:リージョン間VPN接続 (powered by Windows Server 2012 R2)
と歩を進めてきた、三部作(勝手に後付け♪)のファイナルw VyOS編。

  • VyOSがSoftEther/Windowsより優れているのは、VPN Connection接続(下記【a】)もPeer-to-Peer接続(下記【b】)もMobileアプリからのL2TP/IPSec接続(下記【c】)も可能なこと(と思っている)。
    ⇒SoftEtherは【a】不可、Windowsはそりゃ頑張ればナンでもできるでしょうが。。
    OVERVIEW_20170516.jpg

  • 。。。と思っていたところ、VPN ConnectionでVyOS(Vyatta)用のconfigをDLできなくなった???一昔前はできたはず…(・・;)
    ⇒なんと、RegionによってVPNCがサポートしているルータが異なることを発見!!(VyOS(Vyatta)は、N.Virginiaにはあるが、N.Californiaにはない、といった具合)

  • 半面、VyOSのDisadvantageは、学習コストが(少なくともCCNAしか持っていないヲレには)高い、という点か。
    ※SoftEtherもWindowsも、自動化とか欲張らなければ、GUIでも設定可能

    • といいつつ、VyOSもLinuxベース(vbash?)なので、そこまではぢめまして感はなく。
    • 「configure~commit/save」は、Cisco IOSの「configure terminal」と似ていると感じたよ。
    • その後、VyOSも、接続するまでなら、そんなに敷居が高くない(低くはない)ことを識る(いわゆる「知らないから怖い」&「食わず嫌い」)

⇒いや、やはりVyOSの敷居は高かった。。。CentOS等と似て非なり。Cisco IOSと非なりて似る。

構成オプション、どん!

VyOSは、様々なインストール方法・様々なVPN接続方法がある(♬そしてワタシはツブされる…(@_@))

  • VyOSインストール方法
     【1】 AWSのAMIを利用してEC2インスタンスとして作成
     【2】 オンプレミスにVirtualBox上に仮想マシンとして作成
     【3】 VirtualBox上のDockerホストにてDockerコンテナとして作成【Dockerでルータをコンテナ化してみた】
     【4】 あらゆるDockerホストにてDockerコンテナとして動作
        ⇒ そしてマルチクラウドへ。。。(【VyOSを使ってSoftLayerとAWSをIPSecVPN接続】

  • VPN接続方法
     【a】 AWSのCustomer Gateway(CGW)/VPC Connection(VPNC)/Virtual Private Gateway(VGW)を利用 [//]
     【b】 VyOS同士の直接接続(Site-to-Site接続)[]
     【c】 Windows10やAndroidから接続(Point-to-Site接続) [/]

【1】AMIを利用してEC2インスタンスとして作成

  • VyOS AMIのビルトインユーザは、「ec2-user」でなく「vyos」
  • rootユーザにパスワードを設定。。。passwdコマンドではできない。。。!?【保留中】
  • ログインユーザ作成【保留中】
$ configure
# set system login user 《管理者ユーザ》 authentication plaintext-password 《管理者ユーザのパスワード》
# commit
# save
# exit
  • 「vyos」の公開鍵を、《管理者ユーザ》にコピー【保留中】
$ sudo cp /home/vyos/.ssh/authorized_keys /home/《管理者ユーザ名》/.ssh/
  • ビルトインユーザ「vyos」削除 ※「vyos」以外のユーザでログインし直した上で
    【保留中】VyOSサーバを再起動したら、作成した管理者ユーザでSSHログインできなくなった (・・;)
$ configure
# delete system login user vyos
# commit
# save
# exit
  • 2枚目のENI(Private側)にプライベートIPアドレスをアサイン

※EC2を作成するCloudFormationにおいて、Public側ENIをIndex0、Private側ENIをIndex1と指定済み
OR
※ENI 1枚挿しのEC2インスタンスを起動した後で、2枚目ENIを手動でAttach済み

$ configure
# set interfaces ethernet eth1 address '10.100.12.100/24'
# commit
# save
# exit
$ sudo ifconfig
《eth1にプライベートIPアドレス「10.100.12.100」が割り当たっていることを確認》 

※ifconfigやroute等のNW系コマンドはrootでのみ実行可能

【1】を使ってAWS上で【a】でツナぐずら

(韓国(KR)のVyOSサーバと、USのVGWをVPN Connectionで接続)

  • 【US】VyOSサーバ持つEIPを使って、CSGを作成

    • BGPのAS番号を「65000」と指定
  • 【US】VPCとVPNCを作成

    • VPCのRouteTableにおいて「Propagated=yes」
      ⇒KRへのルーティング情報が、VGW経由で、US側のRouteTableに動的に追加されるようになる!
  • 【US】VPNCから「VyOS用config」をDL・・・少なくとも「N.Virginia」なら可能

  • 【KR】「VyOS用config」の一部を手動で書き換え:

    • local-address

      • VyOSサーバのパブリック側プライベートIPアドレス(10.100.11.100)に書き換え
        NAT-Tを利用するため、EIPではなくてプライベートIPアドレスと理解)
    • BGPのアドバタイズ情報
      • 10.100.11.0/24, 10.100.12.0/24
      • このBGPアドバタイズが中々できずにハマる。。。「10.100.12.0/24」をVyOSが認識していないためと推理(後述)
  • 【KR】「VyOS用config」を投入!!

    • AWSからDLしたスクリプトを一部書き換え後に愚直に実行すると、BGPアドバタイズの行で「Already exists」のエラーが発生★ そりゃそうなので無視~
  • 【KR】BGPアドバタイズ情報の追加

$ configure
# set protocols static route 10.100.12.0/24 next-hop 10.100.12.1
# set protocols bgp 65000 network 10.100.12.0/24
# commit
# save
# exit

【とっても素敵な参考情報】BGPアドバタイズするネットワーク設定の追加

  • 【US】ルーティング情報の確認

    • VGWのTunnel(2本)がUpしていることを確認
    • KR側のVPCサブネットに関するルーティング情報が、BGPによって動的にUS側RouteTableに反映されていることを確認する。
  • 【KR】疎通確認
    ①KR側のVyOSサーバからUS側にPingを送って確認

vyos@VyOS-AMI:~$ ping 172.16.12.200 interface 10.100.11.100
PING 172.16.12.200 (172.16.12.200) from 10.100.11.100 : 56(84) bytes of data.
64 bytes from 172.16.12.200: icmp_req=1 ttl=254 time=196 ms
64 bytes from 172.16.12.200: icmp_req=2 ttl=254 time=196 ms
64 bytes from 172.16.12.200: icmp_req=3 ttl=254 time=196 ms
・・・

VyOS上の送信IF(Public側)を明示的に指定する必要がある模様

②KR側のプライベートEC2からUS側にPingを送って確認

ping 172.16.12.200
  • 【US】疎通確認

    • US側からKR側にPingを送って確認

【閑話休題】BGPアドバタイズでハマった考察

疎通できた後で振り返ってみれば単純なことだが、めっちゃハマったのは、BGPによる動的ルーティング情報交換

  1. VyOS EC2の2枚目のENIの(プライベート)IPアドレスが、いつの間にか外れていた…
    ⇒最初ifconfigで設定していたから、再起動中に消失?VyOS設定「set interfaces ethernet eth1 address~」なら永続的?

  2. VyOSサーバに対して、KR側プライベートサブネットへのStatic Routeが必要だった。
    ⇒これは…自力では思いつかない…
    【再掲】BGPアドバタイズするネットワーク設定の追加

  3. BGPのルーティング情報伝達(コンバージェンス?)に、設定投入~環境反映の間のタイムラグがある?
    ⇒KR側のVyOSへのBGP設定投入後、US側のVGWからRouteTableへの伝達に少し時間がかかることを知らず、設定ミスだと思いこんでいた…

【2】オンプレミスのVirtualBox上に仮想マシンとして作成

VyOSは、一般的にはISOファイルで提供されているのか(AWSならAMIで提供)。
ISOファイルをDLし、VirtualBoxの起動時の「Optical Device」として指定してあげると、VyOSのインストールプロセス開始☆
格闘中…)

【1】と【2】を【b】でツナぐもな

(オンプレミスのVirtualBox仮想マシンと、VyOS-EC2を、VyOS機能で直接通信)

【1】と【3】を【c】でツナぐだなも

(Windows10/Android端末と、VyOS-EC2を、IPSec/L2TPで通信)

エピローグ

AWSリージョン間接続について、SoftEther/Windows/VyOSをひととおりやってみた。
まだまだ試したいこと・課題はあるけど、ひとまずおなかいっぱい。

続きを読む

〇FLAGの中の人に憧れてMastodon×AWSでモンストドン作ってみた

Mastodon立ち上げたらいい会社に入れると聞いて、邪な気持ちで。。。いや、Mastodonとモンストって相性よさそうだなぁと思いたち、少し乗り遅れた感をかもしだしながら、フルにAWSを使って規模拡大しても大丈夫な構成で作ってみた。

モンストドン (https://monstdn.com)

構成

monstdn.png

最小構成のざっくり料金($1=113円、1ヶ月30日計算)

サービス 単価 月額料金
ALB 1台 × $0.0243/1H + データ転送的なの  約2000円 + α
EC2 2台(t2.nano) × $0.008/1H + データ転送的なの 約1300円 + α
RDS 1台(db.t2.micro シングルAZ) $0.028/1H + データ転送的なの 約2300円 + α
ElasticCache 1台(cache.t2.micro) $0.026/1H + データ転送的なの 約2100円 + α
S3Bucket $0.025/GB + リクエスト数的なの + α
SES $0.10/1,000通あたり + データ転送的なの + α
合計     ( 約7700円 + α なので ) ざっくり1万ぐらい

※無料枠があるので1年目はもう少しやすくできそう

やったこと

  • AWSのアカウント作成
  • IAMの作成とアカウントの初期設定(二段階認証とか、パスワードポリシーとか)
  • Route53でドメインを買う
  • SESでメール設定と制限解除申請
  • ACMの取得(無料でHTTPS通信)
  • S3バケット作成(画像とかのアップロードファイルの配信用)
  • VPCとセキュリティグループの作成
  • SES、S3へアクセスする為のIAMユーザの作成
  • ElasticCacheでRedisの作成
  • RDSでPostgreSQLの作成
  • EC2でCentOSを使ってMastodonの構築(下に詳細)とイメージ(AMI)の作成
  • AutoScallingの設定
  • ALB(ApplicationLoadBalancer)の作成(ACMをつける)
  • Route53でHostZoneのレコード設定

CentOSでのMastdon構築(20170517現在)

sudo su -
yum -y update
yum -y install vim

localectl set-locale LANG=ja_JP.utf8
localectl set-keymap jp106
localectl status

timedatectl set-timezone Asia/Tokyo
timedatectl status

dd if=/dev/zero of=/mnt/swapfile bs=1M count=2560
mkswap /mnt/swapfile
swapon /mnt/swapfile
chmod 0644 /mnt/swapfile
echo "/mnt/swapfile                             swap                    swap    defaults                0 0" >> /etc/fstab
free

vim /etc/sysconfig/selinux
 SELINUX=enforcing
 ↓
 SELINUX=disabled

systemctl disable postfix
systemctl disable auditd.service

yum -y install libxml2-devel ImageMagick libxslt-devel git curl nodejs file
yum -y install epel-release
rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
yum -y install ffmpeg ffmpeg-devel

yum -y group install "Development tools"
curl -sL https://rpm.nodesource.com/setup_4.x | sudo bash -

yum -y install nodejs
npm -g install yarn

yum -y install postgresql postgresql-contrib postgresql-devel
yum install -y openssl-devel readline-devel

useradd mastodon
passwd mastodon
su - mastodon
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src && cd ~
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile && source ~/.bash_profile
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
rbenv install 2.4.1 && rbenv global $_ && rbenv rehash

# 確認
ruby -v

cd ~
git clone https://github.com/tootsuite/mastodon.git live
cd live
git checkout $(git tag | tail -n 1)

gem install bundler
bundle install --deployment --without development test
yarn install --pure-lockfile

cp .env.production.sample .env.production
sed -i "/^PAPERCLIP_SECRET=$/ s/$/`rake secret`/" .env.production
sed -i "/^SECRET_KEY_BASE=$/ s/$/`rake secret`/" .env.production
sed -i "/^OTP_SECRET=$/ s/$/`rake secret`/" .env.production

vim .env.production
#Redis,Postgresql,言語,SMTP,S3の設定

RAILS_ENV=production bundle exec rails db:setup
RAILS_ENV=production bundle exec rails assets:precompile

exit

cat << "_EOF_" > /etc/systemd/system/mastodon-web.service
[Unit]
Description=mastodon-web
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_


cat << "_EOF_" > /etc/systemd/system/mastodon-sidekiq.service
[Unit]
Description=mastodon-sidekiq
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_

cat << "_EOF_" > /etc/systemd/system/mastodon-streaming.service
[Unit]
Description=mastodon-streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/usr/bin/npm run start
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_

systemctl enable mastodon-{web,sidekiq,streaming}
systemctl start mastodon-{web,sidekiq,streaming}

cat << "_EOF_" | crontab -
RAILS_ENV=production
@daily cd /home/mastodon/live && /home/mastodon/.rbenv/shims/bundle exec rake mastodon:daily > /dev/null
_EOF_

yum -y install nginx

cat << "_EOF_" > /etc/nginx/conf.d/mastodon.conf
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name {domainName};

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /home/mastodon/live/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  location / {
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";

    proxy_pass http://localhost:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}
_EOF_

systemctl enable nginx
systemctl start nginx

# ユーザ登録後 admin設定
RAILS_ENV=production bundle exec rails mastodon:make_admin USERNAME={UserName}

メモ

EC2のDiskはSSDで(swapで使う)
ロードバランサーはApplicationの方じゃないとwebSocketがうまくいかない
コミュニティベースのシステムだからCloudFrontはあまり必要性感じなかったので使わなかった
(日本向けだしS3のバケット東京リージョンにあるし、S3もかなり性能いいし)
もしCloudFrontを使うなら、websocketできないからS3の前に置く感じ
今回CloudFrontの利点があるとすれば”ドメイン”が自分の使えることぐらいかな
CentOSじゃなくてAmazonLinux使いたかったけど、ffmpeg入れるのにやたら時間かかったからやめた。一応動いたけど(純正AWSが。。。)
DockerはDeployまで楽そうだけど、効率よくなさそうだったのでやめた
AWSでDocker使うならECSでやってみたいけど、Mastodonはすんなりできるのかなー
セキュリティ的にはロードバランサーからの80番ポートしか受け付けないように制御してるから大丈夫かな。
sshでのログインは同じVPC内に踏み台サーバ立ててと。

最後に

ここまで読んで頂きありがとうございます。
技術的なことを投稿するのはこれが初めてですが、だれかのお役にたてれたら嬉しいです。
普段はPHPとAWS少しいじる程度なのでいい勉強になりました。
ほとんど公開されている文献をもとにプラモデル感覚で作りましたので、ご指摘等あればコメント頂ければと思います。
個人でのサイト運用となりますので、落ちたらごめんなさい。

続きを読む

AWS S3ライクなオブジェクトストレージMinioを使ってみた

はじめに

オブジェクトストレージとかS3使えばいいっしょとか思っていたんですが、
開発環境を作ったときに開発者一人ひとりにS3のバケット切って管理してもらうとか、
まあめんどくさくいつか絶対共有で使うバケットができてしまうなって思っており、何かないかなって探してたらMinioを見つけた。

Minioとは

公式ページから引用すると

Minio is a distributed object storage server built for cloud applications and devops.

意訳
Minioは、クラウドアプリケーションとdevops用に作られた分散オブジェクトストレージサーバーです。

他にもなんか色々書かれてて気になったところで言うと、
– Amazon S3 v4 APIの実装してあるからAWS CLIとか使える
– ライセンスはApache License 2.0
– Goで書いたから軽量でいい感じ

スクリーンショット

ログイン画面

スクリーンショット 2017-05-08 17.23.55.png

WEB UI

スクリーンショット 2017-05-08 19.10.59.png

サーバ構築

クイックスタートを参考に立ててみた。
僕はdockerが大好きなのでdockerで試すことにした。
大好きな理由はローカルの環境を汚さないことと、共有が楽だからである。

クイックスタートに書かれてる内容は下記である。
しかし、アクセスキーが立ち上げるたびに違ったりすると開発時にはこまるので固定化する方法を探すことにした。

$ docker pull minio/minio
$ docker run -p 9000:9000 minio/minio server /export

上記にポートの変更とバックグラウンド実行とアクセスキーの固定とデータの永続化を追加して上げると下記になる。アクセスキーとシークレットキーはAWSのサンプル用のキーと合わせた。実際に実行する場合は環境変数などに埋めて渡したり docker secret コマンドを使ったりするべきだと思う。

$ docker run -p 8080:9000 
  -d 
  -e MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE 
  -e MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY 
  -v "/mnt/export/minio:/export" 
  -v "/mnt/config/minio:/root/.minio" 
  minio/minio server /export

あとは表示された情報でアクセスするだけで動作が確認できる
(Browser Accessはdocker hostのip+port番号に読み替える必要がある)

Endpoint:  http://172.100.0.1:9000  http://127.0.0.1:9000
AccessKey: AKIAIOSFODNN7EXAMPLE
SecretKey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Region:    us-east-1
SQS ARNs:  <none>

Browser Access:
   http://172.100.0.1:9000  http://127.0.0.1:9000

AWS CLIで操作

公式のドキュメント参照すると

やること

  • Minio Serverを立てる(上でやった)
  • AWS CLIインストール($ pip install awscli)
  • configure設定

というわけでconfigureの設定のみです。

direnv を使っているので好きなディレクトリで$ direnv edit . をしてやりたいところですが、direnv 説明がめんどくさいのでコマンドベースで設定します。

$ export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
$ export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
$ export AWS_DEFAULT_REGION=us-east-1

以上。

あとはコマンドを実行していくだけです。

バケットの作成
$ aws --endpoint-url http://192.168.99.100:8080/ s3 mb s3://test1
make_bucket: test1
バケットの一覧
$ aws --endpoint-url http://192.168.99.100:8080/ s3 ls
2017-05-08 18:54:37 test1
オブジェクトの追加
$ aws --endpoint-url http://192.168.99.100:8080/ s3 cp hogehoge.txt s3://test1
upload: ./hogehoge.txt to s3://test1/hogehoge.txt
バケットの中身一覧
$ aws --endpoint-url http://192.168.99.100:8080/ s3 ls s3://test1
2017-05-08 18:54:37          6 hogehoge.txt
オブジェクトの削除
$ aws --endpoint-url http://192.168.99.100:8080/ s3 rm s3://test1/hogehoge.txt
delete: s3://test1/hogehoge.txt
バケットの削除
$ aws --endpoint-url http://192.168.99.100:8080/ s3 rb s3://test1
remove_bucket: test1

見ての通りAWS CLIのコマンドがそのまま使えるので(要endpoint-url)巷にあるawsライブラリのほとんどがそのまま使えると思われる(未検証)

良いところ

  • 構築ワンコマンド
  • AWS CLIで操作できる(他のクライントも動く)
  • 一時的にシェアー用のリンクを生成できる
  • ファイルをおいたとか削除したイベントが取得できるみたいなのでhookしたりできるみたい

良くないところ

調査不足感が否めないです。

  • IAMユーザー的なのが複数用意できない?
  • バケットごとでユーザーによるアクセス制限できない?
  • 分散の設定調べてない

まとめ

気軽に試せて楽しい。開発用途ぐらいならこれぐらいできればよさそう。
間違っているところなどあればコメントや編集リクエスト送っていただけると。

続きを読む

AMIMOTO AMIを使ってAWSにWordPressをインストールする

2016年の春頃に書いてた下書きが出てきたのでとりあえず投稿する。

  • AMIMOTO HHVM AMIを選んでインスタンスを起動
  • 10分ぐらい待つ
  • Elastic IPを紐付ける
  • Elastic IPのアドレスにアクセス
  • インスタンスIDを入力してWordPressをインストール
  • Route 53から独自ドメインのAアドレスをElastic IPに設定 参考
  • SSHでインスタンスに接続$ ssh -i ec2.pem ec2-user@[Elastic IP]
  • $ sudo yum update
  • SFTPで接続してファイルを操作できるように$ curl -L https://raw.githubusercontent.com/amimoto-ami/run-httpd-as-ec2-user/master/run-httpd-as-ec2-user.sh | sudo bashを実行
  • TransmitでSFTPを設定。サーバ:Elastic IP、ユーザ名:ec2-user、パスワード:ec2.pem、リモートパス:/var/www/vhosts/[インスタンスID]/

SSLを設定

  • $ sudo openssl genrsa -des3 -out ./ssl.newpeace.vision.key 2048で鍵を生成
  • $ sudo openssl rsa -in /etc/nginx/ssl.newpeace.vision.key -out /etc/nginx/ssl.newpeace.vision.keyでパスフレーズを解除
  • $ sudo openssl req -new -key ./ssl.newpeace.vision.key -out ./ssl.newpeace.vision.csrで鍵を生成
  • 参考
  • ssl.globalsign.com.csrの内容を申請フォームに入力
  • SSL証明書を取得
  • SSL証明書をインストール
  • $ sudo vi /etc/nginx/ssl.newpeace.vision.crtで「証明書 SHA256」の内容をコピー
  • $ sudo vi /etc/nginx/dvcacert.cerで「中間CA証明書」の内容をコピー
  • $ sudo cat ssl.newpeace.vision.crt dvcacert.cer > ssl.newpeace.vision.pemで「証明書 SHA256」と「中間CA証明書」を結合(これできない。。)
  • $ sudo chmod -R 777 /etc/nginx/conf.d/でNGINXの設定ファイルに書き込み権限を付与
  • /etc/nginx/conf.d/ssl.default.confを作成して、
/etc/nginx/conf.d/ssl.default.conf
server {
    listen      443 default spdy ssl;
    server_name _;
    root        /var/www/vhosts/i-c3880266;
    index       index.html index.htm;
    charset     utf-8;

    ssl                       on;
    ssl_certificate           /etc/nginx/ssl.newpeace.vision.pem;
    ssl_certificate_key       /etc/nginx/ssl.newpeace.vision.key;
    ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers               HIGH:!aNULL:!MD5;

を書く。

default.confcharset utf-8;の下にreturn 301 https://$host$request_uri;を追記

続きを読む

Rails4+Capistrano3+Nginx+Unicorn EC2へのデプロイ < 前編 >

インフラ勉強中の者がAWSにrailsアプリをEC2にデプロイしたので備忘録として残します。
長いので<前編><後編>の2回に分けて実施致します。

【前提】
・今回はscaffoldで作成した簡単なアプリ(userの名前と年齢を登録するアプリ)をデプロイすることをゴールに進めていきます。
・デプロイのディレクトリは /var/www/qiitaAppです。
shared/config 配下のdatabase.ymlsecrets.yml は手動で作成します。
・ローカルへのRubyやRailsのインストールとEC2、RDSのセットアップ(VPCやセキュリティグループ等々の設定)、GitHubのアカウント登録に関しての説明は致しません。
・hostの設定、各環境毎の設定は実施しません。

Rubyのインストール
Ruby on Railsのインストールと設定
AWSの設定

【イメージ】
スクリーンショット 2017-05-09 11.04.30.png

【バージョン】

software version
Ruby 2.3.3
Rails 4.2.7
Nginx 1.10.2
Unicorn 5.3.0
MySql 5.6
Capistrano 3.8.1
capistrano3-unicorn 0.2.1

今回は、デプロイ時にUnicornの再起動もさせるべくcapistrano3-unicornというgemを使用します。
(自身でtaskとして設定する事も可能です。)

GitHubの設定

1)New Repositoryの生成
Repository name → qiitaApp
Public
Create repository

2)ローカルからSSH接続ができるように[SSH keys]を追加する
今回は説明は省略します。
(EC2からもGitHub接続が必要なのでそちらは下記に記載します。)
GitHub 初心者による GitHub 入門(1)

EC2(AmazonLinux)の設定

1) git,rbenvのインストール

[myname@ip-10-0-1-86 ~]$ sudo yum -y install git
[myname@ip-10-0-1-86 ~]$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv #rbenvインストール
[myname@ip-10-0-1-86 ~]$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build #ruby-buildインストール
[myname@ip-10-0-1-86 ~]$ sudo vi .bash_profile #.bash_profileの編集

=ファイルの編集画面=

export PATH
export PATH="$HOME/.rbenv/bin:$PATH" ← 追加
eval "$(rbenv init -)" ← 追加

=================

[myname@ip-10-0-1-86 ~]$ source ~/.bash_profile #環境変数の反映
[myname@ip-10-0-1-86 ~]$ rbenv -v #バージョン確認
rbenv 1.1.0-2-g4f8925a

2) Ruby2.3.3のインストール

[myname@ip-10-0-1-86 ~]$ sudo yum install -y gcc
[myname@ip-10-0-1-86 ~]$ sudo yum install -y openssl-devel readline-devel zlib-devel
[myname@ip-10-0-1-86 ~]$ rbenv install 2.3.3

3) Nginxのインストール・設定

[myname@ip-10-0-1-86 ~]$ sudo yum install nginx
[myname@ip-10-0-1-86 ~]$ cd /etc/nginx/conf.d/
[myname@ip-10-0-1-86 conf.d]$ sudo touch local.conf
[myname@ip-10-0-1-86 conf.d]$ sudo vi local.conf

==================== local.conf ===================================

upstream unicorn {
  server unix:/tmp/unicorn.sock;
}

server {
  listen 80;
  server_name YOUR_IP_ADDRESS;

  access_log /var/log/nginx/sample_access.log;
  error_log /var/log/nginx/sample_error.log;

  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://unicorn;
  }
}

=================================================================

4) デプロイするディレクトリと権限の変更

[myname@ip-10-0-1-86 conf.d]$ cd /var/
[myname@ip-10-0-1-86 var]$ sudo mkdir www
[myname@ip-10-0-1-86 var]$ sudo chmod 777 www
[myname@ip-10-0-1-86 var]$ cd www
[myname@ip-10-0-1-86 www]$ mkdir qiitaApp
[myname@ip-10-0-1-86 www]$ cd qiitaApp
[myname@ip-10-0-1-86 qiitaApp]$ rbenv local 2.3.3

5) GitHubへの接続 SSHkey

[myname@ip-10-0-1-86 qiitaApp]$ cd 
[myname@ip-10-0-1-86 ~]$ ssh-keygen -t rsa
$ cat .ssh/id_rsa.pub
ssh-rsa abcdefghijklmnopqrstuvwxyz1234567890 ec2-user@ip-10-0-1-86

[ssh-rsa]から[ec2-user@ip-10-0-1-86]までをコピーしてGitHubのSSH keysにNewKeyとして追加する

5) その他必要なソフトウェアのインストール・設定

  • bundler
[myname@ip-10-0-1-86 ~]$ rbenv exec gem install bundler
[myname@ip-10-0-1-86 ~]$ rbenv rehash
  • Node.js
[myname@ip-10-0-1-86 ~]$ sudo yum install nodejs --enablerepo=epel
  • sqlite
[myname@ip-10-0-1-86 ~]$ sudo yum install sqlite-devel
  • mysql
[myname@ip-10-0-1-86 ~]$ sudo yum install mysql-devel

ローカルの設定

1) railsアプリの生成とgemのインストール

$ rails new qiitaApp
$ cd qiitaApp/
$ git init
$ vi Gemfile

============ Gemfile ===============

gem 'unicorn' #コメントアウトを解除する
gem 'mysql2'                                                                                                                                                                                                       
group :development do
  gem 'capistrano'
  gem 'capistrano-rails'
  gem 'capistrano-bundler'
  gem 'capistrano-rbenv'
  gem 'capistrano3-unicorn'
end

===================================

$ bundle install
$ rails g scaffold user name:string age:integer
$ rake db:migrate

2) Capistranoの設定に必要なファイルの生成

$ bundle exec cap install STAGES=production #自動で下記のディレクトリとファイルを生成
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified

3) Capfileの設定

$ vi Capfile #下記を追加
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano3/unicorn'

4) config/deploy.rbの設定

$ vi config/deploy.rb

lock "3.8.1"

set :application, "qiitaApp"
set :repo_url, "git@github.com:YOUR_GITHUB_ACCOUNT/qiitaApp.git"

set :rbenv_type, :user
set :rbenv_ruby, '2.3.3'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all

set :log_level, :warn 

# Default value for :linked_files is []
set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml')

# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

# Default value for keep_releases is 5
set :keep_releases, 3

set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"

set :unicorn_config_path, -> { File.join(current_path, "config", "unicorn.rb") }

namespace :deploy do
  after :restart, :clear_cache do
    on roles(:web), in: :groups, limit: 3, wait: 10 do
      # Here we can do anything such as:
      # within release_path do
      #   execute :rake, 'cache:clear'
      # end
    end
  end
end

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end
end    

5) config/deploy/production.rbの設定

set :branch, 'master'

server 'EC2-IP-ADDRESS', user: 'ec2-user', roles: %w{app db web} ※

set :ssh_options, {
    keys: %w(~/.ssh/YOUR_EC2_KEY.pem),
    forward_agent: true,
    auth_methods: %w(publickey)
  }

※今回はEC2-userでログインする設定で進めていきます。
本来はデプロイユーザを設定して、そのユーザのみがデプロイ可能にすべきだと思います。

7) Unicornの設定

手動で config 配下に unicorn.rb を生成

$ cd config
$ touch unicorn.rb
$ vi unicorn.rb

========================== unicorn ==============================

APP_PATH   = "#{File.dirname(__FILE__)}/.." unless defined?(APP_PATH)
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
RAILS_ENV  = ENV['RAILS_ENV'] || 'development'

worker_processes 3

listen "/tmp/unicorn.sock"
pid "tmp/pids/unicorn.pid"

preload_app true

timeout 60
working_directory APP_PATH

# log
stderr_path "#{RAILS_ROOT}/log/unicorn_error.log"
stdout_path "#{RAILS_ROOT}/log/unicorn_access.log"

if GC.respond_to?(:copy_on_write_friendly=)
  GC.copy_on_write_friendly = true
end

before_exec do |server|
  ENV['BUNDLE_GEMFILE'] = APP_PATH + "/Gemfile"
end

before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!

  old_pid = "#{ server.config[:pid] }.oldbin"
  unless old_pid == server.pid
    begin
      Process.kill :QUIT, File.read(old_pid).to_i
    rescue Errno::ENOENT, Errno::ESRCH

    end
  end
end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

=================================================================

ここまでで各種設定に関しては終了です。
shared/config 配下のdatabase.ymlsecrets.yml は手動で作成します。
が残っていますが、こちらは後編で実施します。

最後にGitHubにここまでのコードをあげておきます。

$ git add .
$ git commit -m "first commint(任意)"
$ git remote add origin git@github.com:YOUR_GITHUB_ACCOUNT/qiitaApp.git
$ ssh-add ~/.ssh/id_rsa_github
$ git push origin master

前編は以上です。
後編ではデプロイチェック、デプロイ、ログ確認を実施します。

続きを読む

AWSのGPUインスタンスを利用してTensorflowを試す

EC2のセットアップ

以下の通りp2.xlargeというGPUインスタンスを利用して、Ubuntuのインスタンスを構築します。
(現状、東京リージョンではp2インスタンスは利用できないため、ここでは)

  • AMIの選択: Ubuntu Server 16.04 LTS (HVM), SSD Volume Type – ami-80861296
  • インスタンスタイプの選択: p2.xlarge(GPUコンピューティング) $0.90/h
  • インスタンスの詳細の設定: デフォルトのまま
  • ストレージの追加: 64GiB 汎用SSD
  • Add Tags: なし
  • セキュリティグループの追加: 新しいセキュリティグループを作成する
    • sg_01: ssh 22, カスタムTCP 9898(jupyter用)
  • 確認と作成
    • 新しいキーペアの作成: kp_01.pem

セキュリティグループとキーペアは適当な名前を設定します。(ここでは、sg_01kp_01としています。)
また、既存のキーペアを利用しても問題ありません。

キーペアをダウンロードしたら、.sshに移動してパーミションを変更します。

$ mv ~/Download/kp_01.pem ~/.ssh/.
$ chmod 600 ~/.ssh/kp_01.pem

インスタンスが作成されたら、マネジメントコンソールでPublic DNSを確認し、SSHでログインします。

$ ssh -i ~/.ssh/kp_01.pem ubuntu@<Public DNS>

以下はEC2上での作業となります。まず、パッケージを更新しておきます。

$ sudo apt-get update
$ sudo apt-get upgrade

CUDA

CUDA 8.0のインストール

URL: https://developer.nvidia.com/cuda-downloads
Installation guide: http://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html

事前確認

CUDAに対応したGPUを搭載しているか確認

$ lspci | grep -i nvidia
00:1e.0 3D controller: NVIDIA Corporation GK210GL [Tesla K80] (rev a1)

CUDAに対応したOSか確認

$ uname -m && cat /etc/*release
x86_64
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
NAME="Ubuntu"
VERSION="16.04.2 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.2 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

gcc(+開発ツール群)のインストール

$ sudo apt-get install build-essential

動作中のカーネルと同じバージョンのカーネルヘッダーをインストール

$ sudo apt-get install linux-headers-$(uname -r)

インストール

https://developer.nvidia.com/cuda-downloads の「Select Target Platform」で以下の通り選択するとダウンロードリンクとインストール手順が表示されます。
リンク先のURLからwgetでファイルを取得して、インストールします。
(ここではcuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64-debをインストールします。)

$ wget https://developer.nvidia.com/compute/cuda/8.0/Prod2/local_installers/cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64-deb
$ sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64-deb
$ sudo apt-get update
$ sudo apt-get install cuda

環境変数の設定

~/.bash_profileに以下の通り設定します。

~/.bash_profile
export CUDA_HOME="/usr/local/cuda-8.0"
export PATH="${CUDA_HOME}/bin${PATH:+:${PATH}}"
export LD_LIBRARY_PATH="${CUDA_HOME}/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"

ログインし直して設定を反映します。

$ exec $SHELL -l

動作確認

サンプルプログラムをビルドして動作確認をします。(実行しなくても問題ありません。)

$ cuda-install-samples-8.0.61.sh test
$ cd test/NVIDIA_CUDA-8.0_Samples
$ sed -i "s/nvidia-367/nvidia-375/g" `grep "nvidia-367" -r ./ -l`
$ make

※sedの行は、今回のバージョンのサンプルプログラムに含まれるMakefileでドライバの指定に誤りがあったため置換しています。
(参考: https://askubuntu.com/questions/911548/cuda-examples-not-working-after-cuda-8-0-install)

cuDNN 5.1のインストール

URL: https://developer.nvidia.com/cudnn
ダウンロードにはNVIDIA Developer Programの会員登録が必要です。
認証が必要なため、ファイルをいったんローカルPCにダウンロードし、SCPでEC2にアップロードします。
(ここではcudnn-8.0-linux-x64-v5.1.tgzを利用します。)

ローカルからSCP

$ scp -i ~/.ssh/kp_01.pem ~/Downloads/cudnn-8.0-linux-x64-v5.1.tgz ubuntu@<Public DNS>:~/.

EC2上でインストール(ファイルの展開と配置のみ)

$ tar zxvf cudnn-8.0-linux-x64-v5.1.tgz
$ sudo cp cuda/include/* ${CUDA_HOME}/include/.
$ sudo cp cuda/lib64/* ${CUDA_HOME}/lib64/.

NVIDIA CUDA Profile Tools Interface(libcupti-dev)のインストール

apt-getでインストールできます。

$ sudo apt-get install libcupti-dev

ただし、今回は実行した時に”*** is not a symbolic link”というエラーが出たため、以下の通り解決しました。
(参考: http://stackoverflow.com/questions/43016255/libegl-so-1-is-not-a-symbolic-link)

$ sudo mv /usr/lib/nvidia-375/libEGL.so.1 /usr/lib/nvidia-375/libEGL.so.1.org
$ sudo ln -s /usr/lib/nvidia-375/libEGL.so.375.39 /usr/lib/nvidia-375/libEGL.so.1

$ sudo mv /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudnn.so.5 /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudnn.so.5.org
$ sudo ln -s /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudnn.so.5.1.10 /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudnn.so.5

$ sudo mv /usr/lib32/nvidia-375/libEGL.so.1 /usr/lib32/nvidia-375/libEGL.so.1.org
$ sudo ln -s /usr/lib32/nvidia-375/libEGL.so.375.39 /usr/lib32/nvidia-375/libEGL.so.1

GPU設定

http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/accelerated-computing-instances.html
を参考に”GPU 設定の最適化 (P2 インスタンスのみ)”を適用します。

$ sudo nvidia-smi -pm 1
$ sudo nvidia-smi --auto-boost-default=0
$ sudo nvidia-smi -ac 2505,875

Python環境

こちらの記事を参考にして、pyenv + minicondaの環境を作成します。
(「anaconda単独だと実は問題があります。」とのこと)

pyenv

https://github.com/pyenv/pyenv#installation

git cloneして~/.bash_profileの設定をします。

$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
~/.bash_profile
export PYENV_ROOT="${HOME}/.pyenv"
export PATH="${PYENV_ROOT}/bin:${PATH:+:${PATH}}"
eval "$(pyenv init -)"

miniconda

pyenvで最新のminiconda(ここでは、miniconda3-4.3.11)をインストールします。

$ pyenv install -l | grep miniconda
...
(省略)
...
  miniconda3-4.3.11

$ pyenv install miniconda3-4.3.11
~/.bash_profile
export CONDA_HOME="${PYENV_ROOT}/versions/miniconda3-4.3.11"
export PATH="${CONDA_HOME}/bin${PATH:+:${PATH}}"

Tensorflow

Install with Anaconda

condaでAnaconda環境を作成して、Tensorflowをインストールします。

$ conda create -n tensorflow python=3.5 anaconda
$ source activate tensorflow
(tensorflow)$ pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.1.0-cp35-cp35m-linux_x86_64.whl

Jupyter notebook

http://jupyter-notebook.readthedocs.io/en/latest/public_server.html
EC2上で起動したJupyter notebookにローカルPCから接続するための設定をします。

  • 任意のホスト名でアクセス
  • ポート番号をデフォルトから変更(ここでは9999に設定)
  • httpsで接続
  • パスワードの設定

サーバ証明書と鍵ファイルの作成

(tensorflow)$ mkdir certificate
(tensorflow)$ cd certificate
(tensorflow)$ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mykey.key -out mycert.pem

パスワードのハッシュ値作成

(tensorflow)$ python
>>> from notebook.auth import passwd
>>> passwd()
Enter password: 
Verify password:
'sha1:********'
>>> exit()

jupyterの設定ファイル作成

設定ファイルの雛形を出力

(tensorflow)$ jupyter notebook --generate-config

以下の設定を追加

~/.jupyter/jupyter_notebook_config.py
c.NotebookApp.certfile = '/home/ubuntu/certificate/mycert.pem'
c.NotebookApp.keyfile = '/home/ubuntu/certificate/mykey.key'
c.NotebookApp.ip = '*'
c.NotebookApp.port = 9999
c.NotebookApp.open_browser = False
c.NotebookApp.password='sha1:********'

Jupyter notebook起動

(tensorflow)$ jupyter notebook

ローカルPCのブラウザでhttps://<Public DNS>:9999にアクセスすると、パスワード入力画面が表示されるので、「パスワードのハッシュ値作成」の際に入力したパスワードを入力してログインします。

続きを読む