CoreOS那些事之Rkt容器嚐鮮(下)

2015年是各類容器技術與名詞扎堆的一年,Docker的出現使得「應用容器」的實施變得易如反掌的同時,也帶動了它的許多競爭者。其中一個比較有趣的看點就在於「容器規範」的較量,最近紅帽和英特爾也按捺不住,拿出自家的產品趁勢攪局。html

5月14日,紅帽宣佈了新的多容器應用規範Nulecule(DockOne翻譯了這篇新聞),同時推出符合這個規範的一個實現:AtomicApp。(這個路子怎麼看都有點像AppC和Rkt採用的模式)python

因特爾則是發揮自家的特長,在5月18日,發佈了介於虛擬機與容器之間的跨界產品ClearLinux(DockOne一樣翻譯了這篇新聞)。之因此特別要提這個項目,是由於它首先會實現基於Rkt/AppC規範的容器模型,而將Docker放在了其次的位置。linux

「容器規範」的概念,看起來讓人有些摸不着頭腦,但在容器業界中,它確實是頗具誘惑力的一塊蛋糕。在這一篇裏,咱們就來聊一聊支撐Rkt背後的那個「容器規範」:AppC Spec。git

AppC規範究竟約定了什麼

使用了開源軟件的人,未必都會有心情仔細閱讀各類開源協議的內容。大多數的使用容器產品用戶,也不見得要對容器規範的內容有很高的興致。github

不過,爲了更好的理解後面將要介紹到的相關工具,仍是不妨稍微深刻的瞭解一些AppC規範約定的內容。其內容概括起來主要有四個方面,下面依次羅列出來,並與當下的主流容器Docker作一個簡要的對比。golang

PS:嚴格來講,AppC與AppC Spec兩個詞是有區別的。前者指的是CoreOS的App Container這個項目,包括規範和相關的工具,然後者特指AppC中約定的容器規範。但在許多地方,特別是翻譯的文章中,常常看到這兩個詞被混用,所以通常也沒必要太講究了。算法

1.容器的鏡像格式

本質上說,容器鏡像就是符合特定目錄結構的文件壓縮包。鏡像中的內容在容器啓動後被展開,而後複製到一個獨立的namespace空間內,並經過cgroup限制容器可以使用的系統資源。稍後在製做鏡像時,會詳細介紹AppC Spec規定的鏡像目錄結構。這裏只先指出一點,AppC的鏡像沒有支持像Docker那樣的分層結構,這種設計簡化了容器運行時的一些操做,但帶來的弊端也是很明顯的:沒法複用鏡像相同的部分。所以在磁盤空間的利用上形成了浪費,也增長了容器鏡像在網絡傳輸成本。docker

除了目錄的結構,鏡像還須要一個描述鏡像內容的文件,稱爲「鏡像屬性清單文件(Image Manifest)」,其中定義的內容包括:鏡像的做者信息、容器暴露的端口、暴露的掛載點、所需的系統資源(CPU/內存)等。此外,AppC Spec的約定的屬性清單中,還會包含許多編排調度所需的信息,例如容器運行所依賴的其餘容器、容器的標籤。ubuntu

在這方面來講,AppC鏡像的信息量遠遠多於Docker鏡像。至關於囊括了Docker鏡像自己、Compose編排配置以及一部分Docker運行參數的內容。緩存

此外,AppC規範也約定的鏡像ID和簽名的生成方法,關於鏡像ID和簽名的做用和在Rkt文章上篇中已經介紹過,稍後還會詳細介紹鏡像簽名的生成方法。

2.鏡像的分發協議

分發協議主要是約定鏡像下載使用的協議類型和URL的樣式。AppC的鏡像URL採用相似Docker的domain.com/image-name這樣的格式,但其實際處理方式有些不一樣。此外,在沒有指定域名時,Docker會默認在官方的DockerHub尋找鏡像,AppC的鏡像沒有所謂「官方源」,所以也沒有這樣的規則。

