本文已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已不正确。

在 Kubernetes 上进行开发

您如何开发 Kubernetes 应用程序?也就是说,您如何编写和测试一个应该在 Kubernetes 上运行的应用程序?本文重点介绍您可能需要了解的挑战、工具和方法,以便单独或在团队环境中成功编写 Kubernetes 应用程序。

我们假设您是一名开发人员,您有自己喜欢的编程语言、编辑器/IDE 和可用的测试框架。总体目标是在为 Kubernetes 开发应用程序时,对您当前的工作流程进行最小的更改。例如,如果您是一名 Node.js 开发人员,并且习惯于热重载设置(即在您的编辑器中保存时,正在运行的应用程序会自动更新),那么处理容器和容器镜像、容器注册表、Kubernetes 部署、触发器等等,不仅会让人感到不知所措,而且真的会让它变得毫无乐趣。

在下文中,我们将首先讨论整体开发设置,然后回顾行业工具,最后但并非最不重要的一点是,对三个允许针对 Kubernetes 进行迭代式本地应用程序开发的示例工具进行实践演练。

在哪里运行你的集群?

作为一名开发者,你需要考虑你正在开发的 Kubernetes 集群在哪里运行,以及开发环境位于何处。从概念上讲,有四种开发模式:

Dev Modes

许多工具支持纯离线开发,包括 Minikube、Docker for Mac/Windows、Minishift 以及我们在下面详细讨论的工具。有时,例如在微服务架构中,某些微服务已经在集群中运行,那么代理设置(将流量转发到集群和从集群转发流量)会更可取,Telepresence 就是此类别中的一个示例工具。实时模式本质上意味着你正在针对远程集群进行构建和/或部署。最后,纯在线模式意味着你的开发环境和集群都是远程的,例如 Eclipse CheCloud 9 就是这种情况。现在让我们仔细看看离线开发的基础知识:在本地运行 Kubernetes。

Minikube 是那些喜欢在本地虚拟机中运行 Kubernetes 的用户的流行选择。最近,Docker for MacWindows 开始将 Kubernetes 作为实验性软件包(在“边缘”频道中)发布。你可能希望选择使用 Minikube 而不是 Docker 桌面选项的一些原因是:

  • 你已经安装并运行了 Minikube
  • 你更喜欢等到 Docker 发布一个稳定的软件包
  • 你是 Linux 桌面用户
  • 你是没有 Windows 10 Pro 和 Hyper-V 的 Windows 用户

运行本地集群允许人们离线工作,并且你无需为使用云资源付费。云提供商的成本通常相当实惠,并且存在免费套餐,但是有些人更喜欢避免向他们的经理批准这些成本,以及潜在地产生意外成本,例如,在周末让集群运行。

一些开发人员更喜欢使用远程 Kubernetes 集群,这通常是为了允许更大的计算和存储容量,并且更容易地实现协同工作流程。这意味着你可以更轻松地拉入同事来帮助调试或共享团队中应用程序的访问权限。此外,对于某些开发人员来说,尽可能地镜像生产环境至关重要,尤其是在涉及外部云服务时,例如,专有数据库、对象存储、消息队列、外部负载均衡器或邮件传递系统。

总而言之,针对本地集群和远程集群进行开发都有充分的理由。这很大程度上取决于你所处的阶段:从早期原型设计和/或单独开发到集成一组更稳定的微服务。

现在你已经对运行时环境的选择有了基本的了解,让我们继续讨论如何迭代开发和部署你的应用程序。

行业工具

我们现在将回顾允许你在 Kubernetes 上开发应用程序的工具,重点是尽可能减少对现有工作流程的影响。我们力求提供公正的描述,包括以一般术语使用每个工具的含义。

请注意,这是一个棘手的领域,即使对于像 JSON vs YAML vs XML 或 REST vs gRPC vs SOAP 这样成熟的技术,很多也取决于你的背景、你的偏好和组织设置。在 Kubernetes 生态系统中比较工具更加困难,因为事情发展非常迅速,并且几乎每周都会宣布新的工具;例如,仅在撰写这篇文章期间,就出现了 GitkubeWatchpod。为了涵盖这些新工具以及相关的现有工具,例如 Weave Flux 和 OpenShift 的 S2I,我们计划撰写一篇你正在阅读的博客文章的后续文章。

Draft

