用于本地Java应用开发的Skaffold

367 阅读5分钟

用于本地Java应用开发的Skaffold

Skaffold是一个处理构建、推送和部署容器镜像的工作流程的工具,它的额外好处是促进了一个优秀的本地开发循环。

在这篇文章中,我将探讨如何使用Skaffold进行基于Java的本地开发。

安装Skaffold

本地安装Skaffold很简单,这里有很好的解释。它与minikube一起作为本地kubernetes开发环境,效果非常好。

Skaffold的配置

我的示例应用程序可在这里的github资源库中找到 - github.com/bijukunjumm…

Skaffold至少需要一个在skaffold.yml文件中表达的配置,包括以下细节:

  • 如何建立一个镜像
  • 在哪里推送镜像
  • 如何部署镜像--Kubernetes工件,应该与发布的镜像的细节水乳交融并用于部署。

在我的项目中,skaffold.yml文件看起来像这样:

apiVersion: skaffold/v2beta16
kind: Config
metadata;
  name: hello-skaffold-gke
build:
  artifacts:
  - image: hello-skaffold-gke
    jib: {}
deploy:
  kubectl:
    manifests:
    - kubernetes/hello-deployment.yaml
    - kubernetes/hello-service.yaml

这告诉Skaffold:

  • 容器镜像应该使用优秀的jib工具来构建。
  • kubernetes部署工件的位置,在我的例子中是一个部署和一个描述应用程序的服务

Kubernetes清单不需要硬编码容器镜像标签,而是可以使用一个占位符,由Skaffold进行水化:

apiVersion: apps/v1
kind: Deployment
metadata;
  name: hello-skaffold-gke-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-skaffold-gke
  template:
    metadata;
      labels:
        app: hello-skaffold-gke
    spec:
      containers:
        - name: hello-skaffold-gke
          image: hello-skaffold-gke
          ports:
            - containerPort: 8080

图像部分由Skaffold用真正的标签图像名称填充。

现在我们有了Skaffold.yml文件和Kubernetes清单方面的Skaffold描述符,让我们看看Skaffold的一些用途。

构建本地镜像

使用 "skaffold build "命令构建一个本地镜像,在我的本地环境中尝试:

skaffold build --file-output artifacts.json

结果是将镜像发布到本地的docker注册中心,同时还有一个artifact.json文件,其中有一个指向所建镜像的内容:

{
  "builds": [
    {
      "imageName": "hello-skaffold-gke",
      "tag": "hello-skaffold-gke:a44382e0cd08ba65be1847b5a5aad099071d8e6f351abd88abedee1fa9a52041"
    }
  ]
}

如果我想把图像的坐标标记到Artifact注册中心,我可以指定一个额外的标志 "default-repo",方法如下:

skaffold build --file-output artifacts.json --default-repo=us-west1-docker.pkg.dev/myproject/sample-repo

导致artifacts.json文件的内容看起来像这样:

{
  "builds": [
    {
      "imageName": "hello-skaffold-gke",
      "tag": "us-west1-docker.pkg.dev/myproject/sample-repo/hello-skaffold-gke:a44382e0c008bf65be1847b5a5aad099071d8e6f351abd88abedee1fa9a52041"
    }
  ]
}

现在可以使用如下命令对kubernetes清单进行水化:

skaffold render -a artifacts.json --digest-source=local

该命令将清单水化,输出结果如下:

apiVersion: apps/v1
kind: Deployment
metadata;
  name: hello-skaffold-gke-deployment
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-skaffold-gke
  template:
    metadata;
      labels:
        app: hello-skaffold-gke
    spec:
      containers:
      - image: us-west1-docker.pkg.dev/myproject/sample-repo/hello-skaffold-gke:a44382e0c008bf65be1847b5a5aad099071d8e6f351abd88abedee1fa9a52041
        name: hello-skaffold-gke
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata;
  name: hello-skaffold-gke-service
  namespace: default
spec:
  ports:
  - name: hello-skaffold-gke
    port: 8080
  selector:
    app: hello-skaffold-gke
  type: LoadBalancer

正确的镜像名称现在被插入Kubernetes清单中,可用于部署到任何Kubernetes环境。

部署

使用Skaffold的本地开发循环

拥有Skaffold配置文件的额外好处在于Skaffold提供的优秀本地开发循环。进入开发环路所需要做的就是运行以下命令:

skaffold dev --port-forward

构建一个镜像,渲染指向该镜像的Kubernetes工件,并将Kubernetes工件部署到相关的本地Kubernetes环境,在我的例子中是minikube:

