首页 文章 精选 留言 我的

精选列表

搜索[卡顿],共9806篇文章
优秀的个人博客,低调大师

Python运用蒙特洛算法模拟植物生长

(细胞二次分裂呈现对称分布) 细胞到生物、胚胎生长曲线、发展模式是随意形成的吗?为什么大多数人都是两只眼睛,很少出现三眼神童?我相信分形数学的进化一定会重新揭开生命的秘密。就像一把开启潘多拉的魔盒的钥匙,也许有天我们的宇宙、DNA组成结构,一个智慧生命社会的生长、演化、发展和兴衰均可以由一个或者数个简洁流畅的数学公式进行模拟和预言,我们现在就可以是一个正在不断迭代计算的公式呢?亚历山大图书馆和研究院的建立,从古代至今人们不断努力试图对宇宙万物表面看似毫无规律可循的各种形态的随机性做概括。隐匿在表面下的密码。科幻小说中调侃的“宇宙终极答案是43”不再是个玩笑。 (达芬奇人体图对称分布) 细胞二次分裂和人体呈现对称分布,但山川,水浪,植物形态属于非对称分布,是否有事物不遵循数学法则?其实万物皆有数,生命发展是遵循一定规律的,可称之为算法。 下

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

CentOS修改eth0网的名字

当虚拟机复制粘贴后,网卡名,就变成了ens33这个网卡名称了,所以想改成eth0的网卡名称,主要步骤有1、新建网卡配置脚本(ifcfg-eth0)2、修改grub 3、重新建立grub 4、重启网卡,虽然在这过程中,可能还会遇到网卡没有网的情况,那就菊花疼了 复制虚拟机后的网卡名称 网卡名称需要修改为eth0格式的 修改网卡脚本 到网卡配置目录,我看到了,我的 网卡配置脚本是ifcfg-ens33,原来的配置文件的网卡 的脚本, cd /etc/sysconfig/network-scripts/ ls #查看网卡配置的脚本 DEVICE和NAME还是以前的,需要修改为我们该成的网卡eth0,如果更改不了文件,需要切换为管理员,然后打开文件 su root 复制网卡文件到ifcfg-eth0 复制网卡配置文件 #移动操作 mv /etc/sysconfig/network-scripts/ifcfg-enp0s3 /etc/sysconfig/network-scripts/ifcfg-eth0 #复制 cp /etc/sysconfig/network-scripts/ifcfg-enp0s3 /etc/sysconfig/network-scripts/ifcfg-eth0 修改grub文件 修改grub配置文件,在rhgb quiet中添加:net.ifnames=0 biosdevname=0 vi /etc/default/grub 修改后的文件 重新建立grub文件 grub2-mkconfig -o /boot/grub2/grub.cfg 重启服务器 #重启服务器 reboot #查看网卡信息 ifconfig -a 本宝宝的网卡配置有问题啊 配置动态获取ip TYPE="Ethernet" BOOTPROTO="dhcp" DEFROUTE="yes" IPV4_FAILURE_FATAL="no" IPV6INIT="yes" IPV6_AUTOCONF="yes" IPV6_DEFROUTE="yes" IPV6_FAILURE_FATAL="no" NAME="eth0" UUID="30441465-3d49-4265-80ee-298c3f246497" DEVICE="eth0" ONBOOT="yes" PEERDNS="yes" PEERROUTES="yes" IPV6_PEERDNS="yes" IPV6_PEERROUTES="yes" IPV6_PRIVACY="no" 重启网卡 #重启网卡 service network restart #当出现了下面失败的信息,需要查看日志 cat /var/log/messages | grep network 无法获取到链接 错误问题 是没法获取到ip,所以可能是我的NAT配置错误了 还原虚拟机NAT 还原 网卡配置信息 网卡加载好了,不过淡腾的是还是没能访问外网,菊花藤啊

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

android用户界面-组件Widget-选项Tab