Draft 旨在帮助你开始将任何应用程序部署到 Kubernetes。它可以应用启发式方法来确定你的应用程序是用哪种编程语言编写的,并生成一个 Dockerfile 以及一个 Helm chart。然后,它为你运行构建并将生成的镜像通过 Helm chart 部署到目标集群。它还允许用户非常轻松地设置到 localhost 的端口转发。

含义

  • 用户可以根据自己的喜好自定义 chart 和 Dockerfile 模板,甚至可以创建一个 自定义包(包含 Dockerfile、chart 等),以供将来使用
  • 要猜测任何应用程序应该如何构建并不简单,在某些情况下,用户可能需要调整 Draft 生成的 Dockerfile 和 Helm chart
  • 使用 Draft 版本 0.12.0 或更早版本,每次用户想要测试更改时,他们都需要等待 Draft 将代码复制到集群,然后运行构建,推送镜像并发布更新的 chart;这可能会很耗时,但是会导致用户每次更改都会生成一个镜像(无论它是否已提交到 git)
  • 从 Draft 版本 0.12.0 开始,构建在本地执行
  • 用户无法选择 Helm 以外的部署方式
  • 它可以监视本地更改并触发部署,但是此功能默认情况下未启用
  • 它允许开发人员使用本地或远程 Kubernetes 集群
  • 部署到生产环境取决于用户,Draft 作者推荐他们的另一个项目 – Brigade
  • 可以代替 Skaffold 使用,也可以与 Squash 一起使用

更多信息

Skaffold

Skaffold 是一种工具,旨在为与不同构建系统、镜像注册表和部署工具的 CI 集成提供可移植性。它与 Draft 不同,但又有些类似。它具有生成清单的基本功能,但这不是一个突出的功能。Skaffold 是可扩展的,允许用户在构建和部署应用程序的每个步骤中选择要使用的工具。

含义

  • 模块化设计
  • 独立于 CI 供应商工作,用户不需要 Docker 或 Kubernetes 插件
  • 无需 CI 即可工作,即从开发人员的笔记本电脑
  • 它可以监视本地更改并触发部署
  • 它允许开发人员使用本地或远程 Kubernetes 集群
  • 它可以用于部署到生产环境,用户可以配置他们喜欢的确切方式,并为每个目标环境提供不同类型的管道
  • 可以代替 Draft 使用,也可以与大多数其他工具一起使用

更多信息

Squash

Squash 由一个与 Kubernetes 完全集成的调试服务器和一个 IDE 插件组成。它允许你插入断点并执行你在使用 IDE 调试应用程序时习惯的所有有趣操作。它通过允许你将调试器附加到 Kubernetes 集群中运行的 pod,将 IDE 调试体验与你的 Kubernetes 集群桥接起来。

含义

  • 可以独立于你选择的其他工具使用
  • 需要一个特权的 DaemonSet
  • 与流行的 IDE 集成
  • 支持 Go、Python、Node.js、Java 和 gdb
  • 用户必须确保容器镜像内的应用程序二进制文件使用调试符号进行编译
  • 可以与此处描述的任何其他工具结合使用
  • 可以与本地或远程 Kubernetes 集群一起使用

更多信息

Telepresence

Telepresence 使用双向代理将开发人员工作站上运行的容器与远程 Kubernetes 集群连接起来,并模拟集群内环境,以及提供对配置映射和密钥的访问。它旨在通过消除将应用程序部署到集群的需求并利用本地容器来抽象网络和文件系统接口来缩短容器应用程序开发的迭代时间,使其看起来好像该应用程序在集群中运行一样。

含义

  • 可以独立于你选择的其他工具使用
  • 可以与 Squash 一起使用,尽管 Squash 必须用于集群中的 pod,而传统的/本地调试器需要用于调试通过 Telepresence 连接到集群的本地容器
  • Telepresence 会产生一些网络延迟
  • 它通过 side-car 进程提供连接 - sshuttle,它基于 SSH
  • 还提供更具侵入性的依赖注入模式,其中包含 LD_PRELOAD/DYLD_INSERT_LIBRARIES
  • 它最常与远程 Kubernetes 集群一起使用,但也可以与本地集群一起使用

更多信息

Ksync

Ksync 在你的本地计算机和 Kubernetes 中运行的容器之间同步应用程序代码(和配置),类似于 oc rsync 在 OpenShift 中所做的事情。它旨在通过消除构建和部署步骤来缩短应用程序开发的迭代时间。

