首页 文章 精选 留言 我的

精选列表

搜索[游戏解决方案],共10000篇文章
优秀的个人博客,低调大师

Docker网络解决方案-Flannel部署记录

Docker跨主机容器间网络通信实现的工具有Pipework、Flannel、Weave、Open vSwitch(虚拟交换机)、Calico实现跨主机容器间的通信。其中Pipework、Weave、Flannel,三者的区别是: Weave的思路 1 2 在每个宿主机上布置一个特殊的route的容器,不同宿主机的route容器连接起来。route拦截所有普通容器的ip请求,并通过udp包发送到其他宿主机上的普通容器。 这样在跨机的多个容器端看到的就是同一个扁平网络。weave解决了网络问题,不过部署依然是单机的。 Flannel的思路 1 2 3 4 5 6 7 8 9 10 11 12 13 Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。但在默认的Docker配置中, 每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。并使这些容器之间能够之间通过IP地址相互找到,也就是相 互 ping 通。 Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得 "同属一个内网" 且 "不重复的" IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。 Flannel实质上是一种 "覆盖网络(overlaynetwork)" ,即表示运行在一个网上的网(应用层网络),并不依靠ip地址来传递消息,而是采用一种映射机制,把ip地址和identifiers做映射来资源定位。也就 是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWSVPC和GCE路由等数据转发方式。 原理是每个主机配置一个ip段和子网个数。例如,可以配置一个覆盖网络使用10.100.0.0 /16 段,每个主机 /24 个子网。因此主机a可以接受10.100.5.0 /24 ,主机B可以接受10.100.18.0 /24 的包。flannel使用etcd来维护分配的子网到实际的ip地址之间的映射。对于数据路径,flannel使用udp来封装ip数据报,转发到远程主机。选择UDP作为转发协议是因为他能穿透防火墙。例如,AWSClassic无法转发IPoIPorGRE网络包,是因为它的安全组仅仅支持TCP /UDP/ICMP 。 flannel使用etcd存储配置数据和子网分配信息。flannel启动之后,后台进程首先检索配置和正在使用的子网列表,然后选择一个可用的子网,然后尝试去注册它。 etcd也存储这个每个主机对应的ip。flannel使用etcd的 watch 机制监视 /coreos .com /network/subnets 下面所有元素的变化信息,并且根据他来维护一个路由表。为了提高性能,flannel优化了UniversalTAP /TUN 设备,对TUN和UDP之间的ip分片做了代理。 默认的节点间数据通信方式是UDP转发.在Flannel的GitHub页面有如下的一张原理图: 对上图的简单解释: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1)数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。 2)Flannel通过Etcd服务维护了一张节点间的路由表,在稍后的配置部分我们会介绍其中的内容。 3)源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡, 然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一下的有docker0路由到达目标容器。 这样整个数据包的传递就完成了,这里需要解释三个问题: 1)UDP封装是怎么回事? 在UDP的数据内容部分其实是另一个ICMP(也就是 ping 命令)的数据包。原始数据是在起始节点的Flannel服务上进行UDP封装的,投递到目的节点后就被另一端的Flannel服务 还原成了原始的数据包,两边的Docker服务都感觉不到这个过程的存在。 2)为什么每个节点上的Docker会使用不同的IP地址段? 这个事情看起来很诡异,但真相十分简单。其实只是单纯的因为Flannel通过Etcd分配了每个节点可用的IP地址段后,偷偷的修改了Docker的启动参数。 在运行了Flannel服务的节点上可以查看到Docker服务进程运行参数( ps aux| grep docker| grep "bip" ),例如“--bip=182.48.25.1 /24 ”这个参数,它限制了所在节 点容器获得的IP范围。这个IP范围是由Flannel自动分配的,由Flannel通过保存在Etcd服务中的记录确保它们不会重复。 3)为什么在发送节点上的数据会从docker0路由到flannel0虚拟网卡,在目的节点会从flannel0路由到docker0虚拟网卡? 例如现在有一个数据包要从IP为172.17.18.2的容器发到IP为172.17.46.2的容器。根据数据发送节点的路由表,它只与172.17.0.0 /16 匹配这条记录匹配,因此数据从docker0 出来以后就被投递到了flannel0。同理在目标节点,由于投递的地址是一个容器,因此目的地址一定会落在docker0对于的172.17.46.0 /24 这个记录上,自然的被投递到了docker0网卡。 Pipework的思路 1 2 3 4 5 pipework是一个单机的工具,组合了brctl等工具,可以认为pipework解决的是宿主机上的设置容器的虚拟网卡、网桥、ip等,可以配合其他网络使用。详情可参考:Docker网络详解及pipework源码解读与实践 如果容器数量不多,想简单的组一个大的3层网络,可以考虑weave 如果容器数量很多,而且你们的环境复杂,需要多个子网,可以考虑 open vswitch或者fannel weave的总体网络性能表现欠佳,flannelVXLAN能满足要求,一般推荐用flannel ------------------------------------------------------------------------------------------------------------------------ Flannel环境部署记录 1)机器环境(CentOS7系统) 1 2 10.10.172.201部署etcd,flannel,docker主机名:node1主控端(通过etcd) 10.10.172.202部署flannel,docker主机名:node2被控端 2)node1(10.10.172.201)机器操作 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 设置主机名及绑定hosts [root@node1~] #hostnamectl--staticset-hostnamenode1 [root@node1~] #vim/etc/hosts 10.10.172.201node1 10.10.172.201etcd 10.10.172.202node2 关闭防火墙,如果开启防火墙,则最好打开2379和4001端口 [root@node1~] #systemctldisablefirewalld.service [root@node1~] #systemctlstopfirewalld.service 先安装docker环境 [root@node1~] #yuminstalldocker-y 安装etcd k8s运行依赖etcd,需要先部署etcd,下面采用yum方式安装: [root@node1~] #yuminstalletcd-y yum安装的etcd默认配置文件在 /etc/etcd/etcd .conf,编辑配置文件: [root@node1~] #cp/etc/etcd/etcd.conf/etc/etcd/etcd.conf.bak [root@node1~] #cat/etc/etcd/etcd.conf #[member] ETCD_NAME=master #节点名称 ETCD_DATA_DIR= "/var/lib/etcd/default.etcd" #数据存放位置 #ETCD_WAL_DIR="" #ETCD_SNAPSHOT_COUNT="10000" #ETCD_HEARTBEAT_INTERVAL="100" #ETCD_ELECTION_TIMEOUT="1000" #ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380" ETCD_LISTEN_CLIENT_URLS= "http://0.0.0.0:2379,http://0.0.0.0:4001" #监听客户端地址 #ETCD_MAX_SNAPSHOTS="5" #ETCD_MAX_WALS="5" #ETCD_CORS="" # #[cluster] #ETCD_INITIAL_ADVERTISE_PEER_URLS="http://localhost:2380" #ifyouusedifferentETCD_NAME(e.g.test),setETCD_INITIAL_CLUSTERvalueforthisname,i.e."test=http://..." #ETCD_INITIAL_CLUSTER="default=http://localhost:2380" #ETCD_INITIAL_CLUSTER_STATE="new" #ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_ADVERTISE_CLIENT_URLS= "http://etcd:2379,http://etcd:4001" #通知客户端地址 #ETCD_DISCOVERY="" #ETCD_DISCOVERY_SRV="" #ETCD_DISCOVERY_FALLBACK="proxy" #ETCD_DISCOVERY_PROXY="" 启动etcd并验证状态 [root@node1~] #systemctlstartetcd [root@node1~] #systemctlenableetcd [root@node1~] #ps-ef|grepetcd etcd151481013:10?00:00:00 /usr/bin/etcd --name=master #节点名称--data-dir=/var/lib/etcd/default.etcd#数据存放位置--listen-client-urls=http://0.0.0.0:2379,http://0.0.0.0:4001#?听客户端地址 root1550615419013:11pts /0 00:00:00 grep --color=autoetcd [root@node1~] #lsof-i:2379 COMMANDPIDUSERFDTYPEDEVICESIZE /OFF NODENAME etcd15148etcd6uIPv6489110t0TCP*:2379(LISTEN) etcd15148etcd11uIPv4480550t0TCPlocalhost:44959->localhost:2379(ESTABLISHED) etcd15148etcd15uIPv6480610t0TCPlocalhost:2379->localhost:44959(ESTABLISHED) [root@node1~] #etcdctlsettestdir/testkey00 0 [root@node1~] #etcdctlgettestdir/testkey0 0 [root@node1~] #etcdctl-Chttp://etcd:4001cluster-health member8e9e05c52164694dishealthy:gothealthyresultfromhttp: //etcd :2379 clusterishealthy [root@node1~] #etcdctl-Chttp://etcd:2379cluster-health member8e9e05c52164694dishealthy:gothealthyresultfromhttp: //etcd :2379 clusterishealthy 安装覆盖网络Flannel [root@node1~] #yuminstallflannel-y 配置Flannel [root@node1~] #cp/etc/sysconfig/flanneld/etc/sysconfig/flanneld.bak [root@node1~] #vim/etc/sysconfig/flanneld #Flanneldconfigurationoptions #etcdurllocation.Pointthistotheserverwhereetcdruns FLANNEL_ETCD_ENDPOINTS= "http://etcd:2379" #etcdconfigkey.Thisistheconfigurationkeythatflannelqueries #Foraddressrangeassignment FLANNEL_ETCD_PREFIX= "/atomic.io/network" #Anyadditionaloptionsthatyouwanttopass #FLANNEL_OPTIONS="" 配置etcd中关于flannel的key(这个只在安装了etcd的机器上操作) Flannel使用Etcd进行配置,来保证多个Flannel实例之间的配置一致性,所以需要在etcd上进行如下配置( '/atomic.io/network/config' 这个key与上文 /etc/sysconfig/flannel 中的配置项FLANNEL_ETCD_PREFIX是相对应的,错误的话启动就会出错): [root@node1~] #etcdctlmk/atomic.io/network/config'{"Network":"10.10.0.0/16"}' { "Network" : "10.10.0.0/16" } 温馨提示:上面flannel设置的ip网段可以任意设定,随便设定一个网段都可以。容器的ip就是根据这个网段进行自动分配的,ip分配后,容器一般是可以对外联网的(网桥模式,只要宿主机能上网就可以) 启动Flannel [root@node1~] #systemctlenableflanneld.service [root@node1~] #systemctlstartflanneld.service [root@node1~] #ps-ef|grepflannel root157301013:16?00:00:00 /usr/bin/flanneld -etcd-endpoints=http: //etcd :2379-etcd-prefix= /atomic .io /network root1587315419013:16pts /0 00:00:00 grep --color=autoflannel 启动Flannel后,一定要记得重启docker,这样Flannel配置分配的ip才能生效,即docker0虚拟网卡的ip会变成上面flannel设定的ip段 [root@node1~] #systemctlrestartdocker [root@node1~] #systemctlenabledocker 3)node2(10.10.172.202)机器操作 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 设置主机名及绑定hosts [root@node2~] #hostnamectl--staticset-hostnamenode2 [root@node2~] #vim/etc/hosts 10.10.172.201node1 10.10.172.201etcd 10.10.172.202node2 关闭防火墙,如果开启防火墙,则最好打开2379和4001端口 [root@node2~] #systemctldisablefirewalld.service [root@node2~] #systemctlstopfirewalld.service 先安装docker环境 [root@node2~] #yuminstalldocker-y 安装覆盖网络Flannel [root@node2~] #yuminstallflannel-y 配置Flannel [root@node2~] #cp/etc/sysconfig/flanneld/etc/sysconfig/flanneld.bak [root@node2~] #vim/etc/sysconfig/flanneld #Flanneldconfigurationoptions #etcdurllocation.Pointthistotheserverwhereetcdruns FLANNEL_ETCD_ENDPOINTS= "http://etcd:2379" #etcdconfigkey.Thisistheconfigurationkeythatflannelqueries #Foraddressrangeassignment FLANNEL_ETCD_PREFIX= "/atomic.io/network" #Anyadditionaloptionsthatyouwanttopass #FLANNEL_OPTIONS="" 启动Flannel [root@node2~] #systemctlenableflanneld.service [root@node2~] #systemctlstartflanneld.service [root@node2~] #ps-ef|grepflannel root317651013:20?00:00:00 /usr/bin/flanneld -etcd-endpoints=http: //etcd :2379-etcd-prefix= /atomic .io /network root3181431576013:20pts /0 00:00:00 grep --color=autoflannel 启动Flannel后,一定要记得重启docker,这样Flannel配置分配的ip才能生效,即docker0虚拟网卡的ip会变成上面flannel设定的ip段 [root@node2~] #systemctlrestartdocker [root@node2~] #systemctlenabledocker 4)创建容器,验证跨主机容器之间的网络联通性 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 首先在node1(10.10.172.201)上容器容器,如下,登陆容器发现已经按照上面flannel配置的分配了一个ip段(每个宿主机都会分配一个182.48.0.0 /16 的网段) [root@node1~] #dockerrun-ti-d--name=node1.testdocker.io/nginx/bin/bash 2dea24b1562f3d4dc89cde3fa00c707276edcfba1c8f3b0ab4c62e075e63de2f [root@node1~] #dockerexec-tinode1.test/bin/bash root@2dea24b1562f:/ #apt-getupdate&&apt-getinstalliprouteiputils-ping-y root@2dea24b1562f:/ #ipaddr 1:lo:<LOOPBACK,UP,LOWER_UP>mtu65536qdiscnoqueuestateUNKNOWNgroupdefault link /loopback 00:00:00:00:00:00brd00:00:00:00:00:00 inet127.0.0.1 /8 scopehostlo valid_lftforeverpreferred_lftforever inet6::1 /128 scopehost valid_lftforeverpreferred_lftforever 7:eth0@if8:<BROADCAST,MULTICAST,UP,LOWER_UP>mtu1472qdiscnoqueuestateUPgroupdefault link /ether 02:42:0a:0a:5c:02brdff:ff:ff:ff:ff:fflink-netnsid0 inet10.10.92.2 /24 scopeglobaleth0 valid_lftforeverpreferred_lftforever inet6fe80::42:aff:fe0a:5c02 /64 scopelink valid_lftforeverpreferred_lftforever 接着在node2(10.10.172.202)上容器容器 [root@node2~] #dockerrun-ti-d--name=node2.testdocker.io/nginx/bin/bash 374417e9ce5bcd11222d8138e684707c00ca8a4b7a16243ac5cfb1a8bd19ee6e [root@node2~] #dockerexec-tinode2.test/bin/bash root@374417e9ce5b:/ #cat/etc/debian_version 9.3 root@374417e9ce5b:/ #apt-getupdate&&apt-getinstalliprouteiputils-ping-y root@374417e9ce5b:/ #ipaddr 1:lo:<LOOPBACK,UP,LOWER_UP>mtu65536qdiscnoqueuestateUNKNOWNgroupdefault link /loopback 00:00:00:00:00:00brd00:00:00:00:00:00 inet127.0.0.1 /8 scopehostlo valid_lftforeverpreferred_lftforever inet6::1 /128 scopehost valid_lftforeverpreferred_lftforever 469:eth0@if470:<BROADCAST,MULTICAST,UP,LOWER_UP>mtu1472qdiscnoqueuestateUPgroupdefault link /ether 02:42:0a:0a:1d:02brdff:ff:ff:ff:ff:fflink-netnsid0 inet10.10.29.2 /24 scopeglobaleth0 valid_lftforeverpreferred_lftforever inet6fe80::42:aff:fe0a:1d02 /64 scopelink valid_lftforeverpreferred_lftforever root@374417e9ce5b:/ #ping10.10.92.2 PING10.10.92.2(10.10.92.2)56(84)bytesofdata. 64bytesfrom10.10.92.2:icmp_seq=1ttl=60 time =0.657ms 64bytesfrom10.10.92.2:icmp_seq=2ttl=60 time =0.487ms ....... root@374417e9ce5b:/ #pingwww.baidu.com PINGwww.a.shifen.com(115.239.210.27)56(84)bytesofdata. 64bytesfrom115.239.210.27(115.239.210.27):icmp_seq=1ttl=48 time =11.7ms 64bytesfrom115.239.210.27(115.239.210.27):icmp_seq=2ttl=48 time =12.4ms ....... 发现,在两个宿主机的容器内,互相 ping 对方容器的ip,是可以 ping 通的!也可以直接连接外网(桥接模式) 查看两台宿主机的网卡信息,发现docker0虚拟网卡的ip(相当于容器的网关)也已经变成了flannel配置的ip段,并且多了flannel0的虚拟网卡信息 [root@node1~] #ifconfig docker0:flags=4163<UP,BROADCAST,RUNNING,MULTICAST>mtu1472 inet10.10.92.1netmask255.255.255.0broadcast0.0.0.0 inet6fe80::42:ebff:fe16:438dprefixlen64scopeid0x20<link> ether02:42:eb:16:43:8dtxqueuelen0(Ethernet) RXpackets5944bytes337155(329.2KiB) RXerrors0dropped0overruns0frame0 TXpackets6854bytes12310552(11.7MiB) TXerrors0dropped0overruns0carrier0collisions0 eno16777984:flags=4163<UP,BROADCAST,RUNNING,MULTICAST>mtu1500 inet10.10.172.201netmask255.255.255.0broadcast10.10.172.255 inet6fe80::250:56ff:fe86:2135prefixlen64scopeid0x20<link> ether00:50:56:86:21:35txqueuelen1000(Ethernet) RXpackets98110bytes134147911(127.9MiB) RXerrors0dropped0overruns0frame0 TXpackets60177bytes5038428(4.8MiB) TXerrors0dropped0overruns0carrier0collisions0 flannel0:flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>mtu1472 inet10.10.92.0netmask255.255.0.0destination10.10.92.0 unspec00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00txqueuelen500(UNSPEC) RXpackets2bytes168(168.0B) RXerrors0dropped0overruns0frame0 TXpackets2bytes168(168.0B) TXerrors0dropped0overruns0carrier0collisions0 ....... 通过下面命令,可以查看到本机的容器的ip所在的范围 [root@node1~] #psaux|grepdocker|grep"bip" root165890.31.373491227840?Ssl13:230:04 /usr/bin/dockerd-current --add-runtimedocker-runc= /usr/libexec/docker/docker-runc-current --default-runtime=docker-runc-- exec -optnative.cgroupdriver=systemd--userland-proxy-path= /usr/libexec/docker/docker-proxy-current --selinux-enabled--log-driver=journald--signature-verification= false --bip=10.10.92.1 /24 --ip-masq= true --mtu=1472 这里面的“--bip=10.10.92.1 /24 ”这个参数,它限制了所在节点容器获得的IP范围。 这个IP范围是由Flannel自动分配的,由Flannel通过保存在Etcd服务中的记录确保它们不会重复。 [root@node2~] #ifconfig docker0:flags=4163<UP,BROADCAST,RUNNING,MULTICAST>mtu1472 inet10.10.29.1netmask255.255.255.0broadcast0.0.0.0 inet6fe80::42:42ff:fe21:d694prefixlen64scopeid0x20<link> ether02:42:42:21:d6:94txqueuelen0(Ethernet) RXpackets644697bytes41398904(39.4MiB) RXerrors0dropped0overruns0frame0 TXpackets675608bytes1683878582(1.5GiB) TXerrors0dropped0overruns0carrier0collisions0 eth0:flags=4163<UP,BROADCAST,RUNNING,MULTICAST>mtu1500 inet10.10.172.202netmask255.255.255.0broadcast10.10.172.255 inet6fe80::250:56ff:fe86:6833prefixlen64scopeid0x20<link> ether00:50:56:86:68:33txqueuelen1000(Ethernet) RXpackets2552698bytes2441204870(2.2GiB) RXerrors0dropped104overruns0frame0 TXpackets1407162bytes3205869301(2.9GiB) TXerrors0dropped0overruns0carrier0collisions0 flannel0:flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>mtu1472 inet10.10.29.0netmask255.255.0.0destination10.10.29.0 unspec00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00txqueuelen500(UNSPEC) RXpackets2bytes168(168.0B) RXerrors0dropped0overruns0frame0 TXpackets2bytes168(168.0B) TXerrors0dropped0overruns0carrier0collisions0 ....... [root@node2~] #psaux|grepdocker|grep"bip" root319250.31.774740435864?Ssl13:210:05 /usr/bin/dockerd-current --add-runtimedocker-runc= /usr/libexec/docker/docker-runc-current --default-runtime=docker-runc-- exec -optnative.cgroupdriver=systemd--userland-proxy-path= /usr/libexec/docker/docker-proxy-current --selinux-enabled--log-driver=journald--signature-verification= false --bip=10.10.29.1 /24 --ip-masq= true --mtu=1472 本文转自 dengaosky 51CTO博客,原文链接:xxxxhttp://blog.51cto.com/dengaosky/2069377xxx,如需转载请自行联系原作者

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