使用Tab的步骤: 1、在布局文件中使用FrameLayout列出Tab组件及Tab中的内容组件。 2、Activity要继承TabActivity。 3、调用TabActivity的getTabHost()方法获得TabHost对象。 4、通过TabHost创建Tab选项。 /Chapter04_UI_Tab01/src/com/amaker/test/MainActivity.java 代码 packagecom.amaker.test; importandroid.app.TabActivity; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.Window; importandroid.view.WindowManager; importandroid.widget.TabHost; importandroid.widget.Toast; importandroid.widget.TabHost.OnTabChangeListener; publicclassMainActivityextendsTabActivity{ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); /*requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);*/ TabHostth=getTabHost(); LayoutInflater.from(this).inflate(R.layout.main,th.getTabContentView(),true); th.addTab(th.newTabSpec("all").setIndicator("所有通话记录").setContent(R.id.TextView01)); th.addTab(th.newTabSpec("ok").setIndicator("已接来电").setContent(R.id.TextView02)); th.addTab(th.newTabSpec("cancel").setIndicator("未接来电").setContent(R.id.TextView03)); th.setOnTabChangedListener( newOnTabChangeListener(){ @Override publicvoidonTabChanged(StringtabId){ Toast.makeText(MainActivity.this,tabId,Toast.LENGTH_LONG).show(); } } ); } } /Chapter04_UI_Tab01/res/layout/main.xml 代码 <?xmlversion="1.0"encoding="utf-8"?> <FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/FrameLayout01" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="所有通话记录"></TextView> <TextView android:id="@+id/TextView02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="已接来电"></TextView> <TextView android:id="@+id/TextView03" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="未接来电"></TextView> </FrameLayout> 通过实现一个接口TabHost.TabContentFactory的createTabContent方法来制定Tab的内容 /Chapter04_UI_Tab02/src/com/amaker/test/MainActivity.java 代码 packagecom.amaker.test; importjava.util.ArrayList; importjava.util.List; importandroid.app.TabActivity; importandroid.os.Bundle; importandroid.view.View; importandroid.widget.ArrayAdapter; importandroid.widget.ListView; importandroid.widget.TabHost; publicclassMainActivityextendsTabActivityimplements TabHost.TabContentFactory{ /**Calledwhentheactivityisfirstcreated.*/ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); TabHostth=getTabHost(); th.addTab(th.newTabSpec("all").setIndicator("所有通话记录").setContent(this)); th.addTab(th.newTabSpec("ok").setIndicator("已接来电").setContent(this)); th .addTab(th.newTabSpec("cancel").setIndicator("未接来电") .setContent(this)); } publicViewcreateTabContent(Stringtag){ ListViewlv=newListView(this); List<String>list=newArrayList<String>(); list.add(tag); if(tag.equals("all")){ list.add("tom"); list.add("kite"); list.add("rose"); }elseif(tag.equals("ok")){ list.add("tom"); list.add("kite"); }else{ list.add("rose"); } ArrayAdapteradapter=newArrayAdapter(this, android.R.layout.simple_list_item_checked,list); lv.setAdapter(adapter); returnlv; } } /Chapter04_UI_Tab02/res/layout/main.xml 代码 <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout> 本文转自linzheng 51CTO博客,原文链接:http://blog.51cto.com/linzheng/1080696

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

Android -- 系统信息(内存、cpu、sd、电量、版本)获取

