基于 Docker 的 PHP 项目完整容器化教程
后知后觉 暂无评论

随着容器技术的成熟及火热,博主也从最初的无感变为拥趸,于是在某次更换服务器时直接将 PHP 项目(博客)全部容器化,本文记录此过程。

概述

容器的出现拯救了众多运维和开发人员,将其从复杂的运维工作中解放出来,让其专注于代码和功能实现。

容器实现了快速部署和高移植性(类似于 Java)只要支持 Docker 的宿主机不论环境如何都能产生相同的效果。

实际上容器最适合部署无永久数据存储的纯运行环境类项目,目前基于性能等方面考虑,并不推荐数据库容器化。不过随着容器技术的提高,相信性能等瓶颈终将消失。

过程

应他人要求,本文记录了 Typecho (PHP 项目)引擎的博客迁移至容器的过程。

部署基础环境

在 CentOS/RadHat 上安装 Docker 可以参考 从零开始的 Docker 使用教程

因本环境使用 Debian 系统,因此仅写 Debian 的部署方式。其他发行版请替换包管理器相关命令,容器向命令并无区别。

目前 Docker CE 支持 Debian 9/10 等系统,本文基于 Debian 10 Buster 系统进行操作。

移除旧版本

apt remove docker docker-engine docker.io containerd runc
小贴士:容器的工作目录 /var/lib/docker/ 可以选择手动删除,注意备份旧的镜像等。

安装 Docker CE

更新源并安装依赖

apt update && apt install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg2 \
    software-properties-common

添加官方源密钥

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -

添加仓库

add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"
小贴士:此命令会将官方源添加至 /etc/apt/sources.list ,实际上推荐将其单独存放至 /etc/apt/sources.list.d/docker.list

安装 Docker CE

apt install docker-ce docker-ce-cli containerd.io

修改系统启动参数

Debian 系统为了使用内存限制功能,请查看 解决 Docker 启动容器时内存限制失效警告问题。其他发行版请忽略此部分内容。

容器部署

将容器所需文件及逻辑卷准备好

本文文件结构

ll /usr/mystack/
.
├── backups # 备份
├── data    # 数据库卷
├── www     # 网站文件
│   └── html
├── nginx   # 服务配置
│   ├── cert.d
│   ├── conf.d
│   ├── logs.d
│   └── nginx.conf
├── php     # 服务配置
│   ├── php.ini
│   └── www.conf
├── sbin    # 可执行文件
└── scripts # 脚本文件

拉取镜像

为了安全起见,本文全部镜像使用官方镜像进行构建。

docker pull redis:5-alpine
docker pull nginx:stable-alpine-perl
docker pull php:7-fpm-alpine
docker pull mariadb:latest
docker pull memcached:1-alpine

构建私有网络

容器间互联之前使用 --link 现在此特性已经废弃,在未来的版本可能去除,因此推荐不要使用和依赖此功能,本文使用私有网络来进行容器互联。

docker network create backend
小贴士:创建私有网络(类似于 IaaS 中的 VPC)后,容器之间即可使用容器 Alias 进行通讯( --name 参数中的名称)即可使用,比如数据库可用 mymdb:3306 进行连接。

构建容器

启动数据库

docker run --name mymdb --memory=512m --restart=always --network=backend \
-p 127.0.0.1:3306:3306 \
-v /usr/mystack/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=password -d mariadb:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
小贴士:字符集使用 utf8mb4 为了使用 emoji 表情,监听至本地环回是为了使用工具等可连接,方便导入数据。生产环境上可以不将端口挂载出来。

启动网页服务

docker run --name myngx --memory=64m --restart=always --network=backend \
-p 80:80 -p 443:443 \
-v /usr/mystack/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /usr/mystack/nginx/conf.d:/etc/nginx/conf.d \
-v /usr/mystack/nginx/logs.d:/etc/nginx/logs.d \
-v /usr/mystack/nginx/cert.d:/etc/nginx/cert.d \
-v /usr/mystack/www:/var/www -d nginx:stable-alpine-perl
小贴士:限制内存 128M 实际测试时 10 个工作进程大约占用 12M 内存,因此可以适当削减其内存限制阈值。

