2017/04/30

Linux 與 Mac 使用者如何透過終端機 ssh 登入 GCP

以下是 Linux 以及 Mac 使用者透過終端機 ssh 登入 GCP instance 的方法:

  1. 使用終端機建立 ssh keys,其中 [USERNAME] 輸入你登入 gcp instance 時要用的 linux username。gcp 機器沒這帳號時會他會自動建立。

    輸入指令,然後按兩次確定:
    ssh-keygen -t rsa -f ~/.ssh/my-ssh-key -C [USERNAME]
    
    範例:
    ssh-keygen -t rsa -f ~/.ssh/mayer-gcp-key -C mayer
  2. 改 private key 權限

    chmod 400 ~/.ssh/mayer-gcp-key
  3. 使用 cat 看 public key,然後把 cat 出來的內容完全複製:

    cat ~/.ssh/mayer-gcp-key.pub
    
    ssh-rsa xxxxxxxxxxxxxxxxxxxxAQABAAABAQDcO7K0z/etx95xbVG10ttgmuwnlyRuDu0LgoTU0X/RGsIhm3kaQxxxxxxxxxxxxxxxxxxxUWOC0L9RAO0+bQI36DY2XiubrbwzrfrkMk6o40u1lCVg5H/cFaTlkyyyyyyyyyyyyyyyyyyyyyyyyyORbQ8E8bBgSV/AlJqV/Qnm0z4Rp3ezAngI77I9GtbEgKxa7pHtRFf/YA99frvLYFVLh94sWDVijx6oKEp/emteL5ham6hZmPVe181Q9+hU8wfUNlAKDE8ZdJ+QC3hkSzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz7AL+Xp6u3epX1Hc+NgeEhtqnJ6ijmP mayer
  4. 到中繼資料(metadata)頁面,把複製出來的公鑰加到 SSH 金鑰上。如何進入中繼資料設定頁面請參考官網 - Setting instance metadata

  5. 接著就可以透過終端機使用 ssh 登入,ip 請到 VM 執行個體,看機器的 External IP。

    ssh -i ~/.ssh/mayer-gcp-key [USERNAME]@x.x.x.x
  6. 可以把上述指令加到 ssh config 內加快登入流程:

    vi ~/.ssh/config
    
    Host gcp1
      HostName x.x.x.x
      User mayer
      IdentityFile ~/.ssh/mayer-gcp-key
    
    設定完之後就能直接使用 hostname 登入
    ssh gcp1
  7. 登入 GCP 機器之後,使用者身份會是你的 [USERNAME]。要將使用者切換成 root,使用以下指令:

    sudo -s

    參考:

Connecting to Linux Instances

談談 docker network-alias

在使用 docker run 指令啟動 container 時,有一個與 network 有關的 flag,為 network-alias,可以為 container 在網路拓墣取一個別名,讓其他 container 透過這個別名找到該 container。一開始看到時,不知道能拿來做什麼,想說我透過 container name 就能直接找到我要的 container 了,為何還要在另外命名?但是最近在弄新的產品需求時,卻剛好有需要用到 network-alias 的應用場景出現,因此想做個記錄。

network-alias 效果

container 在自定義的 network 底下時,可以使用 container name 找到目標對象,若對象有定義 network-alias 時,則也可以使用 network-alias 找到目標。比如說以下範例,建立兩個 container,名為 foo1 與 foo2,將他們加到 foonet 這個自定義的 network 底下,然後 foo2 取 network-alias 為 bar2。使用 docer exec 指令進入到 foo1 的 bash 後,試著執行 ping foo2 與 ping bar2,都是能找到相同的 container。

docker network create --driver bridge foonet
docker run -d --name foo1 --network foonet tomcat:8.5.11-jre8
docker run -d --name foo2 --network foonet --network-alias bar2 tomcat:8.5.11-jre8
docker exec -it foo1 /bin/bash

root@3769d24e6700:/usr/local/tomcat# ping foo2 -c 3
PING foo2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: icmp_seq=0 ttl=64 time=0.172 ms
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.106 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.141 ms
--- foo2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.106/0.140/0.172/0.027 ms

root@3769d24e6700:/usr/local/tomcat# ping bar2 -c 3
PING bar2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: icmp_seq=0 ttl=64 time=0.093 ms
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.097 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.099 ms
--- bar2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.093/0.096/0.099/0.000 ms
root@3769d24e6700:/usr/local/tomcat#

兩個 container 擁有相同 network-alias

接下來是在官方文件看到的,可以多個 container 使用相同的 network-alias。對這真實的運作情況有點好奇,因此建立了模擬狀況來測試。

底下範例建立了三個 container,分別為 foobar1、foobar2 與 foobar3。其中 foobar1 要做為 ping 的發起端,而 foobar2 與 foobar3 為測試對象,他們使用相同的 network-alias,名為 app。

