K8s开发-Skaffold让k8s开发变得简单

271

感谢参考原文-bjbsair.com/2020-04-01/…

简介

2019年11月份,Skaffold普遍可用的版本发布了,承诺自动化开发工作流程,以此来节省开发者的时间。那么,Skaffold提供哪些功能呢?

  • 当你开发的时候,检测代码的变动
  • 基于你的Dockerfile或者Jib自动化build和创建你的artifacts(也就是Docker image)
  • 给artifacts打tag
  • 把artifacts发布/部署到你的kubernetes集群

为了熟悉Skaffold,我们使用minikube来运行一个本地的K8S集群,部署K8S的命令行工具kubectl。

更深入了解Skaffold,建议读者去官方网站去查看文档和例子。本文使用的源代码都在github是可见的。

先决条件

开始之前,我们需要安装minikube,kubectl和Skaffold,OS版本是Ubuntu 18.04

安装Minikube

Minikube(1.6.2)的安装工作在Linux上非常简单。如果使用Windows操作系统,请查询我们的过往文章,操作步骤还是非常复杂,未来可能会有所改善。

首先,检查我们的OS是否开启了虚拟化支持

$ egrep -q 'vmx|svm' /proc/cpuinfo && echo yes || echo no   
yes

命令的输出是yes,我们不需要额外执行命令了。

下载并安装minikube

$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_1.6.2.deb && sudo dpkg -i minikube_1.6.2.deb

启动minikube

$ minikube start  
minikube v1.6.2 on Ubuntu 18.04  
Automatically selected the 'virtualbox' driver (alternates: [none])  
Downloading VM boot image ...  
> minikube-v1.6.0.iso.sha256: 65 B / 65 B [--------------] 100.00% ? p/s 0s  
> minikube-v1.6.0.iso: 150.93 MiB / 150.93 MiB [-] 100.00% 8.44 MiB p/s 18s  
Creating virtualbox VM (CPUs=2, Memory=2000MB, Disk=20000MB) ...  
Preparing Kubernetes v1.17.0 on Docker '19.03.5' ...  
Downloading kubelet v1.17.0  
Downloading kubeadm v1.17.0  
Pulling images ...  
Launching Kubernetes ...  
Waiting for cluster to come online ...  
Done! kubectl is now configured to use "minikube"

安装Kubectl

Kubectl的安装手册可以在kubernetes官网查看。对于LinuxOS,我们需要执行下面的步骤,使用kubectl version命令可以确认是否安装成功。

$ curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl  
$ chmod +x ./kubectl  
$ sudo mv ./kubectl /usr/local/bin/kubectl  
$ kubectl version  
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:20:10Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}  
Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:12:17Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}

安装Skaffold

Skaffold的安装文档可以在官网查看。安装方法非常和kubectl非常类似。

$ curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64  
$ chmod +x skaffold  
$ sudo mv skaffold /usr/local/bin  
$ skaffold version  
v1.1.0

创建Demo应用

我们将会创建一个简单的Spring Boot demo应用。我们使用Spring MVC,创建一个REST endpoint,将会返回一个欢迎信息。

我们的Pom包含了下面的依赖关系和插件

<dependencies>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-actuator</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-web</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-test</artifactId>  
<scope>test</scope>  
</dependency>  
</dependencies>  
<build>  
<plugins>  
<plugin>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-maven-plugin</artifactId>  
</plugin>  
</plugins>  
</build>

其中的HelloController包含一个方法,返回欢迎信息和执行的主机地址。

@RestController  
public class HelloController {  
@RequestMapping("/hello")  
public String hello() {  
StringBuilder message = new StringBuilder("Hello Skaffold!");  
try {  
InetAddress ip = InetAddress.getLocalHost();  
message.append(" From host: " + ip);  
} catch (UnknownHostException e) {  
e.printStackTrace();  
}  
return message.toString();  
}  
}

使用Skaffold

现在我们已经完成了所有的准备,是时候开始使用Skaffold了。现在,我们故意遗漏一些Skaffold需要的重要配置,这可以让我们来检查报错信息和解决方法。

生成skaffold.yaml

Skaffold需要一个skaffold.yaml文件,包含你需要使用的开发工作流程。可以在你的项目目录里使用init命令来自动生成。

$ skaffold init  
FATA[0000] one or more valid Kubernetes manifests is required to run skaffold

Skaffold init并不会为我们创建k8s的mainfest文件,我们需要手动创建。

我们将会使用kubectl工具来创建k8s的部署文件。我们把Kubectl命令的输出复制到deployment.yaml文件中。 命令行参数 –dry-run 可以确保部署并没有被执行, -oyaml参数会输出配置文件,我们可以从中复制内容。

$ kubectl create deployment myskaffoldplanet --image=docker.io/mydeveloperplanet/myskaffoldplanet --dry-run -oyaml  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
name: myskaffoldplanet  
spec:  
replicas: 1  
selector:  
matchLabels:  
app: myskaffoldplanet  
strategy: {}  
template:  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
spec:  
containers:  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
name: myskaffoldplanet  
resources: {}  
status: {}

再次执行skaffold init命令,返回新的错误

$ skaffold init  
FATA[0000] one or more valid builder configuration (Dockerfile or Jib configuration) must be present to build images with skaffold; please provide at least one build config and try again or run `skaffold init --skip-build`

上面的报错是可以预见的,因为我们并没有提供Dockerfile或者Jib配置。使用我们在之前的文章中提到的方法,我们使用Jib。添加Jib Maven插件到我们的pom中。这一次我们不提供认证,因为我们并不会把docker镜像提交给docker仓库。

<plugin>  
<groupId>com.google.cloud.tools</groupId>  
<artifactId>jib-maven-plugin</artifactId>  
<version>1.8.0</version>  
<configuration>  
<!-- openjdk:11.0.5-jre -->  
<from>  
<image>openjdk@sha256:b3e19d27caa8249aad6f90c6e987943d03e915bbf3a66bc1b7f994a4fed668f6</image>  
</from>  
<to>  
<image>docker.io/${docker.image.prefix}/${project.artifactId}</image>  
<tags>  
<tag>${project.version}</tag>  
</tags>  
</to>  
<container>  
<mainClass>com.mydeveloperplanet.myskaffoldplanet.MySkaffoldPlanetApplication</mainClass>  
<user>nobody</user>  
</container>  
</configuration>  
</plugin>

为了使用Jib,我们需要在skaffold命令中加入参数: –XxenableJibInit

$ skaffold init --XXenableJibInit  
apiVersion: skaffold/v2alpha1  
kind: Config  
metadata:  
name: myskaffoldplanet  
build:  
artifacts:  
  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
jib: {}  
deploy:  
kubectl:  
manifests:  
  
- k8s/deployment.yaml  
Do you want to write this configuration to skaffold.yaml? [y/n]: y  
Configuration skaffold.yaml was written  
You can now run [skaffold build] to build the artifacts  
or [skaffold run] to build and deploy  
or [skaffold dev] to enter development mode, with auto-redeploy

Skaffold持续发布

为了配合skaffold dev命令做实验,我们已经做好了所有配置。这将会扫描我们的项目,一旦有变更,便会自动build和部署到我们的K8S集群中。执行下面的命令

$ skaffold dev

我们的应用正在被build部署到K8S集群中。操作可以在minikube的dashboard里观察。

minikube dashboard

因为并没有创建service的缘故,现在还不能调用我们的URL。我们通过NodePort方式映射端口8080。使用kubectl命令生成service yaml文件,复制内容(忽略label)到文件service.yaml到k8s的目录:

$ kubectl expose deployment myskaffoldplanet --type=NodePort --port=8080 --dry-run -oyaml  
  
apiVersion: v1  
kind: Service  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
app.kubernetes.io/managed-by: skaffold-v1.1.0  
skaffold.dev/builder: local  
skaffold.dev/cleanup: "true"  
skaffold.dev/deployer: kubectl  
skaffold.dev/docker-api-version: "1.40"  
skaffold.dev/run-id: c8fc23d2-85f5-453a-bc22-19f4a9ec88a6  
skaffold.dev/tag-policy: git-commit  
skaffold.dev/tail: "true"  
name: myskaffoldplanet  
spec:  
ports:  
- port: 8080  
protocol: TCP  
targetPort: 8080  
selector:  
app: myskaffoldplanet  
type: NodePort  
status:  
loadBalancer: {}

还有,把service.yaml文件作为mainfest文件添加到skaffold.yaml文件:

deploy:  
kubectl:  
manifests:  
  
- k8s/deployment.yaml  
- k8s/service.yaml

Skaffold会迅速观察到这些变化,自动创建服务。可以在Skaffold console输出中得到确认:

Starting deploy...  
- deployment.apps/myskaffoldplanet configured  
- service/myskaffoldplanet created  
Watching for changes...

使用Kubectl来确认服务已经被创建:

$ kubectl get services  
NAME                 TYPE         CLUSTER-IP     EXTERNAL-IP    PORT(S)           AGE  
kubernetes           ClusterIP    10.96.0.1                     443/TCP           24h  
myskaffoldplanet     NodePort     10.96.65.87                   8080:30272/TCP    42s

NodePort已经被分配给30272端口。我们现在可以引用我们的Rest endpoint:

$ curl $(minikube ip):30272/hello  
Hello Skaffold! From host: myskaffoldplanet-76f44959c9-tcvw5/172.17.0.6

在HelloController中修改欢迎致辞:

StringBuilder message = new StringBuilder(“Hello there, Skaffold!”);

再一次地,改动自动被Skaffold识别,后台进程中,我们的应用正在被build和部署。我们再一次调用URL:

$ curl $(minikube ip):30272/hello  
Hello there, Skaffold! From host: myskaffoldplanet-54b59fb785-hczn8/172.17.0.7

我们也可以使用skaffold up命令来基于需求部署应用:

$ skaffold run  
...  
Starting deploy...  
- deployment.apps/myskaffoldplanet created  
- service/myskaffoldplanet created  
You can also run [skaffold run --tail] to get the logs

Troubleshooting

之前执行skaffold dev和skaffold run命令遇到的报错如下:

rpc error: code = Unknown desc = Error response from daemon: pull access denied for mydeveloperplanet/myskaffoldplanet, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

解决方案:把下面的内容加入到skaffold.yaml文件。

build:  
local:  
push: false  
artifacts:  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
jib: {}

结论

本文中,我们观察了开发环境中Skaffold自动build和部署应用给K8S集群的过程。本文的内容仅仅是稍微观察Skaffold提供的功能,但是skaffold确实让人印象深刻。Skaffold绝对是值得使用和后续观察的。感谢参考原文-bjbsair.com/2020-04-01/…