Rkt/AppC目前支持如下幾種URL格式:

  • <域名>/<鏡像名>
  • <本地文件路徑>
  • https://<完整網絡路徑>
  • http://<完整網絡路徑>
  • docker://<與Docker同樣的鏡像URL>

第一種方式是AppC推薦的鏡像分發URL,這種方式有點像Docker Repository,但實際上只是HTTPS協議的簡寫方式。AppC會根據指導的域名和路徑依照約定的方式轉換爲完整URL地址,而後下載指定的鏡像。

第二種方式至關於導入本地鏡像。值得一提的是,即使使用本地鏡像,AppC一樣要求鏡像有簽名認證,關於簽名文件的細節在後面的內容裏會詳細討論。

第三種和第四種方式都是直接經過完整URL獲取鏡像,規範中並不推薦直接這樣使用裸的HTTPS的URL,由於這種命名過於隨意的鏡像地址不利於鏡像的管理和統一,特別是HTTP協議的URL更只應該在內網的環境中出現。

第五種方式不是AppC規範支持的協議類型,目前只是Rkt支持這種協議(本質上仍是HTTP或HTTPS)。兼容Docker鏡像的URL,只須要在前面加上docker://便可,下載後會自動轉換爲AppC鏡像格式。因爲Docker的鏡像倉庫不支持簽名認證,使用這種URL時,用戶須要顯示的加上參數--insecure-skip-verify容許使用未認證的鏡像來源。

3.容器的編排結構

AppC規範中的容器編排和集羣描述方式與Kubernetes十分類似,採用「容器組屬性清單文件(Pod Manifest)」描述。其中沿用了Kubernetes中諸如Podslabels等用於在集羣中進行調度策略的規劃和管理的概念。

Pods直譯即是「豆莢」,它指的是由一系列相互關聯的容器組成的,可以對外提供獨立服務功能的容器集合。例如將用於數據收集功能的容器、用於緩存服務的容器以及用於搜索服務的容器組合在一塊兒,做爲一個Pod提供完整的數據查詢服務暴露給外部用戶。Pod能夠做爲容器參與集羣調度的單獨集合提供給集羣管理器,在例如Kubernetes這樣的集羣管理模型中,Pod實際上就是進行服務跨節點調度的最小單位。

labels用於標示具備同一類特性的容器,爲容器的過濾和選擇提供了十分靈活的策略。許多的集羣管理器都可以在調度時利用選擇指定標籤對Pods進行篩選。

考慮到CoreOS公司與谷歌共同合做的背景(已經推出了Tectonic CaaS平臺),這樣的設計爲Kubernetes將來與符合AppC規範的容器進行深度集成提供了良好的技術基礎。

4.容器的執行器

執行器,也就像是Rkt這樣的容器工具。這個部分規範了設計符合AppC Spec的容器執行器所須要遵循的原則和應該具有的功能。

例如,必須爲每一個容器提供惟一的UUID;在容器的運行上下文中必須至少提供一個本地Loopback網卡,以及0個至多個其餘TCP/IP網卡;應該將容器中程序打印到Stdout和Stderr的日誌進行收集和展現等細節。

其中還詳細約定了,對於鏡像屬性清單中的諸多屬性,執行器應當如何進行處理。這些內容對大部分的使用者而言都只能做爲參考,仍是須要以具體實現的容器產品文檔爲準。

鏡像工具

在AppC的項目中,除了一紙洋洋灑灑的規範文書之外,還提供了很多AppC鏡像相關的示範工具。不像Docker這種一個命令集成全部功能的玩法,這些工具中的每一個只是關注於容器的某個方面功能。例如通用類型鏡像的製做、打包、格式轉換以及特定類型鏡像的製做等。

目前已有的工具主要包括:

  • Actool - 用於鏡像的構建和驗證
  • Docker2Aci - 用於將Docker導出的鏡像轉換爲AppC鏡像
  • Goaci - 用於Golang語言的項目一鍵打包和構建鏡像的工具
  • Acbuild - 經過指令的方式構建鏡像

其中,ActoolAcbuild都是用於鏡像構建的工具,它們的區別相似於經過docker commit和Dockerfile兩種方式構建鏡像。須要指出的是,前不久纔剛剛創建的Acbuild項目,如今還只是一個計劃,沒有發佈任何實際可用的版本,其目的是替代以前的另外一個項目baci。後者已經沒法使用而且再也不繼續更新。

Goaci的做用是獲取指定路徑的項目,進行自動編譯,而後把編譯後的可執行程序製做成一個鏡像,全部的這些操做只須要一條命令就能夠完成:goaci <項目路徑>,項目路徑支持全部go get命令所支持的代碼託管網站,包括BitBucket、GitHub、Google Code和Launchpad等。不過,它只能用於使用Golang語言並託管在上述網站中的開源項目。不具備廣泛的適用性。

下面將重點介紹ActoolDocker2Aci這兩個工具。爲了方便非CoreOS系統用戶嚐鮮,也會介紹在這些工具在其餘64位Linux發行版的安裝方法。

鏡像的製做

與鏡像製做相關的工具是Actool,這個軟件已經預裝在CoreOS系統較新的版本上了,能夠經過actool --help命令驗證並得到Actool的版本。其餘64位Linux的用戶能夠經過下面的命令安裝它:

wget https://github.com/AppC/spec/releases/download/v0.5.2/AppC-v0.5.2.tar.gz
tar zxf AppC-v0.5.2.tar.gz
sudo mv AppC-v0.5.2/actool /usr/local/bin/

說到構建鏡像,前面已經提到,新的命令式構建鏡像的工具Acbuild目前尚未發佈任何可用版本。所以當下的狀況是,構建AppC鏡像還只能手工建立鏡像屬性清單文件,拷貝容器中所需的文件,而後直接打包生成鏡像。索性,這樣建立鏡像除了失去諸如「基礎設施即代碼」的好處之外,並無多少值得非議的地方,構建流程自己並不複雜。

下面來製做一個十分樸素的AppC容器鏡像,這個鏡像中只包含一個可執行文件。

首先新建一個用於製做鏡像的工做目錄,例如AppC-image

>mkdir AppC-image

接下來,爲了讓這個例子足夠簡單,咱們須要一個可以不依賴任何外部動態庫或運行時環境,可以單獨運行的程序。咱們寫一個C語言的「Hello World」吧。新建一個叫hello.c的文件,內容以下:

#include <stdio.h>
int main(int argc, char* argv[])
{
    printf("Hello AppC\n"); //隨便輸出點什麼東西
    return 0;
}

而後,須要一個C語言編譯器,有些Linux系統已經自帶了這個東西,用gcc --version命令能夠驗證。若是沒有安裝,那麼...你們看着辦吧,好比在Ubuntu系統下面能夠經過apt-get來獲取:

sudo apt-get install gcc

CoreOS系統會稍微麻煩一點,須要藉助一個額外的容器來完成。提示一下,Docker有一個官方的C/C++語言運行環境鏡像,就叫gcc。能夠docker pull gcc或者rkt --insecure-skip-verify fetch docker://gcc來獲取它,而後啓動一個容器,注意須要映射一個Volumn到主機上,方便編譯完成後將生成的可執行程序拷貝出來。

編譯的命令以下,其中的--static參數是必須的,不然編譯出來的程序在執行時會須要依賴外部的動態庫:

gcc --static -o hello hello.c

在剛剛工做目錄裏面新建一個叫rootfs的目錄,將編譯生成的hello可執行文件拷貝進去。這個rootfs目錄中的內容就是之後容器裏所包含的文件內容了,所以建議在其中再創建一些標準的目錄結構,例如/bin目錄,將可執行程序放到這個目錄裏面。

mkdir -p AppC-image/rootfs/bin
cp hello AppC-image/rootfs/bin/

如今鏡像的目錄結構已經成型了。下面就能夠開始建立鏡像的屬性清單文件了,在工做目錄中新建一個名爲manifest的文件,內容以下:

