2016/05/25

ssh keygen and management

常使用 ssh 連去遠端工作,連遠端時在 windows 我使用 xshell,他有工具可以幫我記帳密,只要點選便可以連結,但在我 ubuntu 的工作環境使用 terminal 的時候,必須打完整的指令 ssh root@chan15.info,然後再輸入密碼,是不會很麻煩啦,但目前專案越來越多跟需另開視窗同一 server 啟用 node.js 服務越來越頻繁的情況,希望可以減少輸入的頻率,最簡單的方式就是用金鑰,可以免輸入密碼,接下來是建構介紹

建立配置金鑰

金鑰的概念是,你在本基上 generate 一組 public key 跟 private key,然後將 public key 置入 remote server 中,你就可以用你的 private key 登入該 remote server,在 Linux 環境有 ssh-keygen 指令可以建立,在 windows 的話,很多 Linux 模擬器例如 cmder,xshell 本身也有 gen 的功能,或者是使用 puttygen,都是在做一樣的事情,這邊我使用 key-gen 來做。

# 先切換到自己帳號的目錄,並建立一個 .ssh 的資料夾
# Windows 的話會是 C:\Users\root\.ssh
# Linux 的話就是 /home/root/.ssh

$ ssh-keygen -t rsa - C "chan15tw@gmail.com"

# 這邊會問你名稱,我要連到自己的 chan15 server,所以我取名為 chan15 然後按下 enter
$ Enter file in which to save the key (/c/Users/kr141101/.ssh/id_rsa):chan15

# 接著問你要不要密碼,可以跳過
$ Enter passphrase (empty for no passphrase):

# 看到這個畫面就表示成功了
$ Your identification has been saved in chan15.
$ Your public key has been saved in chan15.pub.
$ The key fingerprint is:
$ SHA256:IrXB6LfAtW7rFMJlIRjrx+JWaty2nLKNc0jrFM0+9nc chan15tw@gmail.com
$ The key's randomart image is:
$ +---[RSA 2048]----+
$ |  .o. ..         |
$ |   o+.  . E      |
$ |   +.o . .       |
$ |  . + =o         |
$ |   . Bo.S        |
$ |  o.B o.         |
$ |  .BoB.o . .     |
$ |  o+O.*o+.o .    |
$ |   +BO.++o..     |
$ +----[SHA256]-----+

# 資料夾會有 chan15 以及 chan15.pub,兩個檔案,看一下 chan15.pub 有什麼
$ cat chan15.pub
$ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC91YXEW08rAIzVc3xS5owRsIBizSlZJvkaJcddZEGP3XrQk+3fC0lDpMdXR2UfoIMdFQbMHETcsfeuEgivjUzqppA7QRcxV/uCbrfRtrz38nR9Nu/celqUPTEF2k1RjyK19acsTOOaX/hS8KFBw91HnENAUUpFtmjMXEsgPern9YV3DcgFBxKrgtg13lnzDoSVVYdmqqqxZG6OggJE6r27lF9BIfz8fdcuGBMbNx5DvivEu/h6Hejj0ax9N5DAqJhSCASSkd+dRYUur7OMptvM05UDgl/zAuRWlT9jO38yMvMBFztcyAPUGeVANV6nG0Kbr2GQe9T5Hi1wtf1y/yCX chan15tw@gmail.com

網路上很多將 pub key 加到 remote server 的方法,像是用 cat,用 scp 等等的,但最妥當沒風險的,就是你直接打開 remote server,然後找到 /home/root/.ssh/authorized_keys,將 pub key 加到裡面去,如果你沒有這個檔案,還可以自行創立,然後將權限設定為 600。

連線管理與配置

接下來我們就可以用指令連線到 remote server

$ ssh -i chan15 root@chan15.info

這個指令意思就是使用 chan15 這個 private key 去連線到 remote,而 remote 那邊會去找 root 根目錄 authorized_keys 有沒有對應的 key,有的話就會 pass,但樣指令還是太長,我們可以透過設定 config 將指令縮短,打開 local /hoem/root/.ssh/,建立一個叫 config 的檔案,內容如下:

Host chan15
    HostName chan15.info
    User root
    IdentityFile /home/root/.ssh/chan15

這樣設定好以後只要執行 ssh chan15 就可以連線了,是不是很方便。

參考文章

https://blog.longwin.com.tw/2005/12/ssh_keygen_no_passwd/
http://www.cyberciti.biz/faq/create-ssh-config-file-on-linux-unix/

