# 節點類型及角色
盡管比特幣P2P網絡中的各個節點相互對等,但是根據所提供的功能不同,各節點可能具有不同的角色。每個比特幣節點都是路由、區塊鏈數據庫、挖礦、錢包服務的功能集合。一個全節點(full node)包括如圖所示的四個功能:

比特幣網絡節點,具有所有四個功能:錢包,礦工,完整的區塊鏈數據庫和網絡路由。
## 全節點和輕量級節點
一些節點保有一份完整的、最新的區塊鏈拷貝,這樣的節點被稱為“全節點”。全節點能夠獨立自主地校驗所有交易,而不需借由任何外部參照。另外還有一些節點只保留了區塊鏈的一部分,它們通過一種名為“簡易支付驗證(SPV)”的方 式來完成交易驗證。這樣的節點被稱為“SPV節點”,又叫“輕量級節點”。
## 礦池的由來
挖礦節點通過運行在特殊硬件設備上的工作量證明(proof-of-work)算法,以相互競爭的方式創建新的區塊。一些挖礦 節點同時也是全節點,保有區塊鏈的完整拷貝;還有一些參與礦池挖礦的節點是輕量級節點,它們必須依賴礦池服務器維護的全節點進行工作。
# 擴展比特幣網絡
比特幣P2P網絡中的一小部分節點也是挖礦節點,它們競爭挖礦、驗證交易、并創建新的區塊。許多連接到比特幣網絡的大型公司運行 著基于Bitcoin核心客戶端的全節點客戶端,它們具有區塊鏈的完整拷貝及網絡節點,但不具備挖礦及錢包功能。這些節點是網絡中的邊緣路由器(edge routers),通過它們可以搭建其他服務,例如交易所、錢包、區塊瀏覽器、商家支付處理(merchant payment processing)等。
擴展比特幣網絡既包括了運行比特幣P2P協議的網絡,又包含運行特殊協議的網絡節點。比特幣P2P主網絡上連接著許多礦池服務器以及協議網關,它們把運行其他協議的節點連接起來。這些節點通常都是礦池挖礦節點以及輕量級錢包客戶端,它們通常不具備區塊鏈的完整備份。
# 網絡發現
為了能夠加入到比特幣網絡,比特幣客戶端會做一下幾件事情:
a、節點會記住它最近成功連接的網絡節點,當重新啟動后它可以迅速與先前的對等節點網絡重新建立連接。
b、節點會在失去已有連接時嘗試發現新節點。
c、當建立一個或多個連接后,節點將一條包含自身IP地址消息發送給其相鄰節點。相鄰節點再將此消息依次轉發給它們各自的相鄰節點,從而保證節點信息被多個節點所接收、保證連接更穩定。
d、新接入的節點可以向它的相鄰節點發送獲取地址getaddr消息,要求它們返回其已知對等節點的IP地址列表。節點可以找到需連接到的對等節點。
e、在節點啟動時,可以給節點指定一個正活躍節點IP, 如果沒有,客戶端也維持一個列表,列出了那些長期穩定運行的節點。這樣的節點也被稱為種子節點(其實和BT下載的種子文件道理是一樣的),就可以通過種子節點來快速發現網絡中的其他節點。
1、節點通常采用TCP協議、使用8333端口(該端口號通常是比特幣所使用的,除8333端口外也可以指定使用其他端口) 與已知的對等節點建立連接。在建立連接時,該節點會通過發送一條包含基本認證內容的version消息開始“握手”通信過程。