{
    "acKind": "ImageManifest",
    "acVersion": "0.5.2",
    "name": "my-app",
    "labels": [
        {"name": "os", "value": "linux"},
        {"name": "arch", "value": "amd64"}
    ],
    "app": {
        "exec": [
            "/bin/hello"
        ],
        "user": "0",
        "group": "0"
    }
}

此時,工做目錄裏的文件結構應該是這樣的:

AppC-image
├── manifest
└── rootfs
    └── bin
        └── hello

最後就能夠用actool命令構建鏡像了:

actool build AppC-image hello.aci

鏡像的驗證

容器鏡像的來源無非有兩種:本地的或者遠程的。

所以對鏡像的驗證也就包含兩部份內容。

  • 檢驗本地的文件是否符合AppC規範的鏡像
  • 檢驗遠程的URL是不是有效的AppC鏡像地址

對於前一種狀況,前面說過,容器鏡像其實就是符合必定標準結構的打包文件。

$ file hello.aci
hello.aci: gzip compressed data

在AppC規範中,鏡像文件的後綴名應該是.aci,但具備這個後綴名的打包文件未必就是正確的鏡像。所以須要一個方法來驗證鏡像文件的正確性,相應的命令是actool validate

直接執行這個命令時,actool只會經過命令的返回值表示驗證的結果,返回0表示驗證經過:

$ actool validate hello.aci
$ echo $?
0

能夠加上-debug參數讓actool直接將結果打印在控制檯上:

$ actool -debug validate hello.aci
hello.aci: valid app container image

對於後一種狀況,URL的驗證,一樣能夠經過actool工具完成,相應的命令是actool discover

這個命令會返回鏡像的實際下載地址:

$ actool discover coreos.com/etcd
ACI: https://github.com/coreos/etcd/releases/download/latest/etcd
-latest-linux-amd64.aci, ASC: https://github.com/coreos/etcd
/releases/download/latest/etcd-latest-linux-amd64.aci.asc
Keys: https://coreos.com/dist/pubkeys/aci-pubkeys.gpg

試一下在Rkt裏面用過的Docker鏡像地址,會發現這個地址是無效的。

$ actool discover docker://ubuntu
error fetching docker://ubuntu: Get https://docker?ac-discovery=1: 
dial tcp: lookup docker: no such host

這也證明了在前面說的,docker://這種協議只是Rkt額外支持的鏡像獲取方式,並非AppC的規範中的標準協議。

鏡像的轉換

AppC的規範制定者們顯然很清楚,哪些輪子該重造,哪些輪子是能夠直接複用的。在Docker的各類鏡像已然是鋪天蓋地的當下,一個新的容器工具想要最快積累鏡像數量,最好的辦法就是兼容Docker鏡像或者將Docker的鏡像進行轉換。

其實對於鏡像兼容這個問題,新標準們有各自不一樣的作法,紅帽的Nulecule選擇了支持Dockerfile格式,只須要把已有的鏡像代碼加上一些額外的配置文件,從新構建一次就能夠。而AppC作過一樣的嘗試,(以前有個baci項目就是幹這個的,不過已經沒有更新了),效果上有些不三不四,所以索性更乾脆,提供個工具容許將任何的Docker鏡像導出後直接轉換成本身的鏡像格式。

下面就來講說從Docker到AppC鏡像的轉換,相應的工具是Docker2Aci

這個工具不管是在Ubuntu或者CoreOS上都沒有預裝,所以須要單獨安裝。這個工具尚未正式發佈,所以官方也沒有提供編譯好的二進制包,要得到它只能從源代碼編譯。值得慶幸的是,Golang語言的編譯方式仍是比較友好的,若是本地已經安裝了Golang語言的開發環境,能夠直接經過go get github.com/AppC/docker2aci命令完成整個下載和編譯的過程。

考慮到大多數用戶都是沒有Golang開發環境的,另外一個比較簡單的辦法是:經過容器。由於,Docker官方已經提供了一個用於Golang開發的容器鏡像,名字就叫golang。用下面一條命令就能夠搞定編譯。

