分类 服务器架构 下的文章

用docker在阿里云esc上搭建Apache+PHP+MariaDB环境

久闻docker大名,一直没用过
这次在阿里云的一台centOS7上要搭一下环境
正好学习一下,记录一下此过程

首先是安装Docker

yum -y install docker

安装完成后,启动docker服务

systemctl start docker.service

测试运行hello-world

docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

由于本地没有hello-world这个镜像,所以会下载一个hello-world的镜像,并在容器内运行。

还可以运行docker info命令,出现相关信息说明安装成功。

使用阿里云docker镜像加速器

登录阿里云 - 开发者平台:https://dev.aliyun.com/ 进入管理中心->Docker Hub
镜像站点->您的专属加速器地址 就可以看到加速器地址

当你下载安装的Docker Version不低于1.10时,建议直接通过daemon config进行配置。 使用配置文件
/etc/docker/daemon.json(没有时新建该文件)

{
    "registry-mirrors": ["<your accelerate address>"]
}

重启Docker Daemon就可以了。

sudo systemctl daemon-reload
sudo systemctl restart docker

本步骤未配置成功,pull镜像时,显示还是从docker.io下载

通过Dockerfile安装Apache+PHP

到 Docker Hub 找到PHP的镜像
https://hub.docker.com/_/php/

选择需要的Dockerfile
笔者选择的是7.0-apache版的Dockerfile
内容为:

FROM php:7.0-apache
COPY src/ /var/www/html/

FROM php:7.0-apache
表示从php:7.0-apache的镜像构建,而php:7.0-apache的Dockerfile详细内容
https://github.com/docker-library/php/blob/2630167f7e69394bdd91f240443a0a521fd7872d/7.0/apache/Dockerfile

回到自己的服务器
新建my-php-app目录作为项目根目录
进入my-php-app目录并新建src目录存放php代码
将7.0-apache版的Dockerfile
将写好的Dockerfile文件放到此apache-php文件夹内
构建

docker build -t my-php-app .

(注意最后有个点用来表示当前目录,初次构建速度会比较慢,需要多等一会。)
出现如下信息,说明构建成功

Successfully built e669e1432638

然后就可以

运行容器

docker run -p 80:80 --name my-running-app -v $PWD/my-php-app/src:/var/www/html -d my-php-app

-p 80:80 绑定宿主的80端口
--name 容器名称
-v 宿主的目录$PWD/my-php-app/src绑定到容器的/var/www/html目录
-d 后台运行容器
my-php-app镜像名称

在src目录下,新建index.php

<?php
echo "hello world";
?>

访问http://服务器IP/可以看到输出了令人激动的hello world

这样,一个基于docker的php+apache的环境,就算初步建成了

安装mariaDB

新建/home/mariadb/data目录,用来存放数据
将/home/mariadb/data挂载到容器的存放数据的路径
便于保存,否则容器重启之后,数据库数据就没了
对了,要先在宿主主机上把SElinux关闭,否则可能出问题

docker search mariadb
docker pull mariadb
docker run -v /home/mariadb/data:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --name my-mariadb -d mariadb:latest

-e MYSQL_ROOT_PASSWORD 设置root密码

PHP连接mariadb

编辑index.php,连接数据库,报错
因为php没有装相应的mysql扩展
于是安装pdo

编辑Dockerfile

FROM php:7.0-apache
COPY src/ /var/www/html/
RUN docker-php-ext-install pdo_mysql\
    && docker-php-ext-install mysqli

停止my-running-app容器并删除该容器

docker stop my-running-app
docker rm my-running-app 

然后重新build镜像,再从新的镜像运行新容器

docker build -t my-php-app . 
docker run -p 80:80 --name my-running-app -v /home/php/src:/var/www/html -d my-php-app

通过phpinfo发现pdo扩展已经安装上。
编辑index.php

$dsn = 'mysql:dbname=mysql;host=宿主主机IP';
$user = 'root';
$password = 'root';