简介

2019年11月份,Skaffold普遍可用的版本发布了,承诺自动化开发工作流程,以此来节省开发者的时间。那么,Skaffold提供哪些功能呢?

  • 当你开发的时候,检测代码的变动
  • 基于你的Dockerfile或者Jib自动化build和创建你的artifacts(也就是Docker image)
  • 给artifacts打tag
  • 把artifacts发布/部署到你的kubernetes集群

为了熟悉Skaffold,我们使用minikube来运行一个本地的K8S集群,部署K8S的命令行工具kubectl。

更深入了解Skaffold,建议读者去官方网站去查看文档和例子。本文使用的源代码都在github是可见的。

先决条件

开始之前,我们需要安装minikube,kubectl和Skaffold,OS版本是Ubuntu 18.04

安装Minikube

Minikube(1.6.2)的安装工作在Linux上非常简单。如果使用Windows操作系统,请查询我们的过往文章,操作步骤还是非常复杂,未来可能会有所改善。

首先,检查我们的OS是否开启了虚拟化支持

$ egrep -q 'vmx|svm' /proc/cpuinfo && echo yes || echo no   
yes

命令的输出是yes,我们不需要额外执行命令了。

下载并安装minikube

$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_1.6.2.deb && sudo dpkg -i minikube_1.6.2.deb

启动minikube

$ minikube start  
minikube v1.6.2 on Ubuntu 18.04  
Automatically selected the 'virtualbox' driver (alternates: [none])  
Downloading VM boot image ...  
> minikube-v1.6.0.iso.sha256: 65 B / 65 B [--------------] 100.00% ? p/s 0s  
> minikube-v1.6.0.iso: 150.93 MiB / 150.93 MiB [-] 100.00% 8.44 MiB p/s 18s  
Creating virtualbox VM (CPUs=2, Memory=2000MB, Disk=20000MB) ...  
Preparing Kubernetes v1.17.0 on Docker '19.03.5' ...  
Downloading kubelet v1.17.0  
Downloading kubeadm v1.17.0  
Pulling images ...  
Launching Kubernetes ...  
Waiting for cluster to come online ...  
Done! kubectl is now configured to use "minikube"

安装Kubectl

Kubectl的安装手册可以在kubernetes官网查看。对于LinuxOS,我们需要执行下面的步骤,使用kubectl version命令可以确认是否安装成功。

$ curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl  
$ chmod +x ./kubectl  
$ sudo mv ./kubectl /usr/local/bin/kubectl  
$ kubectl version  
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:20:10Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}  
Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:12:17Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}

安装Skaffold

Skaffold的安装文档可以在官网查看。安装方法非常和kubectl非常类似。

$ curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64  
$ chmod +x skaffold  
$ sudo mv skaffold /usr/local/bin  
$ skaffold version  
v1.1.0

创建Demo应用

我们将会创建一个简单的Spring Boot demo应用。我们使用Spring MVC,创建一个REST endpoint,将会返回一个欢迎信息。

我们的Pom包含了下面的依赖关系和插件

<dependencies>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-actuator</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-web</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-test</artifactId>  
<scope>test</scope>  
</dependency>  
</dependencies>  
<build>  
<plugins>  
<plugin>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-maven-plugin</artifactId>  
</plugin>  
</plugins>  
</build>

其中的HelloController包含一个方法,返回欢迎信息和执行的主机地址。

@RestController  
public class HelloController {  
@RequestMapping("/hello")  
public String hello() {  
StringBuilder message = new StringBuilder("Hello Skaffold!");  
try {  
InetAddress ip = InetAddress.getLocalHost();  
message.append(" From host: " + ip);  
} catch (UnknownHostException e) {  
e.printStackTrace();  
}  
return message.toString();  
}  
}

使用Skaffold

现在我们已经完成了所有的准备,是时候开始使用Skaffold了。现在,我们故意遗漏一些Skaffold需要的重要配置,这可以让我们来检查报错信息和解决方法。

生成skaffold.yaml

Skaffold需要一个skaffold.yaml文件,包含你需要使用的开发工作流程。可以在你的项目目录里使用init命令来自动生成。

$ skaffold init  
FATA[0000] one or more valid Kubernetes manifests is required to run skaffold

Skaffold init并不会为我们创建k8s的mainfest文件,我们需要手动创建。

我们将会使用kubectl工具来创建k8s的部署文件。我们把Kubectl命令的输出复制到deployment.yaml文件中。 命令行参数 –dry-run 可以确保部署并没有被执行, -oyaml参数会输出配置文件,我们可以从中复制内容。

$ kubectl create deployment myskaffoldplanet --image=docker.io/mydeveloperplanet/myskaffoldplanet --dry-run -oyaml  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
name: myskaffoldplanet  
spec:  
replicas: 1  
selector:  
matchLabels:  
app: myskaffoldplanet  
strategy: {}  
template:  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
spec:  
containers:  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
name: myskaffoldplanet  
resources: {}  
status: {}

再次执行skaffold init命令,返回新的错误

$ skaffold init  
FATA[0000] one or more valid builder configuration (Dockerfile or Jib configuration) must be present to build images with skaffold; please provide at least one build config and try again or run `skaffold init --skip-build`