2016/05/05

vim auto install

我之前有把 vim 的安裝過程寫成 script,放在 dropbox 上,但前天遇到 dropbox 連不上而必須建立環境的窘境,所以還是 blog 放一篇比較穩當。

vim_centos_dispatch.sh

sudo localedef -i zh_TW -c -f UTF-8 zh_TW.UTF-8
sudo yum -y install vim
sudo yum -y install git
sudo mkdir -p ~/.vim/autoload
sudo mkdir -p ~/.vim/bundle
sudo curl -o ~/.vimrc https://dl.dropboxusercontent.com/u/15447570/vim/config.txt
sudo curl -o ~/.vim/bundle/install.sh https://dl.dropboxusercontent.com/u/15447570/vim/install.sh
sudo curl -o ~/.vim/bundle/update.sh https://dl.dropboxusercontent.com/u/15447570/vim/update.sh
sudo curl -o ~/.vim/autoload/pathogen.vim https://raw.githubusercontent.com/tpope/vim-pathogen/master/autoload/pathogen.vim
cd ~/.vim/bundle
sudo sh install.sh
cd -

vim_ubuntu_dispatch.sh

sudo locale-gen --lang zh_TW.UTF-8
sudo aptitude -y install vim
sudo aptitude -y install git
sudo mkdir -p ~/.vim/autoload
sudo mkdir -p ~/.vim/bundle
sudo curl -o ~/.vimrc https://dl.dropboxusercontent.com/u/15447570/vim/config.txt
sudo curl -o ~/.vim/bundle/install.sh https://dl.dropboxusercontent.com/u/15447570/vim/install.sh
sudo curl -o ~/.vim/bundle/update.sh https://dl.dropboxusercontent.com/u/15447570/vim/update.sh
sudo curl -o ~/.vim/autoload/pathogen.vim https://raw.githubusercontent.com/tpope/vim-pathogen/master/autoload/pathogen.vim
cd ~/.vim/bundle
sudo sh install.sh
cd -

.vimrc

execute pathogen#infect()
syntax on
filetype plugin indent on
:filetype plugin on
language messages zh_TW.utf-8
autocmd FileType php set omnifunc=phpcomplete#CompletePHP
colorscheme jellybeans

set autoindent
set backspace=indent,eol,start
set clipboard=unnamed
set cursorline
set encoding=utf-8
set expandtab
set fileencoding=utf-8
set fileencodings=utf-8,big5,euc-jp,gbk,euc-kr,utf-bom,iso8859-1
set foldcolumn=1
set guifont=Consolas:h12
set laststatus=2
set nobackup
set noundofile
set nowritebackup
set number
set shellslash
set shiftwidth=4
set t_Co=256
set tabstop=4
set tenc=utf-8
set winheight=5
set winminheight=5
set winheight=999

let g:EasyMotion_leader_key = '<Leader>'
let g:ctrlp_working_path_mode = 0
let g:syntastic_auto_jump = 1
let g:syntastic_php_checkers = ['php']
let g:airline_powerline_fonts = 1
let g:vim_php_refactoring_auto_validate_sg = 1
let g:vim_php_refactoring_auto_validate_rename = 1
let g:vim_php_refactoring_auto_validate_visibility = 1
let g:phpunit_bin = '/root/.composer/vendor/bin/phpunit'

:map <C-t> :tabnew<CR>
:map <C-l> :tabnext<CR>
:map <C-h> :tabprevious<CR>
:map <F2> :NERDTreeToggle<CR>
:map <F3> :Flisttoggle<CR>

_vimrc

set nocompatible
source $VIMRUNTIME/vimrc_example.vim
source $VIMRUNTIME/mswin.vim
behave mswin

execute pathogen#infect()
syntax on
filetype plugin indent on
:filetype plugin on
source $VIMRUNTIME/delmenu.vim
language messages zh_TW.utf-8
autocmd FileType php set omnifunc=phpcomplete#CompletePHP
colorscheme jellybeans

set autoindent
set cursorline
set encoding=utf-8
set expandtab
set fileencoding=utf-8
set fileencodings=utf-8,big5,euc-jp,gbk,euc-kr,utf-bom,iso8859-1
set foldcolumn=1
set guifont=Consolas:h12
set laststatus=2
set noundofile
set nowritebackup
set nobackup
set number
set shellslash
set shiftwidth=4
set tabstop=4
set tenc=utf-8
set winheight=5
set winminheight=5
set winheight=999

let g:EasyMotion_leader_key = '<Leader>'
let g:airline_powerline_fonts = 1
let g:ctrlp_working_path_mode = 0
let g:syntastic_auto_jump = 1
let g:syntastic_php_checkers = ['php']
let g:syntastic_javascript_checkers = ['jshint']
let g:vim_php_refactoring_auto_validate_sg = 1
let g:vim_php_refactoring_auto_validate_rename = 1
let g:vim_php_refactoring_auto_validate_visibility = 1
let g:phpunit_bin = 'C:/Users/chan/AppData/Roaming/Composer/vendor/bin/phpunit'

:map <C-t> :tabnew<CR>
:map <C-l> :tabnext<CR>
:map <C-h> :tabprevious<CR>
:map <F2> :NERDTreeToggle <CR>
:map <F3> :Flisttoggle<CR>

:nunmap <C-a>

set diffexpr=MyDiff()
function MyDiff()
  let opt = '-a --binary '
  if &diffopt =~ 'icase' | let opt = opt . '-i ' | endif
  if &diffopt =~ 'iwhite' | let opt = opt . '-b ' | endif
  let arg1 = v:fname_in
  if arg1 =~ ' ' | let arg1 = '"' . arg1 . '"' | endif
  let arg2 = v:fname_new
  if arg2 =~ ' ' | let arg2 = '"' . arg2 . '"' | endif
  let arg3 = v:fname_out
  if arg3 =~ ' ' | let arg3 = '"' . arg3 . '"' | endif
  if $VIMRUNTIME =~ ' '
    if &sh =~ '\<cmd'
      if empty(&shellxquote)
        let l:shxq_sav = ''
        set shellxquote&
      endif
      let cmd = '"' . $VIMRUNTIME . '\diff"'
    else
      let cmd = substitute($VIMRUNTIME, ' ', '" ', '') . '\diff"'
    endif
  else
    let cmd = $VIMRUNTIME . '\diff'
  endif
  silent execute '!' . cmd . ' ' . opt . arg1 . ' ' . arg2 . ' > ' . arg3
  if exists('l:shxq_sav')
    let &shellxquote=l:shxq_sav
  endif
endfunction

install.sh

git clone https://github.com/Lokaltog/vim-easymotion.git
git clone https://github.com/MarcWeber/vim-addon-mw-utils.git
git clone https://github.com/adoy/vim-php-refactoring-toolbox.git
git clone https://github.com/bling/vim-airline.git
git clone https://github.com/bronson/vim-trailing-whitespace.git
git clone https://github.com/c9s/lftp-sync.vim.git
git clone https://github.com/garbas/vim-snipmate.git
git clone https://github.com/godlygeek/tabular.git
git clone https://github.com/gregsexton/MatchTag.git
git clone https://github.com/honza/vim-snippets.git
git clone https://github.com/jiangmiao/auto-pairs
git clone https://github.com/kien/ctrlp.vim.git
git clone https://github.com/mattn/emmet-vim.git
git clone https://github.com/nanotech/jellybeans.vim.git
git clone https://github.com/pangloss/vim-javascript.git
git clone https://github.com/rhysd/conflict-marker.vim.git
git clone https://github.com/scrooloose/nerdtree.git
git clone https://github.com/scrooloose/syntastic.git
git clone https://github.com/stephpy/vim-php-cs-fixer.git
git clone https://github.com/tmhedberg/matchit.git
git clone https://github.com/tomtom/tcomment_vim.git
git clone https://github.com/tomtom/tlib_vim.git
git clone https://github.com/tpope/vim-surround.git
git clone https://github.com/vim-scripts/AutoComplPop.git
git clone https://github.com/vim-scripts/IndexedSearch.git
git clone https://github.com/vim-scripts/SearchComplete.git
git clone https://github.com/vim-scripts/functionlist.vim.git
git clone https://github.com/vim-scripts/restore_view.vim.git
git clone https://github.com/vim-scripts/smarty-syntax.git
git clone https://github.com/c9s/phpunit.vim.git
git clone https://github.com/captbaritone/better-indent-support-for-php-with-html.git

update.sh

for item in *
do
    if [ -d "$item" ]; then
        echo processing $item
        cd $item
        git pull
        echo finish $item
        printf "\n------------------------------------\n\n"
        cd ..
    fi
done

2016/03/15

Node.js VS PHP PDO Insert MySQL

最近在考慮將公司某些專案的排程改到 node.js 上面,主要是程式本體的 driver 跟 MySQL 之間的效能,先講結論,如果有用 MVC 的人不要用內建的 ORM 跑大資料寫入的排程,雖然 ORM 慢是大家都知道的事情,畢竟他本來就是圖方便的工具,但沒想到大量資料寫入他可以慢到將近一倍,本測試執行 node.js、PHP PDO,一次跑 3000 個 insert 各跑十次,以下是寫法以及結果。

環境說明
  1. PHP 5.6.15
  2. MySQL Ver 15.1 Distrib 10.1.9-MariaDB
  3. node.js 5.8.0
  4. Windows 7 64bit
  5. 8GB Ram
套件說明
  1. npm-mysql
  2. drealecs/time-benchmark

程式寫法

node.js
console.time('add');
console.log('start');

var mysql = require('mysql');
var connection = mysql.createConnection({
    host     : 'localhost',
    user     : '',
    password : '',
    database : 'test',
});
var length = process.argv[2] || 100;
var counter = 1;

connection.connect();

var add = (counter) => {
    connection.query('INSERT INTO `users` SET ?', {name: 'test'}, (err, rows, fields) => {
        if (err) {
            console.log(err);
        }

        if (counter < length) {
            add(++counter);
        } else {
            console.log('all done');
            console.timeEnd('add');
            connection.end();
        }
    });
};

add(counter);
PHP PDO
<?php

include 'vendor/autoload.php';

echo 'start'.PHP_EOL;

use TimeBenchmark\Stopwatch;

$length = isset($argv[1]) ? $argv[1] : 100;
$counter = 1;
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8';
$db = new PDO($dsn, '');

$stopwatch = Stopwatch::create();
$stopwatch->start();

$add = function($counter) use ($length, $db, $stopwatch, &$add) {
    $sql = "INSERT INTO `users`(`name`) VALUES(:name)";
    $stmt = $db->prepare($sql);
    $stmt->bindValue(':name', 'test');
    $stmt->execute();

    if ($counter < $length) {
        $add(++$counter);
    } else {
        $stopwatch->stop();
        echo $stopwatch->getElapsedMilliseconds().'ms'.PHP_EOL;
    }
};

$add($counter);

測試結果

node.js
  1. 75500.335ms
  2. 76234.479ms
  3. 60025.145ms
  4. 68833.407ms
  5. 71067.471ms
  6. 63238.021ms
  7. 63908.226ms
  8. 57938.820ms
  9. 66872.004ms
  10. 66817.116ms
PHP PDO
  1. 63311.331ms
  2. 66825.682ms
  3. 67424.741ms
  4. 67320.732ms
  5. 62134.213ms
  6. 65586.558ms
  7. 67117.711ms
  8. 54427.443ms
  9. 66046.604ms
  10. 67741.773ms

結論

其實數據沒有差很多,但 node.js 比較會飄,所以還是觀察一下吧。

2016/03/11

$.Deferred VS Promise

Promise 是非常好用的一個特性,主要用來處理 async 的工作,在 jQuery 上很早就有 deferred 的實作,而 JavaScript ES6 也把這個功能加了進來,而且寫法非常清爽,但 ES6 要普及應該還要一段時間,jQuery 的 deferred 比較沒有相容性的問題,在這邊列一下同樣功能怎樣在雙邊實現。

單一運用

$.Deferred

let response = () => {
    return $.Deferred(dfd => {
        setTimeout(() => {
            dfd.resolve('done');
        }, 2000)
    }).promise();
}

$.when(response()).then(response => {
    console.log(response);
});

// or

let response = () => {
    let dfd = $.Deferred();

    setTimeout(() => {
        dfd.resolve('done');
    }, 2000)

    return dfd.promise();
}

$.when(response()).then(response => {
    console.log(response);
});

Promise

let response = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('done');
        }, 2000);
    });
}

response().then(response => {
    console.log(response);
});

等待多重結果

$.Deferred

let results = [];

[2000, 3000, 4000].forEach(timer => {
    results.push($.Deferred(dfd => {
        setTimeout(() => {
            console.log('single done!');
            dfd.resolve();
        }, timer);
    }));
});

$.when.apply($, results).then(() => {
    console.log('all done');
});

promise

let promises = [];

[2000, 3000, 4000].forEach(timer => {
    promises.push(new Promise(resolve => {
        setTimeout(() => {
            console.log('single promise done!');
            resolve();
        }, timer);
    }));
});

