前言

Caddy 是一款用golang编写的轻量级web server

老早就看到caddy web server,但是找不到去用的理由,因为nginx足够好
如果用docker打包应用,如果要实现跑php php-fpm应用且要单容器,用nginx就不太爽了
一般用php官方docker镜像作为base,然后在上面再装nginx,nginx复杂的编译环境非常麻烦
试用了下 caddy,他就没那么麻烦,还是用php官方docker镜像,然后只需要简单的几行就可以把caddy加进去
非常特别的一点是caddy自带php-fpm的进程启动
在配置文件中增加on startup php-fpm —nodaemonize 即可
容器的入口只需要启动caddy即可,caddy可以自动启动php-fpm进程
目前发现唯一的问题是caddy对个人免费,对公司和商业收费,价格还非常贵
下面是一些参考文件

Dockerfile 参考

FROM php:7.2-fpm-alpine

LABEL version="7.2" \
    description="php-fpm7.2 alpine" \
    maintainer="wwek<licoolgo@gmail.com>"

# 国内源
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
# 系统基础包
RUN apk update \
    && apk upgrade \
    # && apk add s6 \
    && apk add bash \
    # && apk add openssl \
    # && apk add openssl-dev \
    && apk add ca-certificates \
    #&& apk add wget \
    && apk add curl \
    #&& apk add tcpdump \
    && apk add iputils \
    && apk add iproute2 \
    && apk add libc6-compat \
    && apk add zlib \
    && apk add -U tzdata \
    && apk add libpng \
    && apk add libpng-dev \
    #&& apk add libstdc++ \
    && rm -rf /var/cache/apk/*

# 系统设置(设置时区)
RUN rm -rf /etc/localtime \
    && ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" /etc/timezone

# --- PHP --- #
## PHP Core核心扩展
RUN docker-php-ext-install opcache mysqli pdo_mysql mbstring zip pcntl gd
## 三方扩展
## redis
RUN set -xe \
    #&& apk add --no-cache --update --virtual .phpize-deps $PHPIZE_DEPS \
    && apk add --update --virtual .phpize-deps $PHPIZE_DEPS \
    && pecl install -o -f redis  \
    #&& pecl install -o -f swoole  \
    && echo "extension=redis.so" > /usr/local/etc/php/conf.d/redis.ini \
    #&& echo "extension=swoole.so" > /usr/local/etc/php/conf.d/swoole.ini \
    && rm -rf /usr/share/php \
    && rm -rf /tmp/* \
    && apk del  .phpize-deps


# 增加 PHP 配置文件
ADD docker/www.conf /usr/local/etc/php-fpm.d/
ADD docker/php.ini /usr/local/etc/php

##  PHP设置
# 设置php运行权限为母鸡中 www的用户和组 1000:1000
# RUN addgroup -g 1000 -S www && \
#     adduser -u 1000 -S www -G www www

# 设置php-fpm运行账户和组为 www www
# RUN set -ex \
#     && cd /usr/local/etc \
#     && { \
#         echo 'listen.owner = www'; \
#         echo 'listen.group = www'; \
#         echo 'listen.mode = 0660'; \
#     } | tee php-fpm.d/www.conf

# -- Caddy -- #
## 安装 Caddy Web Server
#RUN curl -sSL -o- "https://caddyserver.com/download/linux/amd64?plugins=http.jwt,http.login,http.prometheus,http.realip,http.restic,http.expires&license=personal&telemetry=off" | tar -xvz -C /usr/bin/
RUN curl --silent --show-error --fail --location \
    --header "Accept: application/tar+gzip, application/x-gzip, application/octet-stream" -o - \
    "https://caddyserver.com/download/linux/amd64?plugins=http.jwt,http.login,http.prometheus,http.realip,http.restic,http.expires&license=personal&telemetry=off" \
    | tar --no-same-owner -C /usr/bin/ -xz caddy \
    && chmod 0755 /usr/bin/caddy \
    && /usr/bin/caddy -version

## 增加 Caddy 配置文件
ADD docker/Caddyfile /etc/Caddyfile


# -- APP -- #
## APP环境变量
ENV APPROOT="/data/wwwroot/www.iamle.com/" \
    PHP_MYSQL_HOSTNAME='' \
    PHP_MYSQL_DATABASE='' \
    PHP_MYSQL_USERNAME='' \
    PHP_MYSQL_PASSWORD='' \
    PHP_MYSQL_PORT='' \
    PHP_REDIS_HOST=''

## APP增加
RUN mkdir -p $APPROOT
COPY ./ $APPROOT


## APP配置
WORKDIR $APPROOT
# 巴拉巴拉 APP 配置修改


## APP 权限
## APP设置文件权限 这里的82为php-fpm容器中的 www-data的 usreid 和groupid
RUN chown -R www-data:www-data $APPROOT
#RUN chown -R 82:82 $APPROOT

# 入口
CMD ["/usr/bin/caddy", "--conf", "/etc/Caddyfile", "--log", "stdout"]

Caddyfile 参考

0.0.0.0:80
log stdout
errors stdout

root /data/wwwroot/www.iamle.com/public/

fastcgi / 127.0.0.1:9000 php {
    ext   .php
    split .php
    index index.php
}

rewrite {
    to {path} {path}/ /index.php?s={uri}
}

on startup php-fpm --nodaemonize

www.conf

[www]
prefix = /var/
user = www-data
group = www-data
listen = 127.0.0.1:9000
;listen.backlog = 511
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
;listen.acl_users =
;listen.acl_groups =
;listen.allowed_clients = 127.0.0.1

pm = dynamic
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
pm.max_children = 100
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 20
pm.min_spare_servers = 20
pm.max_spare_servers = 100
;pm.process_idle_timeout = 10s;
pm.max_requests = 50000
;pm.status_path = /status

;ping.path = /ping
;ping.response = pong

;access.log = log/$pool.access.log

;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"

slowlog = log/$pool.slow.log

request_slowlog_timeout = 30

;request_terminate_timeout = 0

;rlimit_files = 1024

;rlimit_core = 0

;chroot =

;chdir = /var/www

catch_workers_output = yes

;clear_env = no

;security.limit_extensions = .php .php3 .php4 .php5 .php7

;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp


;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
;php_admin_value[error_log] = /var/log/fpm-php.www.log
;php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M

其他

在dockerfile中,是直接下载的caddy二进制程序,这个会受到caddy的授权协议影响,对个人免费,对商业收费
如果是基于源码自己编译安装那么就只需要遵守Apache license协议即可,即可以用于商业

常见应用框架配置参考
https://github.com/caddyserver/examples

cady缺省(占位符)变量
https://caddyserver.com/docs/placeholders

使用工具来检查项目代码对PHP7的兼容情况

使用 PHP_CodeSniffer + PHPCompatibility
PHP_CodeSniffer
PHP_CodeSniffer对PHP,JavaScript和CSS文件进行标记,并检测违反已定义的一组编码标准的行为。
https://github.com/squizlabs/PHP_CodeSniffer

PHPCompatibility
PHP兼容性检查
https://github.com/PHPCompatibility/PHPCompatibility
更多PHPCompatibility相关内容,请参考 https://www.sitepoint.com/quick-intro-phpcompatibility-standard-for-phpcs-are-you-php7-ready/

安装和运行

按照官方文档进行安装
先安装PHP_CodeSniffer
再安装插件PHPCompatibility

composer global require “squizlabs/php_codesniffer=*”
phpcs –config-set installed_paths /data/wwwroot/php/PHPCompatibility
phpcs –config-set installed_paths “D:\projects\php\PHPCompatibility”

查看是否安装上
phpcs -i
#The installed coding standards are MySource, PEAR, PSR1, PSR12, PSR2, Squiz, Zend and PHPCompatibility
执行检查
phpcs –standard=PHPCompatibility –runtime-set testVersion 7.0 /data/wwwroot/project/ >phpcs.log
#执行的过程会有些慢,慢慢等吧

根据phpcs.log中的日志,解决error级别的错误

FILE: \data\wwwroot\project\Common\function.php

FOUND 3 ERRORS AFFECTING 2 LINES

9 | ERROR | Function split() is deprecated since PHP 5.3 and removed since PHP 7.0; Use preg_split() instead
11 | ERROR | Function eregi() is deprecated since PHP 5.3 and removed since PHP 7.0; Use preg_match() instead

11 | ERROR | Extension ‘ereg’ is deprecated since PHP 5.3 and removed since PHP 7.0; Use pcre instead

再加上手动按照PHP版本逐级检查不兼容

PHP5.4.x 》 PHP5.6.x

从 PHP 5.4.x 迁移到 PHP 5.5.x

不向后兼容的变更的检查
检查 pack() 和 unpack() pass

PHP 5.5.x 中废弃的特性的检查
preg_replace() 函数中用到的 /e 修饰符现在被弃用。可以使用 preg_replace_callback() 函数来替代。
mcrypt 的相关函数

PHP 5.5.x 》 PHP5.6.x

从PHP 5.5.x 移植到 PHP 5.6.x
不向后兼容的变更的检查
严格的 json_decode()

PHP 5.6.x 》 PHP7.0.x

从PHP 5.6.x 移植到 PHP 7.0.x
不向后兼容的变更的检查
ereg相关函数
split()

测试

把主要业务功能都过一遍测试

升级部署问题

方式1:容器部署:php-fpm改用docker的方式部署,这样比较方便多版本切换和回滚
方式2:传统部署:部署新服,业务流量切过去,原有服暂时不要销毁,等业务没问题后再慢慢销毁原有服, 如果是云平台应该为所有将要销毁的系统做一个快照以备不时之需

需求

在vue单页应用开发中,如果为了省事PHP代码和vue代码都放在同一个GIT仓库中
那么如何进行vue前端代码和php Api 后端代码组织呢?
下面提供一个组织方式

项目目录结构

frontend
public
.gitignore
.travis.yml
application
build.php
composer.json
extend
LICENSE.txt
phpunit.xml
README.md
runtime
tests
think
thinkphp
vendor

其中
frontend 为vue前端项目目录
public 为php的web目录,index.php入口文件在这里,nginx也配置的是public

vue 配置

文件 frontend/config/index.js

module.exports = {
  build: {
    env: require('./prod.env'),
    index: path.resolve(__dirname, '../../public/ui/index.html'),
    assetsRoot: path.resolve(__dirname, '../../public/ui'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/ui/',
    .........其他代码.......
  },

前台访问

api接口为
http://appweb.dev/v1/api

访问/ui/即可
http://appweb.dev/ui/

需要注意的问题

public/ui 目录为前端项目build后的文件,不适宜纳入git管理
线上发布的通过CI&CD工具解决
目前适用于PHP项目的CI&CD工具有
Jenkins
Walle 瓦力上线部署系统
Piplin 持续集成与部署系统

mysql雪崩

故事

小王刚下班回家正准备露一手做2个菜。
手机就收到监控报警,网站打不开报警,mysql IO报警,xx报警
还没等开电脑,公司的人就来电话了
老板:小王啊,网站怎么又打不开了?快点解决啊!损失很大啊!
小王:好的马上处理。
凭借小王丰富的经验,mysql IO都那么高了,肯定是什么sql查询把IO都耗干净了,估计还卡了一堆查询
熟练的用show processlist看了下mysql正在处理的查询,果不其然,这查询队列都长到姥姥家了
苦逼的小心易易的结束了卡的时间很长的SELECT查询,这他妈结束的数据远远跟不上新增加的慢查询的速度呀
应用层程序也来不及改呀
最后小王还是恢复了数据库,可时间已经过去了整整1小时。

事后
老版:这次网站挂了1小时,以后别这样,的解决啊!
小王解释道:网站被恶意爬虫了,爬到了耗IO的sql查询,这个时候业务又是高峰期,导致数据库卡死了。
(对于业务来说,更多时候只关注结果,业务不能离线)

其他公司怎么做的?

  • 数据库中间件处理
  • 缓存机制保证不会让数据库太难受
  • 应用层熔断机制
    等等

纯PHP + MYSQL的项目如何应对数据库雪崩?

  • 数据库中间件处理
    我想绝大用PHP + MYSQL的中小公司是没有MYSQL中间件的,有中间件的可以在数据库中间件处理
    有用Swoole写MYSQL中间件的案例
  • 缓存要顶用
    设计好Redis机制,最小量的请求落在MYSQL上
  • 用MySQL Query Rewrite Plugin 进行SQL语句重写
    MySQL · 5.7新特性 · Query Rewrite Plugin
  • 用pt-kill自动杀掉过长的慢查询
    pt-kill是Percona-Toolkit系列之一,Percona公司出品的工具都没用过,绝对有相见恨晚的感觉。
    pt-kill杀掉慢查询,这算是一个简单粗暴的“熔断方案”,都已经到了让数据库雪崩的阶段,与其业务都不可用,还不如有损服务。
    需要注意的是只结束从库的SELECT慢查询

直接上手,pt-kill常驻进程自动结束SQL慢查询实例

以下实例作用是
* 127.0.0.1这台msyql实例上
* 字符集指定为 utf8
* 查询中有关键字 “SELECT|select” (支持正则)
* mysql帐号是 “业务mysql帐号”
* 查询时间大于60秒
* kill掉查询,不kill mysql连接
* 表示从匹配的结果中选择,类似SQL中的where部分,all是全部的查询
* 10秒检查一次
* 日志记录位置
* 打印日志
* 守护进程的方式运行

pt-kill \
--host 127.0.0.1 --port 3306 --user 'root' --password 'password' \
--charset utf8 \
--match-info "SELECT|select" \
--match-user 业务mysql帐号 \
--busy-time 60 \
--kill-query \
--victims all \
--interval 10 \
--log=/var/log/mysql-pt-kill.log \
--print \
--daemonize

常用参数说明

  • host 连接数据库的地址
  • port 连接数据库的端口
  • user 连接数据库的用户名
  • passowrd 连接数据库的密码
  • charset 指定字符集
  • match-command 指定杀死的查询类型
  • match-user 指定杀死的用户名,即杀死该用户的查询
  • match-info 匹配查询的信息
  • busy-time 指定杀死超过多少秒的查询
  • kill 执行kill命令
  • kill-query 与kill匹配查询的连接不同,此选项只会kill查询,而不会杀死连接
  • victims 表示从匹配的结果中选择,类似SQL中的where部分,all是全部的查询
  • interal 每隔多少秒检查一次
  • print 把kill的查询打印出来
  • log 输出日志到文件仅在 daemonize 守护进程的时候
  • daemonize 守护进程(常驻进程)
    更多参考官方pt-kill手册

监控多个mysql

如果多个mysql都需要用pt-kill监控慢查询并干掉,那么运行多个即可

/usr/bin/pt-kill \
--host 10.10.0.1 \
--port 3306 \
--user ptkill \ 
--password ptkill账号密码 \
--charset utf8 \
--match-info 'SELECT|select' \ 
--match-user 业务mysql帐号 \
--busy-time 20 \
--victims all \
--interval 10 \
--log=/var/log/pt-kill-mysql-slave1.log \
--pid=/var/run/pt-kill-mysql-slave1.pid \
--kill \
--print \ 
--daemonize

/usr/bin/pt-kill \
--host 10.10.0.2 \
--port 3306 \
--user ptkill \ 
--password ptkill账号密码 \
--charset utf8 \
--match-info 'SELECT|select' \ 
--match-user 业务mysql帐号 \
--busy-time 20 \
--victims all \
--interval 10 \
--log=/var/log/pt-kill-mysql-slave2.log \
--pid=/var/run/pt-kill-mysql-slave2.pid \
--kill \
--print \ 
--daemonize

pt-kill守护进程

虽然pt-kill自带参数daemonize可以后台守护运行,但是很多时候会莫名其妙的进程挂掉,
那么还需要一个外部的进程守护工具来守护他,在pt-kill自动退出后再次把进程拉起来
这里进程守护工具我们使用 Monit
配置文件参考为

#pt-kill-mysql 从库1守护
check process pt-kill-mysql-slave1 with pidfile /var/run/pt-kill-mysql-slave1.pid
   start program  "/usr/bin/pt-kill --host 10.10.0.1 --port 3306 --user ptkill --password ptkill账号密码 --charset utf8 --match-info 'SELECT|select' --match-user 业务mysql帐号 --busy-time 20 --victims all --interval 10 --log=/var/log/pt-kill-mysql-slave1.log --kill --print --pid=/var/run/pt-kill-mysql-slave1.pid --daemonize"
   stop program  "kill `cat /var/run/pt-kill-mysql-slave1.pid`"
   if changed pid then start

#pt-kill-mysql 从库2守护
check process pt-kill-mysql-slave2 with pidfile /var/run/pt-kill-mysql-slave2.pid
   start program "/usr/bin/pt-kill --host 10.10.0.2 --port 3306 --user ptkill --password ptkill账号密码 --charset utf8 --match-info 'SELECT|select' --match-user 业务mysql帐号 --busy-time 20 --victims all --interval 10 --log=/var/log/pt-kill-mysql-slave2.log --kill --print --pid=/var/run/pt-kill-mysql-slave2.pid --daemonize"
   stop program  "kill `cat /var/run/pt-kill-mysql-slave2.pid`"
   if changed pid then start

参考

官方pt-kill手册

隐藏nginx、openresty版本号

隐藏nginx、openresty的版本号有什么用?
假设一个场景,nginx被爆出0.9-1.5的版本被爆出一个0day漏洞,
攻击者会先大量扫描匹配的nginx版本,然后实施攻击。
如果事先隐藏了会降低第一时间被攻击的风险

在 http {} 中间配置增加

server_tokens off;

在http头中从原来的
Server: nginx/1.0.15 变为 Server: nginx
Server: openresty/1.11.2.3 变为 Server: openresty

nginx 日志格式化完整增强版

本完整增强版主要解决了后端执行时间的记录、哪台后端处理的、日志集中化后日志来自于哪台服务器ip、cdn传过来的客户端ip等扩展等问题。

在默认的nginx日志上,扩展增加了http头中代理服务器ip($http_x_forwarded_for)、
http头中cdn保存的客户端用户真实IP($http_x_real_ip)、服务端ip($server_addr)、http头中host主机($host)、
请求时间($request_time)、后端返回时间($upstream_response_time)、后端地址($upstream_addr)、
URI($uri)、ISO 8601标准时间($time_iso8601)

#log format
log_format access '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"$http_x_real_ip" "$server_addr" "$host" '
'$request_time $upstream_response_time "$upstream_addr" '
'"$time_iso8601"';

nginx日志滚动切割

繁忙的nginx服务器每天都会产生大量的web日志,所以每天需要切割。
每天切割的日志需要保留一段时间,更老的日志需要删除,专业叫法叫做日志滚动类似于视频监控,
所需要保留一定时间的日志还需要删除更老的日志。

很多人喜欢手动用bash shell去写nginx的日志切割滚动配合定时计划任务执行执行。
其实用系统自带的logrotate更好。
新建文件

/etc/logrotate.d/nginx

写入

/data/wwwlogs/*.log {
    #设置权限
    su root www
    #以天为周期分割日志
    daily
    #最小 比如每日分割 但如果大小未到 1024M 则不分割
    minsize 1024M
    #最大 当日志文件超出 2048M 时,即便再下一个时间周期之前 日志也会被分割
    maxsize 2048M
    #保留七天
    rotate 7
    #忽略错误
    missingok
    #如果文件为空则不分割 not if empty
    notifempty
    #以日期为单位
    dateext
    #以大小为限制做日志分割 size 会覆盖分割周期配置 1024 1024k 1024M 1024G
    size 1024M
    #开始执行附加的脚本命令 nginx写日志是按照文件索引进行的 必须重启服务才能写入新日志文件
    sharedscripts
    postrotate
    if [ -f /usr/local/nginx/logs/nginx.pid ]; then
    #重启nginx服务
    kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
    fi
    endscript
}

elastic stack elk日志系统

采集的日志需要格式化格式,要么在采集端做,要么在入库elasticsearch的时候做。
在nginx中直接配置输出的日志就是json格式,可以减少格式化日志的cpu开销
在日志采集端,用filebeat、或者logstash作为日志采集工具可以不做任务的格式化处理,
仅仅采集json格式的文本即可。

log_format logstash_json '{"@timestamp":"$time_iso8601",'
'"host":"$server_addr",'
'"clientip":"$remote_addr",'
'"remote_addr":"$remote_addr",'
'"http_x_forwarded_for":"$http_x_forwarded_for",'
'"http_x_real_ip":"$http_x_real_ip",'
'"http_cf_connecting_ip":"$http_cf_connecting_ip",'
'"size":$body_bytes_sent,'
'"responsetime":$request_time,'
'"upstreamtime":"$upstream_response_time",'
'"upstreamhost":"$upstream_addr",'
'"http_host":"$host",'
'"request":"$request",'
'"url":"$uri",'
'"referer":"$http_referer",'
'"agent":"$http_user_agent",'
'"status":"$status"}';

nginx反向代理

listen 80;
#listen [::]:80;
server_name proxy.iamle.com;

location / {
#auth_basic "Password please";
#auth_basic_user_file /usr/local/nginx/conf/htpasswd;
proxy_pass http://127.0.0.1:5601/;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

nginx反向代理的时候要支持后端服务器为DDNS动态域名

server_name proxy.iamle.com;
resolver 1.1.1.1 valid=3s;
set $HassHost "http://backend.iamle.com:999";
location / {
    #auth_basic "Password please";
    #auth_basic_user_file /usr/local/nginx/conf/htpasswd;
    proxy_pass $HassHost;
    proxy_redirect off;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
}

nginx 反向代理 WebScoket

location /api/ {
        proxy_pass http://webscoket:80/;
        # WebScoket Support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        #proxy_set_header Origin xxx;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
}

nginx代理设置php pm status

php-fpm.conf设置

pm.status_path = /phpfpm-status-www

phpfpm.conf

server
{
listen 80;
server_name localhost;
location ~ ^/(phpfpm-status-www|phpstatuswww)$
{
fastcgi_pass unix:/tmp/php-cgi.sock;
include fastcgi.conf;
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
}
}

nginx 域名SEO优化 301

把 iamle.com 默认 301到 www.iamle.com

listen 80;
server_name www.iamle.com iamle.com;
if ($host != 'www.iamle.com' ) {
rewrite ^/(.*)$ https://www.iamle.com/$1 permanent;
}

nginx 全站http跳转https

server{
listen 80;
server_name www.iamle.com;
return 301 https://www.iamle.com$request_uri;
}

server {

listen 443 ssl http2;
ssl    on;
ssl_certificate         /usr/local/nginx/conf/ssl/www.iamle.com.crt;
ssl_certificate_key     /usr/local/nginx/conf/ssl/www.iamle.com.key;
ssl_session_cache           shared:SSL:10m;
ssl_session_timeout         10m;
ssl_session_tickets         off;

# intermediate configuration. tweak to your needs.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;

# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
#add_header Strict-Transport-Security max-age=15768000;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";

# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;

server_name www.iamle.com;
#  ....
}

XSS、Ifram

add_header X-Frame-Options SAMEORIGIN;                                                                                                                        
add_header X-Content-Type-Options nosniff;                          
add_header X-Xss-Protection "1; mode=block";

nginx http2 openssl 支持情况介绍

Supporting HTTP/2 for Website Visitors

ssl https 证书配置生成巩固

Mozilla SSL Configuration Generator

nginx+php 开启OPCACHE 并且使用软连接发布

软连接的方式发布新版本php代码,但是因为OPCACHE未及时更新某些时候会导致502错误
2个方式,方式1直接在nginx这里自动更新最新路径,方式2刷新php-fpm的缓存
下面是方式1

        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;

Cache invalidation for scripts in symlinked folders
Symfony Configuring a Web Server

用PECL自动安装Redis扩展、Swoole扩展

yum install GeoIP GeoIP-devel GeoIP-data
pecl install igbinary
pecl install redis
pecl install swool

编译安装PHP7并安装Redis扩展Swoole扩展

在编译php7的机器上已经有编译安装过php5.3以上的版本,从而依赖库都有了

本php7是编译成fpm-php 使用的,

如果是apache那么编译参数应该为

--with-apxs2=/usr/local/apache/bin/apxs

编译安装php7

PHP_VERSION=7.2.6
wget -c http://www.php.net/distributions/php-$PHP_VERSION.tar.gz
tar zxvf php-$PHP_VERSION.tar.gz
cd php-$PHP_VERSION

./configure \
--prefix=/usr/local/php \
--with-config-file-path=/usr/local/php/etc \
--enable-fpm \
--with-fpm-user=www \
--with-fpm-group=www \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-iconv-dir \
--with-freetype-dir=/usr/local/freetype \
--with-jpeg-dir -\
-with-png-dir \
--with-zlib \
--with-libxml-dir=/usr \
--enable-xml \
--disable-rpath \
--enable-bcmath \
--enable-shmop \
--enable-sysvsem \
--enable-inline-optimization \
--with-curl \
--enable-mbregex \
--enable-mbstring \
--enable-ftp \
--with-gd \
--with-openssl \
--with-mhash \
--enable-pcntl \
--enable-sockets \
--with-xmlrpc \
--enable-zip \
--enable-soap \
--with-gettext \
--disable-fileinfo \
--enable-opcache

make ZEND_EXTRA_LIBS='-liconv'

make install

    echo "Copy new php configure file..."
    mkdir -p /usr/local/php/{etc,conf.d}
    \cp php.ini-production /usr/local/php/etc/php.ini

    # php extensions
    echo "Modify php.ini......"
    sed -i 's/post_max_size =.*/post_max_size = 50M/g' /usr/local/php/etc/php.ini
    sed -i 's/upload_max_filesize =.*/upload_max_filesize = 50M/g' /usr/local/php/etc/php.ini
    sed -i 's/;date.timezone =.*/date.timezone = PRC/g' /usr/local/php/etc/php.ini
    sed -i 's/short_open_tag =.*/short_open_tag = On/g' /usr/local/php/etc/php.ini
    sed -i 's/;cgi.fix_pathinfo=.*/cgi.fix_pathinfo=0/g' /usr/local/php/etc/php.ini
    sed -i 's/max_execution_time =.*/max_execution_time = 300/g' /usr/local/php/etc/php.ini
    sed -i 's/disable_functions =.*/disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server/g' /usr/local/php/etc/php.ini

ln -sf /usr/local/php/bin/php /usr/bin/php
ln -sf /usr/local/php/bin/phpize /usr/bin/phpize
ln -sf /usr/local/php/bin/pear /usr/bin/pear
ln -sf /usr/local/php/bin/pecl /usr/bin/pecl
ln -sf /usr/local/php/sbin/php-fpm /usr/bin/php-fpm
pear config-set php_ini /usr/local/php/etc/php.ini
pecl config-set php_ini /usr/local/php/etc/php.ini

cd ..

编译安装php7的redis扩展支持

wget -c https://github.com/phpredis/phpredis/archive/php7.zip
unzip php7.zip

cd phpredis-php7
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make
make install
cd ..

/usr/local/php7/etc/php.ini
中加入
extension=redis.so

编译安装php7的swoole

wget -c https://github.com/swoole/swoole-src/archive/swoole-1.7.21-stable.tar.gz
tar zxvf swoole-1.7.21-stable.tar.gz
cd swoole-src-swoole-1.7.21-stable/
/usr/local/php7/bin/phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make
make install
cd ..

/usr/local/php7/etc/php.ini
中加入
extension=swoole.so