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
,然后重启 Finder
Finder
中使用快捷键 Shift + Command + "."
参考: