IDEA Note

  • the input device is not a TTY

    the input device is not a TTY

    はじめに

    cronでdockerコンテナ内のシェルを動かし、結果をリダイレクトしようとした際、

    “` the input device is not a TTY “`

    とエラーが出て、期待通りに動かなかった。

    原因

    /usr/bin/docker exec -it redash_server_1 ./manage.py users list > /tmp/hoge

    cronで動かすのに
    “` docker exec -it “`

    -it のtが邪魔していた。

     

    dockerのコンテナ内に入るとき、もしくはコンテナ内のシェルを動かすとき、docker exec -itがもはや決まり文句のように打っていたが、

    改めてオプションを見直して見ると、-t はTTYデバイスに割り当てるオプションらしい。

     

    https://docs.docker.com/engine/reference/commandline/exec/#usage

    --tty , -t        Allocate a pseudo-TTY

    これがcronで実行するときには不要であり、これがあることによりエラーになる。

    対応策

    tオプションを削る

    /usr/bin/docker exec -i redash_server_1 ./manage.py users list > /tmp/hoge

    これで問題なくcronで実行される

  • 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"
            },

     

     

  • rbenvのインストール

    rbenvのインストール

    はじめに

    rubyを自由にversion切り替えられるrbenvはとても便利。

    しかし、インストールは少しめんどくさい。

    よく使う手順なのでとりあえず晒す

    手順

    以下、コピペでOK。

    インストールしたいversionは適宜変更すること

    yum -y install openssl-devel readline-devel zlib-devel libcurl-devel git
    cd /usr/local
    git clone https://github.com/sstephenson/rbenv.git rbenv
    mkdir rbenv/shims rbenv/versions rbenv/plugins
    groupadd rbenv
    chgrp -R rbenv rbenv
    chmod -R g+rwxXs rbenv
    cd /usr/local/rbenv/plugins
    git clone https://github.com/sstephenson/ruby-build.git ruby-build
    chgrp -R rbenv ruby-build
    chmod -R g+rwxs ruby-build
    git clone https://github.com/sstephenson/rbenv-default-gems.git rbenv-default-gems
    chgrp -R rbenv rbenv-default-gems
    chmod -R g+rwxs rbenv-default-gems
    
    cat << EOF > /etc/profile.d/rbenv.sh
    export RBENV_ROOT="/usr/local/rbenv" 
    export PATH="\$RBENV_ROOT/bin:\$PATH" 
    eval "\$(rbenv init -)" 
    EOF
    
    cat /etc/profile.d/rbenv.sh
    
    cat << EOF > /usr/local/rbenv/default-gems
    bundler
    rbenv-rehash
    EOF
    
    cat /usr/local/rbenv/default-gems
    source /etc/profile.d/rbenv.sh
    rbenv install -l
    
    yum install gcc bzip2
    rbenv install 2.2.5
    rbenv global 2.2.5
    ruby -v
    
    yum install rubygems -y

    Good ruby life!

  • 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が楽しみ。

  • ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2 “No such file or directory”)

    ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2 “No such file or directory”)

    はじめに

    以下のようなエラーが発生する事がある

    ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2 "No such file or directory")

     

    原因

    mysqlが立ち上がっていない事が多い。

    CentOS7の場合は起動する

    systemctl start mysql

     

  • DQLで手の届きにくいところをredashでなんとかする

    DQLで手の届きにくいところをredashでなんとかする

    はじめに

    DynamoDBを操作するとき、DQLの操作が難しかったり、GroupByができなかったり。SQLでできないところができなかったりすることが多い。redashを使えばそれもなんとかなったりします。

    Query Resultsを活用

    redashにはQeury Resultsという機能があります(2018/10現在、ベータ版)。これは取得したクエリに対し、SQLを用いてデータを結合させたり、編集することができる機能です。

    一度DQLの結果をredashのQuery Resultsを経由させることで細かいところに手が届くようになります。

    Query Resultを指定し、redashで以下のようなクエリを書きます

    select *, sum(price)
    from query_123
    group by userID

    もちろんDQLのみならず、他の言語も似たようにQuery Resultをかえすことで編集可能です。

     

     

     

     

     

  • mauticをDockerで試したが…

    mauticをDockerで試したが…

    はじめに

    OSS版のMarketoのようなマーケティングツールを使いたいとマーケティングチームから話が上がってきたが、Dockerでインストールを試みたが一筋縄に行かなかった。

    問題

    https://qiita.com/kooooooooooooooooohe/items/43f9452acc539dbd3083

    を参考にdocker-composeで立ち上げ、ブラウザでinstallしようとする。

     

    するとこの画面でSQL Syntaxエラーが発生。

    An error occurred while attempting to install the data: An exception occurred while executing 'CREATE TABLE reports (id INT AUTO_INCREMENT NOT NULL, is_published TINYINT(1) NOT NULL, date_added DATETIME DEFAULT NULL, created_by INT DEFAULT NULL, created_by_user VARCHAR(255) DEFAULT NULL, date_modified DATETIME DEFAULT NULL, modified_by INT DEFAULT NULL, modified_by_user VARCHAR(255) DEFAULT NULL, checked_out DATETIME DEFAULT NULL, checked_out_by INT DEFAULT NULL, checked_out_by_user VARCHAR(255) DEFAULT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, system TINYINT(1) NOT NULL, source VARCHAR(255) NOT NULL, columns LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', filters LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', table_order LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', graphs LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', group_by LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', aggregators LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', settings LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json_array)', is_scheduled TINYINT(1) NOT NULL, schedule_unit VARCHAR(255) DEFAULT NULL, to_address VARCHAR(255) DEFAULT NULL, schedule_day VARCHAR(255) DEFAULT NULL, schedule_month_frequency VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB': SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'system TINYINT(1) NOT NULL, source VARCHAR(255) NOT NULL, columns LONGTEXT DEFAU' at line 1

    ソースを読んで直そうと試みたが、実運用ではなくちょっと試したいという温度感のようだったので、Dockerを諦め、とりあえず最短で使える方法を選んだ。

    解決方法

    Dockerを諦めた。以下のQiitaの記事のコピペでinstallができる。

    https://qiita.com/bezeklik/items/3cd61156367909e31421

     

    https://jp.mautic.org/
  • 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