标签 serverless 下的文章

Java 仍是开发企业应用程序最流行的语言之一。那么,为什么 无服务器 serverless 开发人员对它望而却步?

 title=

几十年来,企业已经在各类平台上开发了关键业务应用程序,包括物理服务器、虚拟机和云环境。这些应用程序在各行各业都有一个共同点,那就是无论需求如何,它们都需要持续可用(24x7x365),保证稳定性、可靠性和性能。因此,即使实际资源利用率低于 50%,每个企业都必须付出高额成本维护基础架构(如 CPU、内存、磁盘、网络等)。

无服务器架构是为了帮助解决这些问题而产生的。无服务器允许开发人员按需构建和运行应用程序,保证高可用性,不必在多云和混合云环境中管理服务器。在幕后,无服务器拓扑中仍有很多服务器,但它们是从应用程序开发中抽象出来的。相反,云提供商使用无服务器进行资源管理,例如配置、维护、联网和扩展服务器实例。

由于其高效性,无服务器开发模式现在是一些企业的需求,这些企业希望按需启动服务,而不是一直运行服务。

许多新建的开源项目用来在 Kubernetes 集群上通过 Linux 容器包来管理无服务器应用程序。CNCF 的《交互式无服务器全景》 是一份关于开源项目、工具、框架和公共云平台的指南,帮助 DevOps 团队处理无服务器应用程序。

 title=

开发人员可以编写代码,然后将其快速部署到各种无服务器环境中。然后,无服务器应用程序响应需求,并根据需要自动伸缩扩展。

你可能想知道什么编程语言和运行环境最适合无服务器应用程序开发,以便与上图中的技术集成。这个问题不只一个答案,但是让我们退一步来讨论在企业生产环境中开发业务应用程序最流行的应用程序运行环境:Java。

Developer Economics 称,截至 2020 年第三季度,仍有 800 多万家企业开发人员在使用 Java 来实现其业务需求。然而,根据 2020 年的一项调查,Java(占比 6%)显然不是有前瞻意识的开发人员的首选,他们使用当前云服务做开发。

 title=

