用于本地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环境中。此外,它还提供了一个伟大的开发和调试循环。