Android 数据库升级解决方案

请考虑如下情况: 在数据库升级时,不同版本的数据库,他们定义的表结构完全可能是不一样的,比如V1.0的表A有10个column,而在V1.1的表A有12个colum,在升级时,表A增加了两列,此时我们应该怎么做呢。 总体思路 1,将表A重命名,改了A_temp。 2,创建新表A。 3,将表A_temp的数据插入到表A。 下面代码列出了更新表的实现,upgradeTables,给定表名,更新的列名,就可以实现数据库表的更新。 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 /** * Upgrade tables. In this method, the sequence is: * <b> * <p>[1] Rename the specified table as a temporary table. * <p>[2] Create a new table which name is the specified name. * <p>[3] Insert data into the new created table, data from the temporary table. * <p>[4] Drop the temporary table. * </b> * * @param db The database. * @param tableName The table name. * @param columns The columns range, format is "ColA, ColB, ColC, ... ColN"; */ protected void upgradeTables(SQLiteDatabase db, String tableName, String columns) { try { db.beginTransaction(); // 1, Rename table. String tempTableName = tableName + "_temp" ; String sql = "ALTER TABLE " + tableName + " RENAME TO " + tempTableName; execSQL(db, sql, null ); // 2, Create table. onCreateTable(db); // 3, Load data sql = "INSERT INTO " + tableName + " (" + columns + ") " + " SELECT " + columns + " FROM " + tempTableName; execSQL(db, sql, null ); // 4, Drop the temporary table. execSQL(db, "DROP TABLE IF EXISTS " + tempTableName, null ); db.setTransactionSuccessful(); } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { db.endTransaction(); } } 得到数据库表的列名 我们可以通过SQL表得到表的列名。 这里需要注意的一点,int columnIndex = c.getColumnIndex("name"); 这里根据name去取得index。 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 protected String[] getColumnNames(SQLiteDatabase db, String tableName) { String[] columnNames = null ; Cursor c = null ; try { c = db.rawQuery( "PRAGMA table_info(" + tableName + ")" , null ); if ( null != c) { int columnIndex = c.getColumnIndex( "name" ); if (- 1 == columnIndex) { return null ; } int index = 0 ; columnNames = new String[c.getCount()]; for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) { columnNames[index] = c.getString(columnIndex); index++; } } } catch (Exception e) { e.printStackTrace(); } finally { closeCursor(c); } return columnNames; } upgradeTables方法应该是在onUpgrade方法中去调用。 数据库升级的意义 在应用程序开发的过程中,数据库的升级是一个很重要的组成部分(如果用到了数据库),因为程序可能会有V1.0,V2.0,当用户安装新版本的程序后,必须要保证用户数据不能丢失,对于数据库设计,如果发生变更(如多添加一张表,表的字段增加或减少等),那么我们必须想好数据库的更新策略。 1,定义数据库版本 数据库的版本是一个整型值,在创建SQLiteOpenHelper时,会传入该数据库的版本,如果传入的数据库版本号比数据库文件中存储的版本号大的话,那么SQLiteOpenHelper#onUpgrade()方法就会被调用,我们的升级应该在该方法中完成。 2,如何写升级逻辑 假如我们开发的程序已经发布了两个版本:V1.0,V1.2,我们正在开发V1.3。每一版的数据库版本号分别是18,19,20。 对于这种情况,我们应该如何实现升级? 用户的选择有: 1)V1.0 -> V1.3 DB 18 -> 20 2)V1.1 -> V1.3 DB 19 -> 20 3,注意 数据库的每一个版本所代表的数据库必须是定义好的,比如说V18的数据库,它可能只有两张表TableA和TableB,如果V19要添加一张表TableC,如果V20要修改TableC,那么每一个版本所对应的数据库结构如下: V18 ---> TableA, TableB V19 ---> TableA, TableB, TableC V20 ---> TableA, TableB, TableC (变更) onUpgrade()方法的实现如下: 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 // Pattern for upgrade blocks: // // if (upgradeVersion == [the DATABASE_VERSION you set] - 1){ // .. your upgrade logic.. // upgradeVersion = [the DATABASE_VERSION you set] // } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { int upgradeVersion = oldVersion; if ( 18 == upgradeVersion) { // Create table C String sql = "CREATE TABLE ..." ; db.execSQL(sql); upgradeVersion = 19 ; } if ( 20 == upgradeVersion) { // Modify table C upgradeVersion = 20 ; } if (upgradeVersion != newVersion) { // Drop tables db.execSQL( "DROP TABLE IF EXISTS " + tableName); // Create tables onCreate(db); } } 从上面的代码可以看到,我们在onUpgrade()方法中,处理了数据库版本从18 -> 20的升级过程,这样做的话,不论用户从18 -> 20,还是从19 -> 20,最终程序的数据库都能升级到V20所对应的数据库结构。 4,如何保证数据不丢失 这是很重要的一部分,假设要更新TableC表,我们建议的做法是: 1)将TableC重命名为TableC_temp SQL语句可以这样写:ALERT TABLE TableC RENAME TO TableC_temp; 2)创建新的TableC表 3)将数据从TableC_temp中插入到TableC表中 SQL语句可以这样写:INSERT INTO TableC (Col1, Col2, Col3) SELECT (Col1, Col2, Col3) FROM TableC_temp; 经过这三步,TableC就完成了更新,同时,也保留了原来表中的数据。 注意: 在onUpgrade()方法中,删除表时,注意使用事务处理,使得修改能立即反应到数据库文件中。 本文转自java豆子博客园博客,原文链接:http://www.cnblogs.com/error404/p/3738630.html,如需转载请自行联系原作者

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

SAP & Oracle EBS集成解决方案

一.需求分析 SAP和Oracle EBS都是作为全球顶级的的ERP产 品,得到了众多客户的亲耐。然而由于 系统庞大、价格昂贵以及定位不同,客户在实施过程中经常会面临以下困惑: 1.SAP如何实现“线上+线下"闭环流程 SAP用户外延流程设计以及二次开发流程面临挑战。 2.如何梳理业务流程 企业的制度、规则和流程体系大多散落在各个业务系统中,管理和变更这些散落的要素存在难点。 3.如何实现持续流程改进 SAP工作流在流程规划、分析和变更方面,如何更好的满足中国企业 需求。 4.如何实现跨系统应用 企业的业务大多是跨系统的,SAP 本身也不是孤立存在的,也需要与其他系统配合来完成业务。我们需要一个 产品来实现跨系统流程 二.方案实现 H3 BPM针对SAP和Oracle EBS分别开发了专用的适配器——H3 Connector For SAP 和H3 Connector For Oracle EBS。H3 Connector For SAP采用最新SAP NCO 3.0, 将BAPI/RFC/其他自定义函数转换成H3 BizBus能接受的方法。 H3 Oralce Connector采用Webservice与Oracle ESB的API连接。 H3 BPM与SAP、Oracle EBS连通后,可以提供全面的流程支持。 三.方案价值 更低的总拥有成本 降低用户数成本,二次开发以及维护的难度与费用 更直观的数据展现 集成集中的更便于审批决策的数据展现 更佳的用户体验 WEB化,移动化, 符合用户操作 习惯的个性化人机 界面 更优秀的IT架构 建立以SOA为 架构、流程中心、 门户为展现、集成各业务系统的企业基础架构 企业更完整的业务流程 延伸SAP流程,覆盖线 下业务,整合第三方系统实现端到端的业务流程 本文转自 lwl_BPM 51CTO博客,原文链接:http://blog.51cto.com/12438115/1913392,如需转载请自行联系原作者

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

native程序异常crash 定位解决方案

Android程序崩溃退出的时候,会将崩溃的堆栈信息保存在/data/tombstones目录下。 该目录需要ROOT权限才能够访问。所以为了访问该路径,手机必须先ROOT破解。jni或者java 代码崩溃的信息都被记录。该目录下会有九个tombstones_0(1-9),生成的崩溃信息会循环写入 该文件中,测试之前可以讲所有的文件删除,就可以得到唯一的一个崩溃日志。生成的文件 记录了详细的堆栈信息。 为了拷贝到电脑上,需要通过adb shell进入手机终端,然后su,获取root权限,接着 拷贝文件到sdcard上,退出adb shell,之后,通过adb pull /sdcard/tombstone_00 E: 拷贝到E盘 该文件详细记录了代码崩溃时候的具体信息,包括了崩溃的堆栈,如果能够获取到该信息,就可以通知堆栈了解崩溃点的信息,但是一般情况下,我们看不到该文件。 当我们发现/data目录为空的情况下,实际上表示我们没有root访问权限,(root是最高级的访问权限,而不是启动的时候的访问文件的权限,这个权限需要对android手机进行权限进行破解) 注意 查看tombstons文件,发现很多情况下,没有提供源码错误的函数名称和源码的错误行号,可以通过ndk-stack.exe 程序对崩溃日志再次定位 $NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi -dump tombstons 例子 D:\Development\Android\android-ndk-r10b\ndk-stack.exe–sym armeabi-v7a –dump E:\docs\tombston_01 > E:/detail.txt 附录: 不同的手机遇到空指针的时候,处理的方式是不一样的,小米手机直接闪退,而华为平板依然能够直接运行,跳过崩溃的错误,说明测试多种机型的重要性。 参考 http://cmzx3444.iteye.com/blog/1463035 本文转自fengyuzaitu 51CTO博客,原文链接:http://blog.51cto.com/fengyuzaitu/1408644,如需转载请自行联系原作者

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

ScrollView不能用RelativeLayout的解决方案

RelativeLayout的意义: 布局各个部件的相对布局。使得界面空间合理利用。 一、ScrollView局限: 滑动的只能是linearlayout,甚至整个布局都不能有RelativeLayout。这使得让人觉得ScrollView控件有点鸡肋。其实不然.......... linearlayout跟RelativeLayout布局在界面上来看只是空间间隔的区别,而在liearlayout中有一个<View/>能占用空间,单单用linearlayout要达到 RelativeLayout的效果,可以用<View/>来实现。 二、View用法 <View android:layout_width="fill_parent" android:layout_height="10sp" /> 三、例子 下面的界面 1)用RelativeLayout实现非常容易实现 RelativeLayout代码 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "fill_parent" tools:context = ".ClientActivity" > < ImageView android:id = "@+id/clientinfo_headpicture" android:layout_width = "90sp" android:layout_height = "90sp" android:background = "@drawable/client_pic_back" android:src = "@drawable/client_pic_src" /> < TextView android:id = "@+id/client_tel" android:layout_width = "match_parent" android:layout_height = "30sp" android:layout_alignBottom = "@+id/clientinfo_headpicture" android:layout_toRightOf = "@+id/clientinfo_heaspicture" android:background = "@color/black" android:ems = "12" android:singleLine = "true" android:text = "电话号码" android:textColor = "@color/white" /> < Button android:id = "@+id/client_tel_edit" android:layout_width = "50sp" android:layout_height = "30sp" android:layout_above = "@+id/client_tel" android:layout_alignParentRight = "true" android:background = "@drawable/cliebt_button_edittel" android:text = "编辑" android:textColor = "@color/blue" /> < TextView android:layout_width = "match_parent" android:layout_height = "30sp" android:layout_alignLeft = "@+id/client_tel" android:layout_alignParentTop = "true" android:background = "@color/black" android:singleLine = "true" android:text = "用户名" android:textColor = "@color/white" android:textSize = "24sp" /> < View android:id = "@+id/view1" android:layout_width = "fill_parent" android:layout_height = "10sp" android:layout_alignParentRight = "true" android:layout_below = "@+id/clientinfo_headpicture" android:background = "@color/blue" /> < RelativeLayout android:layout_width = "fill_parent" android:layout_height = "match_parent" android:layout_alignParentLeft = "true" android:layout_below = "@+id/view1" android:background = "@drawable/client_moreinfo_back" > < Button android:id = "@+id/client_myaddress" android:layout_width = "50sp" android:layout_height = "30sp" android:background = "@drawable/client_address" /> < TextView android:id = "@+id/client_myaddress_text" android:layout_width = "wrap_content" android:layout_height = "30sp" android:layout_alignBaseline = "@+id/client_myaddress" android:layout_alignBottom = "@+id/client_myaddress" android:layout_toRightOf = "@+id/client_myaddress" android:text = "我的地址" android:textColor = "@color/blue" android:textSize = "24sp" /> < View android:id = "@+id/client_myaddress_div" android:layout_width = "fill_parent" android:layout_height = "1sp" android:layout_alignParentLeft = "true" android:layout_below = "@+id/client_myaddress" android:background = "@color/blue" /> < TextView android:id = "@+id/client_myaddress_addtext" android:layout_width = "160sp" android:layout_height = "50sp" android:layout_alignLeft = "@+id/client_myaddress_div" android:layout_below = "@+id/client_myaddress_div" android:layout_marginLeft = "14sp" android:text = "31栋127寝室电话18607060000" android:textColor = "@color/blue" /> < Button android:id = "@+id/client_myaddress_edit" android:layout_width = "40sp" android:layout_height = "40sp" android:layout_alignRight = "@+id/client_myaddress_div" android:layout_alignTop = "@+id/client_myaddress_addtext" android:background = "@drawable/client_edit" /> < View android:id = "@+id/client_buyhistory_div" android:layout_width = "fill_parent" android:layout_height = "5sp" android:layout_alignParentLeft = "true" android:layout_below = "@+id/client_myaddress_addtext" android:background = "@color/blue" /> < Button android:id = "@+id/client_buyhistory" android:layout_width = "50sp" android:layout_height = "30sp" android:layout_below = "@+id/client_buyhistory_div" android:layout_marginTop = "1sp" android:layout_toLeftOf = "@+id/client_myaddress_text" android:background = "@drawable/client_shopcar" /> < TextView android:id = "@+id/client_buyhistory_text" android:layout_width = "100sp" android:layout_height = "30sp" android:layout_alignRight = "@+id/client_myaddress_text" android:layout_alignTop = "@+id/client_buyhistory" android:text = "已购买" android:textColor = "@color/blue" android:textSize = "24sp" /> < View android:id = "@+id/client_buyhistory_div_div" android:layout_width = "fill_parent" android:layout_height = "1sp" android:layout_alignParentLeft = "true" android:layout_below = "@+id/client_buyhistory" android:background = "@color/blue" /> < TextView android:id = "@+id/client_buyhistory_info" android:layout_width = "160sp" android:layout_height = "30sp" android:layout_alignLeft = "@+id/client_buyhistory_div_div" android:layout_below = "@+id/client_buyhistory_div_div" android:text = "北京烤鸭100元" android:textColor = "@color/blue" /> < Button android:id = "@+id/client_buyhistory_eidt" android:layout_width = "40sp" android:layout_height = "40sp" android:layout_alignParentRight = "true" android:layout_centerVertical = "true" android:background = "@drawable/client_edit" /> < View android:id = "@+id/client_store_div" android:layout_width = "fill_parent" android:layout_height = "5sp" android:layout_alignParentRight = "true" android:layout_below = "@+id/client_buyhistory_eidt" android:background = "@color/blue" /> < Button android:id = "@+id/client_store" android:layout_width = "50sp" android:layout_height = "30sp" android:layout_alignParentLeft = "true" android:layout_below = "@+id/client_store_div" android:background = "@drawable/client_stored" /> < TextView android:id = "@+id/client_store_text" android:layout_width = "80sp" android:layout_height = "30sp" android:layout_alignBaseline = "@+id/client_store" android:layout_alignBottom = "@+id/client_store" android:layout_alignLeft = "@+id/client_myaddress_text" android:text = "收藏" android:textColor = "@color/blue" android:textSize = "24sp" /> < View android:id = "@+id/client_store_div_div" android:layout_width = "fill_parent" android:layout_height = "1sp" android:layout_alignParentLeft = "true" android:layout_below = "@+id/client_store" android:background = "@color/blue" /> < Button android:id = "@+id/client_store_edit" android:layout_width = "40sp" android:layout_height = "40sp" android:layout_alignParentRight = "true" android:layout_alignTop = "@+id/client_store_info" android:background = "@drawable/client_edit" /> < View android:id = "@+id/View01" android:layout_width = "fill_parent" android:layout_height = "5sp" android:layout_alignParentBottom = "true" android:layout_centerHorizontal = "true" android:background = "@color/blue" /> < TextView android:id = "@+id/client_store_info" android:layout_width = "160sp" android:layout_height = "40sp" android:layout_alignLeft = "@+id/View01" android:layout_below = "@+id/client_store_div_div" android:text = "北京烤鸭30元" android:textColor = "@color/blue" /> </ RelativeLayout > </ RelativeLayout > 但因为有个项目用到的是TabHost,对子activity的布局有限制,上面的布局超过范围。所以要用到ScrollView,为达到上面的布局,要用到<View/>控件。 2)使用view控件的代码如下 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 < ScrollView xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:fadingEdge = "vertical" android:scrollbars = "vertical" > < LinearLayout android:orientation = "vertical" android:layout_width = "fill_parent" android:layout_height = "match_parent" android:background = "@drawable/account_pic_back" > < TextView android:layout_width = "match_parent" android:layout_height = "30sp" android:background = "@color/black" android:singleLine = "true" android:text = "用户名" android:textColor = "@color/white" android:textSize = "24sp" /> < LinearLayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "match_parent" android:background = "@drawable/account_pic_back" > < ImageView android:id = "@+id/clientinfo_headpicture" android:layout_width = "90sp" android:layout_height = "90sp" android:background = "@drawable/client_pic_back" android:src = "@drawable/client_pic_src" /> < Button android:id = "@+id/client_tel_edit" android:layout_width = "50sp" android:layout_height = "30sp" android:background = "@drawable/cliebt_button_edittel" android:text = "编辑" android:textColor = "@color/blue" /> </ LinearLayout >> < TextView android:id = "@+id/client_tel" android:layout_width = "match_parent" android:layout_height = "30sp" android:background = "@color/black" android:ems = "12" android:singleLine = "true" android:text = "电话号码" android:textColor = "@color/white" /> < View android:layout_width = "fill_parent" android:layout_height = "10sp" android:background = "@color/blue" /> < LinearLayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "match_parent" android:background = "@drawable/account_pic_back" > < Button android:id = "@+id/client_myaddress" android:layout_width = "50sp" android:layout_height = "30sp" android:background = "@drawable/client_address" /> < TextView android:id = "@+id/client_myaddress_text" android:layout_width = "wrap_content" android:layout_height = "30sp" android:text = "我的地址" android:textColor = "@color/blue" android:textSize = "24sp" /> </ LinearLayout > < View android:layout_width = "fill_parent" android:layout_height = "1sp" android:background = "@color/blue" /> < LinearLayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "match_parent" android:background = "@drawable/account_pic_back" > < TextView android:id = "@+id/client_myaddress_addtext" android:layout_width = "160sp" android:layout_height = "50sp" android:layout_alignLeft = "@+id/client_myaddress_div" android:layout_below = "@+id/client_myaddress_div" android:layout_marginLeft = "14sp" android:text = "31栋127寝室电话18607060000" android:textColor = "@color/blue" /> < View android:layout_width = "100sp" android:layout_height = "40sp" /> < Button android:id = "@+id/client_myaddress_edit" android:layout_width = "40sp" android:layout_height = "40sp" android:background = "@drawable/client_edit" /> </ LinearLayout > < View android:layout_width = "fill_parent" android:layout_height = "10sp" android:background = "@color/blue" /> < LinearLayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "match_parent" android:background = "@drawable/account_pic_back" > < Button android:id = "@+id/client_buyhistory" android:layout_width = "50sp" android:layout_height = "30sp" android:layout_marginTop = "1sp" android:background = "@drawable/client_shopcar" /> < TextView android:id = "@+id/client_buyhistory_text" android:layout_width = "100sp" android:layout_height = "30sp" android:text = "已购买" android:textColor = "@color/blue" android:textSize = "24sp" /> </ LinearLayout > < View android:id = "@+id/view1" android:layout_width = "fill_parent" android:layout_height = "1sp" android:background = "@color/blue" /> < LinearLayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "match_parent" android:background = "@drawable/account_pic_back" > < TextView android:id = "@+id/client_buyhistory_info" android:layout_width = "160sp" android:layout_height = "30sp" android:text = "北京烤鸭100元" android:textColor = "@color/blue" /> < View android:layout_width = "100sp" android:layout_height = "40sp" /> < Button android:id = "@+id/client_buyhistory_eidt" android:layout_width = "40sp" android:layout_height = "40sp" android:background = "@drawable/client_edit" /> </ LinearLayout > < View android:layout_width = "fill_parent" android:layout_height = "1sp" android:background = "@color/blue" /> < LinearLayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "match_parent" android:background = "@drawable/account_pic_back" > < Button android:id = "@+id/client_store" android:layout_width = "50sp" android:layout_height = "30sp" android:background = "@drawable/client_stored" /> < TextView android:id = "@+id/client_store_text" android:layout_width = "80sp" android:layout_height = "30sp" android:text = "收藏" android:textColor = "@color/blue" android:textSize = "24sp" /> < View android:layout_width = "fill_parent" android:layout_height = "1sp" android:background = "@color/blue" /> </ LinearLayout > < LinearLayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "match_parent" android:background = "@drawable/account_pic_back" > < TextView android:id = "@+id/client_store_info" android:layout_width = "160sp" android:layout_height = "40sp" android:text = "北京烤鸭30元" android:textColor = "@color/blue" /> < View android:layout_width = "100sp" android:layout_height = "40sp" /> < Button android:id = "@+id/client_store_edit" android:layout_width = "40sp" android:layout_height = "40sp" android:background = "@drawable/client_edit" /> </ LinearLayout > < View android:layout_width = "fill_parent" android:layout_height = "5sp" android:background = "@color/blue" /> </ LinearLayout > </ ScrollView > 通过View完美的调出RelativeLayout的效果 本文转自lilin9105 51CTO博客,原文链接:http://blog.51cto.com/7071976/1217017,如需转载请自行联系原作者

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