try {
    $dbh = new PDO($dsn, $user, $password);
    if($dbh) {
        echo "success";
    }
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

打印出success,说明连接数据库成功。

centos6.7 yum安装mysql5.7

两台机器 内网互通
A:192.168.3.39
B:192.168.3.40
以root身份登录

先卸载老版本mysql

已安装centos6.7,选的basic-server模式,默认带了mysql5.1,先卸载掉,再安装

yum list installed|grep mysql
mysql-libs.x86_64 
yum remove mysql-libs.x86_64
........
Complete!
yum list installed|grep mysql

无返回,则已卸载成功。

官网下载系统对应的mysql的rpm包

wget https://repo.mysql.com//mysql57-community-release-el6-9.noarch.rpm

安装rpm包

rpm -Uvh mysql57-community-release-el6-9.noarch.rpm
warning: mysql57-community-release-el6-9.noarch.rpm: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY
Preparing...                ########################################### [100%]
1:mysql57-community-relea########################################### [100%]

选择安装版本
When using the MySQL Yum repository, the latest GA release of MySQL is selected for installation by default.
会默认最新的正式版本(GA)

yum repolist all | grep mysql
mysql-connectors-community        MySQL Connectors Community      enabled:    30
mysql-connectors-community-source MySQL Connectors Community - So disabled
mysql-tools-community             MySQL Tools Community           enabled:    42
mysql-tools-community-source      MySQL Tools Community - Source  disabled
mysql-tools-preview               MySQL Tools Preview             disabled
mysql-tools-preview-source        MySQL Tools Preview - Source    disabled
mysql55-community                 MySQL 5.5 Community Server      disabled
mysql55-community-source          MySQL 5.5 Community Server - So disabled
mysql56-community                 MySQL 5.6 Community Server      disabled
mysql56-community-source          MySQL 5.6 Community Server - So disabled
mysql57-community                 MySQL 5.7 Community Server      enabled:   164
mysql57-community-source          MySQL 5.7 Community Server - So disabled
mysql80-community                 MySQL 8.0 Community Server      disabled
mysql80-community-source          MySQL 8.0 Community Server - So disabled

可以看到默认选的5.7版本

如果要选择其他版本,可以编辑/etc/yum.repos.d/mysql-community.repo 文件,修改对应版本的enabled=1

确认要安装的版本

yum repolist enabled | grep mysql
mysql-connectors-community           MySQL Connectors Community              30
mysql-tools-community                MySQL Tools Community                   42
mysql57-community                    MySQL 5.7 Community Server             164

开始安装

yum install mysql-community-server
.......
Complete!

安装完成。

启动mysql服务

service mysqld start
Initializing MySQL database:                               [  OK  ]
Installing validate password plugin:                       [  OK  ]
Starting mysqld:                                           [  OK  ]  

查看mysql状态

service mysqld status
mysqld (pid  37204) is running...

mysql5.7默认新建了一个临时密码,存在mysqld.log文件,查看临时密码

grep 'temporary password' /var/log/mysqld.log
2017-01-13T16:36:26.580447Z 1 [Note] A temporary password is generated for root@localhost: **5n-(#ex5xqyV**

更改root密码

mysql -uroot -p
Enter password:

使用临时密码 5n-(#ex5xqyV 登录后,修改root密码

ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass4!';

新密码太简单的话,会报ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
因为增加了密码强度验证插件validate_password,相关参数设置的较为严格

windows与centos-mysql主从同步

在主服务器新建一个用户,给从服务器同步使用。

GRANT ALL PRIVILEGES ON *.* TO 'syncAccount'@'172.16.254.170' IDENTIFIED BY 'syncPassword'; 

修改my.ini

server-id=1 
log-bin=mysql-bin 
binlog-do-db=lbqdb
binlog-ignore-db=mysql

重启主服务器 mysql服务

mysql>show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      446 | lbqdb        | mysql            |
+------------------+----------+--------------+------------------+
1 row in set

修改slave机中mysql配置文件my.cnf

同样在[mysqld]字段下添加如下内容

server-id=2  
master-host=xxx.xxx.xxx.xxx
master-user=syncAccount
master-password=syncPassword
master-port=3306 
master-connect-retry=60 
replicate-do-db=lbqdb

重启mysql报错:

service mysql start
Starting MySQL.. ERROR! The server quit without updating PID file (/var/lib/mysql/localhost.localdomain.pid).

经查 因为这些参数5.1.7以后就不支持了

master-host=
master-user=
master-password=
master-port=
master-connect-retry=

这些参数 mysql5.5已经废弃了

必须在slave上用change master to 来设置slave

重新编辑my.cnf

server_id = 2 
log_bin = mysql_bin
binlog_do_db = lbqdb
binlog_ignore_db = mysql

重启成功。

mysql –u root –p 

设置连接master:

mysql->change master to master_host='xxx.xxx.xxx.xxx',master_user='syncAccount',master_password='syncPassword', master_log_file='mysql-bin.000001',master_log_pos=446;

连接的账户 IP 密码为mastermysql的。master_log_file 和log_pos 为前期windows mysql的show master status 中看到。

出现OK之后

Start slave;启动slave

查看状态

Show slave status\G

*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.3.4
                  Master_User: syncAccount
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 1682
               Relay_Log_File: localhost-relay-bin.000002
                Relay_Log_Pos: 1489
        Relay_Master_Log_File: mysql-bin.000001
             **Slave_IO_Running: Yes
            Slave_SQL_Running: Yes**
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 1682
              Relay_Log_Space: 1649
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1
1 row in set (0.00 sec)

重点关注这两个:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

当slave_IO_Running 和Slave_SQL_Running 都为yes 说明配置成功。

测试了一下,在主数据库新建表并插入数据,从数据库也新建了表,里面也有对应数据。证明数据库已经同步了。

MySQL主从不同步问题解决

问题 描述:主从复制在某个时刻,从服务器同步失败,并给出报错消息。

消息内容:基本意思是缺少某个库的某个表

原因:主从服务长时间不同步,主库已经生成很多新的表或者库,而从库却没有。

操作过程:
(1)登陆主服务器,查看主服务器的状态

mysql>show master status;

(2)登陆从服务器,执行同步操作。

mysql>stop slave;mysql > change master to ...(此处省略);mysql > start slave;

(3)从服务器上查看状态

mysql > show slave status\G

看报错信息少什么表或库,少什么就直接从主服务器通过scp复制,然后重复过程(1)~(3)直到不报错为止

以上说明:同步前,主从数据库的数据库结构要相同。

PS:主键自增id重复的错误

......
Slave_SQL_Running: No
......
Last_SQL_Error: Error 'Duplicate entry '60503' for key 'PRIMARY'' on query....

在主服务器做备份,导入到从服务器的时候,由于没有停服务,也没有锁表,在生成binlog文件的时刻,与导出数据的时刻存在误差,导致从服务器上数据多出一些,同步时,某些自增id就重复了,根据给出的提示,我们可以将多出的数据删除,将AUTO_INCREMENT设为binlog中的值。都设置完成后,再次

start slave;

成功!

也就是说,start slave报错的话,不用担心脏数据,可以解决错误后,继续start slave.

nginx/php-fpm及网站目录的权限设置

核心总结:nginx、php-fpm 进程所使用的用户,不能是网站文件所有者。 凡是违背这个原则,则不符合最小权限原则。

最佳实践:
nginx 以nobody运行
修改nginx.conf
user nobody
重启nginx服务器

php-fpm 也以nobody运行
编辑文件php-fpm.conf
user nobody
group nobody
重启php-fpm

网站目录设置为可读即可,某些需要写入权限的目录设置为777

nginx静态资源 403 forbidden
有可能是selinux的原因

临时关闭selinux
setenforce 0

永久关闭selinux
vi /etc/selinux/config
SELINUX=disabled

重启服务器

PS:
访问出现异常,chrome控制台报错ERR_INCOMPLETE_CHUNKED_ENCODING

查看日志 /var/log/nginx/error.log

open() "/var/cache/nginx/fastcgi_temp/4/09/0000000094" failed (13: Permission denied) while reading upstream,

原因是:
nginx会使用fastcgi_buffer_size指定的大小的缓冲区用于缓存fastcgi流的内容。当大小超出此大小时会继续用fastcgi_buffers指定的数量和大小申请缓冲区。如果依然超出此大小,会将多出的内容写入临时文件。上面日志中反应的就是nginx无权写入临时文件。

于是将/var/cache/nginx/用户也设为nobody

chown nobody:nobody -R /var/cache/nginx/

解决问题。

基于swoole的Hprose for PHP 开发后端API服务 实践


注:Hprose for PHP 已更新到2.0 本文代码已失效 请参阅最新官方文档
https://github.com/hprose/hprose-php/wiki


Hprose(High Performance Remote Object Service Engine)
是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。
你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。
http://hprose.com/

Hprose for PHP
https://github.com/hprose/hprose-php

swoole PHP的异步、并行、高性能网络通信引擎
http://www.swoole.com/

简单示例,目标:开发一个后台服务,通过id查询数据库的用户表数据,
当服务器端发布的方法被调用前,通过Hprose提供的onBeforeInvoke函数来验证IP

软件安装:
mariadb 10.1.13
php 5.6.21
swoole 1.8.4

https://github.com/hprose/hprose-php/tree/master/src
hprose文件下载到工作目录下

服务端:
目录结构
QQ图片20160524142310.png

数据库配置文件:config.php

<?php
/**
 *  数据库连接配置
 */
define('DB_NAME', 'xxx');
define('DB_USER', 'xxx');
define('DB_PWD', 'xxx');
define('DB_HOST', '127.0.0.1');
define('DB_PORT','3306');
define('DB_TYPE','mysql');
define('DB_CHARSET','utf8');
define('PAGINATE_LIMIT', '5');
define('PREFIX', '');

User类文件:User.php

<?php
class User {
    
    public function getUserByID($user_id) {
        
        try {

            $dbh = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PWD);

            $rs = array();
            
            foreach($dbh->query("SELECT * from XXXX WHERE `id` = {$user_id}") as $row) {
                $rs[] = $row;
            }
            
            return $rs;

        } catch (PDOException $e) {
            return "Error!: " . $e->getMessage() . "<br/>";
        }
    }
}

服务器文件:server.php

<?php
/**
 *  配置文件    
 */
require_once("config.php");

/**
 *  引入Hprose
 */
require_once("Hprose.php");

/**
 *  autoload加载,将文件都放在classes目录下
 */
spl_autoload_register(function ($class) {
    include 'classes/' . $class . '.php';
});

/**
 *  使用 HproseSwooleServer 来创建一个独立的 hprose 服务
 *  绑定8080端口
 */
$server = new HproseSwooleServer('http://0.0.0.0:8080/');

/**
 *  addInstanceMethods用来发布指定对象上的指定类层次上声明的所有public实例方法
 *  它有三个参数,其中后两个是可选参数。
 *  如果您在使用addInstanceMethods方法时,不指定类层次(或者指定为NULL),则发布这个对象所在类上声明的所有public实例方法
 *  设置别名为my
 */
$server->addInstanceMethods(new User(), NULL, 'my');

/**
 *  onBeforeInvoke 会在接口被调用前执行
 *  因此可以用来验证IP
  */
$server->onBeforeInvoke = function ($name, $args, $byref, $context) {
    if($context->request->server['remote_addr'] == '127.0.0.1') {

    } else {
        //不是正确的IP则抛出异常,终止服务
        throw new Exception("unknow client IP");    
    }
};

//启动服务
$server->start();

客户端:

<?php
require_once("Hprose.php");
$client = new HproseSwooleClient('http://localhost:8080');
//调用getUserByID方法,需要加上设置的别名my_
print_r($client->my_getUserByID(4003));

直接打印出了数据,则走通了整个流程。

Hprose for HTML5客户端调用跨域问题

用Hprose for HTML5的客户端调用,发现不能跨域,
查源码,发现Hprose提供了解决办法,server.php启动服务前加上以下语句,
允许跨域,并将调用的域名加入AccessControlAllowOrigin

$server->setCrossDomainEnabled();
$server->addAccessControlAllowOrigin('http://xxxxxxxxxx');

可解决跨域问题。