搞事情之初識 Docker 與嘗試構建 Swift

搞事情繫列文章主要是爲了繼續延續本身的 「T」 字形戰略所作,同時也表明着畢設相關內容的學習總結。本文章是 Docker 部分的第一篇,主要是給本身解釋與 Docker 有關的內容。python

原文地址:PJ 的 iOS 開發平常linux

虛擬化和容器化技術

虛擬化技術

虛擬化技術是一種將計算機物理資源進行抽象、轉換爲虛擬的計算機資源提供給程序使用的技術。這些資源包括了 CPU 提供的運算控制資源,硬盤提供的數據存儲資源,網卡提供的網絡傳輸資源等。nginx

跨平臺

保證程序跨平臺兼容,也就是要保證操做系統或物理硬件所提供的接口調用方式一致,程序便不須要兼容不一樣硬件平臺的接口。此時忽然想到,使用 Swift 編寫 iOS app 時,構建出包後老是會帶上 Swift 的整個運行時,以保證隨着 iOS 系統版本的升級 app 的正常運行,因其 ABI 並未穩定,還不能內置在操做系統中。git

資源管理

可將虛擬化技術運用於計算機資源的管理,其中最實用的就是「虛擬內存」虛擬化技術可以提升計算機資源的使用率,是指利用虛擬化,能夠將原來程序用不到的一些資源拿出來,分享給另一些程序,讓計算機資源不被浪費。github

虛擬化技術的分類

主要分爲兩大類:硬件虛擬化軟件虛擬化web

  • 硬件虛擬化:好比假設 iOS 基於的 arm 架構 CPU 可以運行基於 x86 架構的 macOS 應用程序,這是由於 CPU 可以將另一個平臺的指令集轉換爲自身的指令集執行(但實際上並不可能)。sql

  • 軟件虛擬化:在 2018 WWDC 中,宣佈能夠在 UIKit 層面提供一部分把 iOS app 轉移到 macOS app 中的特性,能夠理解爲是 Apple 在 Xcode 層面協助開發者構建了遷移代碼,幫開發者解決了不一樣平臺指令的轉換。也就是說,軟件虛擬化其實是經過一層夾雜在應用程序和硬件平臺上的虛擬化實現軟件來進行指令的轉換。docker

其它虛擬化技術的分類:shell

  • 平臺虛擬化:在操做系統和硬件平臺間搭建虛擬化設施,使得整個操做系統都運行在虛擬後的環境中。相似 VMwarePD
  • 應用程序虛擬化:在操做系統和應用程序間實現虛擬化,只讓應用程序運行在虛擬化環境中。相似 Python 的虛擬環境;
  • 內存虛擬化:將不相鄰的內存區,甚至硬盤空間虛擬成統一連續的內存地址,即虛擬內存;
  • 桌面虛擬化:讓本地桌面程序利用遠程計算機資源運行,達到控制遠程計算機的目的。相似華爲雲的雲桌面以及各類遠程桌面控制軟件,如 Teamviewer。
  • ......

虛擬機

虛擬機一般說法是經過一個虛擬機監視器( Virtual Machine Monitor ) 的設施來隔離操做系統與硬件或應用程序和操做系統,以達到虛擬化的目的。這個虛擬機監視器,一般被稱爲:Hypervisordjango

虛擬機有一個永遠都逃不掉的問題:性能低下。這種效率的低下有時候是沒法容忍的,故真實的虛擬機程序經常不徹底遵照 Hypervisor 的設計結構,而是引入一些其它技術來解決效率低下問題,好比解釋執行、即時編譯(Just In Time)運行機制,但這些技術的引入已不屬於虛擬化的範疇了。

容器技術

按分類或者實現方式來講,容器技術應該屬於操做系統虛擬化,也就是在由操做系統提供虛擬化的支持。總的來講,容器技術指的是操做系統自身支持一些接口,可以讓應用程序間能夠互不干擾的獨立運行,並可以對其在運行中所使用的資源進行干預。

那這也不該該被稱爲「容器」呀?是的,這裏所謂的容器指的是因爲應用程序的運行被隔離在了一個獨立的運行環境之中,這個獨立的運行環境就好似一個容器,包裹了應用程序。

容器這麼火爆,火到一心撲在 iOS 上的我都要好好梳理一番,很重要的一個緣由是其在運行性能上遠超虛擬機等其它虛擬化實現,甚至在運行效率上與真實運行在物理平臺的應用程序不相上下。但注意,容器技術並無進行指令轉換,運行愛容器中的應用程序自身必須支持在真實操做系統上運行,也就是必須遵照硬件平臺的指令規則。

曾經看到一篇文章說 linux 內核命名空間的改進,直接推進了容器的最大化發展。

利用內核命名空間,從進程 ID 到網絡名稱,一切均可在 Linux 內核中實現虛擬化。新增的用戶命名空間「使得用戶和組 ID 能夠按命名空間進行映射。對於容器而言,這意味着用戶和組能夠在容器內部擁有執行某些操做的特權,而在容器外部則沒有這種特權。」Linux 容器項目 (LXC) 還添加了用戶亟需的一些工具、模板、庫和語言綁定,從而推進了進步,改善了使用容器的用戶體驗。LXC 使得用戶可以經過簡單的命令行界面輕鬆地啓動容器。(來源 redhat 官網)

容器因爲沒有虛擬操做系統和虛擬機監視器這兩個層次,大幅減小了應用程序帶來的額外消耗。因此在容器中的應用程序其實徹底運行在了宿主操做系統中,與其它真實運行在其中的應用程序在指令運行層面是徹底沒有任何區別的。

Docker 的核心組成

四大組成對象

鏡像

能夠理解爲一個只讀的文件包,其中包含了虛擬環境運行的最原始文件系統的內容。

由於 Docker 採用 AUFS 做爲底層文件系統的實現,實現了一種增量式的鏡像結構。每次對鏡像內容修改,Docker 都會將這些修改鑄形成一個鏡像層,而一個鏡像本質上是由其下層全部的鏡像層所組成的,而每個鏡像層單獨拿出來,均可以與它之下的鏡像層組成一個鏡像。正是因爲這種結構,Docker 的鏡像本質上是沒法被修改的,由於因此的鏡像修改只會產生新的鏡像,而不是更新原有的鏡像。

容器

在容器技術中,容器是用來隔離虛擬環境的基礎設施,但在 Docker 中,被引伸爲隔離出來的虛擬環境。若是咱們把鏡像理解爲類,則容器爲實例對象。鏡像內存放的是不可變化的東西,當以他們爲基礎的容器啓動後,容器內也就成爲類一個「活」的空間。

Docker 的容器應該有三項內容組成:

  • 一個 Docker 鏡像;
  • 一個程序運行環境;
  • 一個指令集合。

網絡

Docker 中可對每一個容器進行單獨的網絡配置,也可對各個容器間創建虛擬網絡,將數個容器包裹其中,同時與其它網絡環境隔離,而且 Docker 還能在容器中構造獨立的 DNS,咱們能夠在不修改代碼和配置的前提下直接遷移容器。

數據卷

在以往的虛擬機中,大部分狀況下都直接使用虛擬機的文件系統做爲應用數據等文件的存儲位置,但並未是徹底安全的,當虛擬機或容器出現問題致使文件系統沒法使用時,雖可直接經過快速的鏡像進行重製文件系統以致於恢復,但數據也就丟失了。

爲保證數據的獨立性,一般會單獨掛在一個文件系統來存放數據,得意與 Docker 底層的 Union File System 技術,咱們能夠不用管相似於搞定掛載在不一樣宿主機中實現的方法、考慮掛載文件系統兼容性、虛擬機操做系統配置等問題。

鏡像與容器

Docker 鏡像

全部的 Docker 鏡像都是按照 Docker 所設定的邏輯打包的,也是收到 Docker Engine 所控制。常見的虛擬機鏡像都是由其它用戶經過各自熟悉的方式打包成鏡像文件,公佈到網上再被其它用戶所下載後,恢復到虛擬機中的文件系統中,但 Docker 的鏡像必須經過 Docker 來打包,也必須經過 Docker 下載或導入後使用,不能單獨直接恢復成容器中的文件系統。這樣,咱們就能夠直接在服務器之間傳遞 Docker 鏡像,並配合 Docker 自身對鏡像的管理功能,使得在不一樣的機器中傳遞和共享變得很是方便。

每個記錄文件系統修改的鏡像層 Docker 都會根據它們的信息生產一個64位的 hash 碼,正是由於這個編碼,能夠可以區分不一樣的鏡像層並保證內容和編碼是一致的,咱們能夠在鏡像之間共享鏡像層。當 A 鏡像依賴了 C 鏡像,且 B 鏡像也依賴了 C 鏡像,在實際使用過程當中,AB 兩個鏡像是能夠公用 C 鏡像內部的鏡像層的。

查看鏡像

$ docker images
複製代碼

鏡像命名

能夠分爲三部分:

  • username:通常都是鏡像創做者,但若是不寫則是由官方進行維護。
  • repository:通常都是該鏡像中所包含的軟件名。但鏡像名歸鏡像名,鏡像歸鏡像,Docker 對容器的設計和定義是微型容器而不是龐大臃腫的完整環境,全部一般只會在一個容器中運行一個應用程序,可以大幅下降程序之間互相的影響,利用容器技術控制每一個程序所使用的資源。
  • tag

主進程

Docker 的設計中,容器的生命週期與容器中 PID 爲 1 這個進程由密切的關係,容器的啓動本質上能夠理解爲這個進程的啓動,而容器的中止也就意味着這個進程的中止。

寫時複製

經過鏡像運行容器時並非當即把鏡像裏全部內容拷貝到容器所運行的沙盒文件系統中,而是利用 UnionFS 將鏡像以只讀方式掛載到沙盒文件系統中,只有在容器對文件的修改時,修改纔會體現到沙盒環境上。

從鏡像倉庫得到鏡像

獲取鏡像

docker pull ubuntu
複製代碼

獲取鏡像更詳細的信息

docker inspect ubuntu
複製代碼

搜索鏡像

docker search django
複製代碼

刪除鏡像

docker rmi ubuntu
複製代碼

運行和管理容器

容器的生命週期

  • Created
  • Running
  • Paused
  • Stopped:容器的中止狀態下,佔用的資源和沙盒環境都存在,只是容器中的應用程序均已中止
  • Deleted

建立容器

$ docker create ubuntu
複製代碼

若是咱們以前選擇的 docker pull 容器並非默認的 latest 版本,而是手動選擇了一個版本,那鏡像的名字將會好比 nginx:1.12,對於後續的操做都十分的不方便,對此,咱們能夠採用 --name 進行重命名:

$ docker create --name nginx nginx:1.12
複製代碼

啓動容器

$ docker start ubuntu
複製代碼

經過 docker run 可將上述兩個命令進行合併:

$ docker run --name nginx nginx:1.12
複製代碼

以上命令跑起來的容器運行都是運行在前臺,若是咱們想要容器運行在後臺,能夠經過 -d,其是 -detach 的簡稱,告訴 Docker 在啓動後將程序和控制進行分離。:

$ docker run -d ubuntu 
複製代碼

管理容器

列出運行中的全部容器

$ docker ps 
複製代碼

列出全部容器

$ docker ps -a/-all
複製代碼

其中打印出的列表須要注意的是 STATUS 字段,常見的狀態表示有三種:

  • Create:容器已建立,沒有啓動過;
  • Up[ Time ]:容器正在運行,[ Time ] 表明從開始運行到查看時的時間;
  • Exited([ Code ]) [ Time ]:容器已結束運行,[ Code ] 表示容器結束運行時,主程序返回的程序退出碼,而 [ Time ] 則表示容器結束到查看時的時間。

中止和刪除容器

$ docker stop ubuntu
複製代碼

容器中止後,其維持的文件系統沙盒環境會一直保存,內部被修改的內容也會被保留。經過 docker start 將容器繼續啓動。

當須要把容器徹底刪除容器,可使用:

$ docker rm ubuntu
複製代碼

但在運行中的容器默認狀況下是不能被刪除的,但咱們能夠經過如下命令進行刪除:

$ docker rm -f ubuntu
複製代碼

隨時刪除容器

Docker 與其它虛擬機不一樣,其所定位的輕量級設計講究隨用隨開,隨關隨刪,當咱們短期內不須要使用容器時,最佳的作法是刪除它而不是僅僅中止它。

若是咱們要對程序作一些環境配置,徹底能夠直接將這些配置打包至一個新的鏡像中,下次直接使用該鏡像建立容器便可。對於一些重要的文件資料,不能隨着容器的刪除而刪除,可使用 Docker 中的數據卷來單獨存放。

進入容器

直接建立,進入

$ docker run -it --name ubuntu ubuntu 
複製代碼

已經建立完成,進入

$ docker exec -it ubuntu /bin/bash
複製代碼
  • -i 表示保持咱們的輸入流;
  • -t 表示啓用一個僞終端,造成咱們與 bash 的交互。

當容器運行在後臺,想要在將當前的輸入輸出流鏈接到指定的容器上,能夠這麼作:

$ docker attach ubuntu
複製代碼

經過 docker attach 啓動的容器,能夠理解爲與 docker run -d 作了相反的事情,把當前容器從後臺拉回了前臺。

爲容器配置網絡

容器網絡

