Permissions 0644 for 'xxx.pem' are too open
使用 SSH 连接到 Azure 的虚拟机时遇到错误:
1 | ➜ ~ ssh -i /path/to/some.pem xxx@x.x.x.x |
这是因为创建虚拟机时从 Azure 下载的私钥默认权限太大,需要将其权限改为只读且仅当前用户可见:
1 | chmod 400 some.pem |
参考:
使用 SSH 连接到 Azure 的虚拟机时遇到错误:
1 | ➜ ~ ssh -i /path/to/some.pem xxx@x.x.x.x |
这是因为创建虚拟机时从 Azure 下载的私钥默认权限太大,需要将其权限改为只读且仅当前用户可见:
1 | chmod 400 some.pem |
参考:
如果一个数除了1和它本身外,没有其他约数,我们称这个数为质数,但在这个定义下,1却不是质数。要回答这个问题需要先了解质数的作用,质数的主要作用在于构建欧几里得的算数基本定理:
任何一个大于1的自然数都可以唯一分解成有限个质数的乘积。
如果把1列为质数,就会破坏这种唯一性,因为在这种情况下每个自然数都有无限种分解方式,即在原有分解的基础上再乘以任意个数的1,所以1不作为质数。
参考:
使用 VisualVM 的 Virsual GC 插件需要先和服务器建立 jstatd 连接,在 JDK 9 之前需要首先创建一个 policy 文件并声明权限:
1 | grant codebase "file:${java.home}/lib/tools.jar" { |
然而,从 JDK 9 开始,tools.jar 已被移除,需要将 policy 文件的内容修改为:
1 | grant codebase "jrt:/jdk.jstatd" { |
参考:
使用 AppVeyor 的Visual Studio 2019 镜像构建 Java 项目时默认使用的是 JDK 1.8(这里说明了 AppVeyor 各个镜像下默认使用的 JDK 版本,虽然表格里写着 Visual Studio 2019 镜像下的默认 JDK 是1.7,不过实际是1.8),如果想更换 JDK 版本,比如更换为 JDK 11,可以重新设置 JAVA_HOME 和 PATH:
1 | before_test: |
完整的代码可参考 GitHub。
假设有如下的 SumService:
1 | public class SumService { |
当 a 或者 b 非正数时会抛出 IllegalArgumentException 异常,由于两者抛出的是同一个异常,所以无法直接使用 expected = IllegalArgumentException.class 进行区分测试,故需要测试具体的异常信息。
用一个 try/catch 包裹测试的方法,判断抛出的异常信息:
1 | @Test |
借助 Assert.assertThrows 执行测试方法返回一个异常,然后判断返回的异常信息:
1 | @Test |
借助 ExpectedException 预先设定预期抛出的异常和异常信息,然后执行测试方法:
1 | @Rule |
完整的代码可参考 GitHub。
对于以下的异步代码:
1 | public class DemoService { |
我们为其编写一个测试用例,并在 CompletableFuture#whenComplete 中判断返回值是否与预期相符,然而即使返回值与预期不符,该测试也不会抛出异常:
1 | @Test |
我们可以借助 CompletableFuture#get() 阻塞主线程等待结果的特点,将异步代码转成同步:
1 | @Test |
上述方案依赖了一个具体的异步类方法,如果实际的异步类不提供相应的同步方法,上述方案则不适合。针对这种情况,可以借助 CountDownLatch,初始化一个计数为1的 CountDownLatch 的实例,在测试方法中调用 CountDownLatch#await() 方法进行等待,当异步方法执行成功后在其回调中调用 CountDownLatch#countDown() 使计数器减1变为0,从而继续执行后续的测试判断:
1 | @Test |
Awaitility 让测试异步代码变得简单明了:
1 | @Test |
完整的代码可参考 GitHub。
首先来看一个例子,构建一个 C 语言版的 hello world 镜像:
1 | /* hello.c */ |
对应的 Dockerfile 为:
1 | FROM gcc |
然后执行 docker build -t hello-world . 构建一个名为 hello-world 的镜像,然而以这种方式构建的镜像的大小竟然有1.19 GB:
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
因为这种构建方式生成的镜像会同时包含 gcc 镜像的内容,查看 gcc 镜像大小发现达到了1.19 GB:
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
如果我们把基础镜像换成 Ubuntu 并安装 gcc 编译 hello.c 重新构建镜像,最后的镜像大小为213 MB:
1 | FROM ubuntu |
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
虽然新镜像相比1.19 GB有大幅减少,但相比于 hello-world 程序本身的大小(17k)来说,213 MB依然是个庞大的数字:
1 | $ ls hello -hl |
对于 hello-world 这个镜像来说,我们真正需要的只是最终的可执行程序,而并不关心中间的编译过程,如果能将编译阶段作为一个临时阶段而并不包含在最终的镜像中,则可有效减少最终的镜像大小。针对此,Docker 在 17.05 版本开始提供了名为 multi-stage 构建的功能。我们将原来的 Dockerfile 稍作修改,将原来的编译阶段抽取为一个 stage,然后将编译好的可执行文件复制到最终的 stage 中:
1 | FROM gcc AS mybuildstage |
最终的镜像大小只有73.9 MB:
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
FROM scratch在上一步中,我们使用 Ubuntu 作为基础镜像来运行 hello-world,相比于一个可执行程序,Ubuntu 依然过于庞大,有没有比 Ubuntu 更轻量的镜像呢?有,那就是 scratch,这表示一个空的镜像,继续将 Dockerfile 稍作修改:
1 | FROM gcc AS mybuildstage |
最终的镜像大小只有16.4 KB:
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
不过在运行该镜像时却提示错误:
1 | standard_init_linux.go:211: exec user process caused "no such file or directory" |
这是因为这种方式构建出的镜像缺少 hello-world 运行时依赖的库。我们可以在编译 hello-world 时通过指定 -static 参数将依赖的库包含到最后的可执行文件中来解决这个问题:
1 | FROM gcc AS mybuildstage |
不过包含了依赖的库后最终镜像的大小也上涨为945 KB:
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
另外,如果不想将依赖的库包含到最终的镜像中,可以使用 busybox:glibc 这个基础镜像,该镜像包含了 C 语言的标准库,有了这个镜像在编译 hello-world 时则无需指定 -static 参数:
1 | FROM gcc AS mybuildstage |
不过由于该镜像本身有一定大小,最终镜像的大小达到了5.22 MB:
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
通过 multi-stage 构建可以有效的减少 Docker 镜像的大小,而基础镜像的选择则要具体情况分析,在满足需求的情况下选择合理的基础镜像。
Ubuntu 下安装 pip 时遇到 ModuleNotFoundError: No module named 'distutils.util' 错误,执行以下命令即可:
1 | sudo apt-get install python3-distutils |
参考:
Ubuntu 下执行 add-apt-repository 添加第三方仓库时遇到 add-apt-repository: not found 错误,执行以下命令即可:
1 | sudo apt-get update |
参考:
Finder 默认并不显示隐藏文件,可以通过如下两种方式开启:
defaults write com.apple.finder AppleShowAllFiles YES,然后重启 FinderFinder 中使用快捷键 Shift + Command + "."参考: