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

在 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 的用户的热门选择。最近,适用于 MacWindows 的 Docker 也开始以实验包的形式(在“edge”通道中)提供 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 不同,但也有一定的可比性。它具有生成 Manifest 的基本能力,但这并不是其突出功能。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 集群,模拟集群内环境,并提供对 ConfigMap 和 Secrets 的访问。它旨在通过消除将应用部署到集群的需要来提高容器应用开发的迭代时间,并利用本地容器抽象网络和文件系统接口,使其看起来像应用正在集群中运行一样。

影响

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

更多信息

Ksync

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

影响

  • 它绕过了容器镜像构建和版本控制
  • 编译语言用户必须在 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

一旦两个 Deployment 都已创建且 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 目录中的代码,例如更新 service.js 中的 /healthz 端点代码,通过向 JSON 响应添加字段,然后观察 Pod 如何更新以及 curl localhost:9898/healthz 命令的响应如何变化。总的来说,最后您应该看到类似如下内容

Preview

演练:使用本地构建的 Minikube

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

$ 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

一旦两个 Deployment 都已创建且 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 目录中的代码,例如更新 service.js 中的 /healthz 端点代码,通过向 JSON 响应添加字段。代码更新完成后,最后一步是构建新的容器镜像并启动新的部署,如下所示

$ 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 Deployment 和 Service 添加一个 namespace 字段,值为 dok。将容器 spec 的 image 字段更改为 quay.io/mhausenblas/stock-con,因为 Skaffold 会动态管理容器镜像标签。

生成的 stock-conapp.yaml 文件如下所示

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 Deployment 的 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 目录中的代码,例如通过更新 service.js 中的 /healthz 端点代码,通过向 JSON 响应添加字段,您应该会看到 Skaffold 注意到更改并创建新镜像并进行部署。结果屏幕看起来会像这样

Skaffold Preview

现在您应该了解了不同的工具如何帮助您在 Kubernetes 上开发应用,如果您有兴趣了解更多关于工具或方法的信息,请查看以下资源

至此,我们结束了关于如何在 Kubernetes 上开发应用的这篇博文,希望您有所收获,如果您有反馈和/或想推荐一个您觉得有用的工具,请通过 Twitter 告知我们:IlyaMichael