版本消息始終是任何對等體發送給另一個對等體的第一條消息。 接收版本消息的本地對等體將檢查遠程對等體報告的nVersion,并確定遠端對等體是否兼容。 如果遠程對等體兼容,則本地對等體將確認版本消息,并通過發送一個verack建立連接。
2、比特幣的核心部分維護一個在啟動時可以連接的對等節點列表。當一個完整的節點第一次啟動時,它必須被自舉(bootstrapped)到網絡。這個過程如今在比特幣的核心部分通過一個短名單上的DNS種子自動執行。選項-dnsseed可以被用來定義這種行為,默認的設置是1。DNS請求返回一個可連接的IP地址列表。比特幣客戶端從那里可以連接到整個比特幣網絡。自舉的另外一種方法是使用參數-seednode=。通過這個參數,用戶可以預先定義連接到哪個服務器,并在建立對等節點列表之后斷開連接。另外一個方法是在啟動比特幣核心時配置-connect=參數來選擇連接到哪些對等節點(未被配置的IP將不會被連接)。添加對等節點的最后一種方法是通過參數-addnode=添加一個單獨的節點到對等節點列表中。
自舉過程完成后,節點向其對等節點發送一個包含其自身IP地址的addr消息。其對等的每個節點向它們自己的對等節點轉發這個信息,以便進一步擴大連接池。
3、當建立一個或多個連接后,新節點將一條包含自身IP地址的addr消息發送給其相鄰節點。相鄰節點再將此條addr消息依次轉發給它們各自的相鄰節點,從而保證新節點信息被多個節點所接收、保證連接更穩定。另外,新接入的節點可以向 它的相鄰節點發送getaddr消息,要求它們返回其已知對等節點的IP地址列表。通過這種方式,節點可以找到需連接到 的對等節點,并向網絡發布它的消息以便其他節點查找。下圖描述了這種地址發現協議。

節點必須連接到若干不同的對等節點才能在比特幣網絡中建立通向比特幣網絡的種類各異的路徑(path)。由于節點可以隨時加入和離開,通訊路徑是不可靠的。因此,節點必須持續進行兩項工作:在失去已有連接時發現新節點,并在其他節點啟動時為其提供幫助。節點啟動時只需要一個連接,因為第一個節點可以將它引薦給它的對等節點,而這些節點又會進一步提供引薦。一個節點,如果連接到大量的其他對等節點,這既沒必要,也是對網絡資源的浪費。在啟動完成 后,節點會記住它最近成功連接的對等節點;因此,當重新啟動后它可以迅速與先前的對等節點網絡重新建立連接。如果先前的網絡的對等節點對連接請求無應答,該節點可以使用種子節點進行重啟動。
在運行比特幣核心客戶端的節點上,您可以使用 getpeerinfo 命令列出對等節點連接信息:
$ bitcoin-cli getpeerinfo
~~~
{
"addr" : "85.213.199.39:8333",
"services" : "00000001",
"lastsend" : 1405634126,
"lastrecv" : 1405634127,
"bytessent" : 23487651,
"bytesrecv" : 138679099,
"conntime" : 1405021768,
"pingtime" : 0.00000000,
"version" : 70002,
"subver" : "/Satoshi:0.9.2.1/",
"inbound" : false,
"startingheight" : 310131,
"banscore" : 0,
"syncnode" : true
},
{
"addr" : "58.23.244.20:8333",
"services" : "00000001",
"lastsend" : 1405634127,
"lastrecv" : 1405634124,
"bytessent" : 4460918,
"bytesrecv" : 8903575,
"conntime" : 1405559628,
"pingtime" : 0.00000000,
"version" : 70001,
"subver" : "/Satoshi:0.8.6/",
"inbound" : false,
"startingheight" : 311074,
"banscore" : 0,
"syncnode" : false
}
~~~
用戶可以通過提供 -connect= 選項來指定一個或多個IP地址,從而達到覆蓋自動節點管理功能并指定IP地址列表的目的。如果采用此選項,節點只連接到這些選定的節點IP地址,而不會自動發現并維護對等節點之間的連接。
如果已建立的連接沒有數據通信,所在的節點會定期發送信息以維持連接。如果節點持續某個連接長達90分鐘沒有任何通信,它會被認為已經從網絡中斷開,網絡將開始查找一個新的對等節點。因此,比特幣網絡會隨時根據變化的節點及網絡問題進行動態調整,不需經過中心化的控制即可進行規模增減的有機調整。
# 全節點
完整區塊鏈節點保有完整的、最新的包含全部交易信息的比特幣區塊鏈拷貝,這樣的節點可以獨立地進行建立并校驗區塊鏈,從第一區塊(創世區塊)一直建立到網絡中最新的區塊。完整區塊鏈節點可以獨立自主地校驗任何交易信息,而不需要借助任何其他節點或其他信息來源。完整區塊節點通過比特幣網絡獲取包含交易信息的新區塊更新,在驗證無誤后將此更新合并至本地的區塊鏈拷貝之中。
# 交換“庫存清單”
一個全節點連接到對等節點之后,第一件要做的事情就是構建完整的區塊鏈。如果該節點是一個全新節點,那么它就不包含任何區塊鏈信息,它只知道一個區塊——靜態植入在客戶端軟件中的創世區塊。新節點需要下載從0號區塊(創世區塊)開始的數十萬區塊的全部內容,才能跟網絡同步、并重建全區塊鏈。
同步區塊鏈的過程從發送version消息開始,這是因為該消息中含有的BestHeight字段標示了一個節點當前的區塊鏈高度(區塊數量)。節點可以從它的對等節點中得到版本消息,了解雙方各自有多少區塊,從而可以與其自身區塊鏈所擁 有的區塊數量進行比較。對等節點們會交換一個getblocks消息,其中包含他們本地區塊鏈的頂端區塊哈希值(指紋)。如果某個對等節點識別出它接收到的哈希值并不屬于頂端區塊,而是屬于一個非頂端區塊的舊區塊,那么它就能推斷出:其自身的本地區塊鏈比其他對等節點的區塊鏈更長。
擁有更長區塊鏈的對等節點比其他節點有更多的區塊,可以識別出哪些區塊們是其他節點需要“補充”的。它會識別出第 一批可供分享的500個區塊,通過使用inv(inventory)消息把這些區塊的哈希值傳播出去。缺少這些區塊的節點便可以 通過各自發送的getdata消息來請求得到全區塊信息,用包含在inv消息中的哈希值來確認是否為正確的被請求的區塊, 從而讀取這些缺失的區塊。
每當一個節點離線,不管離線時間有多長,這個與對等節點比較本地區塊鏈并恢復缺失區塊的過程就會被觸發。如果一 個節點只離線幾分鐘,可能只會缺失幾個區塊;當它離線長達一個月,可能會缺失上千個區塊。但無論哪種情況,它都 會從發送 getblocks 消息開始,收到一個inv響應,接著開始下載缺失的區塊。庫存清單和區塊廣播協議如下圖所示。

