Docker运用于开发环境的坑

有一天一不小心把mac里的开发环境搞炸了,又失手升级了brew。新版本的brew把php的tap迁移了,直接放弃对php5.3的支持了。疲于编译安装,就打算直接上基于全套docker的开发环境,然后就遇到N多坑。包括但不限于:
php53配置Session | docker-compose的 link循环 | docker运行php异常缓慢

php53配置session的文件优先级

php53的session优先级是php-fpm.d/www.conf.其次是php.ini
在php.ini设置session使用memcache的时候,同时用php53,56,72来观察phpinfo(),发现56还有72都能够正确的指向了memcache,但是53还是file。
最后发现,在53的默认www.conf中,也有设置session的位置,优先级高于php.ini。

Docker Compose2个容器互相link出现错误

总结

如果2个容器各自link了对方,会出现如下错误:

$ docker-compose up
ERROR: Circular dependency between nginx and php72

解决方案是引入docker-compose的版本2中出现的network关键字,把大家都置于同一个局域网中。

业务场景

我的业务中,nginx和php-fpm分隔2个容器。(以下只展示了问题相关的关键字,业务相关的关键字都省略了)

1
2
3
4
5
6
7
8
version: ‘2’
services:
php72:
image: php:7.2-fpm
nginx:
image: nginx:alpine
links:
- php72:fpm72

然后nginx的vhost文件中的fastcgi_pass中填写fpm72:9000来挂载php72容器的php-fpm。
问题来了,正常流程是访问网站A,网站A回调向网站B请求鉴权(通过Nginx知道),网站B鉴权通过后返回网站A。
所以网站nginx发起的请求时用户

问题来了,如果网站A和鉴权网站B都在nginx容器的vhost管理下,网站A要通过网站B鉴权,那么首先要向Nginx路由处理A过来的请求给B,然后网站B鉴权通过后,就要通过Nginx来路由回网站B。
可是因为只有Nginx容器单向link了php72容器(所以Nginx容器能知道php72容器的IP地址),而php72容器并没有link向Nginx容器(所以php72容器并不知道Nginx容器的IP地址在哪),那么网站A在请求鉴权的时候,根本不知道Nginx的IP地址(实际情况会被指向127.0.0.1),导致鉴权失败。

但是如果给php72容器加上links: nginx:web,就是让php72容器也link上nginx容器,就会出现如下错误:

$ docker-compose up
ERROR: Circular dependency between nginx and php72
意思翻译过来就是,在nginx和php72容器之间存在了循环依赖,所以报错了。
背后的原因是如果要link一个容器,那个被link的容器就要先确立IP地址,但如果2个容器互相link,docker就不知道从哪个容器先开始确立IP地址。

解决方案

解决方案有2种:

  1. 把Nginx和php72放在同一个容器内,不存在link问题,直接就像平时服务器一样,自己调用本地端口就能解决问题,但是这样,Nginx和php的耦合度较高,对于docker来说不是非常好的实现。
  2. docker实例运行起来后,手动进入php72的主机,在里面加入hosts,把鉴权服务器指向nginx的IP地址。但是这样,每次新构建就会被清空,对于后期维护不利,容易遗忘。
  3. 引入docker-compose.yml的版本2及其以上版本中出现的network关键字,让大家都处于同一个网络。

详解

docker-compose文件版本2的说明,可以戳这里查看官方文档。

修改docker-compose文件为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
version: ‘2’
services:
php72:
image: php:7.2-fpm
networks:
lnmp-network:
aliases:
- fpm72
nginx:
image: nginx:alpine
networks:
lnmp-network:
aliases:
- web
- nginx
networks:
lnmp-network:

通过把php72容器和nginx容器都处于共同的lnmp-network网络(默认是网桥模式),就能够实现互访互通了。
php72容器通过http://web就能访问nginx容器(可以设定多个别名),nginx容器也能通过php72容器的别名fpm72来访问fpm72了。

One More Thing

回到上面的案例,架设鉴权服务器已经被写死了,只能是local.auth.com,而不能通过web来调用,怎么破?
可以通过添加这个容器的aliases的占位符来添加指定的域名.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# docker-compose.yml
version: ‘2’
services:
php72:
image: php:7.2-fpm
networks:
lnmp-network:
aliases:
- fpm72
nginx:
image: nginx:alpine
networks:
lnmp-network:
aliases:
- web
- nginx
- ${AUTH_HOST}
networks:
lnmp-network:

占位符的具体解释文件是在与docker-compose.yml同目录下的.env

1
2
#.env
AUTH_HOST=local.auth.com

就可以了。

Docker PHP运行速度异常缓慢

问题

我把全套开发环境都放在docker上,而网站的文件,这通过Volume的形式,挂载本机的具体文件夹。
等我把所有Docker-Compose遇到的开发坑都解决完毕了,以为可以美滋滋的向各位打开网页并炫耀我成果的时候发现。
Docker上运行的Yii2框架异常的缓慢(打开一张网站要15s),比我在本地构建的LNMP慢上1-2个数量级(本来只要2-3s)。

在我google/baidu了一圈后发现,最主要的问题是因为docker for mac在挂载本地Mac的文件夹时,由于Mac的磁盘格式,导致读写忙出翔。大家如果遇到这种情况,你打开Chrome的Network你会发现,速度慢主要是因为卡在了Waiting(TTFB)上,也就是等待服务器返回的时间。

解决方案

解决这个IO读写问题,现在普遍有3种解决方案:

  1. 直接把网站的文件也丢到docker容器里面
  2. 使用docker-sync
  3. 使用docker volume cache特性

我开宗明义的告诉大家,我的实测效果:
什么优化都没有做的时候,是15s

  1. 这种方式我并没有采用,我认为耦合度太高,而且对宿主机不可见
  2. 5s.速度较快。但是因为docker-sync是一个第三方的插件,操作很烦琐,我弄了一上午才搞出来。
  3. 6s.速度较快,docker内置的新特性,操作非常简单!但是需要升级docker到预览版(也就是edge版本)

具体的评测可以看看这篇文章3 ways to get Docker for Mac faster on your Symfony app.,但是我实测用docker-sync和cached速度相当。

所以接下来,直接介绍如何使用docker内置的Volume Cache特性。

Docker Volume Cached

  1. 切换你的Docker版本,从稳定Stable版本到Edge版本(类似国内说的开发者版本)
  2. 修改docker-compose.yml中的Volume属性,从:rw=>:cached

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
version: '2'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ${SITE_ROOT}:/var/www/html/:cached
- ./conf/nginx/conf.d:/etc/nginx/conf.d/:ro
- ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./log/nginx/:/var/log/nginx/:cached
depends_on:
- php72
networks:
lnmp-network:
aliases:
- nginx
- web
php72:
image: php:7.2-fpm
expose:
- "9000"
volumes:
- ${SITE_ROOT}:/var/www/html/:cached
- ./conf/php/php.ini:/usr/local/etc/php/php.ini:ro
- ./conf/php/php-fpm.d/php56AndHigher/www.conf:/usr/local/etc/php-fpm.d/www.conf:cached
- ./log/php-fpm/:/var/log/php-fpm/:cached
networks:
lnmp-network:
aliases:
- fpm72
networks:
lnmp-network:

就算用上了cached或者docker-sync,实际上速度也是比不上在本地直接构建的,所以还是期待docker官方能够在后续的版本内更好的解决问题。