參考資料
- VirtualBox 下載地址 https://www.virtualbox.org/
- Docker Desktop 下載地址 https://hub.docker.com/editions/community/docker-ce-desktop-windows
- Docker Toolbox 下載地址 https://docs.docker.com/toolbox/overview/
預(yù)備知識(shí)
- 什么是Dcoker
- 什么是Docker Machine
- 什么是Swoole
- 什么是Swoft
環(huán)境配置
- 安裝虛擬機(jī) VirtualBox
- 安裝配置 PHP
- 安裝配置 Docker
- 安裝配置 Swoft
Unbuntu上使用Docker Machine
# 查看docker machine版本
$ docker-machine -v
docker-machine version 0.16.1, build cce350d7
# 查看docker虛擬機(jī)
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
# 創(chuàng)建默認(rèn)虛擬機(jī)
$ docker-machine create default
Creating CA: /home/jc/.docker/machine/certs/ca.pem
Creating client certificate: /home/jc/.docker/machine/certs/cert.pem
Running pre-create checks...
Error with pre-create check: "This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory"
簡(jiǎn)單描述下事故現(xiàn)場(chǎng):我的物理機(jī)上安裝了VirtualBox,在VirtualBox上面安裝Ubuntu,在虛擬機(jī)上的Ubuntu系統(tǒng)上安裝Docker和DockerMachine,由于DockerMachine需要VirtualBox,因此又在Ubuntu上安裝了VirtualBox。接下來(lái)的一幕就出現(xiàn)了:
Error with pre-create check: "This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory"
什么意思?大意是:
您的這臺(tái)電腦,當(dāng)然就是Ubuntu所在VirtualBox,沒(méi)有啟用虛擬化技術(shù)VT-x/AMD-V。
真的是這樣的嗎?真正的原因是:
VirtualBox不支持在虛擬機(jī)里開(kāi)啟另一個(gè)嵌套的虛擬機(jī)。如果確實(shí)需要運(yùn)行這種嵌套的虛擬化場(chǎng)景,需要嘗試VMWare Workstation 11。
反思下,這種操作本身是不是有問(wèn)題的呢?什么是Docker Machine,它是為了在本地快速創(chuàng)建Docker容器環(huán)境用的,如果要在本地快速創(chuàng)建Docker集群環(huán)境,總不能一臺(tái)臺(tái)的手動(dòng)創(chuàng)建虛擬機(jī)吧,所以Docker Machine可以解決這個(gè)問(wèn)題。最重要的是它是為了在Mac或Windows上運(yùn)行Docker而誕生的,反觀下自己的做法。嘿嘿,不理解人家,還說(shuō)人家是錯(cuò)的。我想想應(yīng)該怎么用了,應(yīng)該是在本地的Windows系統(tǒng)上安裝Docker Machine,去管理VirtualBox上Docker,這才是正道。誤入歧途,誤入歧途,學(xué)習(xí)不就是這樣的嗎?久病成良醫(yī)...
好吧,Ubuntu上卸載掉VirtualBox吧。
# 查看VitualBox版本
$ sudo dpkg -l | grep virtualbox
# 卸載VirtualBox
$ sudo apt remove virutalbox
Windows10安裝DockerMachine
首先檢查下環(huán)境,在Windows命令行下使用systeminfo查看系統(tǒng)信息,重點(diǎn)檢查兩項(xiàng):
- Windows系統(tǒng)版本
- Hyper-V是否開(kāi)啟
$ systeminfo
OS 名稱: Microsoft Windows 10 家庭中文版
Hyper-V 要求: 虛擬機(jī)監(jiān)視器模式擴(kuò)展: 是
固件中已啟用虛擬化: 是
二級(jí)地址轉(zhuǎn)換: 是
數(shù)據(jù)執(zhí)行保護(hù)可用: 是
為什么要檢查下Windows環(huán)境呢?因?yàn)镈ocker Machine是在Docker軟件包里面的,所以在Windows上必須安裝Docker,由于Docker引擎使用定制的Linux內(nèi)核,所以在Windows下運(yùn)行Docker就需要使用虛擬機(jī)。通過(guò)Windows Docker客戶端來(lái)控制和管理虛擬機(jī)中的Docker引擎。官方提供的Docker Desktop的Windows版本是要求的:
Requires Microsoft Windows 10 Professional or Enterprise 64-bit. For previous versions get Docker Toolbox.
人家需要微軟64位Windows10專業(yè)版或企業(yè)版,如果你的系統(tǒng)不符合的話,只能使用 Docker Toolbox。
Windows10安裝Docker Toolbox
- Docker Toolbox 下載地址 https://docs.docker.com/toolbox/overview/
為避免出亂子節(jié)外生枝,我先關(guān)掉VirtulBox,然后一路Next,等待良久,終于Finish。安裝成功后會(huì)有兩個(gè)工具:
- Docker Quickstart Terminal :Docker命令行管理工具
- Kitematic:Docker的GUI圖形化界面管理工具

