awkでデータの絞り込み

やりたかったこと

  • 本番RDSから割と大きめのデータをエクスポート
  • 負荷がかからないローカルDBでそのデータをこねくる
  • 割とデータ量が多いのである程度はawkなどを駆使してデータを絞る










実践
[必要なデータを本番DBからexport]

mysql -u hoge_user -P 9000 -h 127.0.0.1 -D hoge_db -pxxxxx N --batch -e "SELECT * FROM hoges PARTITION (p201704);" > hoges.tsv

[uniqu_data作成]

sudo cat  hoges.tsv | cut -f1-16 | tr " " _ | gawk '{volume[sprintf("%s_%s", $3,$4)]=$6};END{for (key in volume) {print sprintf("%s_%s", key, volume[key])}}' > uniqu_log.tsv

[bulk insert用のimport file作成]

cat uniqu_log.text | gawk 'BEGIN{ FS="_"; OFS="," } {print (NR) == 1 ? sprintf("insert into user_summary values(\"%s\", %s, %s)", $1, $2, $3 ) : sprintf(",(\"%s\", %s, %s)", $1, $2, $3 )  } END{print ";"}' > insert.sql

[LocalDBにimport]

mysql -u hoge_user -phoge -D hoge < insert.sql

[error]

ERROR 2006 (HY000) at line 1: MySQL server has gone away

[予想原因]
クライアントからサーバに送ることができるパケットの最大数が小さい

[対策]
max_allowed_packet を増やす

でもできなかった......

他にもタイムアウト系伸ばしたけど、無理


なのでbulkを分割して対応することに

cat uniqu_log.text | gawk 'BEGIN{ FS="_"; OFS="," } {print (NR%100) == 1 ? (NR) == 1 ? sprintf("insert into user_summary values(\"%s\", %s, %s)", $1, $2, $3 ) : sprintf(";insert into user_summary values(\"%s\", %s, %s)", $1, $2, $3 ) : sprintf(",(\"%s\", %s, %s)", $1, $2, $3 )  } END{print ";"}' > insert.sql

[再度import]

mysql -u hoge_user -phoge -D hoge < insert.sql











実践
取得したいデータをもう少しはっきり定義していたら手前のシェルでもう少しデータ加工できそう。










今更ながGitHubのカンバン機能「Project」を使って見る

GitHub Projects

http://dev.classmethod.jp/tool/git/github-projects/

目的

プロジェクトで、ClientとServer間のコミュニケーションの効率化/作業の見える化をしたかった

概要

ディレクターとデザイナーとのコミュニケーションは別ツールを使用。 Projectは基本プロジェクトのエンジニア間での連携で使用するつもりです。

GitHub Projectは大きく分け3つの概念があります。

  • Project
  • Card
  • Note





Projectについて

Projectは名前のとおりです。発足したプロジェクト名を記載してください Projectの単位ですが2通りあります。

  • organization単位(Organizationプランのみ)
  • repository単位

どちらでもいいと思いますが、 organization単位で作成しておくと、一目でどういうプロジェクトが 動いているのかを確認できるので私はこちらをお勧めします。

Cardについて

CardはProjectに1対多で紐づく概念です。 役割や概念の違いで分けるといいと思います。

Noteについて

NoteはCardに1対多で紐づく概念です。 役割や概念に関係するTopicsを記載するものと考えていいと思います。

またNoteはRepositoryのIssueの情報と紐づけることができます。





ルールExample

こういうツール系はある程度ルールを決めておくと運用しやすいですね

Projectのルール

  • 命名規則
    • {事業部名}_{サービス名}_{Version}
    • もちろん英語

Cardのルール

  • 画面機能構成がわかるカードを1つ
  • 大枠の仕様がわかるカードを1つ
  • あとは役割別でCardを作成(ex: Android, iOS, API etc)

Noteのルール

  • 命名規則
    • [画面機能構成名][新規 or 修正 or 要望]-{簡潔なタイトル}
  • 着手中のものはIssueと紐付けて自分をassigneesとして登録
  • 要望のものはIssueと紐付けて要望に対応してほしい人をassigneesとして登録
  • CloseしたIssueのNoteはその機能がリリースされてからCardから削除する

Issueの運用ルール

  • 基本的に直接Issueは登録しない(NoteをIssueに紐づける)
  • タイトルはNoteの命名規則に従う(自然と統一されるはず)
  • ボールを持っている人がassignees
  • labelはNoteのルールの[新規 or 修正 or 要望]に従う
    • 新規 ラベルなし
    • 修正 bugs
    • 要望 help_wanted