首先依序把三個 container 建立起來,然後進入 foobar1 的 bash,接著開始 ping network-alias 的測試。在測試的時候發現在 ping network-alias 時,不管 ping 多少次,只會找到 foobar2,也就是第一個使用該 network-alias 的 container,當第一個 foobar2 停掉時,才能 ping 到第二個使用該 network-alias 的 container,也就是 foobar3。

docker run -d --name foobar1 --network foonet tomcat:8.5.11-jre8
docker run -d --name foobar2 --network foonet --network-alias app tomcat:8.5.11-jre8
docker run -d --name foobar3 --network foonet --network-alias app tomcat:8.5.11-jre8
docker exec -it foobar1 /bin/bash
root@05c0ad2a04b2:/usr/local/tomcat# ping app
PING app (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: icmp_seq=0 ttl=64 time=0.117 ms
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.126 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.129 ms
root@05c0ad2a04b2:/usr/local/tomcat# exit
exit
mayertekiMacBook-Air:~ mayer$ docker stop foobar2
foobar2
mayertekiMacBook-Air:~ mayer$ docker exec -it foobar1 /bin/bash
root@05c0ad2a04b2:/usr/local/tomcat# ping app
PING app (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: icmp_seq=0 ttl=64 time=0.126 ms
64 bytes from 172.18.0.4: icmp_seq=1 ttl=64 time=0.138 ms

也就是說可以使用 network-alias 建立一個至多個備援的 container,提供服務的 container 掛了後相同 network-alias 的 container 會繼續提供服務,不過實際應用上並不建議這樣使用,若是需要備援服務應該使用如 Docker Swarm 或是 kubernetes 之類的叢集管理工具比較恰當。

應用場景

知道了 network-alias 的效果之後,就會開始思考,這要應用在哪?這可能可以從一個 App Server 與 DB container 的互動來切入,首先我們建立一個 Web 應用程式,要將他放在 tomcat 上,這個 Web 應用程式需要與 DB 互動,因此我們需要一個 MySQL,此時用 Docker 來建立整個環境會如下圖所示:

在 Web 應用程式內,要連到 DB,通常會把他寫成屬性檔,用來描述 DB 的位址以及相關登入資訊,有可能如下:

miniweb.driverClassName = com.mysql.jdbc.Driver
miniweb.url = jdbc:mysql://db1:3306/miniweb?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false
miniweb.username = foo
miniweb.password = bar
miniweb.initialSize = 1
miniweb.maxActive = 50
miniweb.maxIdle = 10
miniweb.maxWait = 180000
miniweb.validationQuery = SELECT CURRENT_TIMESTAMP

在開發與測試階段時,這樣是不會有問題的,但是在程式要上線時,這樣做就會有問題了。通常上線前會先把程式放到 staging 環境先經過測試後,才會上到 production 環境。此時依然可以使用 docker 來為我們建立這兩個環境所需要使用的 container,但是會有個問題,由於 container name 不能重複,因此應用程式內的屬性檔,必須要有兩份才能正常運作,比如說目前環境如下圖:

則你在 ap1 上應用程式的設定檔,DB 的 url 要如下:

miniweb.url = jdbc:mysql://db1:3306/miniweb?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false

而在 ap2 上應用程式的 DB url 要如下:

miniweb.url = jdbc:mysql://db2:3306/miniweb?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false

維護兩份設定檔文件會是一件很麻煩的事情,你可以在打包程式時,手動改這屬性檔,或是使用打包工具搭配一些指令來處理這問題,不管怎樣都需要額外花費一些功夫來處理這狀況。

此時就可以使用 docker network 搭配 network-alias 來處理這問題了,我們使用 docker network 建立兩個獨立的 network 環境,然後各跑各的 Web 應用環境,在 DB containers 上使用 network-alias 把他們都命名為 db,這樣一來,我們的應用程式只要透過 db 這個 network-alias,就能正確的找到相對應的 DB containers。整個環境的概念圖如下:

在這個環境下,屬性檔只要填上 network-alias 即可,如下:

miniweb.url = jdbc:mysql://db:3306/miniweb?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false

2017/04/24

Actor Model

Actor Model 是一個 concurrent computing 的計算模型,Actor 是這個模型的基本計算單元,當 actor 透過一個自己的獨立 mailbox 接收到一個訊息,他能夠根據這個訊息,做出決策、進行運算、產生其他訊息、決定如何回應。

Actor model 是 1973 年由Carl Hewitt、Peter Bishop及Richard Steiger的論文: A Universal Modular Actor Formalism for Artificial Intelligence 提出。

在當時由於機器多屬於單核心且網路速度較慢,不同的 actor 甚至是遠端的 actor 之間發送訊息,存在著明顯的資料延遲時間。但由於 Moore's Law 的趨勢,目前的 CPU 已經是多核心的時代,網路速度也已經不可同日而語,以往的資料鎖定 (lock) 方法已經無法滿足於現今處理並行運算的硬體設備。Actor Model 簡化了 data lock 的實作的複雜性,去掉了 deadlock 的問題,在一個運算環境中以大量的actor 小單元完成並行運算的工作。


Everything's an Actor

相對於物件導向的 Everything is an Object,Actor Model 認為 Everything is an Actor。actors 是一個並行的運算系統,能夠處理下列的工作,這是 actor 的基本原則。

  1. 傳送有限數量的訊息給其他 actors
  2. 建立有限數量的 new actors
  3. 指定接受到下一個訊息時的行為

所有訊息的收送都是非同步的,透過 mail address 傳送資料。每一個 actor 都有一個 mailbox ,以 FIFO 方式暫存接收到的訊息。

Actor Model 的爸爸 Carl Hewitt 在 Lang.NEXT 2012 針對 acotr model 有一段討論會形式的說明 Hewitt, Meijer and Szyperski: The Actor Model (everything you wanted to know...),整個影片的過程就像是聊天一樣,3 個 actors 在互相溝通與討論 actor model。

實作 actor 有三個原則,必須要包含下面三種基本要素

  1. processing: 要能進行運算
  2. storage: 必須要能儲存資料
  3. communication: 要能跟其他 actors 溝通

actor 就像是一個獨立的個體,他能持續的接收訊息,進行工作,但同一時間內,只能處理一個訊息,所有訊息都是依照收到的順序依次進行處理。

當 actor 互相傳送訊息,可能透過某種方式發送訊息給自己,這有可能會造成 deadlock 嗎?

Carl Hewitt 給了使用 Futures and promises 的概念,因為 future 就像是在現在預約一個在未來會產生結果的訊息,因此 actor 可以產生一個 future,並發送給自己的 address,然後在收到自己發送的訊息時,進行下一步的運算,並將結果放到 future 裡面,這完全不可能會造成 deadlock。

address 跟 actor 本身並不是一對一的,而是多對多的,一個 actor 可以有多個 addresses,一個 address 也可能因為原本的 actor 失效,而指向到另一個重新產生的 actor,address 並不代表是 actor 的 identity。

actor 因為一次處理一個訊息的特性,因此不會造成資料同步上的問題,基本上資料只存在於這個 actor 裡面,一次又只能因為一個訊息的輸入及運算,而異動了這個資料,因此不會造成資料同步上的問題。

AKKA NOTES - INTRODUCING ACTORS 這篇文章用了幾張圖片,說明 actor 之間如何互相傳送資料,Actor 就像是一群人,他們互相之間不會面對面地交流,而只是通過郵件的方式進行溝通。

傳遞消息是Actor模型的基礎,他以一名學生和一位教師舉例,描述了以下基礎流程:

  1. 一名學生給一位教師發送了一封郵件,郵件一旦發送之後,就不能夠修改了
  2. 教師會在她認為適合的時機去檢查她的郵箱,收到這封郵件
  3. 該教師稍後會寄一封回信給該學生,這封回信也是一旦發送就不可修改的
  4. 學生在一段時間後決定去檢查一下他的郵箱,收到了回信

信件是單向的,那麼它一旦送出之後並不會期待或等待任何響應,但 actor 都能夠在一個 request-response 週期內,為發送者發送回應消息。

Scratch 也使用了 Actor Model

Actor Model 中列出了現在支援了 Actor Model 的程式語言,其中有一個特別的是 Scratch,Scratch 是 MIT 為了小朋友開發的一套圖形介面的程式設計軟體,適合從來沒有學過程式設計的小朋友,通過拖曳預先設定好的積木式程式模件,堆疊出指令,設定或控制角色及背景的行動和變化,從而完成程式設計。

參考這個範例 Scratch 教學的第 1 堂課(1/5)適合親子共學的兒童程式設計入門,如果有用過 Scratch,也會知道在開發時,一開始就是要產生人物的主角,然後再程式區塊,產生數個 blocks,為每個 block 設定 trigger 該 block 的事件條件,當滿足該條件時,就持續下去進行下面程式區塊的運算,也可以在運算後,廣播一個訊息,驅動其他的程式區塊。

這跟 Actor Model 的概念是很接近的,執行時也可以發現,這些程式區塊是可以同時進行運算的,程式區塊之間,也可以透過訊息的發送傳遞消息,不過 scratch 還是支援了全域變數,這跟 actor model 本身就有些不同,但這畢竟是給小朋友使用的工具,因為變數的概念對學程式來說是一個門檻,如果變數沒有辦法共享,coding 就沒辦法那麼直覺地使用變數。

References

Akka in action: actor model

以Akka為示例,介紹Actor模型 I