首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

Zabbix 服务端安装与配置LNMP(学习笔记一)

1、关闭SELINUX和防火墙: sed -i 's/SELINUX=enabled/SELINUX=disabled/g' /etc/selinux/config getenforce 0 service iptables stop service firewalld stop 2、安装相关组件: yum -y install wget libtool expat-devel pcre-devel zlib-devel openssl openssl-devel pcre yum -y install gcc gcc-c++ glibc automake autoconf libtool make yum -y install libmcrypt-devel mhash-devel libxslt-devel yum -yinstalllibjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel yum -yinstallzlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel yum -yinstallncurses ncurses-devel curl curl-devel e2fsprogs e2fsprogs-devel yum -yinstallkrb5 krb5-devel libidn libidn-devel openssl openssl-devel 3、安装pcre: cd /usr/local/src wget http://sourceforge.net/projects/pcre/files/pcre/8.39/pcre-8.39.tar.gz/download -O pcre-8.39.tar.gz--no-check-certificate tar -zxvf pcre-8.39.tar.gz cd pcre-8.39 ./configure make && make install 4、安装zlib: cd /usr/local/src wget http://zlib.net/zlib-1.2.11.tar.gz tar -zxvf zlib-1.2.11.tar.gz cd zlib-1.2.11 ./configure make && make install 5、安装openssl: cd /usr/local/src wget https://www.openssl.org/source/openssl-1.1.0b.tar.gz tar -zxvf openssl-1.1.0b.tar.gz cd openssl-1.1.0b ./config make && make install 6、安装nginx: cd /usr/local/src wget http://nginx.org/download/nginx-1.10.2.tar.gz tar -zxvf nginx-1.10.2.tar.gz cd nginx-1.10.2 groupadd -r nginx useradd -r -g nginx nginx ./configure \ --prefix=/usr/local/nginx \ --sbin-path=/usr/local/nginx/sbin/nginx \ --conf-path=/usr/local/nginx/nginx.conf \ --pid-path=/usr/local/nginx/nginx.pid \ --user=nginx \ --group=nginx \ --with-http_ssl_module \ --with-http_flv_module \ --with-http_mp4_module \ --with-http_stub_status_module \ --with-http_gzip_static_module \ --http-client-body-temp-path=/var/tmp/nginx/client/ \ --http-proxy-temp-path=/var/tmp/nginx/proxy/ \ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \ --http-scgi-temp-path=/var/tmp/nginx/scgi \ --with-pcre=/usr/local/src/pcre-8.39 \ --with-zlib=/usr/local/src/zlib-1.2.11 \ --with-openssl=/usr/local/src/openssl-1.1.0b \ ; make && make install netstat -ano|grep 80 mkdir -p /var/tmp/nginx/client /usr/local/nginx/sbin/nginx 7、搭建MYSQL: useradd -s /sbin/nologin -M mysql cd /usr/local/src wget http://mirrors.sohu.com/mysql/MySQL-5.7/mysql-5.7.17-linux-glibc2.5-x86_64.tar.gz tar -zxvf mysql-5.7.17-linux-glibc2.5-x86_64.tar.gz mv mysql-5.7.17-linux-glibc2.5-x86_64 /usr/local/mysql-5.7.17 ln -s /usr/local/mysql-5.7.17 /usr/local/mysql 8、配置MYSQL: #创建数据库文件目录 mkdir -p /data/mysql chown -R mysql.mysql /data/ #配置启动脚本文件,并加入系统服务,自启动 cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld chmod +x /etc/init.d/mysqld chkconfig --add mysqld chkconfig mysqld on #配置mysql配置文件 cat > /etc/my.cnf << EOF [client] port = 3306 socket = /tmp/mysql.sock default-character-set = utf8 [mysqld] port = 3306 socket = /tmp/mysql.sock basedir = /usr/local/mysql datadir = /data/mysql pid-file = /data/mysql/mysql.pid user = mysql bind-address = 0.0.0.0 server-id = 1 init-connect = 'SET NAMES utf8' character-set-server = utf8 #skip-name-resolve #skip-networking back_log = 300 max_connections = 1000 max_connect_errors = 6000 open_files_limit = 65535 table_open_cache = 128 max_allowed_packet = 4M binlog_cache_size = 1M max_heap_table_size = 8M tmp_table_size = 16M read_buffer_size = 2M read_rnd_buffer_size = 8M sort_buffer_size = 8M join_buffer_size = 8M key_buffer_size = 4M thread_cache_size = 8 query_cache_type = 1 query_cache_size = 8M query_cache_limit = 2M ft_min_word_len = 4 log_bin = mysql-bin binlog_format = mixed expire_logs_days = 30 log_error = /data/mysql/mysql-error.log slow_query_log = 1 long_query_time = 1 slow_query_log_file = /data/mysql/mysql-slow.log performance_schema = 0 explicit_defaults_for_timestamp #lower_case_table_names = 1 skip-external-locking default_storage_engine = InnoDB #default-storage-engine = MyISAM innodb_file_per_table = 1 innodb_open_files = 500 innodb_buffer_pool_size = 64M innodb_write_io_threads = 4 innodb_read_io_threads = 4 innodb_thread_concurrency = 0 innodb_purge_threads = 1 innodb_flush_log_at_trx_commit = 2 innodb_log_buffer_size = 2M innodb_log_file_size = 32M innodb_log_files_in_group = 3 innodb_max_dirty_pages_pct = 90 innodb_lock_wait_timeout = 120 bulk_insert_buffer_size = 8M myisam_sort_buffer_size = 8M myisam_max_sort_file_size = 10G myisam_repair_threads = 1 interactive_timeout = 28800 wait_timeout = 28800 [mysqldump] quick max_allowed_packet = 16M [myisamchk] key_buffer_size = 8M sort_buffer_size = 8M read_buffer = 4M write_buffer = 4M EOF 9、初始化MYSQL数据库: #初始化数据库: /usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql #配合环境变量 echo "export PATH=$PATH:/usr/local/mysql/bin" >>/etc/profile . /etc/profile #启动MySQL服务 /etc/init.d/mysqld start #修改root密码 mysql -uroot #mysql -uroot -pk8008.com -e "use mysql;update user set authentication_string=password('新密码') where user='root';" update mysql.user set authentication_string=password("123456") where user='root'; 10、PHP安装: #安装第三方yum源 wget http://www.atomicorp.com/installers/atomic sh ./atomic #使用yum命令安装 yum -y install zlib libxml libjpeg freetype libpng gd curl libiconv zlib-devel gd-devel curl-devel openssl-devel libxslt-devel* php-mcrypt libmcrypt libmcrypt-devel mhash mhash-devel libevent libevent-devel libxml2 libxml2-devel bzip2-devel libcurl-devel libjpeg-devel libpng-devel freetype-devel wget http://mirrors.sohu.com/php/php-5.6.30.tar.gz tar zxvf php-5.6.30.tar.gz cd php-5.6.30 ./configure \ --prefix=/usr/local/php \ --with-config-file-path=/usr/local/php/etc \ --enable-inline-optimization \ --enable-fpm \ --with-mysql=/usr/local/mysql \ --with-mysqli=/usr/local/mysql/bin/mysql_config \ --with-pdo-mysql=/usr/local/mysql \ --with-gettext \ --enable-mbstring \ --with-iconv=/usr/local/libiconv \ --with-mcrypt \ --with-mhash \ --enable-bcmath \ --enable-soap \ --with-libxml-dir \ --enable-sockets \ --with-curl \ --with-zlib \ --enable-zip \ --with-bz2 \ --with-gd \ --with-freetype-dir \ --with-jpeg-dir \ --with-iconv \ --with-png-dir make make install cp php.ini-production /usr/local/php/etc/php.ini 11、安装zabbix: #安装ZABBIX wget https://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/3.0.4/zabbix-3.0.4.tar.gz yum install net-snmp-devel tar zxvf zabbix-3.0.4.tar.gz cd zabbix-3.0.4 ./configure --prefix=/usr/local/zabbix --enable-server --enable-agent --with-mysql --with-net-snmp --with-libcurl --with-libxml2 make && make install echo $? 12、导入zabbix数据库: mysql create database zabbix grant all on zabbix.* to 'zabbixuser'@'localhost' identified by '123456'; flush privileges; use zabbix; source /usr/local/src/zabbix-3.0.4/database/mysql/schema.sql source /usr/local/src/zabbix-3.0.4/database/mysql/images.sql source /usr/local/src/zabbix-3.0.4/database/mysql/data.sql 13、修改zabbix配置文件: #配置zabbix_server配置文件修改如下: LogFile=/usr/local/zabbix/logs/zabbix_server.log DBHost=127.0.0.1 DBName=zabbix DBUser=zabbixuser DBPassword=123456 #zabbixuser的密码 LogSlowQueries=3000 cp misc/init.d/fedora/core/* /etc/init.d/ chmod +x /etc/init.d/zabbix_server chmod +x /etc/init.d/zabbix_agentd sed -i 's#BASEDIR=/usr/local#BASEDIR=/usr/local/zabbix#g' /etc/init.d/zabbix_agentd sed -i 's#BASEDIR=/usr/local#BASEDIR=/usr/local/zabbix#g' /etc/init.d/zabbix_server useradd -s /sbin/nologin -M zabbix mkdir /usr/local/zabbix/logs chown -R zabbix.zabbix /usr/local/zabbix/ /etc/init.d/zabbix_agentd start /etc/init.d/zabbix_server start #######配置脚本存放目录 mkdir /usr/local/zabbix/alertscripts vi /usr/local/zabbix/etc/zabbix_server.conf AlertScriptsPath=/usr/local/zabbix/alertscripts 14、修改PHP.INI: sed -i 's#;date.timezone =#date.timezone = Asia/Shanghai#g' /usr/local/php/etc/php.ini sed -i 's#post_max_size = 8M#post_max_size = 16M#g' /usr/local/php/etc/php.ini sed -i 's#max_execution_time = 30#max_execution_time = 300#g' /usr/local/php/etc/php.ini sed -i 's#max_input_time = 60#max_input_time = 300#g' /usr/local/php/etc/php.ini sed -i 's#;always_populate_raw_post_data = -1#always_populate_raw_post_data = -1#g' /usr/local/php/etc/php.ini 15、配置nginx: user www www; pid /usr/local/nginx/nginx.pid; worker_processes auto; worker_rlimit_nofile 100000; events { use epoll; multi_accept on; worker_connections 51200; } http { server_tokens off; sendfile on; tcp_nopush on; tcp_nodelay on; access_log off; error_log /var/log/nginx/error.log crit; keepalive_timeout 10; client_header_timeout 10; client_body_timeout 10; reset_timedout_connection on; send_timeout 10; limit_conn_zone $binary_remote_addr zone=addr:5m; limit_conn addr 100; include /usr/local/nginx/mime.types; default_type text/html; charset UTF-8; gzip on; gzip_disable "msie6"; gzip_proxied any; gzip_min_length 1000; gzip_comp_level 6; gzip_disable <93>MSIE [1-6].(?!.*SV1)<94>; open_file_cache max=100000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; include /usr/local/nginx/conf.d/*.conf; server { listen 9001; location ~ /nginx_status { stub_status on; access_log off; allow all; #deny all; } } server { listen 80; server_name zabbix.com; server_name 你的本机IP; root /usr/local/nginx/html; location /zabbix{ index index.php index.html index.htm; } location ~ \.php$ { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name; } } } } 16、启动登录: cd /usr/local/php/etc cp php-fpm.conf.default php-fpm.conf /usr/local/php/sbin/php-fpm cd /usr/local/src/zabbix-3.0.4 cp -ra frontends/php/ /usr/local/nginx/html/zabbix chown -R www.www /usr/local/nginx/html/zabbix #zabbix登录 账号:admin 密码:zabbix

