华为云用户手册

  • RocketMQ最佳实践汇总 本文汇总了基于RocketMQ常见应用场景的操作实践,为每个实践提供详细的方案描述和操作指导,帮助用户轻松构建基于RocketMQ的业务。 表1 RocketMQ最佳实践一览表 最佳实践 说明 通过消息幂等实现消息去重 为了防止消息重复消费导致业务处理异常,RocketMQ的消费者在接收到消息后,有必要根据业务上的唯一Key对消息做幂等处理。本章节介绍消息幂等的概念、适用场景以及实施方法。 通过Topic和Tag实现消息分类 在RocketMQ中,Topic与Tag都是业务上用来归类的标识,区分在于Topic是一级分类,而Tag可以理解为是二级分类。本章节介绍如何搭配使用Topic和Tag来实现消息过滤。 实现订阅关系一致 订阅关系不一致,可能导致消息消费逻辑混乱,消息被重复消费或遗漏。本章节介绍订阅关系一致的概念、原理以及实施方法。 消息堆积处理建议 本章节介绍出现消息堆积的主要原因和实施方法。 配置消息堆积数监控 本章节介绍RocketMQ通过 云监控 配置告警规则的具体操作,以便您可以实时监控实例的运行状态和关键的业务指标,并能够及时收到异常的告警通知,实现生产环境的风险预警。 DMS for RocketMQ安全使用建议 本章节提供了RocketMQ使用过程中的安全最佳实践,旨在为提高整体安全能力提供可操作的规范性指导。
  • 实施方法 订阅一个Topic且订阅一个Tag 同一消费组Group1中的消费者Consumer1、Consumer2和Consumer3都订阅了Topic_A,且都订阅了Topic_A的Tag_A,符合订阅关系一致原则。Consumer1、Consumer2、Consumer3的订阅关系一致,即Consumer1、Consumer2、Consumer3订阅消息的代码必须完全一致,代码示例如下: DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("Group1");consumer.subscribe("Topic_A", "Tag_A"); 订阅一个Topic且订阅多个Tag 同一消费组Group1中的消费者Consumer1、Consumer2和Consumer3都订阅了Topic_A,且都订阅了Topic_A的Tag_A和Tag_B(即订阅Topic_A中所有Tag为Tag_A或Tag_B的消息),顺序都是Tag_A||Tag_B,符合订阅关系一致性原则。Consumer1、Consumer2、Consumer3的订阅关系一致,即Consumer1、Consumer2、Consumer3订阅消息的代码必须完全一致,代码示例如下: DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("Group1");consumer.subscribe("Topic_A", "Tag_A||Tag_B"); 订阅多个Topic且订阅多个Tag 同一消费组Group1中的消费者Consumer1、Consumer2和Consumer3都订阅了Topic_A和Topic_B,且订阅Topic_A都未指定Tag(即订阅Topic_A中的所有消息),订阅Topic_B的Tag都是Tag_A和Tag_B(即订阅Topic_B中所有Tag为Tag_A或Tag_B的消息),顺序都是Tag_A||Tag_B,符合订阅关系一致性原则。Consumer1、Consumer2、Consumer3的订阅关系一致,即Consumer1、Consumer2、Consumer3订阅消息的代码必须完全一致,代码示例如下: DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("Group1");consumer.subscribe("Topic_A", "*");consumer.subscribe("Topic_B", "Tag_A||Tag_B");
  • 方案概述 订阅关系一致指的是同一个消费组下所有消费者所订阅的Topic、Tag必须完全一致。如果订阅关系不一致,消息消费的逻辑就会混乱,甚至导致消息丢失。 消费原理 RocketMQ为每个Topic划分了消息队列(Queue),队列数越大消费的并发度越大。一个消费组表示一个消费者群组,在分布式应用场景下,同一个消费组中的多个消费者共同完成Topic所有Queue的消费。Queue的分配以消费组为单位,会均匀分配给消费组下的消费者,而不会在意该消费者是否订阅了当前Topic。一个消费者只会分配到Topic中的某几个Queue,而一个Queue只会分配给一个消费者。 正确的订阅关系 在分布式应用场景下,一个消费组中所有的消费者拥有一个相同的消费组ID,他们需订阅相同的Topic和Tag,保持订阅关系一致,才能保证消息消费逻辑正确,消息不丢失。 同一个消费组的消费者必须订阅同一个Topic。例如,消费组Group1中有消费者A和消费者B,消费者A订阅了Topic A和Topic B,则消费者B也必须订阅Topic A和Topic B,不能只订阅Topic A或只订阅Topic B,或者额外订阅Topic C。 同一个消费组的消费者订阅同一个Topic下的Tag必须一致,包括Tag的数量和顺序。例如,消费组Group2中有消费者A和消费者B,消费者A订阅Topic A且Tag为Tag A||Tag B,则消费者B订阅Topic A时Tag也必须为Tag A||Tag B,不能只订阅Tag A或只订阅Tag B或订阅Tag B||Tag A。 图1 正确的订阅关系一致设置 订阅关系一致性保证了同一个消费组中消费消息的正常运行,避免消息逻辑混乱和消息丢失。在实际使用中,生产者端要做好消息的分类,便于消费者可以使用Tag进行消息的准确订阅。而在消费者端,则要保证订阅关系的一致性。 错误的订阅关系 同一消费组下的消费者订阅了不同的Topic 例如,消费组Group1下有消费者A和消费者B,消费者A订阅了Topic A,消费者B订阅了Topic B。当生产者向Topic A发送消息时,消息会按Queue均匀发送给消费者A和消费者B。由于消费者B没有订阅Topic A的消息,会把Topic A消息过滤掉(即图2中Topic A的Queue2中的消息会被消费者B过滤),从而导致部分Topic A消息未被消费。 图2 错误的Topic订阅 同一消费组下的消费者订阅了相同Topic下不同的Tag 例如,消费组Group1下有消费者A和消费者B,消费者A订阅了Topic A的Tag A,消费者B订阅了Topic A的Tag B。当生产者A向Topic A发送Tag A的消息时,Tag A消息会按Queue均匀发送给消费者A和消费者B。由于消费者B没有订阅Tag A的消息,会把Tag A消息过滤掉(即图3中Queue2中的Tag A消息会被消费者B过滤),从而导致部分Tag A消息未被消费。 图3 错误的Tag订阅
  • 方案概述 在RocketMQ的业务处理过程中,如果消息重发了多次,消费者端对该重复消息消费多次与消费一次的结果是相同的,多次消费并没有对业务产生负面影响,那么这个消息处理过程是幂等的。消息幂等保证了无论消息被重复投递多少次,最终的处理结果都是一致的,避免了因消息重复而对业务产生影响。 例如在支付场景下,用户购买商品后进行支付,由于网络不稳定导致用户收到多次扣款请求,导致重复扣款。但实际上扣款业务只应进行一次,商家也只应产生一条订单流水。这时候使用消息幂等就可以避免这个问题。 在实际应用中,导致消息重复的原因有网络闪断、客户端故障等,且可能发生在消息生产阶段,也可能发生在消息消费阶段。因此,可以将消息重复的场景分为以下两类: 生产者发送消息时发生消息重复: 生产者发送消息时,消息成功发送至服务端。如果此时发生网络闪断,导致生产者未收到服务端的响应,此时生产者会认为消息发送失败,因此尝试重新发送消息至服务端。当消息重新发送成功后,在服务端中就会存在两条内容相同的消息,最终消费者会消费到两条内容一样的重复消息。 消费者消费消息时发生消息重复: 消费者消费消息时,服务端将消息投递至消费者并完成业务处理。如果此时发生网络闪断,导致服务端未收到消费者的响应,此时服务端会认为消息投递失败。为了保证消息至少被消费一次,服务端会尝试投递之前已被处理过的消息,最终消费者会消费到两条内容一样的重复消息。
  • 审计是否存在异常数据访问 开启 云审计 服务,记录DMS for RocketMQ的所有访问操作,便于后期审查。 云审计服务(Cloud Trace Service, CTS ),是华为 云安全 解决方案中专业的日志审计服务,提供对各种云资源操作记录的收集、存储和查询功能,可用于支撑安全分析、合规审计、资源跟踪和问题定位等常见应用场景。 您开通云审计服务并创建和配置追踪器后,CTS可记录DMS for RocketMQ的管理事件和数据事件用于审计。相关文档请参见查看RocketMQ审计日志。 使用 云监控服务 对安全事件进行实时监控和告警。 为使您更好地掌握DMS for RocketMQ实例状态,华为云提供了云监控服务(Cloud Eye)。您可使用该服务监控自己的DMS for RocketMQ实例,执行自动实时监控、告警和通知操作,帮助您实时掌握DMS for RocketMQ实例中所产生的请求、流量等信息。 云监控服务不需要开通,会在用户创建DMS for RocketMQ实例后自动启动。相关文档请参见RocketMQ支持的监控指标、配置RocketMQ监控告警。
  • 通过访问控制,保护数据安全性 建议对不同角色的 IAM 用户仅设置最小权限,避免权限过大导致数据泄露或被误操作。 为了更好的进行权限隔离和管理,建议您配置独立的IAM管理员,授予IAM管理员IAM策略的管理权限。IAM管理员可以根据您业务的实际诉求创建不同的用户组,用户组对应不同的数据访问场景,通过将用户添加到用户组并将IAM策略绑定到对应用户组,IAM管理员可以为不同职能部门的员工按照最小权限原则授予不同的数据访问权限,详情请参见权限管理。 建议配置安全组访问控制,保护您的数据不被异常读取和操作。 参照表1配置安全组的入方向规则限制,控制连接实例的网络范围,避免DMS for RocketMQ暴露给不可信的第三方。 表1 安全组规则 实例版本 方向 协议 端口 源地址 说明 4.8.0 5.x 入方向 TCP 8100 访问DMS for RocketMQ实例的客户端地址,应避免设置为0.0.0.0/0。 使用TCP协议,通过内网访问实例的端口。 4.8.0 5.x 入方向 TCP 8200 使用TCP协议,通过公网访问实例的端口。 4.8.0 5.x 入方向 TCP 10100-10199 使用TCP协议,访问业务节点的端口。 5.x 入方向 TCP 8080 使用gRPC协议,通过内网访问实例的端口。 5.x 入方向 TCP 8081 使用gRPC协议,通过公网访问实例的端口。 建议将访问DMS for RocketMQ实例方式设置为密码访问(即开启ACL访问控制),防止未经认证的客户端误操作实例。 您可以选择如下任意一种方法开启ACL访问控制。 在“购买实例”页面开启ACL访问控制,具体可参考购买RocketMQ实例。 购买实例后,在实例的“基本信息”页面中开启ACL访问控制,具体可参考查看和修改RocketMQ实例基本信息。 开启敏感操作多因子认证保护您的数据不被误删。 DMS for RocketMQ支持敏感操作保护,开启后执行删除实例等敏感操作时,系统会进行身份验证,进一步对数据的高危操作进行控制,保证DMS for RocketMQ数据的安全性。详情请参见敏感操作。
  • 实施方法 从上面的消费重复场景可以看到,不同Message ID的消息可能有相同的消息内容,因此Message ID无法作为消息的唯一标识符。RocketMQ可以为消息设置Key,把业务的唯一标识作为消息的唯一标识,从而实现消息的幂等。为消息设置Key的示例代码如下: Message message = new Message();message.setKey("Order_id"); // 设置消息的Key,可以使用业务的唯一标识作为Key,例如订单号等。SentResult sendResult = mqProducer.send(message); 生产者发送消息时,消息已经设置了唯一的Key,在消费者消费消息时,可以根据消息的Key进行幂等处理。消费者通过getKeys()能够读取到消息的唯一标识(如订单号等),业务逻辑围绕该唯一标识进行幂等处理即可。
  • 方案概述 在RocketMQ的实际业务中,消息堆积是比较常见的问题。在消息处理过程中,如果客户端的消费速度跟不上服务端的发送速度,未处理的消息会越来越多,这部分消息就被称为堆积消息。消息没有被及时消费而产生消息堆积,从而会造成消息消费延迟。对于消息消费实时性要求较高的业务系统,即使是消息堆积造成的短暂消息延迟也无法接受。造成消息堆积的原因有以下两个: 消息没有及时被消费,生产者生产消息的速度快于消费者消费消息的速度,从而产生消息积压且无法自行恢复。 业务系统本身逻辑耗费时间较长,导致消息消费效率较低。 消息消费过程 图1 消息消费的过程 一个完整的消息消费过程主要分为2个阶段: 消息拉取 客户端通过批量拉取的方式从服务端获取消息,将拉取到的消息缓存到本地缓存队列中。对于拉取式消费,在内网环境下的吞吐量很高,因此消息拉取阶段一般不会引起消息堆积。 消息消费 客户端将本地缓存的消息提交到消费线程中,提供给业务消费逻辑进行消息处理,待消息处理完成后获取处理结果。此阶段的消费能力依赖于消息的消费耗时和消费并发度。如果由于业务处理逻辑复杂等原因,导致处理单条消息的耗时较长,就会影响整体的消息吞吐量。而消息吞吐量低会导致客户端本地缓存队列达到上限,从而停止从服务端拉取消息,引起消息堆积。 所以,消息堆积的主要瓶颈在于客户端的消费能力,而消费能力由消费耗时和消费并发度决定。消费耗时的优先级要高于消费并发度,应在保证消费耗时合理性的前提下,再考虑消费并发度问题。 消费耗时 影响消息处理时长的主要因素是业务处理的代码逻辑,而代码逻辑中会影响处理时长的主要有两种代码类型:CPU内部计算型代码和外部I/O操作型代码。如果代码中没有复杂的递归和循环处理,CPU内部计算耗时相对于外部I/O操作耗时来说几乎可以忽略,因此应关注外部I/O操作型代码的消息处理效率。 外部IO操作型代码主要有以下业务操作: 读写外部数据库,例如对远程MySQL数据库读写。 读写外部缓存系统,例如对远程Redis读写。 下游系统调用,例如Dubbo的RPC远程调用,Spring Cloud对下游系统的HTTP接口调用。 提前梳理下游系统的调用逻辑,掌握每个调用操作的预期耗时,有助于判断业务逻辑中I/O操作的耗时是否合理。通常消息堆积都是由于下游系统出现了服务异常或容量限制,从而导致消费耗时增加。而服务异常,并不仅仅是系统出现报错,也可能是更加隐蔽的问题,比如网络带宽问题。 消费并发度 客户端的消费并发度由单客户端线程数和客户端数量决定。单客户端线程数是指单个客户端所包含的线程数量,客户端数量是指消费组所包含的客户端(消费者)数量。对于普通消息、定时/延时消息、事务消息及顺序消息的消费并发度计算方法如下: 消息类型 消费并发度 普通消息 单客户端线程数 * 客户端数 定时/延时消息 事务消息 顺序消息 Min(单客户端线程数 * 客户端数,队列数) 单客户端线程数的调整需谨慎,不能盲目调大线程数,如果设置过大的线程数反而会带来大量的线程切换开销。 理想环境下单客户端的最优线程数计算模型为:C *(T1+T2)/T1。 其中,C为单机vCPU核数,T1为业务逻辑的CPU计算耗时,T2为外部I/O操作耗时,另外线程切换耗时忽略不计,I/O操作不消耗CPU,线程需有足够消息等待处理且内存充足。 此处最大线程数的计算模型是在理想环境下得到的,在实际应用中建议逐步调大线程数,在观察效果后再进行调整。
  • 实施方法 为了避免在实际业务中出现非预期的消息堆积问题,需要在业务系统的设计阶段梳理业务逻辑的消费耗时和设置消费并发度。 梳理消费耗时 通过压测获取消息的消费耗时,并对耗时较高的操作代码逻辑进行分析和优化。梳理消息的消费耗时需要注意以下几点: 消息消费逻辑的计算复杂度是否过高,代码是否存在复杂的递归和循环处理。 消息消费逻辑中的I/O操作是否是必须,是否可以使用本地缓存等方案规避。 消息消费逻辑中的复杂耗时操作是否可以做异步化处理。 设置消费并发度 对于消费并发度的计算,可以按如下方法进行处理: 根据公式计算出线程数的理想值,然后选取一个比理想值小的数据作为线程数起始值。逐步调大单个客户端的线程数,并观测客户端的系统指标,得到单个客户端的最优消费线程数和消息吞吐量。 根据上下游链路的流量峰值,计算出需要设置的客户端数量:客户端数=流量峰值/单客户端消息吞吐量。
  • 操作步骤 购买一个CCE集群。 登录CCE控制台。 在购买CCE集群页面配置集群参数,详细请参考创建集群参数配置。 等待集群创建成功。创建成功后在集群管理下会显示一个运行中的集群,且集群节点数量为0。 创建CCE节点。 集群创建成功后,您还需要在集群中创建运行工作负载的节点。CCE节点默认安装了Linux操作系统和Docker。我们可以用它创建基础镜像。 在下面的步骤中,我们将以Centos7.6为例,详细介绍如何创建JDK8基础镜像,并将它上传到SWR。 登录CCE控制台。 单击1中创建的集群,进入集群控制台。 在左侧菜单栏选择节点管理,进入节点页签,单击右上角“创建节点”,在弹框中配置节点的参数。 在网络配置中,选择“自动创建”1个弹性公网IP,带宽为5Mbit/s。 图1 网络配置 单击“下一步: 规格确认”。 查看节点规格无误后,阅读页面上的使用说明,勾选“我已阅读并知晓上述使用说明和《云容器引擎服务声明》”,单击“提交”。 等待节点创建成功。创建成功后在节点管理下会显示一个运行中的节点。 图2 CCE节点示例 下载JDK软件包。 节点创建成功后,单击节点名称,进入云服务器详情页。 在云服务器详情页,单击右上角“远程登录”。 选择一种登录方式,单击“登录”。 以root用户登录弹性云服务器。 新建一个目录image。 mkdir image 进入image目录。 cd image 下载JDK软件包。 wget https://builds.openlogic.com/downloadJDK/openlogic-openjdk/8u352-b08/openlogic-openjdk-8u352-b08-linux-x64.tar.gz 构建一个镜像。 执行vi dockerfile命令,编写一个Dockerfile,并写入以下信息: FROM centos #使用centos作为基础镜像RUN useradd -d /home/springboot -m springboot #在工作目录下创建一个用户ADD ./openlogic-openjdk-8u352-b08-linux-x64.tar.gz /home/springboot #拷贝jdk软件包到镜像,并自动解压RUN chown springboot:springboot /home/springboot/openlogic-openjdk-8u352-b08-linux-x64 -RUSER springboot #指定用户为springbootENV JAVA_HOME=/home/springboot/openlogic-openjdk-8u352-b08-linux-x64 #设置环境变量ENV PATH=$JAVA_HOME/bin:$PATH \ CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar WORKDIR /home/springboot/ #指定镜像的工作目录 按ESC,输入:wq,保存Dockerfile,并退出编辑。 执行下面的命令,构建一个镜像。 docker build -t openjdk:8 . 使用docker images命令,查看镜像是否构建成功。 图3 查看镜像是否创建成功 登录 容器镜像服务 控制台,并创建一个组织。 示例:这里我们创建一个名为testawa0306的组织。 上传镜像到5的组织下。 以root用户登录容器 镜像服务 控制台。 为镜像打标签。 示例如下:docker tag openjdk:8 swr.cn-north-4.myhuaweicloud.com/testawa0306/0penjdk:v8.8 上传镜像到步骤5的组织下。 docker push swr.cn-north-4.myhuaweicloud.com/testawa0306/0penjdk:v8.8 镜像上传成功后,我们可以在容器镜像服务控制台-“我的镜像”中找到刚刚上传成功的镜像。 (可选)镜像上传成功后,您可以使用已上传的镜像在CCE中部署工作负载。
  • 一个容器只运行一个进程 从技术角度讲,Docker容器中可以运行多个进程,您可以将数据库、前端、后端、ssh等都运行在同一个Docker容器中。但是,这样跟未使用容器前没有太大区别,且这样容器的构建时间非常长(一处修改就要构建全部),镜像体积大,横向扩展时非常浪费资源(不同的应用需要运行的容器数并不相同)。 通常所说的容器化改造是对应用整体微服务进行架构改造,改造后,再容器化。这样做可以带来如下好处: 单独扩展:拆分为微服务后,可单独增加或缩减每个微服务的实例数量。 提升开发速度:各微服务之间解耦,某个微服务的代码开发不影响其他微服务。 通过隔离确保安全:整体应用中,若存在安全漏洞,一旦被攻击,所有功能的权限都可能会被窃取。微服务架构中,若攻击了某个服务,只可获得该服务的访问权限,无法入侵其他服务。 提升稳定性:如果其中一个微服务崩溃,其他微服务还可以持续正常运行。 因此,上述企业门户网站可以进行如下改造,Web应用和MySQL运行在不同容器中。 MySQL运行在独立的镜像中,这样的好处就是,可以对它们分别进行修改,且不会牵一发而动全身。如下面这个例子所示,可以删除MySQL,只安装node.js。 FROM ubuntuADD . /appRUN apt-get update RUN apt-get upgrade -yRUN apt-get install -y nodejs RUN cd /app && npm installCMD npm start
  • 合理调整COPY与RUN的顺序 将变化最少的部分放在Dockerfile的前面,这样可以充分利用镜像缓存。 示例中,源代码会经常变化,则每次构建镜像时都需要重新安装NPM模块,这显然不是希望看到的。因此可以先拷贝package.json,然后安装NPM模块,最后才拷贝其余的源代码。这样的话,即使源代码变化,也不需要重新安装NPM模块。 FROM node:7-alpineWORKDIR /appCOPY package.json /app RUN npm install COPY . /appENTRYPOINT ["./entrypoint.sh"] CMD ["start"]
  • 添加HEALTHCHECK 运行容器时,可以指定--restart always选项。这样的话,容器崩溃时,docker daemon会重启容器。对于需要长时间运行的容器,这个选项非常有用。但是,如果容器的确在运行,但是不可用怎么办?使用HEALTHCHECK指令可以让Docker周期性的检查容器的健康状况。只需要指定一个命令,如果一切正常的话返回0,否则返回1。当请求失败时,curl --fail命令返回非0状态。示例如下: FROM node:7-alpine LABEL com.example.version="0.0.1-beta"ENV PROJECT_DIR=/app WORKDIR $PROJECT_DIRCOPY package.json $PROJECT_DIR RUN npm install COPY . $PROJECT_DIRENV MEDIA_DIR=/media \ APP_PORT=3000VOLUME $MEDIA_DIR EXPOSE $APP_PORT HEALTHCHECK CMD curl --fail http://localhost:$APP_PORT || exit 1ENTRYPOINT ["./entrypoint.sh"] CMD ["start"]
  • 不要在构建中升级版本 为了降低复杂性、减少依赖、减小文件大小、节约构建时间,您应该避免安装任何不必要的包。例如,不要在数据库镜像中包含一个文本编辑器。 如果基础镜像中的某个包过时了,但您不知道具体是哪一个包,您应该联系它的维护者。如果您确定某个特定的包,比如foo需要升级,使用apt-get install -y foo就行,该指令会自动升级foo包。 apt-get upgrade会使得镜像构建过程非常不稳定,在构建时不确定哪些包会被安装,此时可能会产生不一致的镜像。因此通常会删掉apt-get upgrade。 删掉apt-get upgrade后,Dockerfile如下: FROM ubuntuADD . /appRUN apt-get updateRUN apt-get install -y nodejsRUN cd /app && npm installCMD npm start
  • 设置默认的环境变量、映射端口和数据库逻辑卷 运行Docker容器时很可能需要一些环境变量。在Dockerfile设置默认的环境变量是一种很好的方式。另外,应该在Dockerfile中设置映射端口和数据库逻辑卷。示例如下: FROM node:7-alpineENV PROJECT_DIR=/appWORKDIR $PROJECT_DIRCOPY package.json $PROJECT_DIR RUN npm install COPY . $PROJECT_DIRENTRYPOINT ["./entrypoint.sh"] CMD ["start"] ENV指令指定的环境变量在容器中可以使用。如果您只是需要指定构建镜像时的变量,您可以使用ARG指令。
  • 使用EXPOSE暴露端口 EXPOSE指令用于指定容器将要监听的端口。因此,您应该为您的应用程序使用常见的端口。例如,提供Apache web服务的镜像应该使用EXPOSE 80,而提供MongoDB服务的镜像使用EXPOSE 27017。 对于外部访问,用户可以在执行docker run时使用一个标志来指示如何将指定的端口映射到所选择的端口。 FROM node:7-alpineENV PROJECT_DIR=/appWORKDIR $PROJECT_DIRCOPY package.json $PROJECT_DIR RUN npm install COPY . $PROJECT_DIRENV APP_PORT=3000EXPOSE $APP_PORTENTRYPOINT ["./entrypoint.sh"] CMD ["start"]
  • 使用特定的标签 当镜像没有指定标签时,将默认使用latest标签。因此,FROM ubuntu指令等同于FROM ubuntu:latest。当镜像更新时,latest标签会指向不同的镜像,这时构建镜像有可能失败。 如下示例中使用16.04作为标签。 FROM ubuntu:16.04RUN apt-get update && apt-get install -y nodejs ADD . /app RUN cd /app && npm installCMD npm start
  • 删除多余文件 假设更新了apt-get源,下载解压并安装了一些软件包,它们都保存在“/var/lib/apt/lists/”目录中。 但是,运行应用时Docker镜像中并不需要这些文件。因此最好将它们删除,因为它会使Docker镜像变大。 示例Dockerfile中,删除“/var/lib/apt/lists/”目录中的文件。 FROM ubuntu:16.04RUN apt-get update \ && apt-get install -y nodejs \ && rm -rf /var/lib/apt/lists/*ADD . /app RUN cd /app && npm installCMD npm start
  • 选择合适的基础镜像 在示例中,选择了ubuntu作为基础镜像。但是只需要运行node程序,没有必要使用一个通用的基础镜像,node镜像应该是更好的选择。 更好的选择是alpine版本的node镜像。alpine是一个极小化的Linux发行版,只有4MB,这让它非常适合作为基础镜像。 FROM node:7-alpineADD . /app RUN cd /app && npm installCMD npm start
  • 使用VOLUME管理数据库逻辑卷 VOLUME指令用于暴露任何数据库存储文件、配置文件或容器创建的文件和目录。强烈建议使用VOLUME来管理镜像中的可变部分和用户可以改变的部分。 下面示例中填写一个媒体目录。 FROM node:7-alpineENV PROJECT_DIR=/appWORKDIR $PROJECT_DIRCOPY package.json $PROJECT_DIR RUN npm install COPY . $PROJECT_DIRENV MEDIA_DIR=/media \ APP_PORT=3000VOLUME $MEDIA_DIR EXPOSE $APP_PORTENTRYPOINT ["./entrypoint.sh"] CMD ["start"]
  • 设置WORKDIR和CMD WORKDIR指令可以设置默认目录,也就是运行RUN / CMD / ENTRYPOINT指令的地方。 CMD指令可以设置容器创建时执行的默认命令。另外,您应该将命令写在一个数组中,数组中每个元素为命令的每个单词。 FROM node:7-alpineWORKDIR /app ADD . /app RUN npm installCMD ["npm", "start"]
  • 将变化频率一样的RUN指令合一 Docker镜像是分层的,类似于洋葱,它们都有很多层,为了修改内层,则需要将外面的层都删掉。Docker镜像有如下特性: Dockerfile中的每个指令都会创建一个新的镜像层。 镜像层将被缓存和复用。 Dockerfile修改后,复制的文件变化了或者构建镜像时指定的变量不同了,对应的镜像层缓存就会失效。 某一层的镜像缓存失效之后,它之后的镜像层缓存都会失效。 镜像层是不可变的,如果在某一层中添加一个文件,然后在下一层中删除它,则镜像中依然会包含该文件,只是这个文件在Docker容器中不可见。 将变化频率一样的指令合并在一起,目的是为了更好的将镜像分层,避免带来不必要的成本。如本例中将node.js安装与npm模块安装放在一起的话,则每次修改源代码,都需要重新安装node.js,这显然不合适。 FROM ubuntuADD . /appRUN apt-get update \ && apt-get install -y nodejs \ && cd /app \ && npm installCMD npm start 因此,正确的写法是这样的: FROM ubuntuRUN apt-get update && apt-get install -y nodejs ADD . /appRUN cd /app && npm installCMD npm start
  • 使用ENTRYPOINT(可选) ENTRYPOINT指令并不是必须的,因为它会增加复杂度。ENTRYPOINT是一个脚本,它会默认执行,并且将指定的命令作为其参数。它通常用于构建可执行的Docker镜像。 FROM node:7-alpineWORKDIR /app ADD . /app RUN npm installENTRYPOINT ["./entrypoint.sh"] CMD ["start"]
  • 镜像迁移操作步骤 准备镜像仓库访问权限文件:auth.json。 新建一个auth.json文件,并按照格式修改,如果是匿名访问仓库,则不需要填写用户名、密码等信息。将文件放置在image-migrator所在目录下。 示例: { "quay.io/coreos": { }, "swr.cn-north-4.myhuaweicloud.com": { "username": "cn-north-4@RVHVMX******", "password": "cab4ceab4a1545***************", "insecure": true }} 详细的参数说明请参见“auth.json”文件。 准备镜像列表文件:images.json。 通过kubectl连接源集群。具体方法可参考使用kubectl连接集群。 执行镜像迁移config子命令,生成images.json文件。 您可以参考image-migrator config使用方法中的方法和示例,不指定命名空间,或者指定一个、多个命名空间来获取源集群应用中使用的镜像。 根据需求调整images.json文件内容,但要遵循“images.json”文件中所讲的八项要求。 镜像迁移。 您可以执行默认的./image-migrator-linux-amd64命令进行镜像迁移,也可以根据需要设置image-migrator的参数。 例如以下命令: ./image-migrator-linux-amd64 --workers=5 --auth=./auth.json --images=./images.json --namespace=test --registry=swr.cn-north-4.myhuaweicloud.com --retries=2 示例: $ ./image-migrator-linux-amd64 Start to generate images tasks, please wait ...Start to handle images tasks, please wait ...Images(38) migration finished, 0 images tasks failed, 0 tasks generate failed 结果查看。 上述命令执行完毕后,回显如下类似信息: Images(38) migration finished, 0 images tasks failed, 0 tasks generate failed 表示按照配置,成功将38个镜像迁移到SWR仓库中。
  • 准备工作 在开始迁移之前,请确保您已准备了一台安装了kubectl的服务器,用于连接源集群和目标集群。该服务器需要至少拥有5GB左右的本地磁盘空间和≥8G的内存,以确保迁移工具可以正常运行,并存储相关数据,如源集群的采集数据和目标集群的推荐数据等。 迁移工具支持在Linux(x86、arm)、Windows环境中运行,因此您可以在这些操作系统中任选一种作为服务器的操作系统。 对于Linux操作系统来说,使用image-migrator前,需要运行chmod u+x 工具名 命令(例如chmod u+x image-migrator-linux-amd64),授予可执行权限。 表1 image-migrator工具包获取 image-migrator image-migrator是一个镜像迁移工具,能够自动将基于Docker Registry v2搭建的Docker镜像仓库或第三方云镜像仓库中的镜像迁移到SWR中。 Linux x86:https://ucs-migration.obs.cn-north-4.myhuaweicloud.com/toolkits/image-migrator-linux-amd64 Linux arm:https://ucs-migration.obs.cn-north-4.myhuaweicloud.com/toolkits/image-migrator-linux-arm64 Windows:https://ucs-migration.obs.cn-north-4.myhuaweicloud.com/toolkits/image-migrator-windows-amd64.exe
  • image-migrator工作原理 图1 image-migrator工作原理 使用image-migrator工具将镜像迁移到SWR时,需要准备两个文件,一个为镜像仓库访问权限文件“auth.json”,该文件中的两个对象分别为源镜像仓库和目标镜像仓库(即Registry)的账号和密码;另一个为镜像列表文件“images.json”,文件内容由多条镜像同步规则组成,每一条规则包括一个源镜像仓库(键)和一个目标镜像仓库(值)。将这两个文件准备好以后,放在image-migrator工具所在目录下,执行一条简单的命令,就可以完成镜像的迁移。关于两个文件的详细介绍如下: “auth.json”文件 “auth.json”为镜像仓库访问权限文件,其中每个对象为一个registry的用户名和密码。通常源镜像仓库需要具有pull以及访问tags权限,目标镜像仓库需要拥有push以及创建仓库权限。如果是匿名访问镜像仓库,则不需要填写用户名、密码等信息。“auth.json”文件的结构如下: { "源镜像仓库地址": { }, "目标镜像仓库地址": { "username": "xxxxxx", "password": "xxxxxx", "insecure": true }} 其中, “源镜像仓库地址”和“目标镜像仓库地址”支持“registry”和“registry/namespace”两种形式,需要跟下述“images.json”中的registry或registry/namespace对应。images中被匹配到的url会使用对应用户名密码进行镜像同步,优先匹配“registry/namespace”的形式。 目标镜像仓库地址如果为“registry”形式,可以从SWR控制台页面获取,具体方法如下:在“总览”页面单击右上角“登录指令”,登录指令末尾的 域名 即为SWR镜像仓库地址,例如swr.cn-north-4.myhuaweicloud.com。注意每个Region的地址不同,请切换到对应Region获取。如果为“registry/namespace”形式,还要将namespace替换为SWR的组织名称。 username:(可选)用户名,values可以填写具体取值,也可以使用“${env}”或者“$env”类型的字符串引用环境变量。 password:(可选)密码,values可以填写具体取值,也可以使用“${env}”或者“$env”类型的字符串引用环境变量。 insecure:(可选)registry是否为http服务,如果是,insecure为true;默认是false。 目标镜像仓库SWR的用户名形式为:区域项目名称@AK;密码为AK和SK经过加密处理后的登录密钥,详细指导请参考获取长期有效登录指令。 示例: { "quay.io/coreos": { }, "swr.cn-north-4.myhuaweicloud.com": { "username": "cn-north-4@RVHVMX******", "password": "cab4ceab4a1545***************", "insecure": true }} “images.json”文件 该文件本质上是一个待迁移的镜像清单,由多条镜像同步规则组成,每一条规则包括一个源镜像仓库(键)和一个目标镜像仓库(值)。具体的要求如下: 同步的最大单位是仓库(repository),不支持通过一条规则同步整个namespace以及registry。 源仓库和目标仓库的格式与docker pull/push命令使用的镜像url类似(registry/namespace/repository:tag)。 源仓库和目标仓库(如果目标仓库不为空字符串)都至少包含registry/namespace/repository。 源仓库字段不能为空,如果需要将一个源仓库同步到多个目标仓库需要配置多条规则。 目标仓库名可以和源仓库名不同,此时同步功能类似于:docker pull + docker tag + docker push。 当源仓库字段中不包含tag时,表示将该仓库所有tag同步到目标仓库,此时目标仓库不能包含tag。 当源仓库字段中包含tag时,表示只同步源仓库中的一个tag到目标仓库,如果目标仓库中不包含tag,则默认使用源tag。 当目标仓库为空字符串时,会将源镜像同步到默认registry的默认namespace下,并且repository以及tag与源仓库相同,默认registry和默认namespace可以通过命令行参数以及环境变量配置。 示例如下: { "quay.io/coreos/etcd:1.0.0": "swr.cn-north-4.myhuaweicloud.com/test/etcd:1.0.0", "quay.io/coreos/etcd": "swr.cn-north-4.myhuaweicloud.com/test/etcd", "quay.io/coreos/etcd:2.7.3": "swr.cn-north-4.myhuaweicloud.com/test/etcd"} 我们提供了一个自动获取集群中工作负载正在使用的镜像的方法,即image-migrator工具的config子命令,具体用法请参见image-migrator config使用方法。得到images.json文件后,您还可以根据需要进行修改、添加或删除。
  • image-migrator config使用方法 image-migrator工具的config子命令可用于获取集群应用中使用的镜像,在工具所在目录下生成images.json。执行./image-migrator-linux-amd64 config -h命令可以查看config子命令的使用方法。 -k, --kubeconfig:指定kubectl的kubeConfig位置,默认是$HOME/.kube/config。kubeConfig文件:用于配置对Kubernetes集群的访问,KubeConfig文件中包含访问注册kubernetes集群所需要的认证凭据以及Endpoint(访问地址),详细介绍可参见Kubernetes文档。 -n, --namespaces:指定获取镜像的命名空间,多个命名空间用逗号分隔(如:ns1,ns2,ns3),默认是"",表示获取所有命名空间的镜像。 -t, --repo:指定目标仓库的地址(registry/namespace)。 $ ./image-migrator-linux-amd64 config -hgenerate images.jsonUsage: image-migrator config [flags]Flags: -h, --help help for config -k, --kubeconfig string The kubeconfig of k8s cluster's. Default is the $HOME/.kube/config. (default "/root/.kube/config") -n, --namespaces string Specify a namespace for information collection. If multiple namespaces are specified, separate them with commas (,), such as ns1,ns2. default("") is all namespaces -t, --repo string target repo,such as swr.cn-north-4.myhuaweicloud.com/test 示例如下: 指定一个命名空间 ./image-migrator-linux-amd64 config -n default -t swr.cn-north-4.myhuaweicloud.com/test 指定多个命名空间 ./image-migrator-linux-amd64 config -n default,kube-system -t swr.cn-north-4.myhuaweicloud.com/test 不指定命名空间(表示获取所有命名空间的镜像) ./image-migrator-linux-amd64 config -t swr.cn-north-4.myhuaweicloud.com/test
  • image-migrator使用方法 image-migrator工具支持在Linux(x86、arm)和Windows环境中运行,使用方法相似。本文将以Linux(x86)环境为例进行介绍。 若使用Linux(arm)或Windows环境,请将下述命令中的image-migrator-linux-amd64分别替换为image-migrator-linux-arm64或image-migrator-windows-amd64.exe。 在image-migrator工具所在目录下执行./image-migrator-linux-amd64 -h,可以查看image-migrator工具的使用方法。 --auth:指定auth.json的路径,默认在image-migrator所在目录下。 --images:指定images.json的路径,默认在image-migrator所在目录下。 --log:指定image-migrator生成日志的路径,默认是image-migrator当前目录下的image-migrator.log。 --namespace:默认的目标仓库的namespace,也就是说,如果images.json中没有指定目标仓库中的namespace,可以在执行迁移命令时指定。 --registry:默认的目标仓库的registry,也就是说,如果images.json中没有指定目标仓库中的registry,可以在执行迁移命令时指定。 --retries:迁移失败时的重试次数,默认为3。 --workers:镜像搬迁的worker数量(并发数),默认是7。 $ ./image-migrator-linux-amd64 -hA Fast and Flexible docker registry image images tool implement by Go.Usage: image-migrator [flags]Aliases: image-migrator, image-migratorFlags: --auth string auth file path. This flag need to be pair used with --images. (default "./auth.json") -h, --help help for image-migrator --images string images file path. This flag need to be pair used with --auth (default "./images.json") --log string log file path (default "./image-migrator.log") --namespace string default target namespace when target namespace is not given in the images config file, can also be set with DEFAULT_NAMESPACE environment value --registry string default target registry url when target registry is not given in the images config file, can also be set with DEFAULT_REGISTRY environment value -r, --retries int times to retry failed tasks (default 3) -w, --workers int numbers of working goroutines (default 7)$./image-migrator --workers=5 --auth=./auth.json --images=./images.json --namespace=test \--registry=swr.cn-north-4.myhuaweicloud.com --retries=2$ ./image-migrator Start to generate images tasks, please wait ...Start to handle images tasks, please wait ...Images(38) migration finished, 0 images tasks failed, 0 tasks generate failed 示例如下: ./image-migrator --workers=5 --auth=./auth.json --images=./images.json --namespace=test --registry=swr.cn-north-4.myhuaweicloud.com --retries=2 该命令表示将“images.json”文件中的镜像迁移至“swr.cn-north-4.myhuaweicloud.com/test”镜像仓库下,迁移失败时可以重试2次,一次可以同时搬迁5个镜像。
  • 应用现状 随着容器化技术的发展,越来越多的企业使用容器代替了虚拟机完成应用的运行部署。目前许多企业选择自建Kubernetes集群,但是自建集群往往有着沉重的运维负担,需要运维人员自己配置管理系统和监控解决方案。企业自运维大批镜像资源,意味着要付出高昂的运维、人力、管理成本,且效率不高。 容器镜像服务支持Linux、ARM等多架构容器镜像托管。企业可以将镜像仓库迁移到容器镜像服务,节省运维成本。 如何把已有的镜像仓库平滑地迁移到容器镜像服务?这里将介绍4种常见的方案,用户可以根据自己的实际使用场景来选择。
  • 迁移方案 表1 迁移方案及适用场景对比 方案类型 适用场景 注意事项 使用docker命令迁移镜像至SWR 待迁移的镜像数量较少 依赖磁盘存储,需要及时进行本地镜像的清理,而且落盘形成多余的时间开销,难以胜任生产场景中大量镜像的迁移。 依赖docker程序,docker daemon对pull/push的并发数进行了严格的限制,没法进行高并发同步。 一些功能只能经过HTTP api进行操作,单纯使用docker cli 没法做到,使脚本变得复杂。 使用image-syncer迁移镜像至SWR 待迁移的镜像数量庞大 支持多对多镜像仓库同步。 支持基于Docker Registry V2搭建的docker镜像仓库服务 (如Docker Hub、Quay、Harbor等)。 同步只通过内存和网络,不依赖磁盘存储,同步速度快。 增量同步, 经过对同步过的镜像blob信息落盘,不重复同步已同步的镜像。 并发同步,能够通过配置文件调整并发数。 自动重试失败的同步任务,能够解决大部分镜像同步中的网络抖动问题。 不依赖 docker 以及其余程序。 使用image-migrator迁移镜像至SWR 待迁移的镜像数量庞大 支持将基于Docker Registry v2搭建的Docker镜像仓库中的镜像迁移到华为云SWR中。 跨云harbor同步镜像至华为云SWR 部分客户存在多云场景,并且使用某一家云上的自建Harbor作为镜像仓库 仅支持 Harbor V1.10.5 及以上版本
共99354条