Docker 網絡中,有三個比較核心的概念,造成了 Docker 的網絡核心模型,即容器網絡模型(Container Network Model)

  • 沙盒:提供容器的虛擬網絡棧。好比端口套接、IP 路由表、防火牆等;
  • 網絡:Docker 內部的虛擬子網,網絡內的參與者相互可見並可以進行通信。須要注意的是,這種虛擬網絡與宿主機存在隔離關係。
  • 端點:主要目的是造成一個能夠控制的突破封閉網絡環境的出入口,當容器的端點與網絡的端點造成配對後,就如同在這二者之間搭建了橋樑,可進行數據傳輸。

Docker 的網絡實現

目前官方提供了五種網絡驅動:

  • Bridge Driver(default):經過基於硬件或軟件的網橋來實現通信
  • Host Driver
  • Overlay Driver:藉助 Docker 的集羣模塊 Docker Swarm 來搭建的跨 Docker Daemon 網絡,能夠經過它搭建跨物理主機的虛擬網絡,從而讓不一樣物理機中運行的容器感知不到多個物理機的存在。
  • MacLan Driver
  • None Driver

網絡剩餘內容將在下篇文章中繼續進行......

搞點事情

學習到這裏後,開始對 Docker 所謂「輕量級」的主打理念有了一個初步的認識,準備利用 Docker 的這一特性作一個 Swift 編譯服務,主要想利用 Vapor/Perfect (這兩個到底選哪個還需調研)來搭建 HTTP 服務,接收傳入的代碼文本,執行並返回結果。

思考了一下,須要:

  • 具有編譯 Swift 能力的 Docker 鏡像;
  • 具有 Vapor/Perfect 框架的 Docker 鏡像;
  • 具有 Nginx web 服務器的 Docker 鏡像;

這一套下來後,將從新發佈一個「開箱即用」的提供 Swift 編譯服務的 Docker 鏡像~想一想就是個很是美好的事情呢!接下來開始第一步

構建具有編譯 Swift 能力的 Docker 鏡像

以前有看到的文章說直接能夠在 Ubuntu 上構建本身的 Swift 版本,因此個人第一步先去找一個 Ubuntu 鏡像,這點很是容易:

$ docker pull ubuntu
$ docker run -it --name ubuntu ubuntu /bin/bash 複製代碼

成功進入到 bash 後,繼續下一步。找到一個萬能命令,根據這個命令能夠先把編譯 Swift 須要的相關依賴都下載完成:

sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config
複製代碼

接着,下載 Swift 源碼:

git clone https://github.com/apple/swift.git
複製代碼

再下載項目依賴的其它源碼:

./utils/update-checkout --clone
複製代碼

完成後,便可開始利用源碼中的工具進行編譯和測試!

utils/build-script -t
複製代碼

此處將會經歷漫長的等待。二十分鐘後,我獲得了兩個報錯:

clang: error: unable to execute command: Killed
clang: error: linker command failed due to signal (use -v to see invocation)
ninja: build stopped: subcommand failed.
utils/build-script: fatal error: command terminated with a non-zero exit status 1, aborting
複製代碼
clang: error: unable to execute command: Killed
clang: error: linker command failed due to signal (use -v to see invocation)
[1747/3019] Linking CXX shared library lib/libLTO.so.7svn
FAILED: lib/libLTO.so.7svn 
複製代碼

看提示是一些依賴庫出了問題,剛開始覺得更新下對應的依賴庫就完事了,沒想到在網上竟然找到不對應的報錯提示!這對於第一次手動編譯 Swift 的玩家來講十分的不友好,折騰了一下子後放棄!

此時,又看到一篇文章有說能夠直接利用 Swift 官網已經構建完成的二進制文件進行使用,地址在此 swift.org/download/ ,在 Docker 中能夠經過 wget 進行下載。但因未找到 Swift 4.2.1 的正確下載地址,而且也擔憂直接修改以往版本下載地址進行猜想地址不對,在宿主機上下載完成後,經過 docker cp /path dockerContainer:/path 命令把文件夾傳遞到了容器中。

在添加 PATH 我又遇到了以下錯誤:

swift: error while loading shared libraries: libatomic.so.1: cannot open shared object file: No such file or directory
複製代碼
swift: error while loading shared libraries: libedit.so.2: cannot open shared object file: No such file or directory
複製代碼

幾乎已經把 SO 上全部的解決方案進行了嘗試,皆無果,有 issue 說估計是 Docker 自己的問題,折騰了好一下子,遂放棄。

當時覺得這已是最後一種方案,因此折騰了特別久,沒想到其實 Apple 官方竟然維護了一套 swift-docker,開箱即用,特別香!!!

$ docker pull swift
$ docker run --privileged -i -t --name swiftfun swift:latest /bin/bash
複製代碼

至此,第一步已經完成!這回都省去了本身構建鏡像的工做了~

Swift-Docker.png
相關文章
相關標籤/搜索