2015年12月31日 星期四

使用付費版騰訊企業郵箱以及從 Microsoft Exchange server 轉移心得

騰訊企業郵箱-付費版功能

由於公司有超過一半的人在中國,過去一直請 IT 人員維護 Microsoft Exchange Mail server,而 2016 年有了新計劃,公司服務雲端化!其中 Mail service 是一塊非常令人頭痛的問題,但責任扛起來就不重,只好硬著頭皮衝了!原先規劃十一月進行,因故延至十二月,而真正執行是十二月中,原先把測試期間拉到一個月,但發現新舊帳號在一起造成同事們不少困擾,所以加速處理,在 2015/12/31 大家還忙著修 bug、業務忙著關帳、老闆趕著出新版軟體的當下,終於完成轉換。

原本心中的首選是想要用 Gmail for Work,但實在是有太多人在中國生活,存取太不靠譜,所以進而挑中國第一大咖的服務:騰訊企業郵箱,簡單的概念:QQ 這麼大咖,不太會倒,它倒了全中國的郵箱也倒光了!

騰訊企業郵箱-免費版

起初,公司已經有在使用騰訊企業郵箱免費版好一陣子了,免費版跟付費版差只差在進階功能,例如郵件稽核、管理這類服務,若只是單純有個 Webmail 收發肯定夠用的。打個岔一下,有興趣可以試試免費使用,預設應該有 50~100 個帳號,並且最多可以申請到 200 個免費帳號的額度。 而付費版的差別,包括可以一次管理多個網域(最多五個),以及一卡車的增值功能。

那談談如何從 Microsoft Exchange Server 移轉過去,這可真不是普通的蛋疼。首先,要是用騰訊企業郵箱時,騰訊企業郵箱會先檢驗某個 domain 是不是您擁有,既然跟 mail 相關,最直觀就是 MX Record 的設定,但對於已經營運的郵箱是根本不能停的,為了這個我跟客服吵了很久 XD 整體上沒有驗證的話,所建立的帳號是完全不能登入的!為了轉移勢必需要先讓公司同仁登入,也因此下了決定,該衝了!最大的傷害就是 MX Record 切換到騰訊後,若有人要寄信給公司同仁,信件會跑去騰訊企業郵箱(在這之前當然要先完成帳號的建立,不然騰訊企業郵箱會以帳號不存在,不給寄而退信)。所幸老闆們很支持也接受這個風險,就衝啦!我先請 IT 人員調出公司收郵件最少的時段,接著在轉換前幾天先把 MX Record 的 ttl 設小,最後就快速轉換,做完騰訊郵箱驗證,再轉回舊的。

原先我打好的如意算盤:騰訊企業郵箱認證成功,成員可以登入試用,而舊信箱(exchange server)還是可以工作,真是完美啊!

但沒一個小時我就發現問題了:所有委託騰訊企業郵箱管理的信箱(例如abcd.com),該公司的成員要寄信到敝公司時,走的是騰訊企業郵箱的 SMTP server,而寄信路徑最佳化的結果,就是被騰訊企業郵箱收走!

這是一個滿嚴重的問題,代表信件部分會被騰訊企業郵箱收走,而乖乖看 MX Record 會被舊 exchange server 收走,也是這個關鍵因素,讓我把測試期從一個月縮短至兩個禮拜(原先想要一個禮拜,但因為有其他問題而延後)。騰訊企業郵箱真的很大咖,不少公司、新創都採用他,再加上 qq.com 也是他的!所有 @qq.com 寄信來問問題、橋接業務的,也完完全全被騰訊企業郵箱收走,因為我們在那驗證了 MX Record,騰訊企業郵箱會認定這 domain 由他接手處理。

在認清環境後,開始不斷號召同仁盡快試用,接著開始碰到奇奇怪怪的問題:
  • outlook 20010 以下,會碰到檢視憑證的問題?
  • 同仁都習慣用 outlook ,那聯絡人該怎樣同步?
  • 公司有高規格單位,防火牆包得緊緊的,怎樣用騰訊企業郵箱?