Promise.all(promises).then(() => {
    console.log('promise all done');
});

2016/03/09

apt and yum

purpose apt aptitude yum
update repo list apt-get update aptitude update none
install package apt-get install package_name aptitude install package_name yum install package_name
intall from file dpkg -i package.deb yum install package.rpm
rpm -ivh package.rpm
remove package apt-get remove package_name aptitude remove package_name yum remove package_name
check outdated package apt-get -s upgrade aptitude -s upgrade yum check-update
check single outdated package apt-get -s upgrade packge_name
apt-cache policy package_name
aptitude -s upgrade package_name yum check-update package_name
update package apt-get upgrade package_name aptitude package_name yum update package_name
rpm -Uvh package_name
search package apt-cache search package_name aptitude search package_name yum search package_name
list installed package dpkg -l yum list installed
rpm -qa
show package information apt-cache show package_name aptitude show package_name yum info package_name
remove packages from the local cache directory apt-get clean aptitude clean yum clean all
Reference

ubuntu.com

2016/02/27

工作工具

前幾天公司電腦掛點,由於程式碼都有上版控,文件類現在都用雲端,軟體大多用簡單免費的工具,所以重建工作環境沒有花多少時間,但為了下次可以更快,把一些基本必備的東西列一下。

Editor
Development Environment
FTP
Terminal

Xshell

Database

HeidiSQL

Other Tool

2016/01/27

webpack

將 JavaScript 模組化打包減少 http request 是目前幾乎必備的技能,之前使用 requirejs 來做這一塊,後來玩了一下 webpack 後發現回不去了,以此篇做個設定與使用筆記。

webpack.config.js
var webpack = require('webpack');
var sourcePath = __dirname + '/assets/js/src';

module.exports = {
    entry: sourcePath + '/entry.js',
    output: {
        path: __dirname + '/assets/js/build',
        filename: 'bundle.js'
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery"
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ],
    module: {
        loaders: [
            {test: /\.js$/, exclude: /node_modules/, loader: "babel", query: {presets: ['es2015']}}
        ],
    },
    resolve: {
        alias: {
            init: __dirname + '/assets/js/app/init.js'
        }
    }
};

web.config.js 是 webpack 的設定檔,將常用的東西規範好之後,執行 webpack 這個指令他便會照你的設定檔作動,entry 指的是來源檔案,如果是多個檔案要 compiler 的話可以使用物件寫法:

    entry: {
        'a': sourcePath + '/a.js',
        'b': sourcePath + '/b.js'
    },
    output: {
        path: __dirname + '/assets/js/build',
        filename: '[name].js'
    },

這樣的格式他便會照你的 key name gen 一個一模一樣的檔案在 output 資料夾,plugin 的部分我用 npm 安裝了 jQuery,並用 plugin 宣告讓他可以全域使用,不用每次都 require,壓縮的部分使用 UglifyJS 2,基本上如果不使用 plugin 的話在 command line 下 webpack --optimize-minimize 會有一樣的效果,記得如果有下壓縮編譯的話速度會慢一些,開發階段可以先關起來,module 的部分使用了 babel-loader,撰寫的程式碼都會經過 babel 的編譯,確定你的 ES6 語法可以跨瀏覽器使用,alias 的部分就是讓你少打字而已,另外如果在 command line 使用 webpack -w 便會執行監看模式,如果 entry 的檔案有所變動的話就會 auto compiler,但我喜歡自己下指令,因為我還是會稍微看一下編譯結果,接下來開一個 webpack 的資料夾,來安裝相關的模組。

npm init
npm i babel-loader babel-core babel-preset-es2015 babel-preset-react --save-dev
npm i uglify-js --save-dev
npm i jquery@1.12 --save
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack test</title>
    <script src="assets/js/build/bundle.js"></script>
</head>
<body>

</body>
</html>
assets/js/app/init.js
module.exports = {
    hello: (name = 'Chan') => {
        return `Hello world, I am ${name}`;
    }
};
assets/js/src/entry.js
var init = require('init');

$(function() {
    console.log(init.hello());
});

在 command line 執行 webpack,開啟網頁就會得到,Hello world, I am Chan,我的 VIM 有使用 JSHint 檢查原始碼,他偵測到 ES6 會報錯,只要在根目錄放置設定檔即可。

.jshintrc
{
    "esnext": true
}