社内の開発環境とローカルの開発環境の連携方法

どんな時に便利か?

全てのサービスをローカル環境として構築できれば良いのですが
それがあまり現実的ではなく、基本は外部のアプリを利用して
一部アプリだけローカルのアプリを利用したいとい時に便利です。

ローカル環境構成図

PAC(Proxy Access-Control)ファイルを利用して
まずは、ローカルとそれ以外へのアクセスを振り分けています。


ローカルでは、nginxがアクセスを集約しています。
これは、80番や443番ポートを複数のアプリで利用するためで
ホスト名によって各アプリへリクエストの振り分けをしています。


PACファイル

開発環境用に社内プロキシが無い場合は、設定不要です。
JavaScriptの文法で、proxyの設定が書けるファイルです。


Firefoxだと、下記の場所からから設定できます。
オプション > ネットワーク > 接続設定 > 自動プロキシ設定スクリプト URL


proxyを通さない場合(今回はローカル環境)は、"DIRECT"という文字列を返しておきます。
ドメイン名に"hatena.ne.jp"が入る場合は、開発環境用のproxyを通したい。という場合は、
組み込みのshExpMatch関数を使い、host名とマッチングをかけ、PROXYのIPとポートを返します。
セミコロンでproxyもしくは、"DIRECT"をつなげると、順番に接続を試みてくれます。

function FindProxyForURL(url, host) {
  if (isInNet(host, "127.0.0.1", "255.255.255.255")) {
    return "DIRECT";
  } else if (isDevHost(host)) {
    // dev.proxy:9999
    return "PROXY 172.16.0.2:9999";
  }

  // default.proxy:9999
  return "PROXY 172.16.0.3:9999; DIRECT";
}

function isDevHost(host) {
  return shExpMatch(host, "*.hatena.ne.jp");
}

hostsの設定

isInNet(host, "127.0.0.1", "255.255.255.255")でローカル環境として判定をさせるために
/etc/hosts で、ローカルアプリのドメインのIPを、127.0.0.1として登録しておきます。

# local env
127.0.0.1          d.hatena.ne.jp


これで、http://d.hatena.ne.jp/とアクセスした場合は、127.0.0.1:80へのアクセスに
それ以外のはてなドメインは、172.16.0.2:9999のproxy経由でのアクセスが可能になりました。

nginxの設定

ほぼ、デフォルトの設定のままですが、肝はinclude文です。
これで、各アプリに振り分けるようの設定を読み込んでいます。

