目的
做過Linux開發(fā)的工程師可能都會或多或少有過如下的經(jīng)驗:
- 需要至少兩臺開發(fā)機器,一臺Windows電腦用來運行IDE和各種工具,一臺Linux電腦用來做編譯和測試。
- 兩臺機器同步代碼很麻煩??截惾菀壮霈F(xiàn)錯漏,samba共享又很慢(一個vimgrap運行半天)。
- Linux機器下混雜著各種SDK,環(huán)境變量,臨時包,不知道什么版本的工程軟件……最后誰都不敢動這個神奇的環(huán)境,因為離開這個環(huán)境軟件就跑不起來,甚至編譯都編不過。
- 測試團隊想復(fù)制你的環(huán)境,或者你想復(fù)制測試的環(huán)境來復(fù)現(xiàn)bug?辦不到……
這篇文章就是嘗試來解決這個問題的。
- 首先,我們只需要一臺Windows電腦,并且宿主機和Linux環(huán)境共享數(shù)據(jù)。
- 其次,它不慢,至少在主流的電腦配置上和你分開跑兩臺機器差不多。
- 再次,這個環(huán)境可很容易的清理、復(fù)制、移植
這個方案不適合什么人
- “老破小”(“老”電腦,“破”硬盤,“小”內(nèi)存)
- Windows或Mac等非Linux環(huán)境的開發(fā)。或者桌面系統(tǒng)原本就是Linux。
- 喜歡折騰vagrant的,對虛擬機有偏見特別是性能和穩(wěn)定性上的偏見的,或者開發(fā)環(huán)境很單純很簡單的。
對于直接上了docker Windows版的,可以忽略VirtualBox部分。Docker目前已經(jīng)原生支持Windows,但是有兩個條件:1是必須是Windows 10 Enterprise版本,Home版不帶hyper-v;2是必須打開hyper-v選項。
我之所以采用Virtualbox方案,是為了兼容Windows10 home版,以及關(guān)掉hyper-v。因為我需要跑的一個android虛擬機跟hyper-v沖突。
再說說vagrant。vagrant其實就是在虛擬機(比如VirtualBox)的基礎(chǔ)上再封裝了一層管理腳本,以及提供了許多預(yù)配置的虛擬機鏡像文件一個命令就可以下載+安裝+啟動。本身是很好的工具,批量部署很有優(yōu)勢,我也折騰過兩天。但個人感受有幾個缺點:比如引入了不必要的復(fù)雜性,比如幾個流行的Linux發(fā)行版的鏡像更新很不及時,比如自動化腳本出錯還得手動分析問題等等。因此放棄。
思路
方案的大體思路如下:
- 首先宿主機器是一臺Windows。當然Mac甚至Linux也可以,但不在本文討論范疇。
- 用免費的Oracle VirtualBox 搭建虛擬機,安裝Linux。
- 把宿主機的一個目錄共享到虛擬機中,然后再用這個目錄創(chuàng)建一個所有容器共享的數(shù)據(jù)卷容器。以此在宿主機和各容器之間實現(xiàn)數(shù)據(jù)共享和同步。
- 開發(fā)環(huán)境,版本管理環(huán)境,SDK環(huán)境,運行環(huán)境相互之間使用不同的容器將它們各自獨立。并且這些容器可以快速的部署到測試甚至生產(chǎn)環(huán)境。
組件圖
直接上圖