Commitルール

  • CommitのタイトルはIssueのタイトル名#nでコミット(Issueのタイトルコピペ)
  • 詳細を記載するっ場合は改行して記載





良いなと感じているところ

プロジェクトを開くと開発で必要な情報がどこにあるのか一目でわかるかも? 誰が今何をやっているのかわかるので、自分のタスクの優先順位づけがしやすくなる?

足りないと感じているところ

期日を設定しにくい(他のツールと組み合わせたりして改善?) まだ運用の準備段階なので何とも言えないですが、

運用し始めてからも改善していけたらと思います。

気軽にRDSの負荷分散

RDSの負荷分散にnginxのstream機能を使った話。

やりたいイメージ
f:id:sonedayuya:20170211160017p:plain


前提

  • RDSのマスターとレプリカで負荷分散させたい
  • ELBでやろうとしたけどEC2にしか対応していない
  • HAproxy MySQLproxyちゃんと構築する時間がない


nginx再ビルド

  • nginxの標準で対応しているロードバランサーはhttp通信のみ対応。
  • tcpでも対応させたい場合はstreamモジュールが必要
  • Nginx v1.9.0以上が対応している機能

TCP Load Balancing with NGINX Plus R6 and NGINX 1.9.0

http://nginx.org/download/nginx-1.11.9.tar.gz
./configure --with-stream
make
make install


設定ファイル
nginx.conf

helthcheckの設定とかtimeout設定したい場合は、

色々オプションあるので適当に。

以下最低限

stream {
    error_log /var/log/nginx/mysql_proxy.log;

    upstream mysql {
        zone backends 64k;
        server rds-master:3306 fail_timeout=30s;
        server rds-rep:3306 fail_timeout=30s;
    }

    server {
        listen 3360;
        proxy_pass mysql;
        proxy_timeout 10s;
        proxy_connect_timeout 1s;
    }
}




確認

  • 接続できればOK
mysql -h 127.0.0.1 --port 3360 --protocol tcp -u hoge --pass
  • ちゃんと分散されてるかはMasterとReplicaにそれぞれ接続しコネクション数を確認
 SELECT * FROM information_schema.PROCESSLIST;
  • あとはwightの調整とか分散具合に合わせて調整すればOK
  • writeの場合はmasterに書き込みにいくようApplicatino側を修正。
  • 書き込めたらOK


fluentdによるlogの集約とS3への保存

業務でログ収集をやることになったのでその時のメモ

やりたいイメージ

f:id:sonedayuya:20170210231910p:plain

  • A B C Dのnginxのログはltsv形式
  • A B C DのログをEに集約
  • Eに集約したログを大dailyでzipしてs3に保存

各サーバーのHDのスペックは以下

A B C D - 8G
E - 100G

まずは各サーバーの設定とtd-agentのインストール

[公式ドキュメント]
http://docs.fluentd.org/v0.12/articles/quickstart

[インストール前に]

/etc/security/limits.conf

root soft nofile 65536
root hard nofile 65536
* soft nofile 65536
* hard nofile 65536

/etc/sysctl.conf

net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10240    65535


[インストール]

curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh

[設定]

コレクター A B C D

<source>
  @type tail
  tag nginx.log
  path /usr/local/nginx/logs/access.log
  pos_file /var/log/td-agent/access.log.pos
  format ltsv
  time_key time
</source>

<match nginx.log>
  type forward
  buffer_path /var/log/td-agent/buffer/access.*.buffer
  buffer_chunk_limit 8M
  buffer_queue_limit 256
  queued_chunk_flush_interval 20s
  try_flush_interval 1
  flush_interval 60s
  retry_wait 30s
  retry_limit 5
  num_threads 2
  <server>
    name nginx_log
    host xx.xxx.xxx.xxx
    port 24224
  </server>
</match>

集約サーバー E

<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>

<match nginx.log>
  type copy
  #<store>
  #  type file
  #  format ltsv
  #  path /var/log/fluentd/nginx_log
  #  buffer_chunk_limit 30g
  #  time_slice_format %Y%m%d
  #  time_slice_wait 1m
  #  compress gzip
  #  flush_at_shutdown true
  #</store>

  <store>
    @type s3
    path log/nginx/%Y/%m/%d/ #S3の保存ディレクトリ
    format ltsv #logの形式
    aws_key_id XXXXXXXXXXXXXXX
    aws_sec_key XXXXXXXXXXXXXXX
    s3_bucket hoge_bucket
    s3_region hoge_region
    acl public-read
    buffer_path /var/log/fluentd/access.*.log
    time_slice_format %d
    buffer_type file
    time_slice_wait 10m
    buffer_chunk_limit 30g #デイリーのログの容量に合わせて変更
  </store>
