# 基于php8.x制作跨平台的workerman环境Docker镜像

> wandoubaba / 2024-10-29

## 说明

本方法基于PHP官方`8.1.30-cli`版本的镜像制作，安装好workerman开发常用的依赖和扩展，并通过`docker buildx`构建多平台的Docker镜像。

## Dockerfile文件

```dockerfile
# 拉基础镜像
FROM php:8.1.30-cli

# 替换源（本文操作时使用的是aws主机所以用的是国外源，国内操作可以换成阿里、中科大等国内源）
RUN rm /etc/apt/sources.list.d/debian.sources && \
    echo 'deb http://mirror.us.oneandone.net/debian bookworm main' > /etc/apt/sources.list && \
    echo 'deb-src http://mirror.us.oneandone.net/debian bookworm main' >> /etc/apt/sources.list && \
    echo 'deb http://mirror.us.oneandone.net/debian-security bookworm-security main' >> /etc/apt/sources.list && \
    echo 'deb-src http://mirror.us.oneandone.net/debian-security bookworm-security main' >> /etc/apt/sources.list && \
    echo 'deb http://mirror.us.oneandone.net/debian bookworm-updates main' >> /etc/apt/sources.list && \
    echo 'deb-src http://mirror.us.oneandone.net/debian bookworm-updates main' >> /etc/apt/sources.list && \
    echo 'deb http://mirror.us.oneandone.net/debian bookworm-backports main' >> /etc/apt/sources.list && \
    echo 'deb-src http://mirror.us.oneandone.net/debian bookworm-backports main' >> /etc/apt/sources.list

# 安装一些依赖
RUN apt-get update && apt-get install -y \
    libfreetype6-dev libjpeg62-turbo-dev \
    libpng-dev libwebp-dev zlib1g-dev \
    libzip-dev zip \
    libevent-dev libssl-dev \
    lsb-release libpq-dev \
    libgmp-dev \
    libmagickwand-dev \
    libzookeeper-mt-dev \
    librdkafka-dev

# 安装扩展
RUN docker-php-ext-configure gd --enable-gd --with-freetype --with-jpeg --with-webp \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install exif \
    && docker-php-ext-install opcache \
    && docker-php-ext-install pdo_mysql \
    && docker-php-ext-install mysqli \
    && docker-php-ext-install zip \
    && docker-php-ext-install pcntl \
    && docker-php-ext-install fileinfo \
    && pecl install https://pecl.php.net/get/redis-6.1.0.tgz \
    && docker-php-ext-enable redis \
    && docker-php-ext-install sockets \
    && pecl install https://pecl.php.net/get/event-3.1.4.tgz \
    && docker-php-ext-enable event \
    && cat /usr/local/etc/php/conf.d/docker-php-ext-event.ini >> /usr/local/etc/php/conf.d/docker-php-ext-sockets.ini \
    && mv /usr/local/etc/php/conf.d/docker-php-ext-sockets.ini /usr/local/etc/php/conf.d/docker-php-ext-event.ini \
    && docker-php-ext-install -j$(nproc) pgsql pdo_pgsql \
    && pecl install https://pecl.php.net/get/mongodb-1.19.3.tgz \
    && docker-php-ext-enable mongodb \
    && docker-php-ext-install -j$(nproc) gmp \
    && pecl install https://pecl.php.net/get/imagick-3.7.0.tgz \
    && docker-php-ext-enable imagick \
    && docker-php-ext-install bcmath \
    && docker-php-ext-enable bcmath \
    && pecl install https://pecl.php.net/get/xlswriter-1.5.5.tgz \
    && docker-php-ext-enable xlswriter \
    && pecl install https://pecl.php.net/get/zookeeper-1.2.1.tgz \
    && docker-php-ext-enable zookeeper \
    && pecl install https://pecl.php.net/get/rdkafka-6.0.3.tgz \
    && docker-php-ext-enable rdkafka

# 安装Composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
    && php composer-setup.php \
    && php -r "unlink('composer-setup.php');" \
    && mv composer.phar /usr/local/bin/composer \
    && composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

# 安装swow扩展
# RUN composer require swow/swow && ./vendor/bin/swow-builder --install

# 创建应用目录
RUN mkdir -p /app

# 设置工作目录
WORKDIR /app
```

> 在`替换源`那一段中，应该把其中的源地址替换成你的主机可以快速访问的源地址，不建议直接使用本文的源。

## 用`docker buildx`构建

将上面的Dockerfile脚本直接保存为`Dockerfile`文件。

