読者です 読者をやめる 読者になる 読者になる

AWS S3

今回はS3。
AWS複数あるストレージサービスの一つです。
S3はファイルをアップロード、ダウンロードできるWebサービスです。
Webサーバとしても動作します。
特徴は、
容量制限がなく(5GB/file)、
非常に高い耐久性(99.999999999%)、
低価格です。

AWSは他に以下の様なストレージサービスを提供しています。

EBSはEC2のディスクとしてアタッチするサービス。
Glacierはアーカイブストレージサービス。
Storage Gatewayはオンプレミスのストレージに接続するサービス。
EFSはNASサービス。

といったようにそれぞれ機能が違うので目的に応じたサービスをチョイスしましょう。

EC2でWebAP構築
 ↓
RDSでWebAP-DB構築
 ↓
AMIでEC2のイメージ化、複製
 ↓
ELBで冗長化
 ↓
ElastiCacheでセッション管理
 ↓

S3にアクセスログ保存 (今回はここ)

 ↓
(できれば)SQSでWebAP連携

S3は汎用的なストレージサービスであるため、本当に様々な用途があります。
今回はログの集約用途に利用します。
Webサーバやアプリケーションサーバ冗長化するとその分ログが各OSのファイルシステムに分散されてしまい、いざログを調査したい時に手間です。
またトラブル対応の調査する時にこのメッセージって過去にもあるんだろうか、など過去のログから比較をしたいけどそこまで古いログないや、ってことも多々あります。

ログの置き場所や保持世代数って運用設計でよく悩みます。

そんな時はS3に飛ばすことで、ログデータの集約、蓄積を実現できます。

今回はWebサーバのhttpdアクセスログをFluentdでS3へ飛ばします。

それではいってみよう。

Simple Storage Service

  1. S3画面へ遷移
    AWSのメニューからS3を選択します。

  2. バケットの作成
    ファイル保存先のトップディレクトリ(FQDN)となるバケットを作成します。
    [バケットの作成]ボタンを押下します。

  3. バケット名とリージョンの選択
    各項目を入力して、[作成]ボタンを押下します。
    バケット名:任意の名前を入力します。FQDNに含まれます。
    リージョン:バケットを作成するリージョンを選択します。

  4. フォルダ作成
    作成されたバケットが一覧に表示されます。
    バケットのリンクを選択します。
    バケットの中に入りました。
    作成直後は何もデータが無いため、[バケット「<バケット名>」は空です。]と表示されます。

    とりあえず今回はアクセスログを置くので、
    置き場となるフォルダを作成します。
    [フォルダの作成]ボタンを押下します。
    名前を入力します。

  5. Fluentd設定
    Fluentdのインストールは以下の記事を参考にしてください。sfujimoto.hatenablog.com

    設定は以下になります。
    input pluginはtailでaccess_logを監視します。
    output pluginはs3で対象となるAWSのキー情報とバケット名、パスを指定します。

    以下、サンプル

    # cat /etc/td-agent/td-agent.conf
    <source>
      type tail
      format apache
      path /var/log/httpd/access_log
      tag td.apache.access
    </source>
    
    <match td.apache.access>
      type s3
    
      aws_key_id **************
      aws_sec_key ***********************
      s3_bucket <bucket name>
      s3_region ap-northeast-1
      path <folder name>/
      buffer_path /var/log/td-agent/buffer
    
      # デバッグ用(デフォルトは1h)
      flush_interval 1s
    </match>

    今回は試験用でflush_intervalを1秒毎にしています。
    デフォルト設定では1時間単位にまとまったgzファイルが生成されます。
    # ファイル容量が8MBにならない限りは。


  6. 動作確認
    とりあえず、httpdにアクセスして、access_logに書き込みます。

    S3のManagementConsoleから作成したフォルダの中を確認します。
    httpdにアクセスした数だけgzファイルが存在することでしょう。

    中を覗いてみましょう。
    アップロードしたファイルのデフォルトアクセス権はAWSアカウントにしかないため、
    アクセス権の設定を行います。
    被付与者に「全員」を選択し、「開く/ダウンロード」を選択して、
    「保存」ボタンを押下します。

    オブジェクトの「リンク」からURLを確認します。

    gzファイルの中を確認してみると、
    アクセスログの情報がJSON形式に変換されて格納されています。

    # curl -s https://s3-ap-northeast-1.amazonaws.com/<bucket name>/<folder name>/2015062113_3.gz |gunzip
    2015-06-21T13:46:34Z	td.apache.access	{"host":"127.0.0.1","user":"-","method":"GET","path":"/","code":"200","size":"16","referer":"-","agent":"curl/7.40.0"}

    これで集約を実現できました。

    ただこの方法では単純な読み出しや解析が不便なので、
    ログ集約が目的ならばElasticSearch+Kibanaで可視化する方が有用です笑

    次回はちょっと逸れてCloudFormationを試してみたいと思います。


AWS ElasticCache

先日、クリエーションラインさんが開催したHashiCorp社プロダクトの勉強会に参加しました。
Vagrantが有名で、その他プロダクトも注目されていることは、
色々な媒体で知っていましたが、
なかなか時間を確保出来ず、触ることができませんでした。

勉強会を通して何がいいな、って、
初めて社外の勉強会に参加しましたが、
個人で一から調べれば一週間かかるような知識を2時間という短時間で得ることができます。
今まで引きこもって勉強してきましたが、
これからはもっと外の世界に目を向けて勉強していきたい。
connpassやDoorkeeper等、今は手を伸ばせばこういう機会を得ることができます。