上面的报错是可以预见的,因为我们并没有提供Dockerfile或者Jib配置。使用我们在之前的文章中提到的方法,我们使用Jib。添加Jib Maven插件到我们的pom中。这一次我们不提供认证,因为我们并不会把docker镜像提交给docker仓库。

<plugin>  
<groupId>com.google.cloud.tools</groupId>  
<artifactId>jib-maven-plugin</artifactId>  
<version>1.8.0</version>  
<configuration>  
<!-- openjdk:11.0.5-jre -->  
<from>  
<image>openjdk@sha256:b3e19d27caa8249aad6f90c6e987943d03e915bbf3a66bc1b7f994a4fed668f6</image>  
</from>  
<to>  
<image>docker.io/${docker.image.prefix}/${project.artifactId}</image>  
<tags>  
<tag>${project.version}</tag>  
</tags>  
</to>  
<container>  
<mainClass>com.mydeveloperplanet.myskaffoldplanet.MySkaffoldPlanetApplication</mainClass>  
<user>nobody</user>  
</container>  
</configuration>  
</plugin>

为了使用Jib,我们需要在skaffold命令中加入参数: –XxenableJibInit

$ skaffold init --XXenableJibInit  
apiVersion: skaffold/v2alpha1  
kind: Config  
metadata:  
name: myskaffoldplanet  
build:  
artifacts:  
  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
jib: {}  
deploy:  
kubectl:  
manifests:  
  
- k8s/deployment.yaml  
Do you want to write this configuration to skaffold.yaml? [y/n]: y  
Configuration skaffold.yaml was written  
You can now run [skaffold build] to build the artifacts  
or [skaffold run] to build and deploy  
or [skaffold dev] to enter development mode, with auto-redeploy

Skaffold持续发布

为了配合skaffold dev命令做实验,我们已经做好了所有配置。这将会扫描我们的项目,一旦有变更,便会自动build和部署到我们的K8S集群中。执行下面的命令

$ skaffold dev

我们的应用正在被build部署到K8S集群中。操作可以在minikube的dashboard里观察。

minikube dashboard

因为并没有创建service的缘故,现在还不能调用我们的URL。我们通过NodePort方式映射端口8080。使用kubectl命令生成service yaml文件,复制内容(忽略label)到文件service.yaml到k8s的目录:

$ kubectl expose deployment myskaffoldplanet --type=NodePort --port=8080 --dry-run -oyaml  
  
apiVersion: v1  
kind: Service  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
app.kubernetes.io/managed-by: skaffold-v1.1.0  
skaffold.dev/builder: local  
skaffold.dev/cleanup: "true"  
skaffold.dev/deployer: kubectl  
skaffold.dev/docker-api-version: "1.40"  
skaffold.dev/run-id: c8fc23d2-85f5-453a-bc22-19f4a9ec88a6  
skaffold.dev/tag-policy: git-commit  
skaffold.dev/tail: "true"  
name: myskaffoldplanet  
spec:  
ports:  
- port: 8080  
protocol: TCP  
targetPort: 8080  
selector:  
app: myskaffoldplanet  
type: NodePort  
status:  
loadBalancer: {}

还有,把service.yaml文件作为mainfest文件添加到skaffold.yaml文件:

deploy:  
kubectl:  
manifests:  
  
- k8s/deployment.yaml  
- k8s/service.yaml

Skaffold会迅速观察到这些变化,自动创建服务。可以在Skaffold console输出中得到确认:

Starting deploy...  
- deployment.apps/myskaffoldplanet configured  
- service/myskaffoldplanet created  
Watching for changes...

使用Kubectl来确认服务已经被创建:

$ kubectl get services  
NAME                 TYPE         CLUSTER-IP     EXTERNAL-IP    PORT(S)           AGE  
kubernetes           ClusterIP    10.96.0.1                     443/TCP           24h  
myskaffoldplanet     NodePort     10.96.65.87                   8080:30272/TCP    42s

NodePort已经被分配给30272端口。我们现在可以引用我们的Rest endpoint:

$ curl $(minikube ip):30272/hello  
Hello Skaffold! From host: myskaffoldplanet-76f44959c9-tcvw5/172.17.0.6

在HelloController中修改欢迎致辞:

StringBuilder message = new StringBuilder(“Hello there, Skaffold!”);

再一次地,改动自动被Skaffold识别,后台进程中,我们的应用正在被build和部署。我们再一次调用URL:

$ curl $(minikube ip):30272/hello  
Hello there, Skaffold! From host: myskaffoldplanet-54b59fb785-hczn8/172.17.0.7

我们也可以使用skaffold up命令来基于需求部署应用:

$ skaffold run  
...  
Starting deploy...  
- deployment.apps/myskaffoldplanet created  
- service/myskaffoldplanet created  
You can also run [skaffold run --tail] to get the logs

Troubleshooting

之前执行skaffold dev和skaffold run命令遇到的报错如下:

rpc error: code = Unknown desc = Error response from daemon: pull access denied for mydeveloperplanet/myskaffoldplanet, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

解决方案:把下面的内容加入到skaffold.yaml文件。

build:  
local:  
push: false  
artifacts:  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
jib: {}

结论

本文中,我们观察了开发环境中Skaffold自动build和部署应用给K8S集群的过程。本文的内容仅仅是稍微观察Skaffold提供的功能,但是skaffold确实让人印象深刻。Skaffold绝对是值得使用和后续观察的。感谢参考原文-bjbsair.com/2020-04-01/…

