国内手机制造商注意:未经认证的设备将被禁止运行谷歌应用程序

开源开放的生态系统造就了Android的丰富多样,Google向主要手机商场分发“纯净官方”版应用的同时,市面上还存在数以百万计的衍生产品。如果你的Android设备已经得到了Google的认证,那么自然允许分发Google的官方应用(例如Play Store和Maps等等)。

但是以亚马逊Fire OS平板和国内大多数Android手机为代表的未认证设备,理论上是应该无法分发的。但市面上很多其他方法,技术人员通常会采取 sideloading Google APP的方式,而且其中一部分厂商自然会因为利益等原因用在未经许可的情况下分发Google APP。

为此,Google表示要开始整治这种行为了。

现在当用户尝试运行Google APP的时候,Google会对Android系统镜像的编译日期进行审查。未经认证的设备且运行2018年3月16日之后编译的Android版本,那么Googler APP将会无法运行。

所幸的是,Google为定制ROM用户提供了解决方案。用户可以通过Android ID对设备进行注册,从而允许Google应用在设备上运行。每名用户限制100台设备,或许对于ROM开发者来说会有些麻烦,但是对于大部分人来说已经足够。

反射器3适用于MAC(IOS屏幕镜像软件)v3 2.0最新版本

Reflector是一个简单的AirPlay接收器,用于帮助您在桌面上镜像iPhone,iPad或iPod Touch屏幕。Reflector使您可以轻松地在大屏幕上展示移动应用程序,而无需复杂的设置。您可以直接在Mac上玩游戏,观看电影,演示iOS应用程序。(下载地址https://www.mac69.com/mac/6692.html)

Reflector for mac软件介绍

Reflector允许您将AirPlay镜像到iPhone或iPad上。无需电线或复杂设置即可将手机,平板电脑或计算机镜像到大屏幕。从掌中呈现,教导或娱乐。Reflector使您可以比以往更轻松地共享设备屏幕。

Reflector 3 Mac版软件功能

AirPlay,Google Cast和Miracast在一起

Reflector将Mac,Windows和Android设备转换为AirPlay,Google Cast或Miracast接收器。使用Reflector在更大的屏幕上无线显示和录制iPhone,iPad,Chromebook,Android设备和Windows平板电脑。

Reflector 3结合了Apple TV,Chromecast和Windows设备的功能,创建了一个功能强大的多合一镜像接收器。

没有额外的要求

反射板适用于您每天使用的设备,不需要任何适配器或电缆。反射器是完全无线的,所以您可以将电缆和适配器放在后面。

怎么运行的

Reflector 3 for Mac与使用Google Cast,Miracast和AirPlay镜像的设备进行通信。这允许这些设备连接到反射器并且在没有任何额外软件的情况下镜像它们的屏幕。反射器超越了基本的屏幕镜像,并且包含市场上其他接收器中未发现的许多高级功能。

课堂效益

Reflector 3 for Mac使移动技术的合作变得简单。它允许教育工作者从他们的手掌和学生那里教授课堂上的任何地方。

多个设备

同时连接并显示任何类型的多个设备。

安全

教育者决定学生何时以及如何分享和展示他们的作品。

设备控制

控制中心可以轻松管理所有连接的学生设备。

怎么运行的

Reflector 3 Mac 特别版安装在教师的电脑上。其他教室设备可以无线连接到该计算机。将教师计算机连接到投影仪或外部显示器,以向全班学生展示镜像设备。

反射器超越了基本的屏幕镜像,并且包含市场上其他接收器中未发现的许多高级功能。

反射器导演

Reflector Director旨在让教师离开计算机并继续与镜像学生设备交互。反射器学生

Reflector Student帮助使用iOS设备的学生连接到Reflector Teacher并查看对等设备。

Reflector 3 Mac版软件特征

强大的屏幕镜像功能

反射器3不仅仅是您的平均屏幕镜像接收器。这些是你会谈论的功能。

屏幕镜像

将任何类型的一个或多个设备无线镜像到您的计算机。

记录

记录任何镜像设备。添加配音音频并在需要时自定义录制。

设备控制

强调,隐藏或显示设备,并从一个中心位置选择设备帧。

安全

防止不必要的连接,并限制只有那些应该镜像的用户。

将家庭视频或电影流到Reflector 3,或在更大的屏幕上共享照片。

Reflector 3 Mac版软件特点

AirPlay的

反射器可以接收来自Mac,iPad,iPhone和iPod touch的镜像连接。

Miracast时

将带有内置Miracast的Windows设备连接到反射板3.仅与Windows兼容反射板3。

Google Cast

Reflector轻松接收Chromebook和Android屏幕镜像和流媒体连接。

仅音频流式传输

反射器可以充当纯音频接收器。将您最喜爱的音乐流到连接到较大扬声器的电脑上。

连接多个设备

内置AirPlay,Google Cast或Miracast的设备可以同时连接到Reflector 3。

设备框架

选择设备框架可使镜像屏幕看起来像您的真实设备,或选择其他框架来测试新外观。

全屏模式

消除来自其他应用程序或桌面项目的干扰,并选择背景颜色或图像以配合镜像屏幕。

与AirParrot兼容

将AirParrot添加到您的设备并连接到Reflector 3以获得更高级的屏幕镜像选项。

连接提示

在屏幕上显示之前选择允许或拒绝设备连接。

Reflector 3新功能

Miracast与Reflector for Windows的兼容性

•iPhone X镜架

•全分辨率屏幕截图按钮

•新的,易于使用的界面

•屏幕显示连接的设备

•大大提高了性能,稳定性和速度

Reflector一个简单,易用且实用的AirPlay接收器,Reflector让您在Mac上无线显示iOS设备的屏幕,Reflector 3并支持同时显示多个设备,享受全屏模式下提供的无干扰iOS录制环境,欢迎大家下载Reflector for mac最新版

使用cloudflare制作GitHub“镜像站”

转自:https://kermsite.com/p/cf-GitHub/

由于格式问题,部分链接、表格可能会失效,若失效请访问原文

——可以用于间接访问一些打不开的网站,例如

Dec 13, 2021

阅读时长: 4 分钟

使用说明

本项目仍然是使用cloudflare这位慷慨无私的云服务提供商的免费服务workers。

按照本教程进行配置,你可以自己搭建一个Github的镜像站点,以解决某些情况下无法访问的问题。

注意:请遵守当地法律法规,尊重知识产权,维护清朗的网络环境,人人有责

关于cloudflare(如已注册可以跳过)

简介

  • 官网:https://www.cloudflare.com/

注意:Cloudflare是有官方中文的,在界面的右上角可以选择语言。

Cloudflare是一家国外的良心CDN加速服务提供商,最近他家的服务也是在不断扩展,并且难能可贵的是他家服务全部免费,并且免费限额非常之高。比如CDN就是纯免费并且没有流量限制的。而且用它的DNS也不容出现备案问题。

我之前在freenom注册了几个免费域名,一开始是托管在dnspod,现在全部转移到cloudflare上面了。这里就简单讲讲怎么用上他的免费域名服务。~~至于worker这类更加复杂(灵活)的服务就靠大家自己探索了。~~顺便把worker和page也说一下。

从官网注册之后,会跳转到转到https://dash.cloudflare.com/,接下来的步骤都是在控制台进行的。

开启worker

可以将worker理解为cloudflare的一款serverless平台,这个平台的一大优点就是自带CDN。按照官方的说法:

构建无服务器应用程序并在全球范围内即时部署,从而获得卓越的性能、可靠性和规模性。

在控制面板主页左侧可以找到workers。

如图选择

如图所示,中间可以创建服务,右侧显示每天的额度,如果只是搭建个人服务这些额度绰绰有余了。下方会显示所有已经搭建的服务。

额度很高

创建服务的界面,其实我们只要稍微配置一下这个服务名称,因为会涉及到之后访问的链接的问题:

注意服务名称

这样我们的服务就搭建好了。但是如何进行代码编辑呢?等待部署完成,转到控制界面,可以找到右下角有一个快速编辑的按钮。

个人觉得这个设计十分反人类

在这里你就可以提交对于代码的修改了。关于Worker的代码配置,我看了一下文档,感觉有点复杂,还是基于javascript。推荐使用现成的代码实例。左边修改代码,下方部署,右侧可以选择预览窗口,以及打开链接。

一般我们就打开这个窗口,把代码复制进去,保存部署,然后直接访问链接就好了。

编辑界面

代码

原来是设置为Google的,我这里改成Github了。全部复制贴到worker里面即可。

// 你要镜像的网站.
const upstream = 'www.github.com'

// 镜像网站的目录,比如你想镜像某个网站的二级目录则填写二级目录的目录名,镜像 google 用不到,默认即可.
const upstream_path = '/'

// 镜像站是否有手机访问专用网址,没有则填一样的.
const upstream_mobile = 'www.github.com'

// 屏蔽国家和地区.
const blocked_region = ['KP', 'SY', 'PK', 'CU']

// 屏蔽 IP 地址.
const blocked_ip_address = ['0.0.0.0', '127.0.0.1']

// 镜像站是否开启 HTTPS.
const https = true

// 文本替换.
const replace_dict = {
'$upstream': '$custom_domain',
'//github.com': ''
}

// 以下保持默认,不要动
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request));
})