老規(guī)矩,GUI看看了解就行,重點(diǎn)是命令行工具。打開(kāi)Docker Quickstart Terminial,安靜的等待從Github上下載boot2docker.iso文件。不過(guò),需要注意的是,系統(tǒng)中必須提前安裝VirtualBox。

最后,裝滿集裝箱的鯨魚(yú)終于出現(xiàn)了。看標(biāo)題上出現(xiàn)了MINGW64的字眼,我應(yīng)該是什么時(shí)候提前安裝過(guò)的。所以也沒(méi)有怎么在意...

說(shuō)實(shí)話,一直不太習(xí)慣很多命令行工具,直到有一天遇到了Cmder,于是乎怎么才能在Cmder里面使用Docker命令呢?
Cmder中輸入:
$ @FOR /f "tokens=*" %i IN ('docker-machine env default') DO @%i
'docker-machine' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序
或批處理文件。
嗯~ o( ̄▽ ̄)o,為什么不行了,上次就可以啊。算了不糾結(jié),還是傳統(tǒng)手工方式靠譜,將docker安裝目錄設(shè)置到windows系統(tǒng)環(huán)境變量path中。
# 臨時(shí)僅對(duì)當(dāng)前命令行窗口有效
$ set path=%path%;C:\Program Files\Docker Toolbox
$ echo %path%
# 永久生效,`/m`是系統(tǒng)環(huán)境變量。
$ setx PATH "%PATH%;C:\Program Files\Docker Toolbox" /m
以防萬(wàn)一,檢查下,檢查下。果然沒(méi)有,但是在用戶環(huán)境變量Path里面卻找到了。算了不折騰,原始方式來(lái)吧。

來(lái)來(lái)來(lái),檢查下Docker版本驗(yàn)證下安裝成功。
$ docker -v
Docker version 18.03.0-ce, build 0520e24302
$ docker-machine -v
docker-machine version 0.14.0, build 89b8332
查看docker machine默認(rèn)安裝的虛擬機(jī)
# 查看docker安裝的虛擬機(jī)列表
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default - virtualbox Stopped Unknown
# 開(kāi)啟default默認(rèn)虛擬機(jī)
$ docker-machine start default
設(shè)置default虛擬機(jī)的共享目錄