优秀的个人博客,低调大师

深入java虚拟机学习 -- 类的加载机制(四)

类加载的命名空间 每个类加载器都有自己的命名空间,命名空间由所有以此加载器为初始类加载器的类组成,不同命名空间的两个类是不可见的,但只要得到类所对应的Class对象的refrence(反射),还是可以访问另一个命名空间的类信息的。 同一个命名空间内的类是相互可见的,子加载器的命名空间包含所有父加载器的命名空间,也就是说由子加载器加载的类能看到父加载器加载的类。例如:系统类加载器加载的类能看到根类加载器加载的类(用户自定的类可以访问java.lang.*包下的信息),由父加载器加载的类不能看到子加载器加载的类。 如果两个加载器之间没有直接或者间接的父子关系,那么它们个字加载的类相互不可见。 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定的类的名字,返回对应的Class对象的引用。 protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } 我们可以按到ClassLoader里面的findClass方法的默认实现会抛出ClassNotFoundException异常,我们只需要在自定义的加载器里面重写,即可。 public class MyClassLoader extends ClassLoader { private String name;//类加载器的名字 private String path="";//加载类的默认路径 private String fileType=".class"; //class文件的扩展名 public MyClassLoader(String name){ super();//让系统类加载器成为该类加载器的父加载器 this.name=name; } public MyClassLoader(ClassLoader parent,String name){ super(parent);//显示指定该类加载器的父加载器 this.name=name; } @Override public String toString() { return this.name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } /** * 抽象类ClassLoader的findClass函数默认是抛出异常的。而前面我们知道, * loadClass在父加载器无法加载类的时候,就会调用我们自定义的类加载器中的findeClass函数, * 因此我们必须要在loadClass这个函数里面实现将一个指定类名称转换为Class对象. * @param name * @return */ @Override protected Class<?> findClass(String name) { byte [] data=this.loadClassData(name); return this.defineClass(name,data,0,data.length); } private byte[] loadClassData(String name) { InputStream is=null; byte [] data=null; ByteArrayOutputStream baos=null; try { this.name=this.name.replace(".","\\"); is=new FileInputStream(new File(path+name+fileType)); baos=new ByteArrayOutputStream(); int ch=0; while(-1!=(ch=is.read())){ baos.write(ch); } data= baos.toByteArray(); } catch(Exception e){ e.printStackTrace(); } finally { try { is.close(); baos.close(); } catch(IOException e) { e.printStackTrace(); } } return data; } public static void main(String[] args) throws Exception { MyClassLoader loader1 = new MyClassLoader("loader1");//没有指定loader的父加载器,则默认的父加载器为系统类加载器 loader1.setPath("G:\\myapp\\loader1\\"); MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");//指定loader2的父加载器为loader1,这里的loader1和loader2都为MyClassLoader的实例 loader2.setPath("G:\\myapp\\loader2\\"); MyClassLoader loader3 = new MyClassLoader(null,"loader3");//bootstrap类加载器 loader3.setPath("G:\\myapp\\loader3\\"); //test(loader1); test(loader2); test(loader3); } public static void test(ClassLoader loader) throws Exception { Class clazz=loader.loadClass("Sample"); Object object=clazz.newInstance(); } 开开心心编码,快快乐乐生活。

优秀的个人博客,低调大师

深入java虚拟机学习 -- 类的加载机制(三)

类的初始化时机 在上篇文章中讲到了类的六种主动使用方式,反射是其中的一种(Class.forName(“com.jack.test”)),这里需要注意一点:当调用ClasLoader类的loadClass方法对类进行加载的时候,并不是对类的主动调用,不会导致类的初始化。 那么接下来我继续给大家2个例子,让我们来看看他们的执行结果分别是什么样的,看看你能猜对吗? public class Test2 { public static void main(String[] args) { System.out.println(FinalTest2.x); } } class FinalTest2{ public static final int x=6/2; static { System.out.println("I am a final x"); } } public class Test2 { public static void main(String[] args) { System.out.println(FinalTest2.x); } } class FinalTest2{ public static final int x=new Random().nextInt(); static { System.out.println("I am a final x"); } } 是不是看到结果很意外呢,2份代码看起来几乎是一模一样的,而且都是static修饰的,为什么和上篇文章讲的不一样了呢,为什么第一个demo里面的静态块没有执行呢?下面让我们带着这一系列问题来解决下。 讲解 如果大家细心的话可以看到多了一个final修饰符,是的,结果的造成就是它在起作用。 public static final int x=6/2; 这行代码,java虚拟机在编译期就可以知道x的值是什么,因此在编译期就已经把3放到了常量池,所以在main方法中调用的时候不会触发类的初始化 即此时的x为编译期的常量 public static final int x=new Random().nextInt(); 这行代码中的x在编译期不能确定具体的值,需要等到运行的时候才能确定x的值,所以在运行时会触发类的初始化,即此时的x为编译器的变量 接口和父类 前面讲的类主动加载的7种方式,都是再说单个类的情况,下面我们来介绍下接口和父类 当Java虚拟机初始化一个类的时候,要求他的所有父类都已经被初始化,但是如果此类实现的有接口,则: 在初始化一个类的时候,并不会先初始化它所实现的接口 在初始化一个接口的时候,并不会先初始化它的父接口 因此,一个父接口并不会因为他的子接口或者实现类的初始化而初始化。只有当程序首次使用他的静态变量时,才会导致该接口的初始化。 只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用。这句话是什么意思呢?下面让我们看下这个Demo public class Test3 { public static void main(String[] args) { System.out.println(Child.x); } } class Parent{ public static int x=3; static { System.out.println("this is a parent"); } } class Child extends Parent{ static { System.out.println("this is a Child"); } } public class Test3 { public static void main(String[] args) { System.out.println(Child.x); } } class Parent{ public static int x=3; static { System.out.println("this is a parent"); } } class Child extends Parent{ public static int x=3; static { System.out.println("this is a Child"); } } 上面两个例子同样是相差一行代码,结果却差别很大,一个Child发生了初始化,另一个没有。 双亲委派模型 类加载器用来把类加载到Java虚拟机中,从JDK1.2版本开始,类的加载过程采用双亲委派模型机制,这种机制能更好的保证Java平台的安全,在这种机制中,除了java虚拟机自带的根加载器以外(根加载器由c++实现,没有父加载器),其余的类加载器有且只有一个父加载器。当Java程序请求加载Loader1加载一个类的时候,loader1首先会委托自己的父加载器去加载这个类,若父加载器还有父加载器的话,依次类推,如果父加载器能加在,则有父加载器完成加载,否则才会有loader1本身加载。 需要注意的事,这里的加载器之间的父子关系实际上指的是加载器对象之间的包装关系,并不是类之间的继承关系。一对父子加载器可能是同一个加载器类的2个实例,也可能不是,只是在子加载器对象中包装了一个父加载器对象。 若有一个类加载器能成功加载Sample类,那么这个类加载器被成为定义类加载器,所有能成功返回Clas对象的引用的类加载器(包括定义类加载器)都被称为初始类加载器。 双亲委派模型的有点是能够提高软件系统的安全性。在此机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类,从而防止不可靠甚至恶意的代码代替由父加载器加载的可靠代码。例如:java.lang.Object类总是由根加载器加载,其他任何用户自定义的类加载器都不可能加载该类。 运行时包 由同一类加载器加载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看他们的包名是否相同,还要看定义类加载器是否相同。只有属于同一个运行时包的类才能相互访问包可见(即默认访问级别)的类和类成员。这样的限制能避免用户自定义的类冒充核心类库的类去访问核心类库的包可见成员。 假设用户自定义了一个类:java.lang.Spy,并由用户自定义的类加载器加载,由于java.lang.Spy和核心类库java.lang.*由不同的加载器加载,所以他们属于不同的运行时包,因此java.lang.Spy不能访问核心类库java.lang包中的可见成员。 我们下一篇来着重说下如何来实现一个自定义的类加载器 开开心心编码,快快乐乐生活。

优秀的个人博客,低调大师

虚拟化学习笔记-KVM虚拟化跨机迁移优化

版权声明:转载请注明出处:http://blog.csdn.net/dajitui2024 https://blog.csdn.net/dajitui2024/article/details/79396692 参考:http://blog.ucloud.cn/archives/2375 在迁移过程中我们需要解决以下几个问题: 宿主机的选择; 磁盘镜像处理; 网络切换设置; 内存磁盘压力的处理等。 普通迁移优化 准备阶段 在迁移准备阶段,需要选择相同业务类型的宿主机,以便方便创建相同配置的虚拟机。该机型除了具有足够空闲内存和磁盘的物理机外,还需要考虑目标物理机的配置是否合适。特别是需要考虑CPU的型号和内核的版本号。 另外,考虑到迁移过程网络带宽有限,如果带宽被其他任务占用,就会使得迁移速度下降,甚至影响最终迁移的成功率。为此,除非物理机之间的网络带宽足够大,原则上并不允许在相同两台物理机之间进行多个并行的迁移任务,以便尽量确保在线迁移的成功率。 迁移阶段 在线迁移过程中需要迁移虚拟机的所有磁盘和内存数据,而且虚拟机在迁移过程中并不停机,这使得需要迁移更多的增量数据。如果能够减少数据的迁移量,就能够减少迁移时间,从而更快的实现云平台的资源动态调整和故障处理。磁盘在物理机上是以文件方式存在的,这个文件是sparse文件,假设虚拟机的磁盘是100G,但实际使用了10G,那么这个磁盘文件在物理机上只占用了10G空间。如图 2�-1所示,但是原生的Qemu在迁移磁盘时并没有保持sparse特性,所以这个100G的磁盘迁移到目标物理机上后就实际占用了100G空间。这个对物理机的磁盘空间来说是非常浪费的,而且还严重影响迁移的完成时间。 图 2-1磁盘文件零块数据迁移前后示意图 图 2-2 磁盘文件零块数据迁移优化示意图 如图2-2所示,在源端读到全0的块,就不发送到目标端,这样目标端就是跳着块来写一个文件,这样就保持了磁盘文件的sparse特性。同时,考虑到线上往往存在多种Qemu版本,还要考虑到原生的Qemu和打了这个patch的Qemu之间迁移机器如何保持sparse。为此,可以通过在Qemu中接收磁盘数据过程判断一个块是否全部为0,如果是就不实际写磁盘,即可解决。所有的零块数据都被跳过,不进行传输。 通过上述方法就可以忽略磁盘迁移过程中的零数据,大大减少传输的数据量。最终,通过Qemu的这些patch,还可以明显减少在线迁移的数据传输量和镜像文件的空间占用量。 存储盘优化。直接进行挂载卸载操作。不需要创建新盘进行数据copy。直接将盘转移过去。 迁移结束优化 如果用户产生的脏内存数据过多,就会使得迁移的增量数据传输时间一直大于最大停机时间,使得增量传输阶段不断进行循环迭代,导致整个迁移过程无法完成 图 2-3 内存迁移的auto-coverge优化 由于用户产生脏内存数据的速度与虚拟机的vCPU被提交运行的时间有关,因此能够减少用户虚拟机vCPU执行时间可以阻止用户产生过多的内存脏数据,从而让迁移数据传输得以在内存脏数据产生之前的完成。 对Qemu的auto-converge功能进行优化,首先,提高Qemu的throttling迁移速度,避免迁移速度限制迁移的完成时间。其次,修改auto-converge的触发条件。在增量迁移数据时,如果产生脏数据的量大于上次产生传输数据量的50%,并重复发生多次,则自动触发自动auto-converge功能。该功能默认情况下,将削减20%的虚拟机vCPU执行时间,也就减低用户虚机vCPU的执行速度,并减少新增的脏数据。 在每次内存迭代开始时,它会根据之前的削减情况,决定后续削减粒度。如果新增脏数据仍然过多,它将重复削减10%的许可vCPU的运行时间,直至削减CPU直至99%,从而触发最后阶段的停机迁移,以完成迁移过程。 虽然这种优化会使得虚拟机的停机时间稍长,但是根据长期实践结果,整个迁移的停机时间仍然非常小(小于100ms),因此这项优化对提高迁移的成功率有重要意义。 压缩迁移优化 虽然Qemu的auto-converge功能可以在一定程度上解决用户内存负载对迁移的影响,提高迁移成功率,但如果用户产生的脏数据对vCPU的执行速度依赖不大,则会使迁移的增量数据传输时间一直大于最大停机时间,使整个迁移过程无法完成。考虑到内存负载高的用户,往往会反复修改某一内存页,这些内存页面很容易被压缩。为此,可以考虑在迁移内存数据前进行压缩。如图 2-4所示,当前Qemu支持的XBZRLE压缩算法会将之前发送的内存页面维护在其内存缓存区内。在迁移内存页面时,会先查找该页面是否在其XBZRLE缓存内,如果在缓存内,则进行异或编码,只传输被压缩后的增量数据;如果没有,则直接传输内存页面。通过这个过程可以大大减少发送内存页面数据量,并提高内存迁移速度。 图 2-4内存迁移的xbzrle压缩迁移优化 实际使用表明通过这种压缩方法可以提高高内存负载虚拟机的迁移成功率、并缩短迁移时间,同时CPU使用率提高也在合理范围内;对于普通内存负载虚拟机的迁移,几乎没有额外的CPU使用率消耗。后续还会结合底层硬件加速卡,并适时的开启多线程内存压缩迁移优化。 切换阶段 1. 源端paused优化 迁移的过程是由Qemu来具体执行,但是对于整个迁移过程的控制则是来自更上层的Libvirt。当Qemu在执行最后一步机器数据迁移切换时,两边的虚拟机都是处于paused状态。后续Libvirt将关闭源端SourceHost上的被迁移虚拟机,并拉起目标端DestHost上的对应虚拟机。 在线迁移的最大优点在于不能因为迁移失败而导致虚拟机关机,不管成功或者失败,都要保障虚拟机实例的存活(源端或目标端)。 2. OVS切换优化 此外,我们在迁移过程中观察到,在迁移即将完成时存在数秒网络中断的情况,这会导致用户业务出现短暂中断,使得后台的迁移过程对用户不透明。而且为减少对用户业务造成不利影响,往往需要事先和用户协调沟通迁移事项,限制了在线迁移的应用。 为此,通过大量的测试迁移实验发现最后一轮的虚拟机的迁移关机时间downtime基本在70ms左右,并不是长时间网络中断的主要原因,而且虚拟机内部压力和迁移速度的变化对迁移downtime并无明显影响。虚拟机网络是采用openswitch来定义组建的,经过大量实验确认迁移过程中的网络中断时间和openswtich设置目标端虚拟机新flow规则的延时时间存在正相关关系。 在虚拟机迁移后到目标端DestHost后,及时为虚拟机下发的新flow规则。通过优化openswtich的网络配置机制,目前已经将迁移过程中的网络中断时间控制在几百毫秒左右,基本做到用户无感知,不会因为在线迁移造成用户业务的中断。 如果用户虚拟机正在运行高IO负载业务,会导致磁盘迁移过程迟迟无法结束,最终导致迁移失败。 如图 3-1所示,首先就需要打通目标端DestHost和源端SourceHost之间的存储系统,即共享两个Host上虚拟机镜像的磁盘数据;同时,在此基础上进行共享存储的跨机迁移,从而实现先进行虚拟机内存和CPU等数据的迁移,以便在目标端DestHost快速拉起虚拟机,缓解源端SourceHost的内存和CPU压力。之后,再将虚拟机的大磁盘数据从源端SourceHost拉取到目标端DestHost。最后,删除源DestHost和目标SourceHost直接的共享存储。具体快速在线迁移的具体实现步骤如下: 图 3-1远程拉取磁盘数据示意图 通过快速迁移优化,整个完整的迁移过程所需的时间,和传统的迁移方法所需的时间相当。但是迁移过程中,共享存储迁移的过程非常短暂,可以快速的在目标端DestHost上拉起虚拟机VM,迅速降低源端SourceHost的负载,改善用户VM之间的资源竞争,改善用户VM的性能,特别对于具有大数据盘的VM用户具有重要意义。 ,在进行这种特殊跨存储迁移时,需要通过Libvirt的特殊配置,先在目标端建立一个不同存储类型的虚拟机(其他配置完全一样),然后再进行后续数据迁移。 如下图 3�-2所示,通过这种特殊配置之后,源端Qemu将通过qcow2驱动从qcow2磁盘文件中读取客户磁盘数据,再通过网络发送到目标端,目标端Qemu在接收到数据之后,通过raw驱动将数据写入到lvm块设备中。 通过多次的反复迭代最终完成整个磁盘的迁移,并最终将源端普通云主机上的用户虚拟机迁移切换到目标端SSD机型的云主机上。整个迁移过程对用户是透明的,不会对用户业务造成不利影响,即便目标端虚拟机迁移失败也不会影响源端用户虚拟机的正常运行。 图 3-2跨机型迁移的存储格式变换示意图 由于Libvirt位于虚拟化组件的最上层,它的升级不会影响正在运行的虚拟机,而且直接可生效,无需停机和迁移就可完成。而Qemu升级以往常需要通过在线迁移才能保证无感知的升级。考虑到磁盘迁移占迁移时间的大部分,如果能够避免磁盘的迁移就可以大大节省软件升级的时间。 如图 3-3所示,在进行本地热升级的时候,需要在本地安装新版的Qemu_new,而原有已经运行的虚拟机VM_old仍然为旧版Qemu_old,然后将创建一台相同配置的新虚拟机VM_new,此时新建的虚拟机VM_new的Qemu版本为Qemu_new,并且新虚拟机VM_new此时处于paused状态。 新虚拟机VM_new和旧虚拟机VM_old之间通过socket文件进行内存数据迁移,这个迁移过程和普通在线迁移过程一致,也是先进行一次全量的内存迁移,然后再进行多次迭代的增量内存迁移,并最终短暂停机完成最后一次的内存和虚拟机机器信息等迁移。 图 3-3虚拟机本地迁移的内存迁移过程

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册