简介

2019年11月份,Skaffold普遍可用的版本发布了,承诺自动化开发工作流程,以此来节省开发者的时间。那么,Skaffold提供哪些功能呢?

  • 当你开发的时候,检测代码的变动
  • 基于你的Dockerfile或者Jib自动化build和创建你的artifacts(也就是Docker image)
  • 给artifacts打tag
  • 把artifacts发布/部署到你的kubernetes集群

为了熟悉Skaffold,我们使用minikube来运行一个本地的K8S集群,部署K8S的命令行工具kubectl。

更深入了解Skaffold,建议读者去官方网站去查看文档和例子。本文使用的源代码都在github是可见的。

先决条件

开始之前,我们需要安装minikube,kubectl和Skaffold,OS版本是Ubuntu 18.04

安装Minikube

Minikube(1.6.2)的安装工作在Linux上非常简单。如果使用Windows操作系统,请查询我们的过往文章,操作步骤还是非常复杂,未来可能会有所改善。

首先,检查我们的OS是否开启了虚拟化支持

$ egrep -q 'vmx|svm' /proc/cpuinfo && echo yes || echo no   
yes

命令的输出是yes,我们不需要额外执行命令了。

下载并安装minikube

$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_1.6.2.deb && sudo dpkg -i minikube_1.6.2.deb

启动minikube

$ minikube start  
minikube v1.6.2 on Ubuntu 18.04  
Automatically selected the 'virtualbox' driver (alternates: [none])  
Downloading VM boot image ...  
> minikube-v1.6.0.iso.sha256: 65 B / 65 B [--------------] 100.00% ? p/s 0s  
> minikube-v1.6.0.iso: 150.93 MiB / 150.93 MiB [-] 100.00% 8.44 MiB p/s 18s  
Creating virtualbox VM (CPUs=2, Memory=2000MB, Disk=20000MB) ...  
Preparing Kubernetes v1.17.0 on Docker '19.03.5' ...  
Downloading kubelet v1.17.0  
Downloading kubeadm v1.17.0  
Pulling images ...  
Launching Kubernetes ...  
Waiting for cluster to come online ...  
Done! kubectl is now configured to use "minikube"

安装Kubectl

Kubectl的安装手册可以在kubernetes官网查看。对于LinuxOS,我们需要执行下面的步骤,使用kubectl version命令可以确认是否安装成功。

$ curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl  
$ chmod +x ./kubectl  
$ sudo mv ./kubectl /usr/local/bin/kubectl  
$ kubectl version  
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:20:10Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}  
Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:12:17Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}

安装Skaffold

Skaffold的安装文档可以在官网查看。安装方法非常和kubectl非常类似。

$ curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64  
$ chmod +x skaffold  
$ sudo mv skaffold /usr/local/bin  
$ skaffold version  
v1.1.0

创建Demo应用

我们将会创建一个简单的Spring Boot demo应用。我们使用Spring MVC,创建一个REST endpoint,将会返回一个欢迎信息。

我们的Pom包含了下面的依赖关系和插件

<dependencies>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-actuator</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-web</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-test</artifactId>  
<scope>test</scope>  
</dependency>  
</dependencies>  
<build>  
<plugins>  
<plugin>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-maven-plugin</artifactId>  
</plugin>  
</plugins>  
</build>

其中的HelloController包含一个方法,返回欢迎信息和执行的主机地址。

@RestController  
public class HelloController {  
@RequestMapping("/hello")  
public String hello() {  
StringBuilder message = new StringBuilder("Hello Skaffold!");  
try {  
InetAddress ip = InetAddress.getLocalHost();  
message.append(" From host: " + ip);  
} catch (UnknownHostException e) {  
e.printStackTrace();  
}  
return message.toString();  
}  
}

使用Skaffold

现在我们已经完成了所有的准备,是时候开始使用Skaffold了。现在,我们故意遗漏一些Skaffold需要的重要配置,这可以让我们来检查报错信息和解决方法。

生成skaffold.yaml

Skaffold需要一个skaffold.yaml文件,包含你需要使用的开发工作流程。可以在你的项目目录里使用init命令来自动生成。

$ skaffold init  
FATA[0000] one or more valid Kubernetes manifests is required to run skaffold

Skaffold init并不会为我们创建k8s的mainfest文件,我们需要手动创建。

我们将会使用kubectl工具来创建k8s的部署文件。我们把Kubectl命令的输出复制到deployment.yaml文件中。 命令行参数 –dry-run 可以确保部署并没有被执行, -oyaml参数会输出配置文件,我们可以从中复制内容。

$ kubectl create deployment myskaffoldplanet --image=docker.io/mydeveloperplanet/myskaffoldplanet --dry-run -oyaml  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
name: myskaffoldplanet  
spec:  
replicas: 1  
selector:  
matchLabels:  
app: myskaffoldplanet  
strategy: {}  
template:  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
spec:  
containers:  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
name: myskaffoldplanet  
resources: {}  
status: {}

再次执行skaffold init命令,返回新的错误

$ skaffold init  
FATA[0000] one or more valid builder configuration (Dockerfile or Jib configuration) must be present to build images with skaffold; please provide at least one build config and try again or run `skaffold init --skip-build`

上面的报错是可以预见的,因为我们并没有提供Dockerfile或者Jib配置。使用我们在之前的文章中提到的方法,我们使用Jib。添加Jib Maven插件到我们的pom中。这一次我们不提供认证,因为我们并不会把docker镜像提交给docker仓库。