測(cè)試共享目錄是否成功工作,在Windows共享目錄下新建文件test.txt,然后進(jìn)入default虛擬機(jī)中查看。
# 使用SSH連接進(jìn)入default虛擬機(jī)
$ docker-machine ssh default
( '>')
/) TC (\ Core is distributed with ABSOLUTELY NO WARRANTY.
(/-_--_-\) www.tinycorelinux.net
docker@default:~$
# 查看共享目錄下文件
docker@default:~$ ls -ali /share/
total 0
0 drwxrwxrwx 1 docker staff 0 Apr 19 17:00 .
11162 drwxr-xr-x 18 root root 440 Apr 19 16:57 ..
2 -rwxrwxrwx 1 docker staff 0 Apr 19 17:00 test.txt

到這里基本上告一段落,小結(jié)一下,為什么要設(shè)置共享目錄呢?
設(shè)置本地機(jī)器與VirtualBox共享目錄是為了寫(xiě)代碼用,在Windows系統(tǒng)的共享目錄下將會(huì)存在Swoft項(xiàng)目源碼,通過(guò)VirtualBox共享目錄使Windows中的代碼可以同步到default虛擬機(jī)下設(shè)置的/share目錄中,下一步的任務(wù)就使讓default虛擬機(jī)的share共享目錄與Docker容器中的項(xiàng)目代碼同樣進(jìn)行共享,這樣一層層的實(shí)現(xiàn)了在Windows上修改代碼,Docker容器中的代碼就會(huì)同步更新。
使用Docker本地搭建Swoft開(kāi)發(fā)環(huán)境
接上述,時(shí)間過(guò)去許久,我們已經(jīng)設(shè)置好共享目錄,這是第一個(gè)需要非常非常注意的地方,接下來(lái)進(jìn)入正題:
如何在Docker中安裝Swoft并搭建本地開(kāi)發(fā)環(huán)境呢?
首先安裝官方提供的Swoft項(xiàng)目工程
# 查看docker虛擬機(jī)
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default - virtualbox Stopped Unknown
# 開(kāi)啟默認(rèn)default的docker虛擬機(jī)
$ docker-machine start default
Starting "default"...
(default) Check network to re-create if needed...
(default) Windows might ask for the permission to configure a dhcp server. Sometimes, such confirmation window is minimized in the taskbar.
(default) Waiting for an IP...
Machine "default" was started.
Waiting for SSH to be available...
Detecting the provisioner...
Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.
# 查看docker的默認(rèn)虛擬機(jī)default是否開(kāi)啟
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default * virtualbox Running tcp://192.168.99.100:2376 v18.09.5
# 查看default虛擬機(jī)IP地址
$ docker-machine ip default
192.168.99.100
# 使用SSH進(jìn)入默認(rèn)default虛擬機(jī)
$ docker-machine ssh default
( '>')
/) TC (\ Core is distributed with ABSOLUTELY NO WARRANTY.
(/-_--_-\) www.tinycorelinux.net
# 查看默認(rèn)default虛擬機(jī)中所有的容器列表
docker@default:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 后臺(tái)運(yùn)行swoft容器,若本地不存在則先會(huì)去下載。
docker@default:~$ docker run -d -p 80:80 --name myswoft swoft/swoft
Unable to find image 'swoft/swoft:latest' locally
latest: Pulling from swoft/swoft
177e7ef0df69: Pull complete
9bf89f2eda24: Pull complete
350207dcf1b7: Pull complete
a8a33d96b4e7: Pull complete
bdf13640ae19: Pull complete
61a6857c7236: Pull complete
2918a8d29448: Pull complete
03d889ff4798: Pull complete
37cb0809dd09: Pull complete
dee720195aa0: Downloading [=================================================> ] 31.59MB/31.91MB
cceae20a736f: Download complete
3fa52edcdb62: Download complete
b8fce7ac7556: Download complete
29bf687e6ed1: Downloading [=============================================> ] 617.2kB/679.5kB
f26ae7635cf0: Download complete
1e4f488d1bc9: Download complete
58c2980c6e23: Download complete
feaab8c224b7: Download complete
由于網(wǎng)絡(luò)情況不同,有的下載很快,也有的會(huì)很慢。
docker@default:~$ docker pull swoft/swoft
Using default tag: latest
latest: Pulling from swoft/swoft
177e7ef0df69: Already exists
9bf89f2eda24: Pulling fs layer
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bb19cde7d20a swoft/swoft "php /var/www/swoft/…" 3 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp myswoft
$ docker exec -it myswoft bash
root@bb19cde7d20a:/var/www/swoft#
root@bb19cde7d20a:/var/www/swoft# composer install
root@bb19cde7d20a:/var/www/swoft# composer update
# 退出容器
root@bb19cde7d20a:/var/www/swoft# exit
官方Swoft項(xiàng)目安裝成功后,可在本地瀏覽器輸入網(wǎng)頁(yè)進(jìn)行查看http://192.168.99.100:80。

接下來(lái)是重點(diǎn),思路是這樣的:
首先我們將swoft的官方項(xiàng)目在docker中運(yùn)行起來(lái),然后使用composer安裝好它的依賴后。在將容器內(nèi)的swoft項(xiàng)目拷貝到宿主機(jī)中的共享目錄,記住宿主機(jī)的共享目錄通過(guò)VirtualBox已經(jīng)設(shè)置與本地Windows系統(tǒng)做了共享,這一點(diǎn)是個(gè)關(guān)鍵。接下刪除掉官方swoft項(xiàng)目,然后重新運(yùn)行并設(shè)置共享目錄的swoft,完成最終的環(huán)境搭建。
# 停止myswoft容器
docker@default:~$ docker stop myswoft
myswoft
# 將容器中的項(xiàng)目拷貝到宿主機(jī)的共享目錄下
docker@default: docker cp myswoft:/var/www/swoft /share
remove /share/swoft/.git/objects/pack/pack-1d5696b166afa70ef87c6b4ed4b9dca2bcb9cfd8.idx: operation not permitted
# 刪除容器
docker@default:~$ docker rm myswoft
此處需要注意下拷貝的語(yǔ)法
docker cp 容器名稱:容器內(nèi)源文件的路徑 宿主機(jī)目的路徑
設(shè)置宿主機(jī)與容器共享文件,注意共享的前提是docker容器中的文件地址應(yīng)該和宿主機(jī)對(duì)應(yīng)的文件是一摸一樣的。
# 建立共享目錄
$ docker run -it --rm -d -p 80:80 -v /share/swoft:/var/www/swoft --name myswoft swoft/swoft /bin/bash
# 查看當(dāng)前所有容器
docker@default:/share/swoft$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db68453f8674 swoft/swoft "php /var/www/swoft/…" 3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp myswoft
到這里,基本山已經(jīng)搭建完畢,注意最后一步很關(guān)鍵,如果設(shè)置共享目錄不能成功,則整個(gè)搭建是失敗的。在這里饒了很久很久。此時(shí),只要在本地windows的共享目錄修改文件,在docker的default虛擬機(jī)中的myswoft項(xiàng)目會(huì)同步更新,此時(shí)再次訪問(wèn)http://192.168.99.100:80。
后記:swoft環(huán)境搭建好了,下一步就要看看swoft項(xiàng)目如何使用。
這又過(guò)了一段時(shí)間了,今天碰到一個(gè)比較奇怪的文件,不知道那一步操作將已經(jīng)運(yùn)行很久的myswoft容器給整沒(méi)了。什么意思了,就是使用docker ps -a查看的時(shí)候,原來(lái)的myswoft容器現(xiàn)在已經(jīng)沒(méi)有了...
好吧,第一反應(yīng)就是給運(yùn)行起來(lái),執(zhí)行運(yùn)行命令:
docker@default: ~$ docker run -it -d -p 80:80 -v /share/swoft:/var/www/swoft --name myswoft swoft/swoft /bin/bash
docker@default:~$ docker ps -a | grep myswoft
cc81ba6cdcf0 swoft/swoft "php /var/www/swoft/…" 5 minutes ago Exited (0) 3 minutes ago myswoft
心中頓時(shí)涼涼,Exited (0) 3 minutes ago 三分鐘前已經(jīng)退出。
看看日志怎么說(shuō)的吧
docker@default:~$ docker logs --details myswoft
The server have been running!(PID:
)
恭喜你,The server have been running! 服務(wù)器正在運(yùn)行,容器已經(jīng)退出了,怎么辦???

發(fā)現(xiàn)PHPStorm上面Docker的面板,發(fā)現(xiàn)掛載數(shù)據(jù)卷旁邊有個(gè)Save按鈕,一頓狂點(diǎn)。
docker@default:~$ docker ps -a | grep myswoft
91cf88a63a99 swoft/swoft "php /var/www/swoft/…" 7 minutes ago Created myswoft
忽然發(fā)現(xiàn)容器的狀態(tài),從之前的Exited變成了Created,然并卵~
一旦開(kāi)啟容器docker start myswoft,瞬間狀態(tài)又回到了Exited,日志里面仍舊是The server have been running!。
docker@default:~$ free -mh
total used free shared buff/cache available
Mem: 989M 555M 283M 59M 151M 356M
Swap: 1.1G 349M 814M

Kitematic (Alpha)上面顯示的是一直處于運(yùn)行狀態(tài)RUNNING,可使用docker ps -a中仍舊顯示的是Exited。
改變端口也無(wú)濟(jì)于事,怎么辦呢?
現(xiàn)在查看狀態(tài)
docker@default:~$ docker logs myswoft
Could not open input file: /var/www/swoft/bin/swoft
怎么辦呢?
當(dāng)我靜靜的關(guān)閉電腦后,第二天打開(kāi)電腦,Docker被停了,里面什么都沒(méi)有了。該死!Shit,Shit,Shit...!
我的MySQ,我的Redis,一切又從0開(kāi)始,還好我寫(xiě)了日志,留了點(diǎn)備份。
問(wèn)題還是之前的問(wèn)題,還是從日志那點(diǎn)兒蛛絲馬跡下手吧。日志里面有個(gè)漏掉的地方:
The server have been running!(PID:
)
這里顯示的PID為什么是空的,記得開(kāi)啟調(diào)試模式后會(huì)在runtime文件夾下生成一個(gè)swoft.pid文件,里面記錄的是Swoft的進(jìn)程ID,這個(gè)地方很值得懷疑。不管那么多,先在環(huán)境變量中關(guān)閉調(diào)試模式,另外將runtime文件夾給刪除掉,是不是這貨的問(wèn)題很有可能,是不是啟動(dòng)后找不到PID才導(dǎo)致的,這些猜測(cè)邊做邊驗(yàn)證。
重啟docker run,OK天下太平了,Swoft又跑起來(lái)了。回頭想想,開(kāi)始并沒(méi)有注意到日志中的PID位置,才感覺(jué)無(wú)從下手,日后日志自己還是要記錄清晰些,這些細(xì)節(jié)防微杜漸引以為戒。
關(guān)于Swoft關(guān)聯(lián)本地鏡像啟動(dòng)時(shí)日志文件中出現(xiàn)的Could not open input file: /var/www/swoft/bin/swoft錯(cuò)誤,錯(cuò)誤很清晰的描述說(shuō)沒(méi)有/bin/swoft這個(gè)問(wèn)題,這個(gè)時(shí)候要問(wèn)下自己對(duì)-v參數(shù)的理解到底是什么樣的,-v是本地文件夾關(guān)聯(lián)到容器內(nèi)的文件夾,前提條件是什么,是本地文件夾內(nèi)是需要有代碼的,不然關(guān)聯(lián)到容器內(nèi)干嘛?難道讓容器會(huì)見(jiàn)代碼復(fù)制到本地嗎,注意你是在本地修改然后同步到Docker容器內(nèi)的,所以是本地首先要有代碼。到這里這個(gè)問(wèn)題的答案基本也就清楚了。
首先需要在共享文件夾中下載Swoft項(xiàng)目
$ git clone https://github.com/swoft-cloud/swoft
這里使用git clone下載的Swoft項(xiàng)目工程默認(rèn)的文件夾名稱為swoft,第一步完成后就可以了嗎?如果現(xiàn)在使用docker run命令的-v選項(xiàng)去關(guān)聯(lián),是會(huì)出現(xiàn)錯(cuò)誤的。
$ docker run -it -d -p 80:80 -v /share/swoft:/var/www/swoft --name myswoft swoft/swoft /bin/bash
Warning: require_once(/var/www/swoft/vendor/autoload.php): failed to open stream: No such file or directory in /var/www/swoft/bin/bootstrap.php on line 3
Fatal error: require_once(): Failed opening required '/var/www/swoft/vendor/autoload.php' (include_path='.:/usr/local/lib/php') in /var/www/swoft/bin/bootstrap.php on line 3
警告Warning和致命錯(cuò)誤Fatal error很清晰的告訴你項(xiàng)目的依賴沒(méi)有安裝,現(xiàn)在需要做的是進(jìn)入swoft項(xiàng)目工程中使用Composer工具安裝依賴。
$ cd swoft
$ composer install
如果安裝依賴沒(méi)有出錯(cuò),再運(yùn)行容器并關(guān)聯(lián)本地文件,按道理來(lái)說(shuō)應(yīng)該是沒(méi)有問(wèn)題的,等一等,我的Composer還是下載安裝依賴中。
$ docker run -it -d -p 80:80 -v /share/swoft:/var/www/swoft --name myswoft swoft/swoft /bin/bash
$ docker@default:/share$ docker logs myswoft
Fatal error: Uncaught Error: Class 'Swoole\Runtime' not found in /var/www/swoft/bin/swoft:7
Stack trace:
#0 {main}
thrown in /var/www/swoft/bin/swoft on line 7
問(wèn)題又來(lái)了,這個(gè)時(shí)候又出現(xiàn)了一個(gè)致命的錯(cuò)誤:Class 'Swoole\Runtime' not found in /var/www/swoft/bin/swoft:7
安裝配置
# 在docker中拉取swoft最新鏡像
$ docker pull swoft/swoft:latest
# 運(yùn)行swoft啟動(dòng)一個(gè)HTTP服務(wù)器
$ docker run -p 80:80 --name myswoft --rm -d swoft/swoft:latest
# 將myswoft容器中的項(xiàng)目文件拷貝到當(dāng)前目錄下
$ docker cp myswoft:/var/www/swoft .
# 重新啟動(dòng)容器時(shí)映射文件
$ docker stop myswoft
$ docker run -p 80:80 --name myswoft --rm -d -v /share/swoft:/var/www/swoft swoft/swoft:latest
梳理下流程:拉取鏡像->啟動(dòng)服務(wù)->拷貝鏡像->掛載文件
未完待續(xù)...