Dockershim即将被删除,你准备好了吗?

重点:1.24版本的k8s将不会自带dockershim,即默认不支持docker作为容器运行时了。现在可以给Kubernetes反馈相关信息了:https://forms.gle/svCJmhvTv78jGdSx8。

Mirantis单独维护了dockershim组件,仍然可以使用:https://github.com/Mirantis/cri-dockerd


正文:

去年我们宣布Dockershim已被弃用:Dockershim弃用FAQ。我们目前的计划是尽快将dockershim从Kubernetes代码库中移除。我们正在寻求您的反馈,您是否已经准备好移除dockershim,并确保您已经准备好了。请填写这个调查问卷:https://forms.gle/svCJmhvTv78jGdSx8

Docker作为Kubernetes容器运行时的dockershim组件已被弃用,取而代之的是直接使用容器运行时接口(CRI)的运行时。许多Kubernetes用户已经顺利地迁移到其他容器运行时。然而,我们看到dockershim仍然很受欢迎。您可能会在DataDog最近的容器报告中看到一些公开统计数据。一些Kubernetes供应商最近才启用了其他运行时支持(特别是Windows节点)。我们知道许多第三方工具供应商还没有准备好:迁移遥测和安全代理

在这一点上,我们相信Docker和其他运行时之间的特性是等价的。许多终端用户已经使用了我们的迁移指南,并且正在使用这些不同的运行时运行生产工作负载。现在的计划是,dockershim将在明年4月发布的1.24版本中被移除。对于那些Kubernetes开发者或运行alpha和beta版本的人,dockershim将在12月开始的1.24发布周期被移除。

只有一个月的时间给我们反馈。我们想让你告诉我们你准备好了多少。

为了更好地了解dockershim移除的准备工作,我们的调查问卷会询问您当前使用的Kubernetes版本,以及您认为采用Kubernetes 1.24的估计时间。所有关于dockershim移除的汇总信息最终将被公开。一些评论将由SIG Node来审查过滤。如果您想讨论从dockershim迁移的任何细节、报告bug或阻碍点,您可以在在任何时候联系SIG Node:https://github.com/kubernetes/community/tree/master/sig-node#contact

Kubernetes是一个成熟的项目。dockershim的移除是为了摆脱“永久的beta特性”并提供更强的稳定性和兼容性保证。通过dockershim的迁移,你将获得更多的灵活性和容器运行时的选项,减少了对特定底层技术的依赖。请花时间查看dockershim迁移文档,并咨询Kubernetes供应商有哪些容器运行时选项可供您选择。阅读有关如何使用容器和CRI-O的容器运行时文档,以帮助您在升级到1.24时做好准备。

阅读更多

sockfwd 一个数据转发的小工具

最近在看containerd的代码,上手试的时候才发现它监听的是unix socket,没法从外部访问containerd。
而我要验证的是从远端能不能访问containerd、管理containerd的容器,所以需要一个从远端访问unix socket的工具。

网上搜了一圈,没有现成的实现,就自己写了 sockfwd

用法

1
2
3
4
5
6
7
Usage:
sockfwd [flags]

Flags:
-d, --destination string 目的地址,即要转发到的地址
-s, --source string 源地址,即接收请求的地址
-q, --quiet 静默模式

例子

将本地的containerd实例暴露到网络上:

./sockfwd -s tcp://127.0.0.1:8090 -d unix:///var/run/containerd.sock

将本地的127.0.0.1:8080端口暴露到0.0.0.0:8090端口上:

./sockfwd -s tcp://127.0.0.1:8090 -d unix://127.0.0.1:8090

将本地的服务暴露到网络上,需要格外注意是否有安全隐患!

阅读更多

containerd/nerdctl - 一个开源、免费的Docker的替代品

2021年8月31日,Docker公司更改了收费策略,简而言之就是对个人和小公司不收费,对大公司收费。

那么,是时候开始试一下 containerd + lima这个开源、免费的Docker替代品。

什么是containerd?

containerd是一个标准化的容器运行时,可以被其他系统很方便的集成。正是由于这个原因,containerd也成为了Kubernetes的默认容器运行时。

但是在containerd的目标聚焦在和其他系统集成,所以它的默认命令行工具(crictl)也不是很好用,和docker也不兼容。

后来,nttlabs贡献了一个名为nerdctl的containerd客户端,可以兼容docker命令行工具。于是我们就可以使用nerdctl来作为docker的替代品了。

nerdctl不仅与docker兼容,而且还支持了更多的功能:

  • 支持containerd的命名空间查看,nerdctl不仅可以管理Docker容器,也可以直接管理本地的的Kubernetes pod
  • 支持将Docker Image Manifest镜像转换为OCI镜像、estargz镜像
  • 支持OCIcrypt(镜像加密)

什么是lima?

containerd要在macOS上使用,需要安装虚拟机、然后在虚拟机配置containerd,这个过程很浪费时间。

阅读更多

如何优雅关闭maven-default-http-blocker?

最近升级Maven到3.8.1后,mvn编译的时候总是提示拉不到依赖,报错如下:

Could not validate integrity of download from http://0.0.0.0/...

全部报错如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[WARNING] Could not validate integrity of download from http://0.0.0.0/com/alibaba/nacos/nacos-client-mse-extension/1.4.2-SNAPSHOT/maven-metadata.xml
org.eclipse.aether.transfer.ChecksumFailureException: Checksum validation failed, expected <!doctype but is 18420d7f1430a348837b97a31a80e374e3b00254
at org.eclipse.aether.connector.basic.ChecksumValidator.validateExternalChecksums (ChecksumValidator.java:174)
at org.eclipse.aether.connector.basic.ChecksumValidator.validate (ChecksumValidator.java:103)
at org.eclipse.aether.connector.basic.BasicRepositoryConnector$GetTaskRunner.runTask (BasicRepositoryConnector.java:460)
at org.eclipse.aether.connector.basic.BasicRepositoryConnector$TaskRunner.run (BasicRepositoryConnector.java:364)
at org.eclipse.aether.util.concurrency.RunnableErrorForwarder$1.run (RunnableErrorForwarder.java:75)
at org.eclipse.aether.connector.basic.BasicRepositoryConnector$DirectExecutor.execute (BasicRepositoryConnector.java:628)
at org.eclipse.aether.connector.basic.BasicRepositoryConnector.get (BasicRepositoryConnector.java:235)
at org.eclipse.aether.internal.impl.DefaultMetadataResolver$ResolveTask.run (DefaultMetadataResolver.java:573)
at org.eclipse.aether.util.concurrency.RunnableErrorForwarder$1.run (RunnableErrorForwarder.java:75)
at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:630)
at java.lang.Thread.run (Thread.java:832)
[WARNING] Checksum validation failed, expected <!doctype but is 18420d7f1430a348837b97a31a80e374e3b00254 from maven-default-http-blocker for http://0.0.0.0/com/alibaba/nacos/nacos-client-mse-extension/1.4.2-SNAPSHOT/maven-metadata.xml
Downloaded from maven-default-http-blocker: http://0.0.0.0/com/alibaba/nacos/nacos-client-mse-extension/1.4.2-SNAPSHOT/maven-metadata.xml (63 kB at 19 kB/s)

从关键字maven-default-http-blocker可以找到相关资料。

简而言之,如果使用HTTP协议下载依赖,可能会导致中间人攻击。比如,本来想下载一个nacos-client的,结果下载的结果中被插入了恶意代码,然后开发人员运行了一下,黑客就能获得开发人员的计算机控制权了。

所以Maven 3.8.1就禁止了所有HTTP协议的Maven仓库。

详情见Maven 3.8.1的发布日志

问题是在日常开发中,我们经常会用到公司内部的maven仓库。这些仓库一般都是http协议,Maven 3.8.1禁止了http协议,那么就会导致开头的报错。

