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

在 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 作为实验性软件包(在“edge”通道中)发布。您可能更喜欢使用 Minikube 而不是 Docker 桌面选项的一些原因是:

  • 您已经安装并运行了 Minikube
  • 您宁愿等到 Docker 发布稳定版本
  • 您是 Linux 桌面用户
  • 您是 Windows 用户,但没有带有 Hyper-V 的 Windows 10 专业版

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

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

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

现在您对运行时环境的选项有了基本的了解,接下来让我们看看如何迭代开发和部署您的应用程序。

行内工具

现在我们将审查允许您在 Kubernetes 上开发应用程序的工具,重点是最大限度地减少对现有工作流程的影响。我们努力提供公正的描述,包括使用每种工具的一般含义。

请注意,这是一个棘手的领域,因为即使是像 JSON 与 YAML 与 XML 或 REST 与 gRPC 与 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 会造成一定的网络延迟
  • 它通过基于 SSH 的 Sidecar 进程 sshuttle 提供连接
  • 更具侵入性的依赖注入模式,使用 LD_PRELOAD/DYLD_INSERT_LIBRARIES 也可用
  • 它最常用于远程 Kubernetes 集群,但也可以用于本地集群

更多信息

Ksync

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

影响

  • 它绕过了容器镜像构建和版本控制
  • 编译语言用户必须在 Pod 内部运行构建(待确认)
  • 双向同步 – 远程文件复制到本地目录
  • 每次远程文件系统更新时,容器都会重启
  • 无安全功能 – 仅限开发用途
  • 利用 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 通过 selector 参数指定

$ 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 兼容

stock-con 部署和服务都添加一个 namespace 字段,值为 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