首页 文章 精选 留言 我的

精选列表

搜索[开发经验],共10009篇文章
优秀的个人博客,低调大师

goreplay 使用经验

测试 HTTP 服务,为了覆盖更多的场景,可以考虑录制线上流量,在测试环境进行重放。之前用 tcpcopy 比较多,最近遇到一些需求,需要在 HTTP 层做一些过滤,例如只录制指定 URL 的请求。 经过调研,发现 goreplay,其前称是 gor,很适合这个场景,有以下优点。 支持 HTTP 请求的录制和重放,可以在线上录制请求,在测试环境进行重放。 支持 HTTP 层面的流量过滤,可以只挑选我们感兴趣的流量。 支持请求放大,用于性能测试。 1. 用法 免 root 运行,抓包并不需要 root 权限,同样的方法适用于 tcpdump,其实 goreplay 和 tcpdump 一样,都用 libpcap 来抓包。 $ sudo setcap "cap_net_raw,cap_net_admin+eip" ./goreplay 抓取 80 端口的 HTTP 请求,只抓请求 URL 是 /api/v1 的,并输出到终端。这个比 tcpdump 更直观,打印到终端的是我们熟悉的 HTTP 协议。 第一行是 goreplay 自定义的 header,平常使用可以不必理会,不是实际抓到的包,从第二行开始才是实际抓到的包。 $ ./goreplay --input-raw :80 --http-allow-url '/api/v1' --output-stdout 抓取 80 端口的所有请求,并保存到文件。实际会分批保存为 request_0.gor,request_1.gor 这种文件名。 $ ./goreplay --input-raw :80 --output-file 'request.gor' 重放请求,例如 host2.com 是我们的新机房域名。这种重放,会根据请求的时间戳,按照抓取时的请求顺序重放。 例如抓取的时候,第一秒 10 个请求,第二秒 20 个请求,那么重放的时候,也会按照这个顺序。并且读完 request.gor 文件,就会停止。 上面我们看到了 goreplay 自定义的 header,其第三个字段,是一个纳秒级的时间戳,根据这个来保证重放的顺序和速率。 $ ./goreplay --input-file 'request.gor' --output-http 'http://host2.com' 如果是性能测试,可以不考虑请求的顺序和速率,并且要求无限循环。 # --input-file 从文件中获取请求数据,重放的时候 100x 倍速 # --input-file-loop 无限循环,而不是读完这个文件就停止 # --output-http 发送请求到 http://host2.com # --output-http-workers 并发 100 发请求 # --stats --output-http-stats 每 5 秒输出一次 TPS 数据 $ ./goreplay --input-file 'request.gor|10000%' --input-file-loop --output-http 'http://host2.com' --output-http-workers 100 --stats --output-http-stats 更多的命令行参数及用法,可以查看 goreplay 源码的 settings.go 文件。 其输出的 stats 含义,可以看看 这个帖子 的解释。 2. 坑 使用过程中遇到的坑。 如果 HTTP 请求不符合规范,可能会抓不到包。遇到过 HTTP 请求头里面的 Content-Length 不等于实际的 Body 大小,goreplay 认为其请求未结束。 input-file 是单 goroutine 在跑,会有性能瓶颈。测试的时候,读取的 RPS 在 1.7w - 1.8w 左右,如果压测需求大于这个,需要开多个进程同时跑。 3. 深入 goreplay 是用 golang 编写的,抓包的时候调用 gopacket,后者通过 cgo 来调用 libpcap。从编译开始,从源码层面学习一下其实现。 TL;DR 3.1 编译 由于 goreplay 使用了第三方 C 代码,不能使用 Go 的交叉编译功能来跨平台编译。只能在 Linux 下编译 Linux 使用的可执行文件。 在 Redhat 系发型版下,可以使用 yum 来安装依赖。 $ sudo yum install libpcap libpcap-devel 或者从源码编译 libpcap。 # 安装依赖 $ sudo yum install gcc flex byacc bison $ wget http://www.tcpdump.org/release/libpcap-1.8.1.tar.gz && tar xzf libpcap-1.8.1.tar.gz $ cd libpcap-1.8.1 $ ./configure $ sudo make install cd 到 goreplay 的源码目录,执行命令。 # 纯静态编译 $ go build -ldflags '-extldflags "-static"' 如果编译成功,在当前目录会生成一个 goreplay 文件,试运行一下。 [vagrant@localhost goreplay]$ ./goreplay Version: 2018/06/08 08:06:22 Required at least 1 input and 1 output 不依赖任何库。 [vagrant@localhost goreplay]$ ldd goreplay not a dynamic executable 一个合法的可执行文件。 [vagrant@localhost goreplay]$ file goreplay goreplay: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=e334de401c00057ca56a33c0136dc5c86debee61, not stripped 如果遇到以下编译错误。 [vagrant@localhost goreplay]$ go build -ldflags '-extldflags "-static"' # github.com/buger/goreplay /home/vagrant/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit status 1 /usr/bin/ld: cannot find -lpthread /usr/bin/ld: cannot find -lc collect2: error: ld returned 1 exit status 需要安装 glibc 的静态库。 $ sudo yum install glibc-static.x86_64 如果不是通过 go get 来获取 goreplay,而是通过 git clone 下来的,那么会缺少一些第三方库的依赖,通过 go get 命令补充就好。 $ go get github.com/Shopify/sarama 3.2 input 和 output input 和 output 是 goreplay 对数据流的抽象,在源码目录有很多 input_xxx.go 和 output_xxx.go,实现了 goreplay 的核心功能。 在启动的时候,会解析命令行参数中指定的 input 和 output,接着启动 emitter,从 input 中读数据,写到 output 中。 emitter.go 中核心代码如下: for _, in := range Plugins.Inputs { go CopyMulty(in, Plugins.Outputs...) } 多个 input 之间是并行的,但单个 input 到多个 output,是串行的。所有 input 都实现了 io.Reader 接口,output 都实现了 io.Writer 接口。所以阅读代码时,input 的入口是 Read() 方法,output 的入口是 Write() 方法。 3.3 UDP 抓包 抓包是核心功能,也算是一种 input 的类型。不过 goreplay 的实现中,实现上和 HTTP 协议绑定的很死。我参考 goreplay 的代码,实现了 goreplay-udp,用法上和 goreplay 保持一致。

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