さて、本題。
前回はAMIを利用してELBを利用して、
複数のEC2インスタンスへのバランシングを利用することができました。

ロードバランサーのバランシングルールにRoundRobinやLeastConnectionを採用するに直面する課題があります。
セッション維持です。
アプリケーションサーバでクライアントのキャッシュ情報を保持することで、
クライアントはそのアプリケーションサーバのみアクセスするようにしないといけないこととなります。

この対策として有名な実装方式として、以下があります。

前者はL4LBではソースIPによる振り分けやL7LBではクッキーパーシステンスによる振り分けを利用することで実装可能です。
L4では振り分けが均等にならない、L7は高価だし、負荷が気になります。

ということで、今回はElasticCacheを利用した後者を実装します。

EC2でWebAP構築
 ↓
RDSでWebAP-DB構築
 ↓
AMIでEC2のイメージ化、複製
 ↓
ELBで冗長化
 ↓

ElastiCacheでセッション管理 (今回はここ)

 ↓
S3にアクセスログ保存
 ↓
(できれば)SQSでWebAP連携

今回はElasticCacheを起動して、
どちらのWebサーバにアクセスしてもセッション情報を返すような構成を組みます。
ElasticCacheでやることは少ないです。。

ElasticCahceは実装方式として、Memcached、Redisがあります。
両方共、オンメモリタイプのKVSです。
私のイメージは安定的なMemcached、高機能なRedisです。
Memcachedはメモリにデータの参照、追加/追記/変更/削除、加減算しかできないが、シンプルな分、安定動作が期待できる。
Redisはパターン一致やList,Hash型の取り扱いや永続化等の機能を実装している。

今回はセッション情報の扱いたいだけなのでMemcachedを利用します。

環境はずっと利用しているDjangoを利用したWebアプリケーション構成を引き続き利用します。
実はDjangoは初期設定でセッション情報をデータベースに出力するようになっています。
つまり、既にセッション管理不要な状況です笑
で、でもパフォーマンス面を考えれば、ElasticCacheの方がよいよね。そうだよね。

それではいってみよう。

Elastic Cache

  1. Elastic Cache画面へ遷移
    AWSのメニューからElastic Cacheを選択します。

  2. Cache Subnet Group作成
    Elastic Cacheを作成する前にElastic Cache用にSubnet Groupを作成する必要があります。

    覚えている方もいると思いますが、RDS作成時にもSubnet Groupを作成しました。
    考え方は同じです。
    Elastic CacheはCluster構成とすることが一般的です。
    またCluster構成はAvailabilityZone(AZ)を跨いだ分散配置が可能です。

    APサーバがAZを跨いでいるのでそれぞれのAPサーバは同じAZのElastic Cacheを参照したいです。
    その願い叶えましょう、AWSが。
    Elastic Cache作成前は確認できないですが、作成済みだと思ってください。

    AP#1(AZ#1)で作成されたElastic CacheのEndpointホスト名を名前解決してください。

    [root@ip-192-168-0-54 ~]# dig test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com
    
    ; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.30.rc1.36.amzn1 <<>> test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33761
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com. IN A
    
    ;; ANSWER SECTION:
    test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com. 60 IN A 192.168.1.13
    
    ;; Query time: 84 msec
    ;; SERVER: 192.168.0.2#53(192.168.0.2)
    ;; WHEN: Sun May 31 03:05:35 2015
    ;; MSG SIZE  rcvd: 81

    次はAP#2(AZ#2)。

    [root@ip-192-168-100-35 ~]# dig test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com
    
    ; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.30.rc1.36.amzn1 <<>> test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17159
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com. IN A
    
    ;; ANSWER SECTION:
    test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com. 60 IN A 192.168.101.230
    
    ;; Query time: 2 msec
    ;; SERVER: 192.168.0.2#53(192.168.0.2)
    ;; WHEN: Sun May 31 03:05:46 2015
    ;; MSG SIZE  rcvd: 81

    結果が違います。
    ちな、192.168.1.0/24はAZ#1のPrivateSubnet、192.168.101.0/24はAZ#2のPrivateSubnet。
    それぞれCache Subnet Groupに割り当てています。
    つまり、そういうことです。

    順を追って一応説明します。

    AP#1/AP#2は同じIPアドレス(192.168.0.2)のDNSサーバを参照しています。
    なのに、返ってきたIPアドレスが違います。
    ということは同じIPアドレスですが、それぞれ違うDNSサーバを参照していることが想像できます。

    これによってそれぞれのAPサーバは通信範囲を自身が配置されたAZ内だけとすることができます。

    設定に戻ります。

    左メニューからCache Subent Groupsを選択し、[Create Cache Subnet Group]ボタンを押します。

        • -

    Name: 任意の名前
    Description: 任意の説明
    VPC ID: Elastic Cacheを設置するVPC
    Availability Zone: 追加するSubnetが存在するAZ
    Subnet ID: 追加するSubnet

        • -

    Subnet IDを選択して[Add]ボタンを押すことで追加されます。
    追加したいSubnetの数だけAvailability Zone、Subnet IDを選択、[Add]を繰り返します。

    Subnet IDがIDというところがイケてないです。
    プログラミングする上では一意となるIDが嬉しいですが、GUIでIDって言われても覚えていません。
    一度[Add]しないとそれらの情報は表示されません。
    任意で入力できる名前やCIDR Blockをプルダウン内に表示してほしいところです。


  3. Elastic Cache用Security Group作成
    Security Groupを作成します。
    Memcachedを利用する場合、デフォルト接続ポートは11211です。
    ルールはInboundでAPサーバのセキュリティグループからの通信に対して11211のみを開放します。

    ちな、Redisのデフォルト接続ポートは6379です。


  4. Elastic Cache Cluster作成
    Elastic Cacheを作成します。
    左メニューからElastic Cache Clustersを選択し、[Get Started]ボタンを押します。


  5. Select Engine
    実装方式を選択します。
    先の説明にあったように今回はMemcachedを選択します。


  6. Specify Cluster Details
    Memcachedの一般設定情報を入力します。

    Engine Version: 利用可能なバージョンからプルダウンで選択可能
    Port: デフォルトは11211
    Parameter Group: 詳細設定のパラメータを選択可能
         事前にParameter Groupで詳細設定情報を定義しておくことでMemcachedに設定を投入できます。
    Cluster Name: 任意の名前。
         Endpointにも利用されます。
    Node Type: スペックを選択可能
    Number of Nodes: Clusterのノード数(MultiAZ構成とする場合、最低2つ)


  7. Configure Advanced Settings
    Cache Subnet Group: 2で作成したSubnet Groupを選択
    Availability Zone(s): どういうAZ構成にするか選択可能
         [No Preference]、[Spread Nodes Cross Zones]、[Specify Zones]から選択可能
         分散配置したい場合、[Spread Nodes Cross Zones]を選択
    VPC Security Group(s): 3で作成したSecurity Groupを選択
    Maintenance Window: 自動メンテナンス(パッチ適用)とするか、する場合、可能な日にち、曜日、時間等で指定可能。
         [Select Window]、[No Preference]から選択可能
    Topic for SNS Notification: 自動メンテナンス時の通知先(SNSのARNで指定)


  8. 作成
    確認画面で設定内容を確認し、作成を開始します。
    5分ほどで出来ます。


  9. AP側の準備
    LoginURLにユーザ名、パスワードを渡すと、RDSのUserテーブルからユーザ名を元にデータを取得し、パスワードを照合できると、ログイン成功とします。
    セッションにユーザ名を登録します。
    HelloURLにアクセスすると、セッション情報からユーザ名を取得し、Hello, ユーザ名、を返します。
    セッション情報からユーザ名を取得できなければ、ログインしろよ、を返します。

    またそれぞれアクセス先がわかるようにレスポンスにAPサーバのホスト名も含めます。

    それではササッと作ります。

    あー、時間かかった。
    無駄にロギング、エラーハンドリング入れすぎた。

    AWS関係ないのでメモ程度

    Elastic Cache設定追加、CSRF対策解除

    vi /var/lib/project/project/settings.py
    -----
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': 'test-cache.g0y8q8.cfg.apne1.cache.amazonaws.com:11211',
        }
    }
    
    #    'django.middleware.csrf.CsrfViewMiddleware',

    Login、Hello URL追加

    vi /var/lib/project/project/urls.py
    -----
    
        url(r'^login/', views.login, name='login'),
        url(r'^hello/', views.hello, name='hello'),

    model設定追加

    vi /var/lib/project/app/models.py
    -----
    
    class User(models.Model):
      name = models.CharField(max_length=32)
      password = models.CharField(max_length=32, null=True)
      created = models.DateField()

    ※ 今回、passwordカラムだけを追加したのですが、
      dbmigrations、migrateでテーブルにカラムが追加されます。
      既にデータがある場合、not null成約を外さないとエラーとなります。

    views設定追加(エラー処理削除)

    vi /var/lib/project/app/views.py
    -----
    
    def login(request):
      res = {"server": gethostname(), "message":""}
      body = request.body
      user_dict = json.loads(body)
      users = User.objects.filter(name=user_dict["user"])
      user = users[0]
      request.session["user"] = user.name
    
      res["message"] = "ok"
      return HttpResponse(json.dumps(res))
    
    
    def hello(request):
      res = {"server": gethostname(), "message":""}
      username = request.session.get("user")
      if not username:
        res["message"] = "You are not logined"
        return HttpResponse(json.dumps(res))
      res["message"] = "Hello, " + username
      return HttpResponse(json.dumps(res))


  10. 動作確認
    ELBをエンドポイントにアカウント情報を引き連れて、LoginURLへアクセスします。

    $ curl -d '{"user":"shinji","pass":"password"}' -X POST http://lb-*********.ap-northeast-1.elb.amazonaws.com/app/login/
    {"message": "ok", "server": "ip-192-168-100-35"}

    AP#2からレスポンスが返ってきました。

    もう一回。

    $ curl -d '{"user":"shinji","pass":"password"}' -X POST http://lb-*********.ap-northeast-1.elb.amazonaws.com/app/login/
    {"message": "ok", "server": "ip-192-168-0-54"}

    AP#1からレスポンスが返ってきました。

    次にHelloURLへCookieを渡さずアクセスします。

    $ curl http://lb-*********.ap-northeast-1.elb.amazonaws.com/app/hello/
    {"message": "You are not logined", "server": "ip-192-168-100-35"}

    ログインしてませんよー、よしよし。

    次はCookieを取得して、Cookieを渡してHelloを受け取ります。

    $ curl -c cookie.txt -d '{"user":"shinji","pass":"password"}' -X POST http://lb-*********.ap-northeast-1.elb.amazonaws.com/app/login/
    {"message": "ok", "server": "ip-192-168-100-35"}
    
    $ curl -b cookie.txt http://lb-***********.ap-northeast-1.elb.amazonaws.com/app/hello/
    {"message": "Hello, shinji", "server": "ip-192-168-0-54"}

    AP#2にログインして、AP#1からHelloを受け取れました。


いかがでしたでしょうか?

これである程度の可用性、スケーラビリティを確保したWebアプリケーションシステムを組むことが出来ました。

クラウドIaaSを利用すると、なんといってもお手軽です。

私が入社した時は検証環境なんて一つの検証サーバをOSレベル、アプリケーションレベルで何人か共有していたり、本番環境はOSが分かれているところ同居させていたりが当たり前で、検証環境なのに気を使ったり、本番環境と違う構成だから本番環境にデプロイすると想定外のエラーがあったりと本当に苦労しました。(ブツブツ

次回は運用面です。

AWS ELB

4ヶ月ぶりにダーツ部が活動しました。
ぜんっぜんダメでした。
3時間投げても、4ヶ月前の投げ方がわかりませんでした。
1年ぐらい前に三ヶ月間ほど週二で通って身につけてフォームもブランク空くと分からなくなるもんです。
ブログはそうならないように頑張ります。

さて、本題。
前回はAMIを利用してEC2インスタンスを複製し、
同一構成のEC2インスタンスを2つのAZに分けて配置しました。

ただし、これだけでは2つのWebサーバがあるだけで、
負荷分散も冗長化も構成されていません。
入口となるグローバルIPアドレスDNSがバラバラだからです。

一般的な負荷分散方式としてDNSラウンドロビンを使ったDNSによる負荷分散、
ロードバランサーを入口に置くことで振り分けてもらうことによる負荷分散があります。
前者は負荷分散としては意味がありますが、冗長化の観点では役に立たないため、あまり採用されません。

ロードバランサーの過負荷となる場合にロードバランサー複数並べてDNSラウンドロビンして負荷分散することは可能です。
ただし、この場合も各ロードバランサー冗長化しないと、意味を成し得ません。
またGSLBのようにDNSによって死活監視を行うことでDNSレコードをダイナミックに切り替えるシステムを使うことでロードバランサーの負荷も、そもそもロードバランサーを置く必要がなくなる仕組みもあります。
AWSのRoute53を使えば簡単に組むことができます。

、、、今調べたらELBは負荷に応じてスケーリングするようです。
AWSさん、さすがです。

今回はELBを起動して、
2つのWebサーバを負荷分散、冗長化できる構成を組みます。

EC2でWebAP構築
 ↓
RDSでWebAP-DB構築
 ↓
AMIでEC2のイメージ化、複製
 ↓

ELBで冗長化 (今回はここ)

 ↓
ElastiCacheでセッション管理
 ↓
S3にアクセスログ保存
 ↓
(できれば)SQSでWebAP連携

ELB (Elastic Load Balancing)

ELBはロードバランサーです。
通信を振り分けてくれることにより負荷分散してくれたり、
負荷分散対象を監視し、ダウンした対象を切り離すことで冗長化構成としてくれます。

ELB作成

  1. EC2画面へ遷移
    AWSのメニューからEC2を選択します。
    ELBはEC2画面より作成します。

    AWSのポータルが日本語対応しました。
    アマゾンジャパンの方、本当にありがとうございます。

  2. ELB作成
    左のメニューからロードバランサーを選択します。
    ロードバランサーの作成」ボタンを押下します。

  3. ロードバランサーの定義
    基本情報を入力します。
    ロードバランサー
      : 名前です。DNS名にも利用されます。
      今回はlbとします。
    内部向け LB の作成
      : 作成する対象のVPCを指定します。
    内部向けロードバランサーの作成
      : インターネットに開放せず、内部からのアクセスのみの場合、
      こちらをチェックしてください。
    ロードバランサープロトコル/ロードバランサーのポート
      : アクセス元が指定するプロトコル/ポート番号です。
      今回は80番ポート(http)を利用します。
    インスタンスプロトコル/インスタンスのポート
      : ロードバランサーインスタンスにアクセスするプロトコル/ポート番号です。
      今回は80番ポート(http)を利用します。
    サブネットの選択
      : バランシン対象が存在するサブネットを選択します。
      今回は2つのazにあるpublic networkを選択します。
      EC2インスタンスに設定したサブネットです。

  4. セキュリティグループの割り当て
    80番ポートが空いていればいいのでEC2インスタンスに割り当てたセキュリティグループを選択します。

  5. セキュリティ設定の構成
    httpだと注意されます。。

  6. ヘルスチェックの設定
    ELBがEC2インスタンスを監視する方式を指定します。

    pingプロトコル
      : HTTP、HTTPSSSLTCPから選択します。
      今回はHTTPを利用します。
    pingポート
      : そのポート番号です。
      今回は80番ポートを利用します。
    pingパス
      : ヘルスチェック先パスを指定します。デフォルト値は「/index.html」です。
      SSLTCPの場合、指定不要です。
    応答タイムアウト(秒)
      : ヘルスチェックの応答待ち時間を指定します。デフォルト値は「5」です。
      ここで指定した時間以内に応答がない場合、NGと判断します。
    ヘルスチェック間隔(秒)
      : ヘルスチェックを行う間隔を指定します。デフォルト値は「30」です。
    非正常のしきい値
      : EC2インスタンスがダウンしたと判断するヘルスチェックNG連続回数を指定します。
      デフォルト値は「2」です。
    正常のしきい値
      : EC2インスタンスがアップしたと判断するヘルスチェックOK連続回数を指定します。
      デフォルト値は「10」です。

    デフォルト設定の場合、30秒ごとに監視対象サーバに向けて、
    http://<対象サーバ>/index.html
    のリクエストを発行し、5秒間応答がない、を2連続検知すると、
    監視対象サーバがダウンしたと判断します。

  7. EC2 インスタンスの追加
    振り分け対象のサーバを指定します。
    今回は作成したWebサーバ2台を選択します。

    クロスゾーン負荷分散の有効化
      : LBが存在するEC2インスタンスにしか振り分けない場合、こちらのチェックを外してください。
    Connection Drainingの有効化
      : LBがEC2インスタンスを強制的に切り離す時間を設定します。
      ファイルダウンロード等長時間のリクエスト処理があった場合、
      切り離してもここで指定した秒数は待つ、という設定です。
      メンテナンス等で片系切り離して設定変更などする時に大変有用な機能です。

  8. タグの追加
  9. 確認
    LBが作成されます。

  10. 作成状況確認
    一覧画面へ遷移します。

    作成されたELBを選択して、概要タブを選択します。

    ステータスが「2 個のうち 2 個のインスタンスが実行中です」となっていれば準備完了です。
    DNS名はメモってください。

    インスタンスタブを選択します。

    インスタンスの各ステータスが「InService」となっていることも確認します。

  11. 動作確認
    それでは接続しましょう。
    その前に、、、
    接続先がわかるように各Webサーバのindex.htmlをホスト名等分かり易いように編集してください。

    $ curl http://lb-*********.ap-northeast-1.elb.amazonaws.com/index.html
    ip-192-168-100-35

    Web#2(複製先)へアクセスされました。
    それではもう一回。

    $ curl http://lb-*********.ap-northeast-1.elb.amazonaws.com/index.html
    ip-192-168-0-54

    Web#1(複製元)へアクセスされました。
    はい、ちゃんとバランシングされていますね。

    次に片系を落としてみて切り離されることを確認しましょう。

    今回はWeb#2を落とします。

    [root@ip-192-168-100-35 ~]# /etc/init.d/httpd stop
    Stopping httpd:                                            [  OK  ]

    15秒ぐらいでWeb#2のステータスがOutOfServiceとなりました。
    それでは接続

    $ curl http://lb-*********.ap-northeast-1.elb.amazonaws.com/index.html
    ip-192-168-0-54

    Web#1にアクセスされました。
    10回ほどアクセスしても全てWeb#1へアクセスされました。

    Web#2を起動します。

    [root@ip-192-168-100-35 ~]# /etc/init.d/httpd start
    Starting httpd: httpd: apr_sockaddr_info_get() failed for ip-192-168-100-35
    httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
                                                               [  OK  ]
    >||
    
    10分ほどでWeb#2のステータスはInServiceとなりました。
    
    >||
    $ curl http://lb-*********.ap-northeast-1.elb.amazonaws.com/index.html
    ip-192-168-0-54
    $ curl http://lb-*********.ap-northeast-1.elb.amazonaws.com/index.html
    ip-192-168-100-35

    またWeb#2へ振り分けられるようになりました。
    以前、ELBは切り離されたインスタンスはアップしても自動復旧しない(ELBに組み込まれない)という情報を得たのですが、改善したようです。

いかがでしたでしょうか?

AWSを使えば負荷分散、冗長構成のWebAPシステムを簡単に構成することが出来ます。
メンテナンスも考慮されたシステムとなっており、インフラ担当の悩みをどんどんAWSが吸収してくれます。
インフラ担当の運用・保守をAWSに任せることができるので、
その分、楽しい楽しい開発業務に専念することができます。

次回はElastic Cacheを利用してステートレスのWebAP構成を作りましょう。

「Amazon Web Services徹底活用ガイド」を読みました

Amazon Web Services 徹底活用ガイド (日経BPムック)

Amazon Web Services 徹底活用ガイド (日経BPムック)

AWSのノウハウ、Tipsを学びたく読んでみました。

AWSを触ったことがない人にはオススメの一冊だと思います。

AWSの機能紹介はいらなかった。
もっと多くの事例紹介が欲しかったです。

とにかく感じたことはオンプレから移行するなら方式設計、運用設計は大きく見直す必要がある。
郷に入っては郷に従え!に尽きます。

脱vSphereClient (Apple信者向け)

VMware OSX

最近、自宅マシンにESXiをインストールしました。

プライベートの端末がOSXということもあり、vSphereClientの利用がサポートされません(*)。
VirtualBox上のWindowsマシンにインストールして操作する、という利用方法もありますが、
vSphereClientを利用するのに、VirtualBoxを起動して、Windowsを起動して、vSphereClientを起動。
んで、GUI操作ぽちぽち。

めんどい!

実はある程度のことはESXiにSSH接続してコマンドラインで出来るんです!

今回はVM作成を実施してみます。

ESXiのインストールやストレージ、ネットワークは準備済みとします。

ほとんどはvim-cmdコマンドを利用します。

vim-cmd

サブコマンドを実施し、色々なことを実現できます。

[root@localhost:~] vim-cmd
Commands available under /:
hbrsvc/       internalsvc/  solo/         vmsvc/
hostsvc/      proxysvc/     vimsvc/       help

[root@localhost:~] vim-cmd hostsvc
Commands available under hostsvc/:
advopt/                   enable_ssh                refresh_firewall
autostartmanager/         firewall_disable_ruleset  refresh_services
datastore/                firewall_enable_ruleset   reset_service
datastorebrowser/         get_service_status        runtimeinfo
firmware/                 hostconfig                set_hostid
net/                      hosthardware              standby_mode_enter
rsrc/                     hostsummary               standby_mode_exit
storage/                  login                     start_esx_shell
summary/                  logout                    start_service
vmotion/                  maintenance_mode_enter    start_ssh
connect                   maintenance_mode_exit     stop_esx_shell
cpuinfo                   pci_add                   stop_service
disable_esx_shell         pci_remove                stop_ssh
disable_ssh               queryconnectioninfo       task_list
enable_esx_shell          querydisabledmethods      updateSSLThumbprintsInfo

例えば、
vim-cmd hostsvc/enable_sshを実行すると、ESXiホストでssh接続が有効となります。
vim-cmd maintenance_mode_enterを実行すると、ESXiホストをメンテナンスモードにします。
とかです。

それではいってみよう。
今回はCentos6のVMを作成します。

  1. VM作成
  2. ブランクVMを作成します。

    [root@localhost:~] vim-cmd vmsvc/createdummyvm centos6-1 /vmfs/volumes/local_ssd
    4

    戻り値はVMのIDです。

    [root@localhost:~] ls -l /vmfs/volumes/local_ssd/centos6-1_3/
    total 1024
    -rw-------    1 root     root       1048576 May 10 10:42 centos6-1_3-flat.vmdk
    -rw-------    1 root     root           465 May 10 10:42 centos6-1_3.vmdk
    -rw-r--r--    1 root     root             0 May 10 10:42 centos6-1_3.vmsd
    -rwxr-xr-x    1 root     root           977 May 10 10:42 centos6-1_3.vmx
    [root@localhost:~] cat /vmfs/volumes/local_ssd/centos6-1_3/centos6-1_3.vmx
    .encoding = "UTF-8"
    config.version = "8"
    virtualHW.version = "11"
    nvram = "centos6-1_3.nvram"
    pciBridge0.present = "TRUE"
    virtualHW.version = "11"
    nvram = "centos6-1_3.nvram"
    pciBridge0.present = "TRUE"
    svga.present = "TRUE"
    pciBridge4.present = "TRUE"
    pciBridge4.virtualDev = "pcieRootPort"
    pciBridge4.functions = "8"
    pciBridge5.present = "TRUE"
    pciBridge5.virtualDev = "pcieRootPort"
    pciBridge5.functions = "8"
    pciBridge6.present = "TRUE"
    pciBridge6.virtualDev = "pcieRootPort"
    pciBridge6.functions = "8"
    pciBridge7.present = "TRUE"
    pciBridge7.virtualDev = "pcieRootPort"
    pciBridge7.functions = "8"
    vmci0.present = "TRUE"
    hpet0.present = "TRUE"
    floppy0.present = "FALSE"
    scsi0.virtualDev = "lsilogic"
    scsi0.present = "TRUE"
    scsi0:0.deviceType = "scsi-hardDisk"
    scsi0:0.fileName = "centos6-1_3.vmdk"
    scsi0:0.present = "TRUE"
    displayName = "centos6-1"
    guestOS = "other"
    uuid.bios = "56 4d fd a6 54 0c 8e 22-a5 d9 9a 10 df 9d be ae"
    uuid.location = "56 4d fd a6 54 0c 8e 22-a5 d9 9a 10 df 9d be ae"
    vc.uuid = "52 79 a6 30 04 b7 91 37-35 5d ad 4c 42 08 59 a4"

    vmxファイル、vmdkファイル等おなじみのファイルが作成されています。
    勝手に付加された「_3」はどこから来たのでしょう。

  3. ISOファイルアップロード
  4. scpコマンドでCentOS6のisoファイルをdatastoreにアップロードします。

  5. VMリソース設定
  6. 最低限必要な箇所のみ設定します。

    まずは以下を設定。

    • CPU数 -> 2core
    • メモリ値 -> 2GB
    • ネットワーク -> 1つ
    • CDROMデバイス
    • ゲストOS種別
    • VNC
    [root@localhost:~] vi /vmfs/volumes/local_ssd/centos6-1_3/centos6-1_3.vmx
    
    以下、編集
    guestOS = "other"
     ↓
    guestOS = "rhel6-64"
    
    以下、追記
    numvcpus = "2"
    memSize = "2048"
    ide1:0.startConnected = "TRUE"
    ide1:0.deviceType = "cdrom-image"
    ide1:0.fileName = "/vmfs/volumes/local_ssd/CentOS-6.6-x86_64-minimal.iso"
    ide1:0.present = "TRUE"
    ethernet0.virtualDev = "vmxnet3"
    ethernet0.networkName = "VM Network"
    ethernet0.addressType = "generated"
    ethernet0.present = "TRUE"
    RemoteDisplay.vnc.enabled="true"
    RemoteDisplay.vnc.port="5901"
    RemoteDisplay.vnc.password="password"
    RemoteDisplay.vnc.keyMap="jp"

    ide1:0.fileNameはアップロードしたisoファイルのパス指定
    ethernet0.networkNameはポートグループ名
    RemoteDisplay.vnc.portはVNC接続するポート番号
    後述しますがVNC接続する用のポートをESXiで開放する必要があります。
    RemoteDisplay.vnc.passwordはVNC接続時のパスワード


    次はディスクサイズを設定。

    • ディスクサイズ -> 16GB
    [root@localhost:~] vmkfstools -c 10g -d thin /vmfs/volumes/local_ssd/centos6-1_3/centos6-1_3-1.vmdk
    [root@localhost:~] vi /vmfs/volumes/local_ssd/centos6-1_3/centos6-1_3.vmx
    
    以下、編集
    scsi0:0.fileName = "centos6-1_3.vmdk"
     ↓
    scsi0:0.fileName = "centos6-1_3-1.vmdk"
    
    [root@localhost:~] vmkfstools -U /vmfs/volumes/local_ssd/centos6-1_3/centos6-1_3.vmdk

    元のvmdkファイルは削除して構いません。

  7. VNC接続設定
  8. ESXiでVNC接続できるようにポートを開放します。

    セキュリティプロファイル定義
    [root@localhost:~] cp /etc/vmware/firewall/service.xml /etc/vmware/firewall/service.xml.bak
    [root@localhost:~] chmod u+w /etc/vmware/firewall/service.xml
    [root@localhost:~] chmod +t /etc/vmware/firewall/service.xml
    以下を<ConfigRoot>タグ内に追記
      <service id='0140'>
        <id>vnc</id>
        <rule id='0000'>
          <direction>inbound</direction>
          <protocol>tcp</protocol>
          <port type='dst'>5901</port>
        </rule>
       <enabled>false</enabled>
       <required>false</required>
      </service>
    [root@localhost:~] chmod 444 /etc/vmware/firewall/service.xml
    ポート開放
    [root@localhost:~] esxcli network firewall refresh
    [root@localhost:~] esxcli network firewall ruleset list
    vnc                    false
    [root@localhost:~] esxcli network firewall ruleset set -r vnc -e true
    [root@localhost:~] esxcli network firewall ruleset set -r vnc -a true
    Already allowed all ip

  9. VM起動
  10. [root@localhost:~] vim-cmd vmsvc/power.on 3
    Powering on VM:

  11. コンソール接続
  12. OSXの場合、デフォルトでVNCクライアントが備わっています。
    Finderのメニューから[移動] - [サーバへ接続]を選択
    f:id:sfujimoto:20150510215217p:plain

    vnc://:<設定したポート番号>
    f:id:sfujimoto:20150510215013p:plain

    vmxファイルに設定したパスワードを入力
    f:id:sfujimoto:20150510220408p:plain

    コンソールの画面が表示されます。
    f:id:sfujimoto:20150510215356p:plain

このようにコマンドとVNC接続により、VMを構築できました。
細やかな設定は難しいところもありますが、
多くの機能を扱うことができます。

私は省エネのため、利用しない時はESXiを落としています。
そのため、VMを全てシャットダウンして、シャットダウンが完了したらESXiをシャットダウンするスクリプトと、
ESXiを起動したらtxtファイルに登録したVMを自動で起動するスクリプトを作成しています。
またESXiはpythonを標準搭載しているのでpythonistaには助かります。

(*) Wineでほとんどの機能を利用可。

AWS AMI

最近、MAC用のSSHクライアントを探しています。
iTerm2でやっていますが、
WindowsSSHクライアントはログの設定保存でホスト名でログファイルを作成できますが、
iTerm2は元々SSHクライアントツールじゃないところもあり、
ログは都度Startさせなきゃ開始してくれないし、
ファイル名も都度指定しなきゃいけないし、で正直めんどい。

はい、本題です。
前回はRDSを使ってWeb-AP-DB構成を作りました。
本日はWebAPサーバ(EC2インスタンス)をAMIのImage化し、
異なるEC2インスタンスとして生成します。

EC2でWebAP構築
 ↓
RDSでWebAP-DB構築
 ↓

AMIでEC2のイメージ化、複製(今回はここ)

 ↓
ELBで冗長化
 ↓
ElastiCacheでセッション管理
 ↓
S3にアクセスログ保存
 ↓
(できれば)SQSでWebAP連携

AMI (Amazon Machine Image)

AMIはテンプレートです。
EBSディスクのコピーです。
AMIは単体では何もできず、
AMIからEC2インスタンスを作成して複製したり、
AMIを他リージョンにコピーしてDR構成を取ることが可能です。

AMIは不変なデータであるため、
バックアップとしても利用可能です。

それでは作成します。

  1. EC2画面へ遷移
    AWSのメニューからEC2を選択

  2. AMI作成
    対象となるEC2インスタンスを選択して、
    [Actions] -> [Image] -> [Create Image]を選択します。

  3. 設定値入力
    Image name -> イメージ名
    Image description -> 説明

    [Create Image]ボタンを押せば、作成が開始されます。

    AMI一覧ページを表示するとpenddingステータスのAMIを確認することができます。

次は作成したAMIからEC2インスタンスを作成します。

  1. AMI画面へ遷移
    AWSのメニューからEC2を選択
    左のメニューからAMIsを選択

  2. EC2インスタンス作成
    作成したAMIを選択して、
    [Actions] -> [Launch]を選択します。
    それ以降はEC2インスタンス作成と同じです。

    EC2インスタンス一覧を確認すると、
    作成したAMIのIDと同じAMI IDのEC2インスタンスが作成されていることを確認できます。

それでは確認です。

# curl http://<EC2 Instance Public DNS>/django/list/
Hello, fuji

アクセスできました。
今回はボリューム少な目でしたね。。

次回はELBを作成してアクセスが分散されることを確認します。

AWS RDS

早いもので更新が2週間空いてしまった。

AWSスクリプトを書くことが楽しくなっちゃってアウトプットをサボりました。
botoというPython製のAWSライブラリがあって、
これがまた利用者に優しくAPIリファレンスさえ読めば、
PythonAWSも理解が乏しい私が簡単にAWSの環境構築スクリプトを組めちゃいました。

さて、本題。
前回はEC2インスタンスを作成して、Webサーバを立ち上げました。

今回はRDSインスタンスを立ち上げて、Webサーバと連携します。

EC2でWebAP構築
 ↓

RDSでWebAP-DB構築 (今回はここ)

 ↓
AMIでEC2のイメージ化、複製
 ↓
ELBで冗長化
 ↓
ElastiCacheでセッション管理
 ↓
S3にアクセスログ保存
 ↓
(できれば)SQSでWebAP連携

では早速RDSを作成しましょう。
(キャプチャは後日こっそりつけときます)

あれ?
2週間の間にEC2インスタンスのOSがRHEL7になってる。。。

  1. RDS画面へ遷移
     AWSのメニューからRDSを選択

  2. RDS作成開始
     [Get Started Now]ボタンやら[Launch DB Instance]ボタンやらをクリック

  3. DBエンジン選択
     DBエンジンは4種類が選べます。(ブログ執筆現在:2015/04/18)
     Auroraはまだ東京リージョンでは登場していません。

     今回はMySQLを利用します。


  4. MultiAZ
     MultiAZかどうかを選択します。
     MultiAZはDBの配置方式として複数のAZに配置するかどうかです。
     MultiAZを選択すると、稼働率99.95%以上を保証されます。
     99.95%を下回る場合、AWSから返金があります。
     ※注意:MultiAZは無償枠外です!
      私のような無償プレイヤーは要注意です。
      でも、本番運用するような場合は必須だと思う。


  5. 基本設定

     t1.micro以外は無償枠外

      • ストレージタイプ

     SSD、高性能SSD、HDDから選択可

      DNS等に利用され、データベースでは持たないっぽ

      • DBユーザ名
      • パスワード


  6. 詳細設定

    • ネットワーク設定
      • VPC
      • SubnetGroup

      VPCのSubnetとは別で、複数のSubnetをまとめたもの

      • Availability Zone
      • SecurityGroup

      EC2作成時とは別でDBポート番号のみを許可した紐づけるのが望ましい

    • DB詳細設定
      • DB名

      作成する空のデータベース

      • ポート番号

      最初からデフォルト値が入っている

     以上の設定で作成されます。


  7. 作成中
     けっこう時間かかります。。。


  8. APサーバ作成
     DB作成中にDBを利用可能なAPサーバを構築しましょう。
     APサーバはDjangoでちょちょいと。。。


     ん?


     あれ?こうかな。


     ダメだな。


     あー、これか。


     はぁ。。。


     あーーーー
     ですよねー
     だいたいこういう時ってSELINUXですよねー


     ちょちょいと、3時間(内、SELINUXに2時間半)で出来ました。
     さて、そろそろRDS立ち上がったかな。

     簡単にやったことを備忘録

    # yum install -y gcc python-devel mysql mysql-devel
    # pip install django mysql-python
    # django-admin startproject project
    # cd project
    # vi project/settings.py
    databaseの設定追記
    # ./manage.py startapp app
    # vi project/settings.py
    appを追加
    # vi app/models.py
    Userクラス追加
    # ./manage.py makemigrations
    # ./manage.py makemigrate
    # vi app/views.py
    list関数定義
    UserテーブルからSelectしたデータにHelloを付けて返す
    # vi project/urls.py
    list/とapp.views.listを紐付け
    # vi /etc/httpd/conf.module.d/10-wsgi.conf
    魔法の言葉を追加
    # systemctl 
    
    # setenforce 0
    こ、こいつまで遠かった。。。


  9. DB接続確認
     試験用にDBを設定します。

    # mysql -h <RDS Public DNS> -u <username> -p
    Password: 
    
    > show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | innodb             |
    | mysql              |
    | newdb              |
    | performance_schema |
    +--------------------+

    newdbがRDS作成時に指定したDB名です。

    > use newdb;
    Database changed
    
    > show tables;
    +----------------------------+
    | Tables_in_newdb            |
    +----------------------------+
    | app_user                   |
    | auth_group                 |
    | auth_group_permissions     |
    | auth_permission            |
    | auth_user                  |
    | auth_user_groups           |
    | auth_user_user_permissions |
    | django_admin_log           |
    | django_content_type        |
    | django_migrations          |
    | django_session             |
    +----------------------------+
    
    > desc app_user;
    +-------------+-------------+------+-----+---------+----------------+
    | Field       | Type        | Null | Key | Default | Extra          |
    +-------------+-------------+------+-----+---------+----------------+
    | id          | int(11)     | NO   | PRI | NULL    | auto_increment |
    | name        | varchar(32) | NO   |     | NULL    |                |
    | create_date | date        | NO   |     | NULL    |                |
    +-------------+-------------+------+-----+---------+----------------+
    

    Djangoが勝手に色々作ってます。
    app_userテーブルにデータを登録して取得します。

    > insert into app_user values(1, 'fuji', '2015-04-18');
    Query OK, 1 row affected (0.00 sec)
    
    > select * from app_user;
    +----+------+-------------+
    | id | name | create_date |
    +----+------+-------------+
    |  1 | fuji | 2015-04-18  |
    +----+------+-------------+
    1 row in set (0.00 sec)

    おk


  10. 接続確認
    早速繋いでみましょう。

    # curl http://<EC2 Instance Public DNS>/django/list/
    Hello, fuji

    おー、うまくいきました。

    今回は
    クライアント -> Internet -> Web -> AP -> RDS
    というあるある構成をAWS上に作ることができました。

    次回は今回作ったAPサーバをAMIにイメージ化して複製します。