sudo docker run -v $(pwd):/pkg -i -t golang:1.4 /bin/bash -c "go 
get github.com/AppC/docker2aci; cp \$GOPATH/bin/docker2aci /pkg/"

編譯好的docker2aci二進制文件會被拷貝到當前目錄,將它放到系統變量PATH所指的的任意目錄中便可,好比:

sudo mv docker2aci /usr/local/bin/

執行docker2aci --version命令能夠打印出軟件的使用幫助,證實已經成功安裝。

$ docker2aci
Usage of docker2aci:
docker2aci [--debug] [--nosquash] IMAGE
  Where IMAGE is
    [--image=IMAGE_NAME[:TAG]] FILEPATH
  or
    docker://[REGISTRYURL/]IMAGE_NAME[:TAG]
Flags:
  -debug=false: Enables debug messages
  -image="": When converting a local file, it selects a particular image to convert. Format: IMAGE_NAME[:TAG]
  -nosquash=false: Don't squash layers and output every layer as ACI

將一個Docker鏡像導出,而後能夠經過docker2aci命令轉換成AppC鏡像。

$ docker pull ubuntu
$ docker save -o ubuntu.docker ubuntu
$ ./docker2aci ubuntu.docker
... ...
Generated ACI(s):
ubuntu-latest.aci

轉換後的鏡像會保存在當前目錄,並自動用「<鏡像>-<標籤>」的格式命名。

此外,比較實用的是,docker2aci一樣支持docker://協議的URL直接獲取網上的鏡像。

$ docker2aci docker://busybox
... ...
Generated ACI(s):
busybox-latest.aci

鏡像的簽名

這個時候,若是直接用Rkt運行剛剛建立的hello.aci鏡像,會發現Rkt提示因爲找不到有效的簽名文件,所以拒絕運行這個鏡像。

$ sudo rkt run hello.aci
error opening signature file: open /home/core/hello.aci.asc: no such file or directory

鏡像的簽名,是AppC引入的一種鏡像來源驗證機制,本質上是利用非對稱加密的標準數字簽名。經過將鏡像提供者的私鑰和鏡像文件自己加密生產一組簽名字符串,經過發佈者提供的公鑰就可以解開這串字符並獲得與鏡像匹配的信息,這樣就能驗證鏡像是不是真的來自特定的做者或來源。

AppC的簽名算法是標準是RSA,採用的是開源的GPG實現,關於GPG的詳細介紹參考這篇文章。

首先準備一個密鑰配置文件,命名爲gpg-batch,內容以下:

%echo Generating a default key
Key-Type: RSA 
Key-Length: 2048
Subkey-Type: RSA 
Subkey-Length: 2048
Name-Real: 你的英文名字
Name-Email: 你的郵箱地址
Name-Comment: ACI signing key
Expire-Date: 0
Passphrase: 簽名時的密碼
%pubring rkt.pub
%secring rkt.sec
%commit
%echo done

而後用下面的命令生成一個密鑰對:

gpg --batch --gen-key gpg-batch

執行完成後,在目錄中會多出rkt.secrkt.pub兩個文件,這就是私鑰和公鑰了。

而後就可使用這對密鑰給鏡像簽名了:

gpg --no-default-keyring --armor --secret-keyring ./rkt.sec 
--keyring ./rkt.pub --output hello.aci.asc --detach-sig hello.aci

在提示輸入密碼時,輸入在gpg-batch中設置的密碼。而後就得到了hello.aci鏡像的簽名文件hello.aci.asc

再次嘗試運行容器:

$ sudo rkt run hello.aci
openpgp: signature made by unknown entity

此次提示的錯誤是,簽名文件雖然找到了,可是這個簽名的來源並無在信任列表中。爲了將簽名添加到信任裏面裏面,首先要用rkt.secrkt.pub這兩個二進制的密鑰文件導出爲一個文本的公鑰文件。

gpg --no-default-keyring --armor --secret-keyring ./rkt.sec 
--keyring ./rkt.pub --export 在gpg-batch中的郵箱 > pubkeys.gpg

而後將這個文本文件中的公鑰添加到Rkt的信任列表中。