来自 NewRelic 无服务器基准报告的数据(Daniel Oh, CC BY-SA 4.0

资源使用、响应时间和延迟在无服务器开发中至关重要。公有云提供商提供的无服务器产品通常是按需计量的,只有在无服务器应用程序启动时,才会通过事件驱动的执行模式收费。因此,当无服务器应用程序闲置或缩减为零时,企业无需支付任何费用。

带有容器的 Java 状态

在这种背景下,你可能会问:“既然现有业务应用程序很可能是在 Java 技术上开发的,那么开发人员为什么不尝试使用 Java 栈进行无服务器应用程序开发呢?

隐藏的真相是:很难在新的不可变更的基础设施(例如 Kubernetes 这样的容器平台)中优化 Java 应用程序。

 title=

该图描述了 Java 进程与竞争的语言、框架(如 Node.jsGo)之间内存资源使用的差异。Java HotSpot 占用资源最大,其中包括每个 Java 虚拟机 Java Virtual Machine (JVM)实例分配的堆内存。中间显示了 Node.js 每个进程要比 Java 小得多。最后,Go 是一种流行的云服务编程语言,因为它的内存消耗最低。

如你所见,当你在这张图从左到右走,你会看到更密的节点。这就是开发人员在云、容器和 Kubernetes 上编写无服务器应用程序时回避 Java(包括 Spring Boot,一种顽固的微服务 Java 框架)的原因。

下一步是什么?

企业可以通过实现无服务器应用程序获得明显的好处,但是资源密度问题导致他们避免使用 Java 堆栈在 Kubernetes 上开发无服务器应用程序开发。但是选择其他语言会给全球数百万 Java 开发人员带来学习负担。因此,在本系列的下一篇文章中,我将指导你如何开始使用 Java 无服务器函数,而不是使用其他语言。


via: https://opensource.com/article/21/5/what-serverless-java

作者:Daniel Oh 选题:lujun9972 译者:DCOLIVERSUN 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我在 $work 建立了一个基于 Kubernetes 的平台已经快一年了,而且有点像 Kubernetes 的布道者了。真的,我认为这项技术太棒了。然而我并没有对它的运营和维护的困难程度抱过什么幻想。今年早些时候我读了这样的一篇文章,并对其中某些观点深以为然。如果我在一家规模较小的、有 10 到 15 个工程师的公司,假如有人建议管理和维护一批 Kubernetes 集群,那我会感到害怕的,因为它的运维开销太高了!

尽管我现在对 Kubernetes 的一切都很感兴趣,但我仍然对“ 无服务器 Serverless ”计算会消灭运维工程师的说法抱有好奇。这种好奇心主要来源于我希望在未来仍然能有一份有报酬的工作 —— 如果我们前景光明的未来不需要运维工程师,那我得明白到底是怎么回事。我已经在 Lamdba 和Google Cloud Functions 上做了一些实验,结果让我印象十分深刻,但我仍然坚信无服务器解决方案只是解决了一部分问题。

我关注 AWS Fargate 已经有一段时间了,这就是 $work 的开发人员所推崇为“无服务器计算”的东西 —— 主要是因为 Fargate,用它你就可以无需管理底层节点而运行你的 Docker 容器。我想看看它到底意味着什么,所以我开始尝试从头开始在 Fargate 上运行一个应用,看看是否可以成功。这里我对成功的定义是一个与“生产级”应用程序相近的东西,我想应该包含以下内容:

  • 一个在 Fargate 上运行的容器
  • 配置信息以环境变量的形式下推
  • “秘密信息” 不能是明文的
  • 位于负载均衡器之后
  • 有效的 SSL 证书的 TLS 通道

我以“基础设施即代码”的角度来开始整个任务,不遵循默认的 AWS 控制台向导,而是使用 terraform 来定义基础架构。这很可能让整个事情变得复杂,但我想确保任何部署都是可重现的,任何想要遵循此步骤的人都可发现我的结论。

上述所有标准通常都可以通过基于 Kubernetes 的平台使用一些外部的附加组件和插件来实现,所以我确实是以一种比较的心态来处理整个任务的,因为我要将它与我的常用工作流程进行比较。我的主要目标是看看Fargate 有多容易,特别是与 Kubernetes 相比时。结果让我感到非常惊讶。

AWS 是有开销的

我有一个干净的 AWS 账户,并决定从零到部署一个 webapp。与 AWS 中的其它基础设施一样,我必须首先使基本的基础设施正常工作起来,因此我需要先定义一个 VPC。

遵循最佳实践,因此我将这个 VPC 划分为跨可用区(AZ)的子网,一个公共子网和私有子网。这时我想到,只要这种设置基础设施的需求存在,我就能找到一份这种工作。AWS 是免运维的这一概念一直让我感到愤怒。开发者社区中的许多人理所当然地认为在设置和定义一个设计良好的 AWS 账户和基础设施是不需要付出多少工作和努力的。而这种想当然甚至发生在开始谈论多帐户架构之前就有了——现在我仍然使用单一帐户,我已经必须定义好基础设施和传统的网络设备。

这里也值得记住,我已经做了很多次,所以我很清楚该做什么。我可以在我的帐户中使用默认的 VPC 以及预先提供的子网,我觉得很多刚开始的人也可以使用它。这大概花了我半个小时才运行起来,但我不禁想到,即使我想运行 lambda 函数,我仍然需要某种连接和网络。定义 NAT 网关和在 VPC 中路由根本不会让你觉得很“Serverless”,但要往下进行这就是必须要做的。

运行简单的容器

在我启动运行了基本的基础设施之后,现在我想让我的 Docker 容器运行起来。我开始翻阅 Fargate 文档并浏览 入门 文档,这些就马上就展现在了我面前:

等等,只是让我的容器运行就至少要有三个步骤?这完全不像我所想的,不过还是让我们开始吧。

任务定义

任务定义 Task Definition ”用来定义要运行的实际容器。我在这里遇到的问题是,任务定义这件事非常复杂。这里有很多选项都很简单,比如指定 Docker 镜像和内存限制,但我还必须定义一个网络模型以及我并不熟悉的其它各种选项。真需要这样吗?如果我完全没有 AWS 方面的知识就进入到这个过程里,那么在这个阶段我会感觉非常的不知所措。可以在 AWS 页面上找到这些 参数 的完整列表,这个列表很长。我知道我的容器需要一些环境变量,它需要暴露一个端口。所以我首先在一个神奇的 terraform 模块 的帮助下定义了这一点,这真的让这件事更容易了。如果没有这个模块,我就得手写 JSON 来定义我的容器定义。

首先我定义了一些环境变量:

container_environment_variables = [
    {
      name  = "USER"
      value = "${var.user}"
    },
    {
      name  = "PASSWORD"
      value = "${var.password}"
    }
]

然后我使用上面提及的模块组成了任务定义:

module "container_definition_app" {
  source  = "cloudposse/ecs-container-definition/aws"
  version = "v0.7.0"

  container_name  = "${var.name}"
  container_image = "${var.image}"

  container_cpu                = "${var.ecs_task_cpu}"
  container_memory             = "${var.ecs_task_memory}"
  container_memory_reservation = "${var.container_memory_reservation}"

  port_mappings = [
    {
      containerPort = "${var.app_port}"
      hostPort      = "${var.app_port}"
      protocol      = "tcp"
    },
  ]

  environment = "${local.container_environment_variables}"

}

在这一点上我非常困惑,我需要在这里定义很多配置才能运行,而这时什么都没有开始呢,但这是必要的 —— 运行 Docker 容器肯定需要了解一些容器配置的知识。我 之前写过 关于 Kubernetes 和配置管理的问题的文章,在这里似乎遇到了同样的问题。

接下来,我在上面的模块中定义了任务定义(幸好从我这里抽象出了所需的 JSON —— 如果我不得不手写JSON,我可能已经放弃了)。

当我定义模块参数时,我突然意识到我漏掉了一些东西。我需要一个 IAM 角色!好吧,让我来定义:

resource "aws_iam_role" "ecs_task_execution" {
  name = "${var.name}-ecs_task_execution"

  assume_role_policy = <<EOF
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "ecs_task_execution" {
  count = "${length(var.policies_arn)}"

  role       = "${aws_iam_role.ecs_task_execution.id}"
  policy_arn = "${element(var.policies_arn, count.index)}"
}

这样做是有意义的,我需要在 Kubernetes 中定义一个 RBAC 策略,所以在这里我还未完全错失或获得任何东西。这时我开始觉得从 Kubernetes 的角度来看,这种感觉非常熟悉。

resource "aws_ecs_task_definition" "app" {
  family                   = "${var.name}"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "${var.ecs_task_cpu}"
  memory                   = "${var.ecs_task_memory}"
  execution_role_arn       = "${aws_iam_role.ecs_task_execution.arn}"
  task_role_arn            = "${aws_iam_role.ecs_task_execution.arn}"

  container_definitions    = "${module.container_definition_app.json}"

}

现在,为了运行起来我已经写了很多行代码,我阅读了很多 ECS 文档,我所做的就是定义一个任务定义。我还没有让这个东西运行起来。在这一点上,我真的很困惑这个基于 Kubernetes 的平台到底增值了什么,但我继续前行。

服务

服务,一定程度上是将容器如何暴露给外部,另外是如何定义它拥有的副本数量。我的第一个想法是“啊!这就像一个 Kubernetes 服务!”我开始着手映射端口等。这是我第一次在 terraform 上跑:

resource "aws_ecs_service" "app" {
  name                               = "${var.name}"
  cluster                            = "${module.ecs.this_ecs_cluster_id}"
  task_definition                    = "${data.aws_ecs_task_definition.app.family}:${max(aws_ecs_task_definition.app.revision, data.aws_ecs_task_definition.app.revision)}"
  desired_count                      = "${var.ecs_service_desired_count}"
  launch_type                        = "FARGATE"
  deployment_maximum_percent         = "${var.ecs_service_deployment_maximum_percent}"
  deployment_minimum_healthy_percent = "${var.ecs_service_deployment_minimum_healthy_percent}"

  network_configuration {
    subnets          = ["${values(local.private_subnets)}"]
    security_groups  = ["${module.app.this_security_group_id}"]
  }

}

当我必须定义允许访问所需端口的安全组时,我再次感到沮丧,当我这样做了并将其插入到网络配置中后,我就像被扇了一巴掌。

我还需要定义自己的负载均衡器?

什么?

当然不是吗?

负载均衡器从未远离

老实说,我很满意,我甚至不确定为什么。我已经习惯了 Kubernetes 的服务和 Ingress 对象,我一心认为用 Kubernetes 将我的应用程序放到网上是多么容易。当然,我们在 $work 花了几个月的时间建立一个平台,以便更轻松。我是 external-dnscert-manager 的重度用户,它们可以自动填充 Ingress 对象上的 DNS 条目并自动化 TLS 证书,我非常了解进行这些设置所需的工作,但老实说,我认为在 Fargate 上做这件事会更容易。我认识到 Fargate 并没有声称自己是“如何运行应用程序”这件事的全部和最终目的,它只是抽象出节点管理,但我一直被告知这比 Kubernetes 更加容易。我真的很惊讶。定义负载均衡器(即使你不想使用 Ingress 和 Ingress Controller)也是向 Kubernetes 部署服务的重要组成部分,我不得不在这里再次做同样的事情。这一切都让人觉得如此熟悉。

我现在意识到我需要:

  • 一个负载均衡器
  • 一个 TLS 证书
  • 一个 DNS 名字

所以我着手做了这些。我使用了一些流行的 terraform 模块,并做了这个:

# Define a wildcard cert for my app
module "acm" {
  source  = "terraform-aws-modules/acm/aws"
  version = "v1.1.0"

  create_certificate = true

  domain_name = "${var.route53_zone_name}"
  zone_id     = "${data.aws_route53_zone.this.id}"

  subject_alternative_names = [
    "*.${var.route53_zone_name}",
  ]


  tags = "${local.tags}"

}
# Define my loadbalancer
resource "aws_lb" "main" {
  name            = "${var.name}"
  subnets         = [ "${values(local.public_subnets)}" ]
  security_groups = ["${module.alb_https_sg.this_security_group_id}", "${module.alb_http_sg.this_security_group_id}"]
}

resource "aws_lb_target_group" "main" {
  name        = "${var.name}"
  port        = "${var.app_port}"
  protocol    = "HTTP"
  vpc_id      = "${local.vpc_id}"
  target_type = "ip"
  depends_on  = [ "aws_lb.main" ]
}

# Redirect all traffic from the ALB to the target group
resource "aws_lb_listener" "main" {
  load_balancer_arn = "${aws_lb.main.id}"
  port              = "80"
  protocol          = "HTTP"

  default_action {
    target_group_arn = "${aws_lb_target_group.main.id}"
    type             = "forward"
  }
}

resource "aws_lb_listener" "main-tls" {
  load_balancer_arn = "${aws_lb.main.id}"
  port              = "443"
  protocol          = "HTTPS"
  certificate_arn   = "${module.acm.this_acm_certificate_arn}"

  default_action {
    target_group_arn = "${aws_lb_target_group.main.id}"
    type             = "forward"
  }
}

我必须承认,我在这里搞砸了好几次。我不得不在 AWS 控制台中四处翻弄,以弄清楚我做错了什么。这当然不是一个“轻松”的过程,而且我之前已经做过很多次了。老实说,在这一点上,Kubernetes 看起来对我很有启发性,但我意识到这是因为我对它非常熟悉。幸运的是我能够使用托管的 Kubernetes 平台(预装了 external-dns 和 cert-manager),我真的很想知道我漏掉了 Fargate 什么增值的地方。它真的感觉不那么简单。

经过一番折腾,我现在有一个可以工作的 ECS 服务。包括服务在内的最终定义如下所示:

data "aws_ecs_task_definition" "app" {
  task_definition = "${var.name}"
  depends_on      = ["aws_ecs_task_definition.app"]
}

resource "aws_ecs_service" "app" {
  name                               = "${var.name}"
  cluster                            = "${module.ecs.this_ecs_cluster_id}"
  task_definition                    = "${data.aws_ecs_task_definition.app.family}:${max(aws_ecs_task_definition.app.revision, data.aws_ecs_task_definition.app.revision)}"
  desired_count                      = "${var.ecs_service_desired_count}"
  launch_type                        = "FARGATE"
  deployment_maximum_percent         = "${var.ecs_service_deployment_maximum_percent}"
  deployment_minimum_healthy_percent = "${var.ecs_service_deployment_minimum_healthy_percent}"

  network_configuration {
    subnets          = ["${values(local.private_subnets)}"]
    security_groups  = ["${module.app_sg.this_security_group_id}"]
  }

  load_balancer {
    target_group_arn = "${aws_lb_target_group.main.id}"
    container_name   = "app"
    container_port   = "${var.app_port}"
  }

  depends_on = [
    "aws_lb_listener.main",
  ]

}

我觉得我已经接近完成了,但后来我记起了我只完成了最初的“入门”文档中所需的 3 个步骤中的 2 个,我仍然需要定义 ECS 群集。

集群

感谢 定义模块,定义要所有这些运行的集群实际上非常简单。

module "ecs" {
  source  = "terraform-aws-modules/ecs/aws"
  version = "v1.1.0"

  name = "${var.name}"
}

这里让我感到惊讶的是为什么我必须定义一个集群。作为一个相当熟悉 ECS 的人,你会觉得你需要一个集群,但我试图从一个必须经历这个过程的新人的角度来考虑这一点 —— 对我来说,Fargate 标榜自己“ Serverless”而你仍需要定义集群,这似乎很令人惊讶。当然这是一个小细节,但它确实盘旋在我的脑海里。

告诉我你的 Secret

在这个阶段,我很高兴我成功地运行了一些东西。然而,我的原始的成功标准缺少一些东西。如果我们回到任务定义那里,你会记得我的应用程序有一个存放密码的环境变量:

container_environment_variables = [
    {
      name  = "USER"
      value = "${var.user}"
    },
    {
      name  = "PASSWORD"
      value = "${var.password}"
    }
]

如果我在 AWS 控制台中查看我的任务定义,我的密码就在那里,明晃晃的明文。我希望不要这样,所以我开始尝试将其转化为其他东西,类似于 Kubernetes 的Secrets管理

AWS SSM

Fargate / ECS 执行 secret 管理 secret management 部分的方式是使用 AWS SSM(此服务的全名是 AWS 系统管理器参数存储库 Systems Manager Parameter Store,但我不想使用这个名称,因为坦率地说这个名字太愚蠢了)。

AWS 文档很好的涵盖了这个内容,因此我开始将其转换为 terraform。

指定秘密信息

首先,你必须定义一个参数并为其命名。在 terraform 中,它看起来像这样:

resource "aws_ssm_parameter" "app_password" {
  name  = "${var.app_password_param_name}" # The name of the value in AWS SSM
  type  = "SecureString"
  value = "${var.app_password}" # The actual value of the password, like correct-horse-battery-stable
}

显然,这里的关键部分是 “SecureString” 类型。这会使用默认的 AWS KMS 密钥来加密数据,这对我来说并不是很直观。这比 Kubernetes 的 Secret 管理具有巨大优势,默认情况下,这些 Secret 在 etcd 中是不加密的。

然后我为 ECS 指定了另一个本地值映射,并将其作为 Secret 参数传递:

container_secrets = [
    {
      name      = "PASSWORD"
      valueFrom = "${var.app_password_param_name}"
    },
]

module "container_definition_app" {
  source  = "cloudposse/ecs-container-definition/aws"
  version = "v0.7.0"

  container_name  = "${var.name}"
  container_image = "${var.image}"

  container_cpu                = "${var.ecs_task_cpu}"
  container_memory             = "${var.ecs_task_memory}"
  container_memory_reservation = "${var.container_memory_reservation}"

  port_mappings = [
    {
      containerPort = "${var.app_port}"
      hostPort      = "${var.app_port}"
      protocol      = "tcp"
    },
  ]

  environment = "${local.container_environment_variables}"
  secrets     = "${local.container_secrets}"
出了个问题

此刻,我重新部署了我的任务定义,并且非常困惑。为什么任务没有正确拉起?当新的任务定义(版本 8)可用时,我一直在控制台中看到正在运行的应用程序仍在使用先前的任务定义(版本 7)。解决这件事花费的时间比我预期的要长,但是在控制台的事件屏幕上,我注意到了 IAM 错误。我错过了一个步骤,容器无法从 AWS SSM 中读取 Secret 信息,因为它没有正确的 IAM 权限。这是我第一次真正对整个这件事情感到沮丧。从用户体验的角度来看,这里的反馈非常糟糕。如果我没有发觉的话,我会认为一切都很好,因为仍然有一个任务正在运行,我的应用程序仍然可以通过正确的 URL 访问 —— 只不过是旧的配置而已。

在 Kubernetes 里,我会清楚地看到 pod 定义中的错误。Fargate 可以确保我的应用不会停止,这绝对是太棒了,但作为一名运维,我需要一些关于发生了什么的实际反馈。这真的不够好。我真的希望 Fargate 团队的人能够读到这篇文章,改善这种体验。

就这样了

到这里就结束了,我的应用程序正在运行,也符合我的所有标准。我确实意识到我做了一些改进,其中包括:

  • 定义一个 cloudwatch 日志组,这样我就可以正确地写日志了
  • 添加了一个 route53 托管区域,使整个事情从 DNS 角度更容易自动化
  • 修复并重新调整了 IAM 权限,这里太宽泛了

但老实说,现在我想反思一下这段经历。我写了一个关于我的经历的 推特会话,然后花了其余时间思考我在这里的真实感受。

代价

经过一夜的反思,我意识到无论你是使用 Fargate 还是 Kubernetes,这个过程都大致相同。最让我感到惊讶的是,尽管我经常听说 Fargate “更容易”,但我真的没有看到任何超过 Kubernetes 平台的好处。现在,如果你正在构建 Kubernetes 集群,我绝对可以看到这里的价值 —— 管理节点和控制面板只是不必要的开销,问题是 —— 基于 Kubernetes 的平台的大多数消费者都没有这样做。如果你很幸运能够使用 GKE,你几乎不需要考虑集群的管理,你可以使用单个 gcloud 命令来运行集群。我经常使用 Digital Ocean 的 Kubernetes 托管服务,我可以肯定地说它就像操作 Fargate 集群一样简单,实际上在某种程度上它更容易。

必须定义一些基础设施来运行你的容器就是此时的代价。谷歌本周可能刚刚使用他们的 Google Cloud Run 产品改变了游戏规则,但他们在这一领域的领先优势远远领先于其他所有人。

从这整个经历中,我可以肯定的说:大规模运行容器仍然很难。它需要思考,需要领域知识,需要运维和开发人员之间的协作。它还需要一个基础来构建 —— 任何基于 AWS 的操作都需要事先定义和运行一些基础架构。我对一些公司似乎渴望的 “NoOps” 概念非常感兴趣。我想如果你正在运行一个无状态应用程序,你可以把它全部放在一个 lambda 函数和一个 API 网关中,这可能不错,但我们是否真的适合在任何一种企业环境中这样做?我真的不这么认为。

公平比较

令我印象深刻的另一个现实是,技术 A 和技术 B 之间的比较通常不太公平,我经常在 AWS 上看到这一点。这种实际情况往往与 Jeff Barr 博客文章截然不同。如果你是一家足够小的公司,你可以使用 AWS 控制台在 AWS 中部署你的应用程序并接受所有默认值,这绝对更容易。但是,我不想使用默认值,因为默认值几乎是不适用于生产环境的。一旦你开始剥离掉云服务商服务的层面,你就会开始意识到最终你仍然是在运行软件 —— 它仍然需要设计良好、部署良好、运行良好。我相信 AWS 和 Kubernetes 以及所有其他云服务商的增值服务使得它更容易运行、设计和操作,但它绝对不是免费的。

Kubernetes 的争议

最后就是:如果你将 Kubernetes 纯粹视为一个容器编排工具,你可能会喜欢 Fargate。然而,随着我对 Kubernetes 越来越熟悉,我开始意识到它作为一种技术的重要性 —— 不仅因为它是一个伟大的容器编排工具,而且因为它的设计模式 —— 它是声明性的、API 驱动的平台。 在整个 Fargate 过程期间发生的一个简单的事情是,如果我删除这里某个东西,Fargate 不一定会为我重新创建它。自动缩放很不错,不需要管理服务器和操作系统的补丁及更新也很棒,但我觉得因为无法使用 Kubernetes 自我修复和 API 驱动模型而失去了很多。当然,Kubernetes 有一个学习曲线,但从这里的体验来看,Fargate 也是如此。

总结

尽管我在这个过程中遭遇了困惑,但我确实很喜欢这种体验。我仍然相信 Fargate 是一项出色的技术,AWS 团队对 ECS/Fargate 所做的工作确实非常出色。然而,我的观点是,这绝对不比 Kubernetes “更容易”,只是……难点不同。

在生产环境中运行容器时出现的问题大致相同。如果你从这篇文章中有所收获,它应该是这样的:不管你选择的哪种方式都有运维开销。不要相信你选择一些东西你的世界就变得更轻松。我个人的意见是:如果你有一个运维团队,而你的公司要为多个应用程序团队部署容器 —— 选择一种技术并围绕它构建流程和工具以使其更容易。

人们说的一点肯定没错,用点技巧可以更容易地使用某种技术。在这个阶段,谈到 Fargate,下面的漫画这总结了我的感受:


via: https://leebriggs.co.uk/blog/2019/04/13/the-fargate-illusion.html

作者:Lee Briggs 选题:lujun9972 译者:Bestony 校对:wxy, 临石(阿里云智能技术专家)

本文由 LCTT 原创编译,Linux中国 荣誉推出

以及,对于 无服务器 Serverless 架构,什么时候该用,什么时候不该用呢?

如果将如今互联网体验中最方便实用的那一部分去掉,那么留下来的基本就是 客户端-服务端 client-server 模式了。这一个模式在互联网建立初期就已经在使用了,直到目前都没有太大的变化,也就是说,这个模式仍然在为我们服务。

那么,当人们谈论 无服务器 Serverless 架构的时候,到底是指什么呢?其实,无服务器架构并不是说不使用服务器了。恰恰相反,客户端-服务端模式仍然在其中发挥着重要的作用。

无服务器架构实际上指的是能够让开发者在不需要关心服务器上架、为操作系统打补丁、创建容器镜像这些工作的情况下,就能够完成编码、部署和创建应用这一整套流程的架构。

无服务器架构的三个重要意义

  1. 一些缺乏开发经验的人员现在要参与到开发工作中来了。无服务器架构能够让他们尽量只学习必要的工作内容,把更多的时间放在更具创造性的开发工作中。
  2. 开发者不再需要重复造轮子。运行和维护服务器、为操作系统打补丁、创建容器等这一系列工作,都可以由更专业的无服务器架构提供商来完成。
  3. 最现实的一点是,如果不使用无服务器架构,那么在服务器管理方面,总需要有一个作最终决策的人。当服务器发生崩溃时,或是需要在服务器上执行某些操作时,总是需要这样一个统领全局的人来作出决策。因此最佳的方案是使用无服务器架构。

什么时候该用或者不该用无服务器架构?

听起来无服务器架构是个好东西。但事实上,无服务器架构并不是万能的,在使用之前还需要考虑以下这些因素:

  1. 成本
  2. 使用范围
  3. 时间
  4. 控制方式

其中值得注意的是控制方式。现在已经有一些项目为开发者提供了操作和控制无服务器架构计算环境的工具了,Apache OpenWhisk 就是其中之一。

为什么要将无服务器架构开源?

关于这方面的更多内容,可以观看无服务器架构方面的专家 Saron Yitbarek 在 Command Line Heroes 节目中的访谈。


via: https://opensource.com/article/18/12/serverless-podcast-command-line-heros

作者:Jen Wike Huger 选题:lujun9972 译者:HankChow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本文将介绍如何借助 minikube 在 Kubernetes 1.8 上搭建 OpenFaaS(让 Serverless Function 变得更简单)。minikube 是一个 Kubernetes 分发版,借助它,你可以在笔记本电脑上运行 Kubernetes 集群,minikube 支持 Mac 和 Linux 操作系统,但是在 MacOS 上使用得更多一些。

本文基于我们最新的部署手册 Kubernetes 官方部署指南

安装部署 Minikube

1、 安装 xhyve driverVirtualBox ,然后在上面安装 Linux 虚拟机以部署 minikube 。根据我的经验,VirtualBox 更稳定一些。

2、 参照官方文档 安装 minikube 。

3、 使用 brewcurl -sL cli.openfaas.com | sudo sh 安装 faas-cli

4、 通过 brew install kubernetes-helm 安装 helm 命令行。

5、 运行 minikube :minikube start

Docker 船长小贴士:Mac 和 Windows 版本的 Docker 已经集成了对 Kubernetes 的支持。现在我们使用 Kubernetes 的时候,已经不需要再安装额外的软件了。

在 minikube 上面部署 OpenFaaS

1、 为 Helm 的服务器组件 tiller 新建服务账号:

kubectl -n kube-system create sa tiller \
 && kubectl create clusterrolebinding tiller \
 --clusterrole cluster-admin \
 --serviceaccount=kube-system:tiller

2、 安装 Helm 的服务端组件 tiller:

helm init --skip-refresh --upgrade --service-account tiller

3、 克隆 Kubernetes 的 OpenFaaS 驱动程序 faas-netes:

git clone https://github.com/openfaas/faas-netes && cd faas-netes

4、 Minikube 没有配置 RBAC,这里我们需要把 RBAC 关闭:

helm upgrade --install --debug --reset-values --set async=false --set rbac=false openfaas openfaas/

(LCTT 译注:RBAC(Role-Based access control)基于角色的访问权限控制,在计算机权限管理中较为常用,详情请参考以下链接:https://en.wikipedia.org/wiki/Role-based_access_control

现在,你可以看到 OpenFaaS pod 已经在你的 minikube 集群上运行起来了。输入 kubectl get pods 以查看 OpenFaaS pod:

NAME                            READY     STATUS    RESTARTS   AGE
alertmanager-6dbdcddfc4-fjmrf   1/1       Running   0          1m
faas-netesd-7b5b7d9d4-h9ftx     1/1       Running   0          1m
gateway-965d6676d-7xcv9         1/1       Running   0          1m
prometheus-64f9844488-t2mvn     1/1       Running   0          1m

30,000ft:

该 API 网关包含了一个 用于测试功能的最小化 UI,同时开放了用于功能管理的 RESTful API 。 faas-netesd 守护进程是一种 Kubernetes 控制器,用来连接 Kubernetes API 服务器来管理服务、部署和密码。

Prometheus 和 AlertManager 进程协同工作,实现 OpenFaaS Function 的弹性缩放,以满足业务需求。通过 Prometheus 指标我们可以查看系统的整体运行状态,还可以用来开发功能强悍的仪表盘。

Prometheus 仪表盘示例:

构建/迁移/运行

和很多其他的 FaaS 项目不同,OpenFaaS 使用 Docker 镜像格式来进行 Function 的创建和版本控制,这意味着可以在生产环境中使用 OpenFaaS 实现以下目标:

  • 漏洞扫描(LCTT 译注:此处我觉得应该理解为更快地实现漏洞补丁)
  • 持续集成/持续开发
  • 滚动更新

你也可以在现有的生产环境集群中利用空闲资源部署 OpenFaaS。其核心服务组件内存占用大概在 10-30MB 。

OpenFaaS 一个关键的优势在于,它可以使用容器编排平台的 API ,这样可以和 Kubernetes 以及 Docker Swarm 进行本地集成。同时,由于使用 Docker 存储库 registry 进行 Function 的版本控制,所以可以按需扩展 Function,而没有按需构建 Function 的框架的额外的延时。

新建 Function

faas-cli new --lang python hello

以上命令创建文件 hello.yml 以及文件夹 handler,文件夹有两个文件 handler.pyrequirements.txt 可用于你可能需要的 pip 模块。你可以随时编辑这些文件和文件夹,不需要担心如何维护 Dockerfile —— 我们为你通过以下方式维护:

  • 分级创建
  • 非 root 用户
  • 以官方的 Docker Alpine Linux 版本为基础进行镜像构建 (可替换)

构建你的 Function

先在本地创建 Function,然后推送到 Docker 存储库。 我们这里使用 Docker Hub,打开文件 hello.yml 然后输入你的账号名:

provider:
  name: faas
  gateway: http://localhost:8080
functions:
  hello:
    lang: python
    handler: ./hello
    image: alexellis2/hello

现在,发起构建。你的本地系统上需要安装 Docker 。

faas-cli build -f hello.yml

把封装好 Function 的 Docker 镜像版本推送到 Docker Hub。如果还没有登录 Docker hub ,继续前需要先输入命令 docker login

faas-cli push -f hello.yml

当系统中有多个 Function 的时候,可以使用 --parallel=N 来调用多核并行处理构建或推送任务。该命令也支持这些选项: --no-cache--squash

部署及测试 Function

现在,可以部署、列出、调用 Function 了。每次调用 Function 时,可以通过 Prometheus 收集指标值。

$ export gw=http://$(minikube ip):31112
$ faas-cli deploy -f hello.yml --gateway $gw
Deploying: hello.
No existing function to remove
Deployed.
URL: http://192.168.99.100:31112/function/hello

上面给到的是部署时调用 Function 的标准方法,你也可以使用下面的命令:

$ echo test | faas-cli invoke hello --gateway $gw

现在可以通过以下命令列出部署好的 Function,你将看到调用计数器数值增加。

$ faas-cli list --gateway $gw
Function                       Invocations     Replicas
hello                          1               1

提示:这条命令也可以加上 --verbose 选项获得更详细的信息。

由于我们是在远端集群(Linux 虚拟机)上面运行 OpenFaaS,命令里面加上一条 --gateway 用来覆盖环境变量。 这个选项同样适用于云平台上的远程主机。除了加上这条选项以外,还可以通过编辑 .yml 文件里面的 gateway 值来达到同样的效果。

迁移到 minikube 以外的环境

一旦你在熟悉了在 minikube 上运行 OpenFaaS ,就可以在任意 Linux 主机上搭建 Kubernetes 集群来部署 OpenFaaS 了。

继续学习

我们的 Github 上面有很多手册和博文,可以带你轻松“上车”,把我们的页面保存成书签吧:openfaas/faas

2017 哥本哈根 Dockercon Moby 峰会上,我做了关于 Serverless 和 OpenFaaS 的概述演讲,这里我把视频放上来,视频不长,大概 15 分钟左右。

最后,别忘了关注 OpenFaaS on Twitter 这里有最潮的新闻、最酷的技术和 Demo 展示。


via: https://medium.com/@alexellisuk/getting-started-with-openfaas-on-minikube-634502c7acdf

作者:Alex Ellis 译者:mandeler 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

去年,我为 Up 规划了一份蓝图,其中描述了如何以最小的成本在 AWS 上为大多数构建块创建一个很棒的无服务器环境。这篇文章则是讨论了 Up 的初始 alpha 版本。

为什么关注 无服务器 serverless ?对于初学者来说,它可以节省成本,因为你可以按需付费,且只为你使用的付费。无服务器方式是自愈的,因为每个请求被隔离并被视作“无状态的”。最后,它可以无限轻松地扩展 —— 没有机器或集群要管理。部署你的代码就行了。

大约一个月前,我决定开始在 apex/up 上开发它,并为动态 SVG 版本的 GitHub 用户投票功能写了第一个小型无服务器示例程序 tj/gh-polls。它运行良好,成本低于每月 1 美元即可为数百万次投票服务,因此我会继续这个项目,看看我是否可以提供开源版本及商业的变体版本。

其长期的目标是提供“你自己的 Heroku” 的版本,支持许多平台。虽然平台即服务(PaaS)并不新鲜,但无服务器生态系统正在使这种方案日益萎缩。据说,AWS 和其他的供应商经常由于他们提供的灵活性而被人诟病用户体验。Up 将复杂性抽象出来,同时为你提供一个几乎无需运维的解决方案。

安装

你可以使用以下命令安装 Up,查看这篇临时文档开始使用。或者如果你使用安装脚本,请下载二进制版本。(请记住,这个项目还在早期。)

curl -sfL https://raw.githubusercontent.com/apex/up/master/install.sh | sh

只需运行以下命令随时升级到最新版本:

up upgrade

你也可以通过 NPM 进行安装:

npm install -g up

功能

这个早期 alpha 版本提供什么功能?让我们来看看!请记住,Up 不是托管服务,因此你需要一个 AWS 帐户和 AWS 凭证。如果你对 AWS 不熟悉,你可能需要先停下来,直到熟悉流程。

我遇到的第一个问题是:up(1) 与 apex(1) 有何不同?Apex 专注于部署功能,用于管道和事件处理,而 Up 则侧重于应用程序、API 和静态站点,也就是单个可部署单元。Apex 不为你提供 API 网关、SSL 证书或 DNS,也不提供 URL 重写,脚本注入等。

单命令无服务器应用程序

Up 可以让你使用单条命令部署应用程序、API 和静态站点。要创建一个应用程序,你只需要一个文件,在 Node.js 的情况下,./app.js 监听由 Up 提供的 PORT'。请注意,如果你使用的是package.json,则会检测并使用startbuild` 脚本。

const http = require('http')
const { PORT = 3000 } = process.env
http.createServer((req, res) => {
  res.end('Hello World\n')
}).listen(PORT)

额外的运行时环境也支持开箱可用,例如用于 Golang 的 “main.go”,所以你可以在几秒钟内部署 Golang、Python、Crystal 或 Node.js 应用程序。

package main

import (
 "fmt"
 "log"
 "net/http"
 "os"
)

func main() {
 addr := ":" + os.Getenv("PORT")
 http.HandleFunc("/", hello)
 log.Fatal(http.ListenAndServe(addr, nil))
}

func hello(w http.ResponseWriter, r *http.Request) {
 fmt.Fprintln(w, "Hello World from Go")
}

要部署应用程序输入 up 来创建所需的资源并部署应用程序本身。这里没有模糊不清的地方,一旦它说“完成”了那就完成了,该应用程序立即可用 —— 没有远程构建过程。

后续的部署将会更快,因为栈已被配置:

使用 up url --open 测试你的程序,以在浏览器中浏览它,up url --copy 可以将 URL 保存到剪贴板,或者可以尝试使用 curl:

curl `up url`
Hello World

要删除应用程序及其资源,只需输入 up stack delete

例如,使用 up stagingup productionup url --open production 部署到预发布或生产环境。请注意,自定义域名尚不可用,它们将很快可用。之后,你还可以将版本“推广”到其他环境。

反向代理

Up 的一个独特的功能是,它不仅仅是简单地部署代码,它将一个 Golang 反向代理放在应用程序的前面。这提供了许多功能,如 URL 重写、重定向、脚本注入等等,我们将在后面进一步介绍。

基础设施即代码

在配置方面,Up 遵循现代最佳实践,因此对基础设施的更改都可以在部署之前预览,并且 IAM 策略的使用还可以限制开发人员访问以防止事故发生。一个附带的好处是它有助于自动记录你的基础设施。

以下是使用 Let's Encrypt 通过 AWS ACM 配置一些(虚拟)DNS 记录和免费 SSL 证书的示例。

{
  "name": "app",
  "dns": {
    "myapp.com": [
      {
        "name": "myapp.com",
        "type": "A",
        "ttl": 300,
        "value": ["35.161.83.243"]
      },
      {
        "name": "blog.myapp.com",
        "type": "CNAME",
        "ttl": 300,
        "value": ["34.209.172.67"]
      },
      {
        "name": "api.myapp.com",
        "type": "A",
        "ttl": 300,
        "value": ["54.187.185.18"]
      }
    ]
  },
  "certs": [
    {
      "domains": ["myapp.com", "*.myapp.com"]
    }
  ]
}

当你首次通过 up 部署应用程序时,需要所有的权限,它为你创建 API 网关、Lambda 函数、ACM 证书、Route53 DNS 记录等。

ChangeSets 尚未实现,但你能使用 up stack plan 预览进一步的更改,并使用 up stack apply 提交,这与 Terraform 非常相似。

详细信息请参阅配置文档

全球部署

regions 数组可以指定应用程序的目标区域。例如,如果你只对单个地区感兴趣,请使用:

{
  "regions": ["us-west-2"]
}

如果你的客户集中在北美,你可能需要使用美国和加拿大所有地区:

{
  "regions": ["us-*", "ca-*"]
}

最后,你可以使用 AWS 目前支持的所有 14 个地区:

{
  "regions": ["*"]
}

多区域支持仍然是一个正在进行的工作,因为需要一些新的 AWS 功能来将它们结合在一起。

静态文件服务

Up 默认支持静态文件服务,并带有 HTTP 缓存支持,因此你可以在应用程序前使用 CloudFront 或任何其他 CDN 来大大减少延迟。

typestatic 时,默认情况下的工作目录是 .,但是你也可以提供一个 static.dir

{
  "name": "app",
  "type": "static",
  "static": {
    "dir": "public"
  }
}

构建钩子

构建钩子允许你在部署或执行其他操作时定义自定义操作。一个常见的例子是使用 Webpack 或 Browserify 捆绑 Node.js 应用程序,这大大减少了文件大小,因为 node 模块是很大的。

{
  "name": "app",
  "hooks": {
    "build": "browserify --node server.js > app.js",
    "clean": "rm app.js"
  }
}

脚本和样式表插入

Up 允许你插入脚本和样式,无论是内联方式或声明路径。它甚至支持一些“罐头”脚本,用于 Google Analytics(分析)和 Segment,只需复制并粘贴你的写入密钥即可。

{
  "name": "site",
  "type": "static",
  "inject": {
    "head": [
      {
        "type": "segment",
        "value": "API_KEY"
      },
      {
        "type": "inline style",
        "file": "/css/primer.css"
      }
    ],
    "body": [
      {
        "type": "script",
        "value": "/app.js"
      }
    ]
  }
}

重写和重定向

Up 通过 redirects 对象支持重定向和 URL 重写,该对象将路径模式映射到新位置。如果省略 status 参数(或值为 200),那么它是重写,否则是重定向。

{
  "name": "app",
  "type": "static",
  "redirects": {
    "/blog": {
      "location": "https://blog.apex.sh/",
      "status": 301
    },
    "/docs/:section/guides/:guide": {
      "location": "/help/:section/:guide",
      "status": 302
    },
    "/store/*": {
      "location": "/shop/:splat"
    }
  }
}

用于重写的常见情况是 SPA(单页面应用程序),你希望不管路径如何都提供 index.html,当然除非文件存在。

{
  "name": "app",
  "type": "static",
  "redirects": {
    "/*": {
      "location": "/",
      "status": 200
    }
  }
}

如果要强制实施该规则,无论文件是否存在,只需添加 "force": true

环境变量

密码将在下一个版本中有,但是现在支持纯文本环境变量:

{
  "name": "api",
  "environment": {
    "API_FEATURE_FOO": "1",
    "API_FEATURE_BAR": "0"
  }
}

CORS 支持

CORS 支持允许你指定哪些(如果有的话)域可以从浏览器访问你的 API。如果你希望允许任何网站访问你的 API,只需启用它:

{
  "cors": {
    "enable": true
  }
}

你还可以自定义访问,例如仅将 API 访问限制为你的前端或 SPA。

{
  "cors": {
    "allowed_origins": ["https://myapp.com"],
    "allowed_methods": ["HEAD", "GET", "POST", "PUT", "DELETE"],
    "allowed_headers": ["Content-Type", "Authorization"]
  }
}

日志

对于 $0.5/GB 的低价格,你可以使用 CloudWatch 日志进行结构化日志查询和跟踪。Up 实现了一种用于改进 CloudWatch 提供的自定义查询语言,专门用于查询结构化 JSON 日志。

你可以查询现有日志:

up logs

跟踪在线日志:

up logs -f

或者对其中任一个进行过滤,例如只显示耗时超过 5 毫秒的 200 个 GET/HEAD 请求:

up logs 'method in ("GET", "HEAD") status = 200 duration >= 5'

查询语言是非常灵活的,这里有更多来自于 up help logs 的例子

### 显示过去 5 分钟的日志
$ up logs

### 显示过去 30 分钟的日志
$ up logs -s 30m

### 显示过去 5 小时的日志
$ up logs -s 5h

### 实时显示日志
$ up logs -f

### 显示错误日志
$ up logs error

### 显示错误和致命错误日志
$ up logs 'error or fatal'

### 显示非 info 日志
$ up logs 'not info'

### 显示特定消息的日志
$ up logs 'message = "user login"'

### 显示超时 150ms 的 200 响应
$ up logs 'status = 200 duration > 150'

### 显示 4xx 和 5xx 响应
$ up logs 'status >= 400'

### 显示用户邮件包含 @apex.sh 的日志
$ up logs 'user.email contains "@apex.sh"'

### 显示用户邮件以 @apex.sh 结尾的日志
$ up logs 'user.email = "*@apex.sh"'

### 显示用户邮件以 tj@ 开始的日志
$ up logs 'user.email = "tj@*"'

### 显示路径 /tobi 和 /loki 下的错误日志
$ up logs 'error and (path = "/tobi" or path = "/loki")'

### 和上面一样,用 in 显示
$ up logs 'error and path in ("/tobi", "/loki")'

### 更复杂的查询方式
$ up logs 'method in ("POST", "PUT") ip = "207.*" status = 200 duration >= 50'

### 将 JSON 格式的错误日志发送给 jq 工具
$ up logs error | jq

请注意,and 关键字是暗含的,虽然你也可以使用它。

冷启动时间

这是 AWS Lambda 平台的特性,但冷启动时间通常远远低于 1 秒,在未来,我计划提供一个选项来保持它们在线。

配置验证

up config 命令输出解析后的配置,有默认值和推断的运行时设置 - 它也起到验证配置的双重目的,因为任何错误都会导致退出状态 > 0。

崩溃恢复

使用 Up 作为反向代理的另一个好处是执行崩溃恢复 —— 在崩溃后重新启动服务器,并在将错误的响应发送给客户端之前重新尝试该请求。

例如,假设你的 Node.js 程序由于间歇性数据库问题而导致未捕获的异常崩溃,Up 可以在响应客户端之前重试该请求。之后这个行为会更加可定制。

适合持续集成

很难说这是一个功能,但是感谢 Golang 相对较小和独立的二进制文件,你可以在一两秒中在 CI 中安装 Up。

HTTP/2

Up 通过 API 网关支持 HTTP/2,对于服务很多资源的应用和站点可以减少延迟。我将来会对许多平台进行更全面的测试,但是 Up 的延迟已经很好了:

错误页面

Up 提供了一个默认错误页面,如果你要提供支持电子邮件或调整颜色,你可以使用 error_pages 自定义。

{
  "name": "site",
  "type": "static",
  "error_pages": {
    "variables": {
      "support_email": "[email protected]",
      "color": "#228ae6"
    }
  }
}

默认情况下,它看上去像这样:

如果你想提供自定义模板,你可以创建以下一个或多个文件。特定文件优先。

  • error.html – 匹配任何 4xx 或 5xx
  • 5xx.html – 匹配任何 5xx 错误
  • 4xx.html – 匹配任何 4xx 错误
  • CODE.html – 匹配一个特定的代码,如 404.html

查看文档阅读更多有关模板的信息。

伸缩和成本

你已经做了这么多,但是 Up 怎么伸缩?目前,API 网关和 AWS 是目标平台,因此你无需进行任何更改即可扩展,只需部署代码即可完成。你只需按需支付实际使用的数量并且无需人工干预。

AWS 每月免费提供 1,000,000 个请求,但你可以使用 http://serverlesscalc.com 来插入预期流量。在未来 Up 将提供更多的平台,所以如果一个成本过高,你可以迁移到另一个!

未来

目前为止就这样了!它可能看起来不是很多,但它已经超过 10,000 行代码,并且我刚刚开始开发。看看这个问题队列,假设项目可持续发展,看看未来会有什么期待。

如果你发现这个免费版本有用,请考虑在 OpenCollective 上捐赠我,因为我没有任何工作。我将在短期内开发早期专业版,对早期用户有年费优惠。专业或企业版也将提供源码,因此可以进行内部修复和自定义。


via: https://medium.freecodecamp.org/up-b3db1ca930ee

作者:TJ Holowaychuk 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

The Codeless Developer

互联网世界新概念层出不穷,往往今天流行的概念和技术,明天就被取代和推翻。比如说,以前大家都说什么 服务器 Server SQL 数据库 SQL Database 前端 Frontend ,而现在已经开始流行 ServerLess、No-SQL 了。

当然,ServerLess 不是说不要服务器了,而是指一些运行在无状态的容器的服务器端逻辑,比如 AWS 的 Lambda 的 FaaS。而 No-SQL 则是一种非关系型数据库。

而这位所谓的专家,什么都 “No” 和 “Less” 了,然后,就在办公室里面从早坐到晚,正在践行了“Codeless”(没代码)的日子。


via: http://turnoff.us/geek/codeless/

作者:Daniel Stori 译者:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出