在此文章中,我们将探索Kubernetes(K8s),结合DigitalOcean Kubernetes集群与Buddy自动化运维系统部署以达到以下列出的目标:
- 使用一个K8s示例应用通过Buddy流水线操作构建Docker镜像并推送至Docker Hub注册中心
- 通过K8s示例应用设置两个Hello World演示部署于K8s集群之中以便测试负载均衡器
- 为K8s示例应用安装Ingress NGINX控制器于K8s集群之中
- 使用Cert-Manager添加域名SSL证书
K8s可以将其描述为一个容器编排平台,它可以在云端或远程机器上扩展和运行您的应用程序。为了更容易理解,您可以把它想象成一个容器管理器,它会自动处理您必须手动执行的操作。
以下是使用K8s的一些优势:
- 自愈能力 – 通过自动调度程序,K8s能够在出现错误或超时的情况下用新容器替换容器。
- 滚动(Rollouts)与回滚(Rollbacks) – 除了自我修复能力外,K8s还实现了新部署滚动,类似于蓝绿色部署,大大减少了停机机会。
- 负载分配和自动发现 – 在K8s上运行的解耦应用程序能够在本地集群网络上进行通信,从而减少公开应用程序地址所需的工作量。除此之外,K8s还有多个负载分配点。这意味着,您可以将负载从入口层和服务层分配到Pod。
- 横向与纵向扩展 – K8s允许我们根据场景进行横纵扩展。您可以运行同一应用程序的500多个容器,并且仍然几乎毫不费力地管理分配给每个容器的资源。说明K8s为您的应用程序提供弹性伸缩!
- 交付速率 – 发布应用程序的速度对当今每个团队都至关重要。过去,发布必须由许多团队成员在预定维护期间完成,期间会出现很多中断和停机时间。
即使没有持续部署,K8s也能够在几乎没有停机时间的情况下促进和管理各种规模的发布。
K8s本身由几个组件组成。但我们不会在本文中介绍所有内容,主要关注于容器,我们还将使用Docker。
K8s上的容器以称为Pod的组合运行。Pod中的容器共享相同的网络、存储和地址。这说明访问pod的地址实际上意味着访问pod中的容器之一:
虽然您确实不需要流水线来让应用程序在云服务上运行,但由于SDK,在更大范围内,团队会发现依赖本地机子部署效率非常低。
流水线可以被认为是将服务或应用程序从A点移动到B点的一种方式。在CI/CD方面,我们可以将其分为三种类型:
- 持续集成 – 通过GitHub等版本控制平台对代码进行测试和版本控制。这就是Buddy的用武之地,它提供了更简单、更高效的流水线配置方式。
- 持续交付 – 有助于将应用程序从版本控制平台部署到云服务或其他供应商特定的服务。交付流水线需要批准才能部署到特定环境,例如生产环境或面向客户的环境。
- 持续部署 – 无需人为干预、批准或输入,即可轻松自动部署到云端。
微服务模式引入了一种新的软件实现方式。将此视为一种移动模式,涉及多个移动部件,所有部件都统一起来以呈现单个应用程序。
无论有没有DevOps工作人员,您的团队都不必担心与运维相关的问题,比如弄清楚三个应用程序组件的交付。最重要的是保持对产品的聚焦。
过去,部署堆栈主要基于Shell脚本构建。对于以前没有堆栈经验的团队成员来说,这通常很复杂。现在,几乎每个平台都提供 YAML。作为一种声明式和更透明的语言,YAML的学习曲线相当容易。然而,不幸的是,有些平台仍然需要YAML上对shell的解决方案。
Buddy凭借着其直观的GUI和流水线声明式YAML配置解决了这些问题。
安全性是任何流水线的关键组成部分。关键的安全问题之一是处理密钥和敏感信息。在大多数情况下,密钥或敏感信息在进行加密后作为环境变量添加到平台上,然后在构建过程中进行转译和解密。在定义这些作业的过程中,很容易通过打印密钥或对公共Docker镜像进行版本控制来泄露这些细节信息。同时还建议避免在第三方服务上使用不受限制的API密钥。
Buddy如何处理安全问题
- 只需按一下按钮即可自动加密和手动加密。
- 存储仓通用的操作变量建议和默认环境变量
平台关联肯定是最大的挑战之一。不同的团队以不同的方式处理此问题:从开箱即用的特定于平台YAML模块到脚本连接。建议采用模块化方法代替脚本化流水线,通常涉及几个步骤:从获取SDK到授权,再到实际部署。这通常会导致相当复杂、容易出错且体积庞大的流水线。
Buddy提供与各大商家的各种集成,以及具有声明性流水线操作模式丰富的buddy.yml
脚本。
本文的K8s示例应用可以在这个GitHub Repo中下载源码!
首先创建一个名为hello的Buddy项目,并选择Buddy自带的Git托管作为代码存储仓:
- 如果您还没有Buddy帐号可在此免费创建一个
下载GitHub存储仓中的源码并推送至刚刚创建的项目存储仓:
然后在Buddy中添加DigitalOcean集成,以方便持续集成所要使用的DigitalOcean Kubernetes集群:
在hello项目中创建一条流水线:
接下来,我们将在流水线上添加第一个操作,即Docker构建镜像,为将所构建的镜像推送至Docker Hub而做准备:
选择存储仓上的Dockerfile
文件并提交完成添加Docker构建镜像操作:
添加第二个操作:推送Docker镜像
推送Docker镜像的作用是可将上一个操作构建好的镜像推送至目标Docker注册中心,也就是Docker镜像存储仓,例如:Docker Hub、Amazon ECR、Google GCR以及私有的镜像注册中心等等不一。
如果您是第一次接触Docker镜像构建,推荐使用Docker Hub,目前只需要在Docker官方网站上免费注册一个帐户即可使用。
填写好相关要推送的镜像信息完成添加推送Docker镜像
此时,您应该看到如下图有两个操作添加于流水线之中:
点击以上蓝色“运行”按钮开始运行流水线:
运行完成之后,我们就可以在Docker Hub帐户中看到已有相关镜像信息显示:
在hello项目存储仓中添加第一个Hello World YAML文件hello-kubernetes-first.yaml
,同时在文件中添加以下代码:
apiVersion: v1
kind: Service
metadata:
name: hello-kubernetes-first
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
selector:
app: hello-kubernetes-first
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-kubernetes-first
spec:
replicas: 3
selector:
matchLabels:
app: hello-kubernetes-first
template:
metadata:
labels:
app: hello-kubernetes-first
spec:
containers:
- name: hello-kubernetes
image: paulbouwer/hello-kubernetes:1.10
ports:
- containerPort: 8080
env:
- name: MESSAGE
value: 这是第一个Hello World部署!
此配置定义了部署(Deployment
)和服务(Service
)。 部署包含aulbouwer/hello-kubernetes:1.7
镜像的三个副本(replicas
)和一个名为MESSAGE
的环境变量(您将在访问应用程序时看到此信息)。这里的服务(Service
)定义为在80
端口显露(expose
)集群内的部署(Deployment
)。
在流水线中添加K8s提交部署操作:
添加hello-kubernetes-first.yaml
文件,这个文件将在流水线运行时提交部署至K8s集群中:
根据以上相同的添加步骤再添加一个hello-kubernetes-second.yaml
文件作为第二个Hello World演示,并在文件中添加以下代码:
apiVersion: v1
kind: Service
metadata:
name: hello-kubernetes-second
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
selector:
app: hello-kubernetes-second
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-kubernetes-second
spec:
replicas: 3
selector:
matchLabels:
app: hello-kubernetes-second
template:
metadata:
labels:
app: hello-kubernetes-second
spec:
containers:
- name: hello-kubernetes
image: paulbouwer/hello-kubernetes:1.10
ports:
- containerPort: 8080
env:
- name: MESSAGE
value: 这是第二个Hello World部署!
此时我们就可以在流水线中看到如下图所示的流水线信息:
如上图在流水线上点击蓝色“运行”按钮,将会看到如下图所示的构建Docker镜像、推送Docker镜像以及提交两个Hello World YAML配置文件至K8s集群的流水线运行信息:
在命令行界面上运行以下命令:(创建DigitalOcean Kubernetes集群时会提示您如何配置您的电脑与其连接)
kubectl get service
运行以上代码后会显示以下信息
hello-kubernetes-first和hello-kubernetes-second都已列出,说明已经创建成功Kubernetes。
您已经使用Buddy自动化运维创建了hello-kubernetes应用程序的两个部署。每个在部署规范中都有不同的信息显示,以便在测试期间区分。 下一步,我们将安装Nginx Ingress Controller:
我们将使用Helm安装Nginx Ingress 控制器
Nginx Ingress控制器 由一个Pod和一个Service组成。Pod运行控制器,控制器不断轮询集群API服务器上的/ingresses
端点以获取可用Ingress资源的更新。该服务的类型为LoadBalancer
。因为您将其部署到DigitalOcean Kubernetes集群,集群将自动创建一个DigitalOcean负载均衡器,所有外部流量将通过该负载均衡器流向控制器。然后控制器会将流量路由到适当的服务,如Ingress资源中定义的那样。
只有LoadBalancer
服务知道自动创建的负载均衡IP地址。某些应用程序(例如:ExternalDNS)需要知道其IP地址,但只能读取Ingress的配置。通过在helm install
安装期间将controller.publishService.enabled
参数设置为true
,可以将控制器配置为在每个Ingress上发布IP地址。建议启用此设置以支持可能依赖于负载均衡器IP地址的应用程序。
要安装 K8s Nginx Ingress 控制器,我们首先需要通过运行以下命令将其存储库添加到Helm:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
更新系统,让Helm知道所包含的内容:
helm repo update
最后,运行以下命令安装Nginx Ingress:
helm install nginx-ingress ingress-nginx/ingress-nginx --set controller.publishService.enabled=true
此命令从稳定chart存储仓安装Nginx Ingress,将Helm版本命名为nginx-ingress
,并将publishService
参数设置为true
。
运行后,您将看到类似以下的输出:
Helm已将其在Kubernetes中创建的资源记录为chart安装的一部分。
运行此命令以查看负载均衡器是否可用:
kubectl --namespace default get services -o wide -w nginx-ingress-ingress-nginx-controller
该命令在默认命名空间中获取Nginx Ingress服务并输出其信息,但该命令不会立即退出。使用-w
参数,它会在发生更改时监测并刷新输出信息。
我们已经安装了由Kubernetes社区维护的Nginx Ingress。它将HTTP和HTTPS流量从负载均衡器路由到Ingress资源中适配后端服务。 下一步,我们将显露(expose)公开hello-kubernetes
应用程序部署。
在公开应用程序之前,我们需要准备两个域名并指向负载均衡器IP,我们将使用以下两个域名作为演示:
- 1.m2jd.com
- 2.m2jd.com
首先,通过以上已在Buddy创建的hello项目存储仓中再创建一个名为hello-kubernetes-ingress.yaml的文件,并添加以下代码部署两个示例域名以便在浏览器中测试:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-kubernetes-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: "1.m2jd.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: hello-kubernetes-first
port:
number: 80
- host: "2.m2jd.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: hello-kubernetes-second
port:
number: 80
我们使用名称hello-kubernetes-ingress定义Ingress资源。然后指定两个主机规则,以便将1.m2jd.com域名转向路由到hello-kubernetes-first服务,并将2.m2jd.com域名转向路由到第二个部署hello-kubernetes-second的服务。
接下来,添加hello-kubernetes-ingress.yaml文件到流水线操作提交Kubernetes部署之中并运行流水线。我们就可看到如下图的hello-kubernetes-ingress部署到K8s集群的Buddy流水线运行记录:
现在我们可以在浏览器中打开域名1.m2jd.com即可看到如下显示:
在浏览器中打开域名2.m2jd.com即可看到如下显示:
为了保护Ingress资源,我们将安装Cert-Manager,为生产运营创建ClusterIssuer
,并修改Ingress的配置以使用TLS证书。安装和配置后,应用程序将在HTTPS之下运行。
ClusterIssuers
是Kubernetes中的Cert-Manager资源,它为整个集群提供TLS证书。ClusterIssuer
是一种特定类型的发行者。
在通过Helm将Cert-Manager安装到您的集群之前,您将为它创建一个命名空间:
kubectl create namespace cert-manager
这时,您需要将Jetstack Helm存储仓添加到托管Cert-Manager图谱(chart)的Helm。 为此,运行以下命令:
helm repo add jetstack https://charts.jetstack.io
Helm将显示以下输出信息:
更新Helm图谱缓存:
helm repo update
更新命令运行将显示以下输出信息:
最后,通过运行以下命令将Cert-Manager安装到cert-manager命名空间中:
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.10.1 --set installCRDs=true
在此命令中,我们也将installCRDs
参数设置为true
,以便在Helm安装期间安装cert-managerCustomResourceDefinition
配置清单。在写本文时,v1.10.1是最新版本。您可以参考ArtifactHub查找最新版本号。
除了在命令行界面上运行Helm命令,您也可以在Buddy流水线上添加Helm CLI操作并运行流水线:
输出信息将显示如下:
我们现在创建一个由Let's Encrypt颁发的证书,并将其配置存储在名为production_issuer.yaml
的文件中。在hello项目存储仓中创建并打开此文件并添加以下代码:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Email address used for ACME registration
email: 请在此输入您的电子邮件地址
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Name of a secret used to store the ACME account private key
name: letsencrypt-prod-private-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: nginx
接下来,同样在流水线中添加production_issuer.yaml文件到流水线操作提交Kubernetes部署之中并运行流水线:
在hello-kubernetes-ingress.yaml文件中添加以下高亮背景颜色的代码:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-kubernetes-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- 1.m2jd.com
- 2.m2jd.com
secretName: hello-kubernetes-tls
rules:
- host: "1.m2jd.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: hello-kubernetes-first
port:
number: 80
- host: "2.m2jd.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: hello-kubernetes-second
port:
number: 80
提交文件后Buddy将自动为您运行流水线,此时您的域名已支持SSL证书,恭喜!
在实际操作中,请将域名换成您自己的域名,而不是照搬1.m2jd.com与2.m2jd.com,那样将不会正常运行!
Kubernetes是一个基于容器的平台,用于部署、扩展和运行应用程序。Buddy让您可以通过一系列专用的K8s操作来自动化您的Kubernetes交付工作流。
每次更改应用程序代码或Kubernetes配置时,您都有两个选项来更新集群:kubectl apply
或 kubectl set image
。
在这种情况下,您的工作流程通常如下所示:
- 编辑代码或配置.yaml
- 将其推送至您的Git存储仓
- 构建新的Docker镜像
- 推送至Docker镜像
- 登录至您的K8s集群
- 运行
kubectl apply
或kubectl set image
如果您经常使用 kubectl apply
或 kubectl set image
,这个就是更好的解决方案!
如果你经常在容器中运行任务,比如:
- 新版本部署时数据库迁移
- 备份
- 批量处理作业,例如:为新版本的应用程序创建目录结构。
您可以使用pods或任务,第一种类型使用任务启动单个pod;第二个启动系列pod,直到指定数量的pod以成功状态结束。
假设您在K8s集群上有一个应用程序,存储仓包含如下内容:
- 您的应用程序源码
- 一个Dockerfile文件,其中包含有关创建应用程序镜像的说明。
- 数据库迁移脚本
- 一个Dockerfile文件,其中包含有关创建将在部署期间运行迁移的镜像说明(数据库迁移运行器)。
在这种情况下,您可以配置一个流水线:
A. 构建应用程序并迁移镜像(第一个操作)
B. 推送至Docker Hub(第二个操作)
C. 触发数据库迁移 使用先前构建的镜像(第三个操作)。您可以使用YAML文件定义镜像、命令和部署:
进行推送后,流水线将自动构建并将镜像推送到存储仓并运行迁移脚本,是不是很酷?
作业操作将等到命令执行完毕,如果退出状态不同于0,则操作将被标记为“失败”。
D. 最后一个操作是使用提交Kubernetes部署或Kubernetes设置镜像来更新K8s应用程序中的镜像。添加操作后,整个流水线将如下所示:
一切就绪后,再次推送,即可看到Buddy自动执行整个工作流程。
希望您有所收获,非常感谢您花时间阅读本文!