<plugin>  
<groupId>com.google.cloud.tools</groupId>  
<artifactId>jib-maven-plugin</artifactId>  
<version>1.8.0</version>  
<configuration>  
<!-- openjdk:11.0.5-jre -->  
<from>  
<image>openjdk@sha256:b3e19d27caa8249aad6f90c6e987943d03e915bbf3a66bc1b7f994a4fed668f6</image>  
</from>  
<to>  
<image>docker.io/${docker.image.prefix}/${project.artifactId}</image>  
<tags>  
<tag>${project.version}</tag>  
</tags>  
</to>  
<container>  
<mainClass>com.mydeveloperplanet.myskaffoldplanet.MySkaffoldPlanetApplication</mainClass>  
<user>nobody</user>  
</container>  
</configuration>  
</plugin>

为了使用Jib,我们需要在skaffold命令中加入参数: –XxenableJibInit

$ skaffold init --XXenableJibInit  
apiVersion: skaffold/v2alpha1  
kind: Config  
metadata:  
name: myskaffoldplanet  
build:  
artifacts:  
  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
jib: {}  
deploy:  
kubectl:  
manifests:  
  
- k8s/deployment.yaml  
Do you want to write this configuration to skaffold.yaml? [y/n]: y  
Configuration skaffold.yaml was written  
You can now run [skaffold build] to build the artifacts  
or [skaffold run] to build and deploy  
or [skaffold dev] to enter development mode, with auto-redeploy

Skaffold持续发布

为了配合skaffold dev命令做实验,我们已经做好了所有配置。这将会扫描我们的项目,一旦有变更,便会自动build和部署到我们的K8S集群中。执行下面的命令

$ skaffold dev

我们的应用正在被build部署到K8S集群中。操作可以在minikube的dashboard里观察。

minikube dashboard

因为并没有创建service的缘故,现在还不能调用我们的URL。我们通过NodePort方式映射端口8080。使用kubectl命令生成service yaml文件,复制内容(忽略label)到文件service.yaml到k8s的目录:

$ kubectl expose deployment myskaffoldplanet --type=NodePort --port=8080 --dry-run -oyaml  
  
apiVersion: v1  
kind: Service  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
app.kubernetes.io/managed-by: skaffold-v1.1.0  
skaffold.dev/builder: local  
skaffold.dev/cleanup: "true"  
skaffold.dev/deployer: kubectl  
skaffold.dev/docker-api-version: "1.40"  
skaffold.dev/run-id: c8fc23d2-85f5-453a-bc22-19f4a9ec88a6  
skaffold.dev/tag-policy: git-commit  
skaffold.dev/tail: "true"  
name: myskaffoldplanet  
spec:  
ports:  
- port: 8080  
protocol: TCP  
targetPort: 8080  
selector:  
app: myskaffoldplanet  
type: NodePort  
status:  
loadBalancer: {}

还有,把service.yaml文件作为mainfest文件添加到skaffold.yaml文件:

deploy:  
kubectl:  
manifests:  
  
- k8s/deployment.yaml  
- k8s/service.yaml

Skaffold会迅速观察到这些变化,自动创建服务。可以在Skaffold console输出中得到确认:

Starting deploy...  
- deployment.apps/myskaffoldplanet configured  
- service/myskaffoldplanet created  
Watching for changes...

使用Kubectl来确认服务已经被创建:

$ kubectl get services  
NAME                 TYPE         CLUSTER-IP     EXTERNAL-IP    PORT(S)           AGE  
kubernetes           ClusterIP    10.96.0.1                     443/TCP           24h  
myskaffoldplanet     NodePort     10.96.65.87                   8080:30272/TCP    42s

NodePort已经被分配给30272端口。我们现在可以引用我们的Rest endpoint:

$ curl $(minikube ip):30272/hello  
Hello Skaffold! From host: myskaffoldplanet-76f44959c9-tcvw5/172.17.0.6

在HelloController中修改欢迎致辞:

StringBuilder message = new StringBuilder(“Hello there, Skaffold!”);

再一次地,改动自动被Skaffold识别,后台进程中,我们的应用正在被build和部署。我们再一次调用URL:

$ curl $(minikube ip):30272/hello  
Hello there, Skaffold! From host: myskaffoldplanet-54b59fb785-hczn8/172.17.0.7

我们也可以使用skaffold up命令来基于需求部署应用:

$ skaffold run  
...  
Starting deploy...  
- deployment.apps/myskaffoldplanet created  
- service/myskaffoldplanet created  
You can also run [skaffold run --tail] to get the logs

Troubleshooting

之前执行skaffold dev和skaffold run命令遇到的报错如下:

rpc error: code = Unknown desc = Error response from daemon: pull access denied for mydeveloperplanet/myskaffoldplanet, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

解决方案:把下面的内容加入到skaffold.yaml文件。

build:  
local:  
push: false  
artifacts:  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
jib: {}

结论

本文中,我们观察了开发环境中Skaffold自动build和部署应用给K8S集群的过程。本文的内容仅仅是稍微观察Skaffold提供的功能,但是skaffold确实让人印象深刻。Skaffold绝对是值得使用和后续观察的。感谢参考原文-bjbsair.com/2020-04-01/…

简介

