本文介绍在开发过程中,Skaffold自动化build和deploy应用到K8S集群。 Skaffold是由Google发布的命令行工具,专注于促进K8S应用的持续deployment。自动化building和deploying到k8s集群的任务,可以让开发者专注于编写代码。Skaffold是不是很有趣呢,让我们来仔细观察一下吧。 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(1.6.2)的安装工作在Linux上非常简单。如果使用Windows操作系统,请查询我们的过往文章,操作步骤还是非常复杂,未来可能会有所改善。 首先,检查我们的OS是否开启了虚拟化支持 命令的输出是yes,我们不需要额外执行命令了。 下载并安装minikube 启动minikube Kubectl的安装手册可以在kubernetes官网查看。对于LinuxOS,我们需要执行下面的步骤,使用kubectl version命令可以确认是否安装成功。 Skaffold的安装文档可以在官网查看。安装方法非常和kubectl非常类似。 我们将会创建一个简单的Spring Boot demo应用。我们使用Spring MVC,创建一个REST endpoint,将会返回一个欢迎信息。 我们的Pom包含了下面的依赖关系和插件 其中的HelloController包含一个方法,返回欢迎信息和执行的主机地址。 现在我们已经完成了所有的准备,是时候开始使用Skaffold了。现在,我们故意遗漏一些Skaffold需要的重要配置,这可以让我们来检查报错信息和解决方法。 Skaffold需要一个skaffold.yaml文件,包含你需要使用的开发工作流程。可以在你的项目目录里使用init命令来自动生成。 Skaffold init并不会为我们创建k8s的mainfest文件,我们需要手动创建。 我们将会使用kubectl工具来创建k8s的部署文件。我们把Kubectl命令的输出复制到deployment.yaml文件中。命令行参数 –dry-run 可以确保部署并没有被执行, -oyaml参数会输出配置文件,我们可以从中复制内容。 再次执行skaffold init命令,返回新的错误 上面的报错是可以预见的,因为我们并没有提供Dockerfile或者Jib配置。使用我们在之前的文章中提到的方法,我们使用Jib。添加Jib Maven插件到我们的pom中。这一次我们不提供认证,因为我们并不会把docker镜像提交给docker仓库。 为了使用Jib,我们需要在skaffold命令中加入参数:–XxenableJibInit 为了配合skaffold dev命令做实验,我们已经做好了所有配置。这将会扫描我们的项目,一旦有变更,便会自动build和部署到我们的K8S集群中。执行下面的命令 我们的应用正在被build部署到K8S集群中。操作可以在minikube的dashboard里观察。 因为并没有创建service的缘故,现在还不能调用我们的URL。我们通过NodePort方式映射端口8080。使用kubectl命令生成service yaml文件,复制内容(忽略label)到文件service.yaml到k8s的目录: 还有,把service.yaml文件作为mainfest文件添加到skaffold.yaml文件: Skaffold会迅速观察到这些变化,自动创建服务。可以在Skaffold console输出中得到确认: 使用Kubectl来确认服务已经被创建: NodePort已经被分配给30272端口。我们现在可以引用我们的Rest endpoint: 在HelloController中修改欢迎致辞: StringBuilder message = new StringBuilder(“Hello there, Skaffold!”); 再一次地,改动自动被Skaffold识别,后台进程中,我们的应用正在被build和部署。我们再一次调用URL: 我们也可以使用skaffold up命令来基于需求部署应用: 之前执行skaffold dev和skaffold run命令遇到的报错如下: 解决方案:把下面的内容加入到skaffold.yaml文件。 本文中,我们观察了开发环境中Skaffold自动build和部署应用给K8S集群的过程。本文的内容仅仅是稍微观察Skaffold提供的功能,但是skaffold确实让人印象深刻。Skaffold绝对是值得使用和后续观察的。 译者:李原 原文链接:https:///articles/skaffold-k8s-development-made-easy简介
先决条件
安装Minikube
$ egrep -q 'vmx|svm' /proc/cpuinfo && echo yes || echo no
yes
$ curl -LO https://storage./minikube/releases/latest/minikube_1.6.2.deb && sudo dpkg -i minikube_1.6.2.deb
$ minikube start
minikube v1.6.2 on Ubuntu 18.04Automatically 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 18sCreating virtualbox VM (CPUs=2, Memory=2000MB, Disk=20000MB) ...Preparing Kubernetes v1.17.0 on Docker '19.03.5' ...Downloading kubelet v1.17.0Downloading kubeadm v1.17.0Pulling images ...Launching Kubernetes ...Waiting for cluster to come online ...Done! kubectl is now configured to use 'minikube'
安装Kubectl
$ curl -LO https://storage./kubernetes-release/release/`curl -s https://storage./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
$ curl -Lo skaffold https://storage./skaffold/releases/latest/skaffold-linux-amd64
$ chmod +x skaffold
$ sudo mv skaffold /usr/local/bin
$ skaffold version
v1.1.0
创建Demo应用
<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>
@RestControllerpublic 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.yaml
$ skaffold init
FATA[0000] one or more valid Kubernetes manifests is required to run skaffold
$ 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
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`
<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>
$ 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
minikube dashboard
$ 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: {}
deploy:
kubectl:
manifests:- k8s/deployment.yaml
- k8s/service.yaml
Starting deploy...- deployment.apps/myskaffoldplanet configured
- service/myskaffoldplanet created
Watching for changes...
$ 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
$ curl $(minikube ip):30272/hello
Hello Skaffold! From host: myskaffoldplanet-76f44959c9-tcvw5/172.17.0.6
$ curl $(minikube ip):30272/hello
Hello there, Skaffold! From host: myskaffoldplanet-54b59fb785-hczn8/172.17.0.7
$ skaffold run
...Starting deploy...- deployment.apps/myskaffoldplanet created
- service/myskaffoldplanet created
You can also run [skaffold run --tail] to get the logs
Troubleshooting
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
build:local:
push: false
artifacts:- image: docker.io/mydeveloperplanet/myskaffoldplanet
jib: {}
结论