摩卡IT服务管理(Mocha ITSM)解决方案

企业面临的挑战 不断复杂化的IT系统带给现代企业巨大的挑战,企业需要对整个IT系统进行全面而有效的管理,保持IT基础架构的可靠性和可用性,以支撑业务的正常运作,扭转被动的局面;IT部门在满足企业业务需求的同时,还要承受来自内部和外部的压力,因此必须控制IT系统的预算,降低IT成本;企业的竞争压力是不允许企业有丝毫的动作迟缓,所以IT部门必须提高运作变化的灵活性和反应速度;基于以上各种因素给企业运营带来的高风险,也是需要企业进行分析和回避的,否则将给企业带来毁灭性的打击。 什么是IT服务 IT服务思想能总结为以下几点:  - IT 部门是 IT 服务的提供者(Provider),必须考虑用户的满意度,其评价 和衡量标准是服务等级协议(Service Level Agreement,简称 SLA)。  - 人力资源和财务预算的稀缺性,迫使 IT 部门必须根据 SLA 来优化配置和调度资源,以响应不同优先度的需求。  - IT服务管理是一种以客户为中心、以流程为导向的方法。 IT服务三部曲 第一步 定义IT服务 通过IT服务把以下关联起来: - 运行的网络环境 - 服务所依赖的主机 - 服务所依赖的应用 - 使用服务的用户 第二步 设置服务监控策略 - 为IT服务的事件定义优先级和级别。 - 为IT服务的报警定义规则处理。 第三步 通过仪表盘查看 通过服务仪表盘了解: - IT服务运行状态 - KPI指标 - 服务影响范围 客户收益 通过Portal的展现,给IT部门领导提供IT服务运行状况,有利于做出正确决策 业务服务管理仪表盘提供对IT基础架构的全面管理,并且能把IT服务和相关用户串连一起,提供直观的可视化界面。 以服务视角来管理IT系统,提供共同语言 通过以服务的视角来管理IT系统,IT部门和业务部门开始有了共同的语言,并且更能从各自的角度触发,提高各部门的协作能力。 IT服务事件触发工单,完善的IT运维平台 管理员可以选择把IT服务的可用性或者性能事件与工单绑定,一旦发生问题,可以触发相对应工单和流程。通过此处理方式,企业可以建立一个完善的IT运维平台,达到标准化,规范化和自动化。 服务仪表盘展现,易懂易用 通过格式化的报表,对数据记录进行归纳和分析,为领导层和CIO提供全局的、向导式的分析、预测报告,为决策提供依据。 量化IT服务,显现IT部门的价值 企业的IT部门是IT服务的提供者,通过SLA反应用户的满意度。服务水平高 则说明IT部门做得好。IT部门根据SLA来优化配置和调度资源,以响应不同优先级的需求。 展现服务影响,提高IT部门的响应速度 监控核心服务监控视图,可以帮助管理人员清楚地看到服务的影响范围,一旦发生问题,可以第一事件通知受影响的用户。 更多相关信息,请点击[url]http://www.mochabsm.com[/url 本文转自赖永锋51CTO博客,原文链接:http://blog.51cto.com/mochasoft/86616,如需转载请自行联系原作者]

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