error_log  /usr/local/var/logs/nginx/error.log;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       params/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /usr/local/var/logs/nginx/access.log  main;

    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;

    keepalive_timeout  65;
    sendfile on;
    gzip on;

    include    /usr/local/var/nginx/conf/extra/backends/*.conf;
}
/usr/local/var/nginx/conf/extra/backends/d-hatena-ne-jp.confの場合

d.hatena.ne.jp用のtomcatアプリが、8080ポートで動いている場合は
以下のような設定になります。

server {
    listen      80;
    server_name d.hatena.ne.jp;

    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}


アプリを増やしたい場合は、hostsファイルへの追記とconfファイルの新規作成で簡単に行えます。

魔術でデプロイ

1文字に飽きたら、詠唱でも

準備

# ホームディレクトリへ移動
function I(){ cd ~/ }
# バックアップ
function Steel(){ cp /usr/local/tomcat/webapps/UBW.war ./UBW.war }
# リリースファイルをSTGサーバから取得
function Unknown(){ scp stg:UBW.war ~/UBW.war }
# tomcat停止
function Nor(){ sudo -u tomcat /usr/local/tomcat/bin/shutdown -force }
# アプリ削除
function Have(){ sudo -u tomcat rm -rf /usr/local/tomcat/webapps/UBW }
# warファイル入れ替え
function Yet,(){ sudo -u tomcat cp ./UBW.war /usr/local/tomcat/webapps/UBW.war }
# tomcat開始
function So(){ sudo -u tomcat /bin/sh usr/local/tomcat/bin/startup.sh }

詠唱

I am the bone of my sword.
Steel is my body, and fire is my blood.
I have created over a thousand blades.
Unknown to Death.
Nor known to Life.
Have withstood pain to create many weapons.
Yet, those hands will never hold anything.
So as I pray, unlimited blade works.

1文字シリーズ

毎日、毎日タイプするコマンドならば、タイプ数を減らすに限るよね。
抜けてるところには、何を入れよう?

1文字alias

alias a="sudo aptitude"
alias b=""
alias c="cat"
alias d="diff"
alias e="echo"
alias f="find . -type f -name"
alias g="grep"
alias h="head"
alias i="/sbin/ifconfig"
alias j=""
alias k=""
alias l="ls -ltrah"
alias m=""
alias n="node -e"
alias o=""
alias p="perl -wnl -e"
alias q=""
alias r="reset"
alias s="sqlplus user_name/password@host[:port][/service_name]"
alias t="tail -f"
alias u=""
alias v="view"
alias x="xargs"
alias y=""
alias z=""

1文字変数

aliasよりさらにネタが無いな

export a="access_`date +%Y%m%d`.log"
export b=""
export c=""
export d="`date +%d`"
export e="error_`date +%Y%m%d`.log"
export f=""
export g=""
export h=""
export i=""
export j=""
export k=""
export l=""
export m="`date +%m`"
export n=""
export o=""
export p=""
export q=""
export r=""
export s=""
export t=""
export u=""
export v=""
export w=""
export x=""
export y="`date +%Y`"
export z=""

node.jsでsocket通信

みんな大好きエコーサーバ

Install websocket server module

 % npm install websocket-server

Server side

httpとwsプロトコルで、返す値を変えられる。
サーバの監視用にする、クライアント側のコードを返すなどの利用方法が考えられる。

var http = require('http');
var ws   = require('websocket-server');

// http
server = http.createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'});    
  res.end("<html><body><h1>It's Works!</h1></body></html>");
});
server.listen(10003);

// WebSocket
var socket = ws.createServer({'server': server});
socket.on('connection', function(conn){
  conn.on('message', function(msg){
    conn.send(msg);
  }); 
  conn.on('close', function(){
    console.log('closed');  
  })  
});

Client side

今回は、サーバのコードとは分けてあるので、httpでアクセスできる場所に適当に置く。
ローカルファイル(file://)だと、セキュリティに引っかかりWebSocket通信ができない。

<script>
  var ws = new WebSocket('ws://127.0.0.1:10003/');
  ws.onopen = function() {
    ws.send('hello world'); 
  }
  ws.onmessage = function(evt) {
    console.log(evt.data);
  }
  ws.onclose = function() {
    console.log('closed');  
  }
</script>

カスタムfindコマンド

alias f="find"


でも、そこそこ便利だけれど
.gitディレクトリを、検索対象外にしたいときに

f . -name .git -prune -print


となり、長いし忘れやすいので

#!/bin/bash
case $# in
1)
  path='.'
  name=$1
  ;;
2)
  path=$1
  name=$2
  ;;
esac

#echo "find \"$path\" -name .git -prune -o -name \"$name\" -print"
find "$path" -name .git -prune -o -name "$name" -print


デフォルトで、.gitディレクトリを抜いてfindさせるシェルスクリプトを作成。
引数が1つの場合は、カレントディレクトリから引数で与えられたnameを検索。
引数が2つの場合は、第一引数のディレクトリから、第二引数のnameを検索。

node.jsを使ってWebページからテキストを取得

jsdomモジュール使うと、http周りの記述しなくて良いから楽だね。
レスポンスヘッダもしくは、メタ要素で指定された文字コード見てUTF-8に変換すれば
もうちょっとましになりそう。

ソース

// getText.js
var jsdom = require('jsdom');

function removeElements(list) {
  for (var i = 0, l = list.length; i < l; i++) {
    var elem = list[i];
    elem.parentNode.removeChild(elem);
  }
}

jsdom.env(process.argv[2], function(errors, window) {
  var body = window.document.body;
  removeElements(body.getElementsByTagName('script'));
  removeElements(body.getElementsByTagName('style'));

  console.log(body.textContent);
});

使い方

% node getText.js http://d.hatena.ne.jp/TakiTake/

JSON形式の設定ファイルに対する変更をリアルタイムに反映する方法

JSON形式の設定ファイルに変更があったら動的に変更を読み込みたいな
ということで、

var fs   = require('fs');
var conf = null;
var filepath = '/path/to/file';

// 設定ファイルを再読み込みする関数
// 第二引数で、同期か非同期かを選べる
function reload(filepath, sync) {
  if (sync) {
    conf = JSON.parse(fs.readFileSync(filepath, 'utf8'));
  } else {
    fs.readFile(filepath, 'utf8', function(err, data) {
      if (err) {
        throw err;
      }

      conf = JSON.parse(data);
    })
  }
}

// 設定ファイルを監視するロジック
// watchFileして、更新日時が新しかったら再読み込みする
fs.watchFile(filepath, function(curr, prev) {
  if (curr.mtime > prev.mtime) {
    reload(filepath);
  }   
}); 


デバッグレベルを切り替えてみたり、上限や下限値を変更したりと
アプリを再起動するまでも無い変更には便利かと