前言:为什么2026年选择 Docker 部署转卡码平台?
转卡码(卡密兑换/卡券直冲)系统在源码交易、虚拟商品销售、会员充值等场景中应用广泛。传统的手动部署方式需要逐一配置 PHP 环境、Nginx、MySQL、Redis,环境依赖冲突、版本不兼容等问题频发。
2026年,Docker 容器化部署已成为转卡码平台搭建的最佳实践。本文将从零开始,手把手教你用 Docker Compose 在 Linux 服务器上一键部署完整的转卡码平台,涵盖:
- 服务器选购与基础环境配置
- Docker + Docker Compose 安装
- Nginx 反向代理与 SSL 证书
- MariaDB 主从 + Redis 缓存
- 核心转卡码系统容器化
- 自动发货与对账脚本集成
- 监控与日志采集
一、服务器选型与基础环境
1.1 服务器推荐配置
| 场景 | 配置 | 推荐厂商 |
|---|---|---|
| 入门(日活 <1000) | 2核4G / 50G SSD / 5Mbps | 阿里云轻量、腾讯云轻量 |
| 进阶(日活 1000-5000) | 4核8G / 80G SSD / 10Mbps | 腾讯云CVM、阿里云ECS |
| 高可用(日活 >5000) | 8核16G + 多节点集群 | 阿里云+香港节点混合 |
建议选择 CentOS 7.9+ 或 Ubuntu 22.04 LTS 作为宿主系统。本文示例基于 Ubuntu 22.04。
1.2 初始化服务器
SSH 登录后,先做基础安全配置:
# 更新系统并安装基础工具 sudo apt update && sudo apt upgrade -y sudo apt install -y curl wget git vim ufw # 配置防火墙 sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw --force enable # 创建部署用户(非 root 运行 Docker) sudo adduser deploy sudo usermod -aG sudo deploy su - deploy
二、安装 Docker 运行环境
2.1 安装 Docker Engine
# 官方一键安装脚本(2026年最新) curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh # 将 deploy 用户加入 docker 组 sudo usermod -aG docker deploy # 验证安装 docker --version # Docker version 27.3.1, build 9f0c9b6 # 安装 Docker Compose v2 插件 sudo apt install -y docker-compose-plugin docker compose version # Docker Compose version v2.29.7
2.2 配置 Docker 镜像加速
国内服务器建议配置镜像加速器以提升拉取速度:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.1panel.live",
"https://docker.xuanyuan.me"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
三、项目目录结构与 docker-compose.yml
3.1 创建项目目录
mkdir -p ~/card-qr-platform/{nginx,app,mariadb,redis,logs,scripts}
cd ~/card-qr-platform
3.2 docker-compose.yml 完整配置
下面是一份生产级 docker-compose.yml,包含 Nginx、PHP-FPM、MariaDB、Redis、以及转卡码核心应用:
version: '3.8'
networks:
card-qr-net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
volumes:
mariadb_data:
redis_data:
services:
# MariaDB 数据库
mariadb:
image: mariadb:10.11
container_name: card-qr-db
restart: unless-stopped
networks:
card-qr-net:
ipv4_address: 172.20.0.10
volumes:
- mariadb_data:/var/lib/mysql
- ./mariadb/init:/docker-entrypoint-initdb.d
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS:-ChangeMe123!}
MYSQL_DATABASE: card_qr
MYSQL_USER: card_qr_user
MYSQL_PASSWORD: ${DB_USER_PASS:-CardQrPass456!}
command: >
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--max_connections=500
--innodb_buffer_pool_size=1G
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# Redis 缓存与队列
redis:
image: redis:7-alpine
container_name: card-qr-redis
restart: unless-stopped
networks:
card-qr-net:
ipv4_address: 172.20.0.11
volumes:
- redis_data:/data
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
command: redis-server /usr/local/etc/redis/redis.conf
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
# PHP-FPM 运行转卡码核心应用
app:
image: php:8.3-fpm-alpine
container_name: card-qr-app
restart: unless-stopped
networks:
card-qr-net:
ipv4_address: 172.20.0.12
volumes:
- ./app:/var/www/html
- ./logs/php:/var/log/php
depends_on:
mariadb:
condition: service_healthy
redis:
condition: service_healthy
environment:
DB_HOST: mariadb
DB_PORT: 3306
DB_NAME: card_qr
DB_USER: card_qr_user
DB_PASS: ${DB_USER_PASS:-CardQrPass456!}
REDIS_HOST: redis
REDIS_PORT: 6379
APP_ENV: production
# 安装必要 PHP 扩展
command: >
sh -c "docker-php-ext-install pdo pdo_mysql mysqli
&& docker-php-ext-install bcmath
&& php-fpm"
# Nginx 反向代理
nginx:
image: nginx:1.27-alpine
container_name: card-qr-nginx
restart: unless-stopped
networks:
card-qr-net:
ipv4_address: 172.20.0.13
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./app:/var/www/html
- ./logs/nginx:/var/log/nginx
depends_on:
- app
# 自动发货队列消费者
worker:
image: php:8.3-cli-alpine
container_name: card-qr-worker
restart: unless-stopped
networks:
card-qr-net:
ipv4_address: 172.20.0.14
volumes:
- ./app:/var/www/html
- ./scripts:/scripts
depends_on:
mariadb:
condition: service_healthy
redis:
condition: service_healthy
environment:
DB_HOST: mariadb
DB_NAME: card_qr
DB_USER: card_qr_user
DB_PASS: ${DB_USER_PASS:-CardQrPass456!}
REDIS_HOST: redis
REDIS_PORT: 6379
command: sh -c "docker-php-ext-install pdo pdo_mysql mysqli bcmath
&& php /var/www/html/worker/consume.php"
⚠️ 注意:docker-compose.yml中的敏感变量建议通过.env文件注入,不要在配置中硬编码密码。
3.3 创建 .env 环境变量文件
cat > ~/card-qr-platform/.env << 'EOF' DB_ROOT_PASS=MyStrongRootPass!2026 DB_USER_PASS=CardQrSecurePass!2026 EOF chmod 600 ~/card-qr-platform/.env
四、Nginx 配置与 SSL
4.1 Nginx 站点配置
创建 ./nginx/conf.d/card-qr.conf:
server {
listen 80;
server_name cardqr.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name cardqr.yourdomain.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
root /var/www/html/public;
index index.php index.html;
# 转卡码支付回调地址特殊处理
location /api/notify/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://app:9000;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/public$fastcgi_script_name;
include fastcgi_params;
}
# 安全头
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
# 静态文件缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
access_log /var/log/nginx/card-qr-access.log;
error_log /var/log/nginx/card-qr-error.log;
}
4.2 申请 SSL 证书
使用 acme.sh 自动申请 Let's Encrypt 免费证书:
curl https://get.acme.sh | sh -s email=admin@yourdomain.com source ~/.bashrc # 申请证书(需先确保 80 端口可访问) acme.sh --issue -d cardqr.yourdomain.com --nginx # 安装证书到 Nginx 目录 acme.sh --install-cert -d cardqr.yourdomain.com \ --key-file ~/card-qr-platform/nginx/ssl/privkey.pem \ --fullchain-file ~/card-qr-platform/nginx/ssl/fullchain.pem \ --reloadcmd "docker exec card-qr-nginx nginx -s reload"
五、转卡码核心系统代码与初始化
5.1 核心数据表结构
创建一个初始化 SQL 脚本 ./mariadb/init/01-schema.sql:
CREATE TABLE IF NOT EXISTS `cards` ( `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `card_no` VARCHAR(32) NOT NULL UNIQUE COMMENT '卡密唯一编号', `card_pwd` VARCHAR(64) NOT NULL COMMENT '卡密密码', `amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '面值', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0=未售 1=已售 2=已核销 3=冻结', `order_id` VARCHAR(64) DEFAULT NULL COMMENT '关联订单号', `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, `sold_at` DATETIME DEFAULT NULL, `redeemed_at` DATETIME DEFAULT NULL, INDEX idx_status (`status`), INDEX idx_order_id (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS `orders` ( `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `order_no` VARCHAR(64) NOT NULL UNIQUE COMMENT '商户订单号', `card_id` BIGINT UNSIGNED DEFAULT NULL, `amount` DECIMAL(10,2) NOT NULL, `pay_type` VARCHAR(16) NOT NULL COMMENT 'alipay/wechat', `pay_status` TINYINT DEFAULT 0 COMMENT '0=待支付 1=已支付 2=已发货 3=已完成', `channel` VARCHAR(32) DEFAULT NULL COMMENT '支付通道标识', `notify_url` VARCHAR(512) DEFAULT NULL, `paid_at` DATETIME DEFAULT NULL, `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_order_no (`order_no`), INDEX idx_pay_status (`pay_status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5.2 自动发货核心逻辑
以下是一个简化版的自动发货 PHP 消费者脚本 ./app/worker/consume.php:
<?php // consume.php — Redis 队列消费者,监听已支付订单并自动发货 require_once __DIR__ . '/../vendor/autoload.php'; use Predis\Client as RedisClient; use PDO; $redis = new RedisClient([ 'scheme' => 'tcp', 'host' => getenv('REDIS_HOST') ?: 'redis', 'port' => 6379, ]); $pdo = new PDO( sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', getenv('DB_HOST') ?: 'mariadb', getenv('DB_NAME') ?: 'card_qr' ), getenv('DB_USER') ?: 'card_qr_user', getenv('DB_PASS') ?: 'CardQrPass456!', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] ); echo "[Worker] Card QR auto-delivery consumer started\n"; while (true) { // 从 Redis 阻塞队列获取待发货订单 $payload = $redis->brpop('queue:deliver', 5); if (!$payload) { continue; } $orderNo = $payload[1]; echo "[Worker] Processing order: {$orderNo}\n"; try { $pdo->beginTransaction(); // 查询订单 $stmt = $pdo->prepare( "SELECT id, amount FROM orders WHERE order_no = ? AND pay_status = 1 FOR UPDATE" ); $stmt->execute([$orderNo]); $order = $stmt->fetch(PDO::FETCH_ASSOC); if (!$order) { $pdo->rollBack(); continue; } // 分配一张未售卡密(原子操作) $stmt = $pdo->prepare( "SELECT id, card_no, card_pwd FROM cards WHERE status = 0 ORDER BY id ASC LIMIT 1 FOR UPDATE" ); $stmt->execute(); $card = $stmt->fetch(PDO::FETCH_ASSOC); if (!$card) { // 库存不足,标记订单异常 $pdo->exec("UPDATE orders SET pay_status = 4 WHERE order_no = '{$orderNo}'"); $pdo->commit(); // 发送告警通知 $redis->lpush('queue:alert', json_encode([ 'type' => 'out_of_stock', 'order_no' => $orderNo, 'time' => date('Y-m-d H:i:s') ])); echo "[Worker] WARN: Out of stock for order {$orderNo}\n"; continue; } // 更新卡密状态为已售 $pdo->exec("UPDATE cards SET status = 1, order_id = '{$orderNo}', sold_at = NOW() WHERE id = {$card['id']}"); // 更新订单为已发货 $pdo->exec("UPDATE orders SET card_id = {$card['id']}, pay_status = 2 WHERE order_no = '{$orderNo}'"); $pdo->commit(); echo "[Worker] OK: Order {$orderNo} → Card {$card['card_no']}\n"; // 可选:触发回调通知 $redis->lpush('queue:notify', json_encode([ 'order_no' => $orderNo, 'card_no' => $card['card_no'], 'card_pwd' => $card['card_pwd'] ])); } catch (Exception $e) { $pdo->rollBack(); echo "[Worker] ERROR: " . $e->getMessage() . "\n"; // 重新入队重试 $redis->lpush('queue:deliver', $orderNo); sleep(1); } }
🔐 生产环境中,卡密的 card_pwd 字段应使用 AES-256 加密存储,密钥独立于数据库密码之外管理。
六、一键启动与验证
6.1 启动所有服务
cd ~/card-qr-platform # 启动全部容器 docker compose up -d # 查看运行状态 docker compose ps # 查看实时日志 docker compose logs -f
6.2 健康检查
# 测试 Nginx 是否响应 curl -I https://cardqr.yourdomain.com # 测试 PHP-FPM 是否正常工作 curl https://cardqr.yourdomain.com/health.php # 检查数据库连接 docker exec card-qr-app php -r " try { new PDO('mysql:host=mariadb;dbname=card_qr','card_qr_user','CardQrPass456!'); echo 'DB OK'; } catch(Exception \$e) { echo 'DB FAIL: '.\$e->getMessage(); }"
6.3 模拟测试一笔订单
# 插入一张测试卡密 docker exec -i card-qr-db mysql -uroot -pMyStrongRootPass!2026 card_qr << 'SQL' INSERT INTO cards (card_no, card_pwd, amount) VALUES ('TEST-' || UUID_SHORT(), 'PASSWORD123', 99.00); SQL # 模拟创建一笔订单并触发发货 docker exec card-qr-app php -r " \$orderNo = 'TEST-' . date('YmdHis'); echo \"Order: {\$orderNo}\n\"; // 推送至发货队列 echo \"Push to queue...\n\"; "
七、生产环境运维锦囊
7.1 数据库定时备份
通过 cron 定时执行备份:
cat > ~/card-qr-platform/scripts/backup.sh << 'SCRIPT'
#!/bin/bash
BACKUP_DIR="/data/backups/card-qr"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
docker exec card-qr-db mysqldump \
-uroot -p"${DB_ROOT_PASS:-MyStrongRootPass!2026}" \
--all-databases --single-transaction --routines --triggers \
| gzip > "$BACKUP_DIR/card_qr_$DATE.sql.gz"
# 保留最近 30 天
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete
echo "Backup completed: card_qr_$DATE.sql.gz"
SCRIPT
chmod +x ~/card-qr-platform/scripts/backup.sh
# 添加到 crontab(每天凌晨 3 点)
(crontab -l 2>/dev/null; echo "0 3 * * * /home/deploy/card-qr-platform/scripts/backup.sh >> /var/log/card-qr-backup.log 2>&1") | crontab -
7.2 监控与告警
# 容器健康检查脚本 cat > ~/card-qr-platform/scripts/healthcheck.sh << 'SCRIPT' #!/bin/bash SERVICES="card-qr-nginx card-qr-app card-qr-db card-qr-redis card-qr-worker" for svc in $SERVICES; do STATUS=$(docker inspect -f '{{.State.Status}}' "$svc" 2>/dev/null) if [ "$STATUS" != "running" ]; then echo "ALERT: $svc is $STATUS" # 发送通知(飞书/企业微信 webhook) curl -s -X POST "https://open.feishu.cn/open-apis/bot/v2/hook/YOUR_WEBHOOK" \ -H "Content-Type: application/json" \ -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"⚠️ $svc 状态异常: $STATUS\"}}" fi done SCRIPT chmod +x ~/card-qr-platform/scripts/healthcheck.sh # 每 5 分钟执行一次 (crontab -l 2>/dev/null; echo "*/5 * * * * /home/deploy/card-qr-platform/scripts/healthcheck.sh") | crontab -
7.3 Docker 资源限制与日志轮转
在 docker-compose.yml 中为每个服务设置资源上限:
services:
app:
# ... 原有配置 ...
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
mariadb:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
同时配置系统级日志轮转:
sudo tee /etc/logrotate.d/docker-containers << 'EOF'
/var/lib/docker/containers/*/*.log {
rotate 7
daily
compress
missingok
delaycompress
copytruncate
}
EOF
八、与源码商城产品的完美集成
搭建好的转卡码平台可以与 源码商城 的产品无缝对接。我们的 转卡码系统 V3 已经内置了 Docker Compose 部署模板,支持一键启动并预置了以下功能:
总结
本文完整演示了如何在一台 Linux 服务器上,通过 Docker Compose 一键部署高可用的转卡码平台。容器化方案带来的核心收益:
- 环境隔离 — 每个服务独立容器,互不干扰
- 快速部署 —
docker compose up -d一行命令启动全部服务 - 水平扩展 — 只需增加容器副本即可应对流量增长
- 易于迁移 — 整个项目目录打包即可迁移到新服务器
如果你正在寻找一个开箱即用的转卡码系统,欢迎访问 源码商城 了解我们的专业解决方案。