Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 11857 articles
Browse latest View live

MHA+ProxySQL实现读写分离高可用 - yayun - 博客园

$
0
0

最近在研究ProxySQL,觉得还挺不错的,所以就简单的折腾了一下,ProxySQL目前也是Percona在推荐的一个读写分离的中间件。关于详细的介绍可以参考官方文档。 https://github.com/sysown/proxysql/wiki

本文主要介绍的是MHA+ProxySQL读写分离以及高可用,ProxySQL的细节请参考文档,目前已经有人写的非常详细了,文章最后会给出链接。当然和Group Replication,PXC搭配那是更完美的。关于MHA的配置参考我前面的文章,本文就不再介绍。下面来看看

MHA和ProxySQL搭配使用的架构图:

 

测试环境(1主2从):

M-> 192.168.0.20
S1-> 192.168.0.30
S2-> 192.168.0.40
proxysql -> 192.168.0.10

1. 检查主从复制是否正常:

复制代码
[root@db_server_yayun_04 ~]# masterha_check_repl --conf=/data/mha/3306/mha.cnf 
Thu Jun 15 17:45:32 2017 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Thu Jun 15 17:45:32 2017 - [info] Reading application default configuration from /data/mha/3306/mha.cnf..
Thu Jun 15 17:45:32 2017 - [info] Updating application default configuration from /usr/local/bin/load_cnf..
Thu Jun 15 17:45:32 2017 - [info] Reading server configuration from /data/mha/3306/mha.cnf..
Thu Jun 15 17:45:32 2017 - [info] Setting max_ping_errors to 10, ping_interval to 3.
Thu Jun 15 17:45:32 2017 - [info] MHA::MasterMonitor version 0.57.
Thu Jun 15 17:45:32 2017 - [info] GTID failover mode = 1
Thu Jun 15 17:45:32 2017 - [info] Dead Servers:
Thu Jun 15 17:45:32 2017 - [info] Alive Servers:
Thu Jun 15 17:45:32 2017 - [info]   192.168.0.20(192.168.0.20:3306)
Thu Jun 15 17:45:32 2017 - [info]   192.168.0.30(192.168.0.30:3306)
Thu Jun 15 17:45:32 2017 - [info]   192.168.0.40(192.168.0.40:3306)
Thu Jun 15 17:45:32 2017 - [info] Alive Slaves:
Thu Jun 15 17:45:32 2017 - [info]   192.168.0.30(192.168.0.30:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Thu Jun 15 17:45:32 2017 - [info]     GTID ON
Thu Jun 15 17:45:32 2017 - [info]     Replicating from 192.168.0.20(192.168.0.20:3306)
Thu Jun 15 17:45:32 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Thu Jun 15 17:45:32 2017 - [info]   192.168.0.40(192.168.0.40:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Thu Jun 15 17:45:32 2017 - [info]     GTID ON
Thu Jun 15 17:45:32 2017 - [info]     Replicating from 192.168.0.20(192.168.0.20:3306)
Thu Jun 15 17:45:32 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Thu Jun 15 17:45:32 2017 - [info] Current Alive Master: 192.168.0.20(192.168.0.20:3306)
Thu Jun 15 17:45:32 2017 - [info] Checking slave configurations..
Thu Jun 15 17:45:32 2017 - [info] Checking replication filtering settings..
Thu Jun 15 17:45:32 2017 - [info]  binlog_do_db= , binlog_ignore_db= 
Thu Jun 15 17:45:32 2017 - [info]  Replication filtering check ok.
Thu Jun 15 17:45:32 2017 - [info] GTID (with auto-pos) is supported. Skipping all SSH and Node package checking.
Thu Jun 15 17:45:32 2017 - [info] Checking SSH publickey authentication settings on the current master..
Thu Jun 15 17:45:33 2017 - [info] HealthCheck: SSH to 192.168.0.20 is reachable.
Thu Jun 15 17:45:33 2017 - [info] 
192.168.0.20(192.168.0.20:3306) (current master)
 +--192.168.0.30(192.168.0.30:3306)
 +--192.168.0.40(192.168.0.40:3306)

Thu Jun 15 17:45:33 2017 - [info] Checking replication health on 192.168.0.30..
Thu Jun 15 17:45:33 2017 - [info]  ok.
Thu Jun 15 17:45:33 2017 - [info] Checking replication health on 192.168.0.40..
Thu Jun 15 17:45:33 2017 - [info]  ok.
Thu Jun 15 17:45:33 2017 - [warning] master_ip_failover_script is not defined.
Thu Jun 15 17:45:33 2017 - [warning] shutdown_script is not defined.
Thu Jun 15 17:45:33 2017 - [info] Got exit code 0 (Not master dead).

MySQL Replication Health is OK.
[root@db_server_yayun_04 ~]# 
复制代码

2. 配置后端MySQL。登入ProxySQL,把MySQL主从的信息添加进去。将主库master也就是做写入的节点放到HG 100中,salve节点做读放到HG 1000。在proxysql输入命令:

mysql -uadmin -padmin -h127.0.0.1 -P6032
复制代码
[admin@127.0.0.1][(none)]> insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) values(100,'192.168.0.20',3306,1,1000,10,'test my proxysql');
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) values(1000,'192.168.0.30',3306,1,1000,10,'test my proxysql');
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> insert into mysql_servers(hostgroup_id,hostname,port,weight,max_connections,max_replication_lag,comment) values(1000,'192.168.0.40',3306,1,1000,10,'test my proxysql'); 
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> select * from mysql_servers;
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
| hostgroup_id | hostname     | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment          |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
| 100          | 192.168.0.20 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 0              | test my proxysql |
| 1000         | 192.168.0.30 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 0              | test my proxysql |
| 1000         | 192.168.0.40 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 0              | test my proxysql |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
3 rows in set (0.00 sec)
复制代码

3. 配置后端MySQL用户。这个用户需要先在后端MySQL(20,30,40)里真实存在,一个是监控账号、一个是程序账号。
(1) 监控账号(用来监控后端mysql是否存活以及read_only变量):

GRANT SUPER, REPLICATION CLIENT ON *.* TO 'proxysql'@'192.168.0.10' IDENTIFIED BY 'proxysql';

(2) 程序账号(这里为了后面测试方便给了all权限):

GRANT all ON *.* TO 'yayun'@'192.168.0.10' identified by 'yayun';

4. 在后端MySQL里添加完之后再配置ProxySQL:这里需要注意,default_hostgroup需要和上面的对应。(proxysql)

复制代码
[admin@127.0.0.1][(none)]> insert into mysql_users(username,password,active,default_hostgroup,transaction_persistent) values('yayun','yayun',1,100,1);
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> select * from mysql_users;
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+
| username | password | active | use_ssl | default_hostgroup | default_schema | schema_locked | transaction_persistent | fast_forward | backend | frontend | max_connections |
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+
| yayun    | yayun    | 1      | 0       | 100               | NULL           | 0             | 1                      | 0            | 1       | 1        | 10000           |
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+
1 row in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

5. 设置健康监测账号(proxysql):

复制代码
[admin@127.0.0.1][(none)]> set mysql-monitor_username='proxysql';
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> set mysql-monitor_password='proxysql';
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

6. 加载配置和变量:因为修改了servers、users和variables,所以加载的时候要执行:

复制代码
[admin@127.0.0.1][(none)]> load mysql servers to runtime;
Query OK, 0 rows affected (0.01 sec)

[admin@127.0.0.1][(none)]> load mysql users to runtime;
Query OK, 0 rows affected (0.00 sec)

[admin@127.0.0.1][(none)]> load mysql variables to runtime;
Query OK, 0 rows affected (0.00 sec)

[admin@127.0.0.1][(none)]> save mysql servers to disk;
Query OK, 0 rows affected (0.05 sec)

[admin@127.0.0.1][(none)]> save mysql users to disk;
Query OK, 0 rows affected (0.02 sec)

[admin@127.0.0.1][(none)]> save mysql variables to disk;
Query OK, 74 rows affected (0.01 sec)

[admin@127.0.0.1][(none)]> 
复制代码

7. 连接数据库,通过proxysql的客户端接口访问(6033):(我这里从40从库上面发起连接)

 mysql -uyayun -pyayun -h192.168.0.10 -P6033
复制代码
[root@db_server_yayun_04 ~]# mysql -uyayun -pyayun -h192.168.0.10 -P6033               
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.5.30 (ProxySQL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

[yayun@192.168.0.10][(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| yayun              |
+--------------------+
5 rows in set (0.01 sec)

[yayun@192.168.0.10][(none)]> 
复制代码

 8. 创建表并且写入数据进行查询:

复制代码
[yayun@192.168.0.10][(none)]> use yayun
Database changed, 2 warnings
[yayun@192.168.0.10][yayun]> create table t1 ( id int);
Query OK, 0 rows affected (0.08 sec)

[yayun@192.168.0.10][yayun]> insert into t1 select 1;
Query OK, 1 row affected (0.05 sec)
Records: 1  Duplicates: 0  Warnings: 0

[yayun@192.168.0.10][yayun]> select * from t1;
+------+
| id   |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

[yayun@192.168.0.10][yayun]> 
复制代码

可以看到创建了表,并且插入了数据,查询也没问题。proxysql有个类似审计的功能,可以查看各类SQL的执行情况。在proxysql执行SQL查看。

复制代码
[admin@127.0.0.1][(none)]> select * from stats_mysql_query_digest;
+-----------+--------------------+----------+--------------------+----------------------------------+------------+------------+------------+----------+----------+----------+
| hostgroup | schemaname         | username | digest             | digest_text                      | count_star | first_seen | last_seen  | sum_time | min_time | max_time |
+-----------+--------------------+----------+--------------------+----------------------------------+------------+------------+------------+----------+----------+----------+
| 100       | yayun              | yayun    | 0xA9518ABEA63705E6 | create table t1 ( id int)        | 1          | 1497577980 | 1497577980 | 79733    | 79733    | 79733    |
| 100       | yayun              | yayun    | 0x3765930C7143F468 | select * from t1                 | 1          | 1497577997 | 1497577997 | 1537     | 1537     | 1537     |
| 100       | yayun              | yayun    | 0x4BBB5CD4BC2CFD94 | insert into t1 select ?          | 1          | 1497577986 | 1497577986 | 33350    | 33350    | 33350    |
| 100       | information_schema | yayun    | 0x620B328FE9D6D71A | SELECT DATABASE()                | 1          | 1497577955 | 1497577955 | 4994     | 4994     | 4994     |
| 100       | information_schema | yayun    | 0x594F2C744B698066 | select USER()                    | 1          | 1497577951 | 1497577951 | 0        | 0        | 0        |
| 100       | information_schema | yayun    | 0x226CD90D52A2BA0B | select @@version_comment limit ? | 1          | 1497577951 | 1497577951 | 0        | 0        | 0        |
+-----------+--------------------+----------+--------------------+----------------------------------+------------+------------+------------+----------+----------+----------+
6 rows in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

9. 可以看到读写都发送到了组100上面,组100是主库,说明没有读写分离。那是因为还有配置没有完成,我们需要自己定义规则。
定义路由规则,如:除select * from tb for update的select全部发送到slave,其他的的语句发送到master。

复制代码
[admin@127.0.0.1][(none)]> INSERT INTO mysql_query_rules(active,match_pattern,destination_hostgroup,apply) VALUES(1,'^SELECT.*FOR UPDATE$',100,1);
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> INSERT INTO mysql_query_rules(active,match_pattern,destination_hostgroup,apply) VALUES(1,'^SELECT',1000,1);
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> load mysql query rules to runtime;
Query OK, 0 rows affected (0.00 sec)

[admin@127.0.0.1][(none)]> save mysql query rules to disk;
Query OK, 0 rows affected (0.02 sec)

[admin@127.0.0.1][(none)]> select rule_id,active,match_pattern,destination_hostgroup,apply from runtime_mysql_query_rules;
+---------+--------+----------------------+-----------------------+-------+
| rule_id | active | match_pattern        | destination_hostgroup | apply |
+---------+--------+----------------------+-----------------------+-------+
| 1       | 1      | ^SELECT.*FOR UPDATE$ | 100                   | 1     |
| 2       | 1      | ^SELECT              | 1000                  | 1     |
+---------+--------+----------------------+-----------------------+-------+
2 rows in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

清理掉统计信息,再次进行测试。

select * from stats_mysql_query_digest_reset;

再次运行读写,然后再查看,发现已经实现读写分离。

复制代码
[admin@127.0.0.1][(none)]> select * from stats_mysql_query_digest;                                                                    
+-----------+------------+----------+--------------------+-------------------------+------------+------------+------------+----------+----------+----------+
| hostgroup | schemaname | username | digest             | digest_text             | count_star | first_seen | last_seen  | sum_time | min_time | max_time |
+-----------+------------+----------+--------------------+-------------------------+------------+------------+------------+----------+----------+----------+
| 1000      | yayun      | yayun    | 0x3765930C7143F468 | select * from t1        | 1          | 1497578494 | 1497578494 | 21751    | 21751    | 21751    |
| 100       | yayun      | yayun    | 0x4BBB5CD4BC2CFD94 | insert into t1 select ? | 1          | 1497578492 | 1497578492 | 54852    | 54852    | 54852    |
+-----------+------------+----------+--------------------+-------------------------+------------+------------+------------+----------+----------+----------+
2 rows in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

重点来了,如何配合MHA实现高可用呢?那么需要利用到proxysql里面的mysql_replication_hostgroups表。mysql_replication_hostgroups 表的主要作用是监视指定主机组中所有服务器的read_only值,并且根据read_only的值将服务器分配给写入器或读取器主机组,定义 hostgroup 的主从关系。ProxySQL monitor 模块会监控 HG 后端所有servers 的 read_only 变量,如果发现从库的 read_only 变为0、主库变为1,则认为角色互换了,自动改写 mysql_servers 表里面 hostgroup 关系,达到自动 Failover 效果。

我们看看mysql_replication_hostgroups表结构:

复制代码
[admin@127.0.0.1][(none)]> show create table mysql_replication_hostgroups\G
*************************** 1. row ***************************
       table: mysql_replication_hostgroups
Create Table: CREATE TABLE mysql_replication_hostgroups (
    writer_hostgroup INT CHECK (writer_hostgroup>=0) NOT NULL PRIMARY KEY,
    reader_hostgroup INT NOT NULL CHECK (reader_hostgroup<>writer_hostgroup AND reader_hostgroup>0),
    comment VARCHAR,
    UNIQUE (reader_hostgroup))
1 row in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

3个字段很明显,写主机组,读主机组,备注。那么我们现在插入数据。我们的100是写,1000是读。

复制代码
[admin@127.0.0.1][(none)]> insert into  mysql_replication_hostgroups (writer_hostgroup,reader_hostgroup,comment)values(100,1000,'测试我的读写分离高可用');
Query OK, 1 row affected (0.00 sec)

[admin@127.0.0.1][(none)]> load mysql servers to runtime;
Query OK, 0 rows affected (0.01 sec)

[admin@127.0.0.1][(none)]> save mysql servers to disk;
Query OK, 0 rows affected (0.03 sec)

[admin@127.0.0.1][(none)]> select * from runtime_mysql_replication_hostgroups;
+------------------+------------------+-----------------------------------+
| writer_hostgroup | reader_hostgroup | comment                           |
+------------------+------------------+-----------------------------------+
| 100              | 1000             | 测试我的读写分离高可用            |
+------------------+------------------+-----------------------------------+
1 row in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

我们用vc-mysql-sniffer在从库抓一下。看看能看见什么。

复制代码
[root@db_server_yayun_03 ~]# ./vc-mysql-sniffer 
# Time: 061617 10:26:45
# User@Host: unknown_user[unknown_user] @ 192.168.0.10:36371 []
# Query_time: 0.005198
SHOW GLOBAL VARIABLES LIKE 'read_only';
# Time: 061617 10:26:46
# User@Host: unknown_user[unknown_user] @ 192.168.0.10:36372 []
# Query_time: 0.000564
SHOW SLAVE STATUS;
# Time: 061617 10:26:46
# User@Host: unknown_user[unknown_user] @ 192.168.0.10:36372 []
# Query_time: 0.004008
SHOW GLOBAL VARIABLES LIKE 'read_only';
# Time: 061617 10:26:48
# User@Host: unknown_user[unknown_user] @ 192.168.0.10:36371 []
# Query_time: 0.004923
SHOW GLOBAL VARIABLES LIKE 'read_only';
# Time: 061617 10:26:49
# User@Host: unknown_user[unknown_user] @ 192.168.0.10:36372 []
# Query_time: 0.003617
SHOW GLOBAL VARIABLES LIKE 'read_only';
复制代码

可以看见在检查read_only变量。我们先看看现在主机组的关系:

复制代码
[admin@127.0.0.1][(none)]> select * from runtime_mysql_servers;
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
| hostgroup_id | hostname     | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment          |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
| 100          | 192.168.0.20 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
| 1000         | 192.168.0.30 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
| 1000         | 192.168.0.40 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
3 rows in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

可以看见100主机组是主库,1000主机组是从库。下面使用MHA在线变换主从关系。我们把从库40提升为主库。

复制代码
[root@db_server_yayun_04 ~]# masterha_master_switch --master_state=alive --conf=/data/mha/3306/mha.cnf --new_master_host=192.168.0.40 --new_master_port=3306 --orig_master_is_new_slave 
Fri Jun 16 10:30:39 2017 - [info] MHA::MasterRotate version 0.57.
Fri Jun 16 10:30:39 2017 - [info] Starting online master switch..
Fri Jun 16 10:30:39 2017 - [info] 
Fri Jun 16 10:30:39 2017 - [info] * Phase 1: Configuration Check Phase..
Fri Jun 16 10:30:39 2017 - [info] 
Fri Jun 16 10:30:39 2017 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Fri Jun 16 10:30:39 2017 - [info] Reading application default configuration from /data/mha/3306/mha.cnf..
Fri Jun 16 10:30:39 2017 - [info] Updating application default configuration from /usr/local/bin/load_cnf..
Fri Jun 16 10:30:39 2017 - [info] Reading server configuration from /data/mha/3306/mha.cnf..
Fri Jun 16 10:30:39 2017 - [info] Setting max_ping_errors to 10, ping_interval to 3.
Fri Jun 16 10:30:39 2017 - [info] GTID failover mode = 1
Fri Jun 16 10:30:39 2017 - [info] Current Alive Master: 192.168.0.20(192.168.0.20:3306)
Fri Jun 16 10:30:39 2017 - [info] Alive Slaves:
Fri Jun 16 10:30:39 2017 - [info]   192.168.0.30(192.168.0.30:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:30:39 2017 - [info]     GTID ON
Fri Jun 16 10:30:39 2017 - [info]     Replicating from 192.168.0.20(192.168.0.20:3306)
Fri Jun 16 10:30:39 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Fri Jun 16 10:30:39 2017 - [info]   192.168.0.40(192.168.0.40:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:30:39 2017 - [info]     GTID ON
Fri Jun 16 10:30:39 2017 - [info]     Replicating from 192.168.0.20(192.168.0.20:3306)
Fri Jun 16 10:30:39 2017 - [info]     Primary candidate for the new Master (candidate_master is set)

It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 192.168.0.20(192.168.0.20:3306)? (YES/no): y
Fri Jun 16 10:30:40 2017 - [info] Executing FLUSH NO_WRITE_TO_BINLOG TABLES. This may take long time..
Fri Jun 16 10:30:40 2017 - [info]  ok.
Fri Jun 16 10:30:40 2017 - [info] Checking MHA is not monitoring or doing failover..
Fri Jun 16 10:30:40 2017 - [info] Checking replication health on 192.168.0.30..
Fri Jun 16 10:30:40 2017 - [info]  ok.
Fri Jun 16 10:30:40 2017 - [info] Checking replication health on 192.168.0.40..
Fri Jun 16 10:30:40 2017 - [info]  ok.
Fri Jun 16 10:30:40 2017 - [info] 192.168.0.40 can be new master.
Fri Jun 16 10:30:40 2017 - [info] 
From:
192.168.0.20(192.168.0.20:3306) (current master)
 +--192.168.0.30(192.168.0.30:3306)
 +--192.168.0.40(192.168.0.40:3306)

To:
192.168.0.40(192.168.0.40:3306) (new master)
 +--192.168.0.30(192.168.0.30:3306)
 +--192.168.0.20(192.168.0.20:3306)

Starting master switch from 192.168.0.20(192.168.0.20:3306) to 192.168.0.40(192.168.0.40:3306)? (yes/NO): yes
Fri Jun 16 10:30:42 2017 - [info] Checking whether 192.168.0.40(192.168.0.40:3306) is ok for the new master..
Fri Jun 16 10:30:42 2017 - [info]  ok.
Fri Jun 16 10:30:42 2017 - [info] 192.168.0.20(192.168.0.20:3306): SHOW SLAVE STATUS returned empty result. To check replication filtering rules, temporarily executing CHANGE MASTER to a dummy host.
Fri Jun 16 10:30:42 2017 - [info] 192.168.0.20(192.168.0.20:3306): Resetting slave pointing to the dummy host.
Fri Jun 16 10:30:42 2017 - [info] ** Phase 1: Configuration Check Phase completed.
Fri Jun 16 10:30:42 2017 - [info] 
Fri Jun 16 10:30:42 2017 - [info] * Phase 2: Rejecting updates Phase..
Fri Jun 16 10:30:42 2017 - [info] 
Fri Jun 16 10:30:42 2017 - [info] Executing master ip online change script to disable write on the current master:
Fri Jun 16 10:30:42 2017 - [info]   /usr/local/bin/master_ip_online_change --command=stop --orig_master_host=192.168.0.20 --orig_master_ip=192.168.0.20 --orig_master_port=3306 --orig_master_user='root' --orig_master_password='123' --new_master_host=192.168.0.40 --new_master_ip=192.168.0.40 --new_master_port=3306 --new_master_user='root' --new_master_password='123' --orig_master_ssh_user=root --new_master_ssh_user=root   --orig_master_is_new_slave
Fri Jun 16 10:30:42 2017 783184 Set read_only on the new master.. ok.
Fri Jun 16 10:30:42 2017 786894 Drpping app user on the orig master..
Fri Jun 16 10:30:42 2017 787716 Waiting all running 3 threads are disconnected.. (max 1500 milliseconds)
{'Time' => '0','Command' => 'Sleep','db' => undef,'Id' => '282','Info' => undef,'User' => 'proxysql','State' => '','Host' => '192.168.0.10:54482'}
{'Time' => '1620','Command' => 'Binlog Dump GTID','db' => undef,'Id' => '296','Info' => undef,'User' => 'repl','State' => 'Master has sent all binlog to slave; waiting for more updates','Host' => '192.168.0.30:53451'}
{'Time' => '874','Command' => 'Binlog Dump GTID','db' => undef,'Id' => '310','Info' => undef,'User' => 'repl','State' => 'Master has sent all binlog to slave; waiting for more updates','Host' => '192.168.0.40:53136'}
Fri Jun 16 10:30:43 2017 294399 Waiting all running 2 threads are disconnected.. (max 1000 milliseconds)
{'Time' => '1621','Command' => 'Binlog Dump GTID','db' => undef,'Id' => '296','Info' => undef,'User' => 'repl','State' => 'Master has sent all binlog to slave; waiting for more updates','Host' => '192.168.0.30:53451'}
{'Time' => '875','Command' => 'Binlog Dump GTID','db' => undef,'Id' => '310','Info' => undef,'User' => 'repl','State' => 'Master has sent all binlog to slave; waiting for more updates','Host' => '192.168.0.40:53136'}
Fri Jun 16 10:30:43 2017 800400 Waiting all running 2 threads are disconnected.. (max 500 milliseconds)
{'Time' => '1621','Command' => 'Binlog Dump GTID','db' => undef,'Id' => '296','Info' => undef,'User' => 'repl','State' => 'Master has sent all binlog to slave; waiting for more updates','Host' => '192.168.0.30:53451'}
{'Time' => '875','Command' => 'Binlog Dump GTID','db' => undef,'Id' => '310','Info' => undef,'User' => 'repl','State' => 'Master has sent all binlog to slave; waiting for more updates','Host' => '192.168.0.40:53136'}
Fri Jun 16 10:30:44 2017 306937 Set read_only=1 on the orig master.. ok.
Fri Jun 16 10:30:44 2017 311939 Waiting all running 3 queries are disconnected.. (max 500 milliseconds)
{'Time' => '0','Command' => 'Sleep','db' => undef,'Id' => '281','Info' => undef,'User' => 'proxysql','State' => '','Host' => '192.168.0.10:54481'}
{'Time' => '1622','Command' => 'Binlog Dump GTID','db' => undef,'Id' => '296','Info' => undef,'User' => 'repl','State' => 'Master has sent all binlog to slave; waiting for more updates','Host' => '192.168.0.30:53451'}
{'Time' => '876','Command' => 'Binlog Dump GTID','db' => undef,'Id' => '310','Info' => undef,'User' => 'repl','State' => 'Master has sent all binlog to slave; waiting for more updates','Host' => '192.168.0.40:53136'}
Fri Jun 16 10:30:44 2017 810421 Killing all application threads..
Fri Jun 16 10:30:44 2017 816513 done.
SIOCSIFFLAGS: Cannot assign requested address
Fri Jun 16 10:30:45 2017 - [info]  ok.
Fri Jun 16 10:30:45 2017 - [info] Locking all tables on the orig master to reject updates from everybody (including root):
Fri Jun 16 10:30:45 2017 - [info] Executing FLUSH TABLES WITH READ LOCK..
Fri Jun 16 10:30:45 2017 - [info]  ok.
Fri Jun 16 10:30:45 2017 - [info] Orig master binlog:pos is mysql-bin.000001:1610.
Fri Jun 16 10:30:45 2017 - [info]  Waiting to execute all relay logs on 192.168.0.40(192.168.0.40:3306)..
Fri Jun 16 10:30:45 2017 - [info]  master_pos_wait(mysql-bin.000001:1610) completed on 192.168.0.40(192.168.0.40:3306). Executed 0 events.
Fri Jun 16 10:30:45 2017 - [info]   done.
Fri Jun 16 10:30:45 2017 - [info] Getting new master's binlog name and position..
Fri Jun 16 10:30:45 2017 - [info]  mysql-bin.000002:194
Fri Jun 16 10:30:45 2017 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='192.168.0.40', MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='xxx';
Fri Jun 16 10:30:45 2017 - [info] Executing master ip online change script to allow write on the new master:
Fri Jun 16 10:30:45 2017 - [info]   /usr/local/bin/master_ip_online_change --command=start --orig_master_host=192.168.0.20 --orig_master_ip=192.168.0.20 --orig_master_port=3306 --orig_master_user='root' --orig_master_password='123' --new_master_host=192.168.0.40 --new_master_ip=192.168.0.40 --new_master_port=3306 --new_master_user='root' --new_master_password='123' --orig_master_ssh_user=root --new_master_ssh_user=root   --orig_master_is_new_slave
Fri Jun 16 10:30:45 2017 744690 Set read_only=0 on the new master.
Fri Jun 16 10:30:45 2017 745124 Creating app user on the new master..
Fri Jun 16 10:30:45 2017 - [info]  ok.
Fri Jun 16 10:30:45 2017 - [info] 
Fri Jun 16 10:30:45 2017 - [info] * Switching slaves in parallel..
Fri Jun 16 10:30:45 2017 - [info] 
Fri Jun 16 10:30:45 2017 - [info] -- Slave switch on host 192.168.0.30(192.168.0.30:3306) started, pid: 9457
Fri Jun 16 10:30:45 2017 - [info] 
Fri Jun 16 10:30:45 2017 - [info] Log messages from 192.168.0.30 ...
Fri Jun 16 10:30:45 2017 - [info] 
Fri Jun 16 10:30:45 2017 - [info]  Waiting to execute all relay logs on 192.168.0.30(192.168.0.30:3306)..
Fri Jun 16 10:30:45 2017 - [info]  master_pos_wait(mysql-bin.000001:1610) completed on 192.168.0.30(192.168.0.30:3306). Executed 0 events.
Fri Jun 16 10:30:45 2017 - [info]   done.
Fri Jun 16 10:30:45 2017 - [info]  Resetting slave 192.168.0.30(192.168.0.30:3306) and starting replication from the new master 192.168.0.40(192.168.0.40:3306)..
Fri Jun 16 10:30:45 2017 - [info]  Executed CHANGE MASTER.
Fri Jun 16 10:30:45 2017 - [info]  Slave started.
Fri Jun 16 10:30:45 2017 - [info] End of log messages from 192.168.0.30 ...
Fri Jun 16 10:30:45 2017 - [info] 
Fri Jun 16 10:30:45 2017 - [info] -- Slave switch on host 192.168.0.30(192.168.0.30:3306) succeeded.
Fri Jun 16 10:30:45 2017 - [info] Unlocking all tables on the orig master:
Fri Jun 16 10:30:45 2017 - [info] Executing UNLOCK TABLES..
Fri Jun 16 10:30:45 2017 - [info]  ok.
Fri Jun 16 10:30:45 2017 - [info] Starting orig master as a new slave..
Fri Jun 16 10:30:45 2017 - [info]  Resetting slave 192.168.0.20(192.168.0.20:3306) and starting replication from the new master 192.168.0.40(192.168.0.40:3306)..
Fri Jun 16 10:30:45 2017 - [info]  Executed CHANGE MASTER.
Fri Jun 16 10:30:46 2017 - [info]  Slave started.
Fri Jun 16 10:30:46 2017 - [info] All new slave servers switched successfully.
Fri Jun 16 10:30:46 2017 - [info] 
Fri Jun 16 10:30:46 2017 - [info] * Phase 5: New master cleanup phase..
Fri Jun 16 10:30:46 2017 - [info] 
Fri Jun 16 10:30:46 2017 - [info]  192.168.0.40: Resetting slave info succeeded.
Fri Jun 16 10:30:46 2017 - [info] Switching master to 192.168.0.40(192.168.0.40:3306) completed successfully.
复制代码

可以看见已经成功切换。我们再来看看运行主机组的状态:

复制代码
[admin@127.0.0.1][(none)]> select * from runtime_mysql_servers;
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
| hostgroup_id | hostname     | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment          |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
| 100          | 192.168.0.40 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
| 1000         | 192.168.0.30 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
| 1000         | 192.168.0.40 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
| 1000         | 192.168.0.20 | 3306 | ONLINE | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
4 rows in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

可以看见40自动设置成了主机组100,也就是master。我们进行读写测试。

复制代码
[admin@127.0.0.1][(none)]> select * from stats_mysql_query_digest;
+-----------+------------+----------+--------------------+-----------------------------+------------+------------+------------+----------+----------+----------+
| hostgroup | schemaname | username | digest             | digest_text                 | count_star | first_seen | last_seen  | sum_time | min_time | max_time |
+-----------+------------+----------+--------------------+-----------------------------+------------+------------+------------+----------+----------+----------+
| 1000      | yayun      | yayun    | 0x3765930C7143F468 | select * from t1            | 1          | 1497580568 | 1497580568 | 34920    | 34920    | 34920    |
| 100       | yayun      | yayun    | 0x5A680F86B3D8FB2B | select * from t1 for update | 1          | 1497580565 | 1497580565 | 9609     | 9609     | 9609     |
| 100       | yayun      | yayun    | 0x4BBB5CD4BC2CFD94 | insert into t1 select ?     | 1          | 1497580557 | 1497580557 | 133003   | 133003   | 133003   |
| 100       | yayun      | yayun    | 0x99531AEFF718C501 | show tables                 | 1          | 1497580544 | 1497580544 | 2051     | 2051     | 2051     |
| 100       | yayun      | yayun    | 0x74A739578E179F19 | show processlist            | 1          | 1497580532 | 1497580532 | 4335     | 4335     | 4335     |
+-----------+------------+----------+--------------------+-----------------------------+------------+------------+------------+----------+----------+----------+
5 rows in set (0.00 sec)

[admin@127.0.0.1][(none)]> 
复制代码

可以看见除了for update语句和insert发送到主库,查询是发送到了从库。当然虽然1000主机组里面有40,也就是会有非常少量的查询才会发送到主库。这个没法避免。
下面进行主库宕机测试,启动mha监控,关闭主库。mha切换日志,可以看见30当选主库:

复制代码
[root@db_server_yayun_04 log]# cat manager.log 
Fri Jun 16 10:38:59 2017 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Fri Jun 16 10:38:59 2017 - [info] Reading application default configuration from /data/mha/3306/mha.cnf..
Fri Jun 16 10:38:59 2017 - [info] Updating application default configuration from /usr/local/bin/load_cnf..
Fri Jun 16 10:38:59 2017 - [info] Reading server configuration from /data/mha/3306/mha.cnf..
Fri Jun 16 10:38:59 2017 - [info] Setting max_ping_errors to 10, ping_interval to 3.
Fri Jun 16 10:39:53 2017 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Fri Jun 16 10:39:53 2017 - [info] Reading application default configuration from /data/mha/3306/mha.cnf..
Fri Jun 16 10:39:53 2017 - [info] Updating application default configuration from /usr/local/bin/load_cnf..
Fri Jun 16 10:39:53 2017 - [info] Reading server configuration from /data/mha/3306/mha.cnf..
Fri Jun 16 10:39:53 2017 - [info] Setting max_ping_errors to 10, ping_interval to 3.
nfo]   192.168.0.20(192.168.0.20:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:38:59 2017 - [info]     GTID ON
Fri Jun 16 10:38:59 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:38:59 2017 - [info]   192.168.0.30(192.168.0.30:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:38:59 2017 - [info]     GTID ON
Fri Jun 16 10:38:59 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:38:59 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Fri Jun 16 10:38:59 2017 - [info] Current Alive Master: 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:38:59 2017 - [info] Checking slave configurations..
Fri Jun 16 10:38:59 2017 - [info] Checking replication filtering settings..
Fri Jun 16 10:38:59 2017 - [info]  binlog_do_db= , binlog_ignore_db= 
Fri Jun 16 10:38:59 2017 - [info]  Replication filtering check ok.
Fri Jun 16 10:38:59 2017 - [info] GTID (with auto-pos) is supported. Skipping all SSH and Node package checking.
Fri Jun 16 10:38:59 2017 - [info] Checking SSH publickey authentication settings on the current master..
Fri Jun 16 10:38:59 2017 - [info] HealthCheck: SSH to 192.168.0.40 is reachable.
Fri Jun 16 10:38:59 2017 - [info] 
192.168.0.40(192.168.0.40:3306) (current master)
 +--192.168.0.20(192.168.0.20:3306)
 +--192.168.0.30(192.168.0.30:3306)

Fri Jun 16 10:38:59 2017 - [warning] master_ip_failover_script is not defined.
Fri Jun 16 10:38:59 2017 - [warning] shutdown_script is not defined.
Fri Jun 16 10:38:59 2017 - [info] Set master ping interval 3 seconds.
Fri Jun 16 10:38:59 2017 - [warning] secondary_check_script is not defined. It is highly recommended setting it to check master reachability from two or more routes.
Fri Jun 16 10:38:59 2017 - [info] Starting ping health check on 192.168.0.40(192.168.0.40:3306)..
Fri Jun 16 10:38:59 2017 - [info] Ping(SELECT) succeeded, waiting until MySQL doesn't respond..
Fri Jun 16 10:39:26 2017 - [warning] Got error on MySQL select ping: 2006 (MySQL server has gone away)
Fri Jun 16 10:39:26 2017 - [info] Executing SSH check script: exit 0
Fri Jun 16 10:39:27 2017 - [info] HealthCheck: SSH to 192.168.0.40 is reachable.
Fri Jun 16 10:39:29 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:29 2017 - [warning] Connection failed 2 time(s)..
Fri Jun 16 10:39:32 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:32 2017 - [warning] Connection failed 3 time(s)..
Fri Jun 16 10:39:35 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:35 2017 - [warning] Connection failed 4 time(s)..
Fri Jun 16 10:39:38 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:38 2017 - [warning] Connection failed 5 time(s)..
Fri Jun 16 10:39:41 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:41 2017 - [warning] Connection failed 6 time(s)..
Fri Jun 16 10:39:44 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:44 2017 - [warning] Connection failed 7 time(s)..
Fri Jun 16 10:39:47 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:47 2017 - [warning] Connection failed 8 time(s)..
Fri Jun 16 10:39:50 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:50 2017 - [warning] Connection failed 9 time(s)..
Fri Jun 16 10:39:53 2017 - [warning] Got error on MySQL connect: 2013 (Lost connection to MySQL server at 'reading initial communication packet', system error: 111)
Fri Jun 16 10:39:53 2017 - [warning] Connection failed 10 time(s)..
Fri Jun 16 10:39:53 2017 - [warning] Master is not reachable from health checker!
Fri Jun 16 10:39:53 2017 - [warning] Master 192.168.0.40(192.168.0.40:3306) is not reachable!
Fri Jun 16 10:39:53 2017 - [warning] SSH is reachable.
Fri Jun 16 10:39:53 2017 - [info] Connecting to a master server failed. Reading configuration file /etc/masterha_default.cnf and /data/mha/3306/mha.cnf again, and trying to connect to all servers to check server status..
Fri Jun 16 10:39:53 2017 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Fri Jun 16 10:39:53 2017 - [info] Reading application default configuration from /data/mha/3306/mha.cnf..
Fri Jun 16 10:39:53 2017 - [info] Updating application default configuration from /usr/local/bin/load_cnf..
Fri Jun 16 10:39:53 2017 - [info] Reading server configuration from /data/mha/3306/mha.cnf..
Fri Jun 16 10:39:53 2017 - [info] Setting max_ping_errors to 10, ping_interval to 3.
Fri Jun 16 10:39:53 2017 - [info] GTID failover mode = 1
Fri Jun 16 10:39:53 2017 - [info] Dead Servers:
Fri Jun 16 10:39:53 2017 - [info]   192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:53 2017 - [info] Alive Servers:
Fri Jun 16 10:39:53 2017 - [info]   192.168.0.20(192.168.0.20:3306)
Fri Jun 16 10:39:53 2017 - [info]   192.168.0.30(192.168.0.30:3306)
Fri Jun 16 10:39:53 2017 - [info] Alive Slaves:
Fri Jun 16 10:39:53 2017 - [info]   192.168.0.20(192.168.0.20:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:53 2017 - [info]     GTID ON
Fri Jun 16 10:39:53 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:53 2017 - [info]   192.168.0.30(192.168.0.30:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:53 2017 - [info]     GTID ON
Fri Jun 16 10:39:53 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:53 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Fri Jun 16 10:39:53 2017 - [info] Checking slave configurations..
Fri Jun 16 10:39:53 2017 - [info] Checking replication filtering settings..
Fri Jun 16 10:39:53 2017 - [info]  Replication filtering check ok.
Fri Jun 16 10:39:53 2017 - [info] Master is down!
Fri Jun 16 10:39:53 2017 - [info] Terminating monitoring script.
Fri Jun 16 10:39:53 2017 - [info] Got exit code 20 (Master dead).
Fri Jun 16 10:39:53 2017 - [info] MHA::MasterFailover version 0.57.
Fri Jun 16 10:39:53 2017 - [info] Starting master failover.
Fri Jun 16 10:39:53 2017 - [info] 
Fri Jun 16 10:39:53 2017 - [info] * Phase 1: Configuration Check Phase..
Fri Jun 16 10:39:53 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] GTID failover mode = 1
Fri Jun 16 10:39:54 2017 - [info] Dead Servers:
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:54 2017 - [info] Checking master reachability via MySQL(double check)...
Fri Jun 16 10:39:54 2017 - [info]  ok.
Fri Jun 16 10:39:54 2017 - [info] Alive Servers:
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.20(192.168.0.20:3306)
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.30(192.168.0.30:3306)
Fri Jun 16 10:39:54 2017 - [info] Alive Slaves:
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.20(192.168.0.20:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:54 2017 - [info]     GTID ON
Fri Jun 16 10:39:54 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.30(192.168.0.30:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:54 2017 - [info]     GTID ON
Fri Jun 16 10:39:54 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:54 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Fri Jun 16 10:39:54 2017 - [info] Starting GTID based failover.
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] ** Phase 1: Configuration Check Phase completed.
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] * Phase 2: Dead Master Shutdown Phase..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] Forcing shutdown so that applications never connect to the current master..
Fri Jun 16 10:39:54 2017 - [warning] master_ip_failover_script is not set. Skipping invalidating dead master IP address.
Fri Jun 16 10:39:54 2017 - [warning] shutdown_script is not set. Skipping explicit shutting down of the dead master.
Fri Jun 16 10:39:54 2017 - [info] * Phase 2: Dead Master Shutdown Phase completed.
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] * Phase 3: Master Recovery Phase..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] * Phase 3.1: Getting Latest Slaves Phase..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] The latest binary log file/position on all slaves is mysql-bin.000002:497
Fri Jun 16 10:39:54 2017 - [info] Retrieved Gtid Set: 900f20f2-f48c-11e6-8d78-000c2930a8b9:1
Fri Jun 16 10:39:54 2017 - [info] Latest slaves (Slaves that received relay log files to the latest):
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.20(192.168.0.20:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:54 2017 - [info]     GTID ON
Fri Jun 16 10:39:54 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.30(192.168.0.30:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:54 2017 - [info]     GTID ON
Fri Jun 16 10:39:54 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:54 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Fri Jun 16 10:39:54 2017 - [info] The oldest binary log file/position on all slaves is mysql-bin.000002:497
Fri Jun 16 10:39:54 2017 - [info] Retrieved Gtid Set: 900f20f2-f48c-11e6-8d78-000c2930a8b9:1
Fri Jun 16 10:39:54 2017 - [info] Oldest slaves:
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.20(192.168.0.20:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:54 2017 - [info]     GTID ON
Fri Jun 16 10:39:54 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.30(192.168.0.30:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:54 2017 - [info]     GTID ON
Fri Jun 16 10:39:54 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:54 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] * Phase 3.3: Determining New Master Phase..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] Searching new master from slaves..
Fri Jun 16 10:39:54 2017 - [info]  Candidate masters from the configuration file:
Fri Jun 16 10:39:54 2017 - [info]   192.168.0.30(192.168.0.30:3306)  Version=5.7.17-log (oldest major version between slaves) log-bin:enabled
Fri Jun 16 10:39:54 2017 - [info]     GTID ON
Fri Jun 16 10:39:54 2017 - [info]     Replicating from 192.168.0.40(192.168.0.40:3306)
Fri Jun 16 10:39:54 2017 - [info]     Primary candidate for the new Master (candidate_master is set)
Fri Jun 16 10:39:54 2017 - [info]  Non-candidate masters:
Fri Jun 16 10:39:54 2017 - [info]  Searching from candidate_master slaves which have received the latest relay log events..
Fri Jun 16 10:39:54 2017 - [info] New master is 192.168.0.30(192.168.0.30:3306)
Fri Jun 16 10:39:54 2017 - [info] Starting master failover..
Fri Jun 16 10:39:54 2017 - [info] 
From:
192.168.0.40(192.168.0.40:3306) (current master)
 +--192.168.0.20(192.168.0.20:3306)
 +--192.168.0.30(192.168.0.30:3306)

To:
192.168.0.30(192.168.0.30:3306) (new master)
 +--192.168.0.20(192.168.0.20:3306)
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] * Phase 3.3: New Master Recovery Phase..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info]  Waiting all logs to be applied.. 
Fri Jun 16 10:39:54 2017 - [info]   done.
Fri Jun 16 10:39:54 2017 - [info]  Replicating from the latest slave 192.168.0.20(192.168.0.20:3306) and waiting to apply..
Fri Jun 16 10:39:54 2017 - [info]  Waiting all logs to be applied on the latest slave.. 
Fri Jun 16 10:39:54 2017 - [info]  Resetting slave 192.168.0.30(192.168.0.30:3306) and starting replication from the new master 192.168.0.20(192.168.0.20:3306)..
Fri Jun 16 10:39:54 2017 - [info]  Executed CHANGE MASTER.
Fri Jun 16 10:39:54 2017 - [info]  Slave started.
Fri Jun 16 10:39:54 2017 - [info]  Waiting to execute all relay logs on 192.168.0.30(192.168.0.30:3306)..
Fri Jun 16 10:39:54 2017 - [info]  master_pos_wait(mysql-bin.000001:1879) completed on 192.168.0.30(192.168.0.30:3306). Executed 4 events.
Fri Jun 16 10:39:54 2017 - [info]   done.
Fri Jun 16 10:39:54 2017 - [info]   done.
Fri Jun 16 10:39:54 2017 - [info] Getting new master's binlog name and position..
Fri Jun 16 10:39:54 2017 - [info]  mysql-bin.000002:459
Fri Jun 16 10:39:54 2017 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='192.168.0.30', MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='xxx';
Fri Jun 16 10:39:54 2017 - [info] Master Recovery succeeded. File:Pos:Exec_Gtid_Set: mysql-bin.000002, 459, 56195f28-36e2-11e7-991b-000c29e3f5ab:1-6,
900f20f2-f48c-11e6-8d78-000c2930a8b9:1
Fri Jun 16 10:39:54 2017 - [warning] master_ip_failover_script is not set. Skipping taking over new master IP address.
Fri Jun 16 10:39:54 2017 - [info] Setting read_only=0 on 192.168.0.30(192.168.0.30:3306)..
Fri Jun 16 10:39:54 2017 - [info]  ok.
Fri Jun 16 10:39:54 2017 - [info] ** Finished master recovery successfully.
Fri Jun 16 10:39:54 2017 - [info] * Phase 3: Master Recovery Phase completed.
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] * Phase 4: Slaves Recovery Phase..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] * Phase 4.1: Starting Slaves in parallel..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] -- Slave recovery on host 192.168.0.20(192.168.0.20:3306) started, pid: 9905. Check tmp log /data/mha/3306/log/192.168.0.20_3306_20170616103953.log if it takes time..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] Log messages from 192.168.0.20 ...
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info]  Resetting slave 192.168.0.20(192.168.0.20:3306) and starting replication from the new master 192.168.0.30(192.168.0.30:3306)..
Fri Jun 16 10:39:54 2017 - [info]  Executed CHANGE MASTER.
Fri Jun 16 10:39:54 2017 - [info]  Slave started.
Fri Jun 16 10:39:54 2017 - [info]  gtid_wait(56195f28-36e2-11e7-991b-000c29e3f5ab:1-6,
900f20f2-f48c-11e6-8d78-000c2930a8b9:1) completed on 192.168.0.20(192.168.0.20:3306). Executed 0 events.
Fri Jun 16 10:39:54 2017 - [info] End of log messages from 192.168.0.20.
Fri Jun 16 10:39:54 2017 - [info] -- Slave on host 192.168.0.20(192.168.0.20:3306) started.
Fri Jun 16 10:39:54 2017 - [info] All new slave servers recovered successfully.
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] * Phase 5: New master cleanup phase..
Fri Jun 16 10:39:54 2017 - [info] 
Fri Jun 16 10:39:54 2017 - [info] Resetting slave info on the new master..
Fri Jun 16 10:39:54 2017 - [info]  192.168.0.30: Resetting slave info succeeded.
Fri Jun 16 10:39:54 2017 - [info] Master failover to 192.168.0.30(192.168.0.30:3306) completed successfully.
Fri Jun 16 10:39:54 2017 - [info] 

