カテゴリー: aws

AWSの情報をまとめる

  • S3でアクセス欄に「エラー」と表示される件を対処する

    S3でアクセス欄に「エラー」と表示される件を対処する

    はじめに

    AWSの権限周りは複雑に設定できるだけに難しい。今回の件もその一つ。アクセス欄が「エラー」になるのでその対処を行う。

    問題

    掲題のとおり。エラーになるのだ。

    対処

    ご察しの通りロールが足りないのが問題。

    実際どのロールかというと、以下のとおり。

    以下の設定はS3のディレクトリの確認や、権限まわりを表示させることができる

            {
                "Action": [
                    "s3:ListAllMyBuckets",
                    "s3:GetBucketPublicAccessBlock",
                    "s3:GetBucketPolicyStatus",
    
                    "s3:GetAccountPublicAccessBlock",
    
                    "s3:GetBucketAcl"                
                ],
                "Effect": "Allow",
                "Resource": "arn:aws:s3:::*"
            },

    この対処を行うと、エラーは解消される。

     

  • VMware ESXiにmacからsshする

    VMware ESXiにmacからsshする

    はじめに

    VMware ESXiのホストにログインすることは可能だが、そのログイン方法は少し特殊だ

    ESXiでSSHを有効にする

    デフォルトでESXiはsshが無効になっている。 ESXiシェルとSSHがデフォルトで停止されている。まずはこれを有効にする

    macからESXiへSSHする

    ssh -l user名、 IPで接続する

    ssh -l root 192.168.0.x

    ちなみにシャットダウンは

    poweroff

    コマンドで行う。

  • VPC の作成中にエラーが発生しました: Performing this operation would exceed the limit of 5 NAT gateways

    VPC の作成中にエラーが発生しました: Performing this operation would exceed the limit of 5 NAT gateways

    概要

    VPCを作成する際に出力されたエラー

    原因

    NAT Gatewayの上限に抵触している。

    対処

    上限に達してしまっているので上限を増やす必要がある。

    AWSコンソール上のサポートから問い合わせることで対処が可能

    東京リージョンの「アベイラビリティーゾーン当たりの NAT ゲートウェイ上限数」を10へと変更することによって対応可能。

     

  • AWS ECRにimageをpushする

    AWS ECRにimageをpushする

    はじめに

    ECRのリポジトリへimageをpushする手順を記載する

    準備

    手元の環境にimageがあること(docker imageコマンドでimageが出てくること)

    $ docker images
    REPOSITORY                        TAG                  IMAGE ID            CREATED             SIZE
    sumito_http                          latest               54f5e642e4ae        5 weeks ago         1.1GB

    手順

    ECRにリポジトリを作成する

    ECRへpush用のタグをつける

    $ docker tag sumito_http 123456789.dkr.ecr.ap-northeast-1.amazonaws.com/dmp/sumito:latest 

    pushする

    docker push 123456789.dkr.ecr.ap-northeast-1.amazonaws.com/dmp/sumito:latest

    この際

    “` no basic auth credentials “`

    と表示される場合はログイン処理が必要になる

    $ aws ecr get-login --no-include-email --region ap-northeast-1

    表示されたコマンドを実施して

    “` Login Succeeded “`

    と表示されればOKだ。

    再度pushするとContainer imageがECRに格納される

  • AWS S3でフォルダを作成する方法

    AWS S3でフォルダを作成する方法

    はじめに

    AWSにおける作業の大半はawsコマンドで操作できます。

    しかし、画面上にあるはずの「フォルダの作成」については、オフィシャルドキュメントにコマンドが見つかりませんでした。 https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/using-s3-commands.html

    S3におけるフォルダという概念

    実はS3においてフォルダという概念は存在しませんが、論理的な階層を暗示するためにキー名のプレフィックスや区切り記号を使用することができます。Amazon S3のデータモデルはフラットな構造を持ち、バケット内にオブジェクトが格納されます。バケット内にはサブバケットやサブフォルダの階層は存在しません。

    https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/UsingMetadata.html

    Amazon S3 のデータモデルはフラットな構造です。バケットを作成し、そのバケットにオブジェクトが格納されます。サブバケットやサブフォルダの階層はありません。ただし、Amazon S3 コンソールで使用されているようなキー名のプレフィックスや区切り記号を使用して論理的な階層を暗示できます。

    S3におけるデータモデルはフラットな構造を持ちます。バケットを作成し、そのバケットにオブジェクトが格納されます。サブバケットやサブフォルダの階層は存在せず、1つのバケットに多数のファイルが格納される形式となります。

    S3におけるフォルダの作成方法

    awsコマンドを使用して、S3上にフォルダを作成する方法を説明します。まずは以下のコマンドを使用して、フォルダを作成するためのキー名を指定します。

    aws s3 cp sample.txt s3://test/sample-folder/

    このコマンドでは、testバケット内にsample-folderという名前のフォルダを作成し、その中にsample.txtをコピーすることができます。複数の環境が.awsディレクトリに登録されている場合は、–profileオプションを使用して読み込むプロファイルを指定することができます。

    参考情報: Amazon S3における「フォルダ」という幻想をぶち壊し、その実体を明らかにする https://dev.classmethod.jp/cloud/aws/amazon-s3-folders/

    また、フォルダ内の全てのファイルを再帰的にコピーする場合は、–recursiveオプションを使用します。

    以上が AWS S3でフォルダを作成する方法になります。

  • RDSのパラメータグループをCLIで取得する

    RDSのパラメータグループをCLIで取得する

    はじめに

    GUIのこの部分のパラメータ

    RDSのパラメータグループをGUIで管理するのは何かとは大変。CLIで取得する

    コマンド

    aws rds --profile=stg describe-db-parameters --db-parameter-group-name default.aurora-mysql5.7

    staging環境や、本番環境など複数の環境を管理している場合は、
    credentialを登録すると、環境を簡単に切り替えられるので便利

    cat ~/.aws/credentials 
    [stg]
    aws_access_key_id = xxxxxxx
    aws_secret_access_key = yyyyyyy
    region=ap-northeast-1
    output=json
    [prd]
    aws_access_key_id = xxxxxxxx
    aws_secret_access_key = yyyyyyy
    region=ap-northeast-1
    output=json

    結果

    $ aws rds --profile=stg describe-db-parameters --db-parameter-group-name default.aurora-mysql5.7
    {
        "Parameters": [
            {
                "ParameterName": "allow-suspicious-udfs",
                "Description": "Controls whether user-defined functions that have only an xxx symbol for the main function can be loaded",
                "Source": "engine-default",
                "ApplyType": "static",
                "DataType": "boolean",
                "AllowedValues": "0,1",
                "IsModifiable": false,
                "ApplyMethod": "pending-reboot"
            },
            {
                "ParameterName": "aurora_lab_mode",
                "ParameterValue": "0",
                "Description": "Enables new features in the Aurora engine.",
                "Source": "engine-default",
                "ApplyType": "static",
                "DataType": "boolean",
                "AllowedValues": "0,1",
                "IsModifiable": true,
                "ApplyMethod": "pending-reboot"
            },

     

     

  • redashでALL Queriesを選択すると “Loading…” のままで変わらない

    redashでALL Queriesを選択すると “Loading…” のままで変わらない

    問題発生

    QueriesがLoadingのままで、Query一覧が表示されない

    redash_serverコンテナには以下のログが吐かれていた

    [2018-10-10 12:48:45,419][PID:14][ERROR][redash] Exception on /api/queries [GET]
    Traceback (most recent call last):
      File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1639, in full_dispatch_request
        rv = self.dispatch_request()
      File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1625, in dispatch_request
        return self.view_functions[rule.endpoint](**req.view_args)
      File "/usr/local/lib/python2.7/dist-packages/flask_restful/__init__.py", line 477, in wrapper
        resp = resource(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/flask_login/utils.py", line 228, in decorated_view
        return func(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/flask/views.py", line 84, in view
        return self.dispatch_request(*args, **kwargs)
      File "/app/redash/handlers/base.py", line 31, in dispatch_request
        return super(BaseResource, self).dispatch_request(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/flask_restful/__init__.py", line 587, in dispatch_request
        resp = meth(*args, **kwargs)
      File "/app/redash/permissions.py", line 48, in decorated
        return fn(*args, **kwargs)
      File "/app/redash/handlers/queries.py", line 197, in get
        with_last_modified_by=False
      File "/app/redash/handlers/base.py", line 106, in paginate
        items = serializer(results.items, **kwargs).serialize()
      File "/app/redash/serializers.py", line 75, in serialize
        result = [serialize_query(query, **self.options) for query in self.object_or_list]
      File "/app/redash/serializers.py", line 105, in serialize_query
        d['user'] = query.user.to_dict()
    AttributeError: 'NoneType' object has no attribute 'to_dict'

    解決へのアプローチ

    とりあえずググる。前例が見つからない。

    twitterで騒ぐ。知ってる人いるかも。

    すぐに見つからない。

    redashのフォーラムに聞いた。

    https://discuss.redash.io/t/when-open-queries-all-queries-always-loading/2449

    redashの生みの親、arikfr がコメントをくれた

    https://discuss.redash.io/t/when-open-queries-all-queries-always-loading/2449/2?u=gitsumito

    あ!確かに消しました。

    そういえばやったこと

    この画面にたどり着く前、redashユーザーの作成を行なった。

    docker exec -it redash_server_1 ./manage.py users create taro@sumito.jp taro

    manage.pyを使い、ユーザーの追加した。

    ちなみにこのmanage.pyを使い、ユーザの追加・削除することは随分前のversionから利用可能で、このコマンドを用いた管理方法は @kakakakakku 氏のブログが非常にわかりやすい 

    ただ、redash version 5.0でredashユーザーの作成を行うと(昔のバージョンもかも)、既に登録されているユーザでも新しく登録できてしまう。

    つまりデータベースに二重に登録されてしまう状態だった。

    この状態で作成したユーザでloginしようとすると、Internal Server Errorになりログインできなくなる。

    仕方なくユーザーを削除することにした

    docker exec -it redash_server_1 ./manage.py users delete taro@sumito.jp

    Successfully。削除できた。

    再度ユーザの作成を行なうと、無事作成したユーザでredashにログインできるようになった。

    めでたし、めでたし。と思ったが、今回の問題

    redashでALL Queriesを選択すると “Loading…” のままで変わらない

    という問題に繋がったようだ。

    解決方法

    postgresのコンテナにログインし、userid周りを調整

    docker exec -it redash_postgres_1 /bin/bash
    psql -U postgres postgres

    redashのユーザ情報が格納されているusersテーブルでidを確認する

    postgres=# select * from users order by id desc;
    updated_at | created_at | id | org_id | name | email | password_hash | groups | api_key | profile_image_url | disabled_at 
    -------------------------------+-------------------------------+----+--------+--------------------+-----------------------------------------+--------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------------------+-------------------+-------------------------------
    (skip)
    2018-08-30 06:48:30.812508+00 | 2018-08-30 06:40:15.029328+00 | 51 | 1 | *** | gomes@sumito.jp | *** | {2,5} | *** | | 
    2018-10-09 08:18:34.331088+00 | 2018-10-09 08:17:20.97545+00 | 55 | 1 | taro | taro@sumito.jp | *** | {2,5} | *** | | 
    2018-10-09 08:18:37.220691+00 | 2018-10-09 08:18:00.078037+00 | 56 | 1 | jiro | jiro@sumito.jp | *** | {2,5} | *** | 

    id 52~54 が欠番していることがわかる。

    これが自分が削除したユーザのID。

    まずはid 52~56のeventテーブルから全ての履歴を削除した

    SELECT * FROM events WHERE user_id = 52;
    DELETE FROM events WHERE user_id = 52;
    
    SELECT * FROM events WHERE user_id = 53;
    DELETE FROM events WHERE user_id = 53;
    
    SELECT * FROM events WHERE user_id = 54;
    DELETE FROM events WHERE user_id = 54;
    
    SELECT * FROM events WHERE user_id = 55;
    DELETE FROM events WHERE user_id = 55;
    
    SELECT * FROM events WHERE user_id = 56;
    DELETE FROM events WHERE user_id = 56;

    これでイベントが全て削除された。

    usersテーブルのid 55,56 を 52,53 に変更した

    update users set id = 52 where email = 'taro@sumito.jp';
    update users set id = 53 where email = 'jiro@sumito.jp';

    その後、user idのインクリメント部分を管理しているusers_id_seq

    を56 から 53に変更した。

    ALTER SEQUENCE users_id_seq RESTART WITH 53;

    対応はこれだけ、

    無事問題が解決した。

    最後に

    CLIを見直すことを示唆してくれた。

    今後のversion upが楽しみ。

  • CloudWatch Logsで任意の文字列を監視し、メールで通知する

    CloudWatch Logsで任意の文字列を監視し、メールで通知する

    はじめに

    CloudWatch Logsで集めたログの中で任意の文字列(例えば、Errorとか、Criticalとか)が記載されていた場合、メールなどで通知させたい事がある。

    Node.jsをLambdaで動かす事で文字列監視を実装した。

    通知先の作成

    まずはSNSでメールの宛先から作成しARNを控え、エンドポイントとなるメールアドレスを設定する。 

    node.js(lambda)

    関数コードを編集

     

    ERROR、CRITICALという文字列があったら監視するスクリプト

    var zlib = require('zlib');
    var aws = require('aws-sdk');
    var sns = new aws.SNS({ region: 'ap-northeast-1' });
    exports.handler = function(input, context, callback) {
      var data = new Buffer(input.awslogs.data, 'base64');
      zlib.gunzip(data, function(e, rst) {
        if (e) {
          callback(e);
        } else {
          rst = JSON.parse(rst.toString('utf-8'));
          var errL = rst['logEvents']
              .filter(function(evt) { return evt['message'].match(/ERROR/i) ;})
              .filter(function(evt) { return !evt['message'].match(/$^/) ;})
              .map(function(evt) { return evt['message'] });
          console.log('processing[Error]' + errL.length + '/' + rst['logEvents'].length + ' events.');
          var critL = rst['logEvents']
              .filter(function(evt) { return evt['message'].match(/CRITICAL/i) ;})
              .filter(function(evt) { return !evt['message'].match(/$^/) ;})
              .map(function(evt) { return evt['message'] });
          console.log('processing[Crit]' + critL.length + '/' + rst['logEvents'].length + ' events.');
          if (errL.length === 0 && critL.length === 0) { callback(); return; }
          var date = new Date();
          date.setTime(date.getTime() + 1000*60*60*9);
          var dateTime = date.getFullYear() + '/' + ("0" + (date.getMonth() + 1)).slice(-2) + '/' + ("0" + date.getDate()).slice(-2) +
               ' ' + ("0" + date.getHours()).slice(-2) + ':' + ("0" + date.getMinutes()).slice(-2) + ':' + ("0" + date.getSeconds()).slice(-2);
          if (0 < errL.length) {
            // has error log
            var sjct = '[Error] Notify From CloudWatch Logs';
            var pl = { default: '' };
            pl['default'] += 'NotifyAt: ' + dateTime.valueOf() + '\n';
            pl['default'] += 'Log: ' + rst['logGroup'] + ' - ' + rst['logStream'] + '\n';
            pl['default'] += 'Filter: ' + rst['subscriptionFilters'] + '\n';
            pl['default'] += 'Messages:\n';
            pl['default'] += errL.join('\n---\n');
            sns.publish({
              Subject: sjct,
              Message: JSON.stringify(pl),
              MessageStructure: 'json',
              TargetArn: 'arn:aws:sns:ap-northeast-1:1234567890(your no):(your arn)'
            }, function(err, data) {
              if (err) {
                callback(err);
              } else if (0 < critL.length) {
                // has waring too
                var sjct = '[Critical] Notify From CloudWatch Logs';
                var pl = { default: '' };
                pl['default'] += 'NotifyAt: ' + dateTime.valueOf() + '\n';
                pl['default'] += 'Log: ' + rst['logGroup'] + ' - ' + rst['logStream'] + '\n';
                pl['default'] += 'Filter: ' + rst['subscriptionFilters'] + '\n';
                pl['default'] += 'Messages:\n';
                pl['default'] += critL.join('\n---\n');
                sns.publish({
                  Subject: sjct,
                  Message: JSON.stringify(pl),
                  MessageStructure: 'json',
                  TargetArn: 'arn:aws:sns:ap-northeast-1:1234567890(your no):(your arn)'
                }, function(err, data) {
                  if (err) callback(err);
                  else callback(null, data);
                });
              } else {
                callback(null, data);
              }
            });
          } else {
            // has NOT error log == criticaling only
            var sjct = '[Critical] Notify From CloudWatch Logs';
            var pl = { default: '' };
            pl['default'] += 'NotifyAt: ' + dateTime.valueOf() + '\n';
            pl['default'] += 'Log: ' + rst['logGroup'] + ' - ' + rst['logStream'] + '\n';
            pl['default'] += 'Filter: ' + rst['subscriptionFilters'] + '\n';
            pl['default'] += 'Messages:\n';
            pl['default'] += critL.join('\n---\n');
            sns.publish({
              Subject: sjct,
              Message: JSON.stringify(pl),
              MessageStructure: 'json',
              TargetArn: 'arn:aws:sns:ap-northeast-1:1234567890(your no):(your arn)'
            }, function(err, data) {
              if (err) callback(err);
              else callback(null, data);
            });
          }
        }
      });
    };
    

     

    トリガーを設定

    今回はCloudWatch Logsがトリガーとなるので、CloudWatch Logsを選択し、その後対象のログを選択する。

     

    通知先の設定

    Amazon SNSのARNを選択する(予めSNSでトピックを作っておく必要がある)

    以上

  • Error running query: No index found for query. Please use a SCAN query, or set allow_select_scan=True opt allow_select_scan true

    Error running query: No index found for query. Please use a SCAN query, or set allow_select_scan=True opt allow_select_scan true

    はじめに

    DynamoDBは言わずと知れたKVSだが、それを忘れてredashで実行しようとすると、当然ながらエラーになる。

    redashでDynamoDBのデータを見ようとした際、以下のようなエラーが出た

     
     

    原因

    RDSのように普通のSQLで書いていた。
    たとえば上のように
     
    “` select * from xxxx “`
    と書いていたのが原因
     

    対応

     
    “` scan * from xxxx “`
    という書き方に修正し再度実行
     
    無事データを取得できた
     
  • unsubscribeされてしまったcloudwatch (SNS)通知を検知する

    unsubscribeされてしまったcloudwatch (SNS)通知を検知する

    はじめに

    AWSでcloudwatchで監視し、異常があったらSNSで通知する事はあるが、SNSではデフォルトで「配信停止(unsubscribe)する場合は、ここクリックしてね」とありがたいことに(?)記載されてある。

    チーム開発しているとSNSの通知先がメーリングリストにしていると、誤って押されてしまうと、その後のメールが来なくなってしまう。

    配信停止(unsubscribe)を押された場合検知はさっくり作れたのでそれをまとめる

    手順

    awsコマンドとjqコマンドをうまく組み合わせる

    aws sns list-subscriptions | jq -r '.Subscriptions[] | select(.SubscriptionArn == "Deleted") '

    結果

    aws sns list-subscriptions | jq -r '.Subscriptions[] | select(.SubscriptionArn == "Deleted") '
    {
      "SubscriptionArn": "Deleted",
      "Owner": "xxxxxxxxxx",
      "Protocol": "email",
      "Endpoint": "sute@sumito.jp",
      "TopicArn": "arn:aws:sns:ap-northeast-1:xxxxxxx:xxxxxxxxx"
    }
    

    unsubscribeされたメールドレスや、TopicArn情報などを確認することができる。

    ちなみにチャットワークに通知する場合は以下の通りシェルでラップする

    #!/bin/bash
    aws --profile=$1 sns list-subscriptions | jq -r '.Subscriptions[] | select(.SubscriptionArn == "Deleted") ' > /tmp/check-unsubscribe.$1
    
    # 文字を整形する
    # headder
    cat << _EOT_ > /tmp/check-unsubscribe.$1.txt
    [info]
    [title]Someone clicked unsubscribe link
    [/title]
    _EOT_
    
    # contents
    cat  /tmp/check-unsubscribe.$1 >> /tmp/check-unsubscribe.$1.txt
    echo "[/info]"  >> /tmp/check-unsubscribe.$1.txt
    
    _roomid=xxxxxxxxxxxxx # (your chatroom id)
    _body=`cat /tmp/check-unsubscribe.$1.txt`
    
    # 1件以上あれば通知する
    if [ `wc -l /tmp/check-unsubscribe.$1 | awk '{print $1}'` -gt 0 ] ; then
        curl -X POST -H "X-ChatWorkToken: 123456789" -d "body=${_body}" "https://api.chatwork.com/v2/rooms/${_roomid}/messages"
    fi
    
    exit 0