参考资料
- 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/
预备知识
- 什么是Dcoker
- 什么是Docker Machine
- 什么是Swoole
- 什么是Swoft
环境配置
- 安装虚拟机 VirtualBox
- 安装配置 PHP
- 安装配置 Docker
- 安装配置 Swoft
Unbuntu上使用Docker Machine
# 查看docker machine版本
$ docker-machine -v
docker-machine version 0.16.1, build cce350d7
# 查看docker虚拟机
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
# 创建默认虚拟机
$ 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"
简单描述下事故现场:我的物理机上安装了VirtualBox,在VirtualBox上面安装Ubuntu,在虚拟机上的Ubuntu系统上安装Docker和DockerMachine,由于DockerMachine需要VirtualBox,因此又在Ubuntu上安装了VirtualBox。接下来的一幕就出现了:
Error with pre-create check: "This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory"
什么意思?大意是:
您的这台电脑,当然就是Ubuntu所在VirtualBox,没有启用虚拟化技术VT-x/AMD-V。
真的是这样的吗?真正的原因是:
VirtualBox不支持在虚拟机里开启另一个嵌套的虚拟机。如果确实需要运行这种嵌套的虚拟化场景,需要尝试VMWare Workstation 11。
反思下,这种操作本身是不是有问题的呢?什么是Docker Machine,它是为了在本地快速创建Docker容器环境用的,如果要在本地快速创建Docker集群环境,总不能一台台的手动创建虚拟机吧,所以Docker Machine可以解决这个问题。最重要的是它是为了在Mac或Windows上运行Docker而诞生的,反观下自己的做法。嘿嘿,不理解人家,还说人家是错的。我想想应该怎么用了,应该是在本地的Windows系统上安装Docker Machine,去管理VirtualBox上Docker,这才是正道。误入歧途,误入歧途,学习不就是这样的吗?久病成良医...
好吧,Ubuntu上卸载掉VirtualBox吧。
# 查看VitualBox版本
$ sudo dpkg -l | grep virtualbox
# 卸载VirtualBox
$ sudo apt remove virutalbox
Windows10安装DockerMachine
首先检查下环境,在Windows命令行下使用systeminfo
查看系统信息,重点检查两项:
- Windows系统版本
- Hyper-V是否开启
$ systeminfo
OS 名称: Microsoft Windows 10 家庭中文版
Hyper-V 要求: 虚拟机监视器模式扩展: 是
固件中已启用虚拟化: 是
二级地址转换: 是
数据执行保护可用: 是
为什么要检查下Windows环境呢?因为Docker Machine是在Docker软件包里面的,所以在Windows上必须安装Docker,由于Docker引擎使用定制的Linux内核,所以在Windows下运行Docker就需要使用虚拟机。通过Windows Docker客户端来控制和管理虚拟机中的Docker引擎。官方提供的Docker Desktop的Windows版本是要求的:
Requires Microsoft Windows 10 Professional or Enterprise 64-bit. For previous versions get Docker Toolbox.
人家需要微软64位Windows10专业版或企业版,如果你的系统不符合的话,只能使用 Docker Toolbox。
Windows10安装Docker Toolbox
- Docker Toolbox 下载地址 https://docs.docker.com/toolbox/overview/
为避免出乱子节外生枝,我先关掉VirtulBox,然后一路Next,等待良久,终于Finish。安装成功后会有两个工具:
- Docker Quickstart Terminal :Docker命令行管理工具
- Kitematic:Docker的GUI图形化界面管理工具
老规矩,GUI看看了解就行,重点是命令行工具。打开Docker Quickstart Terminial,安静的等待从Github上下载boot2docker.iso文件。不过,需要注意的是,系统中必须提前安装VirtualBox。
最后,装满集装箱的鲸鱼终于出现了。看标题上出现了MINGW64的字眼,我应该是什么时候提前安装过的。所以也没有怎么在意...
说实话,一直不太习惯很多命令行工具,直到有一天遇到了Cmder,于是乎怎么才能在Cmder里面使用Docker命令呢?
Cmder中输入:
$ @FOR /f "tokens=*" %i IN ('docker-machine env default') DO @%i
'docker-machine' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
嗯~ o( ̄▽ ̄)o,为什么不行了,上次就可以啊。算了不纠结,还是传统手工方式靠谱,将docker安装目录设置到windows系统环境变量path中。
# 临时仅对当前命令行窗口有效
$ set path=%path%;C:\Program Files\Docker Toolbox
$ echo %path%
# 永久生效,`/m`是系统环境变量。
$ setx PATH "%PATH%;C:\Program Files\Docker Toolbox" /m
以防万一,检查下,检查下。果然没有,但是在用户环境变量Path里面却找到了。算了不折腾,原始方式来吧。
来来来,检查下Docker版本验证下安装成功。
$ docker -v
Docker version 18.03.0-ce, build 0520e24302
$ docker-machine -v
docker-machine version 0.14.0, build 89b8332
查看docker machine默认安装的虚拟机
# 查看docker安装的虚拟机列表
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default - virtualbox Stopped Unknown
# 开启default默认虚拟机
$ docker-machine start default
设置default虚拟机的共享目录
测试共享目录是否成功工作,在Windows共享目录下新建文件test.txt
,然后进入default虚拟机中查看。
# 使用SSH连接进入default虚拟机
$ 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
到这里基本上告一段落,小结一下,为什么要设置共享目录呢?
设置本地机器与VirtualBox共享目录是为了写代码用,在Windows系统的共享目录下将会存在Swoft项目源码,通过VirtualBox共享目录使Windows中的代码可以同步到default虚拟机下设置的/share
目录中,下一步的任务就使让default虚拟机的share
共享目录与Docker容器中的项目代码同样进行共享,这样一层层的实现了在Windows上修改代码,Docker容器中的代码就会同步更新。
使用Docker本地搭建Swoft开发环境
接上述,时间过去许久,我们已经设置好共享目录,这是第一个需要非常非常注意的地方,接下来进入正题:
如何在Docker中安装Swoft并搭建本地开发环境呢?
首先安装官方提供的Swoft项目工程
# 查看docker虚拟机
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default - virtualbox Stopped Unknown
# 开启默认default的docker虚拟机
$ 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的默认虚拟机default是否开启
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default * virtualbox Running tcp://192.168.99.100:2376 v18.09.5
# 查看default虚拟机IP地址
$ docker-machine ip default
192.168.99.100
# 使用SSH进入默认default虚拟机
$ docker-machine ssh default
( '>')
/) TC (\ Core is distributed with ABSOLUTELY NO WARRANTY.
(/-_--_-\) www.tinycorelinux.net
# 查看默认default虚拟机中所有的容器列表
docker@default:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 后台运行swoft容器,若本地不存在则先会去下载。
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
由于网络情况不同,有的下载很快,也有的会很慢。
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项目安装成功后,可在本地浏览器输入网页进行查看http://192.168.99.100:80
。
接下来是重点,思路是这样的:
首先我们将swoft的官方项目在docker中运行起来,然后使用composer安装好它的依赖后。在将容器内的swoft项目拷贝到宿主机中的共享目录,记住宿主机的共享目录通过VirtualBox已经设置与本地Windows系统做了共享,这一点是个关键。接下删除掉官方swoft项目,然后重新运行并设置共享目录的swoft,完成最终的环境搭建。
# 停止myswoft容器
docker@default:~$ docker stop myswoft
myswoft
# 将容器中的项目拷贝到宿主机的共享目录下
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
此处需要注意下拷贝的语法
docker cp 容器名称:容器内源文件的路径 宿主机目的路径
设置宿主机与容器共享文件,注意共享的前提是docker容器中的文件地址应该和宿主机对应的文件是一摸一样的。
# 建立共享目录
$ docker run -it --rm -d -p 80:80 -v /share/swoft:/var/www/swoft --name myswoft swoft/swoft /bin/bash
# 查看当前所有容器
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
到这里,基本山已经搭建完毕,注意最后一步很关键,如果设置共享目录不能成功,则整个搭建是失败的。在这里饶了很久很久。此时,只要在本地windows的共享目录修改文件,在docker的default虚拟机中的myswoft项目会同步更新,此时再次访问http://192.168.99.100:80
。
后记:swoft环境搭建好了,下一步就要看看swoft项目如何使用。
这又过了一段时间了,今天碰到一个比较奇怪的文件,不知道那一步操作将已经运行很久的myswoft
容器给整没了。什么意思了,就是使用docker ps -a
查看的时候,原来的myswoft
容器现在已经没有了...
好吧,第一反应就是给运行起来,执行运行命令:
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
心中顿时凉凉,Exited (0) 3 minutes ago
三分钟前已经退出。
看看日志怎么说的吧
docker@default:~$ docker logs --details myswoft
The server have been running!(PID:
)
恭喜你,The server have been running!
服务器正在运行,容器已经退出了,怎么办???
发现PHPStorm上面Docker的面板,发现挂载数据卷旁边有个Save
按钮,一顿狂点。
docker@default:~$ docker ps -a | grep myswoft
91cf88a63a99 swoft/swoft "php /var/www/swoft/…" 7 minutes ago Created myswoft
忽然发现容器的状态,从之前的Exited
变成了Created
,然并卵~
一旦开启容器docker start myswoft
,瞬间状态又回到了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)上面显示的是一直处于运行状态RUNNING
,可使用docker ps -a
中仍旧显示的是Exited
。
改变端口也无济于事,怎么办呢?
现在查看状态
docker@default:~$ docker logs myswoft
Could not open input file: /var/www/swoft/bin/swoft
怎么办呢?
当我静静的关闭电脑后,第二天打开电脑,Docker被停了,里面什么都没有了。该死!Shit,Shit,Shit...!
我的MySQ,我的Redis,一切又从0开始,还好我写了日志,留了点备份。
问题还是之前的问题,还是从日志那点儿蛛丝马迹下手吧。日志里面有个漏掉的地方:
The server have been running!(PID:
)
这里显示的PID为什么是空的,记得开启调试模式后会在runtime
文件夹下生成一个swoft.pid
文件,里面记录的是Swoft的进程ID,这个地方很值得怀疑。不管那么多,先在环境变量中关闭调试模式,另外将runtime
文件夹给删除掉,是不是这货的问题很有可能,是不是启动后找不到PID才导致的,这些猜测边做边验证。
重启docker run
,OK天下太平了,Swoft又跑起来了。回头想想,开始并没有注意到日志中的PID位置,才感觉无从下手,日后日志自己还是要记录清晰些,这些细节防微杜渐引以为戒。
关于Swoft关联本地镜像启动时日志文件中出现的Could not open input file: /var/www/swoft/bin/swoft
错误,错误很清晰的描述说没有/bin/swoft
这个问题,这个时候要问下自己对-v
参数的理解到底是什么样的,-v
是本地文件夹关联到容器内的文件夹,前提条件是什么,是本地文件夹内是需要有代码的,不然关联到容器内干嘛?难道让容器会见代码复制到本地吗,注意你是在本地修改然后同步到Docker容器内的,所以是本地首先要有代码。到这里这个问题的答案基本也就清楚了。
首先需要在共享文件夹中下载Swoft项目
$ git clone https://github.com/swoft-cloud/swoft
这里使用git clone
下载的Swoft项目工程默认的文件夹名称为swoft
,第一步完成后就可以了吗?如果现在使用docker run
命令的-v
选项去关联,是会出现错误的。
$ 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
和致命错误Fatal error
很清晰的告诉你项目的依赖没有安装,现在需要做的是进入swoft
项目工程中使用Composer工具安装依赖。
$ cd swoft
$ composer install
如果安装依赖没有出错,再运行容器并关联本地文件,按道理来说应该是没有问题的,等一等,我的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
问题又来了,这个时候又出现了一个致命的错误:Class 'Swoole\Runtime' not found in /var/www/swoft/bin/swoft:7
安装配置
# 在docker中拉取swoft最新镜像
$ docker pull swoft/swoft:latest
# 运行swoft启动一个HTTP服务器
$ docker run -p 80:80 --name myswoft --rm -d swoft/swoft:latest
# 将myswoft容器中的项目文件拷贝到当前目录下
$ docker cp myswoft:/var/www/swoft .
# 重新启动容器时映射文件
$ docker stop myswoft
$ docker run -p 80:80 --name myswoft --rm -d -v /share/swoft:/var/www/swoft swoft/swoft:latest
梳理下流程:拉取镜像->启动服务->拷贝镜像->挂载文件
未完待续...