### 检查`docker buildx`环境

```sh
docker info
```

执行以上命令可以看到类似下面的信息：

```txt
Client: Docker Engine - Community
 Version:    27.0.3
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.15.1
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.28.1
    Path:     /usr/libexec/docker/cli-plugins/docker-compose
...
```

一般情况下，如果是通过包管理器安装的docker engine或者docker desktop的话，默认就已经安装好了buildx和compose插件了。

### 跨平台构建并发布

接下来，我们的目标是用上面的Dockerfile构建跨平台的`php:8.1-workerman`镜像并直接推送到`quay.io`平台上：

（本文中我的quay.io账号是wandoubaba517，你要替换成你自己的账号。）

```sh
docker login quay.io
docker buildx create --use
docker buildx build --platform linux/386,linux/s390x,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 --push -t quay.io/wandoubaba517/php:8.1-workerman .
```

执行上面的命令，执行过程中会自动从docker hub上拉取名为`moby/buildkit:buildx-stable-1`的镜像并启动容器开始自动构建和推送，整个过程在我的2核4G服务器上会执行数小时，可以使用`tmux`等终端工具异步执行。

## 使用镜像

```sh
docker pull quay.io/wandoubaba517/php:8.1-workerman
docker run -itd -p 8787:8787 -v ./:/app -w /app quay.io/wandoubaba517/php:8.1-workerman
```

### docker-compose.yml

```yml
services:
  workerman:
    image: quay.io/wandoubaba517/php:8.1-workerman
    container_name: workerman
    restart: always
    volumes:
      - ./:/app
    working_dir: /app
    stdin_open: true
    ports:
      - 8787:8787
    ommand: ['php', 'start.php', 'start']
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8787/"]
      interval: 5s
      retries: 3
      start_period: 5s
      timeout: 10s
```

### composer命令脚本

> 仅适用于linux系统

```sh
#!/bin/bash

# 使用heredoc构建docker run命令
read -r -d '' DOCKER_COMMAND <<'EOF'
docker run --rm \
-v "$(pwd)":/app \
-w /app \
quay.io/wandoubaba517/php:8.1-workerman \
composer 
EOF

# 检查是否有传递参数，并将它们附加到命令的末尾
if [ $# -gt 0 ]; then
  DOCKER_COMMAND+=" $@"
fi

# 执行docker run命令
eval "$DOCKER_COMMAND"
```

把上面的脚本保存为`compose`文件，设置可执行权限：

```sh
chmod +x ./composer
```

然后就可以用`./composer`代替composer命令了。

也可以直接把这个composer脚本复制到/usr/bin/目录下以供全局使用（不建议这样，失去了多版本共存的意义）。

### php命令脚本

```sh
#!/bin/bash

# 获取脚本所在目录的绝对路径
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)

# 切换工作目录到脚本所在目录
cd "$SCRIPT_DIR" || exit 1

# 使用heredoc构建docker run命令
read -r -d '' DOCKER_COMMAND <<'EOF'
docker run --rm \
--network host \
-v "$(pwd)":/app \
-v "$(pwd)/php.ini":/usr/local/etc/php/php.ini \
-w /app \
quay.io/wandoubaba517/php:8.1-workerman \
php 
EOF

# 检查是否有传递参数，并将它们附加到命令的末尾
if [ $# -gt 0 ]; then
  DOCKER_COMMAND+=" $@"
fi

# 执行docker run命令
eval "$DOCKER_COMMAND"
```

同样要为`php`脚本赋予可执行权限

```sh
chmod +x ./php
```

然后就可以执行`./php`代替php命令了。

### 最佳实践

建议把composer脚本和php脚本都保存在项目工程目录下，另外再把容器内`/usr/local/etc/php/php-production.ini`文件也复制到工程目录下并命名为`php.ini`，在`docker-compose.yml`文件中配置卷映射

```yml
  volumes:
    - ./:/app
    - ./php.ini:/usr/local/etc/php/php.ini
```

## 其他版本

用本文中的方法可以同样制作`8.0`和`8.2`版本做镜像，但是`8.3`不行，因为`zookeeper`扩展只支持php版本`不高于8.2.999`，一般项目可能也用不上这个扩展，在`Dockerfile`中把这个扩展和相关的依赖删掉，也可以制作出`8.3`版本的镜像。

### 工程示例

<https://git.wandoubaba.com/wandoubaba/docker-webman>

```sh
git clone git@git.wandoubaba.com:wandoubaba/docker-webman.git
cd docker-webman
./composer install
./php start.php start
```