含义

  • 它绕过了容器镜像构建和版本控制
  • 已编译语言用户必须在 pod 内运行构建 (TBC)
  • 双向同步 – 远程文件复制到本地目录
  • 每次更新远程文件系统时,都会重新启动容器
  • 没有安全功能 – 仅限开发
  • 利用 Syncthing,一个用于点对点同步的 Go 库
  • 需要在集群中运行一个特权的 DaemonSet
  • 节点必须使用带有 overlayfs2 的 Docker – 在撰写本文时不支持其他 CRI 实现

更多信息

实践演练

我们将在以下工具的实践演练中使用的应用程序是一个简单的 股票市场模拟器,由两个微服务组成:

  • stock-gen 微服务是用 Go 编写的,它随机生成股票数据并通过 HTTP 端点 /stockdata 公开。第二个微服务 stock-con 是一个 Node.js 应用程序,它使用来自 stock-gen 的股票数据流,并通过 HTTP 端点 /average/$SYMBOL 提供移动平均值的聚合,并在 /healthz 处提供运行状况检查端点。

总的来说,应用程序的默认设置如下:

Default Setup

接下来,我们将对上面讨论的工具进行实际操作演示,包括 ksync、使用本地构建的 Minikube 以及 Skaffold。对于每种工具,我们将执行以下操作:

  • 设置相应的工具,包括部署和本地使用 stock-con 微服务的准备工作。
  • 执行代码更新,即更改 stock-con 微服务中 /healthz 端点的源代码,并观察更新。

请注意,对于目标 Kubernetes 集群,我们一直在本地使用 Minikube,但如果您想跟随操作,也可以为 ksync 和 Skaffold 使用远程集群。

操作演示:ksync

作为准备工作,请安装 ksync,然后执行以下步骤来准备开发环境:

$ mkdir -p $(pwd)/ksync
$ kubectl create namespace dok
$ ksync init -n dok

完成基本设置后,我们准备告诉 ksync 的本地客户端监视特定的 Kubernetes 命名空间,然后创建一个规范来定义我们要同步的内容(本地目录 $(pwd)/ksync 与容器中的 /app)。请注意,目标 pod 是通过选择器参数指定的。

$ ksync watch -n dok
$ ksync create -n dok --selector=app=stock-con $(pwd)/ksync /app
$ ksync get -n dok

现在我们部署股票生成器和股票消费者微服务。

$ kubectl -n=dok apply \
      -f https://raw.githubusercontent.com/kubernauts/dok-example-us/master/stock-gen/app.yaml
$ kubectl -n=dok apply \
      -f https://raw.githubusercontent.com/kubernauts/dok-example-us/master/stock-con/app.yaml

一旦两个部署都已创建并且 pod 正在运行,我们将转发 stock-con 服务以供本地使用(在单独的终端会话中)。

$ kubectl get -n dok po --selector=app=stock-con  \
                     -o=custom-columns=:metadata.name --no-headers |  \
                     xargs -IPOD kubectl -n dok port-forward POD 9898:9898

这样,我们应该能够从本地机器使用 stock-con 服务;我们通过定期检查 healthz 端点的响应来实现这一点,如下所示(在单独的终端会话中):

$ watch curl localhost:9898/healthz

现在更改 ksync/stock-con 目录中的代码,例如,通过在 JSON 响应中添加一个字段来更新 service.js 中的 /healthz 端点代码,并观察 pod 如何更新以及 curl localhost:9898/healthz 命令的响应如何变化。总的来说,您最终应该得到类似以下的结果:

Preview

操作演示:使用本地构建的 Minikube

对于以下操作,您需要启动并运行 Minikube,我们将利用 Minikube 内部的 Docker 守护程序在本地构建镜像。作为准备工作,请执行以下操作:

$ git clone https://github.com/kubernauts/dok-example-us.git && cd dok-example-us
$ eval $(minikube docker-env)
$ kubectl create namespace dok

现在我们部署股票生成器和股票消费者微服务。

$ kubectl -n=dok apply -f stock-gen/app.yaml
$ kubectl -n=dok apply -f stock-con/app.yaml

一旦两个部署都已创建并且 pod 正在运行,我们将转发 stock-con 服务以供本地使用(在单独的终端会话中),并检查 healthz 端点的响应。

$ kubectl get -n dok po --selector=app=stock-con  \
                     -o=custom-columns=:metadata.name --no-headers |  \
                     xargs -IPOD kubectl -n dok port-forward POD 9898:9898 &
$ watch curl localhost:9898/healthz

现在更改 stock-con 目录中的代码,例如,通过在 JSON 响应中添加一个字段来更新 service.js 中的 /healthz 端点代码。完成代码更新后,最后一步是构建新的容器镜像并启动新的部署,如下所示:

$ docker build -t stock-con:dev -f Dockerfile .
$ kubectl -n dok set image deployment/stock-con *=stock-con:dev

总的来说,您最终应该得到类似以下的结果:

Local Preview

操作演示:Skaffold

要执行此操作演示,您首先需要安装 Skaffold。完成后,您可以执行以下步骤来准备开发环境:

$ git clone https://github.com/kubernauts/dok-example-us.git && cd dok-example-us
$ kubectl create namespace dok

现在我们部署股票生成器(但不部署股票消费者微服务,这通过 Skaffold 完成)。

$ kubectl -n=dok apply -f stock-gen/app.yaml

请注意,最初在执行 skaffold dev 时,我们遇到了身份验证错误,需要应用 Issue 322 中描述的修复程序。本质上,这意味着将 ~/.docker/config.json 的内容更改为:

{
   "auths": {}
}

接下来,我们必须稍微修补一下 stock-con/app.yaml,使其与 Skaffold 一起使用。

namespace 字段添加到 stock-con 部署和服务,值为 dok。将容器规范的 image 字段更改为 quay.io/mhausenblas/stock-con,因为 Skaffold 会动态管理容器镜像标签。

生成的 app.yaml 文件 stock-con 如下所示:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  labels:
    app: stock-con
  name: stock-con
  namespace: dok
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: stock-con
    spec:
      containers:
      - name: stock-con
        image: quay.io/mhausenblas/stock-con
        env:
        - name: DOK_STOCKGEN_HOSTNAME
          value: stock-gen
        - name: DOK_STOCKGEN_PORT
          value: "9999"
        ports:
        - containerPort: 9898
          protocol: TCP
        livenessProbe:
          initialDelaySeconds: 2
          periodSeconds: 5
          httpGet:
            path: /healthz
            port: 9898
        readinessProbe:
          initialDelaySeconds: 2
          periodSeconds: 5
          httpGet:
            path: /healthz
            port: 9898
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: stock-con
  name: stock-con
  namespace: dok
spec:
  type: ClusterIP
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 9898
  selector:
    app: stock-con

在我们开始开发之前的最后一步是配置 Skaffold。因此,在 stock-con/ 目录中创建一个名为 skaffold.yaml 的文件,内容如下:

apiVersion: skaffold/v1alpha2
kind: Config
build:
  artifacts:
  - imageName: quay.io/mhausenblas/stock-con
    workspace: .
    docker: {}
  local: {}
deploy:
  kubectl:
    manifests:
      - app.yaml

现在我们准备开始开发。为此,请在 stock-con/ 目录中执行以下命令:

$ skaffold dev

上面的命令触发 stock-con 镜像的构建,然后进行部署。一旦 stock-con 部署的 pod 正在运行,我们再次转发 stock-con 服务以供本地使用(在单独的终端会话中),并检查 healthz 端点的响应。

$ kubectl get -n dok po --selector=app=stock-con  \
                     -o=custom-columns=:metadata.name --no-headers |  \
                     xargs -IPOD kubectl -n dok port-forward POD 9898:9898 &
$ watch curl localhost:9898/healthz

如果您现在更改 stock-con 目录中的代码,例如,通过在 JSON 响应中添加一个字段来更新 service.js 中的 /healthz 端点代码,您应该看到 Skaffold 注意到更改并创建一个新镜像并进行部署。结果屏幕将如下所示:

Skaffold Preview

至此,您应该对不同的工具如何使您能够在 Kubernetes 上开发应用程序有所了解。如果您有兴趣了解有关工具和/或方法的更多信息,请查看以下资源:

至此,我们结束了关于如何在 Kubernetes 上开发应用程序的这篇文章。我们希望您学到了一些东西。如果您有反馈和/或想指出您发现有用的工具,请通过 Twitter 告诉我们:IlyaMichael