于是查了下,可以按照如下方式关闭:

阅读更多

Golang小技巧-在单个仓库中支持多个 go mod 模块

背景

最近在写Go,有一个项目是多模块的,版本的发布都是在一起的,为了其他项目使用这些模块,所以需要在一个仓库中实现多个模块的发布。

仓库结构

仓库结构如下:

1
2
3
4
5
6
7
8
.
├── README.md
├── a
│   ├── a.go
│   └── go.mod
└── b
├── b.go
└── go.mod

其中a/go.mod使用如下命令生成:

1
go mod init github.com/robberphex/go-test-multi-module/a

版本发布

由于是多模块,所以使用不同的tag。tag名字统一为<模块名>/<版本号>

比如现在的tag有:

阅读更多

Linux下的环境变量到底有什么限制?

前言

最近在排查一个eureka问题,发现客户设置了环境变量eureka.client.serviceUrl.defaultZone
于是在本地尝试复现这个现场,发现我设置不了这个环境变量,提示:

1
2
# export eureka.client.serviceUrl.defaultZone=http://localhost:8761/
sh: export: eureka.client.serviceUrl.defaultZone: bad variable name

但是在k8s中,可以给pod设置带点的环境变量。

于是看了下Linux中环境变量的限制。

首先看一下为什么export会报错

以busybox为例,export是一个内置命令,所以执行这个命令的时候,直接调用了 exportcmd 函数

这个函数的核心逻辑就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 函数setvar
static struct var *
setvar(const char *name, const char *val, int flags)
{
//……
q = endofname(name);
p = strchrnul(q, '=');
namelen = p - name;
if (!namelen || p != q)
ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
//……
}

// endofname.c
#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))

const char* FAST_FUNC
endofname(const char *name)
{
if (!is_name(*name))
return name;
while (*++name) {
if (!is_in_name(*name))
break;
}
return name;
}

仔细观察endofname的逻辑就会发现,shell对于环境变量名字的限制是:

阅读更多

虚引用真的不影响对象的生命周期吗?

Java的四大引用,大家都很熟悉吧:

  • 强应用:正常代码中的引用。一个对象能通过强应用访问到,那它就永远不会被回收
  • 软引用:比强引用弱一级的引用,内存不足时引用指向的对象会被回收
  • 弱引用:比软引用弱一级的引用,下一次GC时指向对象会被回收
  • 虚引用

最后一个虚应用是今天要讨论的。很多文章都是这么写的:

一个对象是否有虚引用存在,对其生存不会产生任何影响。

事实上,这个是错的。正确的表述是:

在Java 8以及之前的版本中,在虚引用回收后,虚引用指向的对象才会回收。在Java 9以及更新的版本中,虚引用不会对对象的生存产生任何影响。

一个示例

首先用Java 8,带上-Xmx10m -XX:+HeapDumpOnOutOfMemoryError参数运行如下代码:

Main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public final class Main {

public static void main(String[] args) throws InterruptedException {
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
PhantomReference<byte[]> ref = new PhantomReference<>(new byte[1024 * 1024 * 5], queue);

System.out.println(queue.poll());
System.out.println("第一次gc");
System.gc();
Thread.sleep(300L);
System.out.println(queue.poll());
System.out.println("第二次gc");
System.gc();
byte[] bytes1 = new byte[1024 * 1024 * 6];
System.out.println("ending");
}
}

你猜猜结果是什么?

阅读更多

如何从Spring Cloud Config迁移到阿里云ACM

从零开始使用Spring Cloud Config中,我们简单了解了下Spring Cloud Config,它提供了一套配置管理的解决方案。

但是Spring Cloud Config需要自己搭建config-server,还需要结合eureka来实现高可用;如果需要实时更新配置,还需要Spring Cloud Bus。实在是过于繁琐。

而阿里云ACM(应用配置管理)则提供了一整套完整的解决方案:

  1. 与Spring生态紧密集成
    和Spring Cloud Config一样,ACM通过Envirment和PropertySource与Spring结合。
  2. 配置简单
    只需要在ACM上开一个namespace,然后配置client,就能享受到获取配置、自动更新配置功能。
  3. 免费

阿里云ACM相比与Spring Cloud Config,提供了一揽子配置解决方案,不需要在借助Spring Cloud Bus和eureka了。

我们以上一篇文章中的spring-cloud-config-client作为示例。

迁移配置文件

在使用ACM之前,我们需要将现有的配置迁移到ACM中,要不然直接切换过去没法获取任何配置,会导致应用无法正常运行。

为每个环境(此处的环境就是Spring中的profile)创建一个ACM的命名空间。

比如dev对应namespace dev,test对应test命名空间。

阅读更多

从零开始使用Spring Cloud Config

在传统的Java应用中,应用配置都是直接存储在代码库中(比如放在application.yml中),配置的更新需要修改发布包并重新发布。

而配置的审计也会和代码夹杂,需要从众多提交中中找到配置变更的提交,很难独立完成,也很难追溯何时修改了某一项配置。

随着微服务的发展,服务越来越多、配置也越来越多,这种原始的配置管理方式,很难适应分布式环境。作为开发者,还是需要配置能单独地管理和审计。

Spring Cloud Config就提供了配置管理和审计的解决方案。

Spring Cloud Config有如下特点:

  1. 与Spring生态紧密集成
    提供了和Spring中的EnvironmentPropertySource一致的抽象,能够和原有的配置文件很好的对应起来。
  2. 可以动态更新
    只需要添加注解,不需要重启就能更新配置项
  3. 支持多种provider
    配置仓库可以是git仓库、本地文件、数据库等等。

开始使用Spring Cloud config

Spring Cloud Config 分为两部分:

Spring Cloud Config Server负责从配置仓库中获取配置,并给client提供获取配置的HTTP接口

Spring Cloud Config Client负责给目标应用提供从config-server获取配置、更新配置的能力。

阅读更多

Jackson如何反序列化Java14中的record类型?

Java14近日发布,其中引入了新的record类型,虽然这是个预览特性,但是也不妨碍我们尝试下。

record类型

record类型和普通的类相比,有几个特点

  1. 每个字段都是private final的,类本身是final
  2. 类只有一个所有传递所有参数的构造函数
  3. 每个字段会自动会有一个get方法,但是方法名和字段名一致(没有get前缀
  4. 可以给字段添加注解,支持FIELD、METHOD和PARAMETER(如果一个注解是FIELD、METHOD和PARAMETER的,那么注解会在字段、get方法和构造函数参数上同时出现)
  5. toStringequalshashCode方法都是自带的

这几个特点决定了这个非常适合做DO(Model),DTO(比如RPC中定义的各种entity,http返回的json)。

由于业务中经常用到jackson来序列化反序列化对象,今天我们就来试一试jackson对record类型的支持。

如何使用jackson反序列化record对象

如果我们按照原来的方式来反序列化record(如果字段上没有@JsonProperty注解的话),会直接报错:
InvalidDefinitionException: Cannot construct instance of Person (no Creators, like default construct, exist)

这是因为jackson没法找到构造函数和字段的映射,所以我们自己指定默认的映射:

1
2
3
4
5
6
7
8
9
10
11
12
13
// from https://gist.github.com/youribonnaffe/03176be516c0ed06828ccc7d6c1724ce
JacksonAnnotationIntrospector implicitRecordAI = new JacksonAnnotationIntrospector() {
@Override
public String findImplicitPropertyName(AnnotatedMember m) {
if (m.getDeclaringClass().isRecord()) {
if (m instanceof AnnotatedParameter parameter) {
return m.getDeclaringClass().getRecordComponents()[parameter.getIndex()].getName();
}
}
return super.findImplicitPropertyName(m);
}
};
objectMapper.setAnnotationIntrospector(implicitRecordAI);
阅读更多