【关于重载OnBackPressed无效的解决方案】

在android的开发中,很多时候我们希望用户在点击手机的返回键时不要结束程序,而是进行我们需要的其他逻辑路径,比如询问用户是否确定退出: 我们知道,在android 1.5之前,需要重载onKeyDown并在该方法中监听KeyEvent.KEYCODE_BACK键盘事件,然后做出相应逻辑操作(具体做法网上很多,bill不再赘述),android 1.5之后,新增了一个独立的监听返回键的方法——onBackPressed(),我们只需在Activity中重载该方法并在其中写上相应逻辑即可。 然而,事情远非我们想象的那么顺利。上述方法通常情况下都能够正常运行,但是当我们在TabActivity的子类中重载onBackPressed方法时,奇怪的事情出现了,我们像往常一样,按下返回键,以为程序会像往常一样,弹出我们设定好的窗口,可结果是,程序终止了,就像从来没有重载过onBackPressed()一样。 于是bill在onBackPressed方法中打上断点,情况如预料的一样,断点从未被激活。奇了怪了,bill百思不得其解,曾一度怀疑onBackPressed方法是否存在bug,于是bill不断地在其他Activity中做着实验,这些Activity的表现都很正常,返回键按下后断点被激活,可唯独TabActivity及其派生类表现异常。bill断定问题应该出在TabActivity而不是onBackPressed方法本身。于是bill向TabActivity中添加了其他按键的监听,结果这些监听都没有起到任何作用。 经过不断地思考和折腾,bill总算解决此问题,原因也很简单——TabActivity不具备焦点。也就是说,TabActivity不会对任何按键监听产生反应。 问题的原因已经找到,如何解决?原来,TabActivity中至少有一个子Activity,而这才是真正具备焦点的Activity。 因此,我们只需要重载各个子Activity中的onBackPressed()方法即可实现我们预想的逻辑效果。 本文转自Bill_Hoo 51CTO博客,原文链接:http://blog.51cto.com/billhoo/978439,如需转载请自行联系原作者

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