其中最難處理的是防火牆問題,曾經也有一度要自架 mail server,最後透過管理機制討論再討論,解套了!這個影響是:採用各大 mail service 時,其 smtp/pop/imap server 一定都是用認得那幾組,無論騰訊企業郵箱、Office 365還是 GMail 等,將造成想要管理同仁寄信的安全機制出現了漏洞,例如某同仁只要申請個 a123.com 後,把郵箱也交給騰訊企業郵箱代管,那 a123.com 跟敝公司的 smtp server 基本上是同一組的,完全無法限制同仁外寄信件的。

所以花了將近一週是在做溝通,包含提出 mail reply 架構等(類似多架一台 smtp server,而帳號認證可以走騰訊企業郵箱 pop protocol),所幸最後解套,大家認同有這個風險 XD 我就省去架設及多管理一台機器。但一週就過去了 Orz 接下來就是趕緊處理剩下兩個問題

首先是 outlook 檢視憑證問題,單純只是 outlook bug 而已,解法就是試著更新 outlook 吧!例如 outlook 2007 就裝一下 outlook 2007 sp3。這是因為騰訊企業郵箱在多網域上只用了一組憑證(pop.exmail.qq.com, imap.exmail.qq.com, smtp.exmail.qq.com, hwpop.exmail.qq.com, hwimap.exmail.qq.com, hwsmtp.exmail.qq.com),而 outlook 不支援而已。

接著處理聯絡人同步部分,只需把公用聯絡人打開,同仁寄信時就有自動搜尋補齊信箱的功能,而騰訊企業郵箱有提供 outlook 助手,透過 outlook 助手會自動從騰訊企業郵箱下載聯絡人清單並匯入 outlook 中,也就是 outlook 通訊錄可以繼續用,經測試 outlook 2007 必須更新至 sp3 才能完整使用!若使用 outlook 2013 的使用者,可以試試 activesync 協定,也能同步聯絡人

以上算是完成九成的任務了!剩下的則是佛心來的客服服務,負責做 mail server 轉移,不知不覺就會扛上了管理 mail server 責任。

整個移轉的簡易 SOP:
  1. 請 IT 人員會出聯絡人清單
  2. 撰寫程式將聯絡人清單轉成 CSV 可匯入騰訊企業郵箱(過程包括要製作亂數密碼,但密碼格式有錯時,批次匯入不會告知,有同仁登入不進去時,記得幫他改密碼)
  3. 請 IT 人員給予一天哪個時段收信量最少,將進行 MX Record 的快速轉換,用以認證網域擁有者,完成認證後同仁才能登入 webmail 試用
  4. 請同仁儘速登入使用,並告知會有部份郵件被騰訊企業郵箱收走,在測試期間請新舊信箱都要收信
  5. 協助同仁使用,包含解決 outlook 常見問題,或是請他們直接用騰訊維護的 Foxmail
  6. 時機一到時,直接把 MX Record 切換至騰訊企業郵箱,並設置 TXT Record - spf 的部分,降低騰訊企業郵箱寄出信被誤判成垃圾信
  7. 可以嘗試把舊的 smtp server 關閉,或是限制成員發送的能力(例如信件大於1kb無法寄出),進而半強迫同仁轉用騰訊企業郵箱 smtp server
最後,聊點題外話,其實兩年前也差點用 Mail service 這個題目創業,在學生時代我也開發過 mail app 的,這其中的辛酸血淚說都說不完,而讓我想起這件事是因為 Foxmail !我翻了wiki跟百度,才知道這一開始也是一人開發的,就像 openwebmail 吧 :P 負責開發 Foxmail 的是張小龍,一位 1969 年次的高手,釋出三年後就被公司以 1200萬買走,但最猛的是後來被騰訊收購,以及張小龍後續要開發了 QQMail 以及炙手可熱得微信,是的,他是微信創辦人,更是騰訊副總裁。此外,跟生活在中國的朋友閒聊,更能體會騰訊企業郵箱的恐怖,姑且不論費用的便宜,其實還滿多搬的出檯面上的使用者,例如不少學校採用,如北京大學、深圳大學、上海也有,一個單位開的帳號破萬也都很正常,更別說....還有100k的。不禁回想起當年想拿 mail service 創業,還想說該怎樣打入中國生態這件事,真的...還是要先把市調做好 XD 以及中國生態圈中微信佔了很大很大的一部份,而騰訊企業郵箱又可以跟微信無縫結合,這股氣勢真的非常強。

