为什么要容器化部署
主要是为了实现弹性伸缩,降低基础资源成本。公司之前论坛应用部署在私有云上,申请了固定配置的ECS,基础资源成本较高,也无法实现弹性伸缩。
实施步骤
- docker基础知识了解。
- 选择合适的基础镜像。
- 基于基础镜像进行相关配置和代码拷贝,并进行验证。
- 编写Dockerfile,方便后续自动化部署。
docker基础知识了解
docker目前还是最主流,应用也最广泛的容器,所以还是选择docker完成本次容器化部署。docker基础知识相关可以参考:Docker教程和Get Started With Docker。可以先花一点时间通读一下,大概了解后再往后继续。
选择合适的镜像
docker镜像仓库提供了很多Linux镜像,比如CentOS、Alpine等。我们在下载的时候可以选择Linux镜像自己在增加各种配置,也可以选择社区里面比较成熟的镜像,如果选择社区镜像,建议选择下载量大和star数较多的。公司的php部署环境是Nginx+php-fpm,综合下载量和star数我选择了社区镜像richarvey/nginx-php-fpm。由于公司的php是5.7,所以选择镜像版本是php5,大家可以选择最新版本。
基于基础镜像进行相关配置和代码拷贝,并进行验证
前面选择好镜像和版本后,我们需要一台Linux机器作为部署机进行镜像制作和验证,并按CentOS Docker安装安装docker客户端。建议配置镜像加速,不然下载镜像有点慢。
首先,我们执行命令先下载镜像:
docker pull richarvey/nginx-php-fpm:php5
等待下载完成,使用命令docker images查看本地镜像:
[root@VM-0-3-centos ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
richarvey/nginx-php-fpm php5 557c302f7402 4 years ago 229MB
通过查看版本php5的镜像分层发现默认启动命令为/start.sh,即容器启动会执行start.sh脚本:
更多配置详见文档传送门,由于这个镜像里已经默认集成Nginx和php-fpm,所以建议大家详细阅读下文档说明。使用开源代码前先阅读文档是个好习惯,会少走很多弯路。
使用命令docker run -itd richarvey/nginx-php-fpm:php5启动容器,docker ps查看,下面的命令都会用到CONTAINER ID:
[root@VM-0-3-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d897f6a22b3b richarvey/nginx-php-fpm:php5 "/start.sh" 35 seconds ago Up 34 seconds 80/tcp, 443/tcp priceless_swirles
启动完成后,使用命令docker exec -it d897f6a22b3b bin/bash进入容器,查看start.sh发现默认webroot是“/var/www/html”(镜像的说明文档里也可以看到webroot的说明):
但是由于“/var/www/html”使用VOLUME共享持久化目录,导致commit镜像无法保存,所以我们这里使用自定义webroot:
下面拷贝代码文件进入webroot,使用docker复制文件命令docker cp /home/app/helloworld d897f6a22b3b:/home/app/helloworld(自行替换代码目录)。我的代码目录只有index.php,内容如下:
<?php
echo "Vincent,hello world!\n";
?>
那是怎么开机启动Nginx和php-fpm的呢?通过查看start.sh发现最后启动了守护进程supervisord:
# Start supervisord and services
/usr/bin/supervisord -n -c /etc/supervisord.conf
然后守护进程supervisord启动了Nginx和php-fpm。可以查看详细配置文件,执行命令cat /etc/supervisord.conf。
项目里有一个发邮件的任务脚本,用supervisord守护执行,所以需要修改supervisord的配置文件启动一个线程,执行命令vi /etc/supervisord.conf修改了配置文件(如没有php脚本执行请忽略这步操作)。
项目里有定时任务,需要修改vi /etc/crontabs/root增加自定义定时任务(如没有定时任务请忽略这步操作)。
下面提交我们刚刚修改的镜像,执行命令docker commit -a "Vincent" -m "php demo" d897f6a22b3b demo/php:1.0,再次查看images:
[root@VM-0-3-centos ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
demo/php 1.0 2c278fad2792 24 seconds ago 229MB
richarvey/nginx-php-fpm php5 557c302f7402 4 years ago 229MB
这次我们使用映射外部端口启动,方便在浏览器测试访问。执行命令docker run -itd -p 80:80 -e 'WEBROOT=/home/app/helloworld' demo/php:1.0:
[root@VM-0-3-centos ~]# docker run -itd -p 80:80 demo/php:1.0
fe5b68eb007c858baf25a4f13009d46bca190ad572b39b235d5ecdc66b8b9c76
[root@VM-0-3-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fe5b68eb007c demo/php:1.0 "/start.sh" 8 seconds ago Up 7 seconds 0.0.0.0:80->80/tcp, 443/tcp jolly_hodgkin
d897f6a22b3b richarvey/nginx-php-fpm:php5 "/start.sh" 2 hours ago Up 35 minutes 80/tcp, 443/tcp priceless_swirles
可以进入容器使用内置的curl 127.0.0.1验证是否可以正常访问:
[root@VM-0-3-centos app]# docker exec -it d897f6a22b3b bin/bash
bash-4.3# curl 127.0.0.1
Vincent,hello world!
浏览器访问:
以上完成的镜像可以直接用于部署,但是因为系统会有迭代,每次都这样用命令打包镜像非常麻烦,不符合最佳实践。下面我们看看Dockerfile怎么完成这些操作。
编写Dockerfile,方便后续自动化部署
在“/home/app”目录下新建Dockerfile,完整的dockerfile内容如下:
FROM docker.io/richarvey/nginx-php-fpm:php5
MAINTAINER Fisher "php demo"
# 拷贝当前目录下的php代码。非必须,因为镜像提供了配置git方式自动拉取代码
COPY helloworld /var/www/html/
# 自定义的队列文件路径添加进supervisord。定时任务添加进crontab。非必须
# RUN echo -e "[include] \nfiles = /var/www/html/commands/queue.ini" >> /etc/supervisord.conf \
# && cat /var/www/html/crontab/crontab-job.ini >> /etc/crontabs/root
执行docker build /home/app -t demo/php:1.1:
[root@VM-0-3-centos ~]# docker build /home/app -t demo/php:1.1
Sending build context to Docker daemon 16.38kB
Step 1/3 : FROM docker.io/richarvey/nginx-php-fpm:php5
---> 557c302f7402
Step 2/3 : MAINTAINER Fisher "php demo"
---> Running in 8e646f558832
Removing intermediate container 8e646f558832
---> 7a7b268156d3
Step 3/3 : COPY helloworld /var/www/html/
---> e7ac2bafe7a8
Successfully built e7ac2bafe7a8
Successfully tagged demo/php:1.1
启动镜像,执行docker run -itd -p 80:80 demo/php:1.1,然后使用浏览器访问:
总结
这次实践碰到了一些坑,主要以下几点:
- 一开始没有查看docker layers,run的时候直接使用command
/bin/bash进入容器,导致start.sh没有执行,容器没有完成初始化。 - 没有注意volume持久化webroot目录“/var/www/html”,容器复制代码文件和commit代码文件都有问题。 所以其实多看说明文档,里面各种默认配置都有说明,这样实施过程会事半功倍。