➜  hello-skaffold-gke git:(main) ✗ skaffold dev --port-forward
Listing files to watch...
 - hello-skaffold-gke
Generating tags...
 - hello-skaffold-gke -> hello-skaffold-gke:5aa5435-dirty
Checking cache...
 - hello-skaffold-gke: Found Locally
Tags used in deployment:
 - hello-skaffold-gke -> hello-skaffold-gke:a44382e0c008bf65be1847b5a5aad099071d8e6f351abd88abedee1fa9a52041
Starting deploy...
 - deployment.apps/hello-skaffold-gke-deployment created
 - service/hello-skaffold-gke-service created
Waiting for deployments to stabilize...
 - deployment/hello-skaffold-gke-deployment is ready.
Deployments stabilized in 2.175 seconds
Port forwarding service/hello-skaffold-gke-service in namespace default, remote port 8080 -> http://127.0.0.1:8080
Press Ctrl+C to exit
Watching for changes...

如果项目中的任何文件被改变,开发循环就会启动,镜像会被重建并再次部署,用jib这样的工具来创建镜像,速度之快令人惊讶。

用Skaffold进行调试

调试也可以用skaffold进行,它可以为所使用的语言启动相应的调试代理,所以对于java,如果我运行下面的命令:

skaffold debug --port-forward

并在Intellij中使用指向调试端口的 "远程进程 "来附加一个调试器:

当有断点的代码被调用时,它将暂停执行!

调试Kubernetes神器

由于真正的Kubernetes工件被用于开发循环,我们可以测试这些工件,看看其中是否有任何错误。因此,例如,如果我犯了一个错误,把 "port "说成了 "por",它就会在开发循环中显示出错误,如下所示:

WARN[0003] deployer cleanup:kubectl create: running [kubectl --context minikube create --dry-run=client -oyaml -f /Users/biju/learn/hello-skaffold-gke/kubernetes/hello-deployment.yaml -f /Users/biju/learn/hello-skaffold-gke/kubernetes/hello-service.yaml]
 - stdout: "apiVersion: apps/v1\nkind: Deployment\nmetadata;\n  name: hello-skaffold-gke-deployment\n  namespace: default\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: hello-skaffold-gke\n  template:\n    metadata;\n      labels:\n        app: hello-skaffold-gke\n    spec:\n      containers:\n      - image: hello-skaffold-gke\n        name: hello-skaffold-gke\n        ports:\n        - containerPort: 8080\n"
 - stderr: "error: error validating \"/Users/biju/learn/hello-skaffold-gke/kubernetes/hello-service.yaml\": error validating data; [ValidationError(Service.spec.ports[0]): unknown field \"por\" in io.k8s.api.core.v1.ServicePort, ValidationError(Service.spec.ports[0]): missing required field \"port\" in io.k8s.api.core.v1.ServicePort]; if you choose to ignore these errors, turn validation off with --validate=false\n"
 - cause: exit status 1  subtask=-1 task=DevLoop
kubectl create: running [kubectl --context minikube create --dry-run=client -oyaml -f /Users/biju/learn/hello-skaffold-gke/kubernetes/hello-deployment.yaml -f /Users/biju/learn/hello-skaffold-gke/kubernetes/hello-service.yaml]
 - stdout: "apiVersion: apps/v1\nkind: Deployment\nmetadata;\n  name: hello-skaffold-gke-deployment\n  namespace: default\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: hello-skaffold-gke\n  template:\n    metadata;\n      labels:\n        app: hello-skaffold-gke\n    spec:\n      containers:\n      - image: hello-skaffold-gke\n        name: hello-skaffold-gke\n        ports:\n        - containerPort: 8080\n"
 - stderr: "error: error validating \"/Users/biju/learn/hello-skaffold-gke/kubernetes/hello-service.yaml\": error validating data; [ValidationError(Service.spec.ports[0]): unknown field \"por\" in io.k8s.api.core.v1.ServicePort, ValidationError(Service.spec.ports[0]): missing required field \"port\" in io.k8s.api.core.v1.ServicePort]; if you choose to ignore these errors, turn validation off with --validate=false\n"
 - cause: exit status 1

这是确保Kubernetes清单在部署前以某种方式进行测试的一个好方法。

总结

Skaffold是我的工具箱中一个非常棒的工具,它有助于构建容器镜像,用合理的名称对其进行标记,使用镜像对Kubernetes清单进行水化,将清单部署到Kubernetes环境中。此外,它还提供了一个伟大的开发和调试循环。