經過這次深度接觸騰訊企業郵箱的經驗,中國的領域真的很威,若不是生活在當下的人,真的很難有所突破,有興趣的,大概可以參考這篇:PTT China_travel [心得] 來到中國你會需要什麼App

其他參考資料:

2015年12月29日 星期二

[Microsoft] Outlook 2007 - 檢視憑證 / 目標主體名稱不正確

Outlook檢視憑證

最近幫公司移轉 Mail service,碰到憑證問題,詢問了相關客服才得知,這個算是 outlook 自己的 bug,主因是目前採用的 Mail Service 是使用一張憑證服務多個網域,而 Outlook 2007 本身不支援這個用法,導致每次收下或送信要跟 Mail server 連線就會彈跳出這是警告視窗,且無法設定默認,永遠重開 outlook 就會重新問。

所幸還有解法,那就是安裝更新檔即可 XD

Outlook 2007 就安裝 Outlook 2007 SP3  - https://www.microsoft.com/zh-cn/download/details.aspx?id=27838,真是很久沒碰 Microsoft Office 軟體了。

2015年12月27日 星期日

[戲劇] 韓劇 - 紳士的品格


前陣子一直用它當作測資,當程式寫完後,就順便把他看完了 XD 好久沒有跳脫生活好好欣賞一部戲了!好笑的是...我都熬夜看,大概七天看完吧,甚至面試新人時,新人還說:請問...你們很常加班嗎?你看起來好像很累 囧

我記得碩班時代還滿自豪的跟同學說:ㄟ,我用兩倍數看完某某戲劇、動漫。這部戲應該算是最近第一次沒用加速看完 XD 有興趣可以在 LiTV.tv - 紳士的品格 上觀看正版戲劇!

簡短的分享,還滿享受劇中四位熟男的感情,可以在任何時候拋下手邊的事物、甚至犧牲睡眠來聚在一起,只為了朋友有難!想想,周邊若真的要說的話,大概就像參加大學同學的婚宴時,提早到的反而被抓去當伴男闖關 XD 大概有類似的 fu 吧。

這部戲大家對它褒貶皆有,有人會覺得女主角做作,我反而覺得女主角咬嘴唇演戲還滿自然 XD 且因為演員都頗有年紀的,可能不見得會被時下年輕人接受。這齣是 2012 年的韓劇,除了享受四男的友情外,則是劇中的配樂,都讓我想買原聲帶了!有興趣可以翻翻 PTT 鄉民整理的清單:[LIVE] 《紳士的品格》Ep20 最終回。阿,還有,其實我一開始認錯女主角,以為是河智苑 :P 覺得他們長得真像啊!其實他倆都是挺厲害的演員,有興趣可以翻翻 wiki 簡介:


在台灣...在 35~40 歲之間的演員,不知代表人物會是誰?我大概只會想到舒淇吧。不知下次再好好看戲劇會是何時了。這齣也會讓我想起...日劇,不能結婚的男人。

2015年12月25日 星期五

[Linux] 解決 PHP CodeIgniter 在 Nginx 環境中 $_REQUEST 永遠為空

這塊只是 Nginx 的 try_files 的地方未補好引起的,我有點忘了當初是在哪邊找到的 XD 剛看一下 Nginx 的官網的確也是這樣寫:

https://www.nginx.com/resources/wiki/start/topics/recipes/codeigniter/
location / {
# Check if a file or directory index file exists, else route it to index.php.
try_files $uri $uri/ /index.php;
}


這樣的環境下,在 PHP CodeIgniter 裡頭,每次想要從 $_REQUEST 取資料都會為空

<?php
print_r($_REQUEST);


解法就是將 $query_string 資訊帶入即可:

location / {
# Check if a file or directory index file exists, else route it to index.php.
try_files $uri $uri/ /index.php?$query_string;
}

2015年11月27日 星期五

DevOps 筆記 - 使用 Jenkins、Git、RPM、Ansible、Slack 建置持續整合架構(Continuous integration)