启动 PHP 服务

docker run --name myphp --memory=512m --restart=always --network=backend \
-v /usr/mystack/php/php.ini:/usr/local/etc/php/php.ini:ro \
-v /usr/mystack/www:/var/www -d php:7-fpm-alpine
小贴士:启动 PHP 时内存限制推荐调整高一些,若系统内存足够时,可以多添加工作进程。

启动 Memcached 服务

docker run --name mymcd --memory=64m --restart=always --network=backend \
-p 127.0.0.1:11211:11211 \
-d memcached:1-alpine
小贴士:memcached 用来缓存 session 内存限制可以根据需要增加或者较减少。

容器优化

此时启动的容器组是无法工作的(报错 500),因为官方的 PHP 镜像内扩展不全,需要安装额外组件,没有数据库相关扩展。

先进入容器

docker exec -it myphp /bin/sh

安装数据库支持及基础依赖 GD 扩展(图片处理相关)

docker-php-ext-install pdo_mysql pdo opcache

apk add --no-cache libxml2-dev libpng-dev

docker-php-ext-install gd xml xmlrpc

安装 igbinary 及 msgpack 扩展

apk add --no-cache autoconf build-base

pecl install igbinary
docker-php-ext-enable igbinary

pecl install msgpack
docker-php-ext-enable msgpack

安装 memcached 及 redis 扩展

apk add --no-cache libmemcached \
    libmemcached-libs \
    libmemcached-dev \
    build-base \
    zlib-dev \
    php7-dev \
    autoconf \
    cyrus-sasl-dev

pecl install memcached
docker-php-ext-enable memcached
pecl install redis
docker-php-ext-enable redis
小贴士:编译相关的依赖安装完成后可以删除,使用 apk del 命令即可,安装扩展后需要重启容器才能生效。

迁移项目

将项目文件放入网站目录( /usr/mystack/www/ 中),修改数据库连接方式。将数据库表放入共享的目录中,导入到容器数据库中。或者也可以使用工具远程连接导入(推荐)。

架构优化

将 PHP 的 session 存放到 memcached 中,修改 PHP 的参数。

session.save_handler = memcached
session.save_path = "mymcd:11211"
注意:很多文章中都说 save_path 写为 tcp://127.0.0.1:11211 ,这是不对的!只有 memcache 需要写明协议,memcached 并不需要。

之所以前面将 memcached 监听至本地环回,也是为了在机器上可以连接进行调试。

检查缓存是否成功,使用 stats items 即可查询全部 keys ,类似于 redis 中的 keys *

# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
stats items
STAT items:3:number 1
STAT items:3:number_hot 0
STAT items:3:number_warm 0
STAT items:3:number_cold 1
STAT items:3:age_hot 0
STAT items:3:age_warm 0
STAT items:3:age 13
STAT items:3:evicted 0
STAT items:3:evicted_nonzero 0
STAT items:3:evicted_time 0
STAT items:3:outofmemory 0
STAT items:3:tailrepairs 0
STAT items:3:reclaimed 0
STAT items:3:expired_unfetched 0
STAT items:3:evicted_unfetched 0
STAT items:3:evicted_active 0
STAT items:3:crawler_reclaimed 0
STAT items:3:crawler_items_checked 0
STAT items:3:lrutail_reflocked 0
STAT items:3:moves_to_cold 1
STAT items:3:moves_to_warm 0
STAT items:3:moves_within_lru 0
STAT items:3:direct_reclaims 0
STAT items:3:hits_to_hot 0
STAT items:3:hits_to_warm 0
STAT items:3:hits_to_cold 0
STAT items:3:hits_to_temp 0
END
小贴士:需要先使用浏览器打开浏览器访问后后再查询键值对。

查询容器挂载的存储卷

docker inspect --format='{{json .Mounts}}' 容器名 | python3 -m json.tool

附录

相关链接

参考链接

本文撰写于一年前,如出现图片失效或有任何问题,请在下方留言。博主看到后将及时修正,谢谢!
禁用 / 当前已拒绝评论,仅可查看「历史评论」。