Heap def new generation total 9216K, used 2010K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 24% used [0x00000000fec00000, 0x00000000fedf68c8, 0x00000000ff400000) from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000) Metaspace used 3288K, capacity 4496K, committed 4864K, reserved 1056768K class space used 348K, capacity 388K, committed 512K, reserved 1048576K
新生代中的空间占比 eden:from:to 在默认情况下是 8:1:1,与观察到的数据 8192K:1024K:1024K 一致。 新生代的空间 eden + from + to 为 10240K,符合 -Xmn10M 设置的大小。 total 显示为 9216K,即 eden + from 的大小,是因为 to 的空间不计算在内。新生代可用的空间只有 eden + from,to 空间只是在使用标记-复制算法进行垃圾回收时使用。 老年代的空间为 10240K。 目前仅 eden 中已用 2010K,约占 eden 空间的 24%。
publicstaticvoidmain(String[] args) { List<byte[]> list = newArrayList<>(); list.add(newbyte[_7MB]); }
1 2 3 4 5 6 7 8 9 10
[GC (Allocation Failure) [DefNew: 2013K->721K(9216K), 0.0105099 secs] 2013K->721K(19456K), 0.0105455 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Heap def new generation total 9216K, used 8135K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 90% used [0x00000000fec00000, 0x00000000ff33d8c0, 0x00000000ff400000) from space 1024K, 70% used [0x00000000ff500000, 0x00000000ff5b45f0, 0x00000000ff600000) to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000) Metaspace used 3354K, capacity 4496K, committed 4864K, reserved 1056768K class space used 360K, capacity 388K, committed 512K, reserved 1048576K
from 的已用空间的地址为 [0x00000000ff500000, 0x00000000ff5b45f0),空间大小为 738800 byte,约 721K,与 GC 后的新生代空间占用大小一致。在垃圾回收后,eden 区域存活的对象全部转移到了原 to 空间,from 和 to 空间的角色相互转换(从地址空间的信息可以看到此时 to 的地址指针比 from 的地址指针小)。 eden 的已用空间的地址为 [0x00000000fec00000, 0x00000000ff33d8c0),空间大小为 7592128 byte,约 7.24M,比 7M 大不少。此时 eden 区域除了 byte[] 对象外,还存储了其他对象,比如为了创建 List<byte[]> 对象而新加载的类对象。
eden 空间足够时不发生 GC
1 2 3 4 5
publicstaticvoidmain(String[] args) { List<byte[]> list = newArrayList<>(); list.add(newbyte[_7MB]); list.add(newbyte[_512KB]); }
1 2 3 4 5 6 7 8 9 10
[GC (Allocation Failure) [DefNew: 2013K->721K(9216K), 0.0011172 secs] 2013K->721K(19456K), 0.0011443 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 9216K, used 8647K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 96% used [0x00000000fec00000, 0x00000000ff3bd8d0, 0x00000000ff400000) from space 1024K, 70% used [0x00000000ff500000, 0x00000000ff5b45f0, 0x00000000ff600000) to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000) Metaspace used 3354K, capacity 4496K, committed 4864K, reserved 1056768K class space used 360K, capacity 388K, committed 512K, reserved 1048576K
[GC (Allocation Failure) [DefNew: 2013K->721K(9216K), 0.0013580 secs] 2013K->721K(19456K), 0.0013932 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 8565K->512K(9216K), 0.0046378 secs] 8565K->8396K(19456K), 0.0046540 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 9216K, used 1350K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 10% used [0x00000000fec00000, 0x00000000fecd1a20, 0x00000000ff400000) from space 1024K, 50% used [0x00000000ff400000, 0x00000000ff480048, 0x00000000ff500000) to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) tenured generation total 10240K, used 7884K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 77% used [0x00000000ff600000, 0x00000000ffdb33a0, 0x00000000ffdb3400, 0x0000000100000000) Metaspace used 3354K, capacity 4496K, committed 4864K, reserved 1056768K class space used 360K, capacity 388K, committed 512K, reserved 1048576K
在第三次添加时,由于 eden 空间不足,因此又发生了第二次垃圾回收。 [DefNew: 8565K->512K(9216K), 0.0046378 secs],新生代的空间占用下降到了 512K,应该是在 from 中留下了第二次添加时的 512K。 在第二次添加完成后,eden[0x00000000fec00000, 0x00000000ff3bd8d0) 和 from[0x00000000ff500000, 0x00000000ff5b45f0) 占用的空间为 8116432 + 738800 = 8855232 约 8647.7K,略大于 8565K。很奇怪,第二次垃圾回收前,新生代的空间占用为什么有小幅度下降。 8565K->8396K(19456K), 0.0046540 secs,堆的占用空间并未发生明显下降。部分对象因为新生代空间不足,提前晋升到了老年代中。8396K - 512 K 剩余 7884K,全部晋升到老年代,符合 77% 的统计数据。 eden 中加入了第三次添加时的对象,大于 512K 不少。 此时 eden、from、tenured 中均有不好确认成分的空间占用,比如 from 中多了 56 字节。
新生代空间不足,大对象直接在老年代创建
1 2 3 4
publicstaticvoidmain(String[] args) { List<byte[]> list = newArrayList<>(); list.add(newbyte[_8MB]); }
1 2 3 4 5 6 7 8 9
Heap def newgeneration total 9216K, used 2177K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 26% used [0x00000000fec00000, 0x00000000fee20730, 0x00000000ff400000) from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) tenured generation total 10240K, used 8192K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 80% used [0x00000000ff600000, 0x00000000ffe00010, 0x00000000ffe00200, 0x0000000100000000) Metaspace used 3353K, capacity 4496K, committed 4864K, reserved 1056768K classspace used 360K, capacity 388K, committed 512K, reserved 1048576K
在 Eden 空间肯定不足而老年代空间足够的情况下,大对象会直接在老年代中创建,此时不会发生 GC。
内存不足 OOM
1 2 3 4 5
publicstaticvoidmain(String[] args) { List<byte[]> list = newArrayList<>(); list.add(newbyte[_8MB]); list.add(newbyte[_8MB]); }
Heap def new generation total 9216K, used 1502K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 18% used [0x00000000fec00000, 0x00000000fed77a00, 0x00000000ff400000) from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) tenured generation total 10240K, used 9063K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 88% used [0x00000000ff600000, 0x00000000ffed9c50, 0x00000000ffed9e00, 0x0000000100000000) Metaspace used 4787K, capacity 4884K, committed 4992K, reserved 1056768K class space used 522K, capacity 558K, committed 640K, reserved 1048576K
当新生代和老年代的空间均不足时,在尝试 GC 和 Full GC 后仍不能成功分配对象,就会发生 OutOfMemoryError。
线程中发生内存不足,不会影响其他线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
publicstaticvoidmain(String[] args) { List<byte[]> list = newArrayList<>();
[GC (Allocation Failure) [DefNew: 2013K->721K(9216K), 0.0012274 secs][Tenured: 8192K->8912K(10240K), 0.0113036 secs] 10205K->8912K(19456K), [Metaspace: 3345K->3345K(1056768K)], 0.0125751 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [Full GC (Allocation Failure) [Tenured: 8912K->8895K(10240K), 0.0011880 secs] 8912K->8895K(19456K), [Metaspace: 3345K->3345K(1056768K)], 0.0012009 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 9216K, used 246K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 3% used [0x00000000fec00000, 0x00000000fec3d890, 0x00000000ff400000) from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) tenured generation total 10240K, used 8895K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 86% used [0x00000000ff600000, 0x00000000ffeafce0, 0x00000000ffeafe00, 0x0000000100000000) Metaspace used 3380K, capacity 4496K, committed 4864K, reserved 1056768K class space used 363K, capacity 388K, committed 512K, reserved 1048576K Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.moralok.jvm.gc.JvmGcTest.main(JvmGcTest.java:21)
[GC (Allocation Failure) [DefNew: 2013K->693K(9216K), 0.0015517 secs] 2013K->693K(19456K), 0.0015828 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 8885K->0K(9216K), 0.0048110 secs] 8885K->8885K(19456K), 0.0048264 secs] [Times: user=0.00 sys=0.02, real=0.00 secs] Heap def new generation total 9216K, used 410K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 5% used [0x00000000fec00000, 0x00000000fec66958, 0x00000000ff400000) from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) tenured generation total 10240K, used 8885K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 86% used [0x00000000ff600000, 0x00000000ffead580, 0x00000000ffead600, 0x0000000100000000) Metaspace used 3321K, capacity 4496K, committed 4864K, reserved 1056768K class space used 354K, capacity 388K, committed 512K, reserved 1048576K
尽管最终大部分对象提前晋升到老年代,但是可以看到第二次 GC 前的新生代空间占用,可见数组分配时,所需空间刚好为 Eden 空间大小时,还是会在 eden 创建对象。
// 检测是否有 LoadTimeWeaver,如果存在就准备编织。 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(newLoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(newContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); }
// 手动注册默认的环境 beans。 if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
// Re-register post-processor for detecting inner beans as ApplicationListeners, // moving it to the end of the processor chain (for picking up proxies etc). // 重新注册用于检测 ApplicationListeners 的 Bean 后处理器,将其移动到处理器链的最后(用于获取代理)。 beanFactory.addBeanPostProcessor(newApplicationListenerDetector(applicationContext)); }
添加 BeanPostProcessor 时
先移除
再添加
判断类型并记录标记
感知实例化的后处理器
感知销毁的后处理器
1 2 3 4 5 6 7 8 9 10 11
publicvoidaddBeanPostProcessor(BeanPostProcessor beanPostProcessor) { Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null"); this.beanPostProcessors.remove(beanPostProcessor); this.beanPostProcessors.add(beanPostProcessor); if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { this.hasInstantiationAwareBeanPostProcessors = true; } if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) { this.hasDestructionAwareBeanPostProcessors = true; } }
// Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let post-processors apply to them! // 这段注释看到不止一次,但是不太理解,感觉和代码联系不起来? String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); }
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 以二进制名称获取类加载的锁进行同步 synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded // 首先检查类是否已加载,根据该方法注释可知: // 如果当前类加载器已经被 Java 虚拟机记录为具有该二进制名称的类的加载器(initiating loader),Java 虚拟机可以直接返回 Class 对象。 Class<?> c = findLoadedClass(name); if (c == null) { longt0= System.nanoTime(); try { // 如果类还未加载,先委派给父·类加载器进行加载,如果父·类加载器为 null,则使用虚拟机内建的类加载器进行加载 if (parent != null) { // 递归调用 c = parent.loadClass(name, false); } else { // 递归调用的终结点 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader // 当父·类加载器长尝试加载但是失败,捕获异常但是什么都不做,因为接下来,当前类加载器需要自己也尝试加载。 }
if (c == null) { // If still not found, then invoke findClass in order // to find the class. longt1= System.nanoTime(); // 父·类加载器未找到类,当前类加载器自己找。 c = findClass(name);
// this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
/** * Registers the given class loader type as parallel capabale. * Returns {@code true} is successfully registered; {@code false} if * loader's super class is not registered. */ staticbooleanregister(Class<? extends ClassLoader> c) { synchronized (loaderTypes) { if (loaderTypes.contains(c.getSuperclass())) { // register the class loader as parallel capable // if and only if all of its super classes are. // Note: given current classloading sequence, if // the immediate super class is parallel capable, // all the super classes higher up must be too. // 当且仅当其所有超类都具有并行能力时,才将类加载器注册为具有并行能力。 // 注意:给定当前的类加载顺序(加载类时,Java 虚拟机总是先尝试加载其父类),如果直接超类具有并行能力,则所有更高的超类也必然具有并行能力。 loaderTypes.add(c); returntrue; } else { returnfalse; } } }
/** * Returns {@code true} if the given class loader type is * registered as parallel capable. */ staticbooleanisRegistered(Class<? extends ClassLoader> c) { synchronized (loaderTypes) { return loaderTypes.contains(c); } } }
privatesynchronized Loader getLoader(int index) { if (closed) { returnnull; } // Expand URL search path until the request can be satisfied // or the URL stack is empty. while (loaders.size() < index + 1) { // Pop the next URL from the URL stack // 如果 index 超过数组范围,需要从未打开的 URL 中取出一个,创建 Loader 并返回 URL url; synchronized (urls) { if (urls.empty()) { returnnull; } else { url = urls.pop(); } } // Skip this URL if it already has a Loader. (Loader // may be null in the case where URL has not been opened // but is referenced by a JAR index.) StringurlNoFragString= URLUtil.urlNoFragString(url); if (lmap.containsKey(urlNoFragString)) { continue; } // Otherwise, create a new Loader for the URL. Loader loader; try { // 根据 URL 创建 Loader loader = getLoader(url); // If the loader defines a local class path then add the // URLs to the list of URLs to be opened. URL[] urls = loader.getClassPath(); if (urls != null) { push(urls); } } catch (IOException e) { // Silently ignore for now... continue; } catch (SecurityException se) { // Always silently ignore. The context, if there is one, that // this URLClassPath was given during construction will never // have permission to access the URL. if (DEBUG) { System.err.println("Failed to access " + url + ", " + se ); } continue; } // Finally, add the Loader to the search path. validateLookupCache(loaders.size(), urlNoFragString); loaders.add(loader); lmap.put(urlNoFragString, loader); } if (DEBUG_LOOKUP_CACHE) { System.out.println("NOCACHE: Loading from : " + index ); } return loaders.get(index); }
URLClassPath#getLoader(java.net.URL)
根据指定的 URL 创建 Loader,不同类型的 URL 会返回不同具体实现的 Loader。
如果 URL 不是以 / 结尾,认为是 Jar 文件,则返回 JarLoader 类型,比如 file:/C:/Users/xxx/.jdks/corretto-1.8.0_342/jre/lib/rt.jar。
方法 defineClass,顾名思义,就是定义类,将字节数据转换为 Class 实例。在 ClassLoader 以及其子类中有很多同名方法,方法内各种处理和包装,最终都是为了使用 name 和字节数据等参数,调用 native 方法获得一个 Class 实例。 以下是定义类时最终可能调用的 native 方法。
1 2 3 4 5 6 7 8 9
privatenative Class<?> defineClass0(String name, byte[] b, int off, int len, ProtectionDomain pd);
privatenative Class<?> defineClass1(String name, byte[] b, int off, int len, ProtectionDomain pd, String source);
privatenative Class<?> defineClass2(String name, java.nio.ByteBuffer b, int off, int len, ProtectionDomain pd, String source);
其方法参数有:
name,目标类的名称。
byte[] 或 ByteBuffer 类型的字节数据,off 和 len 只是为了定位传入的字节数组中关于目标类的字节数据,通常分别是 0 和字节数组的长度,毕竟专门构造一个包含无关数据的字节数组很无聊。
publicbyte[] getBytes() throws IOException { byte[] b; // Get stream before content length so that a FileNotFoundException // can propagate upwards without being caught too early // 在获取内容长度之前获取流,以便 FileNotFoundException 可以向上传播而不会过早被捕获(todo: 不理解) // 获取缓存的 InputStream InputStreamin= cachedInputStream();
// This code has been uglified to protect against interrupts. // Even if a thread has been interrupted when loading resources, // the IO should not abort, so must carefully retry, failing only // if the retry leads to some other IO exception. // 该代码为了防止中断有点丑陋。即使线程在加载资源时被中断,IO 也不应该中止,因此必须小心重试,只有当重试导致其他 IO 异常时才会失败。 // 检测当前线程是否收到中断信号,收到的话则返回 true 且清除中断状态,重新变更为未中断状态。 booleanisInterrupted= Thread.interrupted(); int len; for (;;) { try { // 获取内容长度,顺利的话就跳出循环 len = getContentLength(); break; } catch (InterruptedIOException iioe) { // 如果获取内容长度时,线程被中断抛出了异常,捕获后清除中断状态 Thread.interrupted(); isInterrupted = true; } }
if (p instanceof FilePermission) { // if the permission has a separator char on the end, // it means the codebase is a directory, and we need // to add an additional permission to read recursively // 如果文件路径以文件分隔符结尾,表示目录,需要在末尾添加"-"改为递归读的权限 Stringpath= p.getName(); if (path.endsWith(File.separator)) { path += "-"; p = newFilePermission(path, SecurityConstants.FILE_READ_ACTION); } } elseif ((p == null) && (url.getProtocol().equals("file"))) { Stringpath= url.getFile().replace('/', File.separatorChar); path = ParseUtil.decode(path); if (path.endsWith(File.separator)) path += "-"; p = newFilePermission(path, SecurityConstants.FILE_READ_ACTION); } else { /** * Not loading from a 'file:' URL so we want to give the class * permission to connect to and accept from the remote host * after we've made sure the host is the correct one and is valid. */ URLlocUrl= url; if (urlConnection instanceof JarURLConnection) { locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); } Stringhost= locUrl.getHost(); if (host != null && (host.length() > 0)) p = newSocketPermission(host, SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION); }
// make sure the person that created this class loader // would have this permission
// Use byte[] if not a direct ByteBufer: if (!b.isDirect()) { if (b.hasArray()) { return defineClass(name, b.array(), b.position() + b.arrayOffset(), len, protectionDomain); } else { // no array, or read-only array byte[] tb = newbyte[len]; b.get(tb); // get bytes out of byte buffer. return defineClass(name, tb, 0, len, protectionDomain); } }
private ProtectionDomain preDefineClass(String name, ProtectionDomain pd) { // 检查 name 为 null 或者有可能是有效的二进制名称 if (!checkName(name)) thrownewNoClassDefFoundError("IllegalName: " + name);
// Note: Checking logic in java.lang.invoke.MemberName.checkForTypeAlias // relies on the fact that spoofing is impossible if a class has a name // of the form "java.*" // 如果 name 以 java. 开头,则抛出异常 if ((name != null) && name.startsWith("java.")) { thrownewSecurityException ("Prohibited package name: " + name.substring(0, name.lastIndexOf('.'))); } if (pd == null) { // 如果未传入 ProtectionDomain,取默认的 ProtectionDomain pd = defaultDomain; }
$ minikube start * minikube v1.30.1 on Ubuntu 20.04 * Unable to pick a default driver. Here is what was considered, in preference order: - docker: Not healthy: "docker version --format {{.Server.Os}}-{{.Server.Version}}:{{.Server.Platform.Name}}" exit status 1: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/version": dial unix /var/run/docker.sock: connect: permission denied - docker: Suggestion: Add your user to the 'docker' group: 'sudo usermod -aG docker $USER && newgrp docker' <https://docs.docker.com/engine/install/linux-postinstall/> * Alternatively you could install one of these drivers: - kvm2: Not installed: exec: "virsh": executable file not found in $PATH - podman: Not installed: exec: "podman": executable file not found in $PATH - qemu2: Not installed: exec: "qemu-system-x86_64": executable file not found in $PATH - virtualbox: Not installed: unable to find VBoxManage in $PATH
X Exiting due to DRV_NOT_HEALTHY: Found driver(s) but none were healthy. See above for suggestions how to fix installed drivers.
$ sudo minikube start * minikube v1.30.1 on Ubuntu 20.04 * Automatically selected the docker driver. Other choices: none, ssh * The "docker" driver should not be used with root privileges. If you wish to continue as root, use --force. * If you are running minikube within a VM, consider using --driver=none: * https://minikube.sigs.k8s.io/docs/reference/drivers/none/
X Exiting due to DRV_AS_ROOT: The "docker" driver should not be used with root privileges.
$ sudo minikube start --force * minikube v1.30.1 on Ubuntu 20.04 ! minikube skips various validations when --force is supplied; this may lead to unexpected behavior * Automatically selected the docker driver. Other choices: ssh, none * The "docker" driver should not be used with root privileges. If you wish to continue as root, use --force. * If you are running minikube within a VM, consider using --driver=none: * https://minikube.sigs.k8s.io/docs/reference/drivers/none/ * Using Docker driver with root privileges * Starting control plane node minikube in cluster minikube * Pulling base image ... * Downloading Kubernetes v1.26.3 preload ... > preloaded-images-k8s-v18-v1...: 397.02 MiB / 397.02 MiB 100.00% 25.89 M > index.docker.io/kicbase/sta...: 373.53 MiB / 373.53 MiB 100.00% 7.17 Mi ! minikube was unable to download gcr.io/k8s-minikube/kicbase:v0.0.39, but successfully downloaded docker.io/kicbase/stable:v0.0.39 as a fallback image * Creating docker container (CPUs=2, Memory=2200MB) ... * Preparing Kubernetes v1.26.3 on Docker 23.0.2 ... - Generating certificates and keys ... - Booting up control plane ... - Configuring RBAC rules ... * Configuring bridge CNI (Container Networking Interface) ... - Using image gcr.io/k8s-minikube/storage-provisioner:v5 * Verifying Kubernetes components... * Enabled addons: default-storageclass, storage-provisioner * Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
成功启动集群后,使用 kubectl get pod 测试,提示连接被拒绝。
1 2 3 4 5 6 7
$ kubectl get pod E0622 21:55:27.400754 18561 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused E0622 21:55:27.401000 18561 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused E0622 21:55:27.410464 18561 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused E0622 21:55:27.410951 18561 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused E0622 21:55:27.412076 18561 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused The connection to the server localhost:8080 was refused - did you specify the right host or port?
$ minikube start --help | grep repo --image-repository='': Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to "auto" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers $ minikube start --image-repository='registry.cn-hangzhou.aliyuncs.com/google_containers'
如何通过宿主机进行访问 minikube 控制台
启动控制台:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
$ minikube dashboard * Enabling dashboard ... - Using image docker.io/kubernetesui/dashboard:v2.7.0 - Using image docker.io/kubernetesui/metrics-scraper:v1.0.8 * Some dashboard features require the metrics-server addon. To enable all features please run:
minikube addons enable metrics-server
* Verifying dashboard health ... * Launching proxy ... * Verifying proxy health ... * Opening http://127.0.0.1:35967/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser... http://127.0.0.1:35967/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/
一般是在需要从 gcr.io 镜像仓库下载时发生,比如官方教程中需要执行以下命令,会发现 deployment 和 pod 迟迟不能达到目标状态。
1 2 3 4 5 6 7 8 9
$ kubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.39 -- /agnhost netexec --http-port=8080 $ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE hello-node 0/1 1 0 19s $ kubectl get pods NAME READY STATUS RESTARTS AGE hello-node-7b87cd5f68-zwd4g 0/1 ImagePullBackOff 0 38s