----- Failover Report -----

mha: MySQL Master failover 192.168.0.40(192.168.0.40:3306) to 192.168.0.30(192.168.0.30:3306) succeeded

Master 192.168.0.40(192.168.0.40:3306) is down!

Check MHA Manager logs at db_server_yayun_04:/data/mha/3306/log/manager.log for details.

Started automated(non-interactive) failover.
Selected 192.168.0.30(192.168.0.30:3306) as a new master.
192.168.0.30(192.168.0.30:3306): OK: Applying all logs succeeded.
192.168.0.20(192.168.0.20:3306): OK: Slave started, replicating from 192.168.0.30(192.168.0.30:3306)
192.168.0.30(192.168.0.30:3306): Resetting slave info succeeded.
Master failover to 192.168.0.30(192.168.0.30:3306) completed successfully.
复制代码

再来看看proxysql里面现在主机组的情况:

复制代码
[admin@127.0.0.1][(none)]> select * from runtime_mysql_servers;
+--------------+--------------+------+---------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
| hostgroup_id | hostname     | port | status  | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment          |
+--------------+--------------+------+---------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
| 100          | 192.168.0.30 | 3306 | ONLINE  | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
| 1000         | 192.168.0.30 | 3306 | ONLINE  | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
| 1000         | 192.168.0.40 | 3306 | SHUNNED | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
| 1000         | 192.168.0.20 | 3306 | ONLINE  | 1      | 0           | 1000            | 10                  | 0       | 100            | test my proxysql |
+--------------+--------------+------+---------+--------+-------------+-----------------+---------------------+---------+----------------+------------------+
4 rows in set (0.01 sec)

[admin@127.0.0.1][(none)]> 
复制代码

可以看见40已经是SHUNNED状态,这个已经自动被踢出。30当选主库,自动被设置在主机组100. 再次运行读写测试:

复制代码
[admin@127.0.0.1][(none)]> select * from stats_mysql_query_digest;
+-----------+--------------------+----------+--------------------+----------------------------------+------------+------------+------------+----------+----------+----------+
| hostgroup | schemaname         | username | digest             | digest_text                      | count_star | first_seen | last_seen  | sum_time | min_time | max_time |
+-----------+--------------------+----------+--------------------+----------------------------------+------------+------------+------------+----------+----------+----------+
| 100       | yayun              | yayun    | 0x4BBB5CD4BC2CFD94 | insert into t1 select ?          | 1          | 1497581038 | 1497581038 | 114743   | 114743   | 114743   |
| 1000      | information_schema | yayun    | 0x620B328FE9D6D71A | SELECT DATABASE()                | 1          | 1497581026 | 1497581026 | 31128    | 31128    | 31128    |
| 100       | information_schema | yayun    | 0x594F2C744B698066 | select USER()                    | 1          | 1497581025 | 1497581025 | 0        | 0        | 0        |
| 1000      | yayun              | yayun    | 0x3765930C7143F468 | select * from t1                 | 1          | 1497581045 | 1497581045 | 3283     | 3283     | 3283     |
| 100       | information_schema | yayun    | 0x226CD90D52A2BA0B | select @@version_comment limit ? | 1          | 1497581025 | 1497581025 | 0        | 0        | 0        |
+-----------+--------------------+----------+--------------------+----------------------------------+------------+------------+------------+----------+----------+----------+
5 rows in set (0.00 sec)
复制代码

可以看见依然没有问题。到这里本文就结束了,上面是1主2从,如果是1主1从,那么从挂了怎么办呢? 需要额外一条规则,那就是在mysql_servers的hostgroup 1000 里面要插一条主库的记录,然后把weight设小,当读不到从库,回去主库查询

关于ProxySQL比较详细的文章可以参考如下:


ProxySQL之安装及配置详解

ProxySQL之读写分离与分库路由演示

 

作者:Atlas

出处:Atlas的博客  http://www.cnblogs.com/gomysql

您的支持是对博主最大的鼓励,感谢您的认真阅读。本文版权归作者所有,欢迎转载,但请保留该声明。如果您需要技术支持,本人亦提供有偿服务。



MHA高可用架构与Atlas读写分离 - 惨绿少年 - 博客园

$
0
0

1.1 MHA简介

1.1.1 MHA软件介绍

  MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中, MHA能做到在10~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。

  MHA能够在较短的时间内实现自动故障检测和故障转移,通常在10-30秒以内;在复制 框架中,MHA能够很好地解决复制过程中的数据一致性问题,由于不需要在现有的 replication中添加额外的服务器,仅需要一个manager节点,而一个Manager能管理多套复制,所以能大大地节约服务器的数量;另外,安装简单,无性能损耗,以及不需要修改现 有的复制部署也是它的优势之处。

  MHA还提供在线主库切换的功能,能够安全地切换当前运行的主库到一个新的主库中 (通过将从库提升为主库),大概 0.5-2秒内即可完成。

  该软件由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上,MHA Manager会定时探测集群中的master节点, master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master整个故障转移过程对应用程序完全透明。

  在MHA自动故障切换过程中,MHA试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制,可以大大降低数据丢失的风险。

  MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志,MHA可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。

  目前MHA主要支持一主多从的架构,要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当master,一台充当备用master,另外一台充当从库,因为至少需要三台服务器,出于机器成本的考虑,淘宝也在该基础上进行了改造,目前淘宝 TMHA已经支持一主一从。

1.1.2 MHA工作原理

 

工作原理说明:

 

   1、保存master上的所有binlog事件

    2、找到含有最新binlog位置点的slave

    3、通过中继日志将数据恢复到其他的slave

    4、将包含最新binlog位置点的slave提升为master

    5、将其他从库slave指向新的master原slave01 并开启主从复制

    6、将保存下来的binlog恢复到新的master上

 

1、监控所有node节点MHA功能说明:

2、自动故障切换(failover)

     前提是必须有三个节点存在,并且有两个从库

      (1)选主前提,按照配置文件的顺序进行,但是如果此节点后主库100M以上relay-log 就不会选

      (2)如果你设置了权重,总会切换带此节点;一般在多地多中心的情况下,一般会把权重设置在本地节点。

      (3)选择s1为新主

      (4)保存主库binlog日志

3、重新构建主从

      (1)将有问题的节点剔除MHA

          进行第一阶段数据补偿,S2缺失部分补全90

      (2)s1切换角色为新主,将s2指向新主S1

            s2  change master to s1

      (3) 第二阶段数据补偿

            将保存过来的新主和原有主缺失部分的binlog,应用到新主。

      (4)虚拟IP漂移到新主,对应用透明无感知

      (5)通知管理员故障切换

1.1.3 MHA高可用架构图

 

1.1.4 MHA工具介绍

  MHA软件由两部分组成,Manager工具包和Node工具包,具体的说明如下:

  Manager工具包主要包括以下几个工具:

masterha_check_ssh             #检査 MHA 的 ssh-key^
masterha_check_repl            #检査主从复制情况
masterha_manger                #启动MHA
masterha_check_status          #检测MHA的运行状态^
masterha_mast er_monitor       #检测master是否宕机一
masterha_mast er_switch        #手动故障转移—
masterha_conf_host             #手动添加server倍息一
masterha_secondary_check       #建立TCP连接从远程服务器v
masterha_stop                  #停止MHA

Node工具包主要包括以下几个工具:    

save_binary_1ogs       #保存宕机的master的binlog
apply_diff_relay_logs   #识别relay log的差异
filter_mysqlbinlog           #防止回滚事件一MHA已不再使用这个工具
purge_relay_logs           #清除中继曰志一不会阻塞SQL线程

1.1.5 MHA的优点

1、自动故障转移

2、主库崩溃不存在数据不一致的情况

3、不需要对当前的mysql环境做重大修改

4、不需要添加额外的服务器

5、性能优秀,可以工作再半同步和异步复制框架

6、只要replication支持的存储引擎mha都支持

1.2 环境说明

   在本次的实验中,共需要用到三台主机,系统、软件说明如下。

1.2.1 系统环境说明

db01主机(master)

[root@db01 ~]# cat /etc/redhat-release 
CentOS release 6.9 (Final)
[root@db01 ~]# uname -r
2.6.32-696.el6.x86_64
[root@db01 ~]# /etc/init.d/iptables status
iptables: Firewall is not running.
[root@db01 ~]# getenforce 
Disabled
[root@db01 ~]# hostname -I
10.0.0.51 172.16.1.51

db02主机(slave1)

 1 [root@db02 ~]# cat /etc/redhat-release 
 2 CentOS release 6.9 (Final)
 3 [root@db02 ~]# uname -r
 4 2.6.32-696.el6.x86_64
 5 [root@db02 ~]# /etc/init.d/iptables status
 6 iptables: Firewall is not running.
 7 [root@db02 ~]# getenforce 
 8 Disabled
 9 [root@db02 ~]# hostname -I
10 10.0.0.52 172.16.1.52

db03主机(slave1,MHA Manages、Atlas节点)

 1 [root@db03 ~]# cat /etc/redhat-release 
 2 CentOS release 6.9 (Final)
 3 [root@db03 ~]# uname -r
 4 2.6.32-696.el6.x86_64
 5 [root@db03 ~]# /etc/init.d/iptables status
 6 iptables: Firewall is not running.
 7 [root@db03 ~]# getenforce 
 8 Disabled
 9 [root@db03 ~]# hostname -I
10 10.0.0.53 172.16.1.53

1.2.2 mysql软件说明

         三台服务器上都全新安装mysql 5.6.36 :

[root@db01 ~]# mysql --version
mysql  Ver 14.14 Distrib 5.6.36, for Linux (x86_64) using  EditLine wrapper

  关于mysql数据库具体的安装方法参考: http://www.cnblogs.com/clsn/p/8038964.html#_label3

1.3 基于GTID的主从复制配置

1.3.1 先决条件

  🔊 主库和从库都要开启binlog

  🔊 主库和从库server-id必须不同

  🔊 要有主从复制用户

1.3.2 配置主从复制

db01 my.cnf文件

[root@db01 ~]# cat /etc/my.cnf
[mysqld]
basedir=/application/mysql
datadir=/application/mysql/data
socket=/tmp/mysql.sock
log-error=/var/log/mysql.log
log-bin=/data/mysql/mysql-bin
binlog_format=row
secure-file-priv=/tmp 
server-id=51
skip-name-resolve  # 跳过域名解析
gtid-mode=on    # 启用gtid类型,否则就是普通的复制架构
enforce-gtid-consistency=true    #强制GTID的一致性
log-slave-updates=1     # slave更新是否记入日志(5.6必须的)
relay_log_purge = 0 
[mysql]
socket=/tmp/mysql.sock

db02 my.cnf文件

 1 [root@db02 ~]# cat /etc/my.cnf
 2 [mysqld]
 3 basedir=/application/mysql
 4 datadir=/application/mysql/data
 5 socket=/tmp/mysql.sock
 6 log-error=/var/log/mysql.log
 7 log-bin=/data/mysql/mysql-bin
 8 binlog_format=row
 9 secure-file-priv=/tmp 
10 server-id=52
11 skip-name-resolve
12 gtid-mode=on
13 enforce-gtid-consistency=true
14 log-slave-updates=1
15 relay_log_purge = 0 
16 [mysql]
17 socket=/tmp/mysql.sock

db03 my.cnf文件

 1 [root@db03 ~]# cat /etc/my.cnf
 2 [mysqld]
 3 basedir=/application/mysql
 4 datadir=/application/mysql/data
 5 socket=/tmp/mysql.sock
 6 log-error=/var/log/mysql.log
 7 log-bin=/data/mysql/mysql-bin
 8 binlog_format=row
 9 secure-file-priv=/tmp 
10 server-id=53
11 skip-name-resolve
12 gtid-mode=on
13 enforce-gtid-consistency=true
14 log-slave-updates=1
15 relay_log_purge = 0 
16 skip-name-resolve
17 [mysql]
18 socket=/tmp/mysql.sock

创建复制用户 (51作为主节点,52、53为从)

GRANT REPLICATION SLAVE ON *.* TO repl@'10.0.0.%' IDENTIFIED BY '123';

从库开启复制

change master to 
    master_host='10.0.0.51',
    master_user='repl',
    master_password='123',
    MASTER_AUTO_POSITION=1;

启动从库复制

start slave;

1.3.3 GTID复制技术说明

MySQL GTID简介

  GTID的全称为 global transaction identifier ,可以翻译为全局事务标示符,GTID在原始master上的事务提交时被创建。GTID需要在全局的主-备拓扑结构中保持唯一性,GTID由两部分组成:

   GTID = source_id:transaction_id 

   source_id用于标示源服务器,用server_uuid来表示,这个值在第一次启动时生成,并写入到配置文件data/auto.cnf中

   transaction_id则是根据在源服务器上第几个提交的事务来确定。

GTID事件结构

 

GTID在二进制日志中的结构

  4

一个GTID的生命周期包括:

1.事务在主库上执行并提交给事务分配一个gtid(由主库的uuid和该服务器上未使用的最小事务序列号),该GTID被写入到binlog中。

2.备库读取relaylog中的gtid,并设置session级别的gtid_next的值,以告诉备库下一个事务必须使用这个值

3.备库检查该gtid是否已经被其使用并记录到他自己的binlog中。slave需要担保之前的事务没有使用这个gtid,也要担保此时已分读取gtid,但未提交的事务也不恩呢过使用这个gtid.

4.由于gtid_next非空,slave不会去生成一个新的gtid,而是使用从主库获得的gtid。这可以保证在一个复制拓扑中的同一个事务gtid不变。由于GTID在全局的唯一性,通过GTID,我们可以在自动切换时对一些复杂的复制拓扑很方便的提升新主库及新备库,例如通过指向特定的GTID来确定新备库复制坐标。

    GTID是用来替代以前classic的复制方法;

    MySQL5.6.2支持 MySQL5.6.10后完善;

GTID相比传统复制的优点:

1.一个事务对应一个唯一ID,一个GTID在一个服务器上只会执行一次

2.GTID是用来代替传统复制的方法,GTID复制与普通复制模式的最大不同就是不需要指定二进制文件名和位置

3.减少手工干预和降低服务故障时间,当主机挂了之后通过软件从众多的备机中提升一台备机为主机

GTID的限制:

1.不支持非事务引擎

2.不支持create table ... select 语句复制(主库直接报错)

  原理:( 会生成两个sql,一个是DDL创建表SQL,一个是insert into 插入数据的sql。

  由于DDL会导致自动提交,所以这个sql至少需要两个GTID,但是GTID模式下,只能给这个sql生成一个GTID )  

3.不允许一个SQL同时更新一个事务引擎表和非事务引擎表

4.在一个复制组中,必须要求统一开启GTID或者是关闭GTID

5.开启GTID需要重启(5.7除外)

6.开启GTID后,就不再使用原来的传统复制方式

7.对于create temporary table 和 drop temporary table语句不支持

8.不支持sql_slave_skip_counter

1.3.4 COM_BINLOG_DUMP_GTID

从机发送到主机执行的事务的标识符的主范围

   Master send all other transactions to slave 

  同样的GTID不能被执行两次,如果有同样的GTID,会自动被skip掉。

 

  slave1:将自己的UUID1:1发送给master,然后接收到了UUID1:2,UUID1:3 event

  slave2:将自己的UUID1:1,UUID1:2发送给master,然后接收到了UUID1:3事件

GTID组成

  GTID实际上是由UUID+TID组成的。其中UUID是一个MySQL实例的唯一标识。TID代表了该实例上已经提交的事务数量,并且随着事务提交单调递增

GTID = source_id :transaction_id
7E11FA47-31CA-19E1-9E56-C43AA21293967:29

1.3.5 【示例二】MySQL GTID复制配置

主节点my.cnf文件

# vi /etc/my.cnf
[mysqld]
basedir=/usr/local/mysql
datadir=/data/mysql
server-id=1
log-bin=mysql-bin
socket=/tmp/mysql.sock
binlog-format=ROW
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1

从节点my.cnf文件

# vi /etc/my.cnf
[mysqld]
basedir=/usr/local/mysql
datadir=/data/mysql
server-id=2
binlog-format=ROW
gtid-mode=on
enforce-gtid-consistency=true
log-bin=mysql-bin
log_slave_updates = 1
socket=/tmp/mysql.sock

配置文件注解

server-id=x                    # 同一个复制拓扑中的所有服务器的id号必须惟一
binlog-format=RO               # 二进制日志格式,强烈建议为ROW
gtid-mode=on                   # 启用gtid类型,否则就是普通的复制架构
enforce-gtid-consistency=true  # 强制GTID的一致性
log-slave-updates=1            # slave更新是否记入日志

  复制用户准备(Master主节点)

mysql>GRANT REPLICATION SLAVE ON *.* TO rep@'10.0.0.%' IDENTIFIED BY '123';

  开启复制(Slave从节点)

mysql>start slave;
mysql>show slave status\G

  现在就可以进行主从复制测试。

1.4 部署MHA

  本次MHA的部署基于GTID复制成功构建,普通主从复制也可以构建MHA架构。

1.4.1 环境准备(所有节点操作)

安装依赖包

yum install perl-DBD-MySQL -y

    下载mha软件,mha官网: https://code.google.com/archive/p/mysql-master-ha/

         github下载地址: https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads

下载软件包

mha4mysql-manager-0.56-0.el6.noarch.rpm

mha4mysql-manager-0.56.tar.gz

mha4mysql-node-0.56-0.el6.noarch.rpm

mha4mysql-node-0.56.tar.gz

在所有节点安装node

rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm

创建mha管理用户

grant all privileges on *.* to mha@'10.0.0.%' identified by 'mha';

# 主库上创建,从库会自动复制(在从库上查看)

  创建命令软连接 (重要)

  如果不创建命令软连接,检测mha复制情况的时候会报错

ln -s /application/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
ln -s /application/mysql/bin/mysql /usr/bin/mysql

1.4.2 部署管理节点(mha-manager)

在mysql-db03上部署管理节点

# 安装epel源,软件需要
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo 
# 安装manager 依赖包
yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes
# 安装manager管理软件
rpm -ivh mha4mysql-manager-0.56-0.el6.noarch.rpm

创建必须目录

mkdir -p /etc/mha
mkdir -p /var/log/mha/app1    ----》可以管理多套主从复制

编辑mha-manager配置文件

[root@db03 ~]# cat  /etc/mha/app1.cnf
[server default]                        
manager_log=/var/log/mha/app1/manager
manager_workdir=/var/log/mha/app1
master_binlog_dir=/data/mysql
user=mha
password=mha
ping_interval=2
repl_password=123
repl_user=repl
ssh_user=root

[server1]
hostname=10.0.0.51
port=3306

[server2]
hostname=10.0.0.52
port=3306

[server3]
hostname=10.0.0.53
port=3306

【配置文件详解】

 1 [server default]
 2 #设置manager的工作目录
 3 manager_workdir=/var/log/masterha/app1
 4 #设置manager的日志
 5 manager_log=/var/log/masterha/app1/manager.log 
 6 #设置master 保存binlog的位置,以便MHA可以找到master的日志,我这里的也就是mysql的数据目录
 7 master_binlog_dir=/data/mysql
 8 #设置自动failover时候的切换脚本
 9 master_ip_failover_script= /usr/local/bin/master_ip_failover
10 #设置手动切换时候的切换脚本
11 master_ip_online_change_script= /usr/local/bin/master_ip_online_change
12 #设置mysql中root用户的密码,这个密码是前文中创建监控用户的那个密码
13 password=123456
14 #设置监控用户root
15 user=root
16 #设置监控主库,发送ping包的时间间隔,尝试三次没有回应的时候自动进行failover
17 ping_interval=1
18 #设置远端mysql在发生切换时binlog的保存位置
19 remote_workdir=/tmp
20 #设置复制用户的密码
21 repl_password=123456
22 #设置复制环境中的复制用户名 
23 repl_user=rep
24 #设置发生切换后发送的报警的脚本
25 report_script=/usr/local/send_report
26 #一旦MHA到server02的监控之间出现问题,MHA Manager将会尝试从server03登录到server02
27 secondary_check_script= /usr/local/bin/masterha_secondary_check -s server03 -s server02 --user=root --master_host=server02 --master_ip=10.0.0.51 --master_port=3306
28 #设置故障发生后关闭故障主机脚本(该脚本的主要作用是关闭主机放在发生脑裂,这里没有使用)
29 shutdown_script=""
30 #设置ssh的登录用户名
31 ssh_user=root 
32 
33 [server1]
34 hostname=10.0.0.51
35 port=3306
36 
37 [server2]
38 hostname=10.0.0.52
39 port=3306
40 #设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中事件最新的slave
41 candidate_master=1
42 #默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master
43 check_repl_delay=0

配置ssh信任(密钥分发,在所有节点上执行)

# 生成密钥
ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa >/dev/null 2>&1
# 分发公钥,包括自己
for i in 1 2 3 ;do ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.0.0.5$i ;done

         分发完成后测试分发是否成功

for i in 1 2 3 ;do ssh 10.0.0.5$i  date ;done
或
[root@db03 ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf
最后一行信息为如下字样即为分发成功:
Thu Dec 28 18:44:53 2017 - [info] All SSH connection tests passed successfully.

1.4.3 启动mha

经过上面的部署过后,mha架构已经搭建完成

# 启动mha
nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 &

         启动成功后,检查主库状态

[root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf 
app1 (pid:3298) is running(0:PING_OK), master:10.0.0.51

1.4.4 切换master测试

查看现在的主库是哪个

[root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf 
app1 (pid:11669) is running(0:PING_OK), master:10.0.0.51

手动停止主库

[root@db01 ~]# /etc/init.d/mysqld stop 
Shutting down MySQL..... SUCCESS!

再停止数据的同时查看日志信息的变化

[root@db03 ~]# tailf /var/log/mha/app1/manager
~~~
Fri Dec 29 15:51:14 2017 - [info]  All other slaves should start replication from
here. Statement should be: CHANGE MASTER TO MASTER_HOST='10.0.0.52', MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='xxx';

修复主从

①  启动原主库,添加change master to 信息

[root@db01 ~]# /etc/init.d/mysqld start 
Starting MySQL. SUCCESS!
mysql> CHANGE MASTER TO MASTER_HOST='10.0.0.52', MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';
mysql> start slave;

②  查看主从复制状态

mysql> show slave status\G
                   Master_Host: 10.0.0.52
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

修复mha

①  修改app1.cnf配置文件,添加回被剔除主机

[root@db03 ~]# cat  /etc/mha/app1.cnf 
[binlog1]
hostname=10.0.0.53
master_binlog_dir=/data/mysql/binlog/
no_master=1

[server default]
manager_log=/var/log/mha/app1/manager
manager_workdir=/var/log/mha/app1
master_binlog_dir=/data/mysql
master_ip_failover_script=/usr/local/bin/master_ip_failover
password=mha
ping_interval=2
repl_password=123
repl_user=repl
ssh_user=root
user=mha

[server1]
hostname=10.0.0.51
port=3306

[server2]
hostname=10.0.0.52
port=3306

[server3]
hostname=10.0.0.53
port=3306

②  mha检查复制状态

[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
MySQL Replication Health is OK.

③  启动mha程序

nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 &

到此主库切换成功

[root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf 
app1 (pid:11978) is running(0:PING_OK), master:10.0.0.52

实验结束将主库切换回db01.

①  停止mha

[root@db03 ~]# masterha_stop --conf=/etc/mha/app1.cnf 
Stopped app1 successfully.

②  停止所有从库slave(所有库操作)

stop slave;
reset slave all;

③  重做主从复制(db02、db03)

CHANGE MASTER TO 
  MASTER_HOST='10.0.0.51', 
  MASTER_PORT=3306, 
  MASTER_AUTO_POSITION=1, 
  MASTER_USER='repl', 
  MASTER_PASSWORD='123';

④  启动slave

start slave;

                  启动之后检查从库是否为两个yes  show slave status\G 

⑤  mha检查主从复制

[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
MySQL Replication Health is OK.

⑥  启动mha

nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 &

  检查切换是否成功

[root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:12127) is running(0:PING_OK), master:10.0.0.51

         到此主主节点有切回到db01

1.4.5 设置权重

修改[server1]的权重

[server1]
hostname=10.0.0.51
port=3306
candidate_master=1
check_repl_delay=0

配置说明

candidate_master=1                  ----》不管怎样都切到优先级高的主机,一般在主机性能差异的时候用           
check_repl_delay=0                  ----》不管优先级高的备选库,数据延时多久都要往那切

注:

1、多地多中心,设置本地节点为高权重

2、在有半同步复制的环境中,设置半同步复制节点为高权重

3、你觉着哪个机器适合做主节点,配置较高的 、性能较好的

1.5 配置VIP漂移

1.5.1 IP漂移的两种方式

   🐶 通过keepalived的方式,管理虚拟IP的漂移

   🐶  通过MHA自带脚本方式,管理虚拟IP的漂移

1.5.2 MHA脚本方式

修改mha配置文件

[root@db03 ~]# grep "script" /etc/mha/app1.cnf 
[server default]
master_ip_failover_script=/usr/local/bin/master_ip_failover

         再主配置中添加VIP脚本

脚本内容

[root@db03 ~]# cat /usr/local/bin/master_ip_failover 
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Getopt::Long;
my (
    $command,          $ssh_user,        $orig_master_host, $orig_master_ip,
    $orig_master_port, $new_master_host, $new_master_ip,    $new_master_port
);
my $vip = '10.0.0.55/24';
my $key = '0';
my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig eth0:$key down";

GetOptions(
    'command=s'          => \$command,'ssh_user=s'         => \$ssh_user,'orig_master_host=s' => \$orig_master_host,'orig_master_ip=s'   => \$orig_master_ip,'orig_master_port=i' => \$orig_master_port,'new_master_host=s'  => \$new_master_host,'new_master_ip=s'    => \$new_master_ip,'new_master_port=i'  => \$new_master_port,
);

exit &main();

sub main {

    print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";

    if ( $command eq "stop" || $command eq "stopssh" ) {

        my $exit_code = 1;
        eval {
            print "Disabling the VIP on old master: $orig_master_host \n";&stop_vip();
            $exit_code = 0;
        };
        if ($@) {
            warn "Got Error: $@\n";
            exit $exit_code;
        }
        exit $exit_code;
    }
    elsif ( $command eq "start" ) {

        my $exit_code = 10;
        eval {
            print "Enabling the VIP - $vip on the new master - $new_master_host \n";&start_vip();
            $exit_code = 0;
        };
        if ($@) {
            warn $@;
            exit $exit_code;
        }
        exit $exit_code;
    }
    elsif ( $command eq "status" ) {
        print "Checking the Status of the script.. OK \n";
        exit 0;
    }
    else {&usage();
        exit 1;
    }
}

sub start_vip() {
    `ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
sub stop_vip() {
     return 0  unless  ($ssh_user);
    `ssh $ssh_user\@$orig_master_host \" $ssh_stop_vip \"`;
}

sub usage {
    print
    "Usage: master_ip_failover --command=start|stop|stopssh|status --orig_master_host=host --orig_master_ip=ip --orig_master_port=port --new_master_host=host --new_master_ip=ip --new_master_port=port\n";
}

  该脚本为软件自带,脚本获取方法:再mha源码包中的samples目录下有该脚本的模板,对该模板进行修改即可使用。路径如: mha4mysql-manager-0.56/samples/scripts 

  脚本修改内容

my $vip = '10.0.0.55/24';
my $key = '0';
my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig eth0:$key down";

脚本添加执行权限否则mha无法启动

chmod +x /usr/local/bin/master_ip_failover

手动绑定VIP(主库)

ifconfig eth0:0 10.0.0.55/24

  检查

[root@db01 ~]# ip a s eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:6c:7a:11 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.51/24 brd 10.0.0.255 scope global eth0
    inet 10.0.0.55/24 brd 10.0.0.255 scope global secondary eth0:0
    inet6 fe80::20c:29ff:fe6c:7a11/64 scope link 
       valid_lft forever preferred_lft forever

          至此vip漂移配置完成 

1.5.3 测试虚拟IP漂移

查看db02的slave信息

 1 mysql> show slave status\G
 2 *************************** 1. row ***************************
 3                Slave_IO_State: Waiting for master to send event
 4                   Master_Host: 10.0.0.51
 5                   Master_User: repl
 6                   Master_Port: 3306
 7                 Connect_Retry: 60
 8               Master_Log_File: mysql-bin.000007
 9           Read_Master_Log_Pos: 191
10                Relay_Log_File: db02-relay-bin.000002
11                 Relay_Log_Pos: 361
12         Relay_Master_Log_File: mysql-bin.000007
13              Slave_IO_Running: Yes
14             Slave_SQL_Running: Yes
15               Replicate_Do_DB: 
16           Replicate_Ignore_DB: 
17            Replicate_Do_Table: 
18        Replicate_Ignore_Table: 
19       Replicate_Wild_Do_Table: 
20   Replicate_Wild_Ignore_Table: 
21                    Last_Errno: 0
22                    Last_Error: 
23                  Skip_Counter: 0
24           Exec_Master_Log_Pos: 191
25               Relay_Log_Space: 564
26               Until_Condition: None
27                Until_Log_File: 
28                 Until_Log_Pos: 0
29            Master_SSL_Allowed: No
30            Master_SSL_CA_File: 
31            Master_SSL_CA_Path: 
32               Master_SSL_Cert: 
33             Master_SSL_Cipher: 
34                Master_SSL_Key: 
35         Seconds_Behind_Master: 0
36 Master_SSL_Verify_Server_Cert: No
37                 Last_IO_Errno: 0
38                 Last_IO_Error: 
39                Last_SQL_Errno: 0
40                Last_SQL_Error: 
41   Replicate_Ignore_Server_Ids: 
42              Master_Server_Id: 51
43                   Master_UUID: c8fcd56e-eb79-11e7-97b0-000c296c7a11
44              Master_Info_File: /application/mysql-5.6.36/data/master.info
45                     SQL_Delay: 0
46           SQL_Remaining_Delay: NULL
47       Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
48            Master_Retry_Count: 86400
49                   Master_Bind: 
50       Last_IO_Error_Timestamp: 
51      Last_SQL_Error_Timestamp: 
52                Master_SSL_Crl: 
53            Master_SSL_Crlpath: 
54            Retrieved_Gtid_Set: 
55             Executed_Gtid_Set: c8fcd56e-eb79-11e7-97b0-000c296c7a11:1-3
56                 Auto_Position: 1
57 1 row in set (0.00 sec)

停掉主库

[root@db01 ~]# /etc/init.d/mysqld stop

在db03上查看从库slave信息

 1 mysql>  show slave status\G
 2 *************************** 1. row ***************************
 3                Slave_IO_State: Waiting for master to send event
 4                   Master_Host: 10.0.0.52
 5                   Master_User: repl
 6                   Master_Port: 3306
 7                 Connect_Retry: 60
 8               Master_Log_File: mysql-bin.000009
 9           Read_Master_Log_Pos: 191
10                Relay_Log_File: db03-relay-bin.000002
11                 Relay_Log_Pos: 361
12         Relay_Master_Log_File: mysql-bin.000009
13              Slave_IO_Running: Yes
14             Slave_SQL_Running: Yes
15               Replicate_Do_DB: 
16           Replicate_Ignore_DB: 
17            Replicate_Do_Table: 
18        Replicate_Ignore_Table: 
19       Replicate_Wild_Do_Table: 
20   Replicate_Wild_Ignore_Table: 
21                    Last_Errno: 0
22                    Last_Error: 
23                  Skip_Counter: 0
24           Exec_Master_Log_Pos: 191
25               Relay_Log_Space: 564
26               Until_Condition: None
27                Until_Log_File: 
28                 Until_Log_Pos: 0
29            Master_SSL_Allowed: No
30            Master_SSL_CA_File: 
31            Master_SSL_CA_Path: 
32               Master_SSL_Cert: 
33             Master_SSL_Cipher: 
34                Master_SSL_Key: 
35         Seconds_Behind_Master: 0
36 Master_SSL_Verify_Server_Cert: No
37                 Last_IO_Errno: 0
38                 Last_IO_Error: 
39                Last_SQL_Errno: 0
40                Last_SQL_Error: 
41   Replicate_Ignore_Server_Ids: 
42              Master_Server_Id: 52
43                   Master_UUID: c8fa1d13-eb79-11e7-97b0-000c29d60ab3
44              Master_Info_File: /application/mysql-5.6.36/data/master.info
45                     SQL_Delay: 0
46           SQL_Remaining_Delay: NULL
47       Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
48            Master_Retry_Count: 86400
49                   Master_Bind: 
50       Last_IO_Error_Timestamp: 
51      Last_SQL_Error_Timestamp: 
52                Master_SSL_Crl: 
53            Master_SSL_Crlpath: 
54            Retrieved_Gtid_Set: 
55             Executed_Gtid_Set: c8fcd56e-eb79-11e7-97b0-000c296c7a11:1-3
56                 Auto_Position: 1
57 1 row in set (0.00 sec)

在db01上查看vip信息

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:6c:7a:11 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.51/24 brd 10.0.0.255 scope global eth0
    inet6 fe80::20c:29ff:fe6c:7a11/64 scope link 
       valid_lft forever preferred_lft forever

在db02上查看vip信息

 [root@db02 ~]# ip a s eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:d6:0a:b3 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.52/24 brd 10.0.0.255 scope global eth0
inet 10.0.0.55/24 brd 10.0.0.255 scope global secondary eth0:0
    inet6 fe80::20c:29ff:fed6:ab3/64 scope link 
       valid_lft forever preferred_lft forever

         至此,VIP漂移就测试成功

1.6 配置binlog-server

1.6.1 配置binlog-server

1)前期准备:

1、准备一台新的mysql实例(db03),GTID必须开启。

2、将来binlog接收目录,不能和主库binlog目录一样

2)停止mha

masterha_stop --conf=/etc/mha/app1.cnf

3)在app1.cnf开启binlogserver功能

    [binlog1]
    no_master=1
    hostname=10.0.0.53                         ----> 主机DB03
    master_binlog_dir=/data/mysql/binlog/  ----> binlog保存目录

4)开启binlog接收目录,注意权限

mkdir -p /data/mysql/binlog/ 
chown -R mysql.mysql /data/mysql
# 进入目录启动程序
  cd /data/mysql/binlog/ &&\
  mysqlbinlog  -R --host=10.0.0.51 --user=mha --password=mha --raw  --stop-never mysql-bin.000001 &

  参数说明:-R 远程主机

5)启动mha

nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 &

1.6.2 测试binlog备份

#查看binlog目录中的binlog

[root@db03 binlog]# ll
total 44
-rw-r--r-- 1 root root 285 Mar  8 03:11 mysql-bin.000001

#登录主库

[root@mysql-db01 ~]# mysql -uroot -p123

#刷新binlog

mysql> flush logs;

#再次查看binlog目录

[root@db03 binlog]# ll
total 48
-rw-r--r-- 1 root root 285 Mar  8 03:11 mysql-bin.000001
-rw-r--r-- 1 root root 143 Mar  8 04:00 mysql-bin.000002

1.7 mysql中间件Atlas

1.7.1 atlas简介

  Atlas是由 Qihoo 360公司Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基础上,修改了大量bug,添加了很多功能特性。目前该项目在360公司内部得到了广泛应用,很多MySQL业务已经接入了Atlas平台,每天承载的读写请求数达几十亿条。

  同时,有超过50家公司在生产环境中部署了Atlas,超过800人已加入了我们的开发者交流群,并且这些数字还在不断增加。而且安装方便。配置的注释写的蛮详细的,都是中文。

1.7.2 主要功能

  读写分离、从库负载均衡、自动分表、IP过滤

  SQL语句黑白名单、DBA可平滑上下线DB、自动摘除宕机的DB

Atlas相对于官方MySQL-Proxy的优势

1.将主流程中所有Lua代码用C重写,Lua仅用于管理接口

2.重写网络模型、线程模型

3.实现了真正意义上的连接池

 

4.优化了锁机制,性能提高数十倍 

1.7.3 使用场景

  Atlas是一个位于前端应用与后端MySQL数据库之间的中间件,它使得应用程序员无需再关心读写分离、分表等与MySQL相关的细节,可以专注于编写业务逻辑,同时使得DBA的运维工作对前端应用透明,上下线DB前端应用无感知。

 

  Atlas是一个位于应用程序与MySQL之间中间件。在后端DB看来,Atlas相当于连接它的客户端,在前端应用看来,Atlas相当于一个DB。

  Atlas作为服务端与应用程序通讯,它实现了MySQL的客户端和服务端协议,同时作为客户端与MySQL通讯。它对应用程序屏蔽了DB的细节,同时为了降低MySQL负担,它还维护了连接池.

1.7.4 企业读写分离及分库分表其他方案介绍

    Mysql-proxy(oracle)

    Mysql-router(oracle)

    Atlas (Qihoo 360)

    Atlas-sharding (Qihoo 360)

    Cobar(是阿里巴巴(B2B)部门开发)

    Mycat(基于阿里开源的Cobar产品而研发)

    TDDL Smart Client的方式(淘宝)

    Oceanus(58同城数据库中间件)

    OneProxy(原支付宝首席架构师楼方鑫开发 )

    vitess(谷歌开发的数据库中间件)

    Heisenberg(百度)

    TSharding(蘑菇街白辉)

    Xx-dbproxy(金山的Kingshard、当当网的sharding-jdbc )

    amoeba

1.7.5 安装Atlas

  软件获取地址: https://github.com/Qihoo360/Atlas/releases

注意:

1、Atlas只能安装运行在64位的系统上

2、Centos 5.X安装 Atlas-XX.el5.x86_64.rpm,Centos 6.X安装Atlas-XX.el6.x86_64.rpm。

3、后端mysql版本应大于5.1,建议使用Mysql 5.6以上

  Atlas (普通) : Atlas-2.2.1.el6.x86_64.rpm

  Atlas (分表) : Atlas-sharding_1.0.1-el6.x86_64.rpm

 下载安装atlas

wget https://github.com/Qihoo360/Atlas/releases/download/2.2.1/Atlas-2.2.1.el6.x86_64.rpm
rpm -ivh Atlas-2.2.1.el6.x86_64.rpm

         至此安装完成

1.7.6 配置Atlas配置文件

  atlas配置文件中的密码需要加密,可以使用,软件自带的加密工具进行加密

cd /usr/local/mysql-proxy/conf/
/usr/local/mysql-proxy/bin/encrypt  密码      ---->制作加密密码

生产密文密码:

[root@db03 bin]# /usr/local/mysql-proxy/bin/encrypt 123
3yb5jEku5h4=
[root@db03 bin]# /usr/local/mysql-proxy/bin/encrypt mha
O2jBXONX098=

编辑配置文件

vim  /usr/local/mysql-proxy/conf/test.cnf
[mysql-proxy]
admin-username = user
admin-password = pwd
proxy-backend-addresses = 10.0.0.55:3306
proxy-read-only-backend-addresses = 10.0.0.52:3306,10.0.0.53:3306
pwds = repl:3yb5jEku5h4=,mha:O2jBXONX098=
daemon = true
keepalive = true
event-threads = 8
log-level = message
log-path = /usr/local/mysql-proxy/log
sql-log=ON
proxy-address = 0.0.0.0:33060
admin-address = 0.0.0.0:2345
charset=utf8

配置文件内为全中文注释,这里有一份较为详细的解释:

 View Code Atlas配置文件说明

1.7.7 启动Atlas

编写一个atlas的管理脚本,当然也可以写脚本,可以直接手动的管理:

/usr/local/mysql-proxy/bin/mysql-proxyd test start   #启动
/usr/local/mysql-proxy/bin/mysql-proxyd test stop    #停止
/usr/local/mysql-proxy/bin/mysql-proxyd test restart #重启

  注意:test是配置文件的名称

脚本内容:

 1 [root@db03 ~]# cat /etc/init.d/atlasd 
 2 #!/bin/sh
 3 #
 4 # atlas:    Atlas Daemon
 5 #
 6 # chkconfig:    - 90 25
 7 # description:  Atlas Daemon
 8 # user:clsn
 9 # Blog: http://blog.nmtui.com
10 # Source function library.
11 
12 Demo=test
13 
14 start()
15 {
16         echo -n $"Starting atlas: "
17         /usr/local/mysql-proxy/bin/mysql-proxyd $Demo start
18 }
19 stop()
20 {
21         echo -n $"Shutting down atlas: "
22         /usr/local/mysql-proxy/bin/mysql-proxyd $Demo stop
23 }
24 status()
25 {
26         echo  $"Atlas status: "
27         /usr/local/mysql-proxy/bin/mysql-proxyd $Demo status
28 }
29 restart()
30 {
31         echo $"Atlas Restart Info: "
32         /usr/local/mysql-proxy/bin/mysql-proxyd $Demo restart
33 }
34 
35 
36 ATLAS="/usr/local/mysql-proxy/bin/mysql-proxyd"
37 [ -f $ATLAS ] || exit 1
38 # See how we were called.
39 case "$1" in
40         start)
41                 start
42                 ;;
43         stop)
44                 stop
45                 ;;
46         restart)
47                 restart
48                 ;;
49         status)
50                 status
51                 ;;
52         *)
53                 echo $"Usage: $0 {start|stop|restart|status}"
54                 exit 1
55 esac
56 exit 0

检查端口是否正常

[root@db03 ~]# netstat -lntup|grep mysql-proxy
tcp        0      0 0.0.0.0:33060               0.0.0.0:*                   LISTEN      2125/mysql-proxy    
tcp        0      0 0.0.0.0:2345                0.0.0.0:*                   LISTEN      2125/mysql-proxy

1.7.8 Atlas管理操作

  登入管理接口

[root@db03 ~]# mysql -uuser -ppwd -h127.0.0.1 -P2345

  查看帮助信息

mysql> SELECT * FROM help;

  查看后端的代理库

mysql> SELECT * FROM backends;
+-------------+----------------+-------+------+
| backend_ndx | address        | state | type |
+-------------+----------------+-------+------+
|           1 | 10.0.0.55:3306 | up    | rw   |
|           2 | 10.0.0.52:3306 | up    | ro   |
|           3 | 10.0.0.53:3306 | up    | ro   |
+-------------+----------------+-------+------+
3 rows in set (0.00 sec)

  平滑摘除mysql

mysql>  REMOVE BACKEND 2;
Empty set (0.00 sec)

   检查是否摘除

mysql> SELECT * FROM backends;
+-------------+----------------+-------+------+
| backend_ndx | address        | state | type |
+-------------+----------------+-------+------+
|           1 | 10.0.0.55:3306 | up    | rw   |
|           2 | 10.0.0.53:3306 | up    | ro   |
+-------------+----------------+-------+------+
2 rows in set (0.00 sec)

  保存到配置文件中

mysql> SAVE CONFIG;

  将节点再添加回来

mysql> add slave 10.0.0.52:3306;
Empty set (0.00 sec)

  查看是否添加成功

mysql> SELECT * FROM backends;
+-------------+----------------+-------+------+
| backend_ndx | address        | state | type |
+-------------+----------------+-------+------+
|           1 | 10.0.0.55:3306 | up    | rw   |
|           2 | 10.0.0.53:3306 | up    | ro   |
|           3 | 10.0.0.52:3306 | up    | ro   |
+-------------+----------------+-------+------+
3 rows in set (0.00 sec)

 保存到配置文件中

mysql> SAVE CONFIG;

1.7.9 连接数据库查看负载

通过atlas登陆数据,注意,使用的是数据库上的用户及密码

shell> mysql -umha -pmha -h127.0.0.1 -P33060

第一次查询server_id

mysql> show variables like "server_id";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 53    |
+---------------+-------+
1 row in set (0.00 sec)

第二次查询server_id

mysql> show variables like "server_id";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 52    |
+---------------+-------+
1 row in set (0.00 sec)

          通过上面可以看到负载成功

1.7.10 读写分离的说明

  Atlas会透明的将事务语句和写语句发送至主库执行,读语句发送至从库执行。具体以下语句会在主库执行:

显式事务中的语句

autocommit=0时的所有语句

含有select GET_LOCK()的语句

除SELECT、SET、USE、SHOW、DESC、EXPLAIN外的。

从库负载均衡配置

proxy-read-only-backend-addresses=ip1:port1@权重,ip2:port2@权重

1.7.11 Atlas高级功能

自动分表

  使用Atlas的分表功能时,首先需要在配置文件test.cnf设置tables参数。

tables参数设置格式:数据库名.表名.分表字段.子表数量,比如:

你的数据库名叫school,表名叫stu,分表字段叫id,总共分为2张表,那么就写为school.stu.id.2,如果还有其他的分表,以逗号分隔即可。

用户需要手动建立2张子表(stu_0,stu_1,注意子表序号是从0开始的)。

所有的子表必须在DB的同一个database里。

当通过Atlas执行(SELECT、DELETE、UPDATE、INSERT、REPLACE)操作时,Atlas会根据分表结果(id%2=k),定位到相应的子表(stu_k)。

例如,执行select * from stu where id=3;,Atlas会自动从stu_1这张子表返回查询结果。

但如果执行SQL语句(select * from stu;)时不带上id,则会提示执行stu表不存在。

Atles功能的说明

Atlas暂不支持自动建表和跨库分表的功能。

Atlas目前支持分表的语句有SELECT、DELETE、UPDATE、INSERT、REPLACE。

IP过滤:client-ips

  该参数用来实现IP过滤功能。

  在传统的开发模式中,应用程序直接连接DB,因此DB会对部署应用的机器(比如web服务器)的IP作访问授权。

在引入中间层后,因为连接DB的是Atlas,所以DB改为对部署Atlas的机器的IP作访问授权,如果任意一台客户端都可以连接Atlas,就会带来潜在的风险。

  client-ips参数用来控制连接Atlas的客户端的IP,可以是精确IP,也可以是IP段,以逗号分隔写在一行上即可。

  如: client-ips=192.168.1.2, 192.168.2 

  这就代表192.168.1.2这个IP和192.168.2.*这个段的IP可以连接Atlas,其他IP均不能连接。如果该参数不设置,则任意IP均可连接Atlas。如果设置了client-ips参数,且Atlas前面挂有LVS,则必须设置lvs-ips参数,否则可以不设置lvs-ips。

SQL语句黑白名单功能: Atlas会屏蔽不带where条件的delete和update操作,以及sleep函数。

1.8 Atlas-Sharding版本

1.8.1 版本介绍

  Sharding的基本思想就是把一个数据表中的数据切分成多个部分,  存放到不同的主机上去(切分的策略有多种),  从而缓解单台机器的性能跟容量的问题.

  sharding是一种水平切分, 适用于单表数据庞大的情景. 目前atlas支持静态的

  sharding方案, 暂时不支持数据的自动迁移以及数据组的动态加入.

  Atlas以表为单位sharding, 同一个数据库内可以同时共有sharding的表和不sharding的表, 不sharding的表数据存在未sharding的数据库组中.

  目前Atlas sharding支持insert, delete, select, update语句,  只支持不跨shard的事务. 所有的写操作如insert, delete,  update只能一次命中一个组, 否则会报"ERROR 1105 (HY000):write operation  is only allow to one dbgroup!"错误.

  由于sharding取替了Atlas的分表功能, 所以在Sharding分支里面,  Atlas单机分表的功能已经移除, 配置tables将不会再有效.

1.8.2 Atlas-Sharding架构

 

1.8.3 Sharding配置示例

  Atlas支持非sharding跟sharding的表共存在同一个Atlas中, 2.2.1之前的配置可以直接运行. 之前的配置如

proxy-backend-addresses = 192.168.0.12:3306
proxy-read-only-backend-addresses = 192.168.0.13:3306,192.168.0.14:3306 ... 

  这配置了一个master和两个slave,这属于非sharding的组, 所有非sharding的表跟语句都会发往这个组内. 

  所以之前没有Sharding的Atlas的表可以无缝的在新版上使用,

          注意: 非Sharding的组只能配置一个, 而sharding的组可以配置多个. 下面的配置, 配置了Sharding的组,  注意与上面的配置区分

[shardrule-0]
table = test.sharding_test

  分表名,有数据库+表名组成  t

ype = range

  sharding类型:range 或 hash

shard-key = id

  sharding 字段

groups = 0:0-999,1:1000-1999

  分片的group,如果是range类型的sharding,则groups的格式是:group_id:id范围。如果是hash类型的sharding,则groups的格式是:group_id。例如groups = 0, 1

[group-0]
proxy-backend-addresses=192.168.0.15:3306
proxy-read-only-backend-addresses=192.168.0.16:3306
[group-1]
proxy-backend-addresses=192.168.0.17:3306
proxy-read-only-backend-addresses=192.168.0.18:3306

1.8.4 Sharding限制

关于支持的语句

  Atlas sharding只对sql语句提供有限的支持, 目前支持基本的Select, insert/replace, delete,update语句,支持全部的Where语法(SQL-92标准), 不支持DDL(create drop alter)以及一些管理语句,DDL请直连MYSQL执行, 请只在Atlas上执行Select, insert, delete, update(CRUD)语句.

  对于以下语句, 如果语句命中了多台dbgroup, Atlas均未做支持(如果语句只命中了一个dbgroup, 如  select count(*) from test where id < 1000, 其中dbgroup0范围是0 - 1000,  那么这些特性都是支持的)Limit Offset(支持Limit)  

Order by

Group by  Join

ON

Count, Max, Min等函数

增加节点

  注意: 暂时只支持range方式的节点扩展, hash方式由于需要数据迁移, 暂时未做支持.

  扩展节点在保证原来节点的范围不改变的情况下, 如已有dbgroup0为范围0 - 999, dbgroup1为范围 1000-1999, 这个时候可以增加范围>2000的节点. 如增加一个节点为2000 - 2999, 修改配置文件,  重启Atlas即可.

1.9 参考文献

[1]  http://www.cnblogs.com/cenalulu/p/4309009.html

[2]  https://yq.aliyun.com/articles/57731

[3]  https://www.jianshu.com/p/b68e429d09c7

[4]  https://www.cnblogs.com/yyhh/archive/2015/12/29/5084844.html

[5]  http://blog.csdn.net/jhq0113/article/details/44302703

[6]  http://blog.csdn.net/jhq0113/article/details/44239823

[7]  https://www.guokr.com/blog/475765/

[8]  http://blog.csdn.net/bluishglc/article/details/6161475/


有赞容器化实践

$
0
0

前言

容器化已经成为一种趋势,它可以解决很多运维中的痛点,比如效率、成本、稳定性等问题,而接入容器的过程中往往也会碰到很多问题和不便。在有赞最开始做容器化是为了快速交付开发测试环境,在容器化的过程中,我们碰到过容器技术、运维体系适配、用户使用习惯改变等各种问题,本文主要介绍有赞容器化过程中碰到的问题以及采取的方案。

有赞容器化的初衷

在有赞同时会有很多个项目、日常在并行开发,环境的抢占问题严重影响了开发、测试和上线的效率,我们需要给每个项目提供一套开发联调(daily)、测试环境(qa),并且随着项目、日常的生命周期项目环境也会随着创建和销毁,我们最早的容器化需求就是怎么解决环境快速交付的问题。

[有赞环境]
图片描述
上面是有赞大致的研发流程,在标准流程中我们有四套稳定环境,分别是 Daily 环境、Qa 环境、预发环境和测试环境。我们的开发、测试、联调工作一般并不会直接在稳定环境中进行,而是会拉一套独立的项目环境出来,随着代码经过开发、测试、预发验收最终发布到生产环境后再同步回 Daily/Qa 的稳定环境中。

[项目环境]
图片描述

我们提供了一套以最小的资源投入满足最大项目并行度的环境交付方案,在 Daily/Qa 稳定环境的基础上,隔离出N个项目环境,在项目环境里只需要创建该项目所涉及应用的计算资源,其它缺失的服务调用由稳定环境提供,在项目环境里,我们大量使用了容器技术。

[持续交付]
图片描述

后面我们又在项目环境快速交付的解决方案的基础上实现了持续交付流水线,目前已经有超过 600 套项目/持续交付环境,加上 Daily/Qa 稳定环境,涉及计算实例四五千个,这些计算实例无论是 cpu 还是内存使用率都是非常低的,容器化可以非常好的解决环境交付的效率问题,以及提高资源使用率来节省成本的投入。

有赞容器化方案

我们的容器化方案基于 kubernetes(1.7.10)和 docker(1.12.6)、docker(1.13.1),下面介绍一下我们在各个方面遇到的问题以及解决方案。

网络

有赞后端主要是 java 应用,采用定制的 dubbo 服务化方案,过程中无法做到整个单元全量容器化,和原有集群在网络路由上互通也就成了刚需,由于我们无法解决公有云上 overlay 网络和公有云网络的互通问题,所以一开始我们放弃了 overlay 网络方案,采用了托管网络下的 macvlan 方案,这样既解决了网络互通的问题也不存在网络性能问题,但是也就享受不到公有云弹性资源的优势了。随着有赞多云架构的发展以及越来越多的云厂商支持容器 overlay 网络和 vpc 网络打通,弹性资源的问题才得到了缓解。

隔离性

容器的隔离主要利用内核的 namespace 和 cgroup 技术,在进程、cpu、内存、IO等资源隔离限制上有比较好的表现,但其他方面和虚拟机相比存在着很多的不足,我们在使用过程中碰到最多的问题是容器里看到的 cpu 数和内存大小不准确,因为/proc文件系统无法隔离,导致容器里的进程"看到"的是物理机的 cpu 数以及内存大小。

内存问题

我们的 java 应用会根据服务器的内存大小来决定 jvm 参数应该怎么配置,我们是采用 lxcfs 方案来规避的。
图片描述

CPU 数的问题

因为我们有超卖的需求以及 kubernetes 默认也是采用 cpu share 来做 cpu 限制,虽然我们使用了 lxcfs,CPU 数还是不准的。jvm 以及很多 Java sdk 都会根据系统的 CPU 数来决定创建多少线程,导致 java 应用在线程数和内存使用上都比虚拟机多的多,严重影响运行,其他类型的应用也有类似的问题。
我们会根据容器的规格内置一个环境变量 NUM_CPUS,然后比如 nodejs 应用就会按照这个变量来创建它的 worker 进程数。在解决 java 类应用的问题时,我们索性通过 LD_PRELOAD 将 JVM_ActiveProcessorCount 函数覆盖掉,让它直接返回 NUM_CPUS 的值[1]。

应用接入

在容器化之前,有赞的应用已经全部接入到发布系统,在发布系统里已经标准化了应用的打包、发布流程,所以在应用接入方面成本还是比较小的,业务方无需提供 Dockerfile。

  1. nodejs, python,php-soa 等用 supervisord 托管的应用,只需要在 git 仓库里提供 app.yaml 文件定义运行需要的 runtime 和启动命令即可。 图片描述
  2. java 标准化启动的应用业务方无需改动
  3. java 非标准化的应用需要做标准化改造

镜像集成

图片描述
容器镜像我们分了三层,依次为 stack 层(os),runtime 层(语言环境),应用层(业务代码和一些辅助agent),应用以及辅助 agent 由 runit 来启动。由于我们的配置还没有完全分离,在应用层目前还是每个环境独立打包,镜像里除了业务代码之外,我们还会根据业务的语言类型放一些辅助的 agent。我们一开始也想将各种 agent 拆成多个镜像,然后每个 pod 运行多个容器,后来因为解决不了 pod 里容器的启动顺序(服务启动有依赖)问题,就把所有服务都扔到一个容器里去运行了。

图片描述
我们的容器镜像集成过程也是通过 kubernetes 来调度的(会调度到指定的打包节点上),在发布任务发起时,管控系统会在集群中创建一个打包的 pod,打包程序会根据应用类型等参数编译代码、安装依赖,并且生成 Dockerifile,然后在这个 pod 中使用 docker in docker 的方式来集成容器镜像并推送到仓库。
为了加速应用的打包速度,我们用 pvc 缓存了 python 的 virtualenv,nodejs 的 node_modules,java 的 maven 包等文件。另外就是 docker 早的版本里,Dockerfile ADD 指令是不支持指定文件属主和分组的,这样会带来一个问题就是需要指定文件属主时(我们的应用是以 app 账号运行的)需要多运行一次 RUN chown,这样镜像也就多了一层数据,所以我们打包节点的 docker 版本采用了官方比较新的 ce 版本,因为新版本支持 ADD --chown 特性。

负载均衡(ingress)

图片描述
有赞的应用内部调用有比较完善的服务化和 service mesh 方案,集群内的访问不用过多考虑,负载均衡只需要考虑用户和系统访问的 http 流量,在容器化之前我们已经自研了一套统一接入系统,所以在容器化负载均衡上我们并没有完整按照 ingress 的机制来实现 controller,ingress 的资源配置是配在统一接入里的,配置里面转发的 upstream 会和 kubernetes 里的 service 关联,我们只是做了一个 sync 程序 watch kube-api,感知 service 的变化来实时更新统一接入系统中 upstream 的服务器列表信息。

容器登录和调试

图片描述
在容器化接入过程中开发会反馈是控制台比较难用,虽然我们优化了多次,和 iterm2 等的体验还是有所不足,最终我们还是放开了项目/持续交付环境这种需要频繁登陆调试的 ssh 登陆权限。
另外一个比较严重的问题是,当一个应用启动后健康检查有问题会导致 pod 一直在重新调度,而在开发过程中开发肯定是希望看到失败现场的,我们提供了调试发布模式,让容器不做健康检查。

日志

图片描述
有赞有专门的日志系统,我们内部叫天网,大部分日志以及业务监控数据都是通过 sdk 直接打到天网里去了,所以容器的标准输出日志仅仅作为一种辅助排查问题的手段。我们容器的日志收集采用的是 fluentd,经过 fluentd 处理后按照天网约定的日志格式打到 kafka,最终由天网处理进入 es 做存储。

灰度发布

我们涉及到灰度发布的流量主要包含三部分:

  1. 用户端的 http 访问流量
  2. 应用之间的 http 调用
  3. 应用之间的 dubbo 调用

首先,我们在入口的统一接入上统一打上灰度需要用的各种维度的标签(比如用户、店铺等),然后需要对统一接入、http client 以及 dubbo client 做改造,目的是让这些标签能够在整个调用链上透传。我们在做容器灰度发布时,会发一个灰度的 deployment,然后在统一接入以及灰度配置中心配置灰度规则,整个链路上的调用方都会感知这些灰度规则来实现灰度发布。

标准环境容器化

标准环境的出发点

  1. 和项目环境类似,标准稳定环境中的 daily,qa,pre 以及 prod 中超过一半运行在低水位的服务器的资源非常浪费。
  2. 因为成本考虑 daily,qa,pre 里都是以单台虚拟机运行的,这样一旦需要发布稳定环境将会造成标准稳定环境和项目环境的短暂不可用。
  3. 虚拟机交付速度比较慢,使用虚拟机做灰度发布也比较复杂。
  4. 虚拟机往往会存在几年甚至更长的时间,运行过程中操作系统以及基础软件版本的收敛非常麻烦。