$ sudo rkt trust --root pubkeys.gpg
Prefix: ""
Key: "pubkeys.gpg"
GPG key fingerprint is: 37E2 6071 5382 5868 5A0D  1356 98A9 5E24 6E19 7AED
    Subkey fingerprint: 46AF 81E4 77D4 BFCA DFCE  73C6 3D94 79C2 2611 F243
    Kelsey Hightower (ACI signing key) 
Are you sure you want to trust this key (yes/no)? yes
Trusting "pubkeys.gpg" for prefix "".
Added root key at "/etc/rkt/trustedkeys/root.d/37e26071538258685a0d135698a95e246e197aed"

此次運行容器,就能夠看到容器中的Hello程序已經正確的執行了。

$ sudo rkt run hello.aci
rkt: signature verified:
  Kelsey Hightower (ACI signing key) <kelsey.hightower@coreos.com>
Hello AppC

鏡像的倉庫

最後簡單的介紹一下AppC的鏡像倉庫(Image Repository)。

AppC規範定義了獲取鏡像的URL,其形式大體是域名/鏡像路徑:版本,例如CoreOS提供的包含Etcd的鏡像能夠經過命令rkt fetch coreos.com/etcd:v2.0.9來獲取。

這裏只說兩個比較有意思的地方。

首先,AppC是沒有所謂「官方鏡像倉庫」的,因此URL中的域名部分始終會存在。由CoreOS公司提供的鏡像被放在coreos.com域名下的普通倉庫中。這一點也符合AppC標準開放化的初衷。

其次,AppC會對用戶的URL嘗試經過兩種方式解析。換句話說,鏡像倉庫的實現方式能夠有兩種。第一種幾乎不須要額外的配置工做,將鏡像依照必定的命名規則放在域名的相應路徑下便可,例如coreos.com/etcd:v2.0.9,若是使用第一種方式建倉庫,相應的鏡像就應該保存在https://coreos.com/etcd-v2.0.9-linux-amd64.aci(固然,CoreOS的鏡像其實是用的第二種方式,因此這個路徑不存在)。第二種方式更加靈活,但須要額外的程序來處理鏡像地址的映射,具體過程,再也不詳述。

此外,前面介紹過,對於內網環境,還能夠直接使用HTTP路徑獲取鏡像。舉個栗子,把以前製做好的鏡像文件hello.aci和簽名文件hello.aci.asc放到一個目錄裏面,而後在這個目錄中啓動一個簡易的HTTP服務:

$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

在任意另外一個主機上就能夠直接使用HTTP全路徑下載鏡像了。

$ sudo rkt fetch http://<服務器IP>:8000/hello.aci
... ...
Downloading ACI: [        ] 1.26 KB/358 KB
sha512-f7a2feff02a07ed7c604c14133b7aede

這種簡單粗暴到無以復加的方式,對於懶人們也許算得上是一種福利,然而不管是從安全性仍是鏡像版本的可管理性上看來,其能力都至關弱。這從一個側面說明,一些爲開發者們提供的便利通道,若是沒有規範的約束,可能帶來很大的隱患。在實際運用時,建議遵循AppC的URL解析規範設計倉庫爲上策。

小結

與處於Alpha開發期的Rkt同樣,AppC項目距離最終的成熟還有很長的路要走。然而隨着谷歌、VMWare和因特爾等互聯網公司的加入,他們旗下的產品,如Kubernetes、Photon、ClearLinux等都已經將集成AppC容器做爲其運營策略的一部分,其運用場景也日漸明朗。

「輕量」、「開放」、「安全」,很難說這樣一種由「一個公司牽頭,多個公司入夥,但願依賴社區力量發展」的容器標準會走向何方。至少咱們已經看到,不管是CoreOS的AppC仍是紅帽的Nulecule,這些後來的容器標準的出現,或多或少是爲了改善Docker的某些短板,也爲容器業界提供了的一些可供選擇的新思路。從技術的大環境來講,它們的將來是值得期待的。

相關文章
相關標籤/搜索