Docker网络解决方案-Calico部署记录

Calico简单简介 1 2 Calico是一个纯三层的协议,为OpenStack虚机和Docker容器提供多主机间通信。Calico不使用重叠网络比如flannel和libnetwork重叠网络驱动, 它是一个纯三层的方法,使用虚拟路由代替虚拟交换,每一台虚拟路由通过BGP协议传播可达信息(路由)到剩余数据中心。 Calico 架构 1 Calico 是一个三层的数据中心网络方案,而且方便集成 OpenStack 这种 IaaS 云架构,能够提供高效可控的 VM、容器、裸机之间的通信。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 结合上面这张图,我们来过一遍 Calico 的核心组件: Felix,Calico agent,跑在每台需要运行 workload 的节点上,主要负责配置路由及 ACLs 等信息来确保 endpoint 的连通状态; etcd,分布式键值存储,主要负责网络元数据一致性,确保 Calico 网络状态的准确性; BGP Client(BIRD), 主要负责把 Felix 写入 kernel 的路由信息分发到当前 Calico 网络,确保 workload 间的通信的有效性; BGP Route Reflector(BIRD), 大规模部署时使用,摒弃所有节点互联的 mesh 模式,通过一个或者多个BGP Route Reflector来完成集中式的路由分发; 通过将整个互联网的可扩展 IP 网络原则压缩到数据中心级别,Calico 在每一个计算节点利用Linux kernel实现了一个高效的vRouter来负责数据转发而每个vRouter通过BGP 协议负责把自己上运行的 workload 的路由信息像整个 Calico 网络内传播 - 小规模部署可以直接互联,大规模下可通过指定的 BGP route reflector 来完成。 这样保证最终所有的 workload 之间的数据流量都是通过 IP 包的方式完成互联的。 Calico原理 1 Calico 节点组网可以直接利用数据中心的网络结构(支持 L2 或者 L3),不需要额外的 NAT,隧道或者 VXLAN overlay network。 1 2 如上图所示,这样保证这个方案的简单可控,而且没有封包解包,节约 CPU 计算资源的同时,提高了整个网络的性能。 此外,Calico 基于 iptables 还提供了丰富而灵活的网络 policy, 保证通过各个节点上的 ACLs 来提供 workload 的多租户隔离、安全组以及其他可达性限制等功能。 calico网络通信模型 1 2 3 4 5 calico是纯三层的SDN 实现,它基于BPG 协议和Linux自身的路由转发机制,不依赖特殊硬件,容器通信也不依赖iptables NAT或Tunnel 等技术。 能够方便的部署在物理服务器、虚拟机(如 OpenStack)或者容器环境下。同时calico自带的基于iptables的ACL管理组件非常灵活,能够满足比较复杂的安全隔离需求。 在主机网络拓扑的组织上,calico的理念与weave类似,都是在主机上启动虚拟机路由器,将每个主机作为路由器使用,组成互联互通的网络拓扑。当安装了calico的主机组成集群后, 其拓扑如下图所示: 1 2 3 4 每个主机上都部署了calico /node 作为虚拟路由器,并且可以通过calico将宿主机组织成任意的拓扑集群。当集群中的容器需要与外界通信时, 就可以通过BGP协议将网关物理路由器加入到集群中,使外界可以直接访问容器IP,而不需要做任何NAT之类的复杂操作。 当容器通过calico进行跨主机通信时,其网络通信模型如下图所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 从上图可以看出,当容器创建时,calico为容器生成veth pair,一端作为容器网卡加入到容器的网络命名空间,并设置IP和掩码,一端直接暴露在宿主机上, 并通过设置路由规则,将容器IP暴露到宿主机的通信路由上。于此同时,calico为每个主机分配了一段子网作为容器可分配的IP范围,这样就可以根据子网的 CIDR为每个主机生成比较固定的路由规则。 当容器需要跨主机通信时,主要经过下面的简单步骤: 1)容器流量通过veth pair到达宿主机的网络命名空间上。 2)根据容器要访问的IP所在的子网CIDR和主机上的路由规则,找到下一跳要到达的宿主机IP。 3)流量到达下一跳的宿主机后,根据当前宿主机上的路由规则,直接到达对端容器的veth pair插在宿主机的一端,最终进入容器。 从上面的通信过程来看,跨主机通信时,整个通信路径完全没有使用NAT或者UDP封装,性能上的损耗确实比较低。但正式由于calico的通信机制是完全基于三层的,这种机制也带来了一些缺陷,例如: 1)calico目前只支持TCP、UDP、ICMP、ICMPv6协议,如果使用其他四层协议(例如NetBIOS协议),建议使用weave、原生overlay等其他overlay网络实现。 2)基于三层实现通信,在二层上没有任何加密包装,因此只能在私有的可靠网络上使用。 3)流量隔离基于iptables实现,并且从etcd中获取需要生成的隔离规则,有一些性能上的隐患。 Calico实现Docker跨主机容器通信的示例配置说明 1)环境准备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 机器信息 node-1:182.48.115.233 centos7.2 安装Docker、etcd、calicoctl node-2:182.48.115.236 centos7.2 安装Docker、etcd、calicoctl node-3:182.48.115.239 centos7.2 安装Docker、etcd、calicoctl 设置三台机器的主机名 node-1 [root@node-1 ~] # hostnamectl --static set-hostname node-1 node-2 [root@node-2 ~] # hostnamectl --static set-hostname node-2 node-3 [root@node-3 ~] # hostnamectl --static set-hostname node-3 关闭三台主机的防火墙。若开启iptables防火墙,则需要打开2380端口通信。 [root@node-1 ~] # systemctl disable firewalld.service [root@node-1 ~] # systemctl stop firewalld.service 在三台机器上都要设置hosts,均执行如下命令: [root@node-1 ~] # vim /etc/hosts 182.48.115.233 node-1 182.48.115.236 node-2 182.48.115.239 node-3 2)安装docker环境 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 三个节点都要安装docker [root@node-1 ~] # yum install -y docker [root@node-1 ~] # systemctl start docker [root@node-1 ~] # docker pull nginx [root@node-1 ~] # docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io /nginx latest 3448f27c273f 6 days ago 109.4 MB 在三个节点上配置docker网桥,三个网桥指定为不同的网络,网络配置细节如下: node-1节点,Docker网桥网络指定为192.168.10.1 /24 [root@node-1 ~] # systemctl stop docker [root@node-1 ~] # ip link set dev docker0 down [root@node-1 ~] # brctl delbr docker0 [root@node-1 ~] # brctl addbr bridge0 [root@node-1 ~] # ip addr add 192.168.10.1/24 dev bridge0 //注意,这个192.168.10.1就是所建容器的网关地址。通过docker inspect container_id能查看到 [root@node-1 ~] # ip link set dev bridge0 up [root@node-1 ~] # ip addr show bridge0 [root@node-1 ~] # vim /etc/sysconfig/docker //即将虚拟的桥接口由默认的docker0改为bridge0 将 OPTIONS= '--selinux-enabled --log-driver=journald' 改为 OPTIONS= '--selinux-enabled --log-driver=journald -b=bridge0' // 即添加-b=bridge0 [root@node-1 ~] # systemctl restart docker [root@node-1 ~] # ifconfig //发现只有bridge0虚拟网桥,没有默认的docker0网桥了。(如果还有docker0,就再次执行"ip link set dev docker0 down && brctl delbr docker0 && systemctl restart docker) 与上面操作同理: node2节点的网桥网络指定为192.168.20.1 /24 node3节点的网桥网络指定为192.168.30.1 /24 3)安装并配置etcd(这里采用的是集群模式,建议做集群) 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 etcd 是一个分布式一致性k- v 存储系统,可用于服务注册发现与共享配置,具有以下优点: 1)简单:相比于晦涩难懂的paxos算法,etcd基于相对简单且易实现的raft算法实现一致性,并通过gRPC提供接口调用 2)安全:支持TLS通信,并可以针对不同的用户进行对key的读写控制 3)高性能:10,000/秒的写性能 etcd是一个分布式高可用的键值(key /value )存储系统,主要用于服务注册发现与共享配置,etcd 会在集群的各个节点中复制这些数据并保证这些数据始终正确。具有以下优点: 1)简单:相比于晦涩难懂的paxos算法,etcd基于相对简单且易实现的raft算法实现一致性,并通过gRPC提供接口调用 2)安全:支持TLS通信,并可以针对不同的用户进行对key的读写控制 3)高性能:10,000/秒的写性能 etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性,有基于HTTP+JSON的API接口。 Raft是一个来自Stanford的新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的方式来实现一致性,在Raft中,任何一个节点都可能成为Leader。Google的容器 集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。raft 共识算法的优点在于可以在高效的解决分布式系统中各个节点日志内容一致 性问题的同时,也使得集群具备一定的容错能力。即使集群中出现部分节点故障、网络故障等问题,仍可保证其余大多数节点正确的步进。甚至当更多的节点(一般来说超过集 群节点总数的一半)出现故障而导致集群不可用时,依然可以保证节点中的数据不会出现错误的结果。 ---------------------------------------------------------------------------------------------------------------------------------- 在三个节点机上都要安装etcd,构成etcd集群环境 (etcd下载:https: //pan .baidu.com /s/1jIsTV3C 密码:djcv) [root@node-1 ~] # curl -L https://github.com/coreos/etcd/releases/download/v2.3.6/etcd-v2.3.6-linux-amd64.tar.gz -o etcd-v2.3.6-linux-amd64.tar.gz [root@node-1 ~] # tar zxvf etcd-v2.3.6-linux-amd64.tar.gz [root@node-1 ~] # cd etcd-v2.3.6-linux-amd64/ [root@node-1 etcd-v2.3.6-linux-amd64] # 如上在三个节点机上安装好etcd后,进行etcd静态配置。 静态配置主要预先将集群的配置信息分配好,然后将集群分布启动,集群将根据配置信息组成集群。这里按如下的配置信息分别启动三个etcd。 在node-1上的etcd配置: [root@node-1 ~] # ./etcd --name node1 --initial--advertise-peer-urls http://182.48.115.233:2380 \ --listen-peer-urls http: //0 .0.0.0:2380 \ --listen-client-urls http: //0 .0.0.0:2379,http: //127 .0.0.1:4001 \ --advertise-client-urls http: //0 .0.0.0:2379 \ --initial-cluster-token etcd-cluster \ --initial-cluster node1=http: //182 .48.115.233:2380,node2=http: //182 .48.115.236:2380,node3=http: //182 .48.115.239:2380\ --initial-cluster-state new 在node-2上的etcd配置: [root@node-2 ~] # ./etcd --name node2 --initial--advertise-peer-urls http://182.48.115.236:2380 \ --listen-peer-urls http: //0 .0.0.0:2380 \ --listen-client-urls http: //0 .0.0.0:2379,http: //127 .0.0.1:4001 \ --advertise-client-urls http: //0 .0.0.0:2379 \ --initial-cluster-token etcd-cluster \ --initial-cluster node1=http: //182 .48.115.233:2380,node2=http: //182 .48.115.236:2380,node3=http: //182 .48.115.239:2380\ --initial-cluster-state new 在node-3上的etcd配置: [root@node-3 ~] # ./etcd --name node3 --initial--advertise-peer-urls http://182.48.115.239:2380 \ --listen-peer-urls http: //0 .0.0.0:2380 \ --listen-client-urls http: //0 .0.0.0:2379,http: //127 .0.0.1:4001 \ --advertise-client-urls http: //0 .0.0.0:2379 \ --initial-cluster-token etcd-cluster \ --initial-cluster node1=http: //182 .48.115.233:2380,node2=http: //182 .48.115.236:2380,node3=http: //182 .48.115.239:2380\ --initial-cluster-state new 按如上配置分别启动集群,启动集群后,将会进入集群选举状态,若出现大量超时,则需要检查主机的防火墙是否关闭,或主机之间是否能通过2380端口通信,集群建立后通过以下命令检查集群状态。 特别注意一个细节: 上面的命令执行后会一直在前台执行中,不能关闭当前终端窗口,关闭当前窗口后,上面启动的etcd集群程序就会关闭!! 正确的做法是将其切到后台执行,在上面etcd集群程序启动后,依次: 1)按键ctrl+z 将其程序挂起,会出现一个序列号,一般为1 2)执行命令: bg 1 1就是上面挂起后出现的序列号 3)执行命令disown -a 这样就将这个程序切到后台运行了 然后关闭当前程序运行的终端窗口,然后 ps -ef| grep etcd,会发现etcd的集群程序还在运行中: [root@node-1 ~] # ps -ef|grep etcd root 13138 1 1 14:33 ? 00:00:07 . /etcd -name node1 --initial--advertise-peer-urls http: //182 .48.115.233:2380 --listen-peer-urls http: //0 .0.0.0:2380 --listen-client-urls http: //0 .0.0.0:2379,http: //127 .0.0.1:4001 --advertise-client-urls http: //0 .0.0.0:2379 --initial-cluster-token etcd-cluster --initial-cluster node1=http: //182 .48.115.233:2380,node2=http: //182 .48.115.236:2380,node3=http: //182 .48.115.239:2380 --initial-cluster-state new root 13226 13186 0 14:40 pts /1 00:00:00 grep --color=auto etcd [root@node-1 ~] # lsof -i:2380 COMMAND PID USER FD TYPE DEVICE SIZE /OFF NODE NAME etcd 13138 root 5u IPv6 1827574 0t0 TCP *:2380 (LISTEN) etcd 13138 root 8u IPv6 1831276 0t0 TCP node-1:2380->node-3:60886 (ESTABLISHED) etcd 13138 root 11u IPv4 1827581 0t0 TCP node-1:49991->node-2:2380 (ESTABLISHED) 以上配置项说明( /root/etcd-v2 .3.6-linux-amd64 /etcd --help 查看帮助信息): --name etcd集群中的节点名,这里可以随意,可区分且不重复就行 ---listen-peer-urls 监听的用于节点之间通信的url,可监听多个,集群内部将通过这些url进行数据交互(如选举,数据同步等) ---initial--advertise-peer-urls 建议用于节点之间通信的url,节点间将以该值进行通信。 ---listen-client-urls 监听的用于客户端通信的url,同样可以监听多个。 ---advertise-client-urls 建议使用的客户端通信url,该值用于etcd代理或etcd成员与etcd节点通信。 ---initial-cluster-token etcd-cluster-1 节点的token值,设置该值后集群将生成唯一 id ,并为每个节点也生成唯一 id ,当使用相同配置文件再启动一个集群时,只要该token值不一样,etcd集群就不会相互影响。 ---initial-cluster 也就是集群中所有的initial--advertise-peer-urls 的合集 ---initial-cluster-state new 新建集群的标志 查看集群成员(在三个节点机任意一个上面查看都可以,因为做的是集群环境): [root@node-1 etcd-v2.3.6-linux-amd64] # ./etcdctl member list //如下,发现node1节点目前是leader 4590a741acf3f320: name=node1 peerURLs=http: //182 .48.115.233:2380 clientURLs=http: //0 .0.0.0:2379 isLeader= true 5e984e2093d56d63: name=node3 peerURLs=http: //182 .48.115.239:2380 clientURLs=http: //0 .0.0.0:2379 isLeader= false 951afc93e5278fdc: name=node2 peerURLs=http: //182 .48.115.236:2380 clientURLs=http: //0 .0.0.0:2379 isLeader= false 检查集群健康状态(在三个节点中的任意一个上面都可以查看) [root@node-1 etcd-v2.3.6-linux-amd64] # ./etcdctl cluster-health member 4590a741acf3f320 is healthy: got healthy result from http: //0 .0.0.0:2379 member 5e984e2093d56d63 is healthy: got healthy result from http: //0 .0.0.0:2379 member 951afc93e5278fdc is healthy: got healthy result from http: //0 .0.0.0:2379 cluster is healthy 如上集群搭建成功。 4)启动Calico服务(这是安装最新版的做法,新版和老版的命令已有所改变) 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 首先在三个节点机上下载calico的命令管理工具calicoctl(百度云下载地址:https: //pan .baidu.com /s/1pKKTGbL 密码:r7sm) [root@node-1 ~] # wget http://www.projectcalico.org/builds/calicoctl [root@node-1 ~] # chmod +x calicoctl [root@node-1 ~] # mv calicoctl /usr/local/bin/ [root@node-1 ~] # calicoctl --help //查看帮助信息 启动Calico服务 在Docker环境中Calico服务是做为容器来运行的,使用host的网络配置。所有容器配置使用Calico服务,做为calico节点互相通信。 Calico在每个主机上通过一个自己的container与其他主机或者网络通讯,即calico-node的container,这个container里面包含了Bird路由管理、Felix协议等。 千万别忘了在三个节点上都要下载calico的node镜像 (可以先在一个节点上下载镜像,然后将镜像通过docker save导出保存到本地,再将镜像拷贝到其他节点上通过docker load导入,这样对于其他节点来说,比使用docker pull要快) [root@node-1 ~] # docker pull calico/node [root@node-1 ~] # docker pull calico/node-libnetwork [root@node-1 ~] # docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io /calico/node latest 1e0928760e74 11 hours ago 255.2 MB docker.io /nginx latest 3448f27c273f 6 days ago 109.4 MB docker.io /calico/node-libnetwork latest 84d99cab9fc4 7 months ago 70.2 MB 下面分别在三个节点上,以Docker方式启动calico-node(这个命令会执行一段时间,耐心等待......) node-1 [root@node-1 ~] # calicoctl node run --ip=182.48.115.233 Running command to load modules: modprobe -a xt_set ip6_tables Enabling IPv4 forwarding ....... Using node name: node-1 Starting libnetwork service Calico node started successfully node-2 [root@node-2 ~] # calicoctl node run --ip=182.48.115.236 node-3 [root@node-3 ~] # calicoctl node run --ip=182.48.115.239 可以在三个节点上查看calico-node启动情况 [root@node-1 ~] # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2ac613b1af9a quay.io /calico/node :latest "start_runit" 22 seconds ago Up 17 seconds calico-node [root@node-1 ~] # ps -ef|grep calico root 14339 14336 0 15:43 ? 00:00:00 svlogd -tt /var/log/calico/bird6 root 14340 14336 0 15:43 ? 00:00:00 bird6 -R -s /var/run/calico/bird6 .ctl -d -c /etc/calico/confd/config/bird6 .cfg root 14341 14337 0 15:43 ? 00:00:00 svlogd /var/log/calico/confd root 14342 14337 0 15:43 ? 00:00:00 confd -confdir= /etc/calico/confd -interval=5 - watch -no-discover --log-level=debug -node=http: //127 .0.0.1:2379 -client-key= -client-cert= -client-ca-keys= root 14343 14334 0 15:43 ? 00:00:00 svlogd /var/log/calico/felix root 14344 14334 2 15:43 ? 00:00:03 calico-felix root 14346 14338 0 15:43 ? 00:00:00 svlogd /var/log/calico/libnetwork root 14349 14335 0 15:43 ? 00:00:00 svlogd -tt /var/log/calico/bird root 14350 14335 0 15:43 ? 00:00:00 bird -R -s /var/run/calico/bird .ctl -d -c /etc/calico/confd/config/bird .cfg root 14597 13854 0 15:45 pts /2 00:00:00 grep --color=auto calico 查看节点状态信息(在三个节点上都可以查看) [root@node-1 ~] # calicoctl node status Calico process is running. IPv4 BGP status +----------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +----------------+-------------------+-------+----------+-------------+ | 182.48.115.236 | node-to-node mesh | up | 07:47:20 | Established | | 182.48.115.239 | node-to-node mesh | up | 07:52:55 | Established | +----------------+-------------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found. [root@node-2 ~] # calicoctl node status Calico process is running. IPv4 BGP status +----------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +----------------+-------------------+-------+----------+-------------+ | 182.48.115.233 | node-to-node mesh | up | 07:46:19 | Established | | 182.48.115.239 | node-to-node mesh | up | 07:51:54 | Established | +----------------+-------------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found. [root@node-3 ~] # calicoctl node status Calico process is running. IPv4 BGP status +----------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +----------------+-------------------+-------+----------+-------------+ | 182.48.115.233 | node-to-node mesh | up | 09:51:54 | Established | | 182.48.115.236 | node-to-node mesh | up | 09:51:54 | Established | +----------------+-------------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found. 使用calicoctl创建ipPool 在启动别的容器之前,我们需要配置一个IP地址池带有ipip和nat-outgoing选项。所以带有有效配置的容器就可以访问互联网,在每个节点上运行下面的命令: 先查看calico的ip池(任意一个节点上都能查看) [root@node-1 ~] # calicoctl get ipPool CIDR 192.168.0.0 /16 fd80:24e2:f998:72d6:: /64 [root@node-1 ~] # calicoctl get ippool -o wide CIDR NAT IPIP 192.168.0.0 /16 true false fd80:24e2:f998:72d6:: /64 true false 上面查出的192.168.0.0 /16 是calico默认的网络。 可以使用命令 "calicoctl delete ippool 192.168.0.0/16" 删除calico默认的网络,这是非必要操作,可以保留calico默认的网络! 现在开始在三个节点机器上使用calicoctl创建ipPool 创建ip pool首先定义一个资源文件ipPool.yaml,如下: node-1节点机器上 [root@node-1 ~] # vim ipPool.yaml apiVersion: v1 kind: ipPool metadata: cidr: 192.168.10.1 /24 spec: ipip: enabled: true nat-outgoing: true disabled: false [root@node-1 ~] # calicoctl create -f ipPool.yaml Successfully created 1 'ipPool' resource(s) [root@node-1 ~] # calicoctl get ippool -o wide CIDR NAT IPIP 192.168.0.0 /16 true false 192.168.10.1 /24 true true // 两个 true ,说明使用了IPIP fd80:24e2:f998:72d6:: /64 true false -------------------------------------------------------------------------------- 上面在创建ipPool的时候,使用了IPIP。也可以选择不使用IPIP,如下:(这里我选择的是使用IPIP) [root@node-1 ~] # vim ipPool.yaml apiVersion: v1 kind: ipPool metadata: cidr: 192.168.10.1 /24 spec: ipip: enabled: false nat-outgoing: true disabled: false ------------------------------------------------------------------------------- 同理,node-2节点机器上 [root@node-2 ~] # vim ipPool.yaml apiVersion: v1 kind: ipPool metadata: cidr: 192.168.20.1 /24 spec: ipip: enabled: true nat-outgoing: true disabled: false [root@node-2 ~] # calicoctl create -f ipPool.yaml Successfully created 1 'ipPool' resource(s) [root@node-2 ~] # calicoctl get ippool -o wide CIDR NAT IPIP 192.168.0.0 /16 true false 192.168.10.1 /24 true true 192.168.20.1 /24 true true fd80:24e2:f998:72d6:: /64 true false node-3节点机器上 [root@node-3 ~] # vim ipPool.yaml apiVersion: v1 kind: ipPool metadata: cidr: 192.168.30.1 /24 spec: ipip: enabled: true nat-outgoing: true disabled: false [root@node-3 ~] # calicoctl create -f ipPool.yaml Successfully created 1 'ipPool' resource(s) [root@node-3 ~] # calicoctl get ippool -o wide CIDR NAT IPIP 192.168.0.0 /16 true false 192.168.10.1 /24 true true 192.168.20.1 /24 true true 192.168.30.1 /24 true true fd80:24e2:f998:72d6:: /64 true false 由上面可以看出,当三个节点都创建了ipPool后,再次查看calico的ip池,就会发现三个节点的Docker网桥网络ip都显示出来了 3)容器网络配置 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 78 79 80 在3个节点上启动容器 ------------node-1节点上创建容器-------------- [root@node-1 ~] # docker run -itd --name=my-test1 docker.io/nginx 0f09da38cb1a9b0155bcb1ed9fe8192676355e06ed474d411fc31f1207a20a63 [root@node-1 ~] # docker exec -ti my-test1 /bin/bash //登陆my-test1容器,发现容器ip是安装指定的docker网桥网络分配的 root@0f09da38cb1a:/ # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1 /8 scope host lo valid_lft forever preferred_lft forever inet6 ::1 /128 scope host valid_lft forever preferred_lft forever 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default link /ipip 0.0.0.0 brd 0.0.0.0 2964: eth0@if2965: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:c0:a8:0a:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.10.2 /24 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:c0ff:fea8:a02 /64 scope link valid_lft forever preferred_lft forever ------------node-2节点上创建容器-------------- [root@node-2 ~] # docker run -itd --name=my-test2 docker.io/nginx 707a1e64a3072b1c49dad3009e63b7307ab725fa43147c1ad3c809f5214c761b [root@node-2 ~] # docker exec -ti my-test2 /bin/bash root@707a1e64a307:/ # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1 /8 scope host lo valid_lft forever preferred_lft forever inet6 ::1 /128 scope host valid_lft forever preferred_lft forever 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1 link /ipip 0.0.0.0 brd 0.0.0.0 15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:c0:a8:14:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.20.2 /24 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:c0ff:fea8:1402 /64 scope link valid_lft forever preferred_lft forever ------------node-3节点上创建容器-------------- [root@node-3 ~] # docker run -itd --name=my-test3 docker.io/nginx d52d18d9e1eb0e309ece7cc20c76654c195db5aee609cb3a2075dfcb1aff2425 [root@node-3 ~] # docker exec -ti my-test3 /bin/bash root@d52d18d9e1eb:/ # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link /loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1 /8 scope host lo valid_lft forever preferred_lft forever inet6 ::1 /128 scope host valid_lft forever preferred_lft forever 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default link /ipip 0.0.0.0 brd 0.0.0.0 39: eth0@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link /ether 02:42:c0:a8:1e:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.30.2 /24 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:c0ff:fea8:1e02 /64 scope link valid_lft forever preferred_lft forever root@d52d18d9e1eb:/ # ping 192.168.10.2 PING 192.168.10.2 (192.168.10.2): 56 data bytes 64 bytes from 192.168.10.2: icmp_seq=0 ttl=62 time =0.799 ms ^C--- 192.168.10.2 ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min /avg/max/stddev = 0.799 /0 .799 /0 .799 /0 .000 ms root@d52d18d9e1eb:/ # ping 192.168.20.2 PING 192.168.20.2 (192.168.20.2): 56 data bytes 64 bytes from 192.168.20.2: icmp_seq=0 ttl=62 time =1.021 ms ^C--- 192.168.20.2 ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min /avg/max/stddev = 1.021 /1 .021 /1 .021 /0 .000 ms 可以发现,在三个节点上创建的容器都是按照指定的docker网桥网络分配的ip,并且各节点机上的容器间的ip是可以相互 ping 通的! 除此之外: 1)容器可以访问外网(默认采用的是bridge网桥模式,只要宿主机能访问外网,容器也就可以)。 2)宿主机可以 ping 通任意一个节点上的容器ip 3)容器可以 ping 通任意一个节点ip ***************当你发现自己的才华撑不起野心时,就请安静下来学习吧*************** 本文转自散尽浮华博客园博客,原文链接:http://www.cnblogs.com/kevingrace/p/6864804.html,如需转载请自行联系原作者

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

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等操作系统。

用户登录
用户注册