2014年3月31日 星期一

[Linux] 架設 Proftp 與 FTP over TLS 供網頁工讀生上傳資料 @ Ubuntu 12.04

突然收到個命令,要架一檯機器給工讀生用 Orz 雖然幾乎都用 sftp 了,為了讓不熟 Unix 的網頁開發者可以輕鬆上傳資料,最後就是架一台機器,開個 ftp 讓他連進去使用。

流程:

$ sudo apt-get install nginx proftpd
$ sudo vim /etc/proftpd/proftpd.conf
Include /etc/proftpd/tls.conf

$ sudo vim /etc/proftpd/tls.conf
TLSEngine                               on
TLSLog                                  /var/log/proftpd/tls.log
TLSProtocol                             SSLv23
TLSRSACertificateFile                   /etc/ssl/certs/proftpd.crt
TLSRSACertificateKeyFile                /etc/ssl/private/proftpd.key

$ sudo openssl req -x509 -newkey rsa:1024 -keyout /etc/ssl/private/proftpd.key -out /etc/ssl/certs/proftpd.crt -nodes -days 3650

$ sudo vim /etc/proftpd/conf.d/AuthUserFile.conf
DefaultRoot ~
AuthUserFile /etc/proftpd/ftpd.passwd
RequireValidShell off

$ id www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ sudo ftpasswd --file /etc/proftpd/ftpd.passwd --passwd --name webadmin --home /usr/share/nginx/www --shell=/bin/false --uid 33

$ sudo chown -R www-data:www-data /usr/share/nginx/www

$ sudo service proftpd restart

2014年3月28日 星期五

[PHP] foreach $key => $value Passing by Reference Problem

最近開發時,碰到了一個 passing by reference 的問題,追了一陣子才發現 Orz

程式碼:

<?php

$a = array( 'A', 'B', 'C' , 'D' );
print_r($a);
foreach( $a as &$v )
        $v = strtolower($v);
$v = 123123123;
print_r($a);


結果:

Array
(
    [0] => A
    [1] => B
    [2] => C
    [3] => D
)
Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => 123123123
)


可以看到上述的最後一個 element 變成 123123123,而解法就是透過 unset 來處理:

<?php
$a = array( 'A', 'B', 'C' , 'D' );
print_r($a);
foreach( $a as &$v )
        $v = strtolower($v);
unset($v);
$v = 123123123;
print_r($a);


結果:

Array
(
    [0] => A
    [1] => B
    [2] => C
    [3] => D
)
Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => d
)

2014年3月27日 星期四

[Linux] MySQL 資料庫 charset 從 latin1 轉 utf8 筆記 @ Ubuntu 12.04

昨天剛順利移機([Linux] 透過 MySQL Replication 之 Master Slave 切換進行 DB server 移機 @ Ubuntu 12.04),移機後自以為沒事就收工了,結果發現 MySQL DB Server 預設的 charset 是 latin1 而非 utf8 ,接著就開始改設定檔([Linux] MySQL Server & Client characterset 設定 @ Ubuntu 12.04),以為改完沒事...才發現大條的才剛開始。

那就是昨天移機後的資料,都是以 latin1 編碼儲存進去的:

mysql> \s
...
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
...
mysql> SELECT message FROM TABLE;


會有一堆看不懂,改用 latin1 編碼就看懂了

mysql> SET NAMES latin1;
mysql> \s
...
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    latin1
Conn.  characterset:    latin1
...
mysql> SELECT message FROM TABLE;


接著就在想到底該怎樣轉 XD 因為 TABLE 的 charset 是 UTF8 編碼,不管怎樣做 convert, cast 似乎都沒成效,最後找到一些招術,幸運地真的解掉,流程:
  1. 先備份資料庫
  2. 先確認到底是哪幾筆資料有問題,用 mysqldump db_name table_name --where "id>=100 AND id <=200" > data.sql 的方式匯出
  3. 修改 data.sql 的 table 名稱、編碼定義(例如 TABLE_LATIN1、DEFAULT CHARSET=latin1),記得要把 table 改名稱,以免匯入時會 DROP 之前的資料
  4. 匯入 data.sql
  5. 建立一個新 table ,其資料表跟 TABLE_LATIN1 一樣,但 DEFAULT CHARSET=utf8,名為 TABLE_UTF8
  6. 設定 mysql 編碼為 latin1 ,並從 TABLE_LATIN1 取出資料,以 CONVERT(message USING BINARY) 存入 TABLE_UTF8 中
  7. 設定 mysql 編碼為 utf8 ,接著查證 TABLE_UTF8 是否正確,若正確的話,可以把資料從 TABLE_UTF8 更新到為本的 table 中
連續動作:

$ mysqldumo -u account -p db_name table_src_name --where 'id>=100 AND id<=200' > table_src_name.latin1.sql
$ vim table_src_name.latin1.sql
-- ...
DROP TABLE IF EXISTS `table_src_name_latin1`;
CREATE TABLE `table_src_name_latin1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
-- ...

$ mysql -u account -p -D dbname < table_src_name.latin1.sql
$ mysql -u account -p
mysql> use db_name;
mysql> CREATE TABLE `table_src_name_utf8` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
mysql> \s
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
mysql> SET NAMES latin1;
mysql> \s
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    latin1
Conn.  characterset:    latin1
mysql> INSERT INTO table_src_name_utf8 SELECT id, CONVERT(message USING BINARY) FROM table_src_name_latin1;
mysql> SET NAMES utf8;
mysql> SELECT * FROM table_src_name_utf8;
mysql> UPDATE table_src_name as d, table_src_name_utf8 as s SET d.message = s.message WHERE d.id = s.id;

[Linux] MySQL Server & Client characterset 設定 @ Ubuntu 12.04

這...太久沒設定了,記得 n 年前也都會碰到 Orz

首先先查詢現況:

mysql> status;
...
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    utf8
Conn.  characterset:    utf8


接著設定:

$ ls -la /etc/mysql/
total 24
drwxr-xr-x  3 root root 4096 Mar 27 08:58 .
drwxr-xr-x 91 root root 4096 Mar 26 22:41 ..
drwxr-xr-x  2 root root 4096 Mar 27 09:03 conf.d
-rw-------  1 root root  333 Mar 25 16:00 debian.cnf
-rwxr-xr-x  1 root root 1220 Jan 22 05:48 debian-start
-rw-r--r--  1 root root 3638 Mar 27 08:56 my.cnf
$ sudo touch /etc/mysql/conf.d/charset.cnf
[client]
default-character-set=utf8

[mysqld]
character-set-server=utf8
collation-server=utf8_unicode_ci

$ sudo service mysql restart


再查詢一次:

mysql> status;
...
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8


參考資料:

2014年3月26日 星期三

[Linux] 透過 MySQL Replication 之 Master Slave 切換進行 DB server 移機 @ Ubuntu 12.04

事情的緣由是想要進行 DB server 的升級,做法有不少種,而最重要的是要將 down time 降低。由於未來有考慮把 DB server 移到 AWS RDB 上頭,參考 AWS RDB 的 import/export 方式,故選擇從 replication slave 來摸摸。

建立 Replication Slave 的流程:
  1. 將原先的 db1 server 調整成可以成為 master db 的功能
    • $ vim /etc/mysql/my.cnf
      • 拿掉 bind-address 127.0.0.1
      • 指定 server-id = 1
      • 指定 log_bin = /var/log/mysql/mysql-bin.log
      • innodb_flush_log_at_trx_commit=1
      • sync_binlog=1
    • $ sudo service mysql restart
  2. 在 master db 上建立 replication slave 存取權限
    • master:mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%' IDENTIFIED BY 'repl_password';
  3. 在 master db 上匯出資料
    • master:mysql> FLUSH TABLES WITH READ LOCK;
    • master:mysql> SHOW MASTER STATUS;
    • $ mysqldump -u root -p --all-databases --lock-all-tables > all.sql
    • master:mysql> UNLOCK TABLES;
    • 想辦法把 all.sql 弄到 slave db server
  4. 在 db2 server 設定可以成為 slave db 功能
    • $ vim /etc/mysql/my.cnf
      • 拿掉 bind-address 127.0.0.1
      • 指定 server-id = 2
      • 指定 log_bin = /var/log/mysql/mysql-bin.log
    • $ sudo service mysql restart
  5. 在 db server 匯入 master db 的資料
    • 從 master db server 取得 all.sql
    • $ mysql -u root -p < all.sql
  6. 在 slave db 設定 master db 資訊
    • slave:mysql> CHANGE MASTER TO
      MASTER_HOST='MasterDBIP',
      MASTER_USER='repl',
      MASTER_PASSWORD='repl_password',
      MASTER_LOG_FILE='File',
      MASTER_LOG_POS=Position;
    • slave:mysql> START SLAVE;
    • slave:mysql> SHOW MASTER STATUS;
    • slave:mysql> SHOW SLAVE STATUS;
如此一來,就可以看到 Slave 跟 Master 一直保持同步了,可以用一些指令,如 SELECT count(*) FROM TABLE; 來比較看看。

以上就是架設 MySQL Replication Slave 的流程,接下來是進行 DB Server 移機的過程,目的就是將上述的 db1 server 下線,並改用 db2 server 當 master,有點像是把 switch slave server to master 的味道,過程:
  1. 鎖住 db1 server
    • db1:mysql> FLUSH TABLES WITH READ LOCK;
  2. 確認 db2 server 資料有 sync
  3. 在 db2 server 取消 slave 行為
    • db2:mysql> STOP SLAVE;
    • db2:mysql> RESET SLAVE;
  4. 將所有相關的 application 的 db 連線導到 db2 server
透過上述 MySQL Replication 移機的方式,所造成的 down time 會小很多。另外,更好的解法是先處理好 primary key (例如 2N 與 2N+1)的問題,然後先把 application db 切到 db2,過一陣子後再把 db1 shutdown ,就可以避免資料遺失問題。

最後補充一下,在 Master DB Server 新增帳號時,在 Slave DB server 並沒有 sync ,解法除了一開始在設定 Slave 前就把帳號建立好外,另一種解法就是讓 Slave DB 先暫停 Slave 角色,接著新增完帳號再重回 Slave 角色,而新資料又可以從 Master sync 回來。
  1. slave:mysql> STOP SLAVE;
  2. slave:mysql> 新增使用者;
  3. slave:mysql> START SLAVE;
關於新增帳號的問題是在於要把 db2(Slave) 轉成 Master 時,需要更新帳號能夠存取 db2 的權限,然而透過上述 mysqldump -u root -p --all-databases --lock-all-tables 更新方式,db2 裡的資料都是以 db1 的環境去設定的。