今天終於端出一盤比較好吃的菜了,用料有點雜筆記一下吧!之前已經用 Jenkins + Git + Slack 可以做到持續發布跟通知開發者了,但面對 AWS Auto Scaling 議題還是頗抖的,因為建立 AMI (Amazon Machine Image) 後,機器的狀態被保留在某一刻,這時服務量一大自動開啟機器時,就會出現服務更新的需求。原先同事採用 Puppet 方案,但仍不適合處理 Auto Scaling 的問題,因為 Puppet 需要比較強的關係式(Server/Client認證),雖然硬做也是 ok 的,但要一直維護 Server/Client 架構還是頗累,所幸有了 Ansible 的出現!這提供比較分散式的管理架構,Server 跟 Client 都是透過 keypair + ssh remote command 溝通的。

粗略配置作法:
  • Git
    • 主要特色還是 branch 的規劃,跟公司文化有關
      • master:正式網站的狀態
      • alpha:內部最新網站狀態
      • develop:最新程式碼
    • 開發者都是從 develop branch 開發服務,當有版本要釋出時,merge 進 alpha branch 後,發佈到 alpha site 測試,妥當後再把 alpha branch merge 進 master branch
      • 概念上有點從 develop -> alpha -> master 角度
      • 若有 hotfix 時,從 master 產生 branch 修正後,在 merge 回 alpha 跟 develop
      • 在 alpha site 測試出問題時,不斷地在 alpha branch 處理,到最後才 merge 回 develop branch,以及發佈到 master branch
    • 透過 Jenkins 每一次發版時,會下 git tag 標記狀態
      • develop-version-number
      • alpha-version-number
      • production-version-number
    • 相關資料:A successful Git branching model
  • Jenkins 
    • 新增 Slack Notification Plugin/Version Number Plug-In
    • 權限設定,為了不要暴露太多專案給開發者,做了簡易的權限管控
      • 設定全域安全性
        • 安全性領域:採用 Jenkins 內建使用者資料庫 且不允許註冊
        • 授權:採用專案型矩陣授權策略
          • 每一個 task 都可以自訂可以觀看的使用者
    • 管理方面,新增工作分成 build 跟 deploy 兩種
      • build 就是把 source code 包成 RPM ,方便作業系統管理和除錯追蹤,另外,對於開發者測試機的準備也有幫助
        • 取得最新程式碼後,透過 RPMBuild 包裝起來,擺至指定位置
        • 每一次 build 都會下 git tag 標記版本狀態,方便之後除錯
        • 若封裝過程可以接觸到 git 資訊,則把 source code 最後 3 筆 commit log 拉出來製作版本資訊
          • git log -3 --pretty=format:"{\"commit\":\"%H\",\"update\":\"%ad\",\"author\":\"%an\"}"
      • deploy 就是透過 ansible 進行發佈,因此也可以把 ansible 操作用 git 管理
        • 發布過程中,第一步先到指定位置找出最新的 RPM files
        • 第二部透過 AWS EC2 API 得知待更新機器列表
        • 透過 ssh remote command 進行機器更新
        • 盡量把所有動作濃縮至 Ansible command,而透過 Jenkins Web UI 撰寫幾個指令就好 
      • 工作通知
        • 每一次 build 或 deploy 都可以透過 slack plugin 將更新訊息發佈至指定頻道通知相關開發者
      • 採用輪詢 SCM 方式,由 Jenkins 主動確認程式碼狀態,以此決定是否出 build ,不採用 git hook 等被動方式進行工作任務
        • 無法規範開發者 git push 行為,擔心 push 太多造成大量 build event
      • build 完畢後,可以設立相關專案,自動進行 deploy 任務
  • AWS
    • 透過 AIM 建立一個只有 AmazonEC2ReadOnlyAccess 權限的帳號
    • 對於需被管理的機器,都透過 Tag 標記管理
      • 使用 AWS EC2 API 來動態查詢指定的機器,以此取得機器列表,例如指定 ELB 裡所有的機器
    • 關於 Auto Scaling
      • 由於機器 IP 非固定,在不同 data center 溝通容易踩到 Security Group 限制,建議還是把服務以 data center 切開
        • 例如有美西跟日本時,若 db server 開在美西,那需要存取此 db server 的機器就擺在美西就好,這樣 Security Group 比較好做(類似限制 LAN 即可)
  • Ansible
    • 將 RPM 複製到機器上安裝,在 Ubuntu server 可偷懶用 alien :P
    • 依照腳本,進行機器的環境配置,如 limits、web server 等
    • 埋入一個 boot script ,當開機時透過 ssh remote command 向 deploy server 要求更新自己