2019年11月份,Skaffold普遍可用的版本发布了,承诺自动化开发工作流程,以此来节省开发者的时间。那么,Skaffold提供哪些功能呢?

  • 当你开发的时候,检测代码的变动
  • 基于你的Dockerfile或者Jib自动化build和创建你的artifacts(也就是Docker image)
  • 给artifacts打tag
  • 把artifacts发布/部署到你的kubernetes集群

为了熟悉Skaffold,我们使用minikube来运行一个本地的K8S集群,部署K8S的命令行工具kubectl。

更深入了解Skaffold,建议读者去官方网站去查看文档和例子。本文使用的源代码都在github是可见的。

先决条件

开始之前,我们需要安装minikube,kubectl和Skaffold,OS版本是Ubuntu 18.04

安装Minikube

Minikube(1.6.2)的安装工作在Linux上非常简单。如果使用Windows操作系统,请查询我们的过往文章,操作步骤还是非常复杂,未来可能会有所改善。

首先,检查我们的OS是否开启了虚拟化支持

$ egrep -q 'vmx|svm' /proc/cpuinfo && echo yes || echo no   
yes

命令的输出是yes,我们不需要额外执行命令了。

下载并安装minikube

$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_1.6.2.deb && sudo dpkg -i minikube_1.6.2.deb

启动minikube

$ minikube start  
minikube v1.6.2 on Ubuntu 18.04  
Automatically selected the 'virtualbox' driver (alternates: [none])  
Downloading VM boot image ...  
> minikube-v1.6.0.iso.sha256: 65 B / 65 B [--------------] 100.00% ? p/s 0s  
> minikube-v1.6.0.iso: 150.93 MiB / 150.93 MiB [-] 100.00% 8.44 MiB p/s 18s  
Creating virtualbox VM (CPUs=2, Memory=2000MB, Disk=20000MB) ...  
Preparing Kubernetes v1.17.0 on Docker '19.03.5' ...  
Downloading kubelet v1.17.0  
Downloading kubeadm v1.17.0  
Pulling images ...  
Launching Kubernetes ...  
Waiting for cluster to come online ...  
Done! kubectl is now configured to use "minikube"

安装Kubectl

Kubectl的安装手册可以在kubernetes官网查看。对于LinuxOS,我们需要执行下面的步骤,使用kubectl version命令可以确认是否安装成功。

$ curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl  
$ chmod +x ./kubectl  
$ sudo mv ./kubectl /usr/local/bin/kubectl  
$ kubectl version  
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:20:10Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}  
Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:12:17Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}

安装Skaffold

Skaffold的安装文档可以在官网查看。安装方法非常和kubectl非常类似。

$ curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64  
$ chmod +x skaffold  
$ sudo mv skaffold /usr/local/bin  
$ skaffold version  
v1.1.0

创建Demo应用

我们将会创建一个简单的Spring Boot demo应用。我们使用Spring MVC,创建一个REST endpoint,将会返回一个欢迎信息。

我们的Pom包含了下面的依赖关系和插件

<dependencies>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-actuator</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-web</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-test</artifactId>  
<scope>test</scope>  
</dependency>  
</dependencies>  
<build>  
<plugins>  
<plugin>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-maven-plugin</artifactId>  
</plugin>  
</plugins>  
</build>

其中的HelloController包含一个方法,返回欢迎信息和执行的主机地址。

@RestController  
public class HelloController {  
@RequestMapping("/hello")  
public String hello() {  
StringBuilder message = new StringBuilder("Hello Skaffold!");  
try {  
InetAddress ip = InetAddress.getLocalHost();  
message.append(" From host: " + ip);  
} catch (UnknownHostException e) {  
e.printStackTrace();  
}  
return message.toString();  
}  
}

使用Skaffold

现在我们已经完成了所有的准备,是时候开始使用Skaffold了。现在,我们故意遗漏一些Skaffold需要的重要配置,这可以让我们来检查报错信息和解决方法。

生成skaffold.yaml

Skaffold需要一个skaffold.yaml文件,包含你需要使用的开发工作流程。可以在你的项目目录里使用init命令来自动生成。

$ skaffold init  
FATA[0000] one or more valid Kubernetes manifests is required to run skaffold

Skaffold init并不会为我们创建k8s的mainfest文件,我们需要手动创建。

我们将会使用kubectl工具来创建k8s的部署文件。我们把Kubectl命令的输出复制到deployment.yaml文件中。 命令行参数 –dry-run 可以确保部署并没有被执行, -oyaml参数会输出配置文件,我们可以从中复制内容。

$ kubectl create deployment myskaffoldplanet --image=docker.io/mydeveloperplanet/myskaffoldplanet --dry-run -oyaml  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
name: myskaffoldplanet  
spec:  
replicas: 1  
selector:  
matchLabels:  
app: myskaffoldplanet  
strategy: {}  
template:  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
spec:  
containers:  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
name: myskaffoldplanet  
resources: {}  
status: {}

再次执行skaffold init命令,返回新的错误

$ skaffold init  
FATA[0000] one or more valid builder configuration (Dockerfile or Jib configuration) must be present to build images with skaffold; please provide at least one build config and try again or run `skaffold init --skip-build`

上面的报错是可以预见的,因为我们并没有提供Dockerfile或者Jib配置。使用我们在之前的文章中提到的方法,我们使用Jib。添加Jib Maven插件到我们的pom中。这一次我们不提供认证,因为我们并不会把docker镜像提交给docker仓库。

