Docker 和 Kubernetes 应用程序打包:Metaparticle、Pulumi 与 Ballerina 比较

  • 2018-08-06
  • 本文字数:11706 字

    阅读完需:约 38 分钟

本文要点

  • 随着微服务架构的出现,软件行业正向着云原生应用程序开发和部署发展。
  • Docker 和 Kubernetes 是现代化云原生部署自动化的关键要素。
  • 目前常见的做法是借助容器创建可复制的应用程序包,但是,这涉及手工编写(和维护)YAML 部署描述文件。
  • Metaparticle、Ballerina 和 Pulumi 是三个开源项目,分别提供了自己的方法,解决 Docker 和 Kubernetes 应用程序部署中的应用打包问题。
  • 本文探讨了使用每种框架部署一个简单的“HelloWorld”应用所需要的步骤。

随着微服务架构的出现,软件行业正向着云原生应用程序开发和部署发展。双披萨团队、敏捷性、再现性、CI/CD 在当前生产力快速创新的软件行业中扮演越来越重要的角色。

Docker Kubernetes 是现代化云原生部署自动化的关键要素。常见的做法是借助容器把开发出的应用程序创建成可复制的应用程序包。Docker 使开发人员能够创建可重复的运行时环境,并在其中使用一个简单、可重复的方式定义依赖和配置应用程序。Kubernetes 是一个开源容器编排平台,使这些应用程序容器可以跨多个主机部署,并且提供了可扩展性和高可用性。这需要编写 Dockerfile 和 Kubernetes YAML 部署描述文件,这很痛苦,而且容易出错。

Metaparticle Ballerina Pulumi 是三个开源项目,分别提供了自己解决这个问题的方法。最近,我发现了三个讨论这些方法的推特。

第一个是 Andress Guisado介绍 Metaparticle 如何提供一个标准库用于创建可直接部署到 Kubernetes 的云原生应用程序。Brendan Burns 在年初的 KubeCon 大会上宣布了 Metapaticle,Andress 那时就认为这将是 2018 年的一大焦点。

在 Istio 社区聚会上接触到 Ballerina 之后,Dan Ciruli 就发推特说,Ballerina 是一门有趣的语言,因为它可以自动生成Kubernetes 和Istio YAML,作为构建过程的一部分。他进一步表示,这是一个很棒的主意,他认为其他框架也将采用这种方式。

第三个是 Ustun Ozgur 在推特上说,与繁琐的YAML 文件相比,Pulumi 把基础设施定义成代码所做的工作对于DevOps 而言就像React 针对Web 开发所做的工作。

在本文中,我将比较这三个项目如何帮助你在像 Kubernetes 这样的容器编排平台中自动化应用程序代码部署,而不必手工编写 YAML。下文将详细介绍这些方法。

Metaparticle

Metaparticle/Package 简化了构建和部署容器镜像的任务。这一个库集合使程序员可以使用他们熟悉的代码构建和部署容器。目前,它支持 Java、.NET core、Javascript(NodeJS)、Go、Python 和 Ruby 编程语言。

让我们看下如何使用 Metaparticle 把 Java 代码部署到 Kubernetes 中。

前提条件:

  1. Docker/ Kubernetes
  2. 命令行工具 mp-compiler
  3. maven 依赖 io.metaparticle:metaparticle-package

下面的代码启动一个 Kubernetes pod,其中包含一个打印“Hello World!”的 HTTP 服务: 

package io.metaparticle.tutorial;

import io.metaparticle.annotations.Package;
import io.metaparticle.annotations.Runtime;

import static io.metaparticle.Metaparticle.Containerize;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class Main {
   private static final int port = 8080;

   @Runtime(ports = {port},
       replicas = 4,
       publicAddress = true,
       executor = "metaparticle"
   )
   @Package(repository = "docker.io/lakwarus",
           jarFile = "target/metaparticle-package-tutorial-0.1-SNAPSHOT-jar-with-dependencies.jar", publish = true)
   public static void main(String[] args) {
       Containerize(() -> {
           try {
               HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
               server.createContext("/", new HttpHandler() {
                   @Override
                   public void handle(HttpExchange t) throws IOException {
                       String msg = "Hello World!";
                       t.sendResponseHeaders(200, msg.length());
                       OutputStream os = t.getResponseBody();
                       os.write(msg.getBytes());
                       os.close();
                       System.out.println("[" + t.getRequestURI() + "]");
                   }
               });
               server.start();
           } catch (IOException ex) {
               ex.printStackTrace();
           }
       });
   }
}

有几点需要注意:

  • 引入 io.metaparticle.annotations.Package 和 io.metaparticle.annotations.Runtime;
  • @Package 注解描述如何打包应用程序;
  • @Runtime 注解描述应用程序的运行时配置;
  • 把 main 函数封装在 Containerize 函数中,后者会启动 Metaparticle 代码。

编译代码:

mvn compile

这会创建一个包含所有依赖的 jar 文件。

运行代码:

mvn exec:java -Dexec.mainClass=io.metaparticle.tutorial.Main