以上使用的相關資源:
  • RPM 打包目錄專用
    • https://github.com/changyy/rpm-builder
    • 若打包目錄有 .git 時,會抓出最後幾筆 commit log 塞進 version.json 檔案內,可協助往後的偵錯
    • 由於 RPMBuild 很吃環境變數,面對 Jenkins 同時出 build 會衝到共用資源的部分,在此已經透過修改 $HOME 目錄來避開共用資源的問題,因此不用再擔心 Jenkins 一次只能出一個 build
  • AWS EC2 機器查詢
    • http://docs.ansible.com/ansible/intro_dynamic_inventory.html#example-aws-ec2-external-inventory-script
    • Jenkins 建置範例(script),只要修改 yml 檔案名稱即可:
      • develop site
        • export ANSIBLE_HOST_KEY_CHECKING=False
          export EC2_INI_PATH=$WORKSPACE/dynamic-inventory-ec2.ini
          export AWS_ACCESS_KEY_ID=XXX
          export AWS_SECRET_ACCESS_KEY=OOO
          export DEPLOY_TARGET=site-develop.yml

          ansible-playbook $WORKSPACE/$DEPLOY_TARGET -i $WORKSPACE/bin/ec2.py --private-key=$HOME/.ssh/ansible-deploy.pem
      • alpha site
        • export ANSIBLE_HOST_KEY_CHECKING=False
          export EC2_INI_PATH=$WORKSPACE/dynamic-inventory-ec2.ini
          export AWS_ACCESS_KEY_ID=XXX
          export AWS_SECRET_ACCESS_KEY=OOO
          export DEPLOY_TARGET=site-alpha.yml

          ansible-playbook $WORKSPACE/$DEPLOY_TARGET -i $WORKSPACE/bin/ec2.py --private-key=$HOME/.ssh/ansible-deploy.pem
      • production site
        • export ANSIBLE_HOST_KEY_CHECKING=False
          export EC2_INI_PATH=$WORKSPACE/dynamic-inventory-ec2.ini
          export AWS_ACCESS_KEY_ID=XXX
          export AWS_SECRET_ACCESS_KEY=OOO
          export DEPLOY_TARGET=site-production.yml

          ansible-playbook $WORKSPACE/$DEPLOY_TARGET -i $WORKSPACE/bin/ec2.py --private-key=$HOME/.ssh/ansible-deploy.pem
  • Ansible 找尋最新 package files
  • Ansible 設置自動要求更新
    • $ ansible-galaxy install changyy.self-update 
    • https://github.com/changyy/ansible-role-self-update
    • 比較雜亂一點,原理:
      • 機器部署時,建立一個 auto update script (負責通報 deploy server 更新指定 host),將此 script 埋入開機執行區( /etc/rc.local )
        • 當機器啟動時執行該 script ,把部署此機器該有的資訊以 ssh remote command 丟給 deploy server 
      • 該 ssh remote command 包含的動作
        • 登入 deploy server 並切換至 ansible workspace
        • 透過 dynamic inventory 建立動態 host 資訊
        • 執行 ansble-playbook 批次指令
      • Deploy server workspace 範例:
        • $ cat bin/echo.sh
          #!/bin/bash
          echo "{\"$HOST\":$DATA}"

          $ HOST=MySite DATA=[\"SERVER_IP\"] bash bin/echo.sh
          {"MySite":["SERVER_IP"]}

          $ cd ansible-deploy-dir && \
          ANSIBLE_HOST_KEY_CHECKING=false \
          HOST=MySite \
          DATA=[\"SERVER_IP\"] \
          ansible-playbook MySite.yml \
          -i bin/echo.sh \
          --private-key=mysite-login-key.pem