Android -- 经验分享

目录 代码中安装apk 判断某个apk是否已经安装 取得屏幕大小 获得 LayoutInflater 实例的三种方式 ContentResolver.query(),以及 Activity.managedQuery异同 解锁功能 点亮屏幕 activity全屏显示 屏常亮代码 SQL中 inner join、 left join 、right join、 outer join之间的区别 包含button的item也能弹出菜单,回调click以及long click的监听器 背光亮度 /sys/class/leds/lcd-backlight/brightness 判断是否在锁屏界面的函数 OnTouchListener实现双击事件 回到待机界面 定时器相关 sd卡和sim卡状态判断 开机启动,检查sim卡是否发生变更 代码中安装apk Runtime.getRuntime().exec("pm install xxx.apk"); 需要在manifest.xml文件中,加上INSTALL_PACKAGES的权限,可以用 Package install的receiver来处理安装成功后的操作 或者: /** * 安装apk * @param url */ private void installApk(String saveFileName){ File apkfile = new File(saveFileName); if (!apkfile.exists()) { return; } Intent i = new Intent(Intent.ACTION_VIEW); i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive"); mContext.startActivity(i); } 判断某个APK是否已经安装 private boolean isAppInstalled(String uri){ PackageManager pm = getPackageManager(); boolean installed =false; try{ pm.getPackageInfo(uri,PackageManager.GET_ACTIVITIES); installed =true; }catch(PackageManager.NameNotFoundException e){ installed =false; } return installed; } //Just call the method by passing the package name of the application you need to check. if(isAppInstalled("com.yourpackage.package")){ //app installed }else{ //app not installed } 取得屏幕大小 1、在非activity类中 WindowManager windowManager = (WindowManager)(mContext.getSystemService(Context.WINDOW_SERVICE)); int screenWidth = windowManager.getDefaultDisplay().getWidth(); int screenHeight = windowManager.getDefaultDisplay().getHeight(); 2、在activity类中 int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); // 屏幕宽(像素,如:480px) int screenHeight = getWindowManager().getDefaultDisplay().getHeight(); // 屏幕高(像素,如:800px) 获得 LayoutInflater 实例的三种方式 LayoutInflater inflater = getLayoutInflater(); //调用Activity的getLayoutInflater() LayoutInflater localinflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); LayoutInflater inflater = LayoutInflater.from(context); 示意代码: LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.custom, (ViewGroup)findViewById(R.id.test)); 对于上面代码,指定了第二个参数 ViewGroup root,当然你也可以设置为 null 值。 ContentResolver.query(),以及 Activity.managedQuery异同 参数、返回值相同:都返回 Cursor 对象 参数: URI: Content Provider 需要返回的资源索引 Projection: 用于标识有哪些columns需要包含在返回数据中。 Selection: 作为查询符合条件的过滤参数,类似于SQL语句中Where之后的条件判断。 SelectionArgs: 同上。 SortOrder: 用于对返回信息进行排序。 不同: mContext.getContentResolver().query ContentResolver.query(),以及 Activity.managedQuery()所以,我们看到一个是ContentResolver提供的查询方法,位于android.content.ContextWrapper.getContentResolver(),另一个则为Activity。 Activity.managedQuery() 方法导致活动管理 Cursor 的生命周期 解锁功能 KeyguardManager keyguardManager = (KeyguardManager)getSystemService(KEYGUARD_SERVICE); KeyguardLock keyguardLock = keyguardManager.newKeyguardLock(""); keyguardLock.disableKeyguard(); PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE); 点亮屏幕 PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE); WakeLock mWakelock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP|PowerManager.SCREEN_DIM_WAKE_LOCK,"SimpleTimer"); mWakeLock.acquire();//点亮 mWakeLock.release();//关闭 activity全屏显示 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); requestWindowFeature(Window.FEATURE_NO_TITLE); 注意;要在setContentView(R.layout.layout)语句之前调用才行。 屏常亮代码 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//屏常亮 SQL中 inner join、 left join 、right join、 outer join之间的区别 举例说明 A表(a1,b1,c1) B表(a2,b2) a1 b1 c1 a2 b2 01 数学 95 01 张三 02 语文 90 02 李四 03 英语 80 04 王五 select A.*,B.* from A inner join B on(A.a1=B.a2) 结果是: a1 b1 c1 a2 b2 01 数学 95 01 张三 02 语文 90 02 李四 select A.*,B.* from A left outer join B on(A.a1=B.a2) 结果是: a1 b1 c1 a2 b2 01 数学 95 01 张三 02 语文 90 02 李四 03 英语 80 NULL NULL select A.*,B.* from A right outer join B on(A.a1=B.a2) 结果是: a1 b1 c1 a2 b2 01 数学 95 01 张三 02 语文 90 02 李四 NULL NULL NULL 04 王五 select A.*,B.* from A full outer join B on(A.a1=B.a2) 结果是: a1 b1 c1 a2 b2 01 数学 95 01 张三 02 语文 90 02 李四 03 英语 80 NULL NULL NULL NULL NULL 04 王五 包含button的item也能弹出菜单,回调click以及long click的监听器 设置button属性: android:focusable="false" 外还要设置 listview属性: android:longClickable="true" PS: 如果包含的Button是ImageButton,那么在xml中设置了android:focusable="false"属性在当前版本是无效的,必须在代码中,再次呼叫函数ImageButton.setFocusable(false)设置一次。 或者在父Item里面加上 android:descendantFocusability="blocksDescendants" 背光亮度 /sys/class/leds/lcd-backlight/brightness 例如 echo 255 > /sys/class/leds/lcd-backlight/brightness 代码修改亮度: Settings.System.putInt(getContentResolver(), SCREEN_BRIGHTNESS, brightness); //brightness 背光亮度 0~255 Settings.System.putInt(getContentResolver(),SCREEN_OFF_TIMEOUT, timeoutValues);//timeoutValues 灭屏时间 ms 判断是否在锁屏界面的函数 KeyguardManager: inKeyguardRestrictedInputMode(); 例: KeyguardManager km = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE); boolean keyguardexist = km.inKeyguardRestrictedInputMode(); OnTouchListener实现双击事件 class MyOnTouchListener implements View.OnTouchListener{ private long mLastTime = 0; private long mCurTime = 0; @Override public boolean onTouch(View v, MotionEvent event) { if(MotionEvent.ACTION_DOWN == event.getAction()){ mLastTime = mCurTime; mCurTime = System.currentTimeMillis(); if (mCurTime - mLastTime < 1000) { // 双击事件 关闭activity FullBlackActivity.this.finish(); return true; } } return false; } } 回到待机界面 Intent home = new Intent(Intent.ACTION_MAIN); home.addCategory(Intent.CATEGORY_HOME); this.startActivity(home); 定时器相关 采用Handler与线程的sleep(long)方法 Handler主要用来处理接受到的消息。 1. 定义一个Handler类,用于处理接受到的Message。 Handler handler = new Handler() { public void handleMessage(Message msg) { // 要做的事情 super.handleMessage(msg); } }; 新建一个实现Runnable接口的线程类,如下: public class MyThread implements Runnable { @Override public void run() { // TODO Auto-generated method stub while (true) { try { Thread.sleep(10000);// 线程暂停10秒,单位毫秒 Message message = new Message(); message.what = 1; handler.sendMessage(message);// 发送消息 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 3. 在需要启动线程的地方加入下面语句: new Thread(new MyThread()).start(); 4. 启动线程后,线程每10s发送一次消息。 采用Handler的postDelayed(Runnable, long)方法 1. 定义一个Handler类 Handler handler=new Handler(); Runnable runnable=new Runnable() { @Override public void run() { // TODO Auto-generated method stub //要做的事情 handler.postDelayed(this, 2000); } }; 2. 启动计时器 handler.postDelayed(runnable, 2000);//每两秒执行一次runnable. 3. 停止计时器 handler.removeCallbacks(runnable); 采用Handler与timer及TimerTask结合的方法 1. 定义定时器、定时器任务及Handler句柄 private final Timer timer = new Timer(); private TimerTask task; Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub // 要做的事情 super.handleMessage(msg); } }; 2. 初始化计时器任务 task = new TimerTask() { @Override public void run() { // TODO Auto-generated method stub Message message = new Message(); message.what = 1; handler.sendMessage(message); } }; 3. 启动定时器 timer.schedule(task, 2000, 2000); 简要说一下上面三步提到的一些内容: 1. 定时器任务(TimerTask)顾名思义,就是说当定时器到达指定的时间时要做的工作,这里是想Handler发送一个消息,由Handler类进行处理。 2. java.util.Timer.schedule(TimerTask task, long delay, long period):这个方法是说,delay/1000秒后执行task,然后进过period/1000秒再次执行task,这个用于循环任务,执行无数次,当然,你可以用timer.cancel();取消计时器的执行。 闹钟定时 AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(SEND_SMS_BEGIN), 0); am.set(AlarmManager.RTC_WAKEUP, 60*1000, pendingIntent); 耳机状态 private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state"; //保存状态文件位置 private boolean isHeadsetExists(){ //判断耳机是否插入的函数 FM源码 FMplayService.java char[] buffer = new char[1024]; int newState = 0; try { FileReader file = new FileReader(HEADSET_STATE_PATH); int len = file.read(buffer, 0, 1024); newState = Integer.valueOf((new String(buffer, 0, len)).trim()); } catch (FileNotFoundException e) { Log.e(LOGTAG, "This kernel does not have wired headset support"); } catch (Exception e) { Log.e(LOGTAG, "" , e); } return newState != 0; } 未插入耳机: # cat /sys/class/switch/h2w/state cat /sys/class/switch/h2w/state 0 插入耳机: # cat /sys/class/switch/h2w/state cat /sys/class/switch/h2w/state 1 sd卡和sim卡状态判断 //sdcard是否可读写 public boolean IsCanUseSdCard() { try { return Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED); } catch (Exception e) { e.printStackTrace(); } return false; } //sim卡是否可读 public boolean isCanUseSim() { try { TelephonyManager mgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); return TelephonyManager.SIM_STATE_READY == mgr.getSimState(); } catch (Exception e) { e.printStackTrace(); } return false; } 开机启动,检查sim卡是否发生变更 (1)注册receiver,设置意图过滤器 <receiver android:name=".receiver.BootCompleteReceiver" > <intent-filter android:priority="1000" > <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> (2)接受开机广播,并判断sim卡时候发生更改 public class BootCompleteReceiver extends BroadcastReceiver { private static final String TAG = "BootCompleteReceiver"; private SharedPreferences sp; private TelephonyManager tm; @Override public void onReceive(Context context, Intent intent) { Log.i(TAG,"手机重启了"); sp = context.getSharedPreferences("config", Context.MODE_PRIVATE); boolean protecting = sp.getBoolean("protecting", false); //如果防盗保护开启 if(protecting){ //判断sim卡是否发生了变化 tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); String currentSim = tm.getSimSerialNumber(); String savedSim= sp.getString("sim", ""); if(savedSim.equals(currentSim)){ // sim卡为发生变化, do nothint }else{ //sim卡发生了变化 //发送报警短信 给安全号码 SmsManager smsManager = SmsManager.getDefault(); String destinationAddretss = sp.getString("safenumber", ""); smsManager.sendTextMessage(destinationAddretss, null, "sim change !!!", null, null); } } } } 我是天王盖地虎的分割线 本文转自我爱物联网博客园博客,原文链接:http://www.cnblogs.com/yydcdut/p/3886021.html,如需转载请自行联系原作者

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

Kubernetes管理经验

推荐工具 kubectx kubectx:用来切换集群的访问 kubens:用来切换默认的namespace kubectl-aliases kubectl命令别名 集群管理相关命令 kubectl get cs # 查看节点 kubectl get nodes kubectl get ing pdd --n java # 不调度 kubectl taint nodes node1 key=value:NoSchedule kubectl cluster-info dump kubectl get svc --sort-by=.metadata.creationTimestamp kubectl get no --sort-by=.metadata.creationTimestamp kubectl get po --field-selector spec.nodeName=xxxx kubectl get events --field-selector involvedObject.kind=Service --sort-by='.metadata.creationTimestamp' 参考链接: kubernetes 节点维护 cordon, drain, uncordon 应用管理相关 kubectl top pod kubectl delete deployment,services -l app=nginx kubectl scale deployment/nginx-deployment --replicas=2 kubectl get svc --all-namespaces=true 强制删除 有时 删除pv/pvc时会有问题,这个使用得加2个命令参数--grace-period=0 --force 删除所有失败的pod kubectl get po --all-namespaces --field-selector 'status.phase==Failed' kubectl delete po --field-selector 'status.phase==Failed' 一些技巧 k8s目前没有没有类似docker-compose的depends_on依赖启动机制,建议使用wait-for-it重写镜像的command. 集群管理经(教)验(训) 节点问题 taint别乱用 kubectl taint nodes xx elasticsearch-test-ready=true:NoSchedule kubectl taint nodes xx elasticsearch-test-ready:NoSchedule- master节点本身就自带taint,所以才会导致我们发布的容器不会在master节点上面跑.但是如果自定义taint的话就要注意了!所有DaemonSet和kube-system,都需要带上相应的tolerations.不然该节点会驱逐所有不带这个tolerations的容器,甚至包括网络插件,kube-proxy,后果相当严重,请注意 taint跟tolerations是结对对应存在的,操作符也不能乱用 NoExecute tolerations: - key: "elasticsearch-exclusive" operator: "Equal" value: "true" effect: "NoExecute" kubectl taint node cn-shenzhen.xxxx elasticsearch-exclusive=true:NoExecute NoExecute是立刻驱逐不满足容忍条件的pod,该操作非常凶险,请务必先行确认系统组件有对应配置tolerations. 特别注意用Exists这个操作符是无效的,必须用Equal NoSchedule tolerations: - key: "elasticsearch-exclusive" operator: "Exists" effect: "NoSchedule" - key: "elasticsearch-exclusive" operator: "Equal" value: "true" effect: "NoExecute" kubectl taint node cn-shenzhen.xxxx elasticsearch-exclusive=true:NoSchedule 是尽量不往这上面调度,但实际上还是会有pod在那上面跑 Exists和Exists随意使用,不是很影响 值得一提的是,同一个key可以同时存在多个effect Taints: elasticsearch-exclusive=true:NoExecute elasticsearch-exclusive=true:NoSchedule 其他参考链接: Kubernetes中的Taint和Toleration(污点和容忍) kubernetes的调度机制 隔离节点的正确步骤 # 驱逐除了ds以外所有的pod kubectl drain <node name> --ignore-daemonsets kubectl cordon <node name> 这个时候运行get node命令,状态会变 node.xx Ready,SchedulingDisabled <none> 189d v1.11.5 最后 kubectl delete <node name> 维护节点的正确步骤 kubectl drain <node name> --ignore-daemonsets kubectl uncordon <node name> 节点出现磁盘压力(DiskPressure) --eviction-hard=imagefs.available<15%,memory.available<300Mi,nodefs.available<10%,nodefs.inodesFree<5% kubelet在启动时指定了磁盘压力,以阿里云为例,imagefs.available<15%意思是说容器的读写层少于15%的时候,节点会被驱逐.节点被驱逐的后果就是产生DiskPressure这种状况,并且节点上再也不能运行任何镜像,直至磁盘问题得到解决.如果节点上容器使用了宿主目录,这个问题将会是致命的.因为你不能把目录删除掉,但是真是这些宿主机的目录堆积,导致了节点被驱逐. 所以,平时要养好良好习惯,容器里面别瞎写东西(容器里面写文件会占用ephemeral-storage,ephemeral-storage过多pod会被驱逐),多使用无状态型容器,谨慎选择存储方式,尽量别用hostpath这种存储 出现状况时,真的有种欲哭无泪的感觉. Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FreeDiskSpaceFailed 23m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 5182058496 bytes, but freed 0 bytes Warning FreeDiskSpaceFailed 18m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 6089891840 bytes, but freed 0 bytes Warning ImageGCFailed 18m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 6089891840 bytes, but freed 0 bytes Warning FreeDiskSpaceFailed 13m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 4953321472 bytes, but freed 0 bytes Warning ImageGCFailed 13m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 4953321472 bytes, but freed 0 bytes Normal NodeHasNoDiskPressure 10m (x5 over 47d) kubelet, node.xxxx1 Node node.xxxx1 status is now: NodeHasNoDiskPressure Normal Starting 10m kube-proxy, node.xxxx1 Starting kube-proxy. Normal NodeHasDiskPressure 10m (x4 over 42m) kubelet, node.xxxx1 Node node.xxxx1 status is now: NodeHasDiskPressure Warning EvictionThresholdMet 8m29s (x19 over 42m) kubelet, node.xxxx1 Attempting to reclaim ephemeral-storage Warning ImageGCFailed 3m4s kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 4920913920 bytes, but freed 0 bytes 参考链接: Eviction Signals 10张图带你深入理解Docker容器和镜像 节点CPU彪高 有可能是节点在进行GC(container GC/image GC),用describe node查查.我有次遇到这种状况,最后节点上的容器少了很多,也是有点郁闷 Events: Type Reason Age From Message ---- ------ ---- ---- Warning ImageGCFailed 45m kubelet, cn-shenzhen.xxxx failed to get image stats: rpc error: code = DeadlineExceeded desc = context deadline exceeded 参考: kubelet 源码分析:Garbage Collect 对象问题 pod pod频繁重启 原因有多种,不可一概而论 资源达到limit设置值 调高limit或者检查应用 Readiness/Liveness connection refused Readiness检查失败的也会重启,但是Readiness检查失败不一定是应用的问题,如果节点本身负载过重,也是会出现connection refused或者timeout 这个问题要上节点排查 pod被驱逐(Evicted) 节点加了污点导致pod被驱逐 ephemeral-storage超过限制被驱逐 EmptyDir 的使用量超过了他的 SizeLimit,那么这个 pod 将会被驱逐 Container 的使用量(log,如果没有 overlay 分区,则包括 imagefs)超过了他的 limit,则这个 pod 会被驱逐 Pod 对本地临时存储总的使用量(所有 emptydir 和 container)超过了 pod 中所有container 的 limit 之和,则 pod 被驱逐 ephemeral-storage是一个pod用的临时存储. resources: requests: ephemeral-storage: "2Gi" limits: ephemeral-storage: "3Gi" 节点被驱逐后通过get po还是能看到,用describe命令,可以看到被驱逐的历史原因 Message: The node was low on resource: ephemeral-storage. Container codis-proxy was using 10619440Ki, which exceeds its request of 0. 参考: Kubernetes pod ephemeral-storage配置 Managing Compute Resources for Containers kubectl exec 进入容器失败 这种问题我在搭建codis-server的时候遇到过,当时没有配置就绪以及健康检查.但获取pod描述的时候,显示running.其实这个时候容器以及不正常了. ~ kex codis-server-3 sh rpc error: code = 2 desc = containerd: container not found command terminated with exit code 126 解决办法:删了这个pod,配置livenessProbe pod的virtual host name Deployment衍生的pod,virtual host name就是pod name. StatefulSet衍生的pod,virtual host name是<pod name>.<svc name>.<namespace>.svc.cluster.local.相比Deployment显得更有规律一些.而且支持其他pod访问 pod接连Crashbackoff Crashbackoff有多种原因. 沙箱创建(FailedCreateSandBox)失败,多半是cni网络插件的问题 镜像拉取,有中国特色社会主义的问题,可能太大了,拉取较慢 也有一种可能是容器并发过高,流量雪崩导致. 比如,现在有3个容器abc,a突然遇到流量洪峰导致内部奔溃,继而Crashbackoff,那么a就会被service剔除出去,剩下的bc也承载不了那么多流量,接连崩溃,最终网站不可访问.这种情况,多见于高并发网站+低效率web容器. 在不改变代码的情况下,最优解是增加副本数,并且加上hpa,实现动态伸缩容. deploy MinimumReplicationUnavailable 如果deploy配置了SecurityContext,但是api-server拒绝了,就会出现这个情况,在api-server的容器里面,去掉SecurityContextDeny这个启动参数. 具体见Using Admission Controllers service 建了一个服务,但是没有对应的po,会出现什么情况? 请求时一直不会有响应,直到request timeout 参考 Configure Out Of Resource Handling service connection refuse 原因可能有 pod没有设置readinessProbe,请求到未就绪的pod kube-proxy宕机了(kube-proxy负责转发请求) 网络过载 service没有负载均衡 检查一下是否用了headless service.headless service是不会自动负载均衡的... kind: Service spec: # clusterIP: None的即为`headless service` type: ClusterIP clusterIP: None 具体表现service没有自己的虚拟IP,nslookup会出现所有pod的ip.但是ping的时候只会出现第一个pod的ip / # nslookup consul nslookup: can't resolve '(null)': Name does not resolve Name: consul Address 1: 172.31.10.94 172-31-10-94.consul.default.svc.cluster.local Address 2: 172.31.10.95 172-31-10-95.consul.default.svc.cluster.local Address 3: 172.31.11.176 172-31-11-176.consul.default.svc.cluster.local / # ping consul PING consul (172.31.10.94): 56 data bytes 64 bytes from 172.31.10.94: seq=0 ttl=62 time=0.973 ms 64 bytes from 172.31.10.94: seq=1 ttl=62 time=0.170 ms ^C --- consul ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.170/0.571/0.973 ms / # ping consul PING consul (172.31.10.94): 56 data bytes 64 bytes from 172.31.10.94: seq=0 ttl=62 time=0.206 ms 64 bytes from 172.31.10.94: seq=1 ttl=62 time=0.178 ms ^C --- consul ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.178/0.192/0.206 ms 普通的type: ClusterIP service,nslookup会出现该服务自己的IP / # nslookup consul nslookup: can't resolve '(null)': Name does not resolve Name: consul Address 1: 172.30.15.52 consul.default.svc.cluster.local ReplicationController不更新 ReplicationController不是用apply去更新的,而是kubectl rolling-update,但是这个指令也废除了,取而代之的是kubectl rollout.所以应该使用kubectl rollout作为更新手段,或者懒一点,apply file之后,delete po. 尽量使用deploy吧. StatefulSet更新失败 StatefulSet是逐一更新的,观察一下是否有Crashbackoff的容器,有可能是这个容器导致更新卡住了,删掉即可. 进阶调度 使用亲和度确保节点在目标节点上运行 nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: elasticsearch-test-ready operator: Exists 参考链接: advanced-scheduling-in-kubernetes kubernetes-scheulder-affinity 使用反亲和度确保每个节点只跑同一个应用 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: 'app' operator: In values: - nginx-test2 topologyKey: "kubernetes.io/hostname" namespaces: - test 容忍运行 master节点之所以不允许普通镜像,是因为master节点带了污点,如果需要强制在master上面运行镜像,则需要容忍相应的污点. tolerations: - effect: NoSchedule key: node-role.kubernetes.io/master operator: Exists - effect: NoSchedule key: node.cloudprovider.kubernetes.io/uninitialized operator: Exists 阿里云Kubernetes问题 修改默认ingress 新建一个指向ingress的负载均衡型svc,然后修改一下kube-system下nginx-ingress-controller启动参数. - args: - /nginx-ingress-controller - '--configmap=$(POD_NAMESPACE)/nginx-configuration' - '--tcp-services-configmap=$(POD_NAMESPACE)/tcp-services' - '--udp-services-configmap=$(POD_NAMESPACE)/udp-services' - '--annotations-prefix=nginx.ingress.kubernetes.io' - '--publish-service=$(POD_NAMESPACE)/<自定义svc>' - '--v=2' LoadBalancer服务一直没有IP 具体表现是EXTERNAL-IP一直显示pending. ~ kg svc consul-web NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE consul-web LoadBalancer 172.30.13.122 <pending> 443:32082/TCP 5m 这问题跟Alibaba Cloud Provider这个组件有关,cloud-controller-manager有3个组件,他们需要内部选主,可能哪里出错了,当时我把其中一个出问题的pod删了,就好了. 清理Statefulset动态PVC 目前阿里云Statefulset动态PVC用的是nas。 对于这种存储,需要先把容器副本将为0,或者整个Statefulset删除。 删除PVC 把nas挂载到任意一台服务器上面,然后删除pvc对应nas的目录。 升级到v1.12.6-aliyun.1之后节点可分配内存变少 该版本每个节点保留了1Gi,相当于整个集群少了N GB(N为节点数)供Pod分配. 如果节点是4G的,Pod请求3G,极其容易被驱逐. 建议提高节点规格. Server Version: version.Info{Major:"1", Minor:"12+", GitVersion:"v1.12.6-aliyun.1", GitCommit:"8cb561c", GitTreeState:"", BuildDate:"2019-04-22T11:34:20Z", GoVersion:"go1.10.8", Compiler:"gc", Platform:"linux/amd64"} 新加节点出现NetworkUnavailable RouteController failed to create a route 看一下kubernetes events,是否出现了 timed out waiting for the condition -> WaitCreate: ceate route for table vtb-wz9cpnsbt11hlelpoq2zh error, Aliyun API Error: RequestId: 7006BF4E-000B-4E12-89F2-F0149D6688E4 Status Code: 400 Code: QuotaExceeded Message: Route entry quota exceeded in this route table 出现这个问题是因为达到了VPC的自定义路由条目限制,默认是48,需要提高vpc_quota_route_entrys_num的配额 参考(应用调度相关): Kubernetes之健康检查与服务依赖处理 kubernetes如何解决服务依赖呢? Kubernetes之路 1 - Java应用资源限制的迷思 Control CPU Management Policies on the Node Reserve Compute Resources for System Daemons Configure Out Of Resource Handling

资源下载

更多资源
Nacos

Nacos

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

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

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册