<plugin>  
<groupId>com.google.cloud.tools</groupId>  
<artifactId>jib-maven-plugin</artifactId>  
<version>1.8.0</version>  
<configuration>  
<!-- openjdk:11.0.5-jre -->  
<from>  
<image>openjdk@sha256:b3e19d27caa8249aad6f90c6e987943d03e915bbf3a66bc1b7f994a4fed668f6</image>  
</from>  
<to>  
<image>docker.io/${docker.image.prefix}/${project.artifactId}</image>  
<tags>  
<tag>${project.version}</tag>  
</tags>  
</to>  
<container>  
<mainClass>com.mydeveloperplanet.myskaffoldplanet.MySkaffoldPlanetApplication</mainClass>  
<user>nobody</user>  
</container>  
</configuration>  
</plugin>

为了使用Jib,我们需要在skaffold命令中加入参数: –XxenableJibInit

$ skaffold init --XXenableJibInit  
apiVersion: skaffold/v2alpha1  
kind: Config  
metadata:  
name: myskaffoldplanet  
build:  
artifacts:  
  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
jib: {}  
deploy:  
kubectl:  
manifests:  
  
- k8s/deployment.yaml  
Do you want to write this configuration to skaffold.yaml? [y/n]: y  
Configuration skaffold.yaml was written  
You can now run [skaffold build] to build the artifacts  
or [skaffold run] to build and deploy  
or [skaffold dev] to enter development mode, with auto-redeploy

Skaffold持续发布

为了配合skaffold dev命令做实验,我们已经做好了所有配置。这将会扫描我们的项目,一旦有变更,便会自动build和部署到我们的K8S集群中。执行下面的命令

$ skaffold dev

我们的应用正在被build部署到K8S集群中。操作可以在minikube的dashboard里观察。

minikube dashboard

因为并没有创建service的缘故,现在还不能调用我们的URL。我们通过NodePort方式映射端口8080。使用kubectl命令生成service yaml文件,复制内容(忽略label)到文件service.yaml到k8s的目录:

$ kubectl expose deployment myskaffoldplanet --type=NodePort --port=8080 --dry-run -oyaml  
  
apiVersion: v1  
kind: Service  
metadata:  
creationTimestamp: null  
labels:  
app: myskaffoldplanet  
app.kubernetes.io/managed-by: skaffold-v1.1.0  
skaffold.dev/builder: local  
skaffold.dev/cleanup: "true"  
skaffold.dev/deployer: kubectl  
skaffold.dev/docker-api-version: "1.40"  
skaffold.dev/run-id: c8fc23d2-85f5-453a-bc22-19f4a9ec88a6  
skaffold.dev/tag-policy: git-commit  
skaffold.dev/tail: "true"  
name: myskaffoldplanet  
spec:  
ports:  
- port: 8080  
protocol: TCP  
targetPort: 8080  
selector:  
app: myskaffoldplanet  
type: NodePort  
status:  
loadBalancer: {}

还有,把service.yaml文件作为mainfest文件添加到skaffold.yaml文件:

deploy:  
kubectl:  
manifests:  
  
- k8s/deployment.yaml  
- k8s/service.yaml

Skaffold会迅速观察到这些变化,自动创建服务。可以在Skaffold console输出中得到确认:

Starting deploy...  
- deployment.apps/myskaffoldplanet configured  
- service/myskaffoldplanet created  
Watching for changes...

使用Kubectl来确认服务已经被创建:

$ kubectl get services  
NAME                 TYPE         CLUSTER-IP     EXTERNAL-IP    PORT(S)           AGE  
kubernetes           ClusterIP    10.96.0.1                     443/TCP           24h  
myskaffoldplanet     NodePort     10.96.65.87                   8080:30272/TCP    42s

NodePort已经被分配给30272端口。我们现在可以引用我们的Rest endpoint:

$ curl $(minikube ip):30272/hello  
Hello Skaffold! From host: myskaffoldplanet-76f44959c9-tcvw5/172.17.0.6

在HelloController中修改欢迎致辞:

StringBuilder message = new StringBuilder(“Hello there, Skaffold!”);

再一次地,改动自动被Skaffold识别,后台进程中,我们的应用正在被build和部署。我们再一次调用URL:

$ curl $(minikube ip):30272/hello  
Hello there, Skaffold! From host: myskaffoldplanet-54b59fb785-hczn8/172.17.0.7

我们也可以使用skaffold up命令来基于需求部署应用:

$ skaffold run  
...  
Starting deploy...  
- deployment.apps/myskaffoldplanet created  
- service/myskaffoldplanet created  
You can also run [skaffold run --tail] to get the logs

Troubleshooting

之前执行skaffold dev和skaffold run命令遇到的报错如下:

rpc error: code = Unknown desc = Error response from daemon: pull access denied for mydeveloperplanet/myskaffoldplanet, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

解决方案:把下面的内容加入到skaffold.yaml文件。

build:  
local:  
push: false  
artifacts:  
- image: docker.io/mydeveloperplanet/myskaffoldplanet  
jib: {}

结论

本文中,我们观察了开发环境中Skaffold自动build和部署应用给K8S集群的过程。本文的内容仅仅是稍微观察Skaffold提供的功能,但是skaffold确实让人印象深刻。Skaffold绝对是值得使用和后续观察的。