内存(ram) android的总内存大小信息存放在系统的/proc/meminfo文件里面,可以通过读取这个文件来获取这些信息: public void getTotalMemory() { String str1 = "/proc/meminfo"; String str2=""; try { FileReader fr = new FileReader(str1); BufferedReader localBufferedReader = new BufferedReader(fr, 8192); while ((str2 = localBufferedReader.readLine()) != null) { Log.i(TAG, "---" + str2); } } catch (IOException e) { } } 运行信息如下: 05-30 08:05:14.807: INFO/-SystemInfo-(1519): ---MemTotal: 204876 kB 05-30 08:05:14.807: INFO/-SystemInfo-(1519): ---MemFree: 4596 kB 05-30 08:05:14.807: INFO/-SystemInfo-(1519): ---Buffers: 16020 kB 05-30 08:05:14.807: INFO/-SystemInfo-(1519): ---Cached: 82508 kB 05-30 08:05:14.807: INFO/-SystemInfo-(1519): ---SwapCached: 64 kB 05-30 08:05:14.807: INFO/-SystemInfo-(1519): ---Active: 137104 kB 05-30 08:05:14.807: INFO/-SystemInfo-(1519): ---Inactive: 41056 kB 05-30 08:05:14.807: INFO/-SystemInfo-(1519): ---SwapTotal: 65528 kB 05-30 08:05:14.817: INFO/-SystemInfo-(1519): ---SwapFree: 65368 kB 05-30 08:05:14.817: INFO/-SystemInfo-(1519): ---Dirty: 88 kB 05-30 08:05:14.817: INFO/-SystemInfo-(1519): ---Writeback: 0 kB 05-30 08:05:14.817: INFO/-SystemInfo-(1519): ---AnonPages: 79672 kB 05-30 08:05:14.817: INFO/-SystemInfo-(1519): ---Mapped: 38296 kB 05-30 08:05:14.817: INFO/-SystemInfo-(1519): ---Slab: 5768 kB 05-30 08:05:14.817: INFO/-SystemInfo-(1519): ---SReclaimable: 1856 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---SUnreclaim: 3912 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---PageTables: 8184 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---NFS_Unstable: 0 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---Bounce: 0 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---CommitLimit: 167964 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---Committed_AS: 11771920 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---VmallocTotal: 761856 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---VmallocUsed: 83656 kB 05-30 08:05:14.827: INFO/-SystemInfo-(1519): ---VmallocChunk: 674820 kB 第一行是总内存大小(即用户可以使用的ram的大小)! 获取当前剩余内存(ram)大小的方法: public long getAvailMemory() { ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); am.getMemoryInfo(mi); return mi.availMem; } Rom大小 public long[] getRomMemroy() { long[] romInfo = new long[2]; //Total rom memory romInfo[0] = getTotalInternalMemorySize(); //Available rom memory File path = Environment.getDataDirectory(); StatFs stat = new StatFs(path.getPath()); long blockSize = stat.getBlockSize(); long availableBlocks = stat.getAvailableBlocks(); romInfo[1] = blockSize * availableBlocks; getVersion(); return romInfo; } public long getTotalInternalMemorySize() { File path = Environment.getDataDirectory(); StatFs stat = new StatFs(path.getPath()); long blockSize = stat.getBlockSize(); long totalBlocks = stat.getBlockCount(); return totalBlocks * blockSize; } 注意类型,不然相乘之后会有溢出。可用内部存储的大小不能通过getRootDirectory();取得,之前网上传的很多都是用getRootDirectory()取得的,我测试之后发现取得的数值不对。要根据getDataDirectory();取得。 SDcard大小 public long[] getSDCardMemory() { long[] sdCardInfo=new long[2]; String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { File sdcardDir = Environment.getExternalStorageDirectory(); StatFs sf = new StatFs(sdcardDir.getPath()); long bSize = sf.getBlockSize(); long bCount = sf.getBlockCount(); long availBlocks = sf.getAvailableBlocks(); sdCardInfo[0] = bSize * bCount;//总大小 sdCardInfo[1] = bSize * availBlocks;//可用大小 } return sdCardInfo; } 注意类型,不然相乘之后会有溢出。 电池电量 private BroadcastReceiver batteryReceiver=new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { int level = intent.getIntExtra("level", 0); // level加%就是当前电量了 } }; 然后在activity的oncreate()方法中注册 registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); CPU信息 public String[] getCpuInfo() { String str1 = "/proc/cpuinfo"; String str2=""; String[] cpuInfo={"",""}; String[] arrayOfString; try { FileReader fr = new FileReader(str1); BufferedReader localBufferedReader = new BufferedReader(fr, 8192); str2 = localBufferedReader.readLine(); arrayOfString = str2.split("\\s+"); for (int i = 2; i < arrayOfString.length; i++) { cpuInfo[0] = cpuInfo[0] + arrayOfString[i] + " "; } str2 = localBufferedReader.readLine(); arrayOfString = str2.split("\\s+"); cpuInfo[1] += arrayOfString[2]; localBufferedReader.close(); } catch (IOException e) { } return cpuInfo; } /proc/cpuinfo文件中第一行是CPU的型号,第二行是CPU的频率,可以通过读文件,读取这些数据! 系统的版本信息 public String[] getVersion(){ String[] version={"null","null","null","null"}; String str1 = "/proc/version"; String str2; String[] arrayOfString; try { FileReader localFileReader = new FileReader(str1); BufferedReader localBufferedReader = new BufferedReader( localFileReader, 8192); str2 = localBufferedReader.readLine(); arrayOfString = str2.split("\\s+"); version[0]=arrayOfString[2];//KernelVersion localBufferedReader.close(); } catch (IOException e) { } version[1] = Build.VERSION.RELEASE;// firmware version version[2]=Build.MODEL;//model version[3]=Build.DISPLAY;//system version return version; } 版本信息里面还包括型号等信息。 MAC地址和开机时间 public String[] getOtherInfo(){ String[] other={"null","null"}; WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); WifiInfo wifiInfo = wifiManager.getConnectionInfo(); if(wifiInfo.getMacAddress()!=null){ other[0]=wifiInfo.getMacAddress(); } else { other[0] = "Fail"; } other[1] = getTimes(); return other; } private String getTimes() { long ut = SystemClock.elapsedRealtime() / 1000; if (ut == 0) { ut = 1; } int m = (int) ((ut / 60) % 60); int h = (int) ((ut / 3600)); return h + " " + mContext.getString(R.string.info_times_hour) + m + " " + mContext.getString(R.string.info_times_minute); } 我是天王盖地虎的分割线 本文转自我爱物联网博客园博客,原文链接:http://www.cnblogs.com/yydcdut/p/3851716.html,如需转载请自行联系原作者

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

操作猛如虎解决了

事故起因 业务上新集群,本来以为"洒洒水",11 点切,12 点就能在家睡觉了。流量切过来后,在验证过程中,发现网页能够正常打开,在登录时返回了 502,当场懵逼。在相关的容器日志发现一个高频的报错条目“7000 端口无法连接”,向业务组了解到这是 redis 集群中的一个端口,前后端是通过 redis 交互的,该集群同时还有 7001-7003 其它三个端口。 用 nc 命令对 redis 集群进行连接测试:向服务端发送 keys * 命令时,7000 端口返回的是 HTTP/1.1 400 Bad Request,其他三个端口是 redis 返回的 -NOAUTH Authentication required。 $ nc 10.0.0.6 7000 keys * HTTP/1.1 400 Bad Request content-length: 0 connection: close $ nc 10.0.0.6 7003 keys * -NOAUTH Authentication required 判断 7000 端口连接到了其他应用上,至少不是 redis。在宿主机上抓包发现没有抓到访问 7000 端口的流量,然后查看容器的 nf_conntrackb 表,发现 7000 端口的数据只有到本地的会话信息;7003 的有两条会话信息,一条到本机的,一条到目标服务器的。 $ grep 7000 /proc/net/nf_conntrack ipv4 2 tcp 6 110 TIME_WAIT src=10.64.192.14 dst=10.0.0.6 sport=50498 dport=7000 src=127.0.0.1 dst=10.64.192.14 sport=15001 dport=50498 [ASSURED] mark=0 zone=0 use=2 $ grep 7003 /proc/net/nf_conntrack ipv4 2 tcp 6 104 TIME_WAIT src=10.64.192.14 dst=10.0.0.6 sport=38952 dport=7003 src=127.0.0.1 dst=10.64.192.14 sport=15001 dport=38952 [ASSURED] mark=0 zone=0 use=2 ipv4 2 tcp 6 104 TIME_WAIT src=10.64.192.14 dst=10.0.0.6 sport=38954 dport=7003 src=10.0.0.6 dst=10.64.192.14 sport=7003 dport=38954 [ASSURED] mark=0 zone=0 use=2 由此判断出 istio 没有代理转发出 7000 的流量,这突然就触及到了我的知识盲区,一大堆人看着,办公室 26 度的空调,一直在冒汗。没办法了,在与业务商量后,只能先关闭 istio 注入,优先恢复了业务。回去后恶补 istio 的相关资料。终于将问题解决。记录下相关信息,以供日后参考。 背景知识补充 istio Sidecar 的模式 istio 的 Sidecar 有两种模式: ALLOW_ANY:istio 代理允许调用未知的服务,黑名单模式。 REGISTRY_ONLY:istio 代理会阻止任何没有在网格中定义的 HTTP 服务或 service entry 的主机,白名单模式。 istio-proxy(Envoy)的配置结构 istio-proxy(Envoy)的代理信息大体由以下几个部分组成: Cluster:在 Envoy 中,Cluster 是一个服务集群,Cluster 中包含一个到多个 endpoint,每个 endpoint 都可以提供服务,Envoy 根据负载均衡算法将请求发送到这些 endpoint 中。cluster 分为 inbound 和 outbound 两种,前者对应 Envoy 所在节点上的服务;后者占了绝大多数,对应 Envoy 所在节点的外部服务。可以使用如下方式分别查看 inbound 和 outbound 的 cluster。 Listeners:Envoy 采用 listener 来接收并处理 downstream 发过来的请求,可以直接与 Cluster 关联,也可以通过 rds 配置路由规则(Routes),然后在路由规则中再根据不同的请求目的地对请求进行精细化的处理。 Routes:配置 Envoy 的路由规则。istio 下发的缺省路由规则中对每个端口(服务)设置了一个路由规则,根据 host 来对请求进行路由分发,routes 的目的为其他服务的 cluster。 Endpoint:cludter 对应的后端服务,可以通过 istio pc endpoint 查看 inbound 和 outbound 对应的 endpoint 信息。 服务发现类型 cluster 的服务发现类型主要有: ORIGINAL_DST:类型的 Cluster,Envoy 在转发请求时会直接采用 downstream 请求中的原始目的地 IP 地址 EDS:EDS 获取到该 Cluster 中所有可用的 Endpoint,并根据负载均衡算法(缺省为 Round Robin)将 Downstream 发来的请求发送到不同的 Endpoint。istio 会自动为集群中的 service 创建代理信息,listener 的信息从 service 获取,对应的 cluster 被标记为 EDS 类型 STATIC:缺省值,在集群中列出所有可代理的主机 Endpoints。当没有内容为空时,不进行转发。 LOGICAL_DNS:Envoy 使用 DNS 添加主机,但如果 DNS 不再返回时,也不会丢弃。 STRICT_DNS:Envoy 将监控 DNS,而每个匹配的 A 记录都将被认为是有效的。 两个特殊集群 BlackHoleCluster:黑洞集群,匹配此集群的流量将被不会被转发。 { "name": "BlackHoleCluster", "type": "STATIC", "connectTimeout": "10s" } 类型为 static,但是没有指定可代理的 Endpoint,所以流量不会被转发。 PassthroughCluster:透传集群,匹配此集群的流量数据包的目的 IP 不会改变。 { "name": "PassthroughCluster", "type": "ORIGINAL_DST", "connectTimeout": "10s", "lbPolicy": "CLUSTER_PROVIDED", "circuitBreakers": { "thresholds": [ { "maxConnections": 4294967295, "maxPendingRequests": 4294967295, "maxRequests": 4294967295, "maxRetries": 4294967295 } ] } 类型为 original_dst,流量将原样转发。 一个特殊的 Listener istio 中有一个特殊的 Listener 叫 virtualOutbound,定义如下: virtualOutbound:每个 Sidecar 都有一个绑定到 0.0.0.0:15001 的 listener,该 listener 下关联了许多 virtual listener。iptables 会先将所有出站流量导入该 listener,该 listener 有一个字段 useOriginalDst 设置为 true,表示会使用最佳匹配原始目的地的方式将请求分发到 virtual listener,如果没有找到任何 virtual listener,将会直接发送到数据包原目的地的 PassthroughCluster。 useOriginalDst 字段的具体意义是,如果使用 iptables 重定向连接,则代理接收流量的目标地址可能与原始目标地址不同。当此标志设置为 true 时,侦听器会将重定向流量转交给与原始目标地址关联的侦听器。如果没有与原始目标地址关联的侦听器,则流量由接收它的侦听器处理。默认为 false。 virtualOutbound 的流量处理流程如图所示: 这是 virtualOutbound 的部分配置: { "name": "envoy.tcp_proxy", "typedConfig": { "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", "statPrefix": "PassthroughCluster", "cluster": "PassthroughCluster" } } …………… "useOriginalDst": true istio 的 outbond 流量处理 开启流量治理后,pod 访问外部资源的流量转发路径如图所示: istio 注入后 istio-proxy 有一个监听在 15001 的端口,所有非 istio-proxy 用户进程产生的 outbond 流量,通过 iptables 规则被重定向到 15001。 # Sidecar 注入的 pod 监听的端口 $ ss -tulnp State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:80 *:* LISTEN 0 128 *:15090 *:* LISTEN 0 128 127.0.0.1:15000 *:* LISTEN 0 128 *:15001 *:* LISTEN 0 128 *:15006 *:* LISTEN 0 128 [::]:15020 [::]:* # Pod 内部的 iptables 表项 $ iptables-save # Generated by iptables-save v1.4.21 on Fri Sep 17 13:47:09 2021 *nat :PREROUTING ACCEPT [129886:7793160] :INPUT ACCEPT [181806:10908360] :OUTPUT ACCEPT [53409:3257359] :POSTROUTING ACCEPT [53472:3261139] :istio_INBOUND - [0:0] :istio_IN_REDIRECT - [0:0] :istio_OUTPUT - [0:0] :istio_REDIRECT - [0:0] -A PREROUTING -p tcp -j istio_INBOUND -A OUTPUT -p tcp -j istio_OUTPUT -A istio_INBOUND -p tcp -m tcp --dport 22 -j RETURN -A istio_INBOUND -p tcp -m tcp --dport 15020 -j RETURN -A istio_INBOUND -p tcp -j istio_IN_REDIRECT -A istio_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006 -A istio_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN -A istio_OUTPUT ! -d 127.0.0.1/32 -o lo -j istio_IN_REDIRECT -A istio_OUTPUT -m owner --uid-owner 1337 -j RETURN -A istio_OUTPUT -m owner --gid-owner 1337 -j RETURN -A istio_OUTPUT -d 127.0.0.1/32 -j RETURN -A istio_OUTPUT -j istio_REDIRECT -A istio_REDIRECT -p tcp -j REDIRECT --to-ports 15001 COMMIT # Completed on Fri Sep 17 13:47:09 2021 istio-proxy 收到流量后,大致的处理步骤如下: Proxy 在 ALLOW_ANY 模式下没有匹配上 listener 将被直接转发 listener 关联了 type 为 ORIGINAL_DST 的 cluster 将使用原始请求种的 IP 地址 匹配上了 BlackHoleCluster,将不会被转发 被代理流量的匹配步骤大致如下: 疑问:isito 为 svc 创建的 listener 地址是全零的,集群内部的端口是会存在复用的,那 istio 到底是怎么区分流量的呢? 关键就在于 route,route 由 virtual_host 条目组成,这些 virtual_host 条目就是根据 svc 的信息生成的,访问集群内部的 svc 时,在 route 里可以根据域名或者 svc 对应的 virtual_ip 进行精确匹配,所以完全不需要担心啦。 $ kubectl get svc -A | grep 8001 NodePort 10.233.34.158 <none> 8001:30333/TCP 8d NodePort 10.233.9.105 <none> 8001:31717/TCP 8d NodePort 10.233.60.59 <none> 8001:31135/TCP 2d16h NodePort 10.233.18.212 <none> 8001:32407/TCP 8d NodePort 10.233.15.5 <none> 8001:30079/TCP 8d NodePort 10.233.59.21 <none> 8001:31103/TCP 8d NodePort 10.233.17.123 <none> 8001:31786/TCP 8d NodePort 10.233.9.196 <none> 8001:32662/TCP 8d NodePort 10.233.62.85 <none> 8001:32104/TCP 8d ClusterIP 10.233.49.245 <none> 8000/TCP,8001/TCP,8443/TCP,8444/TCP 这是 route 下的 virtual_host 条目: { "name": "8001", "virtualHosts": [ { "name": "merchant-center.open.svc.cluster.local:8001", "domains": [ "merchant-center.open.svc.cluster.local", "merchant-center.open.svc.cluster.local:8001", "merchant-center.open", "merchant-center.open:8001", "merchant-center.open.svc.cluster", "merchant-center.open.svc.cluster:8001", "merchant-center.open.svc", "merchant-center.open.svc:8001", "10.233.60.59", "10.233.60.59:8001" ], "routes": [ { "name": "default", "match": { "prefix": "/" }, "route": { "cluster": "outbound|8001||merchant-center.open.svc.cluster.local", "timeout": "0s", "retryPolicy": { "retryOn": "connect-failure,refused-stream,unavailable,cancelled,resource-exhausted,retriable-status-codes", "numRetries": 2, "retryHostPredicate": [ { "name": "envoy.retry_host_predicates.previous_hosts" } ], "hostSelectionRetryMaxAttempts": "5", "retriableStatusCodes": [ 503 ] }, "maxGrpcTimeout": "0s" }, ………………… { "name": "cashier-busi-svc.pay.svc.cluster.local:8001", "domains": [ "cashier-busi-svc.pay.svc.cluster.local", "cashier-busi-svc.pay.svc.cluster.local:8001", "cashier-busi-svc.pay", "cashier-busi-svc.pay:8001", "cashier-busi-svc.pay.svc.cluster", "cashier-busi-svc.pay.svc.cluster:8001", "cashier-busi-svc.pay.svc", "cashier-busi-svc.pay.svc:8001", "10.233.17.123", "10.233.17.123:8001" ], ………………… { "name": "center-job.manager.svc.cluster.local:8001", "domains": [ "center-job.manager.svc.cluster.local", "center-job.manager.svc.cluster.local:8001", "center-job.manager", "center-job.manager:8001", "center-job.manager.svc.cluster", "center-job.manager.svc.cluster:8001", "center-job.manager.svc", "center-job.manager.svc:8001", "10.233.34.158", "10.233.34.158:8001" ], …………… 问题分析 基于以上信息,对集群内的 svc 进行端口过滤,终于发现了集群中存在使用了 7000 端口的 service: istio 会为 10.233.0.115:7000 自动生成一个 0.0.0.0:7000 的 listener: ADDRESS PORT TYPE 0.0.0.0 7000 TCP 查看详细配置信息,在该 listener 中对于 tcp 流量是不转发(BlackHoleCluster),所以目标地址为 10.0.x.x:7000 的流量被 listener_0.0.0.0:7000 匹配到时,因为是 tcp 的流量(nc 命令默认 tcp 协议),所以代理没有对该流量进行转发。这与开头提到的 pod 没有流量发出来现象一致。 { "name": "0.0.0.0_7000", "address": { "socketAddress": { "address": "0.0.0.0", "portValue": 7000 } }, "filterChains": [ { "filterChainMatch": { "prefixRanges": [ { "addressPrefix": "10.64.x.x", "prefixLen": 32 } ] }, "filters": [ { "name": "envoy.tcp_proxy", "typedConfig": { "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", "statPrefix": "BlackHoleCluster", "cluster": "BlackHoleCluster" } } ] } 至于 7001-7003 为什么能通,是因为 istio-proxy 默认使用的是 ALLOW_ANY 模式,对于没有匹配上 listener 的流量是直接放行。可以通过 istio_configmap 配置信息来验证一下: $ kubectl get cm istio -n istio-system -o yaml | grep -i -w -a3 "mode" # REGISTRY_ONLY - restrict outbound traffic to services defined in the service registry as well # as those defined through ServiceEntries outboundTrafficPolicy: mode: ALLOW_ANY localityLbSetting: enabled: true # The namespace to treat as the administrative root namespace for istio -- drainDuration: 45s parentShutdownDuration: 1m0s # # The mode used to redirect inbound connections to Envoy. This setting # has no effect on outbound traffic: iptables REDIRECT is always used for # outbound connections. # If "REDIRECT", use iptables REDIRECT to NAT and redirect to Envoy. # The "REDIRECT" mode loses source addresses during redirection. # If "TPROXY", use iptables TPROXY to redirect to Envoy. # The "TPROXY" mode preserves both the source and destination IP # addresses and ports, so that they can be used for advanced filtering # and manipulation. # The "TPROXY" mode also configures the Sidecar to run with the # CAP_NET_ADMIN capability, which is required to use TPROXY. #interceptionMode: REDIRECT # 解决方案 最后我们来解决开头提到的问题,总共有三种解决方案。 方法 1:Service Entry 服务条目(Service Entry)是 istio 重要的资源对象之一,作用是将外部的资源注册到 istio 内部的网格服务中来,以提供网格内对外部资源的更加精细化的控制。我们可以简单理解为白名单,istios 根据 Service Entry 的内容生成 listeners。 我们在命名空间 dev-self-pc-ct 中添加如下配置: $ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: rediscluster namespace: dev-self spec: hosts: - redis addresses: - 10.0.x.x/32 ports: - number: 7000 name: redis-7000 protocol: tcp - number: 7001 name: redis-7001 protocol: tcp - number: 7002 name: redis-7002 protocol: tcp - number: 7003 name: redis-7003 protocol: tcp resolution: NONE location: MESH_EXTERNAL EOF 查看 listener: $ ./istioctl proxy-config listeners test-8c4c9dcb9-kpm8n.dev-self --address 10.0.x.x -o json [ { "name": "10.0.x.x_7000", "address": { "socketAddress": { "address": "10.0.x.x", "portValue": 7000 } }, "filterChains": [ { "filters": [ { "name": "mixer", "typedConfig": { "@type": "type.googleapis.com/istio.mixer.v1.config.client.TcpClientConfig", "transport": { "networkFailPolicy": { "policy": "FAIL_CLOSE", "baseRetryWait": "0.080s", "maxRetryWait": "1s" }, "checkCluster": "outbound|9091||istio-policy.istio-system.svc.cluster.local", "reportCluster": "outbound|9091||istio-telemetry.istio-system.svc. cluster.local", "reportBatchMaxEntries": 100, "reportBatchMaxTime": "1s" }, "mixerAttributes": { "attributes": { "context.proxy_version": { "stringValue": "1.4.8" }, "context.reporter.kind": { "stringValue": "outbound" }, "context.reporter.uid": { "stringValue": "kubernetes://test-8c4c9dcb9-kpm8n.dev-self" }, "destination.service.host": { "stringValue": "redis" }, "destination.service.name": { "stringValue": "redis" }, "destination.service.namespace": { "stringValue": "dev-self " }, "source.namespace": { "stringValue": "dev-self " }, "source.uid": { "stringValue": "kubernetes://test-8c4c9dcb9-kpm8n.dev-self" } } }, "disableCheckCalls": true } }, { "name": "envoy.tcp_proxy", "typedConfig": { "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", "statPrefix": "outbound|7000||redis", "cluster": "outbound|7000||redis" } } ] } ], "deprecatedV1": { "bindToPort": false }, "listenerFiltersTimeout": "0.100s", "continueOnListenerFiltersTimeout": true, "trafficDirection": "OUTBOUND" }, ...... ] 我们可以看到 listener "10.0.1.6_7000" 中 tcp 流量关联了 outbound|7000||redis 集群,该集群的类型是 ORIGINAL_DST,即保持源报文的目的地址,并且没有关联任何 service。 所以此时访问 10.0.x.x:7000 的目标地址不会改变。 { "name": "outbound|7000||redis", "type": "ORIGINAL_DST", "connectTimeout": "10s", "lbPolicy": "CLUSTER_PROVIDED", "circuitBreakers": { "thresholds": [ { "maxConnections": 4294967295, "maxPendingRequests": 4294967295, "maxRequests": 4294967295, "maxRetries": 4294967295 } ] } } 再次访问测试: $ nc 10.0.0.6 7000 keys * -NOAUTH Authentication required. $ nc 10.0.0.7 7000 keys * -NOAUTH Authentication required. $ nc 10.0.0.8 7000 keys * -NOAUTH Authentication required. 已经可以正常转发。 方法 2:更改 global.proxy.includeIPRanges 或 global.proxy.excludeIPRanges 配置选项 global.proxy.includeIPRanges:需要进行代理 ip 地址范围 global.proxy.excludeIPRanges:不需要进行代理的 ip 地址范围。 最终效果为在 pod 的 iptables 规则上匹配或排除对应的地址,访问这些地址流量不会被重定向到 istio-proxy,而是直接发送。 我们可以使用 kubectl apply 命令更新 istio-Sidecar-injector 配置,也可以修改 spec. template.metadata. annotations 中的 traffic.Sidecar.istio.io/includeOutboundIPRanges 来达到相同的效果。 template: metadata: annotations: kubectl.kubernetes.io/restartedAt: '2021-06-09T21:59:10+08:00' kubesphere.io/restartedAt: '2021-09-13T17:07:03.082Z' logging.kubesphere.io/logSidecar-config: '{}' Sidecar.istio.io/componentLogLevel: 'ext_authz:trace,filter:debug' Sidecar.istio.io/inject: 'true' traffic.Sidecar.istio.io/excludeOutboundIPRanges: 10.0.1.0/24 Pod 里的 iptables 规则会将目标地址为 10.0.x.x/24 的流量正常转发: # Generated by iptables-save v1.4.21 on Fri Sep 17 14:26:10 2021 *nat :PREROUTING ACCEPT [131058:7863480] :INPUT ACCEPT [183446:11006760] :OUTPUT ACCEPT [53889:3286544] :POSTROUTING ACCEPT [53953:3290384] :istio_INBOUND - [0:0] :istio_IN_REDIRECT - [0:0] :istio_OUTPUT - [0:0] :istio_REDIRECT - [0:0] -A PREROUTING -p tcp -j istio_INBOUND -A OUTPUT -p tcp -j istio_OUTPUT -A istio_INBOUND -p tcp -m tcp --dport 22 -j RETURN -A istio_INBOUND -p tcp -m tcp --dport 15020 -j RETURN -A istio_INBOUND -p tcp -j istio_IN_REDIRECT -A istio_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006 -A istio_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN -A istio_OUTPUT ! -d 127.0.0.1/32 -o lo -j istio_IN_REDIRECT -A istio_OUTPUT -m owner --uid-owner 1337 -j RETURN -A istio_OUTPUT -m owner --gid-owner 1337 -j RETURN -A istio_OUTPUT -d 127.0.0.1/32 -j RETURN -A istio_OUTPUT -d 10.0.0.0/24 -j RETURN -A istio_OUTPUT -j istio_REDIRECT -A istio_REDIRECT -p tcp -j REDIRECT --to-ports 15001 COMMIT # Completed on Fri Sep 17 14:26:10 2021 方法 3:用 Service 打败 Service 这个方法基于 istio 会为集群中的 svc 自动生成 listener 的工作方式,手动在集群中为外部服务创建 service 和 endpoints: apiVersion: v1 kind: Endpoints metadata: name: rediscluster labels: name: rediscluster app: redis-jf user: jf namespace: dev-self subsets: - addresses: - ip: 10.0.x.x - ip: 10.0.x.x - ip: 10.0.x.x ports: - name: tcp-7000 port: 7000 - name: tcp-7001 port: 7001 - name: tcp-7002 port: 7002 - name: tcp-7003 port: 7003 --- apiVersion: v1 kind: Service metadata: name: rediscluster namespace: dev-self spec: ports: - name: tcp-7000 protocol: TCP port: 7000 targetPort: 7000 - name: tcp-7001 protocol: TCP port: 7001 targetPort: 7001 - name: tcp-7002 protocol: TCP port: 7002 targetPort: 7002 - name: tcp-7003 protocol: TCP port: 7003 targetPort: 7003 selector: name: rediscluster app: redis-jf user: jf 应用以上配置后 istio 会自动生成一个 service_ip+port 的 listener。Service 信息如下: Selector: app=redis-jf,name=rediscluster,user=jf Type: ClusterIP IP: 10.233.40.115 Port: tcp-7000 7000/TCP TargetPort: 7000/TCP Endpoints: <none> Port: tcp-7001 7001/TCP TargetPort: 7001/TCP Endpoints: <none> Port: tcp-7002 7002/TCP TargetPort: 7002/TCP Endpoints: <none> Port: tcp-7003 7003/TCP TargetPort: 7003/TCP Endpoints: <none> Session Affinity: None Events: <none> Listener 部分信息如下: { "name": "10.233.59.159_7000", "address": { "socketAddress": { "address": "10.233.59.159", "portValue": 7000 } }, "filterChains": [ { "filters": [ { "name": "mixer", "typedConfig": { "@type": "type.googleapis.com/istio.mixer.v1.config.client.TcpClientConfig", "transport": { "networkFailPolicy": { "policy": "FAIL_CLOSE", "baseRetryWait": "0.080s", "maxRetryWait": "1s" }, "checkCluster": "outbound|9091||istio-policy.istio-system.svc.cluster.local", "reportCluster": "outbound|9091||istio-telemetry.istio-system.svc.cluster.local", "reportBatchMaxEntries": 100, "reportBatchMaxTime": "1s" }, "mixerAttributes": { "attributes": { "context.proxy_version": { "stringValue": "1.4.8" }, ...... 该 listener 指向了一个 cluster: { "name": "envoy.tcp_proxy", "typedConfig": { "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", "statPrefix": "outbound|7000||redis", "cluster": "outbound|7000||redis" } } 对应的 service 信息如下: 可以看到 endpoint 就是刚才我们指定的外部服务器地址: 进行访问测试: 已经可以正常访问了。 总结 最后我们来比较一下这三种方法。 方法 1:通过添加 ServiceEntry,以允许访问外部服务。可以让你使用 istio 服务网格所有的功能去调用集群内或集群外的服务,这是官方推荐的方法。 方法 2:直接绕过了 istio Sidecar 代理,使你的服务可以直接访问任意的外部服务。 但是,以这种方式配置代理需要了解集群提供商相关知识和配置。将失去对外部服务访问的监控,并且无法将 istio 功能应用于外部服务的流量。 方法 3:这个方法相对于其他两种,配置有点复杂,同时还要通过 service 的方式来访问外部服务,这意味着对于已经存在的应用需要进行改造。具体能否实施看实际情况。 方法 1 的做法类似于“白名单”,不但能达到访问外部服务的目的,并且可以像集群内部服务一样对待(可使用 istio 的流量控制功能)。另外,即使服务受到入侵,由于“白名单”的设置入侵者也无法(或较难)将流量回传到入侵机器,进一步保证了服务的安全性; 方法 2 直接绕过了 istio Sidecar 代理,使你的服务可以直接访问任意的外部服务。 但是,以这种方式配置代理需要了解集群提供商相关知识和配置。 你将失去对外部服务访问的监控,并且无法将 istio 功能应用于外部服务的流量; 方法 3 虽然也可以使用 istio 的流量控制功能来管理外部流量,但是在实际操作中会存在配置复杂、改造应用等问题 因此,强烈推荐大家使用方法一。最后,特别提醒一下大家。将 includeOutboundIPRanges 设置为空是有问题的,这相当于将所有的服务都配置代理绕行,那 Sidecar 就没起作用了,没了 Sidecar 的 istio 就没有灵魂了。。 本文由博客一文多发平台 OpenWrite 发布!

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

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

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应用均可从中受益。

用户登录
用户注册