# SPV節點
SPV節點只需下載區塊頭,而不用下載包含在每個區塊中的交易信息。由此產生的不含交易信息的區塊鏈,大小只有完整區塊鏈的1/1000。SPV節點不能構建所有可用于消費的UTXO的全貌,這是由于它們并不知道網絡上所有交易的完整信息。SPV節點驗證交易時所使用的方法略有不同,這個方法需依賴對等節點“按需”提供區塊鏈相關部分的局部視圖。
簡易支付驗證是通過參考交易在區塊鏈中的深度,而不是高度,來驗證它們。一個擁有完整區塊鏈的節點會構造一條驗證鏈,這條鏈是由沿著區塊鏈按時間倒序一直追溯到創世區塊的數千區塊及交易組成。而一個SPV節點會驗證所有區塊的鏈(但不是所有的交易),并且把區塊鏈和有關交易鏈接起來。
舉一個非常簡單能快速理解SPV驗證交易的例子:比如第300000號區塊中的某個交易需要驗證,SPV不能驗證UTXO是否還未被支付。SPV節點會在該交易信息和它所在的區塊之間用merkle路徑建立一條鏈接。
然后SPV節點就等著有6個確認就好了,從300001到300006的6個區塊都在這個交易上面時,也就從側面驗證了這個交易是被確認了,這是通過交易的深度來驗證交易的有效性。這是根據比特幣的代理網關協議,證明該交易不是雙重支付。
## 那SPV如何驗證某個交易不存在呢?
一個交易的存在是可能對SPV節點隱藏的。SPV能證實某個交易的存在性,但它不能驗證某個交易不存在,因為SPV節點沒有一份關于所有交易的記錄。
也就是說SPV節點連接的對等點能欺騙這個SPV節點,反正你并沒有交易的記錄,不能獨自進行驗證。
這樣只要發動對SPV節點的拒絕服務攻擊或者雙重支付驗證攻擊,SPV節點就完蛋了。為了防御這些攻擊,SPV節點需要隨機連接到多個節點,增加與一個可靠節點相連接的概率。
這種隨機連接的需求意味著SPV節點也容易受到網絡分區攻擊或Sybil攻擊。
在絕大多數的實際情況中,具有良好連接的SPV節點是足夠安全的,它在資源需求、實用性和安全性之間維持恰當的平衡。當然,如果要保證萬無一失的安全性,最可靠的方法還是運行完整區塊鏈的節點。
SPV節點使用的是一條getheaders消息,而不是getblocks消息來獲得區塊頭。發出響應的對等節點將用一條headers 消息發送多達2000個區塊頭。這一過程和全節點獲取所有區塊的過程沒什么區別。SPV節點還在與對等節點的連接上設置了過濾器,用以過濾從對等節點發來的未來區塊和交易數據流。任何目標交易都是通過一條getdata的請求來讀取的。對等節點生成一條包含交易信息的tx消息作為響應。區塊頭的同步過程如下圖所示。