标准环境容器化推进

经过之前项目/持续交付的上线和迭代,大部分应用本身已经具备了容器化的条件。不过对于上线来说,需要整个运维体系来适配容器化,比如监控、发布、日志等等。目前我们生产环境容器化准备基本完成,生产网已经上了部分前端 nodejs 应用,其他应用也在陆续推动中,希望以后可以分享更多生产环境中的容器化经验。

结束语

以上是有赞在容器化上的应用,以及在容器化过程中碰到的一些问题和解决方案,我们生产环境的容器化还处于开始阶段,后面还会碰到各种个样的问题,希望能够和大家互相学习,后面能够有更多的经验分享给大家。

参考文献

[1] https://github.com/fabianenar...

图片描述

使用 docker-compose 替代 docker run- 张志敏的技术专栏

$
0
0

使用 docker run 运行镜像

要运行一个 docker 镜像, 通常都是使用  docker run 命令, 在运行的镜像的时候, 需要指定一些参数, 例如:容器名称、 映射的卷、 绑定的端口、 网络以及重启策略等等, 一个典型的  docker run 命令如下所示:

docker run \
  --detach \
  --name registry \
  --hostname registry \
  --volume $(pwd)/app/registry:/var/lib/registry \
  --publish 5000:5000 \
  --restart unless-stopped \
  registry:latest

为了保存这些参数, 可以将这个  run 命令保存成 shell 文件, 需要时可以重新运行 shell 文件。 对于只有单个镜像的简单应用, 基本上可以满足需要了。 只要保存对应的 shell 文件, 备份好卷的内容, 当容器出现问题或者需要迁移活着需要重新部署时, 使用 shell 文件就可以快速完成。

不过不是所有的应用都倾向于做成单个镜像, 这样的镜像会非常复杂, 而且不符合 docker 的思想。 因为 docker 更倾向于简单镜像, 即: 一个镜像只有一个进程。 一个典型的 web 应用, 至少需要一个 web 服务器来运行服务端程序, 同时还需要一个数据库服务器来完成数据的存储, 这就需要两个镜像, 一个是 web , 一个是 db , 如果还是按照上面的做法, 需要两个 shell 文件, 或者是在一个 shell 文件中有两个  docker run 命令:

# PostGIS DB
docker run \
  --datach \
  --publish 5432:5432 \
  --name postgis \
  --restart unless-stopped \
  --volume $(pwd)/db/data:/var/lib/postgresql/data \
  beginor/postgis:9.3

# GeoServer Web
docker run \
  --detach \
  --publish 8080:8080 \
  --name geoserver \
  --restart unless-stopped \
  --volume $(pwd)/geoserver/data_dir:/geoserver/data_dir \
  --volume $(pwd)/geoserver/logs:/geoserver/logs \
  --hostname geoserver \
  --link postgis:postgis \
  beginor/geoserver:2.11.0

在上面的例子中, web 服务器使用的是 geoserver , db 服务器使用的是 postgis , web 服务器依赖 db 服务器, 必须先启动 db 服务器, 再启动 web 服务器, 这就需要编写复杂的 shell 脚本, 需要的镜像越多, 脚本越复杂, 这个问题被称作 docker 的编排。

关于 docker run 的各个参数的使用方法, 请参阅 docker 网站的 说明文档

使用 docker-compose 编排镜像

docker 提供了一个命令行工具  docker-compose 帮助完成镜像的编排, 要使用  docker-compose , 需要先编写一个  docker-compose.yml 文件,  yaml 是一种常用配置文件格式, 维基百科中对  yaml 描述如下:

YAML 是一个可读性高,用来表达数据序列的格式。YAML参考了其他多种语言,包括:C语言、Python、Perl,并从XML、电子邮件的数据格式(RFC 2822)中获得灵感。

如果想了解详细信息, 请 参考 YAML 官方网站或者 维基百科

docker 网站上提供了 docker-compose 的 入门教程, 如果不熟悉的话可以去学习一下。

上面的脚本转换成对应的 docker-compose.yml 文件如下所示:

version: "3"
services:
  web:
    image: beginor/geoserver:2.11.1
    container_name: geoserver-web
    hostname: geoserver-web
    ports:
      - 8080:8080
    volumes:
      - ./web/data_dir:/geoserver/data_dir
      - ./web/logs:/geoserver/logs
    restart: unless-stopped
    links:
      - database:database
  database:
    image: beginor/postgis:9.3
    container_name: postgis
    hostname: postgis
    ports:
      - 5432:5432
    volumes:
      - ./database/data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: 1q2w3e4R
    restart: unless-stopped

上面的  docker-compose.yml 文件定义了两个服务 web 和 database, 一个服务在运行时对应一个容器的实例, 上面的文件表示要启动两个实例。

在部署时, 通常将  docker-compose.yml 文件放到一个目录, 表示一个应用, docker 会为这个应用创建一个独立的网络, 便于和其它应用进行隔离。

要运行这个程序, 只要在这个目录下执行  docker-compose up -d 命令, 就会按照上面的配置启动两个容器的实例:

$ docker-compose up -d
Creating network "geoserver_default" with the default driver
Creating geoserver_database_1
Creating geoserver_web_1

要停止上面的容器, 只需要输入  docker-compose down 命令:

$ docker-compose down
Stopping geoserver_web_1 ... done
Stopping geoserver_database_1 ... done
Removing geoserver_web_1 ... done
Removing geoserver_database_1 ... done
Removing network geoserver_default

从上面的命令可以看出, docker-compose 不仅可以根据配置文件  docker-compose.yml 自动创建网络, 启动响应的容器实例, 也可以根据配置文件删除停止和删除容器实例, 并删除对应的网络, 确实是  docker run 命令更加方便, 因此推荐在测试环境或者生产环境中使用。

部署微服务的时候,Spring Cloud 和 Kubernetes 哪个更好? - DockOne.io

$
0
0

【编者的话】本文比较了当下最火热的两大微服务开发平台:Spring Cloud和Kubernetes的优缺点,并指出结合二者的优点来组合使用,将在微服务旅程上获得更大成功。

当我们需要部署微服务的时候,哪个更好?Spring Cloud还是Kubernetes?答案是都可以,只是各自有其优势。

Spring CloudKubernetes都宣称自己是开发和运行微服务的最佳环境,但是它们的本质非常不一样,所追求的目标也不同。本文我们分析一下两个平台是如何在其擅长的、实现基于微服务的架构(MSA)上起到作用的,并判断如何利用两者的强项,来帮助我们在微服务旅程上获得成功。

背景故事

最近我读了A. Lukyanchikov写的一篇非常精彩的 文章,讲的是用Spring Cloud和Docker来构建微服务架构。如果你还没读过,你应该读一下,因为它给出了一个关于如何利用Spring Cloud来创建一个简单的基于微服务的系统的综合视角。为了构建一个能扩展到数千个服务的可扩展且有弹性的微服务系统,它就必须有一套拥有广泛的构建时和运行时能力的工具集来帮助管理和控制。使用Spring Cloud,既能实现功能型服务(比如统计服务,账号服务以及通知服务),又能实现基础架构服务(比如日志分析,配置服务器,服务发现,认证服务等)。描述这样一个使用Spring Cloud构造的微服务架构(MSA)图如下所示:
365c0d94-eefa-11e5-90ad-9d74804ca412-2.png

MSA with Spring Cloud (by A. Lukyanchikov)

该图包含了运行时视角,但是它不包含打包,持续集成,扩展性,高可用,自愈等在MSA世界中同样重要的功能。本文假设大部分Java开发者都熟悉Spring Cloud,我们来做个对比,看看Kubernetes是如何提出这些额外的概念,以关联到Spring Cloud的。

微服务概念

我们不会针对两者一个一个概念的比对,而是根据更广阔的微服务概念,来看看Spring Cloud和Kubernetes分别是如何实现他们的。今天有关MSA的一个好事是,它是一个有着易于理解的 优缺点评估的架构风格。微服务能加强模块边界,各模块可以有独立的部署和技术差异。但是同时也 带来了代价,需要开发分布式系统以及 明显增加操作成本。一个关键的成功因素是聚焦于使用一套能帮助你实现尽可能多的MSA概念的工具。能使得启动过程迅速且容易是很重要的,但是通向产品化的旅程是很漫长的,你需要达到 这样的高度才能到达那里。
Screen_Shot_2016-12-03_at_09.32_.38_.png

Microservices concerns

如上图所示,我们可以看到MSA中必须实现的那些最普遍的技术概念(我们将不会关注非技术概念,比如组织结构,文化等)。

技术匹配

Sping Cloud和Kubernetes这两个平台非常不一样,并且它们之间也没有直接的等价特性。如果我们将每个MSA概念匹配到这两个平台使用的实现这些概念的技术/项目,我们能得出下表。
Screen_Shot_2016-11-30_at_08.45_.11_.png

Spring Cloud and Kubernetes technologies

从上表中能快速得出的结论是:
  • Spring Cloud有一个丰富的集合,完备整合了Java库,以实现各种运行时概念,作为应用栈的一部分。因此,这些微服务自身有库和运行时代理,来做客户端的服务发现,负载均衡,配置更新,度量跟踪等。各种模式,比如单集群服务和批量任务,也是由JVM来管理的。
  • Kubernetes兼容多种语言,目标不止Java平台,并且以一种通用方式为所有语言实现了分布式的计算挑战。它提供了平台级以及应用栈之外的服务,比如配置管理,服务发现,负载均衡,追踪,度量,单例,调度任务。应用不需要任何库或者代理来实现客户端逻辑,且可用任何语言来写。
  • 在某些领域,两个平台都依赖相似的第三方工具。比如,ELK和EFK栈,追踪库等。某些库,比如Hystric和Spring Boot,在两个环境上都非常有用。有些领域两个平台是互补的,整合到一起能创造出一个更强大的解决方案( KubeFlixSpring Cloud Kubernetes就是这样的例子)。


微服务需求

为了画出每个项目的范围,这里有张表列出了几乎是端到端的MSA需求,从最底层的硬件,到最上层的DevOps和自服务经验,并且列出了如何关联到Spring Cloud和Kubernetes平台。
Screen_Shot_2016-12-03_at_14.09_.41_.png

Microservices requirements

在某些场景下,两个项目使用不同的方法达成了相同的需求,在某些领域,一个项目可能会强于另一个。但是在某些情况下,两个项目是互补的,可以组合起来达成高级的微服务经验。例如,Spring Boot提供了Maven插件来构建单个JAR应用包。这样就可以结合Docker和Kubernetes的声明式部署和调度能力,使得运行微服务变得轻而易举。同样的,Spring Cloud有个内嵌的应用库,可以利用Hystric(自带隔离和熔断模式)和Ribbon(用来负载均衡)来创建有弹性的,容错的微服务。但是光靠这个还不够,当这个能力和Kubernetes的健康检查,进程重启,以及自动伸缩能力结合在一起时,微服务才能成为一个反脆弱系统。

优点和缺点

因为两个平台不能直接用每个特性来比较,我们也不打算针对每个平台都深入每个特性,因此这里就以总结性方式来说明一下两个平台的优缺点。

Spring Cloud

Spring Cloud给开发者提供了一个工具,能在分布式系统中快速构建例如配置管理,服务发现,熔断,路由等通用模式。这是基于Netflix的OSS库,它们是用Java写的,面向Java开发者。

优势
  • 由Spring平台自身来提供统一的编程模型,加上Spring Boot的快速创建应用的能力,可以给开发者大量的微服务开发经验。例如,只要极少量的标签,你就可以创建一个配置服务器,再加一些标签,你就可以得到一个客户端库来配置你的服务。
  • 有大量的覆盖了大多数运行时概念的库可供选择。因为所有的库都是用Java写的,它能提供更多的特性,更强的控制,以及更好的一致性选项。
  • 不同的Spring Cloud库都可以很好的整合在一起。例如,Feign客户端也可以使用Hystrix作为熔断器,以及Ribbon作为请求负载均衡器。每一个都是标签驱动的,这样对Java开发者来说就很容易开发。


缺点
  • Spring Cloud其中一个最主要的优点也是它的缺点,即它只针对Java。MSA 的强烈的目标是具备互换技术栈,库,必要的时候甚至是语言的能力。而这对Spring Cloud来说不可能。如果你想要消费Spring Cloud/Netflix OSS基础服务,比如配置管理,服务发现,或者负载均衡,解决方案不是很优雅。 Netflix Prana项目实现了sidecar模式,让Java客户端库基于HTTP协议,使得那些非JVM语言写的应用也可以存在于NetflixOSS系统中,但是这种方式不是很优雅。
  • Java开发者需要关注非常多的事情,Java应用需要处理非常多的事情。每个微服务都需要运行各种客户端来获得配置恢复、服务发现、负载均衡等功能。这些客户端很容易建立,但是它们没有隐藏环境的构建时和运行时依赖。例如,开发者可以用@EnableConfigServer标签创建一个配置服务器,但是这也是唯一路径。每次开发者想要运行一个单一的微服务,他们需要使配置服务器正常运行。对一个受控环境,开发者不得不考虑让配置服务器高可用,且因为它可以由Git或者Svn来支持,他们还需要一个共享的文件系统。同样的,对服务发现来说,开发者首先需要启动一个Eureka服务器。对一个受控环境,他们需要在每个AZ上都用多个实例来实现集群。这有点类似于,除了实现所有的功能性服务外,Java开发者还需要构建和管理一个非试用型的微服务平台。
  • 单独使用Spring Cloud在微服务旅程上无法走得很长远,在一个完整的微服务经历中,开发者还需要考虑自动化部署,调度,资源管理,进程隔离,自愈,构建流水线等功能。在这点上,我觉得单独拿Spring Cloud和Kubernetes来比较不太公平,更公正的比较应该是Spring Cloud + Cloud Foundry(或者 Docker Swarm)和Kubernetes来比较。但是那也说明,对一个完整的端到端的微服务经历,Spring Cloud还需要补充一个应用平台,就像Kubernetes那样。


Kubernetes

Kubernetes 是一个针对容器应用的自动化部署,伸缩和管理的开源系统。它兼容多种语言且提供了创建,运行,伸缩以及管理分布式系统的原语。

优势
  • Kubernetes是语言不感知的容器管理平台,能兼容运行原生云应用和传统的容器化应用。它提供的服务,比如配置管理,服务发现,负载均衡,度量收集,以及日志聚合,能被各种语言使用。这使得组织可以只提供一个平台供多个项目组使用(包括使用Spring的Java开发者),并且提供多种目的:应用开发,测试环境,构建环境(运行资源控制系统,构建服务器,人工仓库)等。
  • 和Spring Cloud相比,Kubernetes实现了更广阔的MSA概念集合。除了提供运行时服务,Kubernetes也允许我们提供环境变量,设置资源限制,RBAC,管理应用生命周期,使能自动伸缩和自愈(表现为就像一个 反脆弱平台)。
  • Kubernetes的技术是基于Google 15年的管理容器的研究和开发经验。除此以外,还有将近1000个 committer,它几乎是GitHub上开源社区最活跃的项目。


缺点
  • Kubernetes是兼容多种语言的,因此它的服务和原语是通用的,不像Spring Cloud对JVM那样,没有针对不同的平台做优化。例如,配置是通过环境变量或者挂载文件系统传递给应用的。它没有Spring Cloud配置提供的那样精妙的配置更新能力。
  • Kubernetes不是一个针对开发者的平台。它的目的是供有DevOps思想的IT人员使用。因此,Java开发者需要学习一些新的概念,并更开放得学习新的解决问题的方式。不管使用 MiniKube来部署一个Kubernetes开发实例是多么得容易,手工安装一个高可用的Kubernetes集群是有明显的操作成本的。
  • Kubernetes仍是一个相对较新的平台(2年),它也还在活跃得开发和生长中。因此每个版本都会有许多新的特性,使得我们很难去一直跟踪。好消息是这个问题已经被正视,API做成了可扩展且是后向兼容的。


两个平台的最佳实践

正如你所看到的,两个平台都有各自的强项,也有需要提高的地方。Spring Cloud 是一个容易上手的,开发者友好的平台。而Kubernetes是DevOps友好的,有着陡峭的学习曲线,但是包含了更广泛的微服务概念。这里是针对这几点的总结。
Screen_Shot_2016-12-03_at_15.45_.54_.png

Strengths and weaknesses

两个框架实现了不同范围的MSA概念,使用的是从根源上就有区别的方式。Spring Cloud方式是尽力在JVM范畴内来解决每个MSA的挑战,而Kubernetes的方式是尽力为开发者在平台层面消除这些问题。Spring Cloud在JVM内非常强大,Kubernetes在管理这些JVM上非常强大。因此,整合这两者取它们的最佳部分,是一个很自然的进步过程。
spring_cloud_and_kubernetes_mixed_-_Page_1(1).png

Spring Cloud backed by Kubernetes

有了这样一个整合,Spring提供应用的打包,Docker和Kubernetes提供部署和调度。Spring通过Hystrix线程池提供应用内的隔离,而Kubernetes通过资源,进程和命名空间来提供隔离。Spring为每个微服务提供健康终端,而Kubernetes执行健康检查,且把流量导到健康服务。Spring外部化配置并更新它们,而Kubernetes分发配置到每个微服务。这个列表将一直持续。
stack_-_Page_1.png

My favorite microservices stack

我最喜欢的微服务平台是哪个?我 两个都喜欢。我喜欢Spring 框架提供的开发者经验。它是标签驱动的,并且拥有包含了各种功能需求的库。跟任何整合相关的,我喜欢Apache Camel(而不是Spring Integration),它能提供应用级别的连接器,消息,路由,可靠性和容错功能。而对跟集群和管理多应用实例相关的,我更喜欢魔法般的Kubernetes能力。不论何时,有重复功能,比如服务发现,负载均衡,配置管理,我尽量使用Kubernetes提供的跟语言无关的原语。