- 圖中只有一個角色,就是開發(fā)者了。
- 這個角色有兩種主要用例,一是使用IDE等各種工具進行開發(fā),二是使用特定端口訪問運行環(huán)境的服務(wù)。在這里畫的是80端口的web服務(wù)。
- 將Windows下目錄C:\foo\bar\workspace共享到虛擬機內(nèi)
- 虛擬機掛載該共享目錄到/workspace
- 使用docker創(chuàng)建一個workspace數(shù)據(jù)卷容器,卷掛載的是虛擬機系統(tǒng)的/workspace
- 分別創(chuàng)建三個容器,包含運行環(huán)境,編譯環(huán)境,版本管理環(huán)境。這三個容器都把workspace容器卷作為數(shù)據(jù)卷。
- 版本管理容器負責和遠端代碼倉庫同步代碼
- 編譯環(huán)境負責把代碼編譯成可執(zhí)行程序
- 運行環(huán)境負責運行可執(zhí)行程序
- Docker會把運行環(huán)境的端口映射到虛擬機系統(tǒng)上
- 由于虛擬機配置成網(wǎng)橋模式,所以宿主機可以直接訪問該端口
詳細步驟
安裝配置VirtualBox
安裝VirtualBox:先到官網(wǎng)下載最新版本的VirtualBox安裝包和擴展包,下載地址。這里需要注意的是,擴展包的版本必須和安裝包的版本一致。
安裝擴展包:雙擊安裝Virtualbox。裝好Virtualbox后,打開主界面,點擊:菜單->管理->全局設(shè)定->擴展->添加。然后選中擴展包。
下載Alpine Linux安裝盤: 下載地址。
這里多扯兩句。Linux發(fā)行版多如牛毛,最流行的莫過于Ubuntu和CentOS。這兩個發(fā)行版網(wǎng)上中英文資料,以及問題解答都很多,可能更適合初學者選擇。我之所以選用AlpineLinux,是因為其輕量化的特點。有專門為虛擬機優(yōu)化的版本,內(nèi)核經(jīng)過裁剪,安裝盤只有幾十MB。全部安裝完不到300MB磁盤占用。啟動也很快,占用資源少,在虛擬機中后臺運行基本無感。我們基本上所有“功能”都基于Docker搭建,所以Linux宿主系統(tǒng)并不需要任何額外的功能,只要把Docker跑起來即可。另外,AlpineLinux是docker官方御用操作系統(tǒng),很多鏡像文件都基于Alpine構(gòu)建。雖然宿主系統(tǒng)和鏡像系統(tǒng)之間并無太多聯(lián)系,但在學習曲線和操作習慣上,如果兩者統(tǒng)一,效率會有有所提高。
另外提一下,目前Docker的宿主系統(tǒng)中,CoreOS也是一個很好的選擇。CoreOS是專門為Docker量身打造的操作系統(tǒng),擁有許多優(yōu)秀特性。目前阿里云也支持該系統(tǒng)。有興趣可以了解一下。
新建虛擬機:在VirtualBox新建一個虛擬機,類型選擇“Linux 2.6 / 3.x / 4.x (64-bit)”。然后創(chuàng)建一個動態(tài)擴展的硬盤,大小可以選擇100G,反正是動態(tài)擴展。創(chuàng)建完成后,手動修改如下選項:
- 使能PAE/NX
- 使能硬件加速
- 網(wǎng)卡配置成“橋接網(wǎng)卡”
- USB設(shè)置成3.0版本
- 共享文件夾添加一個工作目錄:C:\foo\bar\workspace,名稱設(shè)為“workspace”
- 光驅(qū)選擇iso文件,然后找到我們剛才下載的AlpineLinux的iso
之所以選擇基于“Linux 2.6 / 3.x / 4.x (64-bit)”進行配置,是因為VirtualBox沒有默認的AlpineLinux的配置,而這個選項比較接近我們希望的配置。如果選擇的是“Other Linux”,VirtualBox會給出一個老舊機型的配置。
網(wǎng)卡配置成“橋接網(wǎng)卡”是為了虛擬機和外面的物理網(wǎng)絡(luò)能夠相互訪問。虛擬機能獲得物理網(wǎng)絡(luò)的IP。物理網(wǎng)絡(luò)中的設(shè)備(包括Windows宿主機)可以像訪問其他物理設(shè)備一樣訪問虛擬機(機器內(nèi)部的Docker,后面會說)。
這里選擇的共享文件夾,就是上面組件圖中的共享文件夾,用于在宿主機,虛擬機和Docker之間雙向共享數(shù)據(jù)用。
安裝AlpineLinux
- 啟動虛擬機,用光盤啟動,然后用root登錄AlpineLinux,沒有密碼
- 敲入命令
setup-alpine
- 跟隨命令行向?qū)б徊揭徊綀?zhí)行
- 在選擇安裝源時,敲入“f”讓系統(tǒng)自己尋找一個最快的源
- 在選擇安裝目標盤時,敲入“sda”
- 在選擇分區(qū)類型時,敲入“sys”
- 幾分鐘就裝完了,退出光盤,重啟虛擬機
配置AlpineLinux
把網(wǎng)絡(luò)配置改成DHCP(非必須): 編輯/etc/network/interfaces,刪除eth0的固定IP配置,然后添加一行:
iface eth0 inet dhcp
配置打開軟件倉庫:編輯/etc/apk/repositories,把community源前面的注釋去掉。docker包是在community倉庫里面的,所以這步必須做。
升級系統(tǒng):執(zhí)行如下命令
apk update
apk upgrade
配置sshd:AlpineLinux默認是關(guān)閉root用戶ssh登錄的。最安全的做法是創(chuàng)建一個非root用戶。懶人可以直接打開root登錄。編輯/etc/ssh/sshd_config,加入一行:
PermitRootLogin yes
安裝Virtualbox擴展包:這步是為了使AlpineLinux能夠支持VirtualBox的共享目錄。命令:
apk add virtualbox-guest-additions virtualbox-guest-modules-virt virtualbox-guest-modules-vanilla
啟動掛載共享目錄:這步是為了讓AlpineLinux每次啟動都自動掛載VirtualBox的共享目錄。編輯/etc/fstab,加入一行
workspace /workspace vboxsf defaults 0 0
安裝Docker:如下命令安裝docker,并使其服務(wù)端隨系統(tǒng)啟動:
apk add docker
ln -s /etc/init.d/docker /etc/runlevels/default/docker
Docker國內(nèi)鏡像加速:創(chuàng)建一個文件/etc/docker/daemon.json,內(nèi)容為(如果有阿里云或者DaoCloud賬號,也可以配置成私有的加速鏈接):
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
搞定,重啟!
創(chuàng)建容器
下載鏡像
首先下載一個Alpine的基礎(chǔ)鏡像。行文時Alpine最新版本為3.8,建議按版本裝鏡像而不是拉latest,這樣以后好進行版本管理和遷移。
docker pull alpine:3.8
之所以再次選擇AlpineLinux作為容器的基礎(chǔ)鏡像,還是因為其輕量化。Alpine3.8的基礎(chǔ)鏡像僅為5MB!
創(chuàng)建數(shù)據(jù)卷容器
docker run -it --name=workspace -v /workspace:/workspace:Z alpine:3.8
這個命令的意思是創(chuàng)建一個名字為workspace的容器,作為卷掛載本地(虛擬機Linux)的目錄/workspace到容器的/workspace目錄,使用alpine作為鏡像,鏡像版本3.8。那個大寫的Z在這里沒什么作用。但是如果虛擬機宿主Linux是CentOS這類啟用了SELinux的系統(tǒng),必須加這個參數(shù),要不然容器是無法讀寫這個目錄的!
創(chuàng)建SDK容器
這里以Golang為例,dockerhub上直接有官方的golang鏡像,所以直接pull然后run即可
docker pull golang:1.10.3-alpine3.8
docker run -it --rm --volumes-from workspace golang:1.10.3-alpine3.8 [your commands...]
創(chuàng)建SCM容器
這里以git為例。git是個很輕量級的工具,我們可以自己制作一個鏡像。
首先編寫Dockerfile,這個文件的作用就跟C/C++的Makefile差不多。類容如下:
FROM alpine:3.8
MAINTAINER Cary Tan tx-cary@163.com
ENV PS1='[git@docker $PWD]\$ '
RUN echo "http://mirrors.tuna.tsinghua.edu.cn/alpine/v3.8/main" > /etc/apk/repositories \
&& echo "http://mirrors.tuna.tsinghua.edu.cn/alpine/v3.8/community" >> /etc/apk/repositories \
&& apk update \
&& apk add git git-doc \
&& mkdir -p /workspace
WORKDIR /workspace
CMD /bin/sh
然后執(zhí)行如下命令構(gòu)建并上傳鏡像到你的dockerhub。
docker build -t <your_name>/git:<tag> /path/to/your/Dockerfile/
docker push <your_name>/git:<tag>
用SCM鏡像創(chuàng)建一個容器去下載某項目的代碼:
docker run -it --rm --volumes-from workspace <your_name>/git:<tag> git clone ...
創(chuàng)建RUN容器
RUN鏡像的環(huán)境根據(jù)項目不同,區(qū)別會很大。有時候可能還需要很多images/container一起協(xié)同工作。我在這里就舉一個最簡單的,用golang做的一個http hello world。
- 首先,在windows機器的workspace目錄下創(chuàng)建一個叫g(shù)ohttp的子目錄,然后創(chuàng)建一個原文件main.go。源碼如下:
package main
import (
"fmt"
"net/http"
)
const (
host string = ":80"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello world!")
})
fmt.Println("Listening ", host)
http.ListenAndServe(host, nil)
}
- 然后用golang的SDK鏡像創(chuàng)建容器進行編譯:
docker run -it --rm --volumes-from workspace golang:1.10.3-alpine3.8 go build -o /workspace/bin/gohttp /workspace/gohtt
p/main.go
- 用alpine鏡像創(chuàng)建一個運行容器,也即我們使用原始alpine鏡像作為我們的RUN鏡像:
docker run -it -p 80:80 --volumes-from workspace alpine:3.8 /workspace/bin/gohttp
以上關(guān)鍵參數(shù)是“-p 80:80”。意思是將容器內(nèi)的80端口映射到宿主機(虛擬機中的Linux系統(tǒng))上。
此時,順利的話,我們可以看到終端打印
Listening :80
- 最后,只需打開Windows上的瀏覽器,輸入虛擬機IP地址,就可以驗證我們的Hello World了!
自動啟動并登錄虛擬機
用過vagrant的都知道,這個工具最方便的地方就是只需要"vagrant up"和"vagrant ssh"兩條命令就可以自動啟動虛擬機并登錄進去。但我們也知道,vagrant其實就是基于Virtualbox命令行封裝的,所以我們只需要把關(guān)鍵命令找到,也可以在沒有vagrant的情況下實現(xiàn)類似的便捷。
我們得選擇一個趁手的終端軟件。我最喜歡的是MobaXTerm,并且是付費用戶。所以這里以MobaXTerm為例,直接使用cygwin,Win10自帶bash或ubuntu等應(yīng)該都能實現(xiàn)相同的功能。
新建一個Shell session,類型是“Bash”。在“Advanced Shell settings”中碼入如下腳本
/drives/c/Program\ Files/Oracle/VirtualBox/VBoxManage.exe startvm "alpine" --type headless
sleep 30
ssh -o ConnectTimeout=30 root@<IP addr>
/drives/c/Program\ Files/Oracle/VirtualBox/VBoxManage.exe controlvm alpine acpipowerbutton
保存,雙擊。這個腳本會自動啟動名為"alpine"的虛擬機,等待30秒,然后ssh登錄到虛擬機IP地址。當用戶退出ssh后,腳本自動關(guān)閉虛擬機。
安利一下MobaXTerm,這個終端軟件是基于cygwin的,功能十分強大。不僅支持SSH、Telnet、串口,還支持CMD,Powershell,bash等終端。甚至還支持VNC,F(xiàn)TP,SFTP,遠程桌面……不僅是客戶端軟件,它還內(nèi)置了各種服務(wù)器軟件,包括Xserver,TFTP,F(xiàn)TP,HTTP,SSH,Telnet,NFS,VNC……這款軟件大部分功能都是免費的,免費版的限制一個是session的數(shù)量有限制,二是各種server啟動時間有限制。