async function fetchAndApply(request) {

const region = request.headers.get('cf-ipcountry').toUpperCase();
const ip_address = request.headers.get('cf-connecting-ip');
const user_agent = request.headers.get('user-agent');

let response = null;
let url = new URL(request.url);
let url_hostname = url.hostname;

if (https == true) {
url.protocol = 'https:';
} else {
url.protocol = 'http:';
}

if (await device_status(user_agent)) {
var upstream_domain = upstream;
} else {
var upstream_domain = upstream_mobile;
}

url.host = upstream_domain;
if (url.pathname == '/') {
url.pathname = upstream_path;
} else {
url.pathname = upstream_path + url.pathname;
}

if (blocked_region.includes(region)) {
response = new Response('Access denied: WorkersProxy is not available in your region yet.', {
status: 403
});
} else if (blocked_ip_address.includes(ip_address)) {
response = new Response('Access denied: Your IP address is blocked by WorkersProxy.', {
status: 403
});
} else {
let method = request.method;
let request_headers = request.headers;
let new_request_headers = new Headers(request_headers);

new_request_headers.set('Host', url.hostname);
new_request_headers.set('Referer', url.hostname);

let original_response = await fetch(url.href, {
method: method,
headers: new_request_headers
})

let original_response_clone = original_response.clone();
let original_text = null;
let response_headers = original_response.headers;
let new_response_headers = new Headers(response_headers);
let status = original_response.status;

new_response_headers.set('access-control-allow-origin', '*');
new_response_headers.set('access-control-allow-credentials', true);
new_response_headers.delete('content-security-policy');
new_response_headers.delete('content-security-policy-report-only');
new_response_headers.delete('clear-site-data');

const content_type = new_response_headers.get('content-type');
if (content_type.includes('text/html') && content_type.includes('UTF-8')) {
original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname);
} else {
original_text = original_response_clone.body
}

response = new Response(original_text, {
status,
headers: new_response_headers
})
}
return response;
}

async function replace_response_text(response, upstream_domain, host_name) {
let text = await response.text()

var i, j;
for (i in replace_dict) {
j = replace_dict[i]
if (i == '$upstream') {
i = upstream_domain
} else if (i == '$custom_domain') {
i = host_name
}

if (j == '$upstream') {
j = upstream_domain
} else if (j == '$custom_domain') {
j = host_name
}

let re = new RegExp(i, 'g')
text = text.replace(re, j);
}
return text;
}

async function device_status(user_agent_info) {
var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (var v = 0; v < agents.length; v++) {
if (user_agent_info.indexOf(agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
}

效果

现在已经无法打开Google了,显示错误(被撸爆也是很正常的吧)。

应该是Cloudflare的IP被屏蔽了

但是Github还是可以使用的:

可以访问

DEMO:GitHub: Where the world builds software · GitHub (kermshare.workers.dev)

可以下载,下载链接也被替换为了workers的地址,速度还不错。

https://kermgithub.kermshare.workers.dev/Fndroid/clash_for_windows_pkg/releases/download/0.19.1/Clash.for.Windows.Setup.0.19.1.exe

速度还行

附录

参考文献

  1. 使用 Cloudflare Workers™ 制作镜像站,可访问谷歌-何先生 (hexsen.com)
  2. Introduction | Booster Labs
  3. xiaoyang-sde/rocket-booster: Lightweight and scalable reverse proxy and load balancing library built for Cloudflare Workers (github.com)

说明:本项目最初的源码应该是来自2,3两处

但是现在已经更新到新版了。

版权信息

本文原载于kermsite.com,复制请保留原文出处。

Docker最佳做法:简化镜像的5种方法

精简Docker镜像大小的必要性

Docker镜像由很多镜像层(Layers)组成(最多127层),镜像层依赖于一系列的底层技术,比如文件系统(filesystems)、写时复制(copy-on-write)、联合挂载(union mounts)等技术,你可以查看Docker社区文档以了解更多有关Docker存储驱动的内容,这里就不再赘述技术细节。总的来说,Dockerfile中的每条指令都会创建一个镜像层,继而会增加整体镜像的尺寸。

下面是精简Docker镜像尺寸的好处:

1、减少构建时间

2、减少磁盘使用量

3、减少下载时间

4、因为包含文件少,攻击面减小,提高了安全性

5、提高部署速度

五点建议减小Docker镜像尺寸

01 优化基础镜像

优化基础镜像的方法就是选用合适的更小的基础镜像,常用的 Linux 系统镜像一般有 Ubuntu、CentOs、Alpine,其中Alpine更推荐使用。大小对比如下:

lynzabo@ubuntu ~/s> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 74f8760a2a8b 8 days ago 82.4MB
alpine latest 11cd0b38bc3c 2 weeks ago 4.41MB
centos 7 49f7960eb7e4 7 weeks ago 200MB
debian latest 3bbb526d2608 8 days ago 101MB
lynzabo@ubuntu ~/s>

Alpine是一个高度精简又包含了基本工具的轻量级Linux发行版,基础镜像只有4.41M,各开发语言和框架都有基于Alpine制作的基础镜像,强烈推荐使用它。

查看上面的镜像尺寸对比结果,你会发现最小的镜像也有4.41M,那么有办法构建更小的镜像吗?答案是肯定的,例如 gcr.io/google_containers/pause-amd64:3.1 镜像仅有742KB。为什么这个镜像能这么小?在为大家解密之前,再推荐两个基础镜像:

>>>> scratch镜像

scratch是一个空镜像,只能用于构建其他镜像,比如你要运行一个包含所有依赖的二进制文件,如Golang程序,可以直接使用scratch作为基础镜像。现在给大家展示一下上文提到的Google pause镜像Dockerfile:

FROM scratch
ARG ARCH
ADD bin/pause-${ARCH} /pause
ENTRYPOINT [“/pause”]

Google pause镜像使用了scratch作为基础镜像,这个镜像本身是不占空间的,使用它构建的镜像大小几乎和二进制文件本身一样大,所以镜像非常小。当然在我们的Golang程序中也会使用。对于一些Golang/C程序,可能会依赖一些动态库,你可以使用自动提取动态库工具,比如ldd、linuxdeployqt等提取所有动态库,然后将二进制文件和依赖动态库一起打包到镜像中。

>>>> busybox镜像

scratch是个空镜像,如果希望镜像里可以包含一些常用的Linux工具,busybox镜像是个不错选择,镜像本身只有1.16M,非常便于构建小镜像。

02 串联 Dockerfile 指令

大家在定义Dockerfile时,如果太多的使用RUN指令,经常会导致镜像有特别多的层,镜像很臃肿,而且甚至会碰到超出最大层数(127层)限制的问题,遵循 Dockerfile 最佳实践,我们应该把多个命令串联合并为一个 RUN(通过运算符&&和/ 来实现),每一个 RUN 要精心设计,确保安装构建最后进行清理,这样才可以降低镜像体积,以及最大化的利用构建缓存。

下面是一个优化前Dockerfile:

FROM ubuntu
ENV VER 3.0.0
ENV tarBALL https://download.redis.io/releases/redis-$VER.tar.gz
# ==> Install curl and helper tools…
RUN apt-get update
RUN apt-get install -y curl make gcc
# ==> Download, compile, and install…
RUN curl -L $TARBALL | tar zxv
WORKDIR redis-$VER
RUN make
RUN make install
#…
# ==> Clean up…
WORKDIR /
RUN apt-get remove -y –auto-remove curl make gcc
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /redis-$VER
#…
CMD [“redis-server”]

构建镜像,名称叫 test/test:0.1。

我们对Dockerfile做优化,优化后Dockerfile:

FROM ubuntu
ENV VER 3.0.0
ENV TARBALL https://download.redis.io/releases/redis-$VER.tar.gz
RUN echo “==> Install curl and helper tools…” &&
apt-get update &&
apt-get install -y curl make gcc &&
echo “==> Download, compile, and install…” &&
curl -L $TARBALL | tar zxv &&
cd redis-$VER &&
make &&
make install &&
echo “==> Clean up…” &&
apt-get remove -y –auto-remove curl make gcc &&
apt-get clean &&
rm -rf /var/lib/apt/lists/* /redis-$VER
#…
CMD [“redis-server”]

构建镜像,名称叫 test/test:0.2。

对比两个镜像大小:

root@k8s-master:/tmp/iops# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/test 0.2 58468c0222ed 2 minutes ago 98.1MB
test/test 0.1 e496cf7243f2 6 minutes ago 307MB
root@k8s-master:/tmp/iops#

可以看到,将多条RUN命令串联起来构建的镜像大小是每条命令分别RUN的三分之一。

提示:为了应对镜像中存在太多镜像层,Docker 1.13版本以后,提供了一个压扁镜像功能,即将 Dockerfile 中所有的操作压缩为一层。这个特性还处于实验阶段,Docker默认没有开启,如果要开启,需要在启动Docker时添加-experimental 选项,并在Docker build 构建镜像时候添加 –squash 。我们不推荐使用这个办法,请在撰写 Dockerfile 时遵循最佳实践编写,不要试图用这种办法去压缩镜像。

03 使用多阶段构建

Dockerfile中每条指令都会为镜像增加一个镜像层,并且你需要在移动到下一个镜像层之前清理不需要的组件。实际上,有一个Dockerfile用于开发(其中包含构建应用程序所需的所有内容)以及一个用于生产的瘦客户端,它只包含你的应用程序以及运行它所需的内容。这被称为“建造者模式”。Docker 17.05.0-ce版本以后支持多阶段构建。使用多阶段构建,你可以在Dockerfile中使用多个FROM语句,每条FROM指令可以使用不同的基础镜像,这样您可以选择性地将服务组件从一个阶段COPY到另一个阶段,在最终镜像中只保留需要的内容。

下面是一个使用COPY –from 和 FROM … AS … 的Dockerfile:

# Compile
FROM golang:1.9.0 AS builder
WORKDIR /go/src/v9.git…com/…/k8s-monitor
COPY . .
WORKDIR /go/src/v9.git…com/…/k8s-monitor
RUN make build
RUN mv k8s-monitor /root
# Package
# Use scratch image
FROM scratch
WORKDIR /root/
COPY –from=builder /root .
EXPOSE 8080
CMD [“/root/k8s-monitor”]

构建镜像,你会发现生成的镜像只有上面COPY 指令指定的内容,镜像大小只有2M。这样在以前使用两个Dockerfile(一个Dockerfile用于开发和一个用于生产的瘦客户端),现在使用多阶段构建就可以搞定。

04 构建业务服务镜像技巧

Docker在build镜像的时候,如果某个命令相关的内容没有变化,会使用上一次缓存(cache)的文件层,在构建业务镜像的时候可以注意下面两点:

1、不变或者变化很少的体积较大的依赖库和经常修改的自有代码分开;

2、因为cache缓存在运行Docker build命令的本地机器上,建议固定使用某台机器来进行Docker build,以便利用cache。

下面是构建Spring Boot应用镜像的例子,用来说明如何分层。其他类型的应用,比如Java WAR包,Nodejs的npm 模块等,可以采取类似的方式。

1、在Dockerfile所在目录,解压缩maven生成的jar包

$ unzip <path-to-app-jar>.jar -d app

2、Dockerfile 我们把应用的内容分成4个部分COPY到镜像里面:其中前面3个基本不变,第4个是经常变化的自有代码。最后一行是解压缩后,启动spring boot应用的方式。

FROM openjdk:8-jre-alpine
LABEL maintainer “opl-xws@xiaomi.com”
COPY app/BOOT-INF/lib/ /app/BOOT-INF/lib/
COPY app/org /app/org
COPY app/META-INF /app/META-INF
COPY app/BOOT-INF/classes /app/BOOT-INF/classes
EXPOSE 8080
CMD [“/usr/bin/java”, “-cp”, “/app”, “org.springframework.boot.loader.JarLauncher”]

这样在构建镜像时候可大大提高构建速度。

05 其他优化办法

当然,除了以上4类办法外,还有其他优化办法可以精简镜像;

1. RUN命令中执行apt、apk或者yum类工具技巧

如果在RUN命令中执行apt、apk或者yum类工具,可以借助这些工具提供的一些小技巧来减少镜像层数量及镜像大小。举几个例子:

(1)在执行apt-get install -y 时增加选项— no-install-recommends ,可以不用安装建议性(非必须)的依赖,也可以在执行apk add 时添加选项–no-cache 达到同样效果;

(2)执行yum install -y 时候, 可以同时安装多个工具,比如yum install -y gcc gcc-c++ make …。将所有yum install 任务放在一条RUN命令上执行,从而减少镜像层的数量;

(3)组件的安装和清理要串联在一条指令里面,如 apk –update add php7 && rm -rf /var/cache/apk/* ,因为Dockerfile的每条指令都会产生一个文件层,如果将apk add …和 rm -rf … 命令分开,清理无法减小apk命令产生的文件层的大小。Ubuntu或Debian可以使用 rm -rf /**var**/lib/apt/lists/* 清理镜像中缓存文件;CentOS等系统使用yum clean all 命令清理。

2. 压缩镜像

Docker 自带的一些命令还能协助压缩镜像,比如 export 和 import

$ docker run -d test/test:0.2
$ docker export 747dc0e72d13 | docker import – test/test:0.3

使用这种方式需要先将容器运行起来,而且这个过程中会丢失镜像原有的一些信息,比如:导出端口,环境变量,默认指令。

查看这两个镜像history信息,如下,可以看到test/test:0.3 丢失了所有的镜像层信息:

root@k8s-master:/tmp/iops# docker history test/test:0.3
IMAGE CREATED CREATED BY SIZE COMMENT
6fb3f00b7a72 15 seconds ago 84.7MB Imported from –
root@k8s-master:/tmp/iops# docker history test/test:0.2
IMAGE CREATED CREATED BY SIZE COMMENT
58468c0222ed 2 hours ago /bin/sh -c #(nop) CMD [“redis-server”] 0B
1af7ffe3d163 2 hours ago /bin/sh -c echo “==> Install curl and helper… 15.7MB
8bac6e733d54 2 hours ago /bin/sh -c #(nop) ENV TARBALL=https://downlo… 0B
793282f3ef7a 2 hours ago /bin/sh -c #(nop) ENV VER=3.0.0 0B
74f8760a2a8b 8 days ago /bin/sh -c #(nop) CMD [“/bin/bash”] 0B
<missing> 8 days ago /bin/sh -c mkdir -p /run/systemd && echo ‘do… 7B
<missing> 8 days ago /bin/sh -c sed -i ‘s/^#s*(deb.*universe)$… 2.76kB
<missing> 8 days ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 8 days ago /bin/sh -c set -xe && echo ‘#!/bin/sh’ > /… 745B
<missing> 8 days ago /bin/sh -c #(nop) ADD file:5fabb77ea8d61e02d… 82.4MB
root@k8s-master:/tmp/iops#

社区里还有很多压缩工具,比如Docker-squash ,用起来更简单方便,并且不会丢失原有镜像的自带信息,大家有兴趣可以试试。

关于DNS根服务器映像,您不知道的五个事实

对“断网”讨论的一个重点被聚焦在域名根服务器,一些“科普”的方式,某些“脑补”的形式,众口一词地认为“不用担心”。其信誓旦旦的理由是:“我国已拥有域名根镜像服务器”。事实上,这是一个悖逆科学真相的严重认知误区。换句话说,应对“断网”的风险和隐患,镜像的域名根服务器不仅无济于事,而且于事无补。

本文可供网络信息领域的专业技术人员、从业人员和监管、监察、审计、执法人员作为科学论证的依据与参考。

域名空间是一个生态系统(ecosystem),其服务体系如图 1 所示,其中“递归域名解析服务器”是域名空间的入口:

虽然域名根服务器在域名解析服务中是一个重要的环节,但在域名空间中仅当各个环节相互依存,方能构成一个能够可靠运行的生态系统。反之,任何一个环节出现安全问题,都可能导致域名解析服务中断(“断服”),或是网络瘫痪(“断网”)。

据美国国防部术语词典的定义,“供应链”(Supply Chain)是:从原材料到向最终用户提供成品和服务相关联的(人的)活动。域名空间的生态系统可以被简化为一个“供应链”(图 2)。

从图 1 和图 2 可见,域名空间是一个复杂的生态系统。而应对“断网”的风险和隐患的本质是:供应链的安全和保障问题。因此,有必要澄清域名空间供应链中的五个重要的关联事实:

事实1:镜像的域名根服务器不能替代13个域名根系统;

事实2:镜像的域名根服务器采集的数据绝大多数是“垃圾”;

事实3:镜像的域名根服务器采集的是不完整和不确定数据;

事实4:镜像的域名根服务器没有指挥和控制的能力;

事实5:镜像的域名根服务器受制于服务系统软件。

结论:域名空间的应用和服务是网络通信的本源及指挥和控制的中枢,一旦发生“断服”,即是“断网”。应对“断服”的风险和隐患,镜像的域名根服务器不仅无济于事,而且于事无补。

事实1:基于域名空间生态系统的基本配置,镜像的域名根服务器不能替代 13 个域名根系统

1)根提示文件

当前,最终用户设备通常将所有 DNS 查询发送到递归解析服务器,该解析服务器在加入网络时通过 DHCP 配置。或者,用户可以将其设备配置为利用公共递归域名解析服务器(例如 Google 的 8.8.8.8 服务器)。递归域名解析服务器查询授权的域名服务器,使用所谓的“根提示文件”(Root Hints File)引导查找授权域名服务器的过程。根提示文件包含所有 13 个命名的授权域名根服务器的列表,它们表示为 A 根 到 M 根。对于每个命名的根,文件都包括域名服务器(NS 记录),以及域名服务器的 IPv4 地址(A 记录)和 IPv6 地址(AAAA 记录)。因此,根提示文件共有 39 个条目,大约 3KB(3000字节)。例如,A 根:

有了这些信息,递归解析服务器可以从根开始查询过程,并按照 DNS 响应中的指示进行。例如,来自根名称服务器的响应将指示递归解析服务器联系指定的顶级域名(TLD)服务器。根提示文件中的每个记录的缓存时间(TTL)为 360 万秒(约 42天)。该文件在很大程度上是静态的,但在某些情况下确实会发生变化,并且递归解析程序应在根提示文件缓存时间到期后获得一个新副本。分析表明,大量的递归域名解析服务器不会及时地更新其根提示文件。

2)根区文件

每个域名根服务器均根据根区文件(Root Zone File)的副本响应查询。该副本由因特网域名与数字地址分配机构(ICANN)官方提供,由 Verisign 公司分发。域名根服务器不会直接响应查询请求,而是会将域名查询的请求引导向顶级域名服务器,以获取查询域名中顶级域名的主机。例如,对“www.ccb.COM”的查询请求,域名根服务器将请求引导向“.com”的授权顶级域名服务器,继而将请求引导向“ccb.com”的权威域名服务器。

自 2009 年 4 月 28 日以来,研究人员每天在根区文件中存档一个记录。图 3 显示了每月 15 号在根区中的记录数。该图显示,经过一段时间的稳定之后,根区文件中的记录数量在 2014 年初至 2017 年初之间增长了五倍。这是由于顶级域名(TLD)数量增加所致,而 2013 年 6 月 15 日为 317 个 TLD,在 2017 年 6 月 15 日达到 1,534 个 TLD。经过这段时期的增长,规模已稳定到大约 22,000 个。当前的根区文件大约为 2.1MB(兆字节)。目前, 根区文件中的 NS、A 和 AAAA 记录的 TTL 为两天(172,800 秒)。因此,递归解析服务器可以缓存来自域名根服务器的响应,并在两天之内继续使用,以响应来自终端用户设备的域名查询请求,而无须访问顶级域名服务器,故不存在所谓的域名解析服务的“加速”。

根区文件是公开的,可以下载(约 2.1 兆字节)。其中的顶级域名(TLD)包括:国家/地区顶级域名(ccTLD)、通用顶级域名(gTLD)、新通用顶级域名(ngTLD),会经常发生变化,或更改域名服务器集(域名或 IP 地址)。相应的问题是:如何确定根区文件是更新的?

此外,DNSSEC 规范的主要作用是,当对根区文件中的所有记录进行验证时,需要一个根区 DNSSEC 密钥的副本,密钥随着时间而改变。故另一个问题是:如何确保根区文件内容的真实性?

因此,获取根区文件并不难,而重要的是:根区文件内容的合规性和可用性受到验证和约束,不允许“为所欲为”。

3)域名根服务器的范围

虽然只有 13 个命名的授权域名根服务器系统, 但是每个命名的域名根服务器系统都通过任播(Anycast)路由设置,由多个“副本”(设置的站点和节点)组成。

在 2019 年 5 月 15 日,统计了 985 个域名根服务器节点。图 4 显示了自 2015 年 3 月以来每个月 15 日的节点数量。由 E 根和 F 根引起的节点数量有几次大跳跃:

  1. 在 2016 年 1 月至 2 月之间,E 根增加了 45 个节点,
  2. 在 2017 年 4 月至 5 月之间,F 根添加了 81 个节点,
  3. 在 2017 年 11 月至 12 月之间,E 根添加了 85 个节点,F 根添加了 43 个节点。

除了这些大的跳跃之外,该图还表明:伴随域名根服务器节点的数量增加,成本也随着时间的推移稳步增长。

域名根系统的“副本”由“站点”(Site)和“节点”(Instance)组成,在同一个站点中可以有多个节点。例如:A 根有 16 个站点及 53 个节点。截至 2020 年 12 月 21 日,13 个域名根系统在全球共有 1143 个站点,1367 个节点。

请注意,由十二个不同的机构或企业运营的 13 个域名根服务器,每个机构或企业都有各自设置站点(和节点)的策略和意图。同时,在全球各地设置的站点和节点,需要满足前提条件,以及受到签署和承诺的相关协议条款的约束,而并不是所属地的“自治系统”(即不是域名根系统的所属权被转移或让渡)。

因此,在中国大陆不断增加域名根系统的站点(和节点),在应用上有助于提高因特网域名根系统的稳定性和可用性,也以增加自身的成本为因特网作出了贡献,但是作为“副本”没有替代域名根的可能性。

事实 2:基于对域名根服务器数据流量的研究和分析,域名根服务器镜像的数据绝大多数是“垃圾”

相关的研究和分析表明,在到达域名根服务器的查询请求中有 95% 以上是虚假的。例如,在 2008 年,专家们研究了八个域名根服务器的服务数据,分析结论是:只有 1.8% 的域名查询请求是有效的。国际计算机科学研究院(ICSI,位于美国加利福尼亚州伯克利,是一个独立的非营利性研究机构),在 2019 年 10 月对该结论进行了验证。为此,利用了由 J 根提供的数据集:分布在世界各地的 142 个 J 根的节点,2018 年 4 月 11 日的 24 小时流量。虽然这个分析仅是 13 个根服务器系统之一的数据,但之前大量研究和分析表明,到达域名根系统的数据流量类型在各个域名根系统之间是相当一致(均匀)的。

  • J 根 在 24 小时内接收了约 57 亿个域名查询请求,即平均每秒约有 6.6 万个域名查询请求。
  • 域名查询请求来自 410 万个递归解析服务器(不同的 IP 地址)。但是有 72.3 万个递归解析服务器仅查询虚假的顶级域名,这意味着最多只有 340 万递归解析服务器进行有用的工作。
  • 与之前的研究结论相同,分析发现大部分请求都是“垃圾”,有 35 亿条是虚假顶级域名查询,占比 61.0%。此外,假设递归解析服务器具有理想的缓存(故,递归解析服务器对同一顶级域名的查询请求在 24 小时的数据集中仅应出现一次),所以另外的 22 亿条查询请求(占比 38.4%)是无效的。这种情况下,虚假的查询(61.0%)和无效的请求(38.4%),表明有效的域名查询请求仅有0.5%。
  • 如果放宽对理想缓存的假设,而是允许递归解析服务器每 15 分钟可以重复对相同顶级域名的有效查询请求,则无效请求的数量为 20 亿条(35.7%)。在这种情况下,对虚假的查询(61.0%)和无效的请求(35.7%),表明有效的域名查询请求仅有 3.3%。或,在 57 亿条域名查询请求中,仅有 1.87 亿条域名查询请求是有效的。这意味着,J 根的每个节点平均每秒处理大约 15 个有效域名查询请求。

这证明了,之前多源研究的结论仍然成立:到达域名根服务器(包括所设置的站点和节点)的绝大多数数据流量都是垃圾。

事实 3:基于域名解析的不可逆过程,镜像的域名根服务器采集的是不完整和不确定数据

如事实 1 所述,递归域名解析服务器配置了根提示文件,并根据具体(或不同的)算法,在 13 个根域名系统(及其站点和节点)之间轮询。

这就是说,域名根服务器所接收的域名查询请求,来自于递归域名解析服务器(图 1),也是域名空间的入口。

根据对全球因特网公共(递归)域名服务器的监测统计,截至 2020 年 12 月 21 日(4:03),中国大陆拥有公共递归域名解析服务器(仅仅监测和统计端口 53/UDP)的数量为 768,103 台,占全球总数的 40.35%。而且,这个数量是在动态变化的,例如:10 月 22 日的数量为 1,106,552 台,占全球总数的 46.18%。

因此,不得不必须面对一个严峻的事实:中国域名空间的入口是完全敞开的,处于良莠不齐的混沌状况和监管缺失的状态。

是谁以及为谁提供如此(异常)大规模及动态变化的公共递归域名解析服务?

换句话说,镜像的域名根服务器的响应和服务是被动的(依赖于递归域名解析服务),在开放的域名空间入口,有多少有效的域名查询请求会指向镜像的域名根服务器,完全未知;因而其采集(和存储)的是不完整和不确定数据,故其作为应急备份也是盲目的!那么,为“公共”服务的动机和意图又是什么?

事实 4:基于域名解析的层次化结构,镜像的域名根服务器没有指挥和控制的能力

域名根服务器的应用功能是,按根区文件把接收到的域名查询请求,推送(referral)到顶级域名服务器(图 1)。

根据中国信息通信研究院的报告(信通院 2020-6),截至 2019 年 12 月,我国域名注册市场规模为 5,108.8 万个,其中,国家顶级域“.CN”域名 2,300 万个;“.COM”域名 1,566 万个;合计占我国域名市场的 75.7%。活跃域名数量分布:

从图 5 可见,我国活跃域名的解析超过 91% 依赖于境外服务。其中,运营顶级域名“.COM、.NET、.CC、.TV、.NAME”的 Verisign 公司,也是管理域名 A 根和 J 根的运营商,并且是作为美国国家电信和信息管理局(NTIA)管理域名空间的合约代理商。

这就是说,在应急响应时,镜像的域名根服务器不得不把绝大多数接收到的有效域名查询请求,“推送”到境外的顶级域名服务器(如 .COM)。显然,假设境外的域名根服务器对中国“断服”了,而顶级域名服务器却仍然提供对中国的“服务”,怎么可能?近乎荒谬。

所以,镜像的域名根服务器没有实用性和实战性!

事实 5:基于经验和教训以及现状,镜像的域名根服务器受制于服务系统软件

据了解,中国大陆的域名系统服务软件以采用开源和免费的模式为主,包括以“自研代码”包围“开源代码”的“马甲”式二次开发。

最近,Solarwinds 网管软件被认为遭到“供应链攻击”,迫使全球各国重新认识在网信安全领域中的“供应链安全”。虽然“供应链”的表面形式是有形的物(包括技术),但是其本质和实质是人的因素(包括政治和意识形态使然)。

以开源和免费的 DNS 软件 BIND 为例。2000 年由美国国土安全部资助开发的 BIND 版本 9(BIND9),从 2004 年 1 月 28 日的第一个版本(BIND 9.0.0)到 2020 年 9 月 17 日(BIND 9.17.5),共计发布了 634 个版本,其中 2020 年(至 9 月 17 日)发布了 15 个版本。

BIND 软件版本的生命周期一般为 1 年。但是,已知国内有相当多的单位和企业仍然在使用已被废弃的BIND软件版本,甚至所使用的 BIND 软件(数年前)版本号仍在网络中裸露着。

另一方面,BIND 软件版本不断地被发现高危的安全漏洞,仅 2019 年公开发布的漏洞补丁(CVE)就是 17 个,不包括一般性的代码错误。如果没有及时下载补丁,等同于开放被入侵的大门。但是,投入和花费资源维护“免费”DNS 软件的单位和企业却并不多见。必要和必须的技术、行政、执法监管普遍不及时、不完善、不严谨或原本就不落实。

尤其是,并不能排除软件中固有的“暗桩”、尚未公开的漏洞,或在下载开源软件的过程中被植入或夹带了病毒。在这样的域名应用和服务环境中,镜像的域名根服务器怎么可能“独善其身”!

综上,从知彼(和对标)的角度,2014 年 7 月,美国国家安全局(NSA)发起了“安全的科学”(简称 SOS)计划,持续至今。SOS 计划明确界定:网信安全领域是一门新兴的、跨学科的、整体的知识体系,旨在作为一项持续发展的长期研究工作。因此,(网信)“安全的科学”既没有唯一的路径,也没有任何捷径。

科学是证伪的过程。所述的五个事实都可以充分证明,镜像的域名根服务器,不能也无法应对潜在的“断服”(或“断网”)风险。重新审视应对“断服”(“断网”)的方案和措施,对于坚持维护国家主权、安全、发展利益十分迫切、至关重要。

悖逆科学事实和常识的“镜像替代”论,“早有准备”和“不用担心”说,当休矣!

(作者:邱实,网络信息安全和工程技术专家;牟承晋,中国移动通信联合会国际战略研究中心主任,昆仑策研究院高级研究员,浙江省北斗未来网际网络空间研究院首席研究员。来源:昆仑策网)

谷歌推出了图像文件更新托管服务,支持Ubuntu、Debian和CentOS

容器应用程序逐渐普遍,已经从开发者爱好工具,进入企业内部基础架构。不过镜像文件更新工作,对于运维人员依旧是件不简单的工作。在近日,Google旗下公有云平台GCP,宣布推出基础镜像文件更新托管服务(managed base images),“从最初构建应用程序时就可以使用。”目前此服务所支持的Linux发行版,分别有CentOS 7、Debian 9,以及Ubuntu 16.04,用户可以直接从GCP市场下载。

这一次推出的新服务,Google会提供用户多种操作系统基础镜像文件,后续更新工作也由该公司进行。如此,开发者除了能时时保持镜像文件在最新版本,由Google进行镜像文件把关,运维人员也不须从未知来源下载镜像文件,借以提高容器基础架构的运行安全。

不过Google表示,即便导入此基础镜像文件更新托管服务,容器镜像文件经过扫描后,仍可能发现镜像文件存在漏洞。 Google表示,出现此状况主要有三大原因。首先,扫瞄出的漏洞可能被归类为低风险漏洞,而项目维护人多半优先处理高风险漏洞,导致当前版本未有可用更新。第二个原因,则可能该项目刚爆出漏洞事件,还未有可用镜像文件更新。最后,则可能为开源项目新推出的功能,可能对系统安全带来风险,但项目维护人并不视其为漏洞。

由于容器的生命周期极短,不停进行重新构建、部局,为了确保用户容器镜像文件安全,Google建议企业用户创建高度一致的CI/CD流程,时时确保基础架构环境内的容器镜像文件,皆有经过把关、认证程序。而该公司表示,实施方法总共有四大方向。第一方向,导入集中化管理的CI/CD,除了减少使用容器存储库数量,而正式环境的软件上线工作,必须通过中央发渠道进行。第二方向,则是选用安全存储库作为镜像文件来源,从开发源头就做好网络安全工作。

再者,创建镜像文件扫描、分析的使用习惯,可使用第三方或公有云厂商原生服务执行。最后,当镜像文件要进入正式环境前,运维人员得确保只有经认证镜像文件,才能部局至正式环境。

而Google表示,未考虑使用此托管服务的开发者,也可尝试其他开源方案,像是该公司发布的开源容器工具Distroless images。此镜像文件内仅打包用户的应用程序、Runtime相依性,删去标准Linux发行版内的套件管理工具、Shells,缩小镜像文件的攻击表面。

五个最新的云原生图像构建工具都经过了完全解析,三个来自谷歌。你知道多少?

1云原生大背景下的镜像构建

在分享开始,我想先跟大家简单聊一下云原生,可能不会详细展开,而是带领大家了解一下云原生对镜像构建方面的影响。

第一,在接触云原生相关的技术时,无论是要解决开发、测试环境的问题,还是解决日常开发、测试等相关的操作和流程,我们经常都会谈到持续集成。持续集成首先要做代码的集成,不同的feature一起交付,使用持续集成的理念尽快把代码合并,保证代码没有冲突,这是持续集成最简单的一些理念。

在持续集成之后,要考虑做哪些业务的验证。验证之外,还需要有一些安全相关的策略。比如,在开发过程中是否使用了不安全的代码或依赖包。在构建的过程中,还要生产许多不同的制品。

那么问题就来了,云原生技术确实能通过容器化、K8s集群编排等提供能够复制的应用环境。无论什么语言,Java或Python等都可以非常简单地去使用docker镜像,或者Kubernetes yaml去部署环境,解决开发和生产环境的区别。

在传统开发模式下,常常会遇到在开发环境里程序好好的,到生产环境就出现各种各样的问题。K8s集群编排可以说很好地解决了这个问题,变成复用地资源。以前部署很多实例需要开很多虚拟机的情况,也成为我们不必再关心的问题。

这些听起来很多都是跟运维相关的,那么开发、测试为什么要去关心这个事情?其实大家是在合作,为了达成一个共同的目标。不管用DevOps,还是敏捷开发,我们都要去考虑从代码交付到真正上线要做哪些事情,并予以解决,而不是总面对“在我的环境里没有问题”这样的问题。同时,尽量统一使用环境的配置、资源、方案。

刚才谈到从持续集成到云原生,大量使用云原生技术,这时候要考虑的是安全。简单来说,我可以使用K8s镜像把docker socket直接部署到自己的Pod容器里。但实际上要做更多的考虑,这个环境可能不同的业务来使用,环境本身是共享的,所以不光要使用环境,还要考虑它是否稳定,有没有CVE漏洞以及容器本身的一些权限等。只有搞清楚这几件事,才能去考虑后面要讲的内容。

2无Dockerfile构建镜像工具

首先来思考为什么会有镜像构建这方面的需求?第一,对很多开发者来说,需要不断学习新的框架,新的技术,新的理念,这其实是很多开发者都不希望面临的状态。开发者希望开发环境和生产环境一致,构建的结果无差距,从而避免在后期发现问题。在没有Dockerfile的情况下,会发现不同的语言使用的构建镜像的方案完全不同,需要去这个语言的生态里面寻找相关的方案。

还有一种场景,在构建一个应用时不知道它到底安不安全,也没法控制它里面有没有Dockerfile。比如说先有一个jar包,jar包是第三方开发的,我先上传到服务器,然后再去下载、构建。这时候可能直接用大的jar包去做。

另外,企业本身对产出物或者制品要求非常高,可能会有专门的人去维护,没有那么开放。

我在这里介绍三个无Dockerfile镜像构建方案。

1、KO

Ko是Google发明的一个工具,它主要服务Golang的用户,比较容易使用。除了构建Golang本身,它还往前进了一步,集成了Kubernetes的使用。比如构建镜像,构建程序,相关的yaml部署到开发环境等等,一切一个命令就可以搞定,不需要再去执行其他的命令。

Ko也应用了Golang语言本身的一些特性,比如用Golang package构建不同的镜像,用package构建二进制文件。我们自己的项目里也会使用Golang,编译很多不同的二进制文件。

这块怎么使用呢?第一,我们有对应的k8s yaml,比如开发环境可能是yaml的方案,默认的代码仓库可能会带一些k8s yaml文件,pod、deployment、services等资源。如果不使用ko,还得去做一些replace,ko能够完全解决这个问题。

在镜像地址里直接放go module的名字,或者对应的二进制文件的名字,二进制文件可能在后面再加相关的package就可以了。ko apply也会构建,根据config的一些配置来更新对应的环境。

此外,它还支持前端比较喜欢的一些技术,比如live server这样的概念,对开发体验效率非常有帮助。

它的概念也非常简单,Golang本身构建出来的是一个二进制文件,需要基础镜像的支持,在基础镜像里面做一些配置,就可以运行起来。谷歌前段时间发明了一个项目叫distroless, distroless会有一些工具,例如busybox、ls,这些是在容器里面通常会使用的工具,但是并没有package,没有办法安装新的包。如果能修改,我们不希望使用distroless默认的镜像,而是通过.ko.yaml文件去覆盖,做默认的全局配置,或者根据不同的package去覆盖。

Ko没有把目标docker镜像仓库的地址放在配置里面,它是一个环境变量,这是因为,同城市不同的人在开发时会使用自己的镜像仓库,并不希望这个信息是共享的。用户加上自己的Docker registry地址之后,可以直接开始构建。这里可以分成两部分,刚才我们提到apply、K8s相关的操作,如果只想构建一个镜像并且推送到registry,可以用一个publish命令,publish一个或者多个二进制文件或者镜像。

这个镜像本身非常简单,Golang对环境的依赖非常少。所以,除了基础镜像的这些层级,还有你的应用。构建镜像层面并不依赖Docker,我们使用docker时,会通过docker执行docker build命令,docker build会访问docker daemon,接到主机里面。用Ko的话,完全不需要docker daemon,容器本身没有docker也是可以构建的。同时,也不需要很高的成本,不需要高权限,直接就可以配置。所以从上手或者用户使用来说,又快又简单。Ko也会大量使用GO本身的缓存,以及镜像缓存。

缺点方面,Google的这个基础镜像还在国外,如果应用本身是比较复杂的,需要额外投入时间去学习它的构建。另外,也没有特别好的网站,或者文档,所有的信息都在GitHub里面。

2、Jib

国内大部分开发者可能都是基于Java的,谷歌也在前段时间发明了一个Java专用镜像工具,叫Jib。Maven、gradle、core(库)都支持Jib这个镜像工具,它可以用于任何一个java项目。它本身也是完全基于Java来实现,对java开发者来说是非常熟悉的一种用法。

这是我们的目标镜像仓库,基础镜像可以写入,不写入的话,也会跟Ko一样用distroless的镜像去做。也可以定制,无论更简单还是更复杂的镜像,都可以做到。

除了使用Java的生态本身,Jib也考虑了对Java应用的优化。这里包含三层:依赖、静态资源和应用jar包。

Jib还有几个优点,对Java开发者来说它非常容易,jib不是特别复杂的一个概念,就是一个插件。第二,Java容器化很占用时间。容器化的Java方案在不断优化,因为Java除了容器化,它使用jvm本来就是为了解决应用环境的一些问题,相当于带有两种虚拟化的概念。我们要去做非常优化的Java镜像要投入不少时间和精力,这块jib是一步到位的。Jib和Ko一样,不依赖docker daemon,也可以在容器里运行,不需要什么高权限。

另外Jib和Ko相比较的话,没有做和K8s的集成,如果进一步优化的话,可能会往这个方向考虑。只是不好理解的是,maven或者gradle这块的生态可能已经有相关的插件。

3、s2i

如果我不是Java,又不是Go开发的,我应该怎么做呢?s2i是红帽发明的简化的Docker构建方案 。它的主要理念是,使用builder pattern 的概念去构建程序,再用运行时的镜像包一层。s2i需要在一个专门的镜像里做构建,然后再包一个运行的环境去做镜像,这个可以用任何语言去做。

s2i的使用,需要一些经验去考虑,同时还需要有专门的人去维护。因为它需要一个镜像做构建,在这个镜像里面要维护一堆东西。简单来说,s2i的理念是有这个流程去声明一下,所以相比前面的Ko、Jib两个工具,s2i有点复杂。

第一,要有一个二进制文件把流程编排起来,除了目标镜像仓库之外,代码还需要builder一个镜像。builder 镜像有几个要求,要有assemble脚本和run脚本,这几个脚本都是为了支持要做哪些事情,包括在镜像里面做单元测试都是支持的,还有其他一些附加功能。

从使用来说,一个小的命令就可以搞定,但是,这个命令背后的很多事情是怎么做到的,比如builder镜像的流程、文档等,有很多需要学习的概念。

这个统一构建的过程,开发者统一去管理,对最终用户来说是无感知的。包括第三方的软件依赖、builder镜像,除了这些脚本之外,还要写dockerfile,dockerfile是统一在一个镜像里面管理的。有自己的缓存机制。支持直接克隆代码构建,支持任何语言。

s2i缺点就是,太依赖docker环境。其次,上手非常复杂,拿现成的s2i工具做业务的话会很好用,但是这之前的学习成本还是很高的。

3 Dockerfile构建镜像工具

我们其实还是更希望在K8s环境下去执行构建,这部分我也跟大家介绍两个工具。

其实更多的情况,现在很多团队学习能力很强,愿意拥抱新的技术。希望深入研究容器化的最佳实践,探索怎么优化才能做到更好,怎么进一步使用工具减少建构的时间。

从社区的角度,现在开源社区越来越庞大,很多企业会选择使用开源社区的方案去做。开源社区也提供了很多的资源,你的团队可能忽然之间就变得很大了。在使用这些工具时,企业里的不同团队也会互相分享经验。

另外,影响Dockerfile镜像工具使用的,在交付物这块一些团队定制要求很高。

1、Kaniko

第一个是kaniko,也是谷歌发明的,因为K8s是谷歌的,所以他们在这块做了不少东西,kaniko不需要docker daemon,在容器里面去构建镜像。Kaniko目的是推广在K8s里构建镜像,任何容器化的方案都支持。除代码仓库,还可以使用对象存储的方案。

Kaniko也有一些自己的概念,有本地缓存,还会用registry镜像作为缓存。构建过程中,每完成一个multistage docker构建,结束一个stage,会把这个镜像上传到registry。Kaniko还有一个基础镜像的缓存,为了减少在拉取时的时间,比如说在k8s里面用PVC来保存缓存,可以用warmer镜像来维护这个缓存。

Kaniko构建时,一个命令一步到位就可以完成,完全兼容docker的config.json。那么Kaniko是怎么在容器里面构建的呢?其实,它的原理也很简单,它把镜像的内容保存在自己的容器里面,所有的run命运都在容器里面运行,再构建文件。

Kaniko支持推送到多个registry.Docker需要一个个去推送,但kaniko可以用一个命令全部搞定。除了推送到registry本身,还可以保存成一个tar包,load到docker本身,其他操作都可以使用。Kaniko有多个上下文支持,可以做的场景非常多,可以用bucket或者其他类似的协议做非常复杂的构建。

Kaniko的缺点,镜像也是在谷歌那边gcr.io,不支持v1 image tag格式,构建原理不直观,无法直接load到docker daemon。

2、makisu

Makisu是Uber公司发明的,从18年开始使用,和Kaniko要解决的问题一样,在一个容器化的环境里面执行构建,不依赖docker daemon,也不需要做很多复杂的操作。能够直接load到docker daemon。Makisu跟Kaniko不同之处在于,除了支持dockerfile,还做了一些优化,加入commit机制,可以选择最终镜像要几个层级,通过dockerfile去解决。

它的缓存除了本地,还有registry Jason。还可以把layer dockerfile里面的命令和相关 layer的信息保存到redis,从而减少构建操作,也可以大量复用。Makisu有一个特点,它没有选择兼容docker的配置,而是自己发明了一个配置文件,执行命令时指定具体文件。

这两个都有它的好处,在不同的构建环境里面维护这个配置,也不需要配置参数,构建参数都是一样的。另外,可以按照它的路径去做不同的配置。比如在docker.io里面,可以用不同的用户名和密码。这点docker配置本身其实没有这方面的支持。Makisu另外一个跟kaniko不太一样的地方,是可以在主机里面直接构建。

Makisu的优点刚才也提到了,Docker使用比较快,支持优化镜像层级和空间,直接load到docker daemon,支持镜像压缩,缓存对接redis,Kubernetes官方的pod模版。

缺点部分跟Kaniko一样,镜像在国外Google,其次,它不支持其它构建上下文类型。第三,不兼容docker配置,尽管它能够搞定这些事情提供更强化的配置,但不兼容总归是一个缺陷。另外一个不容易理解的点是,makisu把push 和 –t 分开成为两个参数,来拼接最终目标镜像仓库地址。

4总结

今天这个探索并不是为了选择最优的工具,或者得出哪个工具更好更牛的结论。而是说,这些不同的工具有自己不同的使用场景。

Ko和jib可能适用于你的团队Java语言和人员占据了大多数,大家可能没有足够的时间和精力去学习怎么构建docker。这会是比较容易去上手使用docker镜像、K8s部署的。

不想用前面两个工具或者语言不支持的情况下,不想每个人都写一遍dockerfile,可以通过s2i的方式去解决。不过这种情况,确实你要去考虑可能要专门的团队去维护。另外一个使用s2i的场景是,所有的产出物需要管理的非常细,比如出于安全,一些依赖的处理本身等原因。

Kaniko适合用于构建场景非常多,并不是一个代码仓库,还有其他一些bucket这样的对接,或者上下文去通过Kaniko去做。Makisu适用于镜像非常大,需要去优化它的空间的情况。

go语言中文文档:www.topgoer.com

转自:https://www.jianshu.com/p/082b67cc3555?

重新学习容器11:介绍容器映像构建工具和解决方案

本节一起看一下都有哪些可用于构建容器镜像的工具和方案。可能你会疑问,构建镜像不就是使用docker build就可以吗,即使将来DOCKER真的退出历史舞台,不还有containerd吗,前面第5节的时候也介绍了在containerd下可以使用nerdctl+buildkit构建容器镜像。 如果你只是在单机本地构建镜像的话,使用Docker build或nerdctl+containerd+buildkit确实没有问题,但实际中由于不同场景和技术背景下的限制,如docker build依赖于docker daemon,这些限制使得在一些特殊的场景下本机构建镜像的方式不再可用。

下图是一个一个CI/CD流水线中的分布式构建架构,结合了容器技术,将Jenkins用于构建的Slave节点放到容器中,并基于K8S来动态调度管理这些Jenkins Slave容器。这样一来Jenkins就具备了基于容器的分布式构建能力, 一个应用从源码经过各个阶段到最终的镜像是采用分布式构建实现的,各个阶段也都是在容器中进行的。

上面基于容器的分布式构建,首先要面对的问题就是要在jenkins slave容器的内部构建镜像。由于docker是以docker cli和docker daemon的C/S形式工作,如果把docker cli放到jenkins slave容器里面执行docker build,那么docker build所需的docker daemon放哪儿呢? 于是就有了Docker in Docker和Docker outside of Docker两种方案。

Docker in Docker

Docker in Docker简称DinD。DinD模式是指在一个容器内部安装完整的docker服务,启动一个Docker Daemon,然后就可以在容器内部使用docker cli进行镜像的构建。 此方案的优点是容器中的Docker Daemon与外部完全隔离,隔离性较好,而缺点是容器内部比较臃肿复杂,同时要解决容器内docker服务的持久化存储、构建缓存的问题,在安全上DinD还需要以特权(–privileged)形式启动容器,有安全风险。 因此这种方案在实际好少使用,也不推荐使用。

Docker outside of Docker

Docker outside of Docker简称DooD。DooD模式是指将容器外部宿主机上的docker daemon的socket挂载到容器内,让容器内的docker cli误认为本地启动了docker daemon,这样进行docker build等命令操作时由容器外部宿主机上的docker daemon处理请求。 此方案的优点是容器内部不需要安装docker daemon,构建在宿主机上进行效率较高,缺点是没有与外部隔离,构建的镜像与宿主机上的镜像存在冲突的可能,同一宿主机上不支持并行构建。 在早些年前使用DooD这种方案的人比较多。

Kaniko

Kaniko是Google开源的一款容器镜像构建工具,可以用来在容器中或者Kubernetes集群中进行镜像的构建。 在Kaniko出现之前,使用Dockerfile和Docker cli来构建镜像时,需要将构建的上下文发送至docker daemon,Docker in Docker和Docker outside of Docker也不例外。 Kaniko构建容器镜像时并不依赖于Docker daemon,也不需要特权权限,这一特性使得kaniko成为DinD或者DooD之外的一种全新的解决方案。

Kaniko构建容器镜像时,需要三个输入: Dockerfile,构建上下文,以及构建成功后镜像在仓库中的存放地址。 kaniko executor需要在容器执行,前面三个输入可以挂载到kaniko executor的容器中。

Kaniko支持多种方式将构建上下文挂载到容器中: 可以使用本地文件夹,GCS bucket,S3 bucket等等方式,使用GCS 或者 S3时需要把上下文压缩为tar.gz,kaniko会自行在构建时解压上下文。

Kaniko executor在找到Dockerfile后会逐条解析Dockerfile内容,并且执行相关的命令。在用户目录中形成容器镜像的文件层,每条Dockerfile中的指令都执行完毕后,Kaniko会将新生成的镜像推送到指定的镜像仓库中去。 整个过程中,完全不依赖于docker daemon。

下面是使用docker启动kaniko容器完成容器镜像构建的一个例子:

docker run --rm
-v $HOME.docker:/root/.docker
-e DOCKER_CONFIG=/root/.docker
-v ./Dockerfile:/workspace/Dockerfile
gcr.io/kaniko-project/executor:latest
--dockerfile /workspace/Dockerfile
--destination harbor.youcompany.com/library/xxapp:1.0
--context dir:///workspace/

下面是k8s中使用kaniko的启动pod的例子:

apiVersion: v1
kind: Pod
metadata:
name: kaniko
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:latest
args: ["--dockerfile=/workspace/Dockerfile",
"--context=dir:///workspace/",
"--destination=harbor.youcompany.com/library/xxapp:1.0"]
volumeMounts:
- name: kaniko-docker-config
mountPath: /workspace/config.json
subPath: config.json
- name: kaniko-dockerfile
mountPath: /workspace/Dockerfile
subPath: Dockerfile
- name: kaniko-workspace
mountPath: /workspace
env:
- name: DOCKER_CONFIG
value: /workspace
restartPolicy: Never
volumes:
- name: kaniko-docker-config
configMap:
name: kaniko-docker-config
- name: kaniko-dockerfile
configMap:
name: kaniko-dockerfile
- name: kaniko-workspace
hostPath:
path: /tmp/kaniko
type: Directory
imagePullSecrets:
- name: harbor-secret

Jib

Jib也是Google开源的一款Java容器镜像构建工具,通过使用Jib,Java 开发人员可以使用他们熟悉的Java构建工具来构建容器镜像。 Jib负责处理将应用程序打包到容器镜像中所需的所有步骤,它不需要我们编写Dockerfile或安装docker daemon,而是直接把镜像构建功能集成到了java构建工具gradle和maven中(通过将插件添加到构建中),即用java的构建工具直接完成容器镜像的构建。

以下示例将使用Jib提供的gradle插件集成到一个spring boot项目的构建中,并展示Jib如何简单快速的构建镜像。

首先,在项目的build.gradle构建文件中引入jib插件:

buildscript{
...
dependencies {
...
classpath "gradle.plugin.com.google.cloud.tools:jib-gradle-plugin:1.1.2"
}
}

apply plugin: 'com.google.cloud.tools.jib'

配置Jib相关参数,以下参数还可以通过 gradle jib -Djib.from.image='********' 这种形式从gradle命令行提供:

jib {
from {
image = 'harbor.youcompany.com/library/base:1.0'
auth {
username = '********'
password = '********'
}
}
to {
image = 'harbor.youcompany.com/library/xxapp:1.0'
auth {
username = '********'
password = '********'
}
}
container {
jvmFlags = ['-Djava.security.egd=file:/dev/./urandom']
ports = ['8080']
useCurrentTimestamp = false
workingDirectory = "/app"
}
}

执行以下命令可以直接触发构建生成容器镜像:

1
2
3
4

// 构建jib.to.image指定的镜像,并且推送至镜像仓库
gradle jib
// 构建jib.to.image指定的镜像,并保存至本地docker daemon
gradle jibDockerBuild

buildkit

buildkit是docker官方社区开源出来下一代的构建工具包,可以更加快速、高效、安全的构建OCI标准的镜像。 前面我们在学习containerd的时候已经结合nerdctl体验过它。它主要包含服务端buildkitd和客户端buildctl。 可以看出buildkit也是C/S架构,但buildctl和buildkitd可以不在同一台服务器上。 buildkitd可以监听TCP端口将服务以gRPC的形式暴露出来,供buildctl调用。 例如,可以将buildkitd部署到k8s集群中,buildctl作为工具放到k8s上的jenkins slave pod容器中,buildctl在k8s集群内以tcp的形式调用buildctl完成容器镜像的构建。

总结

通过本节的学习,可以看出有了kaniko, buildkit等工具,在构建容器镜像时已经完全可以脱离docker daemon, 而且这些工具都能很好地与k8s集成,支持在容器环境下完成构建。

返回windows 10 UI样式:第三方开发人员发布windows 11安装映像

说到 Windows 11 的变化,许多 Windows 10 老用户都觉得 UI 交互的改动最大。然而并不是每个人都喜欢 Windows 11 的外观样式,比如默认居中放置的任务栏图标。好消息是,有许多开发者正在积极打造第三方工具,以恢复经典的 Windows 10 风格元素(例如 Dot Matrix 的作品)。

不过本文要为大家介绍的,则是 MikeRosoft6009 正在开发中的“Rectify 11”。

其表示,“Rectify 11”主要提供以下方面的风格修改:

· 设置主题

· 微软管理控制台(MMC)

· 管理工具

· 开始菜单(集成 StartIsBack)

此外“Rectify 11”的未来版本(v2),也有望带来流畅的 Windows恢复环境(WinRE)图标。

感兴趣的朋友,可下载体验 v1 版本的第三方 ISO 镜像安装包(风险请自行承担):

https://drive.google.com/file/d/1JFQlCAvB1xeN7iDwts5Cy4zBiAPWA_F2/view

此外上方显示的安装起始屏幕展示的是 v2 样式,或与 v1 镜像有些许不同。

谷歌发布安卓6.0版Nexus6p0系统工厂镜像

Yesky天极新闻2015-10-27 14:05:42

【Yesky新闻频道消息】据外媒报道,继LG版Nexus 5X手机之后,谷歌刚刚放出了华为版Nexus 6P的Android 6.0出厂镜像,这样如果你的手机遇到系统故障,就可以刷入这个出厂镜像。

谷歌放出Nexus 6P的Android6.0系统出厂镜像

谷歌放出了两个Nexus 6P的安卓6.0出厂镜像,MDA89D和MDB08K。报道称,一种猜测是其中一个适配Project Fi版Nexus 6P,但非Project Fi版的Nexus 6P两个镜像都可以使用。

谷歌放出Nexus 6P的Android6.0系统出厂镜像

但即使谷歌放出了Nexus 6P的安卓6.0出厂镜像,但也不建议用户刷自制ROM,因为刷自制ROM就要解锁bootloader。此前有报道称,解锁bootloader会导致该机的某一硬件出现问题,并且无法恢复,因此还是小心为妙。