这会生成 Dockerfile 和 Docker 镜像,并把它推送到特定的注册中心。然后,它会在配置好的 Kubernetes 集群中使用 4 个 pod 启动一个 Kubernetes 部署。

访问服务:

你需要创建一个代理来访问服务。

$ kubectl port-forward deployment/io-metaparticle-tutorial-main 8080:8080

$ curl http://localhost:8080/
Hello World!

要点:

  • 不会创建 YAML/JSON;
  • 完全自动部署;
  • 支持多种语言;
  • 支持有限的 Kubernetes 服务和部署功能,仅支持 clusterIP 服务;
  • 需要把用户代码封装在 Containerize() 块中,然后,你的代码不能以独立模式运行。

Ballerina

Ballerina 是一门新开源的云原生编程语言,设计用来把代码优先的敏捷性引入应对跨端点集成的挑战中。Ballerina 为 API、分布事务、断路器、流处理、数据访问、JSON、XML、gRPC 及许多其他集成挑战提供了一流的支持。

Ballerina 可以推断周围的架构;编译器可以感知环境,自动生成 Docker 镜像和 YAML,把微服务直接部署到像 Docker 和 Kubernetes 这样的基础设施中。

让我们看下如何使用 Ballerina Kubernetes 注解把代码部署到 Kubernetes。

前提条件:

  1. Ballerina
  2. Docker/Kubernetes

下面的代码启动一个打印“Hello World!”的 HTTP 服务:

import ballerina/http;
import ballerinax/kubernetes;

@kubernetes:Service {
serviceType: "NodePort",
name: "hello-world" 
}
endpoint http:Listener listener {
 port: 9090
};

@kubernetes:Deployment {
image: "lakwarus/helloworld",
name: "hello-world"
}
@http:ServiceConfig {
  basePath:"/"
}
service<http:Service> helloWorld bind listener {
  @http:ResourceConfig {
     path: "/"
  }
  sayHello(endpoint outboundEP, http:Request request) {
      http:Response response = new;
      response.setTextPayload("Hello World! \n");
      _ = outboundEP->respond(response);
  }
}

有几点需要注意:

  • 引入 ballerinax/kubernetes 包
  • @kubernetes:以 Ballerina 服务为基础的服务

编译代码:

编译hello_world_k8s.bal文件。编译成功后会打印运行 Kubernetes 工件的命令:

$> ballerina build hello_world_k8s.bal

@kubernetes:Docker          - complete 3/3
@kubernetes:Deployment      - complete 1/1
@kubernetes:Service         - complete 1/1

运行下面的命令部署 Kubernetes 工件: 
kubectl apply -f ./kubernetes/

Ballerina 编译器将生成 hello_containers_k8s.balx、Dockerfile、Docker 镜像和 Kubernetes 工件,结构如下:

$> tree
.
├── hello_world_k8s.bal
├── hello_world_k8s.balx
└── kubernetes
    ├── docker
    │   └── Dockerfile
    ├── hello_world_k8s_svc.yaml
    └── hello_world_k8s_deployment.yaml

运行代码kubectl apply -f ./kubernetes/把应用部署到 Kubernetes,并可以通过 Kubernetes NodePort 访问。

访问服务:


$> kubectl get svc
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-world           NodePort    10.96.118.214    <none>        9090:32045/TCP   1m

$> curl http://localhost:<32045>/
Hello, World!

要点:

  • Ballerina 提供 Kubernetes 原生支持;
  • 应该使用 Ballerina 编写代码;
  • 基于代码中定义的注解生成部署工件;
  • 部署不是完全自动的,需要运行 kubectl 命令;
  • 支持许多 Kubernetes 功能,如所有类型的 Kubernetes 服务、部署、Ingress、Secret、持久卷、ConfigMap、活性探针和横向 pod 自动扩展;
  • 不需要修改或封装用户代码;Ballerina 代码使用注解修饰对象;Ballerina 编译器把注解解析成可读可处理的 AST。

Pulumi

Pulumi 是一个云开发平台,使创建云程序变得简单高效。你可以使用自己最喜欢的语言编写云程序,Pulumi 将自动保持基础设施最新:“跳过 YAML,只要编写代码”。Pulumi 支持多语言、多云,其引擎和包生态都可以扩展。

目前,它支持 JavaScript、TypeScript、Python 和 Go 编程语言。支持的云平台包括 Amazon Web Services、 Microsoft Azure、谷歌云平台和 Kubernetes。

Pulumi 主要是针对基础设施代码自动化,而不是应用程序代码自动化。你可以使用你喜欢的编程语言,自动化基础设施部署。

Pulumi 对公共无服务器提供商如 AWS Lambda 的 FaaS 部署提供开箱即用的支持,但是在本文中,我将重点介绍在 Docker 和 Kubernetes 上的自动化部署。

前提条件:

  1. Docker/Kubernetes
  2. 安装 Pulumi(curl -fsSL https://get.pulumi.com/ | sh)
  3. 配置Pulumi 和Kubernetes 集群

让我们看下如何使用 TypeScript 和 Pulumi 部署一个“helloworld”示例应用程序。

我已经创建了一个“helloworld”示例应用程序(对 HTTP 请求打印“Hello World”)以及相应的 Docker 镜像(lakwarus/helloworld:latest)。现在,我将使用 TypeScript 以及 Pulumi 库编写简单的代码,把我的应用部署到 Kubernetes,而不必手工编写 YAML 工件。

创建 Pulumi 项目:

$> pulumi new
Please choose a template: typescript
This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the default, and press <ENTER>.
Press ^C at any time to quit.
project name: (hello) 
project description: hello world
Created project 'hello'.
stack name: (hello-dev) 
Created stack 'hello-dev'.
Installing dependencies...

added 113 packages in 12.549s
Finished installing dependencies.
New project is configured and ready to deploy with 'pulumi update'.

使用下面的依赖项更新 package.json:

{
    "name": "hello",
    "main": "bin/index.js",
    "typings": "bin/index.d.ts",
    "scripts": {
        "build": "tsc"
    },
    "devDependencies": {
        "typescript": "^2.7.2",
        "@types/node": "latest"
    },
    "dependencies": {
        "@pulumi/kubernetes": "^0.14.0",
        "@pulumi/pulumi": "^0.14.0",
        "npm": "^6.1.0"
    }
}

在 index.ts 文件中编辑应用程序和部署信息。这里,我只添加 pod 配置,不过,你可以把它扩展到其他 Kubernetes 功能。

import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";

let helloPod = new k8s.core.v1.Pod("hello", {
   metadata: {
       name: "hello",
   },
   spec: {
       containers: [{
       name: "hello",
       image: "lakwarus/helloworld",
       ports: [{
           containerPort: 9090,
       }],
       }],
   },
});

编译并运行代码:

$> npm update
$> npm run build
$> pulumi update
Previewing update of stack 'hello-dev'
Previewing changes:

     Type                    Name             Plan       Info
 +   pulumi:pulumi:Stack     hello-hello-dev  create     
 +   └─ kubernetes:core:Pod  hello            create     
 
info: 2 changes previewed:
    + 2 resources to create

Do you want to perform this update? yes
Updating stack 'hello-dev'
Performing changes:

     Type                    Name             Status      Info
 +   pulumi:pulumi:Stack     hello-hello-dev  created     
 +   └─ kubernetes:core:Pod  hello            created     
 
info: 2 changes performed:
    + 2 resources created
Update duration: 10.132746709s

访问服务:

$ kubectl port-forward pod/hello 9090:9090

$ curl http://localhost:9090/
Hello World!

要点:

  • 主要是针对基础设施代码自动化;
  • 可以使用喜欢的编程语言控制你的基础设施;
  • 应用程序代码应该在函数(如 AWS Lambda)中,或者需要在可以用于自动化部署的 Docker 镜像中;
  • 支持几乎所有的公有云提供商和 Kubernetes;
  • 仅用几行代码就可以创建复杂的部署,而不必手工编写 YAML;
  • 完全自动;
  • 潜在的供应商锁定,因为你需要有一个 http://pulumi.io 账户。

小结

随着微服务架构的出现,软件行业正向着云原生应用程序开发和部署发展。Docker 和 Kubernetes 是现代化云原生部署自动化的关键要素。不过,目前需要人工创建 YAML 部署描述文件,这个过程很繁琐,而且容易出错。

在 Kubernetes 部署应用程序的其中一种流行方式是采用不同的工具和框架。 Draft Gitkube Helm Ksonnet Skaffold 都是在这方面处于领先地位的流行工具,有一篇非常有趣的文章“ Draft、Gitkube、Helm、Ksonnet、Metaparticle 和 Skaffold 对比”,比较了这些帮助开发人员在 Kubernetes 上构建和部署应用的工具。虽然所有这些工具的工作流程都不一样,但它们解决的是同一个问题,即提高在 Kubernetes 上部署应用程序的敏捷性和效率。

Metaparticle、Ballerina 和 Pulumi 提供了不同的方法,供开发人员借助编程语言本身处理部署自动化,而不必手写 YAML。这正在成为一种趋势,将改变软件行业中的 DevOps 实践。

关于作者

Lakmal Warusawithana 是世界最大开源集成供应商 WSO2 的高级主管兼架构师。Lakmal 长期从事与开源、云、DevOps 技术相关的工作,是 Apache Stratos PaaS 项目的副总裁。2015 年,Lakmal 与人合伙创建了 thinkCube,成为开发更适合电信运营商的下一代协作式云计算产品的先驱。他负责整体的工程流程,特别关注 thinkCube 解决方案的扩展性和服务交付。在与人联合创建 thinkCube 之前,Lakmal 在 ITABS 工作了四年,该公司致力于基于 Linux 的服务器部署,提供了一个简单易用的自定义服务管理接口。Lakmal 还在各种会议上发表演讲,包括 ApacheCon、 CloudOpen、QCon、JaxLondon、Cloud Expo、Cloudstack 协作大会、WSO2Con 及许多技术聚会。Lakmal 拥有斯里兰卡科伦坡大学计算机科学理学士(荣誉)学位。

查看英文原文: Packaging Applications for Docker and Kubernetes: Metaparticle vs Pulumi vs Ballerina