</match>


※ログのローテトでハマりました。buffer_chunk_limitが肝
参考





[監視用スクリプト]*cronで動かす

参考

www.slideshare.net

  • td-agentのプロセスが死んだ場合再起動&slackへの通知
#!/bin/sh
#slack通知
function post_to_slack () {
  SLACK_MESSAGE="*$1*"
  SLACK_URL=hoge
  case "$2" in
    RECOVER)
      SLACK_ICON=':slack:'
      ;;
    DOWN)
      SLACK_ICON=':warning:'
      ;;
    *)
      SLACK_ICON=':slack:'
      ;;
  esac

  curl -X POST --data "payload={\"text\": \"<!channel> \n ${SLACK_ICON} ${SLACK_MESSAGE}\"}" ${SLACK_URL}
}


function restart_td_agent () {
  sudo /etc/init.d/td-agent restart
}

function stop_td_agent () {
  sudo /etc/init.d/td-agent stop
}

#test用
#stop_td_agent

SERVER_NAME="manga-OauthChargeUser-api-prod_0"

# process数が2以下ならalert&t再起動
PROCESS_COUNT=`ps w -C ruby -C td-agent --no-heading | grep td-agent | wc -l`
if  [ ${PROCESS_COUNT} != 2 ] ; then
  DATE=`date`
  STATUS="DOWN"
  MESSAGE="[${SERVER_NAME}][td-agent] \`\`\`プロセスがダウンしました\`\`\`"
  post_to_slack "${MESSAGE}" "${STATUS}"

  restart_td_agent

  NOT_RECOVER=true
  while $NOT_RECOVER
  do
    PROCESS_COUNT=`ps w -C ruby -C td-agent --no-heading | grep td-agent | wc -l`
    if  [ ${PROCESS_COUNT} == 2 ] ; then
      STATUS="RECOVER"
      MESSAGE="[${SERVER_NAME}][td-agent] \`\`\`プロセスが復帰しました\`\`\`"
      NOT_RECOVER=false
      post_to_slack "${MESSAGE}" "${STATUS}"
    fi
  done
fi

exit 0

nginxの設定ファイルのメモ

openresty-1.11.2.2 Linux version 4.4.30-32.54.amzn1.x86_64

APIで高速化するために設定ファイルをごにょごにょ。 一旦こんな感じになった。 もっとこうしたらいいとか、 ここどうなってるのとかあればツッコミが欲しい。

他にもカーネルパラメーターとかちょっといじった。

どんなことしたか、別の機会に書こう!

nginx.conf

user  nginx;
worker_processes  auto;
worker_rlimit_nofile 65536;
pid /var/run/nginx.pid;

events {
    worker_connections 65536;
    #worker_connections 1024;
    multi_accept on;
    #accept_mutex off;
    use epoll;
}


http {
    include       mime.types;

    server_tokens off;
    client_header_timeout  3m;
    client_body_timeout    3m;
    send_timeout           3m;

    sendfile on;
    sendfile_max_chunk 512k;

    tcp_nopush on;
    tcp_nodelay on;

    access_log off;
    error_log off;

    open_file_cache max=1000 inactive=20s;
    open_file_cache_valid    30s;
    open_file_cache_min_uses 5;
    open_file_cache_errors   off;

    keepalive_requests 1000;
    keepalive_timeout 120;

    proxy_headers_hash_max_size 1024;
    proxy_headers_hash_bucket_size 256;

    log_format  main_ext  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for" '
                          '"$host" sn="$server_name" '
                          'rt=$request_time '
                          'ua="$upstream_addr" us="$upstream_status" '
                          'ut="$upstream_response_time" ul="$upstream_response_length" '
                          'cs=$upstream_cache_status' ;

    include /usr/local/openresty/nginx/conf/site-enable/*.conf;
}

site-enable/hoge.conf

upstream hoge {
    server unix:/var/run/circus/hoge.sock;
    keepalive 1000;
}

server {
    listen 80 default_server;

    server_name hoge.com;

    gzip on;
    gzip_http_version 1.0;
    gzip_comp_level  6;
    gzip_types       application/json;
    gzip_vary on;
    gzip_proxied any;
    gzip_buffers 16 8k;

    access_log logs/hoge.access.log main_ext buffer=32k;
    error_log  logs/hoge.error.log warn;

    # health check
    location = /hoge {
       access_log off;
       empty_gif;
       break;
    }

    location / {
      proxy_pass http://hoge;
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-Host $host;
      proxy_set_header Connection "";
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header X-Forwarded-Host $host;
    }

}

vagrant cloud

自前でvagrant boxファイル作ったメモ下書きにあると思ってたら消しちゃってた。あーあ、って思ってたら、vagrantがアップグレードされてて、vagrant cloudっていう便利なものがでてきていて、
なんだか感動しちゃった。と同時に焦りも。

https://vagrantcloud.com/

時間なんとか作ってただ使うだけに留まらないようにする。

virtualbox vagrant chef で仮想環境作りたい

vvirtualbox4.3.12
vagrant1.3.5
インストール済み

Ubuntu 14.04
http://www.ubuntulinux.jp/News/ubuntu1404

新規で仮想マシン作成
Ubuntuの64bit

作成後、設定→ストレージから
CD/DVDドライブにダウンロードしてきたubuntu14.04のisoファイルセット

設定→システムから起動順序変更

ubuntuのインストル設定手順に従いインストール。
ネットワークはNATにした。
ブリッジにしようと思ったがPPPoeでの設定になるので、IP固定できない。

ssh
ポートワードの設定にsshを追加。
ターミナルからssh接続できるようにする。

ubuntusshデーモンインストール
sudo apt-get install ssh

【VirtualboxGuestAdditionインストール】

Cのコンパイルに必要なライブラリー
sudo apt-get install build-essential

VirtualboxGuestAddition インストールに必要らしい
sudo apt-get install module-assistant
sudo m-a prepare

sudo mount /dev/cdrom /mnt

cd /mnt
sudo ./VboxLinunxAdditions.sh


参考
http://d.hatena.ne.jp/kumamidori/20130512/p1

ssh公開鍵認証】
ssh、公開鍵で認証できるようにする。
公開鍵作成済み。

ゲストOS側に.sshディレクトリ作成。
sudo mkdir .ssh

vagrantユーザーで認証するから
sudo chown vagrant:vagrant .ssh
.sshフォルダの権限700じゃないとssh接続できない
sudo chmod 700 .ssh

公開キーを転送(2222でゲストOSの22に転送する設定にしてある)
scp -P 2222 ~/.ssh/id_rsa.pub vagrant@localhost:~/.ssh/authorized_keys

vagrant box作成 vagrant up】

VMを元にボックスファイル作成
vagrant package --base ubuntu64(ホスト名)

・boxファイルから仮想環境(ubuntu14.04VagrantBox)作成
vagrant box add ubuntu14.04VagrantBox package.box

・仮想環境初期化
vagrant init ubuntu14.04VagrantBox

・仮想環境起動
vagrant up

ssh公開鍵認証なのでVagrantfileに秘密キーの場所設定してあげる

Vagrant::Config.run do |config|
    config.vm.box = "ubuntu14.04VagrantBox"
    config.ssh.private_key_path = "~/.ssh/id_rsa"
end


config.ssh.private_key_path = "~/.ssh/id_rsa"
~/.ssh/id_rsa → 公開鍵の場所

vagrant up うまく行けば一旦終わり。
もちょっとカスタマイズしよ。

redisインストール

ただのメモ

現状の安定板のgipファイル
wget http://download.redis.io/releases/redis-2.8.9.tar.gz

解凍
tar xvzf redis-2.8.9.tar.gz

できたフォルダに入って

make
make test
make install

tcl8.5以上必要って怒られたから
yum install tclでインストールした。

【Rails】WEBrickについて

RackでWebサーバーたてた。
よくわからないから、コードを追っていたら前からわからなかったWEBrickについてなんかわかったような。
めも

まずはコード張っとく。

require 'webrick'
require 'erb'

document_root = '/var/www/html/rails/rubyApp/app/'

server = WEBrick::HTTPServer.new({
  :DocumentRoot => document_root,
  :BindAddress => '0.0.0.0',
  :Port => 10080
})


server.mount_proc("/") { |req, res|

  path = File.join(document_root,*req.path.split("/"))
  path += ".utf-8" if /\.html\.[a-z][a-z]$/ =~ path 

  File.open(path){|file|
  	res.body = (ERB.new(File.read(path)).result(binding))
  }
  
  # 拡張子とContent-Typeの対応表
  content_types = {
    ".html" => "text/html", ".txt" => "text/plain",
    ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg",
    ".gif" => "image/gif", ".png" => "image/png", 
    ".mp3" => "audio/mpeg", ".mid"   => "audio/midi", 
    ".css"   => "text/css", ".xhtml" => "application/xhtml+html",
    ".svg"   => "image/svg+xml"  
        
  }
  
  # filenameの拡張子を見てContent-Typeを設定
    content_type = content_types[File.extname(path)]
    
     #Content-Typeが見つからなかったらtext/htmlを設定
    if content_type==nil
      content_type = "text/html"
    end
         
    res["Content-Type"] = content_type
 }

['INT', 'TERM'].each {|signal|
  Signal.trap(signal){ server.shutdown }
}

server.start
<||


まず
werbrickとerb(テンプレートエンジン読み込む)

>|ruby|
require 'webrick'
require 'erb'

WEBrick::HTTPServer.newしてあげる。
/.rbenv/versions/2.1.0/lib/ruby/2.1.0/webrick.rb
のinitialize method で初期化される。

document_root = '/var/www/html/rails/rubyApp/app/'

server = WEBrick::HTTPServer.new({
  :DocumentRoot => document_root,
  :BindAddress => '0.0.0.0',
  :Port => 10080
})


webrickのinitialize method確認
/.rbenv/versions/2.1.0/lib/ruby/2.1.0/webrick.rb

def initialize(config={}, default=Config::HTTP)
      super(config, default)
      @http_version = HTTPVersion::convert(@config[:HTTPVersion])

      @mount_tab = MountTable.new
      if @config[:DocumentRoot]
        mount("/", HTTPServlet::FileHandler, @config[:DocumentRoot],
              @config[:DocumentRootOptions])
      end

      unless @config[:AccessLog]
        @config[:AccessLog] = [
          [ $stderr, AccessLog::COMMON_LOG_FORMAT ],
          [ $stderr, AccessLog::REFERER_LOG_FORMAT ]
        ]
      end

      @virtual_hosts = Array.new
end

いっぱいnewしてる。
ここで書くときりがないので、

super(config, default)

コレより前に

require 'webrick/server'

書いてあるのと

class HTTPServer < ::WEBrick::GenericServer
<||

::WEBrick::GenericServer継承してるから
中身確認
.rbenv/versions/2.1.0/lib/ruby/2.1.0/webrick/server.rb

>|ruby|
def initialize(config={}, default=Config::General)
      @config = default.dup.update(config)
      @status = :Stop
      @config[:Logger] ||= Log::new
      @logger = @config[:Logger]

      @tokens = SizedQueue.new(@config[:MaxClients])
      @config[:MaxClients].times{ @tokens.push(nil) }

      webrickv = WEBrick::VERSION
      rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
      @logger.info("WEBrick #{webrickv}")
      @logger.info("ruby #{rubyv}")

      @listeners = []
      unless @config[:DoNotListen]
        if @config[:Listen]
          warn(":Listen option is deprecated; use GenericServer#listen")
        end
        listen(@config[:BindAddress], @config[:Port])
        if @config[:Port] == 0
          @config[:Port] = @listeners[0].addr[1]
        end
      end
end

またいろいろ書いてる。。。
まぁログの設定とかか。
listen methodだけみとこ。多分ソケット作ってる。

def listen(address, port)
    @listeners += Utils::create_listeners(address, port, @logger)
end

また違うファイルから読んでるー。。
一応確認

.rbenv/versions/2.1.0/lib/ruby/2.1.0/webrick/utils.rb

>|ruby|
##
# Creates TCP server sockets bound to +address+:+port+ and returns them.
#
# It will create IPV4 and IPV6 sockets on all interfaces.


def create_listeners(address, port, logger=nil)
unless port
raise ArgumentError, "must specify port"
end
sockets = Socket.tcp_server_sockets(address, port)
sockets = sockets.map {|s|
s.autoclose = false
TCPServer.for_fd(s.fileno)
}
return sockets
end
|

Dir.chdirについて

rails server コマンド の ソース をおっていたら、Dir.chdirという関数があった。

  Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))

現在のパス上に「config.ru」がなければ、current path を引数の所に変更してくれる。

まぁなんて便利な関数!