原文链接: Deploying Microservices: Spring Cloud vs. Kubernetes(翻译:池剑锋)

    容器平台选型的十大模式:Docker、DC/OS、K8S 谁与当先?(上)-社区博客-网易云

    $
    0
    0

    作者:刘超 网易云基础服务
    无论是在社区,还是在同客户交流的过程中,总会被问到到底什么时候该用 Docker?什么时候用虚拟机?如果使用容器,应该使用哪个容器平台?
     
    显而易见,我不会直接给大家一个答案,而是希望从技术角度进行分析具体的场景。例如客户是大公司还是小公司,将部署小集群还是大集群,倾向于私有云还是公有云,已经采购了 IaaS 还是没有 IaaS,IT 运维能力强还是弱,是否需要物理机、虚拟机、容器的混合部署,是一般的并发系统还是高并发,这里面所应该做的技术选型都不一样。举个例子,如果你是一个初创型的主营业务非 IT 的小公司,自然不应该花大力气在数据中心里面自己搭建一套大规模、高并发、高性能的容器平台。
     
    接下来,首先,我们来谈下什么情况下应该使用 Docker 的问题。
     

     
    如上图所示,左面是我们经常挂在嘴边的所谓容器的优势,但是虚拟机都能一一怼回去。
     
    如果部署的是一个传统的应用,这个应用启动速度慢,进程数量少,基本不更新,那么虚拟机完全能够满足需求。
     
    ○ 应用启动慢:应用启动 15 分钟,容器本身秒级,虚拟机很多平台能优化到十几秒,两者几乎看不出差别;
    ○ 内存占用大:动不动 32G,64G 内存,一台机器跑不了几个;
    ○ 基本不更新:半年更新一次,虚拟机镜像照样能够升级和回滚;
    ○ 应用有状态:停机会丢数据,如果不知道丢了什么,就算秒级启动也没有用,照样恢复不了,而且还有可能因为丢数据,在没有修复的情况下,盲目重启带来数据混乱;
    ○ 进程数量少:两三个进程相互配置一下,不用服务发现,配置不麻烦
     
    如果是一个传统应用,根本没有必要花费精力去容器化,因为白花了力气,享受不到好处。
     

     
    那么什么情况下,才应该考虑做一些改变呢:
     
    传统业务突然被互联网业务冲击了,应用老是变,三天两头要更新,而且流量增大了,原来支付系统是取钱刷卡的,现在要互联网支付了,流量扩大了 N 倍。
     
    这种情况下就只能:拆。
     
    拆开了,每个子模块独自变化,相互影响变少。
    拆开了,原来一个进程扛流量,现在多个进程一起扛。
     
    这被称为微服务。
     

     
    微服务场景下,进程多,更新快,于是出现 100 个进程,每天一个镜像。
     
    容器乐了,每个容器镜像小,没什么问题,虚拟机哭了,因为虚拟机每个镜像太大了。
     
    所以微服务场景下,可以开始考虑用容器了。
     

     
    这时虚拟机又怒了,我不用容器了,微服务拆分之后,用 Ansible 自动部署是一样的。
     
    这从技术角度来讲没有任何问题,问题是从组织角度出现的。一般的公司,开发会比运维多得多,开发写完代码就不用管了,环境的部署完全是运维负责,运维为了自动化,写 Ansible 脚本来解决问题。
     
    然而这么多进程,又拆又合并的,更新这么快,配置总是变,Ansible 脚本也要常改,每天都上线,不得累死运维。
     
    所以在如此大的工作量情况下,运维很容易出错,哪怕通过自动化脚本。这时,容器就可以作为一个非常好的工具运用起来。
     
    除了容器从技术角度,能够使得大部分的内部配置可以放在镜像里面之外,更重要的是从流程角度,将环境配置这件事情,往前推了,推到了开发这里,要求开发完毕之后,就需要考虑环境部署的问题,而不能当甩手掌柜。
     
    这样做的好处就是,虽然进程多,配置变化多,更新频繁,但是对于某个模块的开发团队来讲,这个量是很小的,因为 5-10 个人专门维护这个模块的配置和更新,不容易出错。
     
    如果这些工作量全交给少数的运维团队,不但信息传递会使得环境配置不一致,部署量也会大非常多。
     
    容器是一个非常好的工具,就是让每个开发仅仅多做 5% 的工作,就能够节约运维 200% 的工作量,并且不容易出错。
     
    然而原来运维该做的事情开发做了,开发的老大愿意么?开发的老大会投诉运维的老大么?
     
    这就不是技术问题了,其实这就是 DevOps,DevOps 不是不区分开发和运维,而是公司从组织到流程能够打通,看如何合作,边界如何划分,对系统的稳定性更有好处。
     
    所以微服务、DevOps、容器是相辅相成,不可分割的。不是微服务,根本不需要容器,虚拟机就能搞定,不需要 DevOps,一年部署一次,开发和运维沟通再慢都能搞定。
     
    所以,容器的本质是基于镜像的跨环境迁移。
     
    镜像是容器的根本性发明,是封装和运行的标准,其它什么 namespace,cgroup,早就有了,这是技术方面。
     
    在流程方面,镜像是 DevOps 的良好工具。
     
    容器是为了跨环境迁移的,第一种迁移的场景是开发、测试、生产环境之间的迁移。如果不需要迁移,或者迁移不频繁,虚拟机镜像也行,但总是要迁移,带着几百 G 的虚拟机镜像,太大了。
     
    第二种迁移的场景是跨云迁移,跨公有云,跨 Region,跨两个 OpenStack 的虚拟机迁移都是非常麻烦,甚至不可能的,因为公有云不提供虚拟机镜像的下载和上传功能,而且虚拟机镜像太大了,一传传一天。
     
    所以跨云场景下,混合云场景下,容器也是很好的使用场景。这也同时解决了仅仅私有云资源不足,扛不住流量的问题。
     
    所以这是我认为的容器的本质,是最终应该使用容器的正确姿势,当然一开始你不一定完全按照这个来。
     

    模式一:公有云虚拟机

     
    适合场景:初创公司,无信息安全担忧
     
    如果您是一家初创公司,人员少,IT 运维能力不足,要部署的系统很少,能够花在 IT 系统上的资金有限,当然应该选择公有云的虚拟机部署,它能够解决您的如下问题:
     
    ○ 基层 IT 资源的管理交给公有云平台,公司自身运维人员仅需要基本的 Linux 能力;
    ○ 少量的部署系统,例如 10 台以下的虚拟机,往往替换一个 war,重启 Tomcat 就能解决,如果稍微虚拟机多一点 10 到 20 台,Ansible 脚本可以很好地解决这个问题;
    ○ 公有云按量按时收费,可以在花费很少的情况下启动,并且在业务飞速扩展的时候,迅速申请大量虚拟机;
     
    这里所说的信息安全担忧,真的仅仅是心理的担忧,公有云往往有大量的安全机制来保证每个租户的安全隔离,只要用好了这些机制,公有云的安全性绝对大于一般公司自己搭建的数据中心, 当客户在说要安全的时候,客户在想什么? 这篇文章讲到了绝对的端到端解决方案。
     
    这里贴张图说明公有云的安全性:
     

     

    公有云为支撑自身高并发业务积累了更强的安全防护能力和更多的安全防护经验:

     
    ○ 多线 BGP,外网线路冗余
    ○ 高吞吐量的 DDoS 外网防护
    ○ 更完善的防火墙,入侵检测,WAF
    ○ 更完善的流量清洗规则
     

    公有云为支撑自身高并发业务推出了更安全、更高可靠、更高可用的 PaaS 服务:

     
    数据库:
    ○ 高可用:主备切换数据零丢失
    ○ 高可靠:同城双活,异地备份
    ○ 安全性:访问控制,IP 白名单
     
    对象存储:
    ○ 高可靠:超大容量,三份备份,异地同步
    ○ 安全性:访问控制,防盗链
     

    公有云为支撑自身高并发业务推出更完善的监控运维的系统,流程,经验:

     
    完善的监控系统,保障大促期间系统故障的快速定位和排障
    保障大促能够极大的提升和训练一支有经验的运维团队
    大促的业务层面的数据对运维也是机密的,需要流程保障

     

    道高一尺魔高一丈,公有云为保证自身业务的安全性对云平台不断升级:

     
    ○ 越来越强的 DDoS 防护
    ○ 越来越完善的防火墙规则
    ○ 最新的云平台安全功能和机制
    ○ 不断更新的虚拟机和容器镜像建设漏洞
    ○ 不断更新的病毒库
     

    模式二:无 IaaS,裸用容器

     
    适用场景:初创公司无 IaaS,有信息安全担忧
     
    但是即便如此,还是有初创公司或者初创项目,也许因为心理方面,也许因为合规方面,非常担心信息安全问题,还是希望采取部署在自己机房的方式。
     
    但由于是初创公司,在机房里面一般是不能部署 IaaS,因为 IaaS 平台的运维难度,优化难度更大,没有一个 50 人的团队根本玩不起来,所以一般在使用容器之前,采用的是物理机部署的方式,当物理机数目非常小,比如部署 5 到 10 个应用的时候手动部署或者简单脚本部署就可以,但是一旦到了 20 个应用,手动部署和简单脚本就非常麻烦了:
     
    ○ 运维人员比例低,而应用相对较多
    ○ 部署在同一个物理机上的应用多,配置冲突,端口冲突,互相连接,运维需要一个 excel 去管理,还容易出错
    ○ 物理机容器被脚本和 Ansible 改的乱七八糟,难以保证环境一致性,重装物理机更加麻烦
    ○ 不同的应用依赖不同的操作系统和底层包,千差万别
     
    这个时候,可以试一下裸用容器,即在原来的脚本,或者 Ansible 里面,将启动进程,改为使用 Docker run,可以有以下的作用:
     
    ○ 配置,端口隔离,冲突减少
    ○ 基于容器部署,使得环境一致性,安装和删除干干净净
    ○ 不同的操作系统和底层包,都可以用容器镜像搞定
     
    在这个阶段,最简单的方式就是把容器当做虚拟机来使用,也即先启动容器,然后在里面下载 war 包等,当然也可以更进一步,将 war 包和配置直接打在容器镜像里面,这样需要一个持续集成的流程了,不仅仅是运维的事情,开发也要参与其中。
     
    在这个阶段,网络的模式可以使用桥接打平的方式。
     

     
    这种方式好处是访问 Docker 和访问物理机一样,可很方便地实现 Docker 里面和物理机里面的互通,兼容原来部署在物理机上的应用。
     
    当然 Bridge 的性能一般,如果性能要求比较高,可使用 SR-IOV 网卡嵌入容器内。
     

     

    模式三:有 IaaS,裸用容器

     
    适用场景:创新项目,引入 DevOps 流程
     
    有一些公司规模大一些,已经采购了 IaaS,只不过有一些创新的项目需要部署,这种状态下,基本虚拟机已经能够满足需求,而且由于能够运维 IaaS,IT 能力比较强,一般也采用了 Ansible 等部署工具。
     
    这种情况下,使用容器的动力相对比较少,然而容器也是能够带来一定好处的,就是 DevOps。
     
    创新项目迭代速度比较快,如果有比较多的创新项目,对运维的压力也是非常大的,这里的裸用容器和模式二的裸用容器不同的是,不是拿容器当做虚拟机来用,而是将容器当做交付物来用。
     
    虽然容器化对于运维的整个过程来讲改进有限,但是关键就是要开发写一个 Dockerfile,这一点非常重要,意味着运行环境的配置提前到开发,而非直接交到运维,也即上面说的,开发 5% 的工作量增加减少大量运维工作,容器环境原子性升级回滚使得停服时间变短,可以保持开发、测试、运维环境的一致性。
     

     

    模式四:使用 Docker Swarm Mode

     
    适用场景:发展中公司,中等规模集群
     
    当集群规模超过 50 台时,裸用容器已经非常难受了,因为网络、存储、编排、服务发现等全部要靠自己的脚本或 Ansible 来搞定,是时候引入容器平台了。
     
    当容器平台规模不是很大时,Docker Swarm Mode 还是比较好用的:
     
    ○ 集群的维护不需要 Zookeeper,不需要 Etcd,自己内置
    ○ 命令行和 Docker 是一样的,用起来顺手
    ○ 服务发现和 DNS 是内置的
    ○ Docker Overlay 网络是内置的
     
    总之 docker 帮你料理好了一切,你不用太关心细节,很容易就能够将集群运行起来。
     
    而且可以通过 docker 命令,像在一台机器上使用容器一样使用集群上的容器,可以随时将容器当虚拟机来使用,这样对于中等规模集群,以及运维人员还是比较友好的。
     
    当然内置的太多了也有缺点,就是不好定制化,不好 Debug,不好干预。当你发现有一部分性能不行时,你需要改整个代码,全部重新编译,当社区更新了,合并分支是很头疼的事情。当出现问题时,由于 Manager 大包大揽干了很多活,不知道哪一步出错了,反正就是没有返回,停在那里,如果重启整个 Manager,影响面又很大。
     

     

    模式五:使用 Marathon 和 Mesos

     
    使用场景:万节点集群,多定制
     
    当集群规模大一些,几百个节点时,很多人就不愿意使用 Docker Swarm Mode 了,很多的选择是既没有用 DC/OS,也没有用 Kubernetes,而是仅仅用了 Marathon 和 Mesos。
     
    因为 Mesos 是一个非常优秀的调度器,它的双层调度机制可以使得集群规模大很多。
     
    Mesos 的调度过程如图所示:
     

     
    Mesos 有 Framework、Master、Agent、Executor、Task 几部分组成。这里面有两层的 Scheduler,一层在 Master 里面,allocator 会将资源公平的分给每一个 Framework,二层在 Framework 里面,Framework 的 scheduler 将资源按规则分配给 Task。
     
    其它框架的调度器是直接面对整个集群,Mesos 的优势在于,第一层调度先将整个 Node 分配给一个 Framework,然后 Framework 的调度器面对的集群规模小很多,然后在里面进行二次调度,而且如果有多个 Framework,例如有多个 Marathon,则可以并行调度不冲突。
     
    详细的调度机制非常复杂,可以看 号称了解 mesos 双层调度的你,先来回答下面这五个问题!这篇文章。
     
    而且 Mesos 的架构相对松耦合,有很多可以定制化的地方,从而运维人员可以根据自己的需要开发自己的模块。详细的定制方式看文章 定制化 Mesos 任务运行的几种方法。
     
    这也是很多优秀的公司使用 Marathon 和 Mesos 的原因。
     
    例如爱奇艺、去哪儿、携程、当当等都选择了使用 Mesos,需要提一下的是,大家如果参加社区,能发现裸用 Marathon 和 Mesos 的很多,但是整个 DC/OS 都用得比较少,而用 Marathon 和 Mesos 往往不能解决一些问题,因而这些 IT 能力非常强的互联网公司做了大量的自己的定制化,增加了 Marathon 和 Mesos 的外围模块。
     

    模式六:使用开源 Kubernetes

     
    使用场景:千节点集群,少定制
     
    Kubernetes 模块划分得更细,模块比较多,比起裸 Marathon 和 Mesos 来讲功能丰富,而且模块之间完全的松耦合,可以非常方便地进行定制化。
     

     
    而且 Kubernetes 的数据结构的设计层次比较细,非常符合微服务的设计思想。例如从容器->Pods->Deployment->Service,本来简单运行一个容器,被封装为这么多的层次,每个层次有自己的作用,每一层都可以拆分和组合,这样带来一个很大的缺点,就是学习门槛高,为了简单运行一个容器,需要先学习一大堆的概念和编排规则。
     
    但是当需要部署的业务越来越复杂时,场景越来越多时,你会发现 Kubernetes 这种细粒度设计的优雅,使得你能够根据自己的需要灵活的组合,而不会因为某个组件被封装好了,从而导致很难定制。例如对于 Service 来讲,除了提供内部服务之间的发现和相互访问外,还灵活设计了 headless service,这使得很多游戏需要有状态的保持长连接有了很好的方式,另外访问外部服务时,例如数据库、缓存、headless service 相当于一个 DNS,使得配置外部服务简单很多。很多配置复杂的大型应用,更复杂的不在于服务之间的相互配置,可以有 Spring Cloud 或者 Dubbo 去解决,复杂的反而是外部服务的配置,不同的环境依赖不同的外部应用,External Name 这个提供了很好的机制。
     
    包括统一的监控 cadvisor,统一的配置 confgMap,都是构建一个微服务所必须的。
     
    然而 Kubernetes 当前也有一个瓶颈——集群规模还不是多么大,官方说法是几千个节点,所以超大规模的集群,还是需要有很强的 IT 能力进行定制化,这个在模式七中会说一下我们在网易云上做的事情。但是对于中等规模的集群也足够了。
     
    而且 Kubernetes 社区的热度,可以使得使用开源 Kubernetes 的公司能够很快地找到帮助,等待到新功能的开发和 Bug 的解决。


     本文未结束,敬请期待下篇。

    How to configure autoscaling on docker swarm? - Stack Overflow

    $
    0
    0

    Short answer: There is no easy way to do this with Docker Swarm for now.

    Docker Swarm (or Swarm mode) does not support auto-scalingmachines out of the box. You'd need to use another solution for that like docker-machineto create machines (with docker) on your infrastructure and link these to the existing Swarm cluster (with docker swarm join).

    This will involve a lot of scripting but the idea is to monitor the cluster for CPU / Memory / Network usage (with topor monit) and once it goes beyond a threshold (say 70%of total cluster resources), you trigger a script calling docker-machineto scale up the cluster. Using the same idea you can also scale down by drainingand removingnodes (preferably Agentnodes) from the existing swarm cluster once your are below the lower threshold.

    You need to make sure you are monitoring for sustained resource usage if you want to use this criteria or you will have your Infrastructure spawning and destroying nodes from the frequent and sudden changes in resource usage.

    You can define a lower bound and an upper bound for machines in the cluster to keep things under control.

    Note that Swarm requires at least 3 Managernodes (recommended 5) to maintain a quorum for the Distributed Consensus algorithm. So the minimum recommended lower bound is 5 nodes(which you can extend with Agentnodes as resources are incrementally being used by services).

    To some extent, you can also take a look at Docker InfraKitor Terraformfor Infrastructure automationand Health monitoring.

    Update: There is now a promising cross-platform autoscaler that supports Swarm Mode task auto-scaling: Orbiter. Although still nothing out-of-the-box yet for service/machine autoscaling.

    HTTP协议调试工具汇总,你心目中应该是什么样的?

    $
    0
    0

    前言

    本文收集了大量抓包工具,近40款,涵盖了各种开发语言(Java,C#,Delphi,C,C++,Objective-C,Node.js,Go,Python)、各类前端(GUI,TUI,CUI,Web UI,Browser Addon),请大家赏析。

    页首配图

    Java

    1.Burp Suite

    http://www.burpsuite.com/

    英国PortSwigger团队开发,用起来很顺手,安全行业占有率很高,闭源、收费。

    Burp Suite

    Burp Suite

    最近又出了款企业版:

    Burp Suite

    独特的办公环境:

    Burp Suite

    Burp Suite

    2.Charles

    http://www.charlesproxy.com/

    俗称花瓶,Mac下前端工程师用的较多,闭源、收费。

    Charles

    3.Zed Attack Proxy

    https://www.zaproxy.org/

    OWASP团队开发,功能很多,但操作上没有burp好用,开源、免费。

    Zed Attack Proxy

    4.WebScarab

    https://www.owasp.org/index.php/Category:OWASP_WebScarab_Project

    OWASP团队开发,貌似停止更新了,开源、免费。

    WebScarab

    5.Paros

    http://www.parosproxy.org/

    Kali Linux里有这个工具,简单小巧,05年就停止更新了,闭源、免费。

    Paros

    6.Vega

    https://subgraph.com/vega/

    貌似停止更新了,闭源、免费。

    Vega

    C#

    1.fiddler

    http://www.fiddlertool.com/

    俗称小提琴,被微软收购,Windows下前端工程师用的较多,闭源、免费。

    fiddler

    Delphi

    1.Http Analyzer

    http://www.ieinspector.com/

    嗅探式,闭源、收费。

    Http Analyzer

    C++

    1.HTTP Debugger

    http://www.httpdebugger.com/

    嗅探式,闭源、收费。

    HTTP Debugger

    2.Rythem

    https://github.com/AlloyTeam/Rythem

    腾讯前端团队开发,已停止更新,开源、免费。

    Rythem

    C

    1.Ratproxy

    https://code.google.com/archive/p/ratproxy/

    据说是某Google工程师开发的,09年停止更新,开源、免费。

    Ratproxy

    Objective-C

    1.HTTPScoop

    https://www.tuffcode.com/

    已停止更新,闭源、免费。

    HTTPScoop

    Browser Addon

    1.Firebug

    http://getfirebug.com/

    主要前端工程师用吧,开源、免费。

    Firebug

    2.HttpWatch

    http://www.httpwatch.com/

    主要前端工程师用吧,闭源、收费。

    HttpWatch

    还有iOS版:

    HttpWatch

    HttpWatch

    HttpWatch

    3.Chrome developer tools

    https://www.google.cn/chrome/

    主要前端工程师用吧,开源、免费。

    Chrome developer tools

    4.HackMan

    http://www.freebuf.com/sectool/60379.html

    界面看起来不错,安全工程师用,貌似不更新了吧,开源、免费。

    HackMan

    Node.js + Web UI

    1.whistle

    http://wproxy.org/

    腾讯前端团队开发,性能很赞,更新频繁,开源、免费。

    whistle

    2.AnyProxy

    http://anyproxy.io/

    阿里巴巴前端团队开发,界面很赞,开源、免费。

    AnyProxy

    3.Livepool

    https://github.com/materliu/livepool

    腾讯前端团队开发,已停止更新,开源、免费。

    Livepool

    Node.js + CUI

    1.Dproxy

    https://github.com/deemstone/Dproxy

    已停止更新,开源、免费。

    Dproxy

    2.NProxy

    https://github.com/goddyZhao/nproxy

    已停止更新,开源、免费。

    NProxy

    Go + Web UI

    1.Hyperfox

    https://hyperfox.org/

    主页打不开了,开源、免费。

    Hyperfox

    2.netgraph

    https://github.com/ga0/netgraph

    开源、免费。

    netgraph

    3.pproxy

    https://github.com/hidu/pproxy

    开源、免费。

    pproxy

    4.RequestHub

    https://github.com/kyledayton/requesthub

    已停止更新,开源、免费。

    RequestHub

    Python + Web UI

    1.mitmproxy

    http://mitmproxy.org/

    以前依赖的库太多,这两年新版增加Web UI直接打包了,开源、免费。

    mitmproxy

    最早前端用的TUI(Terminal User Interface),只支持Linux和Mac:

    mitmproxy

    2.HoneyProxy

    http://honeyproxy.org/

    依赖的库太多,已停止更新,开源、免费。

    HoneyProxy

    Python + PyQt

    ProxyStrike

    http://www.edge-security.com/proxystrike.php

    09年停止更新,闭源、免费。

    ProxyStrike

    一些老款:

    EffeTech

    EffeTech

    HTTPLook

    HTTPLook

    HTTPNetworkSniffer

    HTTPNetworkSniffer

    ieHTTPHeaders

    ieHTTPHeaders

    IEWatch

    IEWatch

    KomodiaSSLSniffer

    KomodiaSSLSniffer

    sniffer80

    sniffer80

    sniffx

    sniffx

    欢迎补充。

    有些小伙伴可能已经猜到了:我们也正在开发一款,取名 XProxy ,下篇文章将会对Burp Suite等相关工具的优缺点详细分析一下,还有XProxy项目计划的介绍。

    对于上述有什么看法和对新工具有哪些期待不妨先在这里吐槽一下。(比如Burp的中文乱码,或你心目中的工具应该是什么样的)

    *本文作者:mazekey,转载请注明来自FreeBuf.COM


    k8s docker集群搭建 - CSDN博客

    $
    0
    0

    一、Kubernetes系列之介绍篇

     
    •Kubernetes介绍
    1.背景介绍
      云计算飞速发展
        - IaaS
        - PaaS
        - SaaS
      Docker技术突飞猛进
        - 一次构建,到处运行
        - 容器的快速轻量
        - 完整的生态环境
    2.什么是kubernetes
      首先,他是一个全新的基于容器技术的分布式架构领先方案。Kubernetes(k8s)是Google开源的容器集群管理系统(谷歌内部:Borg)。在Docker技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性。
      Kubernetes是一个完备的分布式系统支撑平台,具有完备的集群管理能力,多扩多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和发现机制、內建智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制以及多粒度的资源配额管理能力。同时Kubernetes提供完善的管理工具,涵盖了包括开发、部署测试、运维监控在内的各个环节。
    Kubernetes中,Service是分布式集群架构的核心,一个Service对象拥有如下关键特征:
    • 拥有一个唯一指定的名字
    • 拥有一个虚拟IP(Cluster IP、Service IP、或VIP)和端口号
    • 能够体统某种远程服务能力
    • 被映射到了提供这种服务能力的一组容器应用上
      Service的服务进程目前都是基于Socket通信方式对外提供服务,比如Redis、Memcache、MySQL、Web Server,或者是实现了某个具体业务的一个特定的TCP Server进程,虽然一个Service通常由多个相关的服务进程来提供服务,每个服务进程都有一个独立的Endpoint(IP+Port)访问点,但Kubernetes能够让我们通过服务连接到指定的Service上。有了Kubernetes内奸的透明负载均衡和故障恢复机制,不管后端有多少服务进程,也不管某个服务进程是否会由于发生故障而重新部署到其他机器,都不会影响我们队服务的正常调用,更重要的是这个Service本身一旦创建就不会发生变化,意味着在Kubernetes集群中,我们不用为了服务的IP地址的变化问题而头疼了。
      容器提供了强大的隔离功能,所有有必要把为Service提供服务的这组进程放入容器中进行隔离。为此,Kubernetes设计了Pod对象,将每个服务进程包装到相对应的Pod中,使其成为Pod中运行的一个容器。为了建立Service与Pod间的关联管理,Kubernetes给每个Pod贴上一个标签Label,比如运行MySQL的Pod贴上name=mysql标签,给运行PHP的Pod贴上name=php标签,然后给相应的Service定义标签选择器Label Selector,这样就能巧妙的解决了Service于Pod的关联问题。
      在集群管理方面,Kubernetes将集群中的机器划分为一个Master节点和一群工作节点Node,其中,在Master节点运行着集群管理相关的一组进程kube-apiserver、kube-controller-manager和kube-scheduler,这些进程实现了整个集群的资源管理、Pod调度、弹性伸缩、安全控制、系统监控和纠错等管理能力,并且都是全自动完成的。Node作为集群中的工作节点,运行真正的应用程序,在Node上Kubernetes管理的最小运行单元是Pod。Node上运行着Kubernetes的kubelet、kube-proxy服务进程,这些服务进程负责Pod的创建、启动、监控、重启、销毁以及实现软件模式的负载均衡器。
      在Kubernetes集群中,它解决了传统IT系统中服务扩容和升级的两大难题。你只需为需要扩容的Service关联的Pod创建一个Replication Controller简称(RC),则该Service的扩容及后续的升级等问题将迎刃而解。在一个RC定义文件中包括以下3个关键信息。
    • 目标Pod的定义
    • 目标Pod需要运行的副本数量(Replicas)
    • 要监控的目标Pod标签(Label)
      在创建好RC后,Kubernetes会通过RC中定义的的Label筛选出对应Pod实例并实时监控其状态和数量,如果实例数量少于定义的副本数量,则会根据RC中定义的Pod模板来创建一个新的Pod,然后将新Pod调度到合适的Node上启动运行,知道Pod实例的数量达到预定目标,这个过程完全是自动化。
      
     Kubernetes优势:
        - 容器编排
        - 轻量级
        - 开源
        - 弹性伸缩
        - 负载均衡
    •Kubernetes的核心概念
    1.Master
      k8s集群的管理节点,负责管理集群,提供集群的资源数据访问入口。拥有Etcd存储服务(可选),运行Api Server进程,Controller Manager服务进程及Scheduler服务进程,关联工作节点Node。Kubernetes API server提供HTTP Rest接口的关键服务进程,是Kubernetes里所有资源的增、删、改、查等操作的唯一入口。也是集群控制的入口进程;Kubernetes Controller Manager是Kubernetes所有资源对象的自动化控制中心;Kubernetes Schedule是负责资源调度(Pod调度)的进程
     
    2.Node
      Node是Kubernetes集群架构中运行Pod的服务节点(亦叫agent或minion)。Node是Kubernetes集群操作的单元,用来承载被分配Pod的运行,是Pod运行的宿主机。关联Master管理节点,拥有名称和IP、系统资源信息。运行docker eninge服务,守护进程kunelet及负载均衡器kube-proxy.
    • 每个Node节点都运行着以下一组关键进程
    • kubelet:负责对Pod对于的容器的创建、启停等任务
    • kube-proxy:实现Kubernetes Service的通信与负载均衡机制的重要组件
    • Docker Engine(Docker):Docker引擎,负责本机容器的创建和管理工作
      Node节点可以在运行期间动态增加到Kubernetes集群中,默认情况下,kubelet会想master注册自己,这也是Kubernetes推荐的Node管理方式,kubelet进程会定时向Master汇报自身情报,如操作系统、Docker版本、CPU和内存,以及有哪些Pod在运行等等,这样Master可以获知每个Node节点的资源使用情况,冰实现高效均衡的资源调度策略。、
     
    3.Pod
      运行于Node节点上,若干相关容器的组合。Pod内包含的容器运行在同一宿主机上,使用相同的网络命名空间、IP地址和端口,能够通过localhost进行通。Pod是Kurbernetes进行创建、调度和管理的最小单位,它提供了比容器更高层次的抽象,使得部署和管理更加灵活。一个Pod可以包含一个容器或者多个相关容器。
      Pod其实有两种类型:普通Pod和静态Pod,后者比较特殊,它并不存在Kubernetes的etcd存储中,而是存放在某个具体的Node上的一个具体文件中,并且只在此Node上启动。普通Pod一旦被创建,就会被放入etcd存储中,随后会被Kubernetes Master调度到摸个具体的Node上进行绑定,随后该Pod被对应的Node上的kubelet进程实例化成一组相关的Docker容器冰启动起来,在。在默认情况下,当Pod里的某个容器停止时,Kubernetes会自动检测到这个问起并且重启这个Pod(重启Pod里的所有容器),如果Pod所在的Node宕机,则会将这个Node上的所有Pod重新调度到其他节点上。
     
    4.Replication Controller
      Replication Controller用来管理Pod的副本,保证集群中存在指定数量的Pod副本。集群中副本的数量大于指定数量,则会停止指定数量之外的多余容器数量,反之,则会启动少于指定数量个数的容器,保证数量不变。Replication Controller是实现弹性伸缩、动态扩容和滚动升级的核心。
     
    5.Service
      Service定义了Pod的逻辑集合和访问该集合的策略,是真实服务的抽象。Service提供了一个统一的服务访问入口以及服务代理和发现机制,关联多个相同Label的Pod,用户不需要了解后台Pod是如何运行。
    外部系统访问Service的问题
      首先需要弄明白Kubernetes的三种IP这个问题
        Node IP:Node节点的IP地址
        Pod IP: Pod的IP地址
        Cluster IP:Service的IP地址
      首先,Node IP是Kubernetes集群中节点的物理网卡IP地址,所有属于这个网络的服务器之间都能通过这个网络直接通信。这也表明Kubernetes集群之外的节点访问Kubernetes集群之内的某个节点或者TCP/IP服务的时候,必须通过Node IP进行通信
      其次,Pod IP是每个Pod的IP地址,他是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络。
      最后Cluster IP是一个虚拟的IP,但更像是一个伪造的IP网络,原因有以下几点
    • Cluster IP仅仅作用于Kubernetes Service这个对象,并由Kubernetes管理和分配P地址
    • Cluster IP无法被ping,他没有一个“实体网络对象”来响应
    • Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备通信的基础,并且他们属于Kubernetes集群这样一个封闭的空间。
    Kubernetes集群之内,Node IP网、Pod IP网于Cluster IP网之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊路由规则。
     
    6.Label
     Kubernetes中的任意API对象都是通过Label进行标识,Label的实质是一系列的Key/Value键值对,其中key于value由用户自己指定。Label可以附加在各种资源对象上,如Node、Pod、Service、RC等,一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上去。Label是Replication Controller和Service运行的基础,二者通过Label来进行关联Node上运行的Pod。
    我们可以通过给指定的资源对象捆绑一个或者多个不同的Label来实现多维度的资源分组管理功能,以便于灵活、方便的进行资源分配、调度、配置等管理工作。
    一些常用的Label如下:
    • 版本标签:"release":"stable","release":"canary"......
    • 环境标签:"environment":"dev","environment":"qa","environment":"production"
    • 架构标签:"tier":"frontend","tier":"backend","tier":"middleware"
    • 分区标签:"partition":"customerA","partition":"customerB"
    • 质量管控标签:"track":"daily","track":"weekly"
      Label相当于我们熟悉的标签,给某个资源对象定义一个Label就相当于给它大了一个标签,随后可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象,Kubernetes通过这种方式实现了类似SQL的简单又通用的对象查询机制。
     
      Label Selector在Kubernetes中重要使用场景如下:
      •   kube-Controller进程通过资源对象RC上定义Label Selector来筛选要监控的Pod副本的数量,从而实现副本数量始终符合预期设定的全自动控制流程
      •   kube-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立起每个Service岛对应Pod的请求转发路由表,从而实现Service的智能负载均衡
      •   通过对某些Node定义特定的Label,并且在Pod定义文件中使用Nodeselector这种标签调度策略,kuber-scheduler进程可以实现Pod”定向调度“的特性
     
    Kubernetes架构和组件

      - 服务分组,小集群,多集群
      - 服务分组,大集群,单集群
     
    •Kubernetes 组件:
      Kubernetes Master控制组件,调度管理整个系统(集群),包含如下组件:
      1.Kubernetes API Server
        作为Kubernetes系统的入口,其封装了核心对象的增删改查操作,以RESTful API接口方式提供给外部客户和内部组件调用。维护的REST对象持久化到Etcd中存储。
      2.Kubernetes Scheduler
        为新建立的Pod进行节点(node)选择(即分配机器),负责集群的资源调度。组件抽离,可以方便替换成其他调度器。
      3.Kubernetes Controller
        负责执行各种控制器,目前已经提供了很多控制器来保证Kubernetes的正常运行。
      4. Replication Controller
        管理维护Replication Controller,关联Replication Controller和Pod,保证Replication Controller定义的副本数量与实际运行Pod数量一致。
      5. Node Controller
        管理维护Node,定期检查Node的健康状态,标识出(失效|未失效)的Node节点。
      6. Namespace Controller
        管理维护Namespace,定期清理无效的Namespace,包括Namesapce下的API对象,比如Pod、Service等。
      7. Service Controller
        管理维护Service,提供负载以及服务代理。
      8.EndPoints Controller
        管理维护Endpoints,关联Service和Pod,创建Endpoints为Service的后端,当Pod发生变化时,实时更新Endpoints。
      9. Service Account Controller
        管理维护Service Account,为每个Namespace创建默认的Service Account,同时为Service Account创建Service Account Secret。
      10. Persistent Volume Controller
        管理维护Persistent Volume和Persistent Volume Claim,为新的Persistent Volume Claim分配Persistent Volume进行绑定,为释放的Persistent Volume执行清理回收。
      11. Daemon Set Controller
        管理维护Daemon Set,负责创建Daemon Pod,保证指定的Node上正常的运行Daemon Pod。
      12. Deployment Controller
        管理维护Deployment,关联Deployment和Replication Controller,保证运行指定数量的Pod。当Deployment更新时,控制实现Replication Controller和 Pod的更新。
      13.Job Controller
        管理维护Job,为Jod创建一次性任务Pod,保证完成Job指定完成的任务数目
      14. Pod Autoscaler Controller
        实现Pod的自动伸缩,定时获取监控数据,进行策略匹配,当满足条件时执行Pod的伸缩动作。
     
    •Kubernetes Node运行节点,运行管理业务容器,包含如下组件:
      1.Kubelet
        负责管控容器,Kubelet会从Kubernetes API Server接收Pod的创建请求,启动和停止容器,监控容器运行状态并汇报给Kubernetes API Server。
      2.Kubernetes Proxy
        负责为Pod创建代理服务,Kubernetes Proxy会从Kubernetes API Server获取所有的Service信息,并根据Service的信息创建代理服务,实现Service到Pod的请求路由和转发,从而实现Kubernetes层级的虚拟转发网络。
      3.Docker
        Node上需要运行容器服务

    二、基于kubernetes构建Docker集群环境实战

    kubernetes是google公司基于docker所做的一个分布式集群,有以下主件组成
      etcd: 高可用存储共享配置和服务发现,作为与minion机器上的flannel配套使用,作用是使每台 minion上运行的docker拥有不同的ip段,最终目的是使不同minion上正在运行的docker containner都有一个与别的任意一个containner(别的minion上运行的docker containner)不一样的IP地址。
      flannel: 网络结构支持
      kube-apiserver: 不论通过kubectl还是使用remote api 直接控制,都要经过apiserver
      kube-controller-manager: 对replication controller, endpoints controller, namespace controller, and serviceaccounts controller的循环控制,与kube-apiserver交互,保证这些controller工作
      kube-scheduler: Kubernetes scheduler的作用就是根据特定的调度算法将pod调度到指定的工作节点(minion)上,这一过程也叫绑定(bind)
      kubelet: Kubelet运行在Kubernetes Minion Node上. 它是container agent的逻辑继任者
      kube-proxy: kube-proxy是kubernetes 里运行在minion节点上的一个组件, 它起的作用是一个服务代理的角色
     
    图为GIT+Jenkins+Kubernetes+Docker+Etcd+confd+Nginx+Glusterfs架构
    如下:

     

    环境:
    centos7系统机器三台:
        10.0.0.81: 用来安装kubernetes master
        10.0.0.82: 用作kubernetes minion (minion1)
        10.0.0.83: 用作kubbernetes minion (minion2)
     
    一、关闭系统运行的防火墙及selinux
    1。如果系统开启了防火墙则按如下步骤关闭防火墙(所有机器)
    # systemctl stop firewalld # systemctl disable firewalld
    2.关闭selinux
    1
    2
    #setenforce 0
    #sed -i '/^SELINUX=/cSELINUX=disabled' /etc/sysconfig/selinux

      

     
    二、MASTER安装配置
    1. 安装并配置Kubernetes master(yum 方式)
    1
    # yum -y install etcd kubernetes

        

     配置etcd。确保列出的这些项都配置正确并且没有被注释掉,下面的配置都是如此 

    1
    2
    3
    4
    5
    6
    #vim /etc/etcd/etcd.conf
      
    ETCD_NAME=default
    ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
    ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
    ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379"

       配置kubernetes

    1
    2
    3
    4
    5
    6
    7
    8
    vim /etc/kubernetes/apiserver
      
    KUBE_API_ADDRESS="--address=0.0.0.0"KUBE_API_PORT="--port=8080"
    KUBELET_PORT="--kubelet_port=10250"
    KUBE_ETCD_SERVERS="--etcd_servers=http://127.0.0.1:2379"
    KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.254.0.0/16"
    KUBE_ADMISSION_CONTROL="--admission_control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ResourceQuota"
    KUBE_API_ARGS=""

       

    2. 启动etcd, kube-apiserver, kube-controller-manager and kube-scheduler服务
    1
    # for SERVICES in etcd kube-apiserver kube-controller-manager kube-scheduler; do systemctl restart $SERVICES systemctl enable $SERVICES systemctl status $SERVICES done

    3.设置etcd网络

    1
    #etcdctl -C 10.0.0.81:2379 set /atomic.io/network/config '{"Network":"10.1.0.0/16"}'
    4. 至此master配置完成,运行kubectl get nodes可以查看有多少minion在运行,以及其状态。这里我们的minion还都没有开始安装配置,所以运行之后结果为空
    1
    # kubectl get nodes NAME LABELS STATUS

       

    三、MINION安装配置(每台minion机器都按如下安装配置)
     
    1. 环境安装和配置
    1
    # yum -y install flannel kubernetes

      配置kubernetes连接的服务端IP

    1
    2
    3
    #vim /etc/kubernetes/config
    KUBE_MASTER="--master=http://10.0.0.81:8080"
    KUBE_ETCD_SERVERS="--etcd_servers=http://10.0.0.81:2379"

       配置kubernetes  ,(请使用每台minion自己的IP地址比如10.0.0.81:代替下面的$LOCALIP)

     
    1
    2
    3
    4
    5
    #vim /etc/kubernetes/kubelet<br>KUBELET_ADDRESS="--address=0.0.0.0"
    KUBELET_PORT="--port=10250"
    # change the hostname to this host’s IP address KUBELET_HOSTNAME="--hostname_override=$LOCALIP"
    KUBELET_API_SERVER="--api_servers=http://10.0.0.81:8080"
    KUBELET_ARGS=""

      

    2. 准备启动服务(如果本来机器上已经运行过docker的请看过来,没有运行过的请忽略此步骤)
        运行ifconfig,查看机器的网络配置情况(有docker0)
    1
    2
    3
    4
    5
    # ifconfig docker0
    Link encap:Ethernet HWaddr 02:42:B2:75:2E:67 inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0 UP
    BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0
    errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0
    RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

      warning:在运行过docker的机器上可以看到有docker0,这里在启动服务之前需要删掉docker0配置,在命令行运行:sudo ip link delete docker0

    3.配置flannel网络

    1
    2
    3
    #vim /etc/sysconfig/flanneld
    FLANNEL_ETCD_ENDPOINTS="http://10.0.0.81:2379"
    FLANNEL_ETCD_PREFIX="/atomic.io/network"  
    PS:其中atomic.io与上面etcd中的Network对应
     
    4. 启动服务
    1
    # for SERVICES in flanneld kube-proxy kubelet docker; do systemctl restart $SERVICES systemctl enable $SERVICES systemctl status $SERVICES done

     

    四、配置完成验证安装
        确定两台minion(10.0.0.82和10.0.0.83)和一台master(10.0.0.81)都已经成功的安装配置并且服务都已经启动了。
        切换到master机器上,运行命令kubectl get nodes 
    1
    2
    3
    4
    # kubectl get nodes
    NAME STATUS AGE
    10.0.0.82 Ready 1m
    10.0.0.83 Ready 1m

      可以看到配置的两台minion已经在master的node列表中了。如果想要更多的node,只需要按照minion的配置,配置更多的机器就可以了。

    三、Kubernetes之深入了解Pod

     
    1、yaml格式的Pod配置文件内容及注解
      深入Pod之前,首先我们来了解下Pod的yaml整体文件内容及功能注解。
    如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    # yaml格式的pod定义文件完整内容:
    apiVersion: v1       #必选,版本号,例如v1
    kind: Pod       #必选,Pod
    metadata:       #必选,元数据
      name: string       #必选,Pod名称
      namespace: string    #必选,Pod所属的命名空间
      labels:      #自定义标签
        - name: string     #自定义标签名字
      annotations:       #自定义注释列表
        - name: string
    spec:         #必选,Pod中容器的详细定义
      containers:      #必选,Pod中容器列表
      - name: string     #必选,容器名称
        image: string    #必选,容器的镜像名称
        imagePullPolicy: [Always | Never | IfNotPresent] #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
        command: [string]    #容器的启动命令列表,如不指定,使用打包时使用的启动命令
        args: [string]     #容器的启动命令参数列表
        workingDir: string     #容器的工作目录
        volumeMounts:    #挂载到容器内部的存储卷配置
        - name: string     #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
          mountPath: string    #存储卷在容器内mount的绝对路径,应少于512字符
          readOnly: boolean    #是否为只读模式
        ports:       #需要暴露的端口库号列表
        - name: string     #端口号名称
          containerPort: int   #容器需要监听的端口号
          hostPort: int    #容器所在主机需要监听的端口号,默认与Container相同
          protocol: string     #端口协议,支持TCP和UDP,默认TCP
        env:       #容器运行前需设置的环境变量列表
        - name: string     #环境变量名称
          value: string    #环境变量的值
        resources:       #资源限制和请求的设置
          limits:      #资源限制的设置
            cpu: string    #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
            memory: string     #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
          requests:      #资源请求的设置
            cpu: string    #Cpu请求,容器启动的初始可用数量
            memory: string     #内存清楚,容器启动的初始可用数量
        livenessProbe:     #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
          exec:      #对Pod容器内检查方式设置为exec方式
            command: [string]  #exec方式需要制定的命令或脚本
          httpGet:       #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
            path: string
            port: number
            host: string
            scheme: string
            HttpHeaders:
            - name: string
              value: string
          tcpSocket:     #对Pod内个容器健康检查方式设置为tcpSocket方式
             port: number
           initialDelaySeconds: 0  #容器启动完成后首次探测的时间,单位为秒
           timeoutSeconds: 0   #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
           periodSeconds: 0    #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
           successThreshold: 0
           failureThreshold: 0
           securityContext:
             privileged:false
        restartPolicy: [Always | Never | OnFailure]#Pod的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod
        nodeSelector: obeject  #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
        imagePullSecrets:    #Pull镜像时使用的secret名称,以key:secretkey格式指定
        - name: string
        hostNetwork:false      #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
        volumes:       #在该pod上定义共享存储卷列表
        - name: string     #共享存储卷名称 (volumes类型有很多种)
          emptyDir: {}     #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
          hostPath: string     #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
            path: string     #Pod所在宿主机的目录,将被用于同期中mount的目录
          secret:      #类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
            scretname: string  
            items:     
            - key: string
              path: string
          configMap:     #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
            name: string
            items:
            - key: string
              path: string    

     

    2、Pod基本用法:
      在使用docker时,我们可以使用docker run命令创建并启动一个容器,而在Kubernetes系统中对长时间运行的容器要求是:其主程序需要一直在前台运行。如果我们创建的docker镜像的启动命令是后台执行程序,例如Linux脚本:
      nohup ./startup.sh &
      则kubelet创建包含这个容器的pod后运行完该命令,即认为Pod执行结束,之后根据RC中定义的pod的replicas副本数量生产一个新的pod,而一旦创建出新的pod,将在执行完命令后陷入无限循环的过程中,这就是Kubernetes需要我们创建的docker镜像以一个前台命令作为启动命令的原因。
      对于无法改造为前台执行的应用,也可以使用开源工具supervisor辅助进行前台运行的功能。
    ****Pod可以由一个或多个容器组合而成
    例如:两个容器应用的前端frontend和redis为紧耦合的关系,应该组合成一个整体对外提供服务,则应该将这两个打包为一个pod.
    配置文件frontend-localredis-pod.yaml如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    apiVersion:v1
    kind: Pod
    metadata:
      name: redis-php
      label:
        name: redis-php
    spec:
      containers:
      - name: frontend
        image: kubeguide/guestbook-php-frontend:localredis
        ports:
        - containersPort: 80
      - name: redis-php
        image:kubeguide/redis-master
        ports:
        - containersPort: 6379

      

      属于一个Pod的多个容器应用之间相互访问只需要通过localhost就可以通信,这一组容器被绑定在一个环境中。
      使用kubectl create创建该Pod后,get Pod信息可以看到如下图:
    1
    2
    3
    #kubectl get gods
    NAME READY STATUS RESTATS AGE
    redis-php 2/2Running 0 10m

      可以看到READY信息为2/2,表示Pod中的两个容器都成功运行了.

      查看pod的详细信息,可以看到两个容器的定义和创建过程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    [root@kubernetes-master ~]# kubectl describe redis-php
    the server doesn't have a resourcetype"redis-php"
    [root@kubernetes-master ~]# kubectl describe pod redis-php
    Name: redis-php
    Namespace: default
    Node: kubernetes-minion/10.0.0.23
    Start Time: Wed, 12 Apr 2017 09:14:58 +0800
    Labels: name=redis-php
    Status: Running
    IP: 10.1.24.2
    Controllers: <none>
    Containers:
    nginx:
    Container ID: docker://d05b743c200dff7cf3b60b7373a45666be2ebb48b7b8b31ce0ece9be4546ce77
    Image: nginx
    Image ID: docker-pullable://docker.io/nginx@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582
    Port: 80/TCP
    State: Running
    Started: Wed, 12 Apr 2017 09:19:31 +0800

      

    3、静态Pod
      静态pod是由kubelet进行管理的仅存在于特定Node的Pod上,他们不能通过API Server进行管理,无法与ReplicationController、Deployment或者DaemonSet进行关联,并且kubelet无法对他们进行健康检查。静态Pod总是由kubelet进行创建,并且总是在kubelet所在的Node上运行。
    创建静态Pod有两种方式:配置文件或者HTTP方式
    1)配置文件方式
      首先,需要设置kubelet的启动参数"--config",指定kubelet需要监控的配置文件所在的目录,kubelet会定期扫描该目录,冰根据目录中的 .yaml或 .json文件进行创建操作
    假设配置目录为/etc/kubelet.d/配置启动参数:--config=/etc/kubelet.d/,然后重启kubelet服务后,再宿主机受用docker ps或者在Kubernetes Master上都可以看到指定的容器在列表中
    由于静态pod无法通过API Server直接管理,所以在master节点尝试删除该pod,会将其变为pending状态,也不会被删除
    1
    2
    3
    4
    5
    #kubetctl delete pod static-web-node1
    pod "static-web-node1"deleted
    #kubectl get pods
    NAME READY STATUS RESTARTS AGE
    static-web-node1 0/1Pending 0 1s

      

      要删除该pod的操作只能在其所在的Node上操作,将其定义的.yaml文件从/etc/kubelet.d/目录下删除
    1
    2
    #rm -f /etc/kubelet.d/static-web.yaml
    #docker ps

      

    4、Pod容器共享Volume
      Volume类型包括:emtyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、scsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume等等,可以定义多个Volume,每个Volume的name保持唯一。在同一个pod中的多个容器能够共享pod级别的存储卷Volume。Volume可以定义为各种类型,多个容器各自进行挂载操作,讲一个Volume挂载为容器内需要的目录。
    如下图:

     

      如上图中的Pod中包含两个容器:tomcat和busybox,在pod级别设置Volume “app-logs”,用于tomcat想其中写日志文件,busybox读日志文件。
    配置文件如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    apiVersion:v1
    kind: Pod
    metadata:
      name: redis-php
      label:
        name: volume-pod
    spec:
      containers:
      - name: tomcat
        image: tomcat
        ports:
        - containersPort: 8080
        volumeMounts:
        - name: app-logs
          mountPath:/usr/local/tomcat/logs
      - name: busybox
        image:busybox
        command: ["sh","-C","tail -f /logs/catalina*.log"]
      volumes:
      - name: app-logs
        emptyDir:{}
    busybox容器可以通过kubectl logs查看输出内容
    1
    #kubectl logs volume-pod -c busybox 
    tomcat容器生成的日志文件可以登录容器查看
    1
    #kubectl exec -ti volume-pod -c tomcat -- ls /usr/local/tomcat/logs
    5.Pod的配置管理
      应用部署的一个最佳实践是将应用所需的配置信息于程序进行分离,这样可以使得应用程序被更好的复用,通过不用配置文件也能实现更灵活的功能。将应用打包为容器镜像后,可以通过环境变量或外挂文件的方式在创建容器时进行配置注入。ConfigMap是Kubernetes v1.2版本开始提供的一种统一集群配置管理方案。
       5.1 ConfigMap:容器应用的配置管理
      容器使用ConfigMap的典型用法如下:
      (1)生产为容器的环境变量。
      (2)设置容器启动命令的启动参数(需设置为环境变量)。
      (3)以Volume的形式挂载为容器内部的文件或目录。
      ConfigMap以一个或多个key:value的形式保存在Kubernetes系统中共应用使用,既可以用于表示一个变量的值,也可以表示一个完整的配置文件内容。
    通过yuaml配置文件或者直接使用kubelet create configmap 命令的方式来创建ConfigMap
       5.2 ConfigMap的创建
       举个小例子cm-appvars.yaml来描述将几个应用所需的变量定义为ConfigMap的用法:
     
    1
    2
    3
    4
    5
    6
    7
    8
    # vim cm-appvars.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: cm-appvars
    data:
      apploglevel: info
      appdatadir:/var/data

     

      执行kubectl create命令创建该ConfigMap
    1
    2
    #kubectl create -f cm-appvars.yaml
    configmap "cm-appvars.yaml"created
      查看建立好的ConfigMap:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #kubectl get configmap
    NAME DATA AGE
    cm-appvars 2 3s
    [root@kubernetes-master ~]# kubectl describe configmap cm-appvars
    Name: cm-appvars
    Namespace: default
    Labels: <none>
    Annotations: <none>
      
    Data
    ====
    appdatadir: 9 bytes
    apploglevel: 4 bytes
    [root@kubernetes-master ~]# kubectl get configmap cm-appvars -o yaml
    apiVersion: v1
    data:
    appdatadir: /var/data
    apploglevel: info
    kind: ConfigMap
    metadata:
    creationTimestamp: 2017-04-14T06:03:36Z
    name: cm-appvars
    namespace: default
    resourceVersion:"571221"
    selfLink: /api/v1/namespaces/default/configmaps/cm-appvars
    uid: 190323cb-20d8-11e7-94ec-000c29ac8d83 
     
      另:创建一个cm-appconfigfile.yaml描述将两个配置文件server.xml和logging.properties定义为configmap的用法,设置key为配置文件的别名,value则是配置文件的文本内容:
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: cm-appvars
    data:
      key-serverxml:
        <?xml Version='1.0'encoding='utf-8'?>
        <Server port="8005"shutdown="SHUTDOWN">
        .....
          </service>
        </Server>
      key-loggingproperties:
        "handlers=lcatalina.org.apache.juli.FileHandler,
        ...."
      在pod "cm-test-app"定义中,将configmap "cm-appconfigfile"中的内容以文件形式mount到容器内部configfiles目录中。
    Pod配置文件cm-test-app.yaml内容如下:
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #vim cm-test-app.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: cm-test-app
    spec:
      containers:
      - name: cm-test-app
        image: tomcat-app:v1
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: serverxml                          #引用volume名
          mountPath:/configfiles                       #挂载到容器内部目录
            configMap:
              name: cm-test-appconfigfile                  #使用configmap定义的的cm-appconfigfile
              items:
              - key: key-serverxml                     #将key=key-serverxml
                path: server.xml                           #value将server.xml文件名进行挂载
              - key: key-loggingproperties                 #将key=key-loggingproperties    
                path: logging.properties                   #value将logging.properties文件名进行挂载 
      创建该Pod:
    1
    2
    #kubectl create -f cm-test-app.yaml
    Pod "cm-test-app"created  
      登录容器查看configfiles目录下的server.xml和logging.properties文件,他们的内容就是configmap “cm-appconfigfile”中定义的两个key的内容
    1
    2
    3
    #kubectl exec -ti cm-test-app -- bash
    root@cm-rest-app:/# cat /configfiles/server.xml
    root@cm-rest-app:/# cat /configfiles/logging.properties

     

      5.3使用ConfigMap的条件限制
      使用configmap的限制条件如下:
      • configmap必须在pod之间创建
      • configmap也可以定义为属于某个Namespace,只有处于相同namespaces中的pod可以引用
      • configmap中配额管理还未能实现
      • kubelet只支持被api server管理的pod使用configmap,静态pod无法引用
      • 在pod对configmap进行挂载操作时,容器内部职能挂载为目录,无法挂载文件。
    6.Pod生命周期和重启策略
      Pod在整个生命周期过程中被定义为各种状态,熟悉Pod的各种状态有助于理解如何设置Pod的调度策略、重启策略
      Pod的状态包含以下几种,如图:
      
      Pod的重启策略(RestartPolicy)应用于Pod内所有的容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某哥容器异常退出或者健康检查石柏师,kubelet将根据RestartPolicy的设置进行相应的操作
      Pod的重启策略包括Always、OnFailure及Nerver,默认值为Always。
      kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5分钟,并且成功重启后的10分钟后重置该事件。
      Pod的重启策略和控制方式息息相关,当前可用于管理Pod的控制器宝库ReplicationController、Job、DaemonSet及直接通过kubelet管理(静态Pod),每种控制器对Pod的重启策略要求如下:
      • RC和DaemonSet:必须设置为Always,需要保证该容器持续运行
      • Job:OnFailure或Nerver,确保容器执行完成后不再重启
      • kubelet:在Pod失效时重启他,不论RestartPolicy设置什么值,并且也不会对Pod进行健康检查
     
    7、Pod健康检查
      对Pod的健康检查可以通过两类探针来检查:LivenessProbe和ReadinessProbe
      • LivenessProbe探针:用于判断容器是否存活(running状态),如果LivenessProbe探针探测到容器不健康,则kubelet杀掉该容器,并根据容器的重启策略做响应处理
      • ReadinessProbe探针:用于判断容器是否启动完成(ready状态),可以接受请求。如果ReadinessProbe探针探测失败,则Pod的状态被修改。Endpoint Controller将从service的Endpoint中删除包含该容器所在的Pod的Endpoint。
      kubelet定制执行LivenessProbe探针来诊断容器的健康状况。LivenessProbe有三种事项方式。
     
    (1)ExecAction:在容器内部执行一个命令,如果该命令的返回值为0,则表示容器健康
    例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    apiVersion:v1
    kind: Pod
    metadata:
      name: liveness-exec
      label:
        name: liveness
    spec:
      containers:
      - name: tomcat
        image: grc.io/google_containers/tomcat
        args:
        -/bin/sh
        - -c
        -echook >/tmp.health;sleep10;rm-fr /tmp/health;sleep600
        livenessProbe:
          exec:
            command:
            -cat
            -/tmp/health
          initianDelaySeconds:15
          timeoutSeconds:1 
    (2)TCPSocketAction:通过容器ip地址和端口号执行TCP检查,如果能够建立tcp连接表明容器健康
    例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    kind: Pod
    metadata:
      name: pod-with-healthcheck
    spec:
      containers:
      - name: nginx
        image: nginx
        livenessProbe:
          tcpSocket:
            port: 80
          initianDelaySeconds:30
          timeoutSeconds:1
    (3)HTTPGetAction:通过容器Ip地址、端口号及路径调用http get方法,如果响应的状态吗大于200且小于400,则认为容器健康
    例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    apiVersion:v1
    kind: Pod
    metadata:
      name: pod-with-healthcheck
    spec:
      containers:
      - name: nginx
        image: nginx
        livenessProbe:
          httpGet:
            path:/_status/healthz
            port: 80
          initianDelaySeconds:30
          timeoutSeconds:1

      

    对于每种探针方式,都需要设置initialDelaySeconds和timeoutSeconds两个参数,它们含义如下:
    • initialDelaySeconds:启动容器后首次监控检查的等待时间,单位秒
    • timeouSeconds:健康检查发送请求后等待响应的超时时间,单位秒。当发生超时就被认为容器无法提供服务无,该容器将被重启
     
    8.玩转Pod调度
      在Kubernetes系统中,Pod在大部分场景下都只是容器的载体而已,通常需要通过RC、Deployment、DaemonSet、Job等对象来完成Pod的调度和自动控制功能。
      8.1 RC、Deployment:全自动调度
      RC的主要功能之一就是自动部署容器应用的多份副本,以及持续监控副本的数量,在集群内始终维护用户指定的副本数量。
    在调度策略上,除了使用系统内置的调度算法选择合适的Node进行调度,也可以在Pod的定义中使用NodeSelector或NodeAffinity来指定满足条件的Node进行调度。
       1)NodeSelector:定向调度
      Kubernetes Master上的scheduler服务(kube-Scheduler进程)负责实现Pod的调度,整个过程通过一系列复杂的算法,最终为每个Pod计算出一个最佳的目标节点,通常我们无法知道Pod最终会被调度到哪个节点上。实际情况中,我们需要将Pod调度到我们指定的节点上,可以通过Node的标签和pod的nodeSelector属性相匹配来达到目的。
      (1)首先通过kubectl label命令给目标Node打上标签
    kubectl label nodes <node-name> <label-key>=<label-value>
    例:
    1
    #kubectllabel nodes k8s-node-1 zonenorth
      (2)然后在Pod定义中加上nodeSelector的设置
    例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    apiVersion:v1
    kind: Pod
    metadata:
      name: redis-master
      label:
        name: redis-master
    spec:
      replicas: 1
      selector:
        name: redis-master
        template:
          metadata:
            labels:
              name: redis-master
          spec:
            containers:
            - name: redis-master
              images: kubeguide/redis-master
              ports:
              - containerPort: 6379
            nodeSelector:
              zone: north 
    运行kubectl create -f命令创建Pod,scheduler就会将该Pod调度到拥有zone=north标签的Node上。 如果多个Node拥有该标签,则会根据调度算法在该组Node上选一个可用的进行Pod调度。
    需要注意的是:如果集群中没有拥有该标签的Node,则这个Pod也无法被成功调度。
     
      2)NodeAffinity:亲和性调度
    该调度策略是将来替换NodeSelector的新一代调度策略。由于NodeSelector通过Node的Label进行精确匹配,所有NodeAffinity增加了In、NotIn、Exists、DoesNotexist、Gt、Lt等操作符来选择Node。调度侧露更加灵活。
     
      8.2 DaemonSet:特定场景调度
    DaemonSet用于管理集群中每个Node上仅运行一份Pod的副本实例,如图

     

    这种用法适合一些有下列需求的应用:
    • 在每个Node上运行个以GlusterFS存储或者ceph存储的daemon进程
    • 在每个Node上运行一个日志采集程序,例如fluentd或者logstach
    • 在每个Node上运行一个健康程序,采集Node的性能数据。
    DaemonSet的Pod调度策略类似于RC,除了使用系统内置的算法在每台Node上进行调度,也可以在Pod的定义中使用NodeSelector或NodeAffinity来指定满足条件的Node范围来进行调度。
     
      8.3 批处理调度
     
    9.Pod的扩容和缩荣
      在实际生产环境中,我们经常遇到某个服务需要扩容的场景,也有可能因为资源精确需要缩减资源而需要减少服务实例数量,此时我们可以Kubernetes中RC提供scale机制来完成这些工作。
    以redis-slave RC为例,已定义的最初副本数量为2,通过kubectl scale命令可以将Pod副本数量重新调整
    1
    2
    3
    4
    5
    6
    7
    #kubectl scale rc redis-slave --replicas=3
    ReplicationController"redis-slave"scaled
    #kubectl get pods
    NAME READY STATUS RESTARTS AGE
    redis-slave-1sf23 1/1Running 0 1h
    redis-slave-54wfk 1/1Running 0 1h
    redis-slave-3da5y 1/1Running 0 1h 
      除了可以手工通过kubectl scale命令完成Pod的扩容和缩容操作以外,新版本新增加了Horizontal Podautoscaler(HPA)的控制器,用于实现基于CPU使用路进行启动Pod扩容缩容的功能。该控制器基于Mastger的kube-controller-manager服务启动参数 --horizontal-pod-autoscler-sync-period定义的时长(默认30秒),周期性监控目标Pod的Cpu使用率并在满足条件时对ReplicationController或Deployment中的Pod副本数量进行调整,以符合用户定义的平均Pod Cpu使用率,Pod Cpu使用率来源于heapster组件,所以需预先安装好heapster。
     
    10.Pod的滚动升级
      当集群中的某个服务需要升级时,我们需要停止目前与该服务相关的所有Pod,然后重新拉取镜像并启动。如果集群规模较大,因服务全部停止后升级的方式将导致长时间的服务不可用。由此,Kubernetes提供了rolling-update(滚动升级)功能来解决该问题。
    滚动升级通过执行kubectl rolling-update命令一键完成,该命令创建一个新的RC,然后自动控制旧版本的Pod数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,最终实现Pod的升级。需要注意的是,系统要求新的RC需要与旧的RC在相同的Namespace内,即不能把别人的资产转到到自家名下。
      例:将redis-master从1.0版本升级到2.0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    apiVersion: v1
    kind: replicationController
    metadata:
      name: redis-master-v2
      labels:
        name: redis-master
        Version: v2
    spec:
      replicas: 1
      selector:
        name: redis-master
        Version: v2
      template:
        labels:
          name: redis-master
          Version: v2
        spec:
          containers:
          - name: master
            images: kubeguide/redis-master:2.0
            ports:
            - containerPort: 6379

      需要注意的点:

      (1)RC的name不能与旧的RC名字相同
      (2)在sele中应至少有一个label与旧的RC的label不同,以标识为新的RC。本例中新增了一个名为version的label与旧的RC区分
      运行kubectl rolling-update来完成Pod的滚动升级:
    1
    #kubectl rolling-update redis-master -f redis-master-controller-v2.yaml 
      另一种方法就是不使用配置文件,直接用kubectl rolling-update加上--image参数指定新版镜像名来完成Pod的滚动升级
    1
    #kubectl rolling-update redis-master --image=redis-master:2.0
      与使用配置文件的方式不同的是,执行的结果是旧的RC被删除,新的RC仍然使用就的RC的名字。
      如果在更新过程总发现配置有误,则用户可以中断更新操作,并通过执行kubectl rolling-update-rollback完成Pod版本的回滚。

    有没有可以随意使用的无版权音乐资源库?

    $
    0
    0

    五大免版权音乐网站!https://www.zhihu.com/video/1028592583492120576


    不管是剪辑视频还是音频制作,我们都对音乐有大量的需求,那么在哪里能找到免费、高质量又合法的版权音乐呢? 以下,就是我们所推荐的一些(部分网站的版权有详细说明,并非完全免):


    NO.1 FreePD

    一个汇集了互联网海量免版税的公共领域音乐资源网站,提供了许多不同类型的音乐资源,你可以下载任意一首音乐,

    更可以以14美元的价格一键下载全部内容。


    NO.2 Jamendo

    Jamendo上的所有音乐都是由独立艺术家创作的,个人使用都免费。但用于视频制作或其他商业用途需要遵守该网站的相关条例

    NO.3 musopen

    该网站主要提供古典音乐资源,包括吉他、大提琴、小提琴等等,所有音乐均可免费下载,值的一提的是该网站还提供大量的乐谱、教材、录音,这些资源也可以免费使用。

    NO.4 Imslp

    全球最大的免费公共版权音乐网站,网站中的乐谱、音乐都是可以免费下载的,由于该网站涉及的国家很多,在使用的时候需要注意自己国家中的版权条例。


    NO.5 AudioNautix

    一个免费音乐收集平台,该网站的音乐里可以用于任何地方,但是必须标注该音乐由 Audionautix 网站提供。


    最后,如果你有梯子,在 YouTube 的创作者区域,也有大量免版权的内容,有的可以直接使用,有的只需要你注明音乐的来源即可。欢迎在 Topbook 微信后台回复“ 音乐资源”获取以上提到的所有网站链接。


    更多资源技能戳这里: topbook.cc

    收藏也要点赞,可不敢白嫖!


    Copyright.

    吐血出品,未经许可不得转载、洗稿、盗用。

    基于Nginx和Consul构建高可用及自动发现的Docker服务架构 - DockOne.io

    $
    0
    0

    导读

    如果你在大量接触或使用微服务的话,你可能会碰到一个问题:当你创建的服务数量越来越多时,这些服务之间的通信便越难管理,而且维护代价会越来越高。

    针对这个问题,Consul给出了一份完美的答卷。
    01.jpeg

    Consul是一套开源的分布式服务发现和配置管理系统,支持多数据中心分布式高可用。Consul是HashiCorp(Vagrant的创建者)开发的一个服务发现与配置项目,用Go语言开发,基于 Mozilla Public License 2.0 的协议开源。

    另外,架构里的另一个重要的角色则是Docker。Docker技术的不断成熟,孵化出了大量优秀的相关技术,比如docker监控,开源技术有cAdvisor、Cloud Insight或Telegraf等,自定义开发方式,Docker stats、Python API或伪文件系统;Docker管理,Forman、Kubernetes与CoreOS(都已整合各类组件),各类技术覆盖网络、监控、维护、部署、开发等方面,帮助开发、运维人员快速构建、运营Docker服务环境。

    面对如此优秀和快速发展的一个生态圈技术,在企业当中,我们该如何选择并发挥Docker的强大之处?

    而现实中,我们一直渴望着追求提供高质量、高可用的服务架构体系,同时减少不必要的部署和维护代价,减少容错率。

    面对如此高的要求,我们提供了以下两种架构方案:
    1. Docker+Etcd+Confd+Nginx
    2. Docker+Consul+Nginx


    架构设计与架构流程

    Docker+Etcd+Confd + Nginx,采用松散式的组织结构,但各个组件之间的通讯是非常严密的,且扩展性更强,定制也更加灵活,这样一个架构体系在实用环境中,确实也体现了各个组件的强大功效。Etcd用于提供分享配置和服务发现,Confd通过查询Etcd,结合配置模板引擎,保持本地配置最新,同时具备定期探测机制,配置变更自动reload;Nginx提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,为此提供一套完美的架构服务体系。

    以上的一套架构方案确实也是当前用得比较普遍的方案,而今天我将对比以上方案,给大家提供另外一种更加高效、快捷,并且维护代价和容错率更低,分布式支持力度更强的架构方案Docker+Consul+Nginx(如图)。
    02.jpeg

    服务架构图

    使用Docker将Consul、Consul Template、Registrator和Nginx组装成一个值得信任且可扩展的服务框架,这套架构让你在这个框架中添加和移除服务,不需要重写任何配置,也不需要重启任何服务,一切都能正常运行,具体工作流程如下图。
    03.jpeg

    架构流程图

    架构优势

    Docker+Consul+Nginx虽然看起来是三个组件的运用,但却证明是一个有机的整体。它们互相联系、互相作用,完全满足我们对高可用、高效服务架构方案的需求,是Docker生态圈中最理想的组合之一,具有以下优势:

    自动发现与注册组件Consul(内部的架构设计参见图Consul内部的架构设计)使用Raft算法来保证一致性,比复杂的Paxos算法更直接。相比较而言,ZooKeeper 采用的是 Paxos,而etcd使用的则是Raft;

    支持多数据中心(参见图Consul多数据中心分布式集群架构),多数据中心集群可以避免单数据中心的单点故障,ZooKeeper和etcd均不提供多数据中心功能的支持;

    自动、实时发现及无感知服务刷新,具备资源弹性,伸缩自如(通过生成、销毁容器实现);

    支持健康检查,负载能动态在可用的服务实例上进行均衡,etcd不提供此功能;

    支持足够多台Docker容器(前提架构资源足以保证性能支撑);

    支持HTTP和DNS协议接口,ZooKeeper的集成较为复杂,etcd只支持HTTP协议;

    服务规模方便进行快速调整,官方提供Web管理界面,etcd无此功能;

    Consul Template搭配Consul使用,支持多种接入层,如Nginx、Haproxy。
    04.jpeg

    Consul内部的架构设计

    05.jpeg

    Consul多数据中心分布式集群架构

    模块介绍

    服务发现模块

    服务发现的项目已经有不少,包括之前介绍的Consul,以及etcd、SkyDNS,以及主要关注一致性的强大的ZooKeeper等。

    这些项目各有优缺点,功能上大同小异,都是要通过某种机制来获取服务信息,然后通过维护一套(分布式)数据库来存储服务的信息。在这里,选用HashiCorp公司的Consul作为服务发现的管理端。

    服务注册模块

    服务注册的手段有很多,从发起方是谁可以分为主动注册和被动探测,它们都对网络连通性要求较高。从短期看,主动注册方式会比较容易实现一些,应用情形更广泛;但长期维护上,被动探测方式应该是更高效的设计。

    这里,我们选用Registrator,它可以通过跟本地的Docker引擎通信,来获取本地启动的容器信息,并且注册到指定的服务发现管理端。

    配置更新模块

    服务被调整后,负载均衡器要想动态重新分配负载,就需要通过配置来获取更新。笔者本着追求简单和高效,使用HashiCorp公司的Consul Template,可以通过监听Consul的注册信息,当添加或者移除服务时,Consul Template可以通过连接Consul更新配置并重启应用。

    负载均衡模块

    负载均衡对性能要求很高,其实并不是软件所擅长的领域,但软件方案胜在成本低、维护方便,包括 LVS、HAProxy、Nginx都是很优秀的设计方案。

    这里,我们选用Nginx(Consul Template官方同时提供了Nginx和HAProxy的配置demo)。Nginx不仅是个强大的Web代理服务器,同时在负载均衡方面表现也不俗。更关键的,新版本的Nginx对在线升级支持做到了极致。实时配置更新更是不在话下,可以保证服务的连续性。
    06.jpeg

    服务架设

    这样一来我们应用的具体场景将会变成如下模式:

    用户将会通过访问到我们的前端服务;

    Nginx将会根据转发策略把请求转发给其中一个健康的服务实体;

    前端服务,将会通过负载策略去访问某个后端服务。这一点也是通过Nginx的自身组件实现。

    接下来,我们一步步部署实施这套架构组件(所有的组件都是基于Docker方式部署,其他部署方式参见各组件官网文档):

    Consul Template创建Docker镜像,并运行:
    [root@sc ~]# docker run --rm -it yeasy/nginx-consul-template:latest bash      

    同时我们参考一下Consul Template官方提供的Nginx配置示例:
    upstream frontend { {{range service "app.frontend"}}      

    server {{.Address}};{{end}}



    为此产生的nginx.conf配置模板如下:
    upstream app {      

    least_conn;

    {{range service "app.frontend"}}server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1;

    //”app.frontend”必须要和真实服务(如下面部署的“simple http server”在consul上发现的service name相同

    {{else}}server 127.0.0.1:65535; # force a 502{{end}}

    }

    server {

    listen 80 default_server;

    location / {

    proxy_pass http://app;

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_set_header Host $host;

    proxy_set_header X-Real-IP $remote_addr;

    }

    }  

    这个Docker容器将会运行Consul Template和Nginx,当服务有变化时,它将重写Nginx的app.conf文件并且重新加载Nginx。

    Consul创建Docker镜像,并运行:

    Consul是一个拥有DNS和HTTP API的服务。它主要负责服务发现,以及对服务的健康检测和“键-值”对存储库。

    执行以下命令在Docker Container中运行Consul:
    docker run -it -h node \      

    -p 8500:8500 \

    -p 8600:53/udp \

    progrium/consul \

    -server \

    -bootstrap \

    -advertise $DOCKER_IP \

    -log-level debug 

    如果你通过浏览器能够访问$DOCKER_IP:8500,你将在控制面板上看到Consul中已经注册的所有服务。
    07.jpeg

    在Docker容器启动后,Registrator配置好相应的环境变量并将这个容器注册到Consul上,示例如下:
    docker run -it \      

    -v /var/run/docker.sock:/tmp/docker.sock \

    -h $DOCKER_IP gliderlabs/registrator:latest\

    consul://$DOCKER_IP:8500 


    最后,我们搭建一个SimpleHTTPServer作为真实服务的模拟:

    为方便我们动态调控真实服务的数量,我们使用docker-compose来做批管理,编写docker-compose.yml如下:
    #app application, scale this with docker-compose scale web=3      

    web:

    image: yjli/simple-http-service:v2

    environment:

    SERVICE_NAME: SimpleHttpService

    ports:

    -"80" 

    使用docker-compose创建并运行:
    [root@sc ~]# docker-compose up      

    通过Consul的WebUI查看:
    08.jpeg

    查看到SimpleHTTPServer已经被Consul自动发现,显示正常服务个数是1个;

    访问 http://192.168.1.107(模拟Nginx对外提供的服务访问地址)可以看到一个 web 页面,显示实际访问的目标地址:
    2016-10-07 09:37:44: <1> requests from to simple-server <172.17.0.5>      
    2016-10-07 09:37:45: <2> requests from to simple-server <172.17.0.5>
    2016-10-07 09:37:46: <3> requests from to simple-server <172.17.0.5>
    ... ...

    结果显示,我连续发送HTTP请求,响应的真实服务只有一个,并正常响应;

    调整SimpleHTTPServer为3个:
    [root@sc ~]# docker-compose scale web=3      

    同样,通过Consul的WebUI查看:
    09.jpeg

    查看到新增的2个SimpleHTTPServer也被Consul自动发现,显示正常服务个数是3个;

    同样,访问 http://192.168.1.107,显示实际访问的目标地址:
    2016-10-07 09:37:51: <1> requests from to simple-server <172.17.0.5>      
    2016-10-07 09:37:51: <1> requests from to simple-server <172.17.0.6>
    2016-10-07 09:37:51: <1> requests from to simple-server <172.17.0.7>
    2016-10-07 09:37:52: <2> requests from to simple-server <172.17.0.5>
    2016-10-07 09:37:52: <2> requests from to simple-server <172.17.0.6>
    2016-10-07 09:37:52: <2> requests from to simple-server <172.17.0.7>
    2016-10-07 09:37:53: <3> requests from to simple-server <172.17.0.5>
    ... ...

    结果显示,笔者连续发送多次HTTP请求,响应的真实服务目前是3个,而且响应策略是轮询方式(取决于Nginx配置策略),并全部正常响应。

    技术扩展

    1. 使用Kubernetes进行Docker的统一管理,再结合Docker+Consul+Nginx架构,形成一套覆盖了高可用、自动发现、自动注册、健康检测以及Docker发布与创建的完整架构体系。
    2. 利用Consul对Docker实例端部署的真实服务进行监控,利用Consul自动发现功能,同时弥补Docker因需求快速创建、发布、终止、停用带来监控上的难点。


    总结

    借助于Consul这么好用的工具,使用Docker更加强大的工具来组装服务是非常有趣和有用的,现在我们就创建一种横向可扩展的框架,并且一切都能高效运行。

    本文对于Docker和Consul Template以及Nginx如何结合使用做了较为详细的介绍。正如你所看到的那样,构建一套高可用及自动发现的Docker服务架构就如此方便、快捷。一旦你配好了Docker跟Consul,其余的组件自然也就变得更加容易了。在实际的场景里我们也会通过Consul让Nginx自己注册自己,这样一来其他的服务便可以轻松地发现到Nginx实例。

    本文作者:李友佳(点融黑帮),就职于点融成都DevOps团队,致力于点融日志系统和监控系统的建设工作,具备中小规模集群的运维、开发、架构规划与设计工作;曾打造过一款智能路由产品,发明专利2项。

    原文链接: 基于Nginx和Consul构建高可用及自动发现的Docker服务架构

      基于Python的开源人脸识别库:离线识别率高达99.38% | 机器之心

      $
      0
      0

      仅用 Python 和命令行就可以实现人脸识别的库开源了。该库使用 dlib 顶尖的深度学习人脸识别技术构建,在户外脸部检测数据库基准(Labeled Faces in the Wild benchmark)上的准确率高达 99.38%。

      该项目是要构建一款免费、开源、实时、离线的网络 app,支持组织者使用人脸识别技术或二维码识别所有受邀人员。

      有了世界上最简单的人脸识别库,使用 Python 或命令行,即可识别和控制人脸。

      该库使用 dlib 顶尖的深度学习人脸识别技术构建,在户外脸部检测数据库基准(Labeled Faces in the Wild benchmark)上的准确率高达 99.38%。

      这也提供了一个简单的 face_recognition 命令行工具,你可以打开命令行中任意图像文件夹,进行人脸识别!

      项目地址: https://github.com/ageitgey/face_recognition#face-recognition

      特征

      找出图片中的人脸

      找出下面图片中所有的人脸:


      importface_recognition
      image = face_recognition.load_image_file("your_file.jpg")
      face_locations = face_recognition.face_locations(image)

      找到并且控制图像中的脸部特征

      找到并勾勒出每个人的眼睛、鼻子、嘴和下巴。


      importface_recognition
      image = face_recognition.load_image_file("your_file.jpg")
      face_landmarks_list = face_recognition.face_landmarks(image)

      找出脸部特征对很多重要的事情都非常有用。但是你也可以用它来做一些「蠢事」,比如数字化妆(美图):


      识别图片中的人脸

      识别每张图片中的人物。


      importface_recognition
      known_image = face_recognition.load_image_file("biden.jpg")
      unknown_image = face_recognition.load_image_file("unknown.jpg")
      
      biden_encoding = face_recognition.face_encodings(known_image)[0]
      unknown_encoding = face_recognition.face_encodings(unknown_image)[0]
      
      results = face_recognition.compare_faces([biden_encoding], unknown_encoding)

      你甚至可以使用该库和其他的 Python 库执行实时人脸识别:


      此处可查看代码示例: https://github.com/ageitgey/face_recognition/blob/master/examples/facerec_from_webcam_faster.py

      安装

      要求:

      使用pin3从pypi安装这一模块:

      pip3 install face_recognition

      重要提示:pip 尝试编译 dlib 依赖时很可能会遇到一些问题。如果遇到问题,前往该地址( https://gist.github.com/ageitgey/629d75c1baac34dfa5ca2a1928a7aeaf)从来源(而不是 pip)中安装 dlib,从而修复该错误。

      手动安装 dlib 后,再次运行 pip3 install face_recognition,完成安装。

      如果安装方面还有问题,你还可以试试预配置的 VM( https://medium.com/@ageitgey/try-deep-learning-in-python-now-with-a-fully-pre-configured-vm-1d97d4c3e9b

      用途

      命令行界面

      安装 face_recognition 时,你会得到一个名为 face_recognition 的简单命令行程序,该程序可用于识别照片或装满照片的文件夹中的人脸。

      首先,你需要提供一个包含图片的文件夹,且每张图片中的每个人你都认识。每个人有一个图像文件,文件名就是图片中人物的名字:


      然后,你需要再建一个文件夹,包含你想要识别的图像文件:


      之后,你仅需要在已知人物文件夹和未知人物文件夹(或单个图像)中运行 face_recognition 命令,该程序会告诉你每个图像中的人物是谁:

      $ face_recognition ./pictures_of_people_i_know/ ./unknown_pictures//unknown_pictures/unknown.jpg,Barack Obama
      /face_recognition_test/unknown_pictures/unknown.jpg,unknown_person

      每张人脸的输出结果只有一行,由文件名和找到的人物名组成,中间用逗号分隔。

       unknown_person 是未与已知人物文件夹中任何照片相匹配的人脸。

      如果你只想知道每张照片中的人物姓名,不在意文件名,那么你可以采用以下做法:

      $ face_recognition ./pictures_of_people_i_know/ ./unknown_pictures/ | cut -d','-f2
      
      Barack Obama
      unknown_person

      如果你的电脑配有多核 CPU,你就可以同时执行多个人脸识别任务。例如,如果你的系统有 4 个 CPU 核,你可以同时使用这 4 个 CPU 核,那么同样时间内处理的图像数量是原来的四倍。

      如果你使用 Python 3.4 或更新的版本,传入--cpus <number_of_cpu_cores_to_use>参数:

      $ face_recognition -cpus4./pictures_of_people_i_know/ ./unknown_pictures/

      你还可以传入--cpus -1,来使用系统中所有的 CPU 核。

      Python 模块

      使用 face_recognition 模块,几行代码轻松控制人脸,so easy!

      API 文件地址 :https://face-recognition.readthedocs.io 

      自动定位图像中人物的脸部特征

      importface_recognition
      
      image = face_recognition.load_image_file("my_picture.jpg")
      face_locations = face_recognition.face_locations(image)
      
      # face_locations is now an array listing the co-ordinatesofeach face!

      图像人脸识别

      importface_recognition
      
      picture_of_me = face_recognition.load_image_file("me.jpg")
      my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
      
      # my_face_encoding now contains a universal'encoding'ofmy facial features that can be compared to any other pictureofa face!
      
      unknown_picture = face_recognition.load_image_file("unknown.jpg")
      unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
      
      # Now we can see the two face encodings areofthe same personwith`compare_faces`!
      
      results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding)ifresults[0] == True:
          print("It's a picture of me!")else:
          print("It's not a picture of me!")

      注意事项

      该人脸识别模型基于成年人照片训练,因此对儿童照片的识别效果不好。该模型默认比较阈值是 0.6,容易混淆儿童的面部。

      将该模型配置到云主机(Heroku、AWS 等)

      face_recognition 赖以存在的 dlib 是用 C++语言写的,因此将该内置该模型的 app 配置到 Heroku 或 AWS 等云主机提供商就很复杂。在该 repo 中有一个 Dockerfile 示例,展示如何在 Docker 容器中运行内置 face_recognition 模型的 app(详见该网址:https://www.docker.com/)。参考该示例,您能够将该模型配置到任何支持 Docker 图像的服务。

      常见问题

      问题:使用 face_recognition 或运行样本时,出现 Illegal instruction (core dumped)。

      解决方案:dlib 需要在 SSE4 或 AVX 支持下编译,但是你的 CPU 太旧,无法支持编译。你需要根据此处( https://github.com/ageitgey/face_recognition/issues/11#issuecomment-287398611)所示修改代码,然后对 dilb 进行重新编译。

      问题:运行摄像头样本时,出现 RuntimeError: Unsupported image type, must be 8bit gray or RGB image.

      解决方案:你的摄像头可能并未在 OpenCV 上正确设置。点击此处( https://github.com/ageitgey/face_recognition/issues/21#issuecomment-287779524)了解更多。

      问题:运行 pip2 install face_recognition 时出现 MemoryError。

      解决方案:face_recognition_models 文件太大,不适合你可用的 pip 缓存内存。试一下 pip2 --no-cache-dir install face_recognition,解决该问题。

      问题:AttributeError: 'module' object has no attribute 'face_recognition_model_v1'

      解决方案:你安装的 dlib 版本过旧,需要 19.4 或者更新的版本。请升级 dlib 版本。

      问题:TypeError: imread() got an unexpected keyword argument 'mode'

      解决方案:你安装的 scipy 版本过旧,需要 0.17 或者更新的版本。请升级 scipy 版本。

      Android dlib人脸识别 dlib-android-app: Android app to demo dlib-android(https://github.com/tzutalin/dlib-android). Use the prebuilt shared-lib built from dlib-android

      $
      0
      0

      dlib-android-app

      Build StatusDownload

      See http://dlib.netfor the main project documentation.

      See dlib-androidfor JNI lib. Refer to dlib-android/jni/jnilib_ex

      Grap the source

      $ git clone https://github.com/tzutalin/dlib-android-app.git

      Features

      • Support HOG detector

      • HOG Face detection

      • Facial Landmark/Expression

      Demo

      Demo video

      Build

      Android app

      • Open Android studio to build

      • Use command line to build (Optional)

      On Windows platforms, type this command:

      $ gradlew.bat assembleDebug

      On Mac OS and Linux platforms, type these commands:

      $ ./gradlew assembleDebug
      
      or
      
      $ make ; make install

      Update shared lib (Optional)

      You can build shared library from dlib-android

      Copy the shared library to ./dlib/src/main/jniLibs/

      Try directly

      Download and install the apk

      $ adb install demo/app-debug.apk

      Otherwise, import the library to your build.gradle

      repositories {
          maven {
              url 'https://dl.bintray.com/tzutalin/maven'
          }
      }
      
      dependencies {
          implementation 'com.tzutalin.dlib-android-app:dlib:1.0.4'
      }

      Sample code

      Facial landmark detection

      FaceDetfaceDet=newFaceDet(Constants.getFaceShapeModelPath());Bitmapbitmap=BitmapFactory.decodeFile("Image Path");List<VisionDetRet>results=faceDet.detect(bitmap);for(finalVisionDetRetret:results) {Stringlabel=ret.getLabel();intrectLeft=ret.getLeft();intrectTop=ret.getTop();intrectRight=ret.getRight();intrectBottom=ret.getBottom();//Get 68 landmark pointsArrayList<Point>landmarks=ret.getFaceLandmarks();for(Pointpoint:landmarks) {intpointX=point.x;intpointY=point.y;
          }
      }

      Pedestrian detection

      PedestrianpedestrianDet=newPedestrianDet();List<VisionDetRet>personList=pedestrianDet.detect(imgPath);

      License

      License

      关于5G 我们都在期待什么?

      $
      0
      0

      华灯初上,绛红色清洁车正打扫街道。他边移动边抱怨,「又剩我一个人加班了,没劲!」大厦前的路灯恰巧听见了这话,宽慰对方,「我也在加班好吗!」安装在灯杆上的圆形摄像头发现远处红绿灯在偷懒,红绿灯立马变换颜色反驳,「没看到吗?我这儿也忙着呢?」

      此时,躲在路边花圃里的水龙头监测到空气湿度不足,自动打开了阀门朝空中洒水,还不忘打趣同事们,「大家都消消火吧!」

      「最新消息,明天下雪!」一旁沉默不语的公告牌突然播起天气。原本各自置气的路灯、红绿灯和摄像头,一下子高兴得欢呼起来。只有清洁车还闷闷不乐,无奈地说:「我讨厌下雪!」

      就像电影《博物馆奇妙夜》里发生的一幕,零点时分,馆内的藏品全都复活,他们聊天、玩耍、恶作剧。而这部由美国高通公司发布的短 视频,打造的是一座智能互连的奇幻大都市。

      金属制清冷色调极具未来感,大都市内,大小设备全部有自己的生命,有喜怒哀乐,有善恶是非。影片开场,右下角却有两行字幕,写着该视频内容是「基于高通基础科技演绎的未来构想,所有设备均源于合理想象」。

      也就是说,支持这些智能设备的基础科技,不止出现在科幻大片中,也存在于如高通一般的科技公司的实验室里,更可能于不久的将来成为你我生活中习以为常的一部分。而这个基础科技正是 5G 通信技术。

      短片以协同合作的道路设备,呈现了智慧城市一角。除此之外,5G 技术还能给智慧城市带来哪些想象?

      5G 时代的智慧城市

      行驶在智慧城市的道路上,智慧运输系统根据天气、拥堵、交通事故等实时路况,为你提供道路信息、相应预警,以及最优出行路线。

      交通智能系统正 24 小时监控车流人流,通过调节红绿灯时长等方式,优化城市路径。顺利抵达目的地后,城市智能停车服务还将为你搜索附近所有合适价位的可用停车空间。

      街道两旁的路灯设有智能点亮系统,可根据节气与时间自适应调正亮度。角落处的垃圾桶则安装了自动检测与分拣程序,能高效分装不同类型的废品,实现优化回收。

      这条街巷你从未来过。你想快速找到要去的楼栋,通过智能眼镜调取地图导航。如果你不赶时间,想慢慢逛逛,了解当地风貌,每个建筑的历史、用材、整体结构,甚至是安全系数都会一并整合,出现在你的眼前。

      忙碌了一整天,你回到家,一边通过液晶显示屏确认冰箱内的食物类别、保质期限、来源,一边查找相应食谱,准备晚餐。此外,你还可以通过远程控制系统,快速让洗衣机自动运转,让烤箱、炉灶灯设备自动清洁。

      等待晚餐出炉的间隙,你收到了一份打折货品信息。这是智慧购物系统根据消费习惯、喜好、体质等信息,为你量身定制的。你想起周末约了好朋友逛街,却临时有事无法赴约。于是,逛街由线下改成线上,你们对着家中的镜子,一起试穿,互相点评。

      你最近易感疲劳,周末预约了医生问诊,通过智慧医疗平台。在这个平台上,你有属于自己的健康档案,包括体温、心跳、血压、血糖等详细信息。如果外出不便,也可以选择远程就医。

      车联网、智能家居、远程医疗...... 作为智慧城市的重要组成部分,其背后是数以万计的设备接入网络,由此形成一张「万物互连」的物联网。

      物联网是智慧城市实现的根基之一。我们该如何实现「万物互连」?通向智慧城市的途径又是什么?

      是 5G 通信技术。

      从 1875 年世界第一台电话诞生,到 1973 年世界第一台手机出现,再到 2007 年第一台智能手机问世,在这段通信业历史中,无论通信技术的发展还是移动终端的进步,都是围绕人与人之间的沟通展开。

      彼时,人与人之间的沟通需求还没得到完全满足,人与物、物与物的连接需求便放在了次要位置。这种状况随着 3G、4G 蜂窝网络完善部署,工业化、自动化、智能化技术的发展,得以改善。

      如今,现有的通信系统已经能够满足人际交流的日常需求,人们对「物」的功能和智能化正在加强,对「物」的依赖也在逐步加深。5G 时代,则意味着人与人之间的沟通,正在向人与物、物与物之间扩展,最终实现「万物互连」。

      5G 与多元的应用场景

      十多年前,手机只能打电话和发短信,之后才支持上网。如果网速可以达到 30~40kbit/s 左右,说明你的手机已经很先进。

      如今,智能终端用户普及、移动互联网快速发展,促使人们生活和信息交互方式的巨大变化。在 5G 智能设备上,增强现实(AR)、虚拟现实(VR)、超高清视频、移动云等新型业务将得以发展,使人们的工作与休闲更加数字化、信息化。

      人们如同侵入数据之海,无限畅游,随时随地汲取需要的数据信息。

      可是,侵入数据之海的完美体验,通过现有的 4G 技术已经无法满足。5G 凭借其低延时、高速率、高容量的特点,能够满足人们对网络连接超大流量、超多设备连接数、超高移动性的需求。

               高通合作伙伴最新发布搭载骁龙移动平台的智能终端

      5G 通信技术有四个典型的应用场景:

      1. 连续广域覆盖场景,这是移动通信最基本的覆盖方式,以保证用户的移动性和业务连续性为目标。该场景主要的挑战在于,随时随地(包括小区边缘、高速移动、偏远地区等恶劣环境),为用户提供无缝的高速业务体验

      2. 热点高容量场景,主要面向局部热点区域,为用户提供极高的传输速率与流量密度需求,比如在广场、运动馆、音乐厅等人流密集之地。

      3. 低功耗大连接场景,主要面向智慧城市、环境检测、智能农业、森林防火等,以传感和数据采集为目标的应用场景,具有小数据包、低功耗、海量连接等特点。

      4. 低时延高可靠场景,主要面向车联网、工业控制等垂直行业的特殊应用需求。这类应用对延时和可靠性具有极高的指标要求。

      低时延、高可靠的典型场景之一是无人驾驶。设想下,一辆汽车正快速向你驶来,而你的车正在自动驾驶中。如果车载智能系统不能及时接收到来车提醒,就有可能发生交通事故。

      自动驾驶的大部分应用场景如紧急刹车,都需要瞬间进行大量的数据处理,并及时做出决策。这需要网络同时具有大带宽、超高连接数、高可靠性和高精度定位等能力,唯有 5G 能同时满足这些条件。

      实际上,高通早在 2017 年便推出了突破性的 C-V2X 车联网解决方案,其拓展了该领域的通信范围、增强了可靠性,保障了自动驾驶对安全的高要求。为了满足汽车制造企业采用 C-V2X 解决方案,实现道路安全的量产需求,该芯片组预计将于 2018 下半年 商用出样。

      此外,连续广域覆盖和热点高容量场景并非 5G 独有。在 3G、4G 的系统设计内,均有对这两个场景的描述。对普通用户而言,三者的差异可能直观体现在网速上。只是,不光是速率,在时延、连接数量、移动性等方面,5G 相比于 3G、4G 都有极大提升。

      HtcVive.jpeg

      VR

      以近年来流行的 VR 游戏为例。对于现有的通讯技术而言,由于网络带宽小、速率低和高延时等因素,用户体验时可能出现画面卡顿,甚至头晕目眩的感觉。

      在 2018 年世界移动通信大会上,针对 4G、5G 在速率、延时等方面的性能,高通做了一系列模拟实验。其中一组实验,用户浏览速度从 4G 的 71 Mbps 上升至 5G 的 1.4 Gbps,响应速度大约快了 23 倍。用户在线观看 8K 120fps 10bit 的视频也毫无压力。

      5G 本身具备低至 1ms 的网络时延、高达 10Gbps 的峰值下载速率和满足 1000 亿量级的网络连接三大特性,足以实现完美的虚拟现实体验。

      除了 VR,5G 的这些特性还与工业设备、医疗仪器、交通工具等深度融合,有效满足工业、医疗、交通等垂直行业的信息化服务需求。

      那么,我们距离 5G 时代还有多远?

      5G 大门的关键密钥

      本月中旬,在中国电信与高通举办的天翼智能生态博览会上,现场观众与「5G 手机」有过一次近距离接触。

      那是高通展示的用于 5G 测试的移动平台。它就像一台大号的普通智能手机。让人们惊讶的是,它却能够实现每秒数千兆比特的下载速度,并将很快应用到今年下半年的 5G 试验室和外场测试中,为商用铺平道路。

      你可能会对毫米波感到陌生,它也是加速 5G 普及的关键密钥之一。目前这项技术也取得了重大进展。

      毫米波是一种频谱。我们平时使用的无线通信,包括移动、卫星等都被国际电信联盟划分在指定频段工作。从这个角度看,频谱是一种有限资源。国际上,2G/3G/4G 移动通信系统普遍采用 6GHz 以下中低频段,如今这一段位的频谱已相当稀缺。

      5G 与 4G 的不同点是,前者可考虑使用更高的频段——毫米波。毫米波的技术挑战在于,使用该频段传输容易造成路径受阻、信号衰减的情况。如何将毫米波天线安置在手机上?如何利用这一技术增强信号覆盖?这些都是实现 5G 商用亟待解决的问题。

               Qualcomm QTM052 天线模组

      今年 7 月,高通推出了全球首款面向智能手机及其他移动终端的毫米波 5G 天线——QTM052。

      这款天线的模块设计小巧,仅一美分硬币尺寸,可允许在一部手机中安装四套 QTM052 模组,缓解了 5G 设备天线板块的设计难题。此外,通过与骁龙 X50 5G 调制解调器配合,这套模组还可以实现更为优秀的信号覆盖范围。

      可以把调制解调器看作是一个「翻译员」,功能是实现通信链路中数字与模拟信号的转换。骁龙 X50 是全球首款 5G 调制解调器。这款产品既支持 6GHz,也支持毫米波,利用毫米波的大带宽,结合先进的信号处理技术,能够实现每秒五千兆比特的下载速度。

      全球首次 5G 数据连接也是高通实现的。2017 年 10 月,在香港召开的高通 4G/5G 峰会上,高通公布了这一消息,还一并推出了第一款 5G 手机参考设计,意味着骁龙 X50 这款 5G 调制解调器成功「出道」,也意味着从运营商到手机厂商,整个产业链都能利用这颗芯片,加速 5G 的产品设计。

      这更意味着你距离 5G 又近了一大步。

      5G,未来可期

      高通首席执行官史蒂夫·莫伦科夫曾说:「移动通信之路,并非一蹴而就,只有在 3G/4G 时代深厚积累,才能推动 5G 到来。」

      为了加速 5G 普及,除了加大投入,加强自身产品与技术的研发,行业内外的协同合作也必不可少。

      今年一月,高通联合联想、OPPO、小米、vivo 等国产手机企业共同发布了 5G 领航计划,加强了与中国手机企业间的合作,力图在 2019 年实现 5G 智能手机的商用。实际上,基于骁龙 X50 5G 调制解调器,三个 yi』han 相继实现了 5G 通信连接。9 月 27 号联想创新科技大会上,联想 CEO 杨元庆甚至率先发布了一款 5G 手机。

      在我国,5G 技术研发试验一直由工信部指导、IMT-2020(5G)推进组负责实施。当前进度已经走到了 5G 第三阶段测试,这是 5G 技术研发测试的最后一环,将为 2019 年展开 5G 规模试验及预商用做好准备。

      第三阶段的测试在近期有了重大进展。今年 9 月 28 日,IMT-2020(5G) 推进组发布了中国 5G 技术研发试验的第三阶段成果。高通与中兴和大唐(信科)成功的进行了新空口的互操作测试。在之前国际与国内的测试中,高通完成了与包括中兴、 华为、爱立信、诺基亚、大唐、三星等所有网络设备厂家的互操作测试。高通也将其终端产品商用的时间点预定在 2019 年。

      5G 真的来了。

      古登堡 1440 年左右发明了印刷机。此前,书籍必须依靠人力复制,生产低效。使用印刷机,书籍实现了量产。

      蒸汽机出现前,大型工厂必须建造在河流附近,依靠水能投产。蒸汽机打破了这种地域依赖。

      电力则更加向前,允许机器实现集成能源供应,支持更高效的机器配置,重新定义了行业与竞争格局。

      印刷机、蒸汽机、电力...... 它们都属于通用技术。这类技术的共性是,普遍用于多个行业,能带来长期且持续的改进,催生多种创新。

      5G 技术也是通用技术。毫米波、调制解调器...... 这些看似离你很遥远的发明创造,却是实现 5G 技术实现不可或缺的部分。这些创新,都是一代代如高通这般的通信公司、通信人不断努力的成果。

      正如高通坚持践行的一句话:「我们发明的基础科技,让世界智能互连」。

      查看评论

      阿里资深技术专家行易:我所理解的工程师文化

      $
      0
      0

      对于什么是工程师文化,很多人都能说出很多关键词,比如:匠心,创新,专业,担当,开放……


      然而如果要用一句话来描述什么是工程师文化,如何判断一家公司是不是一家工程师文化的公司,却不是一个很容易的问题。


      个人认为要回答这个问题,首先需要从了解工程师是一群什么样的人着手。


      工程师是什么样的一种人?


      工程师其实就是解决问题的人,他们发现问题,找出规律,使用适当的工具和手段来解决问题。


      一个优秀的工程师,需要有敏锐的洞察力,深厚的知识以及丰富的想象力。缺乏洞察力,则无法找到根本的问题,只能头疼医头,脚疼医脚。缺乏知识,面对问题只能束手无策。缺乏想象力,则找不到最好的解决办法。


      成为优秀工程师的核心要素是什么?


      优秀的工程师需要具备好奇心和理性思维两个核心要素。好奇心是驱使我们探索未知的原动力,缺乏好奇心,就失去了探索和求知的欲望,也无法培养丰富的想象力。而理性思维,则是把握事物本质和规律的能力活动,是工程师能够脚踏实地解决问题的根本。


      理性思维是什么?


      理性思维包括很多方面,个人认为理性的核心是两点:

      • 相信客观规律

      • 逻辑思维


      这里说的相信客观规律只是说相信世界有其规律。因为相信客观规律,所以我们需要观察世界才可能发现规律,同时因为客观世界的复杂性,作为观察者永远只知道部分真相,也只能无限接近而永远也无法得到真理,这也就是老子说的“道可道,非常道”。


      而逻辑思维则是我们透过纷繁复杂的现象,分析和理解抽象规律的方法和手段。通过运用抽象与概括、分析与综合,归纳与演绎,对比,找出因果关系等,我们可以梳理杂乱的信息,抽丝拨茧,深刻而冷静地洞察问题本质。


      为什么要理性思维?


      对于事物有更深刻的认识,能够提高决策的准确性和有效性,同时也更有可能积累可以复现的经验,也就是知识。由于对事物的观察不够深入,人们非很容易把表面的相关性当成规律,比如很多伟大的公司都建有宏伟的总部大楼,会飞的鸟儿都有羽毛,但是我们不能说要成为伟大的公司就应该建设宏伟的总部大楼,而要想飞就一定要给自己粘上羽毛。只有真正认识到逻辑性的人,才有可能洞察其中的真正的规律,找出有价值的理论,比如按照流体力学的理论来设计飞机,而非粘上羽毛。


      整个人类的技术发展史其实就是一部理性思维的历史,如果不是历史上的人们用理性思维来观察和了解这个世界,那么今天我们还是非洲草原上的一群猴子。


      对于个人而言,理性地看待自己的人生,有助于认识自己,获得内心的平静,做真正想要的自己,简单来说就是活得比较明白。只有这样,才更有可能在自己真正感兴趣的事情上做出成绩,获得成就感。


      理性思维不等于自动掌握了一切知识,恰恰相反,运用理性思维的人会认识到自身的不足,知道自己的知识仅仅是对世界非常小的一部分的抽象总结,因此也愿意以更加开放的心态来了解世界上的事物。


      是不是有了理性思维就不要感性思维了呢?当然不是这样,人都有感性和理性的两面,感性和理性,可以在这件事情上感性,另外一件事情上理性,或者一会儿感性一会儿理性,取决于到底想要什么。当然能做到快速切换思维模式是很难的,能够做到的人是幸福的,对于普通人来说,他通常会形成思维习惯,并且体现在生活和工作的方方面面。


      理性思维不是什么?


      理性思维不是价值观,但是理性思维有助于梳理价值观。


      理性思维不是万能的,比如碰到经典的掉到水里先救谁的问题的时候,一定不能用理性思维来回答此类问题,不过可以理性地识别这个问题是感性问题,需要切换到感性模式。


      理性思维不等于能力,能力还是需要靠学习和训练才能达到,但是理性思维可能有助于学习的效率。


      理性思维不是辩论手法,不是玩文字游戏,而是一个人思考的过程。


      感性思维是什么?


      感性思维就是凭感觉、本能或者直觉行事,其实人类的直觉是千万年来演化出来的生存能力,比如在大草原上碰到猛兽时,先反应过来开跑的肯定会有更高的生存指数,因而也就留存在我们的基因之中。然而今天我们面对的问题早已不是大草原上的猛兽,当我们面对一个复杂的问题的时候,还是依赖感觉来行事可能会碰到比较大的问题。


      中国有很多成语,比如刻舟求剑、拔苗助长、守株待兔等等,都是感性思维失败的典型案例。


      那是不是说感性思维就没有价值了呢?当然也不是这样的,我们还是有很多场合需要做出快速响应,这个时候我们的感觉就非常重要,比如开车时遇到紧急情况,系统崩溃时快速处理。但是这些感觉都需要在不紧急的时候的理性思考,并且通过刻意训练固化到我们的大脑,成为我们的直觉。


      那到底什么是工程师文化?


      在写这篇帖子之前,我看到了某神贴上一句话,比较有感触:”土壤能种什么不在种子,在土壤……“。虽然不完全赞同这位同学的观点,但是好的土壤和环境确实也是一颗种子长成参天大树的必要条件。


      回到本文的主题上来,团队工程师文化不仅仅是工程师的事情,也是整个组织的事情,在我看来工程师文化就是鼓励好奇心和用理性思维来主导决策的文化。


      如果我们只是要求工程师具备理性思维,而领导者和其他合作方都是感性主导决策,是不可能培养出工程师文化出来的。


      为什么要鼓励好奇心?


      鼓励好奇心意味着鼓励知识共享,鼓励思想的自由交流,包容不同的想法,允许开放的争议。

      当然,鼓励好奇心主要在于思想层面,而不是行为层面,一个团队或者组织可以允许自由的思想,却很难允许完全自由的行为。


      为什么要用理性思维来主导决策?


      首先用理性思维来主导决策并不意味着每个决策都有充分严密的论证,而是决策的因素主要来自于理性的考量,而非感性的认知。


      理性思维往往意味着找到深层次的系统性问题,产生更好的经验总结,好的经验总结意味着成功可以复制,一个团队或者组织需要大量的经验积累和传播。虽然说失败是成功之母,但如果没有理性思维的分析和总结得到改进的方法,重复失败一万次也很难成功。即使偶然成功,往往知其然不知其所以然,导致无法大量复制成功的经验,抑或即使知道所以然,也因为缺乏逻辑性的梳理而很难正确的传播,导致学习者反而因此受累。


      最后理性主导也不等于感性在决策过程中需要被摒弃,因为:


      • 感性来自于人类千百万年的进化,在信息不充分的时候有其重要的价值

      • 决策往往涉及到人,而人都有感性的一面,而且很多人还是感性思维为主


      但是以上两个原因不应该成为凭感觉做决定的充分理由,而是在理性分析问题的时候,需要考虑感性的因素,以及在基于理性思维无法做出判断时,而又必须及时做出决定时可以采取感性思维来做出判断。


      如果不是理性思维主导决策的话,一方面很多人会逐渐形成感性思维的习惯,而习惯的力量是非常强大的,一旦形成会反过来影响和强化整个组织的思维习惯。另外一方面则是两种思维习惯的人很容易产生误解和沟通障碍,从而降低团队的协调效率,甚至导致团队方向感不明确,疲于奔命。


      感性文化主导的公司能否成长出优秀的工程师呢?


      答案当然是可能的,首先在激烈的竞争中生存的组织都有理性的一面,其次大环境和小环境不一定完全一致,最后优秀的人较强的自驱力和自控力,能够自我成长。但是在这种条件下,普通的工程师因为缺乏理性思维的习惯,会难以成长为优秀的工程师,而优秀的工程师也可能因为缺乏成就感而选择离开,总的来说优秀的工程师出现的几率会比较少。


      其实这也是一个组织选择成员的方式,如果一个组织需要更多优秀的工程师,则需要提供适合工程师的文化氛围,如果不需要,则当然也不需要提供相应的文化氛围。


      总结


      工程师文化并不能完全决定一个组织的成败,但是能避免不必要的错误决策,并且能将局部成功的知识应用到更大的范围获得更大的回报,这是一个组织取得长期成功的一个重要因素。


      问与答


      问:前面说的工程师文化的关键词和本文有什么关系?

      答:匠心和专业意味着精益求精,担当意味着责任,这些都是人们对卓越成就的追求,这是人生需求在经过理性思维之后的升华,因为追求卓越才能获得最大的成就感。

      创新是理性思维的结果,而非原因。
      关于开放,当我们知道自己不知道时,就已经拥有了开放的心态。


      问:理性思维是否让我们变得复杂?

      答:不是理性思维让我们变得复杂,而是这个世界本来就是复杂的,理性思维只是让我们更加客观地理解这个世界的复杂性。


      问:为什么在这儿讨论哲学问题,能帮我解决具体问题吗?

      答:一张地图并不能让你环游世界,但是有地图会更方便。


      问:关于如何做个优秀的工程师,有没有一句话总结?

      答:保持好奇心,多问为什么,理性思考。


      问:做到这几条就是优秀工程师吗?

      答:知道珠穆朗玛在哪儿不等于已经上了8848米,路还是要自己走的。


      问:拥有工程师文化的公司一定会成功吗?

      答:不一定,但是个人认为拥有工程师文化的公司会提高公司成功的概率。


      问:你好像也没有把问题讲透嘛?

      答:如果你因此而思考,吾愿足矣。



      有道云笔记是如何使用TensorFlow Lite的?

      $
      0
      0

      近年来,有道技术团队在移动端实时 AI 能力的研究上,做了很多探索及应用的工作。2017 年 11 月 Google 发布 TensorFlow Lite (TFLlite) 后,有道技术团队第一时间跟进 TFLite 框架,并很快将其用在了有道云笔记产品中。 本文将介绍我们是如何将 TFLite 运用在有道云笔记中的文档识别工作中的,以及 Tflite 都有些什么特性。

      文档识别工作的介绍

      1. 文档识别的定义 文档识别最初是开发有道云笔记的文档扫描功能时面对的一个问题。文档扫描功能希望能在用户拍摄的照片中,识别出文档所在的区域,进行拉伸 (比例还原),识别出其中的文字,最终得到一张干净的图片或是一篇带有格式的文字版笔记。实现这个功能需要以下这些步骤:

      1. 识别文档区域: 将文档从背景中找出来,确定文档的四个角;
      2. 拉伸文档区域,还原宽高比: 根据文档四个角的坐标,根据透视原理,计算出文档原始宽高比,并将文档区域拉伸还原成矩形;
      3. 色彩增强: 根据文档的类型,选择不同的色彩增强方法,将文档图片的色彩变得干净清洁;
      4. 布局识别: 理解文档图片的布局,找出文档的文字部分;
      5. OCR: 将图片形式的“文字”识别成可编码的文字;
      6. 生成笔记: 根据文档图片的布局,从 OCR 的结果中生成带有格式的笔记。

      pic1

      文档识别就是文档扫描功能的第一步,也是场景最复杂的一个部分

      2. 文档识别在有道 AI 技术矩阵中的角色

      有道近年来基于深度神经网络算法,在自然语言、图像、语音等媒体数据的处理和理解方面做了一系列工作,产出了基于神经网络的多语言翻译、OCR(光学字符识别)、语音识别等技术。在这些技术的合力之下,我们的产品有能力让用户以他们最自然最舒服的方式去记录内容,用技术去理解这些内容,并将其统一转化为文本以待下一步处理。从这个角度来看,我们的各种技术组成了以自然语言为中心,多种媒体形式相互转换的网络结构。

      文档识别是从图像转化为文本的这条转换链上,不起眼却又不可缺少的一环。有了它的存在,我们可以在茫茫图海中,准确找到需要处理的文档,并将其抽取出来进行处理。

      3. 文档识别的算法简介

      我们的文档识别算法基于 FCNN (Fully Convolutional Neural Network) ,这是一种特别的 CNN(卷积神经网络),其特点是对于输入图片的每一个像素点,都对应着一个输出(相对的,普通的 CNN 网络则是每一张输入图片对应着一个输出)。因此,我们可以标记一批包含文档的图片,将图片中文档边缘附近的像素标注为正样本,其他部分标注为副样本。训练时,以图片作为 FCNN 的输入,将输出值与标注值作对比得到训练惩罚,从而进行训练。关于文档识别算法的更多细节,可以参见有道技术团队的《 文档扫描:深度神经网络在移动端的实践》这篇文章。

      由于算法的主体是 CNN,因此文档扫描算法中主要用到的算子(Operator)包括卷积层、Depthwise 卷积层、全连接层、池化层、Relu 层这些 CNN 中常用的算子。

      4. 文档识别与 TensorFlow

      能够训练和部署 CNN 模型的框架非常多。我们选择使用 TensorFlow 框架,是基于以下几方面的考虑的:

      1. TensorFlow 提供的算子全面且数量众多,自己创建新的算子也并不麻烦。在算法研发的初期会需要尝试各种不同的模型网络结构,用到各种奇奇怪怪的算子。此时一个提供全面算子的框架能够节省大量的精力;
      2. TensorFlow 能够较好的覆盖服务器端、Android 端、iOS 端等多个平台,并在各个平台上都有完整的算子支持;
      3. TensorFlow 是一个比较主流的选择,这意味着当遇到困难时,更容易在互联网上找到现成的解决办法。

      5. 为什么想在文档识别中用 TFLite

      在 TFLite 发布之前,有道云笔记中的文档识别功能是基于移动端 TensorFlow 库 (TensorFlow Mobile) 的。当 TFLite 发布后,我们希望迁移到 TFLite 上。促使我们迁移的主要动力是链接库的体积。

      经过压缩后,Android 上的 TensorFlow 动态库的体积大约是 4.5M 左右。如果希望满足 Android 平台下的多种处理器架构,可能需要打包 4 个左右的动态库,加起来体积达到 18M 左右;而 tflite 库的体积在 600K 左右,即便是打包 4 个平台下的链接库,也只需要占用 2.5M 左右的体积。这在寸土寸金的移动 App 上,价值是很大的。

      TFLite 的介绍

      1. TFLite 是什么 TFLite 是 Google I/O 2017 推出的面向移动端和嵌入式的神经网络计算框架,于2017年11月5日发布开发者预览版本 (developer preview)。相比与 TensorFlow,它有着这样一些优势:

      • 轻量级。如上所述,通过 TFLite 生成的链接库体积很小;
      • 没有太多依赖。TensorFlow Mobile 的编译依赖于 protobuf 等库,而 tflite 则不需要大的依赖库;
      • 可以用上移动端硬件加速。TFLite 可以通过 Android Neural Networks API (NNAPI) 进行硬件加速,只要加速芯片支持 NNAPI,就能够为 TFLite 加速。不过目前在大多数 Android 手机上,Tflite 还是运行在 CPU 上的。

      2. TFLite 的代码结构

      作为 TFLite 的使用者,我们也探索了一下 TFLite 的代码结构,这里分享一下。

      目前,TFLite 的代码位于 TensorFlow 工程中 “tensorflow/contrib/lite” 文件夹下。文件夹下有若干头/源文件和一些子文件夹。

      其中,一些比较重要的头文件有:

      • model.h: 和模型文件相关的一些类和方法。其中 FlatBufferModel 这个类是用来读取并存储模型内容的,InterpreterBuilder 则可以解析模型内容;
      • Interpreter.h: 提供了用以推断的类 Interpreter,这是我们最常打交道的类;
      • context.h: 提供了存储 Tensors 和一些状态的 struct TfLiteContext。实际使用时一般会被包装在 Interpreter 中;

      此外,有一些比较重要的子文件夹:

      • kernels: 算子就是在这里被定义和实现的。其中 regester.cc 文件定义了哪些算子被支持,这个是可以自定义的。
      • downloads: 一些第三方的库,主要包括:
      • abseil: Google 对 c++ 标准库的扩展;
      • eigen: 一个矩阵运算库;
      • farmhash: 做 hash 的库;
      • flatbuffers: TFLite 所使用的 FlatBuffers 模型格式的库;
      • gemmlowp: Google 开源的一个低精度矩阵运算库;
      • neon_2_sse: 把 arm 上的 neon 指令映射到相对应的 sse 指令。
      • java: 主要是 Android 平台相关的一些代码;
      • nnapi: 提供了 nnapi 的调用接口。如果想自己实现 nnapi 可以看一看;
      • schema: TFLite 所使用的 FlatBuffers 模型格式的具体定义;
      • toco: protobuf 模型转换到 FlatBuffers 模型格式的相关代码。

      我们是怎么用TFLite的?

      1. TFLite 的编译

      TFLite 可以运行在 Android 和 iOS 上,官方给出了不同的编译流程。

      在 Android 上,我们可以使用 bazel 构建工具进行编译。bazel 工具的安装和配置就不再赘述了,有过TensorFlow 编译经验的同学应该都熟悉。依照官方文档,bazel 编译的 target 是 “//tensorflow/contrib/lite/java/demo/app/src/main:TfLiteCameraDemo”,这样得到的是一个 demo app。如果只想编译库文件,可以编译 “//tensorflow/contrib/lite/java:tensorflowlite” 这个 target,得到的是 libtensorflowlite_jni.so 库和相应的 java 层接口。

      更多细节见官方文档:

      https://github.com/tensorflow/tensorflow/blob/master/tensorflow/docs_src/mobile/tflite/demo_android.md

      在 iOS 上,则需要使用 Makefile 编译。在 mac 平台上运行 build_ios_universal_lib.sh,会编译生成 tensorflow/contrib/lite/gen/lib/libtensorflow-lite.a 这个库文件。这是个 fat library,打包了 x86_64, i386, armv7, armv7s, arm64 这些平台上的库。

      更多细节见官方文档:

      https://github.com/tensorflow/tensorflow/blob/master/tensorflow/docs_src/mobile/tflite/demo_ios.md

      两个平台上 TFLite 库的调用接口也有所不同:Android 上提供了 Java 层的调用接口,而 iOS 上则是 c++ 层的调用接口。

      当然,TFLite 的工程结构是比较简单的,如果你熟悉了 TFLite 的结构,也可以用自己熟悉的编译工具来编译 TFLite。

      2. 模型转换

      TFLite 不再使用旧的 protobuf 格式(可能是为了减少依赖库),而是改用 FlatBuffers 。因此需要把训练好的 protobuf 模型文件转换成 FlatBuffers 格式。

      TensorFlow 官方给出了模型转化的指导。首先,由于 TFLite 支持的算子比较少,更不支持训练相关的算子,因此需要提前把不需要的算子从模型中移除,即 Freeze Graph ;接着就可以做模型格式转换了,使用的工具是 tensorflow toco。这两个工具也是通过 bazel 编译得到。

      更多细节见官方文档:

      https://github.com/tensorflow/tensorflow/blob/master/tensorflow/docs_src/mobile/tflite/devguide.md

      3. 缺失的算子

      TFLite 目前仅提供有限的算子,主要以 CNN 中使用到的算子为主,如卷积、池化等。我们的模型是全卷积神经网络,大部分算子 TFLite 都有提供,但 conv2d_transpose(反向卷积)算子并没有被提供。幸运的该算子出现在网络模型的末端,因此我们可以将反向卷积之前的计算结果取出,自己用 c++ 实现一个反向卷积,从而计算出最终的结果。由于反向卷积的运算量并不大,所以基本没有影响到运行速度。

      如果不巧,你的模型需要但 TFLite 缺少的算子并非出现在网络的末端,该怎么办呢?你可以自定义一个 TFLite 算子,将其注册在 TFLite 的 kernels 列表中,这样编译得到的 TFLite 库就可以处理该算子了。同时,在模型转换时,还需要加上 –allow_custom_ops 选项,将 TFLite 默认不支持的算子也保留在模型中。

      更多细节见官方文档:

      https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/g3doc/custom_operators.md

      TFLite 优缺点

      优点:在 库的大小、开发方便程度、跨平台性、性能之间达成一个平衡 作为对比,有道技术团队选取了一些其他的移动端深度学习框架,分别分析其在“开发方便程度、跨平台性、库的大小、性能”四个方面的表现:

      • TensorFlow Mobile,由于和 server 上的 TensorFlow 是同一套代码,所以可以直接使用 server 上训练得到的模型,开发非常方便;能支持 Android, iOS, 跨平台性没问题;如前所述,库的大小比较大;性能主流。
      • caffe2,可以比较方便的从 caffe 训练出的模型转换到 caffe2 ,但缺少一些算子, 开发方便程度一般;能支持 Android, iOS,跨平台性没问题;库编译出来比较大,但是是静态库可以压缩;性能主流。
      • Mental/Accelerate,这两个都是 iOS 上的框架。比较底层,需要模型转换&自己写 inference 代码,开发比较痛苦;仅支持 iOS;库是系统自带,不涉及库大小问题;速度很快。
      • CoreML,这个是 WWDC17 发布的 iOS 11 上的框架。有一些模型转换工具,只涉及通用算子时开发不算痛苦,涉及自定义算子时就很难办了;仅支持 iOS 11 以上;库是系统自带,不涉及库大小问题;速度很快。

      最后是 TFLite:

      • TFLite,其模型可以由 TensorFlow 训练得到的模型转换而来,但缺少一些算子, 开发方便程度一般;能支持 Android, iOS,跨平台性没问题;库编译出来很小;就我们的实验来看,速度比TensorFlow 快一点。

      可以看到,TensorFlow Mobile 开发方便,通用性好,但链接库大,性能主流(其他 server 端神经网络框架的 mobile 版也都有类似的特点);Mental/Accelerate 这些比较底层的库速度很快,但不能跨平台,开发比较痛苦;caffe2、TFLite 这类有为移动端优化过的神经网络框架则比较平衡,虽然初时会有算子不全的问题,但只要背后的团队不断支持推进框架的开发,这个问题未来会得到解决。

      优点:相对容易扩展

      由于 TFLite 的代码(相对于 TensorFlow)比较简单,结构比较容易理清,所以可以相对容易的去扩展。如果你想增加一个 TFLite 上没有而 TensorFlow 上有的算子,你可以增加一个自定义的类;如果你想增加一个 TensorFlow 上也没有的算子,你也可以直接去修改 FlatBuffers 模型文件。

      缺点:ops 不够全面

      如前所述,TFLite 目前主要支持 CNN 相关的算子 ,对其他网络中的算子还没有很好的支持。因此,如果你想迁移 rnn 模型到移动端,TFLite 目前是不 OK 的。

      不过根据最新的 Google TensorFlow 开发者峰会,Google 和 TensorFlow 社区正在努力增加 ops 的覆盖面,相信随着更多开发者的相似需求, 更多的模型会被很好的支持。这也是我们选择 TensorFlow 这样的主流社区的原因之一。

      缺点:目前还不能支持各种运算芯片

      虽然 TFLite 基于 NNAPI,理论上是可以利用上各种运算芯片的,但目前还没有很多运算芯片支持 NNAPI。期待未来 TFLite 能够支持更多的运算芯片,毕竟在 CPU 上优化神经网络运行速度是有上限的,用上定制芯片才是新世界的大门。

       总结

      这一两年来,在移动端实现实时的人工智能似乎已经形成了一波潮流。有道技术团队在移动端 AI 算法的研究上,也做了诸多尝试,推出了离线神经网络翻译 (离线 NMT) 、离线文字识别 (离线 OCR) 以及离线文档扫描等移动端实时 AI 能力,并在有道词典、有道翻译官、有道云笔记中进行产品化应用。由于目前移动端 AI 尚处在蓬勃发展阶段,各种框架、计算平台等都尚不完善。 
       

      腾讯的产业长征

      $
      0
      0

      腾讯的2B问题,最近引发了无数讨论。

      把这些讨论总结一下,无外乎就是三个问题:B端市场的产业升级+技术革命,腾讯到底还要不要参与?怎么参与?腾讯还有没有机会?

      刚刚,腾讯时隔六年进行了重大战略升级与公司架构调整,调整后的腾讯,保留了原有的企业发展事业群(CDG)、互动娱乐事业群(IEG)、技术工程事业群(TEG)、微信事业群(WXG);新成立了云与智慧产业事业群(CSIG)和平台与内容事业群(PCG)。

      这当然又是国庆长假的另一大热点,但这里我们已经可以得出结论:云与智慧产业事业群(CSIG)的成立,意味着腾讯自己给出了答案:“产业+智能”这部戏,腾讯不仅要参演,还要从战略和组织架构的层面,调集人马重磅入局。

      并且腾讯已经明确提出,腾讯下一个十年关键在于拥抱产业互联网。

      焦虑也好,嗅到了诱人的气味也罢,显然腾讯对产业互联网已经下了不小的决心。要知道上一次腾讯架构调整,可是明确指向移动互联网这个改变了每一个中国人的历史性机遇。

      架构调整背后,腾讯到底是如何思考的?又是如何行动的?

      我们从腾讯这次“产业长征”的初衷开始说起。

      战场的味道:产业互联网的觉醒与饥饿

      对科技略微敏感的朋友,都不难发现这个秋天弥漫着一股“技术骚动”。百度的升级,阿里与华为各自的大会,乃至于腾讯的架构调整,都指向着一个词:产业。

      为什么大家都突然开始关注日常有点冷的B端市场?

      最基本的市场逻辑,是技术工具成熟度已经临近奇点。信息时代之后,差不多每一场商业革命,都是以技术工具迭代为起点的。家用互联网、个人互联网,无不如是。

      而产业市场的互联网指数、数字指数始终不足以被称为革命,核心原因在于技术工具的迭代没有满足经济体的需要。

      而今天,以AI为核心,带来的IoT、机器人、人机耦合、自主决策能力,正在深刻撬动产业世界的内在创造力,而不仅仅是完成外在连接的基础部分。

      说白了,智能技术的进化,正在开始让企业难以拒绝。我们采访过一家广东的制造业工厂,给产品线和产房加入不算复杂的AI技术之后,基于外部机器视觉检测和AI故障识别,良品率提高、生产效率提升,而原料成本却通过智能配比下降了。

      当一家企业可以利用机器视觉技术提高质检效率、利用设备互联网提高生产效率、利用OCR识别极大提升财会与合同处理能力。几个变量相乘,企业内部规则本身将发生深刻的变化。企业的最大效能是多少?必要劳动力投入是多少?维护成本是多少?这些变量都要重新洗牌。

      而千千万万个这种企业相加,产业互联网的需求也就觉醒了。伴随而来的,是潜藏于B端市场底层,开始涌动的服务饥饿。

      这个市场可能是新的PC,也可能是新的手机,但更可能就是产业互联网本身。一个由中国密密麻麻的企业、工厂、零售门店、公共服务设施、政府组成的新蓝海。

      很难有科技企业能忍耐住这种刺激,腾讯也不例外。

      有了动机,才有行动方案。

      先锋营:云计算正在变成企业的“标准岗位”

      一个大蛋糕耸立在那里,腾讯也跟其他互联网公司一样打算来一叉子。那么第一件事要干什么呢?答案是加码云计算。

      在企业服务市场经历过若干次迭代的今天。云计算已经被证明为包容性最强、体系化最好的产业服务模态。

      从企业成本上看,通过云计算调用新技术应用的换代成本最小,兼容压力最低。

      而从技术覆盖率上看,今天在所有信息与数字化服务中,云计算是今天距离企业最近的一种。

      在技术端看,云计算是能囊括数据服务、连接服务、通信服务,以及AI驱动的智能产业进化的最好平台。无论是人工智能,还是5G、区块链、量子计算,都可以以云平台作为兼容载体。

      总而言之,云计算今天正在变成企业、社会机构与政府的“标准岗位”。你上云了吗?那好,后续的AI生态以及若干服务都可以触摸到你——这是腾讯熟稔的产品逻辑。

      三方面相加,产业智能服务的先锋营一定是云计算。这从阿里、华为的最新布局都可见一斑。而腾讯的战略调整,并没有选择另搞一套产业互联网方案,而是承认了云计算的基础地位,并确定了腾讯云的先锋营身份。

      以腾讯云为轴心搭建CSIG,说明腾讯希望执行更接地气的、快速见效的产业互联网战略。而同时给CSIG搭配更多技术体系与产业服务能力的构建,也透露了其接下来的战略重心:产业AI。

      交火点:产业AI爆发,与T字头的重火力

      毋庸讳言,今天产业经济体的科技服务,之所以可能会迎来爆发式增长,核心就在于人工智能的加入,让大家看到了产业经济体的多种核心问题,可能被解决和优化。

      人工智能让经济体的技术需求被不断放大,这也构成了科技巨头无法放过的新一轮增长点。而伴随着新市场的到来,科技企业制造新市场格局,开始下一轮结构性增强也成为可能。无论是谷歌、亚马逊,还是阿里、华为、百度,产业AI正在成为科技公司追逐的新猎物。

      基于此,腾讯从战略层面给出的第一个对策,是收拢原先分散的技术体系,将AI技术能力、产业服务能力,以及腾讯生态下各自培植的AI解决方案整合到CSIG。

      比如我们看到,优图等腾讯AI技术输出主力被击中到CSIG,智慧零售这个事实上的AI输出口,以及大量解决方案的研发与产品化通道,也被合并进来,与企业服务、政府服务构筑成为产业AI的业务三叉戟。

      不出意外的话,整合后的的CSIG,不仅是话语权和资源倾斜度拔高,更重要的是能够在统一体系下实施研发、产品化与产业服务出口全流程。

      究其原因,是要更适应AI技术与产业AI需求本身的逻辑,现实世界中企业的AI优化需求往往是复杂和多流程的。毕竟要从不同部门调集技术和产品,解决同一客户的问题,多少是有些尴尬的。

      而腾讯云针对产业AI已经布局的超级大脑等产品,也大概率将迎来拓展和资源加注。

      收拢各自枪炮,组成重火力平台,产业AI的巨头交火点已经近在咫尺。而CSIG整合的进一步诱因,则是为了长征做准备。

      持久战:腾讯调兵遣将的逻辑

      AI的爆发正在带来产业互联网可能触发的浪潮,但B端业务显然不可能当做短期风口。持续的技术升级和深层企业需求、产业场景不断下沉,都让产业互联网近乎必须是一个长期投入、持续跟进的持久战。

      这个逻辑也映衬到了腾讯的架构调整上。如果说要用一句话总结CSIG的背后逻辑,那就是“差不多能为一家未来企业型服务的,都给调来了”。

      而从目前信息上看,CSIG还将同时负责面向未来产业需求的技术模块与商业模块,比如IPv6、IoT、5G等应用的研发与产业应用。

      长期来看,CSIG未来将负载三个目标:

      1、能够灵活、针对性解决企业、行业、政府的不同产业升级需求,打造“腾讯牌”产业案例。

      2、构筑腾讯在B端市场的整体差异化。

      3、拥抱5G、IoT、量子计算在未来可能带来的连续产业爆炸。

      为了这些持久战做准备,产业智能服务相关的业务协调和技术优势持续输出,就成为了腾讯调兵遣将的核心逻辑。

      于是我们见到更直接的技术引擎被整合入CSIG,与腾讯云本身的技术研发能力协调,构成更灵活的技术通道。提高了AI等技术直接作用于产业互联网的能力。

      此外,腾讯的技术优势部门被大量整合入CSIG,围绕AI、安全、量子计算等领域建立前沿技术实验室,加强与腾讯云的联动,深化对重点产业的支持,加速整体的腾讯技术转应用的落地实践,打造腾讯的B端护城河。

      而为了提高产业市场的覆盖能力,腾讯也将多个产业服务项目整合,比如著名的腾讯安全,以及智慧零售、腾讯地图等业务。这样CSIG将达成面向各种产业客户提供服务的能力,而不是将客户分门别类,在腾讯体系中再拆解。

      此外,聚焦于更远期技术应用的部门与实验室也被装进了CSIG,将其确立为产业互联网领域拥抱未来技术变化的主体。

      一句话总结腾讯架构调整,在产业互联网层面的战略:加码云平台,近战AI,远迎产业世界的十年变革。

      而随着腾讯的征战檄文,产业互联网世界的竞争激烈度进一步提升。全面进场的巨头、崛起的垂直场景独角兽、大量缺失的产业链生态,与其说产业+智能是腾讯的长征,不如说是整个科技业的长征。

      队伍出发了,未来值得我们期待。

      @今日话题 



      本话题在雪球有0条讨论,点击查看。
      雪球是一个投资者的社交网络,聪明的投资者都在这里。
      点击下载雪球手机客户端 http://xueqiu.com/xz

      GSMA:智能连接——5G、AI和IoT的组合如何改变美洲

      $
      0
      0

      GSMA发布了新报告“智能连接:5G、AI和IoT的组合如何改变美洲”,强调了该地区如何从这些科技中受益。

      GSMA Intelligence预测,到2025年全球5G连接数量将达到13亿,覆盖全球40%的人口或约27亿人口。届时,美洲地区预计将拥有超过2.6亿个5G连接,占全球市场的20%。

      智能交通

      5G网络和AI系统可实现车辆、自行车和人员位置的实时通信,减少事故或碰撞。有关天气、地面条件、道路工程或拥堵的数据可以通过骑车人的头盔实时传递,并且与AI结合使用,可以帮助出行者绘制最佳路线。5G系统还将通过监控相邻车辆的行为并相应地做出响应来改善驾驶,以及在发生事故时自动寻求帮助。5G还将迎来一个可靠的自动驾驶车辆时代。

      无缝娱乐

      5G将从根本上改变我们消费娱乐的方式,为游戏和沉浸式电视以及网络服务提供4K和8K超高清视频、3D视频、全息图、增强现实(AR)和虚拟现实(VR)应用。音乐或现场体育赛事可以从运动员或艺术家的角度进行广播,观众可以在不同的屏幕上访问多个角度或重放,或者在VR耳机上看到沉浸式内容,而这些都可以在家里获得。

      无人机快递

      5G网络可帮助无人机直接向客户提供快速、低成本、安全的快递。还将帮助协调大型机队,使其安全飞行,自动避免与建筑物和其他无人机的碰撞,并提供安全连接,身份验证和智能自主导航,以及紧急情况下的高清视频备份和恢复位置。

      运营商在美洲的角色

      美洲各地的移动运营商已经在推出和部署5G网络方面取得了长足的进步。 美国将成为世界上首批推出5G商业服务的国家之一。 所有四家美国国家移动运营商都宣布了他们的第一个部署计划。

      PDF版本将分享到199IT交流群,支持我们发展可加入!

      35 岁的程序员将何去何从——阮一峰

      $
      0
      0

      作者:阮一峰,IT 技术作家,长期写作个人技术博客。当过高校教师,也当过阿里巴巴集团软件工程师。曾出版译著《黑客与画家》《软件随想录》,技术专著《ES6 标准入门》。

      (一)

      2017年初,网上传言华为公司正在清理34岁以上的员工。

      中国区开始集中清理 34+ 的交付员工,……去向是跟海外服务部门交换今年新毕业的校招员工,也就是进新人,出旧人。

      这些旧人要被输出去海外,实际上就是变相裁员,这些30多岁的老杆子,英语又不好,拖家带口,能去海外安心奋斗的没几个,即使出去了幸存的也不多。

      华为公司否认该传言。但是,不久以后又有传言称,45岁必须退休。

      为保持公司年轻化,退休政策即将微调,从“45岁可以退休”改变为“45岁须退休”,想继续工作的,需人力资源部重新审批。

      一时间,网上议论纷纷:34 岁清理一批,45 岁强制离职,这是什么样的人事政策啊!

      (二)

      我不讨论这个消息的真假,因为我也不知道。我只指出一个事实:IT 行业是一个年轻人的行业。

      随便哪一家 IT 公司,你去参观,主要员工都是年轻人,40岁以上的很少见,高管往往也是20出头的年轻人。长久以来,一直有人问:“40岁以上的程序员都去哪里了?”

      老员工在这个行业是稀有动物,从 35 岁开始,数量急剧减少,年龄越大越稀有。网上的传言只是从一个侧面验证了大家的这种感觉。

      (三)

      为什么 IT 公司都是年轻人的天下?我认为主要原因有两个。

      首先,工作强度太大了。IT 公司的加班是家常便饭,业务越忙、加班时间越长。很多团队都采用 “996” 工作制:早上 9 点上班,晚上 9 点下班,每周六天。杭州有一家全世界最著名的电子商务公司,天天半夜 12 点,办公楼灯火通明,门口等着接生意的出租车排成一长队。有一项统计《2016 年 IT 公司加班时间排行榜》,华为排在第一位,平均每个工作日加班 3.96 小时,第二位是腾讯,加班 3.92 小时。

      人的生理和智能的最高峰是 20 岁~30 岁这个年龄段。过了 30 岁,身体就慢慢走下坡路了,思维也不如以前活跃了。年轻的时候,长年累月的加班或许还可以承受,等进入中年,再这样拼,你的身体吃得消吗?加班好比折旧,加班越凶,折旧越快。我见过很多程序员刚过 30岁,但看上去好像40岁,长期缺乏运动,工作压力大,使得他们的身体有着各种疾病,实际上已经不能承担高强度的工作或者 deadline(截止期)的赶工压力了。

      另一方面,即使你可以咬紧牙关撑下去,家里人答应吗?父母和妻儿天天看不到你,他们能受得了?万一父母住院,或者小孩在幼儿园被其他同学打了,你能不闻不问,继续全部心思扑在工作上?

      (四)

      IT 公司缺少老员工的第二个原因是,这个行业变化太快了,老员工没优势。

      老员工的优势是经验和人脉,可是在 IT 行业,这两样东西都不是特别重要,新事物层出不穷,旧事物没多久就无人问津。最近的行业热点,共享单车、直播、VR、区块链、O2O……都是新事物,史无前例,大家都没经验。谁占领了市场,谁就成了标准。而且,新事物的目标受众往往主要是青年,他们接受新事物的程度最快最高,用“90后”去设计产品、打开市场,可能比使用“70后”有效得多。

      这个行业里面,起决定性作用的是新技术。技术进步的速度,比市场的变化还快,每年都有大量新技术出来。你 22 岁大学毕业入职,等到10年过去了,32 岁时你大学里面学到的东西都没用了,你必须和新人一样从头开始学习新技术。你也许会说,怎么可能都没用呢,难道微积分、统计学、编程原理这些都没用了吗?问题是新人也学过这些啊,而且他们刚刚学,不像你已经忘得差不多了。

      (五)

      企业发现,新人可以全身心投入工作,连续加班,可塑性高,又比较听话,不像老员工,资格太老而变得油滑,一有不满就公开抱怨,或者上班时间经常到吸烟区吞云吐雾。

      更糟糕的是,老员工的工资比新员工高得多。如果老员工没有能力优势,反而拿着比新人高几倍的工资,对企业来说,应该怎么做,就不言自明了。

      中国的 IT 企业里面,第一线员工干到 34 岁时,大概已经拼搏了 10
      年以上,再拼命有点力不从心了。对企业来说,该员工最能创造价值的巅峰也已经过去了。等他到了 45 岁,如果还没有当上高管,很可能已经创造不了价值了,再留着他反而可能有负作用,让企业变成松松跨跨的养老院。

      这就是为什么大家觉得传言可信,因为符合逻辑。如果员工都是奋斗者,你说谁更容易有奋斗精神,25 岁还是 45 岁?

      好在 IT 行业的工资现在还是不错的,即使 45 岁退休了,生活水平也不会一时下降太多。如果能够拿到公司的股票,而公司又非常成功,那么可能不用等到 45 岁,你自己早早就走了,享受生活不再为人打工了。

      马云在湖畔大学给企业家上课,第一课就说:“小公司的成败在于你聘请什么样的人,大公司的成败在于你开除什么样的人。大公司里有很多老白兔,不干活,并且慢慢会传染更多的人。”可见最成功的企业家早就认可这种做法。

      (六)

      我估计,34 岁之前晋升到中级,45 岁之前晋升到高管,否则强制退休,会成为 IT 行业的惯例。随着其他行业正在日益变成“互联网 +”,这种做法还有向其他行业扩展的可能。

      这就提出了一个很严峻的问题。如果你没有在期限之前晋升到中级或高管,那么到 45 岁就没工作了,你该怎么办?45 岁还是一个很有活力的年龄,就这样退休,对社会是人力资源浪费,对个人也很残酷。可是,那时你找得到工作吗?或者即使找到工作,你还能拿到原来岗位的那种报酬吗?

      现实是非常残酷的。一个名叫“蓝血研究”的公众号里,贴出过一篇据说是内部员工的文章。首先,作者表示理解公司的做法。

      年龄大、股票多的员工消耗了华为大量的成本,公司要清理这样的员工为年轻一代释放出更大的空间,这是极其合理的事情。而且大部分的老员工基本已经财务自由,就算没有完全自由,保留的股票也基本可以衣食无忧。

      但是,现实情况却是“强制退休或者不续约的,都不是年龄大和股票多的高成本员工,反而是在华为兢兢业业十来年,考评普通职级一般,收入和股票都偏低的那一群人”。

      作者认为很多高职级的管理者更应该被裁掉。

      高管真的是那么不可或缺吗?我真不觉得是这样,很多情况下恰恰相反。当前很多高管其实离业务很远,让他们去做一件基层员工的事情,他们反而是做不了的,自己也没有做什么高大上的战略性的事情,每天就是开会开会开会,分配任务。大部分都貌似很忙,每天都开会到很晚,但是真的对业务有多大帮助呢?大家心里都有一杆秤!一边是我们的基层大龄扎实贡献的员工被裁被退休,一边是管理者的岗位职级嗖地往上涨,公平何在?公理何在?

      一个 19 级的管理者的年收入大概是他所管理的 15 级员工的 5 倍,但是 19 级的贡献度真的是 15 级的 5 倍吗?以现在的体制,把 15 级的人扔到 19 级的岗位上,该部门会出大乱子吗?业务就会因此出现毁灭性打击吗?还是说 15 级经过几个月适应,竟然也能干 19 级的岗位。到底是谁的报酬过多?到底是谁应该被退休?

      (七)

      我觉得,每个人都应该想一想,你的雇主如果没有你,是不是就会有重大损失?一个新人或更基层的员工接手你的岗位,他能不能上手,而他要求的报酬又会是多少?

      技术的进步让人类活得更长、更健康,但也让我们变得不那么有用了。将来也许每个人都要选择两次自己的人生:一次是大学毕业找工作时,另一次是 45 岁没有工作时。

      alt text

      电商搜索算法技术的演进

      $
      0
      0

      阿里妹导读:2018年9月28日,阿里电商搜索事业部迎来了一场以“搜·荐未来”为主题的技术峰会。


      搜索与推荐算法经过多年的发展,从最初简单的统计模型,机器学习到形成完整的离线在线与实时的深度学习与智能决策体系,每年都有新的算法突破,帮助搜索与推荐的体验与效果取得大幅提升,成为驱动电商商业创新与发展的新引擎。站在今天总结过去的算法演进,同时看未来电商搜索推荐算法的发展,期待从机器智能到结合人类智能做到真正地认知智能,实现搜索推荐新的交互新体验。


      今天,我们邀请青峰老师,带你回顾搜索算法技术的发展之路。

       

      作者简介:青峰,搜索推荐算法技术负责人,阿里巴巴研究员


      一 、淘宝搜索的一些特点


      淘宝有几十亿商品,挂靠在几千个叶子类目,上百个一级类目,十几个行业下面。如何能让用户找到符合意图的商品,是淘宝搜索需要解决的首要问题。


      淘宝搜索从大的架构或流程上来说,与传统的搜索引擎有不少相似的地方。包括对数据的整理、分析、索引产生索引库,如何根据用户输入的关键词在索引倒排表中进行检索,完成商品与检索之间的相关度评价,对将要输出的结果进行排序,并实现某种用户相关性反馈机制等。

        

      当然作为电商的商品搜索来说,它天然的商业属性带来的更多是自身独特的技术特点。


      从数据更新角度来看,淘宝的数据变化和更新非常快。每天大量的新商品数据被上传到网站,一旦新商品被上传,这个商品就需要被搜索到。不像网页搜索,任何人可以发布新的网页,但是否被搜索引擎收录是另一回事。同时在淘宝每天有大量的商品不停地在做更新,包括商品标题描述的变化,商品价格的改变,商品图片的更新,商品的上下架等等,这些变化也需要实时的更新到搜索中,以便让用户及时找到更新后的商品信息。而在全网搜索中,很多网页是静态不变,网页之间的相互关系也变化缓慢,大量索引的更新没有类似淘宝搜索这种实时性的需求。


      从搜索数据源来看,淘宝商品的图片在用户研究和购买过程中起到了很大作用,搜索的展现结果中有很大一部分被图片所占据。如何更有效地利用图片的信息,无论是根据图片来做检索,或是考虑图片的质量,图片与文本的相互关系等都是淘宝搜索需要考虑和处理的。


      另外一个特点是全链路特性。搜索,比较以及购买都发生在淘宝站内,不像一般的全网搜索引擎,用户搜索完后就跳离到其它网站,搜索前和搜索后的用户行数据是很难拿到的。而在淘宝搜索,用户搜索完后,会点击其中一些商品,然后比较这些商品,和卖家进行沟通,然后下单购买,或者返回来继续搜索,搜索前,搜索中和搜索后的数据和信息非常丰富,有全链路的用户行为数据能帮助我们设计一个更好的搜索排序算法。


      最后更重要的一点淘宝是一个生态系统。而搜索排序算法的设计不只是体现了搜索本身的技术追求,也包含了更多的商业诉求。在全网搜索中,一般的网页是不是被索引,被索引后是不是能展现,对网页的拥有者来说并不是一个事关生活的决定点。在淘宝上则完全不同,很多商家依赖于淘宝来解决民生就业问题,网店的流量以及成交关系到很多人的生活。在淘宝搜索的算法设计中,既要考虑用户的搜索体验,也要考虑商业规则来保障公平性和流量的分散性。很多的搜索算法原理,规则或算法结果都会向卖家宣导,引导卖家向更好的方向发展。


      二、搜索算法技术演进


      作为淘宝海量消费者与平台的互动行为,大量商家在平台进行的商业活动的最主要承载者,淘宝搜索是大数据智能化应用的最佳场景;在淘宝搜索算法多年的发展过程中,依托于工程架构体系的逐步完善,逐步实现从简单人工运营加简单算法规则的时代,到形成完整的离线在线与实时的深度学习与智能决策体系,成为阿里电商平台流量分发与商业驱动的智能中枢,总结搜索算法技术的迭代进步,大概可以分成如下四个阶段:

       

      2.1检索时代


      这个阶段和业务相对应,搜索排序主要围绕规则和轮播展开。这个阶段数据量和用户量还处于可控程度,具有领域知识的专业运营和产品往往充当信息展示规则的制定者,根据主观的判断和对市场的敏锐度来制定查询词背后的商品展示逻辑。当然这个阶段搜索也会运用一些基本的算法逻辑来保证信息匹配的正确性和人货匹配的公平性,基于传统搜索引擎技术的相关性模型,保证用户查询词语商品标题的有效匹配;基于商品成交与否的销售人气模型,保证有助于被消费者接受的商品得到更多的展示机会。


      但总体来说还是基于人工规则把各种相关因子进行组合,得出最后的排序。“人工规则”的好处是容易理解和操控,坏处则不言而喻,随着平台规模的增大,简单规则无法精细的表达人货匹配的效率,并且容易被一些不良商家利用规则来扰乱市场秩序;

       

      2.2大规模机器学习时代


      随着平台规模的扩大,大规模商家入驻,积极地在平台上打理店铺,发布商品,相对结构化的商品组织体系,类目结构,属性信息,基于商品为key 的销量的累积,评论的累积,这些为更好地理解商品积累了重要的原始数据资料;消费者通过搜索产品的各级页面与平台的互动越来越频繁;数据的组织形成了以人为key 的结构体系,反馈信号也得以在闭环系统中有效的流转;所有的这些都为理解用户积累了重要的数据资料。


      有效数据的积累为大规模运用机器学习技术解决问题提供了必要的土壤。搜索也进入到各种大规模模型的研究和开发,比如点击预估模型等,研究数据特征规模大、特征复杂度高、数据时效性高、模型训练频繁等相关的问题,利用不断增强的计算处理能力,对百万乃至十亿量级以上的超大规模数据进行分析和挖掘。这时候参与排序的因子也越来越多,从一开始的类目相关性与文本相关性,商品人气分;再后来为了平衡卖家流量,加入的卖家分;再后来为了更好的用户体验,加入了个性化人与商品的点击预估,图像质量等因素等等,开始使用类似Learning to Rank(LTR)方法,根据商品的点击、成交数据构造学习样本,学习回归出排序权重。

       

      2.3大规模实时在线学习时代


      首先,相比通用搜索,电商搜索中对于实时计算/学习有着更高的要求,用户会有着更丰富的多维度的交互,更长的浏览链路。因此,如果用户在链路前期的行为可以实时地被系统捕捉并且建模到引擎中并且作用于链路后期,对整个链路的效率以及用户体验都是及其关键的。


      其次,用户的行为分布也不是一成不变的,从而打破离线训练的监督学习算法所依赖的独立同分布假设,尤其是在双11这样的大促场景,一天的流量和日常数天的相当,分布变化会更加的剧烈。


      最后,由于搜索展示商品的有限,能够进入日志系统得到用户反馈的商品集合只占商品全集的一部分,因此离线训练和线上会有不一致性,这个不一致性在一定程度上可以通过在线学习系统缓解。


      于是,我们建设了一套实时计算和在线学习系统,支持在秒级别内对海量用户行为及其相关联的海量商品作实时分析处理,从中提取多维度的用户/商品数据特征,并采用分布式Parameter Server架构进行在线学习,从而使用户行为可以在几秒内影响搜索排序等在线服务。


      我们逐步实现了“实时特征”->“实时排序因子模型”->“实时顶层LTR/Bandit模型”,完成了三位一体的实时化建设。我们先后实现了秒级更新的基于pointwise的FTRL、基于pairwise的实时矩阵分解模型和实时双线性模型等一系列微观特征,并在此基础之上实现了实时Learning to rank,以及实时Multi-Armed Bandit等宏观调控模型,实现了双链路实时系统的升级。


      与此同时,在线学习系统强有力地支持了对流量的精准调控,可以更迅速有效地实现商业决策。此外,我们还将在线学习的算法部分抽象出来,建立了一个通用的一站式在线机器学习算法平台AOP(Algorithm One-stop Platform),使得建立和部署在线学习模型更加得便捷和高效,同时具有高度可扩展性。到目前为止,在线学习系统已经成为搜索架构中的基础组件之一,在提升用户搜索体验,支持商业决策,以及支撑双11等大促效果方面发挥了巨大作用。

       

      2.4深度学习与智能决策时代


      以深度学习和强化学习为代表的人工智能给搜索技术带来了全新的变化,特别是在语义搜索,搜索个性化和智能决策三个方向。


      在语义搜索领域,我们设计并实现了Query的表征学习框架,通过多任务学习和协同训练等技术,为Query的打标、类目预测、改写以及推荐等一系列应用提供统一的表征向量。与此同时,我们还实现了商品的表征学习框架,为商品内容理解,商品智能创意,商品语义召回和语义匹配提供统一的商品表征。在Query和商品的表征框架基础之上,我们实现了语义召回和语义相似度模型,从而完成了从字面匹配到语义匹配的这一质的突变。此外,语义搜索除了增加搜索结果相关性,提升用户体验外,也可以一定程度上遏制淘宝商品标题堆砌热门关键词的问题。


      在搜索个性化领域,我们通过多项技术对原先的个性化系统进行了升级:通过多任务学习的深度用户感知模型,我们可以从海量用户行为日志中学习用户的通用表达,从而用于用户行为识别、偏好预估、个性化召回、个性化排序等任务;通过多模态融合学习,我们可以将商品的文本、图像、标签、品牌、类目、店铺及统计特征等多维度特征自动的融合在一起形成统一的商品表征;通过在线深度排序学习,我们融合了用户状态实现更加精准的千人千面的排序模型;通过向量召回引擎,我们得到了泛化更好的召回结果,有效提升了关键字和个性化匹配深度;通过深度迁移学习,我们将搜索个性化技术在搜索之外的多个场景得到广泛应用。随着这些深度模型在个性化领域的广泛使用,个性化系统的精准度得到了显著性的提升。


      在智能决策领域,我们针对用户在搜索过程中和引擎的交互特点,对用户的决策序列进行建模,提出了提出搜索会话马尔科夫决策过程模型,将强化学习引导到搜索排序。同时在针对不同场景下搜索结果趋同,浪费曝光问题,我们提出了基于多智能体协同学习实现了搜索多个异构场景间的环境感知、场景通信、单独决策和联合学习,实现联合收益最大化,而不是此消彼长。


      经过了“检索时代->大规模机器学习时代->大规模实时在线学习时代->深度学习与智能决策时代”这4个历史阶段的耕耘和积累,我们逐渐形成了今天的搜索算法排序体系(如下图)。


       

      三、未来发展:认知智能的探索


      如上所示,经过多年的发展,搜索和推荐作为阿里电商两个最大的自然流量入口,已经All in AI,并形成了完整的用户偏好在线学习,流量的精细化匹配,以及基于强化学习等智能决策能力的购物决策体系。


      但在这个过程中,搜索排序或者推荐学习到的知识更多都是通过已有的商品标签数据和用户行为数据来获取的,还缺少对商品和用户更深层次的认知,还无法完全理解用户的多元意图表达的真实需求。比如,用户搜索了“性感连衣裙”,可能是想找“去参加晚场party的低胸晚装裙”,也可能是想找“去海边度假的露肩沙滩裙”;用户收藏了“登山鞋”和“拐杖”,可能有“登山装备”的需求,需要发现更多的和登山装备相关的其它品类商品;一个有孩子的爸爸,在暑假刚开始时,挑选“转换接头”,查看“大英博物馆门票”,可能是想带着家人一起“暑期英国亲子游”,需要发现更多相关的其它品类商品。


      究其原因,目前人工智能技术特别是以深度学习为代表的模型,在现实应用中快速发展,最主要受益于海量大数据以及大规模计算能力,通过对物理世界的数字化抽象和程式化学习,使得人工智能具备很强的限定知识的获取能力,而很难获得数据之外的知识,就更不用说知识的类比、迁移和推理了。而机器的认知智能,比如自主学习和发现,甚至创造能力才是人工智能的更高境界。当然通用的人工智能还有很多工作要做,但在这个过程中,怎么样首先结合人类知识和机器智能做到初步的认知智能,让淘宝搜索和推荐拥有智慧化的体验是我们目前在探索的方向。


      实现认知智能,首先需要我们对用户,商品,卖家等有更深入的认知,系统化的建立起电商领域的认知知识体系。下图展示了我们定义的电商人-货-场三维的认知图谱,由四部分组成,包括用户、场景、类目(淘宝类目/虚拟类目)和商品。这些不同类型的概念构建成了一个异构图,来实现用户-场景-商品的关联以及各维度数据的深刻认知。


      图:人-货-场三维组成的电商认知图谱示例


      其中场景是商品关系的语义化描述,是用户需求的概念化表示,是连接用户与商品的桥梁。从商品端来看,场景可以理解为一种具有语义解释的商品关系描述,例如同属“中秋送礼”这一个场景的商品具备在中秋节作为礼物的商品属性。从用户端来看,场景可以看作是对用户需求的概念化描述,例如“户外烧烤”、“度假穿搭”等。因此我们也可以说,场景是连接用户与商品的桥梁。这些场景关系既可以是通过行为数据挖掘获得,也可以是行业或专家知识的输入。场景,类目与商品最终组成统一的场景图谱。


      有了这样的认知图谱体系后,再通过推理计算识别用户真实场景诉求,就可以逐步实现搜索和推荐的认知智能化。这就涉及到认知智能体系中另一个重要的部分:基于认知图谱的在线图计算与推理引擎。有了计算和推理引擎后就可以实现:在用户需求已有行为表达时,认知用户需求场景,挖掘并满足用户更深层的需求;在用户需求没有行为表达时,根据时间地点、用户图谱等信息,扩展和激发用户需求;同时根据线上投放数据和用户反馈,优化认知图谱的场景挖掘和建设,持续不断修正和发现场景,提升推理能力。


      认知图谱和在线图计算与推理引擎的背后,一方面是一系列我们已经有沉淀和积累的技术的深度应用,包括知识表示存储与推理、信息检索、自然语言处理等一些传统技术;另一方面,认知图谱可以和深度学习、强化学习等近年来取得突破性进展的技术进行深度融合,例如实体和关系的向量化表示(embedding),使得实体的检索和关系的推理从离散走向连续;认知图谱作为优化约束同现有的深度监督网络进行融合,将领域知识更加平滑的应用到模型中,而不是简单的规则生效;知识的推理过程中引入序列决策过程建模,使用强化学习减少搜索空间以加速推理过程等等。


      有了认知图谱和在线推理引擎之后,在全域的搜索推荐导购、智能交互和内容生成等各领域上,都会发生各种精妙的化学反应,并且在认知应用过程中,根据用户对认知推理结果的反馈,系统持续迭代优化的认知图谱以及推理算法,从而提升认知计算能力。逐渐地,我们可以建设完成具备自学习能力、推理能力和验证能力的全方位的电商认知智能化体系。

       

      四 、小结


      搜索推荐算法多年的发展,就是围绕着商品与人的连接以及相应的商业诉求,从最初简单的统计模型,机器学习到形成完整的离线在线与实时的深度学习与智能决策体系,不断突破自我,让连接匹配的质量更高,连接的广度更宽,同时通过机制设计促进整个商业和生态的健康发展,成为整个新商业发展的引擎驱动。站在今天总结过去的算法演进,看未来电商搜索推荐算法的发展,期待从机器智能到如何结合机器智能与人类智能做到真正的认知智能实现搜索推荐新的交互和新的体验。



      Viewing all 11857 articles
      Browse latest View live


      <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>