## SPV詢問的隱私風險
由于SPV節點需要讀取特定交易從而選擇性地驗證交易,這樣就又產生了隱私風險。與全區塊鏈節點收集每一個區塊內的全部交易所不同的是,SPV節點對特定數據的請求可能無意中透露了錢包里的地址信息。例如,監控網絡的第三方可以跟蹤某個SPV節點上的錢包所請求的全部交易信息,并且利用這些交易信息把比特幣地址和錢包的用戶關聯起來,從而損害了用戶的隱私。
在引入SPV節點/輕量級節點后不久,比特幣開發人員就添加了一個新功能:Bloom過濾器,用以解決SPV節點的隱私風 險問題。Bloom過濾器通過一個采用概率而不是固定模式的過濾機制,允許SPV節點只接收交易信息的子集,同時不會精確泄露哪些是它們感興趣的地址。
## Bloom過濾器
Bloom過濾器是一個允許用戶描述特定的關鍵詞組合而不必精確表述的基于概率的過濾方法。它能讓用戶在有效搜索關鍵詞的同時保護他們的隱私。在SPV節點里,這一方法被用來向對等節點發送交易信息查詢請求,同時交易地址不會被 暴露。
Bloom過濾器可以讓SPV節點指定交易的搜索模式,該搜索模式可以基于準確性或私密性的考慮被調節。一個非常具體 的Bloom過濾器會生成更準確的結果,但也會顯示該用戶錢包里的使用的地址;反之,如果過濾器只包含簡單的關鍵 詞,更多相應的交易會被搜索出來,在包含若干無關交易的同時有著更高的私密性。
## SPV節點如何使用Bloom過濾器
8.10 SPV節點如何使用Bloom過濾器
Bloom過濾器用于過濾SPV節點從其對等體接收的交易(和包含它們的塊),僅選擇SPV節點感興趣的交易,而不會泄露其感興趣的地址或密鑰。
SPV節點將初始化“過濾器”為“空”;在該狀態下,bloom過濾器將不匹配任何模式。然后,SPV節點將列出所有感興趣的地址,密鑰和散列,它將通過從其錢包控制的任何UTXO中提取公鑰哈希和腳本哈希和交易ID來實現。 SPV節點然后將其中的每一個添加到Bloom過濾器,以便如果這些模式存在于交易中,則Bloom過濾器將“匹配”,而不會自動顯示模式。
然后,SPV節點將向對等體發送一個過濾器加載消息,其中包含在連接上使用的bloom過濾器。在對等體上,針對每個傳入交易檢查Bloom過濾器。完整節點根據bloom過濾器檢查交易的幾個部分,尋找匹配,包括:
* 交易ID
* 每個交易輸出的鎖定腳本的數據組件(腳本中的每個鍵和哈希)
* 每個交易輸入
* 每個輸入簽名數據組件(或見證腳本)
通過檢查所有這些組件,可以使用Bloom過濾器來匹配公鑰哈希,腳本,OP\_RETURN值,簽名中的公鑰或智能合同或復雜腳本的任何未來組件。
在建立過濾器之后,對等體然后將針對bloom過濾器測試每個交易的輸出。只有與過濾器匹配的交易才會發送到節點。
響應于來自節點的getdata消息,對等體將發送一個merkleblock消息,該消息僅包含與過濾器匹配的塊和每個匹配交易的merkle路徑的塊頭。然后,對等體還將發送包含由過濾器匹配的交易的tx消息。
由于完整節點向SPV節點發送交易,SPV節點丟棄任何誤報,并使用正確匹配的交易來更新其UTXO集和錢包余額。隨著它更新自己的UTXO集視圖,它還會修改bloom過濾器,以匹配任何引用其剛剛發現的UTXO的交易。然后,完整節點使用新的bloom過濾器來匹配新交易,并重復整個過程。
設置bloom過濾器的節點可以通過發送filteradd消息將模式交互式添加到過濾器。要清除bloom過濾器,節點可以發送一個過濾清除消息。因為不可能從Bloom過濾器中刪除模式,所以如果不再需要模式,則節點必須清除并重新發送新的布隆過濾器。
[BIP-37 (Peer Services)](http://bit.ly/1x6qCiO)中定義了SPV節點的網絡協議和布隆過濾機制。
## SPV節點和隱私
實現SPV的節點的隱私比整個節點更弱。完整節點接收所有交易,因此不會顯示關于它的錢包中是否使用某個地址的信息。 SPV節點接收與其錢包中的地址相關的經過過濾的列表。結果,它減少了所有者的隱私。
bloom過濾器是減少隱私損失的一種方式。沒有它們,SPV節點將不得不明確地列出它感興趣的地址,造成嚴重的隱私違規。然而,即使使用過濾器,對手監控SPV客戶端的流量或直接連接到它的P2P網絡中的節點可以隨時隨地收集足夠的信息來了解SPV客戶端的錢包中的地址。
## 加密和認證連接
作為增加比特幣P2P網絡隱私和安全性的一種方法,有兩種解決方案可以通過BIP-150/151提供通信加密:Tor傳輸和P2P認證和加密。
### Tor網絡傳輸
Tor代表洋蔥路由網絡,是一個軟件項目和網絡,通過提供匿名,不可追蹤和隱私的隨機網絡路徑提供數據的加密和封裝。
Bitcoin Core提供了多種配置選項,允許您運行通過Tor網絡傳輸的流量的比特幣節點。此外,Bitcoin Core還可以提供Tor隱藏服務,允許其他Tor節點通過Tor直接連接到您的節點。
從Bitcoin Core版本0.12開始,如果能夠連接到本地Tor服務,節點將自動提供隱藏的Tor服務。如果您安裝Tor并且Bitcoin Core進程作為具有足夠權限的用戶訪問Tor認證cookie的用戶運行,則應自動運行。使用debug標志打開Bitcoin Core對于Tor服務的調試,如下所示:
~~~
$ bitcoind --daemon --debug=tor
~~~
你應該在日志中看到“tor:ADD\_ONION success”,表示Bitcoin Core已經向Tor網絡添加了隱藏的服務。
您可以在Bitcoin Core文檔(docs / tor.md)和各種在線教程中找到有關運行Bitcoin Core作為Tor隱藏服務的更多說明。
## 對等認證和加密
BIP-150和BIP-151兩種比特幣改進方案在比特幣P2P網絡中增加了對P2P認證和加密的支持。這兩個BIP定義了可由兼容的比特幣節點提供的可選服務。 BIP-151啟用了支持BIP-151的兩個節點之間的所有通信的協商加密。 BIP-150提供可選的對等認證,允許節點使用ECDSA和私鑰對對方的身份進行身份驗證。 BIP-150要求在認證之前,兩個節點按照BIP-151建立了加密通信。
截至2017年1月,BIP-150和BIP-151未在Bitcoin Core中實施。但是,這兩個提案已由至少一個名為bcoin的替代比特幣客戶端實施。
BIP-150和BIP-151允許用戶運行連接到受信任的完整節點的SPV客戶端,使用加密和身份驗證來保護SPV客戶端的隱私。
此外,可以使用身份驗證來創建可信比特幣節點的網絡,并防止中間人攻擊。最后,P2P加密(如果廣泛部署)將加強比特幣對流量分析和隱私侵權監控的阻力,特別是在互聯網使用受到嚴格控制和監控的極權主義國家。
## 交易池
比特幣網絡中幾乎每個節點都會維護一份未確認交易的臨時列表,被稱為內存池或交易池。節點們利用這個池來追蹤記錄那些被網絡所知曉、但還未被區塊鏈所包含的交易。例如,保存用戶錢包的節點會利用這個交易池來記錄那些網絡已經接收但還未被確認的、屬于該用戶錢包的預支付信息。
隨著交易被接收和驗證,它們被添加到交易池并通知到相鄰節點處,從而傳播到網絡中。
有些節點的實現還維護一個單獨的孤立交易池。如果一個交易的輸入與某未知的交易有關,如與缺失的父交易相關,該 孤立交易就會被暫時儲存在孤立交易池中直到父交易的信息到達。
當一個交易被添加到交易池中,會同時檢查孤立交易池,看是否有某個孤立交易引用了此交易的輸出(子交易)。任何 匹配的孤立交易會被進行驗證。如果驗證有效,它們會從孤立交易池中刪除,并添加到交易池中,使以其父交易開始的鏈變得完整。對新加入交易池的交易來說,它不再是孤立交易。前述過程重復遞歸尋找進一步的后代,直至所有的后代都被找到。通過這一過程,一個父交易的到達把整條鏈中的孤立交易和它們的父級交易重新結合在一起,從而觸發了整 條獨立交易鏈進行級聯重構。
交易池和孤立交易池(如有實施)都是存儲在本地內存中,并不是存儲在永久性存儲設備(如硬盤)里。更準確的說, 它們是隨網絡傳入的消息動態填充的。節點啟動時,兩個池都是空閑的;隨著網絡中新交易不斷被接收,兩個池逐漸被 填充。
有些比特幣客戶端的實現還維護一個UTXO數據庫,也稱UTXO池,是區塊鏈中所有未支付交易輸出的集合。“UTXO 池”的名字聽上去與交易池相似,但它代表了不同的數據集。UTXO池不同于交易池和孤立交易池的地方在于,它在初始 化時不為空,而是包含了數以百萬計的未支付交易輸出條目,有些條目的歷史甚至可以追溯至2009年。UTXO池可能會被安置在本地內存,或者作為一個包含索引的數據庫表安置在永久性存儲設備中。
交易池和孤立交易池代表的是單個節點的本地視角。取決于節點的啟動時間或重啟時間,不同節點的兩池內容可能有很 大差別。相反地,UTXO池代表的是網絡的突顯共識,因此,不同節點間UTXO池的內容差別不大。此外,交易池和孤立交易池只包含未確認交易,而UTXO池之只包含已確認交易。
- 重要更新說明
- linechain發布
- linechain新版設計
- 引言一
- 引言二
- 引言三
- vs-code設置及開發環境設置
- BoltDB數據庫應用
- 關于Go語言、VS-code的一些Tips
- 區塊鏈的架構
- 網絡通信與區塊鏈
- 單元測試
- 比特幣腳本語言
- 關于區塊鏈的一些概念
- 區塊鏈組件
- 區塊鏈第一版:基本原型
- 區塊鏈第二版:增加工作量證明
- 區塊鏈第三版:持久化
- 區塊鏈第四版:交易
- 區塊鏈第五版:實現錢包
- 區塊鏈第六版:實現UTXO集
- 區塊鏈第七版:網絡
- 階段小結
- 區塊鏈第八版:P2P
- P2P網絡架構
- 區塊鏈網絡層
- P2P區塊鏈最簡體驗
- libp2p建立P2P網絡的關鍵概念
- 區塊鏈結構層設計與實現
- 用戶交互層設計與實現
- 網絡層設計與實現
- 建立節點發現機制
- 向區塊鏈網絡請求區塊信息
- 向區塊鏈網絡發布消息
- 運行區塊鏈
- LineChain
- 系統運行流程
- Multihash
- 區塊鏈網絡的節點發現機制深入探討
- DHT
- Bootstrap
- 連接到所有引導節點
- Advertise
- 搜索其它peers
- 連接到搜到的其它peers
- 區塊鏈網絡的消息訂發布-訂閱機制深入探討
- LineChain:適用于智能合約編程的腳本語言支持
- LineChain:解決分叉問題
- LineChain:多重簽名
- libp2p升級到v0.22版本
- 以太坊基礎
- 重溫以太坊的樹結構
- 世界狀態樹
- (智能合約)賬戶存儲樹
- 交易樹
- 交易收據樹
- 小結
- 以太坊的存儲結構
- 以太坊狀態數據庫
- MPT
- 以太坊POW共識算法
- 智能合約存儲
- Polygon Edge
- block結構
- transaction數據結構
- 數據結構小結
- 關于本區塊鏈的一些說明
- UML工具-PlantUML
- libp2p介紹
- JSON-RPC
- docker制作:啟動多個應用系統
- Dockerfile
- docker-entrypoint.sh
- supervisord.conf
- docker run
- nginx.conf
- docker基礎操作整理
- jupyter計算交互環境
- git技巧一
- git技巧二
- 使用github項目的最佳實踐
- windows下package管理工具