# 单机部署方案

## 约定

* 操作系统：Centos 7.0 x64
* 域名: 文档内域名规划参见 [系统部署/准备/域名](https://github.com/bryantyan/espier-docs/tree/f1b72d96424898684552cce9df5b400d91b18a9b/deploy/deploy/prepare/domain.md)
* 操作权限: root

## 服务清单

* supervisorctl > 3.0
* neo4j 3.5.x
* mysql 5.7
* nginx + fpm
* npm > 6.0
* redis \~ 4.x
* php 7.2

## 基础服务部署

### 调整时间

```
timedatectl set-timezone Asia/Shanghai
```

### 安装sudo

```
yum install sudo
```

### 安装PHP

```
yum -y install epel-release
yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum install yum-utils
yum-config-manager --enable remi-php72

yum install php72 \
            php72-php-gd \
            php72-php-json \
            php72-php-mbstring \
            php72-php-mysqlnd \
            php72-php-xml \
            php72-php-opcache \
            php72-php-bcmath \
            php72-php-pecl-swoole \
            php72-php-pecl-mongodb \
            php72-php-pecl-zip \
            php72-php-pecl-redis4 \
            php72-php-pcntl \
            php72-php-pecl-apcu

ln /opt/remi/php72/root/usr/bin/php /usr/bin/php
```

验证

```
php --version
php --modules
```

### 安装composer

```
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
/usr/bin/composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
```

### 安装supervisor

版本对于3.0

安装supervisor

```
yum install -y epel-release
yum install -y supervisor
```

开启supervisorctl

```
systemctl enable supervisord.service
```

### 创建用户和组

```
groupadd www
useradd --shell /sbin/nologin -g www www
```

### 安装php-fpm

安装php-fpm

```
yum install php72-php-fpm
```

开启php-fpm

```
systemctl enable php72-php-fpm.service
```

修改用户权限, 将用户和组从apache 改为www

```
/etc/opt/remi/php72/php-fpm.d/www.conf
```

```
user = www
group = www
```

### 安装npm

1. 添加**Node.js** **Yum**仓库

   ```
   curl -sL https://rpm.nodesource.com/setup_12.x | bash -
   ```
2. 安装**Node.js**

   ```
   yum install -y nodejs
   ```
3. 检查**Node.js**和**NPM**版本

   ```
   node -v

   v12.6.0
   Also, check the version of npm.

   npm -v

   6.9.0
   ```

参考: [How To Install Latest Nodejs on CentOS/RHEL 7/6](https://tecadmin.net/install-latest-nodejs-and-npm-on-centos/)

### 安装nginx

安装nginx

```
yum install nginx
```

开启nginx

```
systemctl enable nginx.service
```

### 安装crontabs

```
yum install crontabs
```

```
systemctl enable crond.service
```

```
systemctl start crond.service
```

### 安装redis

安装redis

```
yum install -y redis
```

开启redis

```
yum enable redis
```

修改配置文&#x4EF6;**/etc/redis.conf**

```
# requirepass foobared
requirepass    #指定密码为 redis1234
```

启动redis

```
systemctl start redis
```

测试

```
redis-cli -h 127.0.0.1 -a redis1234
keys *
```

### 安装neo4j

1. 导入签名并且添加neo4j的源到本地centos源中

   ```
   rpm --import https://debian.neo4j.org/neotechnology.gpg.key
   cat <<EOF>  /etc/yum.repos.d/neo4j.repo
   [neo4j]
   name=Neo4j RPM Repository
   baseurl=https://yum.neo4j.org/stable
   enabled=1
   gpgcheck=1
   EOF
   ```
2. 安装neo4j
   * 用**root**权限安装neo4j社区版

     ```
     yum install neo4j-3.5.7
     ```
3. 运行以下命令会返回已安装的Neo4j的版本:

   ```
   rpm -qa | grep neo4j
   ```
4. 修改默认**密码**

   生产环境使用时需要修改初始密码, 否则会安装使用时报错.

   ```
   # neo4j-admin set-initial-password <password>
   neo4j-admin set-initial-password neo4j1234
   ```

### 安装mysql

{% hint style="info" %}
如果使用云方提供的RDS, 请忽略安装
{% endhint %}

添加mysql7的源到本地centos源中

```
yum install -y https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm
```

安装mysql

```
yum install -y mysql-server
```

修改配置文&#x4EF6;**/etc/my.conf**(配置位置取决于安装方式)

{% hint style="info" %}
如果使用阿里云的RDS可以忽略此步骤
{% endhint %}

```
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
```

开启mysql

```
systemctl enable mysqld
```

启动mysql

```
systemctl start mysqld
```

在安装过程中，为MySQL**root**用户生成一个临时密码

```
grep 'temporary password' /var/log/mysqld.log
```

输出:

```
2016-12-01T00:22:31.416107Z 1 [Note] A temporary password is generated for root@localhost: mqRfBU_3Xk>r
```

请记录下密码, 在此例里中为**mqRfBU\_3Xk>r**

```
mysql_secure_installation
```

会提示你输入默认的**root**密码, 输入好后, 会被要求改掉密码.

输出

```
The existing password for the user account root has expired. Please set a new password.

New password:
```

输入一个新的12个字符的密码，该密码至少包含一个大写字母、一个小写字母、一个数字和一个特殊字符。在提示时重新输入.

在此例中设置为, **Wka25ijklmng0ada-x**

您将收到关于新密码强度的反馈，然后您将立即被提示再次更改密码.

输出

```
Estimated strength of the password: 100
Change the password for root ? (Press y|Y for Yes, any other key for No) :
```

我们将按下**Y**，然后进入所有后续问题，以便删除匿名用户、禁止远程根登录、删除测试数据库并访问它，并重新加载特权表.

命令行连接**mysql**

```
mysql -uroot -p"Wka25ijklmng0ada-x"
```

创建应用数据库

```
CREATE DATABASE espier_bloated CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```

参考: [How To Install MySQL on CentOS 7](https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-centos-7)

## 系统应用部署

部署几部分

商家端(VUE) + API端

### 配置授权

安装swoole-loader

确认PHP扩展目录

```
# php -r 'phpinfo();' | grep -i '^extension_dir'

extension_dir => /opt/remi/php72/root/usr/lib64/php/modules => /opt/remi/php72/root/usr/lib64/php/modules
```

本例中&#x4E3A;**/opt/remi/php72/root/usr/lib64/php/modules**

```
curl https://business.swoole.com/static/loader2.0.0/swoole_loader72.so > /opt/remi/php72/root/usr/lib64/php/modules/swoole_loader72.so
```

php扩展配置文件位置

```
# php -r 'phpinfo();' | grep -i 'Scan this dir for additional'

Scan this dir for additional .ini files => /etc/opt/remi/php72/php.d
```

设置授权

{% hint style="info" %}
注意配置license的正确位置, 默认license.zl在代码包(espier-bloated)根目录下
{% endhint %}

```
{ \
    echo "extension=swoole_loader72.so"; \
    echo "swoole_license_files=/var/www/html/license.zl";  \
} > /etc/opt/remi/php72/php.d/60-swoole_loader.ini
```

重启fpm

```
systemctl enable php72-php-fpm.service
```

### 部署API服务(php)

#### 配置

后端代码

```
mkdir -p /var/www/espier-bloated
chown -R www:www /var/www/espier-bloated
```

放置代码&#x5230;**/var/www/espier-bloated**目录中

修改配&#x7F6E;**.env**, 拷&#x8D1D;**.env.production**文件做为模版.

1. 配置数据库

   ```
   DB_HOST=127.0.0.1
   DB_PORT=3306
   DB_DATABASE=espier_bloated
   DB_USERNAME=root
   DB_PASSWORD=Wka25ijklmng0ada-x
   ```
2. 配置REDIS

   ```
   # REDIS 相关配置
   REDIS_CLIENT=predis
   REDIS_HOST=redis-ali-espier-master
   REDIS_PASSWORD=redis1234
   REDIS_MEMBERS_PORT=6379
   ```
3. 配置队列

   ```
   QUEUE_DRIVER=redis
   ```
4. 配置neo4j

   ```
   NEO4J_DEFAULT_PROTOCOL=bolt
   NEO4J_DEFAULT_HOST=127.0.0.1
   NEO4J_DEFAULT_PORT=7687
   NEO4J_DEFAULT_USERNAME=neo4j
   NEO4J_DEFAULT_PASSWORD=neo4j1234
   ```
5. 配置缓存

   ```
   CACHE_DRIVER=redis
   ```
6. 配置七牛

   [如何获取Access Key和Secret Key](https://developer.qiniu.com/af/kb/1479/how-to-access-or-locate-the-access-key-and-secret-key)

   [icon.png](https://github.com/bryantyan/espier-docs/tree/f1b72d96424898684552cce9df5b400d91b18a9b/test.md)

   ```
   # 图片CDN域名
   QINIU_IMAGE_DOMAIN=b-img-cdn.yuanyuanke.cn
   # 图片bucket
   QINIU_IMAGE_NAME=espier-images
   # 导入导出文件CDN域名
   QINIU__FILE_DOMAIN=https://b-import-cdn.yuanyuanke.cn
   导入导出bucket
   QINIU_FILE_NAME=espier-import-files
   # 七牛Access Key
   QINIU_ACCESS_KEY=
   # 七牛Secret Key
   QINIU_SECRET_KEY=
   ```
7. 配置微信开放平台第三方平台相关配置

   需要根据实际

   ```
   # 微信开放平台对应第三方平台APPID
   WECHAT_APPID=
   # 微信开放平台对应第三方平台APPSECRET
   WECHAT_SECRET=
   # 微信开放平台对应第三方平台 消息校验Token
   WECHAT_TOKEN=
   # 微信开放平台对应第三方平台 消息加解密Key
   WECHAT_AES_KEY=
   WECHAT_DEBUG=true
   ```
8. 配置商城小程序模版

   模版ID, 需要添加到小程序模版库，获得模版ID, 参考:[开发者平台小程序模板](https://developers.weixin.qq.com/community/develop/doc/000c6c3524cff06575466ccd35ec00)

   ```
   # 商城小程序模版相关配置
   # 小程序模版ID号
   YYKWEISHOP_TEMPLATE_ID=
   # 小程序模版版本
   YYKWEISHOP_VERSION=
   # request合法域名
   YYKWEISHOP_REQUESTDOMAIN=https://b.yuanyuanke.cn
   # socket合法域名
   YYKWEISHOP_WSREQUESTDOMAIN=wss://b-websocket.yuanyuanke.cn
   # uploadFile(上传文件)合法域名
   YYKWEISHOP_UPLOADDOMAIN=https://b.yuanyuanke.cn
   # downloadFile(下载文件)合法域名
   YYKWEISHOP_DOWNLOADDOMAIN1=https://mmbiz.qpic.cn
   YYKWEISHOP_DOWNLOADDOMAIN2=https://b.yuanyuanke.cn
   YYKWEISHOP_DOWNLOADDOMAIN3=https://wx.qlogo.cn
   ```
9. 配置腾讯位置服务

{% hint style="info" %}
腾讯位置服务需要申请账号配置KEY, KEY对接口的访问是有配额的, 配额内是免费的.
{% endhint %}

在[腾讯位置服务Key管理](https://lbs.qq.com/console/mykey.html?console=mykey), 可以查询到Key, 并且可以查询到配额.

{% hint style="danger" %}
为了开发环境方便, 提供只为开发环境公用的免费KEY. 因为是公共的, 无法保证一定可用.
{% endhint %}

```
QQMAP_KEY=PSPBZ-KQ5CW-CSGRF-ON2S4-K2HQJ-XEBQG
```

1. 配置支付通知接口

   ```
   WECHAT_PAYMENT_NOTIFY=https://b.yuanyuanke.cn/wechatAuth/wxpay/notify
   ```

完整的例子

```
# 标识应用环境, 本地环境设置为local
APP_ENV=local
# 调试模式, 正式环境可以关闭
APP_DEBUG=true
# 时区不要修改
APP_TIMEZONE=PRC

# 数据库相关配置
# DB_CONNECTION默认不需要改
DB_CONNECTION=default
# 数据库主机
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=espier_bloated
DB_USERNAME=root
DB_PASSWORD=Wka25ijklmng0ada-x

# 缓存驱动, 默认使用redis
CACHE_DRIVER=redis
# 队列驱动, 默认使用redis
QUEUE_DRIVER=redis

# 七牛相关配置
QINIU_IMAGE_DOMAIN=b-img-cdn.yuanyuanke.cn
QINIU_IMAGE_NAME=espier-image
QINIU__FILE_DOMAIN=https://b-import-cdn.yuanyuanke.cn
QINIU_FILE_NAME=espier-file
QINIU_ACCESS_KEY=
QINIU_SECRET_KEY=

# 短信会用到, 如果需要商派短信, 误删
PRISM_URL=http://openapi.shopex.cn/api
PRISM_KEY=7zfgrchh
PRISM_SECRET=ykiy27tqhlnbn6ndaddr

# JWT 相关配置, 可根据需要调整或不调整
JWT_SECRET=ker4H1sp4TsdDvcVJMa72SMW8Zsh3drv
JWT_TTL=300
JWT_REFRESH_TTL=20160

# 微信第三方平台设置. 本地开发环境不配置, 会影响到调用微信的相关页面, 整体不影响后台开发.
WECHAT_APPID=
WECHAT_SECRET=
WECHAT_TOKEN=
WECHAT_AES_KEY=
WECHAT_DEBUG=true

# 支付通知接口
WECHAT_PAYMENT_NOTIFY=https://b.yuanyuanke.cn/wechatAuth/wxpay/notify

# dingo标准配置, 根据实际场景进行配置
API_PREFIX=api
API_NAME="Espier API"
API_STANDARDS_TREE=vnd
API_STRICT=false
API_DEBUG=false
API_VERSION=v1
API_SUBTYPE=espier
API_CONDITIONAL_REQUEST=false
API_TOKEN=Os6Bass1oT5vig2Yod0yiT8dU0as5cIn

RPCCALL_DRIVER=dingo

# 腾讯地图接口KEY, 建议根据项目进行申请
QQMAP_KEY=PSPBZ-KQ5CW-CSGRF-ON2S4-K2HQJ-XEBQG

# REDIS 相关配置
REDIS_CLIENT=predis
REDIS_HOST=redis-ali-espier-master
REDIS_PASSWORD=redis1234
REDIS_MEMBERS_PORT=6379


LICENSE_PRODUCTION_URL=https://service.ec-os.net/api/yyk/register
LICENSE_PRODUCT=yyk

# 商城小程序相关配置
# 小程序模版ID号
YYKWEISHOP_TEMPLATE_ID=
YYKWEISHOP_REQUESTDOMAIN=https://b.yuanyuanke.cn
YYKWEISHOP_WSREQUESTDOMAIN=wss://b-websocket.yuanyuanke.cn
# 小程序版本
YYKWEISHOP_VERSION=
YYKWEISHOP_DOWNLOADDOMAIN1=https://mmbiz.qpic.cn
YYKWEISHOP_DOWNLOADDOMAIN2=https://b.yuanyuanke.cn
YYKWEISHOP_DOWNLOADDOMAIN3=https://wx.qlogo.cn


WEBSOCKET_SERVER_PORT=9051
WEBSOCKET_SERVER_HOST=b-websocket.yuanyuanke.cn

SENTRY_LARAVEL_DSN=https://eb0daff7a4244e2e8e8131e178952c55:d13add823922418f859c081feb671d73@report.shopex.cn/41

SUPER_ADMIN_LOGIN_NAME=yykpms
SUPER_ADMIN_LOGIN_PASSWORD=c0f50922ef5c2d72adac424fca752aa97523fa33

NEO4J_DEFAULT_PROTOCOL=http
NEO4J_DEFAULT_HOST=distribution-neo4j-neo4j-core-0.distribution-neo4j-neo4j.espier.svc.cluster.local
NEO4J_DEFAULT_PORT=7474
NEO4J_DEFAULT_USERNAME=neo4j
NEO4J_DEFAULT_PASSWORD=oPMfxPwMwY

BROKERAGE_URI=
BROKERAGE_URI_ITEM=
ALIPAY_PAYMENT_NOTIFY=
ALIPAY_PAYMENT_RETURN=

H5_BASE_URL=
```

#### 安装

安装composer扩展包

```
cd /var/www/espier-blaoted
composer install
```

数据库迁移

```
cd /var/www/espier-blaoted
./artisan doctrine:migrations:migrate
```

### 部署商家端(vue)

商家端代码

代码build好后build目录里的文件直接copy至此目录

1. 创建项目目录

   ```
   mkdir -p /var/www/espier-retail-manage
   ```
2. 放置代码 通常会通过git的方式进行部署及更新

   修改**app/config/test.env.js**

   ```
   module.exports = {
     NODE_ENV: '"testing"',
     BASE_API: '"https://b.yuanyuanke.cn/api"',
     //WXIMG_URL: '"https://bbc54.shopex123.com/image/"',
     WXIMG_URL: '""',
     WXAUTHCALL_Url: '"https://b.yuanyuanke.cn/"'
   }
   ```
3. 安装npm宝

   ```
   cd /var/www/espier-retail-manage/app

   npm installl
   ```
4. 代码编译目录 **/var/www/espier-retail-manage/app/config/index.js**

   ```
   assetsRoot: path.resolve(__dirname, '../test'),
   ```

   编译后的文件&#x5728;**/var/www/espier-retail-manage/app/test**下
5. 代码编译

   ```
   npm run build-test
   ```
6. 更改权限

   ```
   chown -R www:www /var/www/espier-retail-manage
   ```

### 配置nginx

修改/etc/nginx/nginx.conf, 将权限nginx 改为www

```
use www;
```

创建项目nginx配置

```
vim /etc/nginx/conf.d/espier.conf
```

{% hint style="danger" %}
需要配置ssl证书, 在下边配置的所有**配置ssl**处.
{% endhint %}

```
server
{
    listen 443;
    # 配置ssl

    server_name b.yuanyuanke.cn;

    location /api/ {
        access_log /var/log/nginx/espier-bloated.log;
        proxy_pass http://localhost:8080;
    }

    location /wechatAuth/ {
        access_log /var/log/nginx/espier-wechatauth.log;
        proxy_pass http://localhost:8080;
    }

    location / {
        proxy_pass http://localhost:8081;
    }
}

# 配置API服务(espier-bloated)
server {

    listen 8080;
    listen [::]:8080;

    server_name localhost;
    root /var/www/espier-bloated/public;
    index index.php index.html index.htm;

    location / {
         try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        add_header Access-Control-Allow-Origin '*' always;
        add_header Access-Control-Allow-Headers "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With";
        add_header Access-Control-Expose-Headers "Authorization";
        add_header Access-Control-Allow-Methods "DELETE, GET, HEAD, POST, PUT, OPTIONS, TRACE, PATCH";

        if ($request_method = OPTIONS ) {
            return 200;
        }

        fastcgi_pass localhost:9000;
        fastcgi_read_timeout 150;
        fastcgi_index index.php;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}

# 配置商家端(espier-retail-manage)
server {
    listen 8081;
    listen [::]:8081;

    server_name localhost;

    location / {
        root   /var/www/espier-retail-manage/app/test;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html =404;
    }

    location /wximage/ {
        set $hostx "";
        set $addrs "";
        if ( $uri ~ "^/wximage/http./+([^/]+)/(.+)$") {
            set $hostx $1;
            set $addrs $2;
        }
        resolver 8.8.8.8;
        proxy_pass http://$hostx/$addrs;
        proxy_set_header referer "http://read.html5.qq.com/image";
    }
}

配置websocket服务
server
{
    listen 443;

    # 配置ssl

    server_name b-websocket.yuanyuanke.cn;

    location / {
        proxy_pass http://localhost:9051/;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
```

### 配置定时任务

```
crontab -e
```

```
*    *    *    *    *       sudo -E -u www php /var/www/espier-bloated/artisan schedule:run >> /var/log/espier-cront.log 2>&1
```

### 配置队列服务

修改队列配置

```
vim /etc/supervisord.d/super-queue.ini
```

```
[program:bloated-queue-default]
command=/var/www/espier-bloated/artisan queue:work --queue=default --delay=3 --memory=128 --timeout=30 --sleep=1 --tries=3
stdout_logfile=/var/www/espier-bloated/storage/logs/supervisor-bloated-queue-default.log
redirect_stderr=true
process_name=%(program_name)s_%(process_num)02d
autostart=true
autorestart=true
numprocs=1
user=www
startsecs=3
startretries=100000

[program:bloated-queue-slow]
command=/var/www/espier-bloated/artisan queue:work --queue=slow --delay=3 --memory=128 --timeout=1800 --sleep=1 --tries=3
stdout_logfile=/var/www/espier-bloated/storage/logs/supervisor-bloated-queue-slow.log
redirect_stderr=true
process_name=%(program_name)s_%(process_num)02d
redirect_stderr=true
autostart=true
autorestart=true
numprocs=1
user=www
startsecs=3
startretries=100000

[program:bloated-queue-sms]
command=/var/www/espier-bloated/artisan queue:work --queue=sms --delay=3 --memory=128 --timeout=1800 --sleep=1 --tries=3
stdout_logfile=/var/www/espier-bloated/storage/logs/supervisor-bloated-queue-sms.log
redirect_stderr=true
process_name=%(program_name)s_%(process_num)02d
redirect_stderr=true
autostart=true
autorestart=true
numprocs=1
user=www
startsecs=3
startretries=100000
```

### 配置websocket服务

配置websocket

```
vim /etc/supervisord.d/super-websocket.ini
```

```
[program:websocket]
command=/var/www/espier-bloated/artisan websocket:start
stdout_logfile=/var/www/espier-bloated/storage/logs/supervisor-websocket.log
redirect_stderr=true
autostart=true
autorestart=true
numprocs=1
user=www
startsecs=3
startretries=100000
```

## 服务启动

启动队列任务, websocket服务

```
systemctl start supervisord.service
```

启动nginx

```
systemctl start nginx.service
```

启动php-fpm

```
systemctl start php72-php-fpm.service
```
