作者选择技术教育基金作为Write for DOnations计划的一部分接受捐赠。
介绍
拥有自动部署流程是可扩展和弹性应用程序的必要条件,而GitOps或基于Git的DevOps已迅速成为组织CI / CD的流行方法,其中Git存储库是“单一事实来源”。 像CircleCI这样的工具与您的GitHub存储库集成,允许您在每次更改存储库时自动测试和部署代码。 当这种CI / CD与Kubernetes基础架构的灵活性相结合时,您可以构建一个可随着需求变化轻松扩展的应用程序。
在本文中,您将使用CircleCI将示例应用程序部署到DigitalOcean Kubernetes(DOKS)集群。 阅读本教程后,您将能够应用这些相同的技术来部署可构建为Docker镜像的其他CI / CD工具。
先决条件
要学习本教程,您需要:
您可以通过注册DigitalOcean帐户文档来设置DigitalOcean帐户 。
Docker安装在您的工作站上,并了解如何构建,删除和运行Docker镜像。 您可以按照Ubuntu 18.04上的如何安装和使用Docker教程在Ubuntu 18.04上安装Docker 。
了解Kubernetes如何工作以及如何在其上创建部署和服务。 强烈建议阅读Kubernetes简介文章。
kubectl
命令行界面工具安装在您将从中控制群集的计算机上。Docker Hub上的一个帐户,用于存储示例应用程序映像。
GitHub帐户和Git基础知识。 您可以按照教程系列Git简介:安装,使用和分支以及如何在GitHub上创建Pull请求来构建这些知识。
在本教程中,您将使用Kubernetes版本1.13.5
和kubectl
版本1.10.7
。
第1步 – 创建您的DigitalOcean Kubernetes集群
注意:如果您已经有一个正在运行的DigitalOcean Kubernetes集群,则可以跳过此部分。
在第一步中,您将创建DigitalOcean Kubernetes(DOKS)集群,您将从该集群部署示例应用程序。 从本地计算机执行的kubectl
命令将直接从Kubernetes集群更改或检索信息。
转到DigitalOcean帐户的Kubernetes页面 。
单击“ 创建Kubernetes群集” ,或单击页面右上角的绿色“ 创建”按钮,然后从下拉菜单中选择“ 群集 ”。
[在DigitalOcean上创建Kubernetes集群](assets.digitalocean.com/articles/cart 64920 /创建 DOKS.gif)
下一页是您要指定群集详细信息的位置。 在选择Kubernetes版本选择版本1.13.5-do.0 。 如果没有此选项,请选择更高的一个。
对于选择数据中心区域 ,请选择离您最近的区域。 本教程将使用旧金山 – 2 。
然后,您可以选择构建节点池 。 在Kubernetes上,节点是一个工作机器,它包含运行pod所需的服务。 在DigitalOcean上,每个节点都是Droplet。 您的节点池将包含一个标准节点 。 选择2GB / 1vCPU配置,并在节点数上更改为1节点。
如果需要,您可以添加额外的标签; 如果您计划使用DigitalOcean API或仅更好地组织节点池,这可能很有用。
在选择名称时 ,对于本教程,请使用kubernetes-deployment-tutorial
。 这将使您在阅读下一部分时更容易理解。 最后,单击绿色“ 创建群集”按钮以创建群集。
创建集群后,UI上会有一个按钮,用于下载名为Download Config File的配置文件 。 这是您将用于验证要对群集运行的kubectl
命令的文件。 将其下载到您的kubectl
机器。
使用该文件的默认方法是始终在使用--kubeconfig
运行的所有命令上传递--kubeconfig
标志及其路径。 例如,如果您将配置文件下载到Desktop
,则可以运行kubectl get pods
命令,如下所示:
kubectl --kubeconfig ~/Desktop/kubernetes-deployment-tutorial-kubeconfig.yaml get pods
这将产生以下输出:
No resources found.
这意味着您访问了群集。 No resources found.
消息是正确的,因为您的群集上没有任何pod。
如果您不维护任何其他Kubernetes群集,则可以将kubeconfig文件复制到主目录中名为.kube
的文件夹中。 如果该目录不存在,请创建该目录:
mkdir -p ~/.kube
然后将配置文件复制到新创建的.kube
目录中并将其重命名为config
:
cp current_kubernetes-deployment-tutorial-kubeconfig.yaml_file_path ~/.kube/config
配置文件现在应该具有路径~/.kube/config
。 这是kubectl
在运行任何命令时默认读取的文件,因此不再需要传递--kubeconfig
。 运行以下命令:
kubectl get pods
您将收到以下输出:
No resources found.
现在使用以下内容访问集群:
kubectl get nodes
您将收到群集上的节点列表。 输出将类似于:
NAME STATUS ROLES AGE VERSIONkubernetes-deployment-tutorial-1-7pto Ready <none> 1h v1.13.5
在本教程中,您将使用所有kubectl
命令和清单文件的default
命名空间,这些文件定义了Kubernetes中工作的工作负载和操作参数。 命名空间就像单个物理集群中的虚拟集群。 您可以更改为您想要的任何其他命名空间; 只需确保始终使用--namespace
标志将其传递给kubectl
,和/或在Kubernetes清单元数据字段中指定它。 它们是组织团队部署及其运行环境的好方法; 在命名空间的官方Kubernetes概述中阅读有关它们的更多信息。
完成此步骤后,您现在可以针对群集运行kubectl
。 在下一步中,您将创建将用于存放示例应用程序的本地Git存储库。
第2步 – 创建本地Git存储库
您现在要在本地Git存储库中构建示例部署。 您还将创建一些Kubernetes清单,这些清单将对您要在群集上执行的所有部署进行全局清理。
注意:本教程已经在Ubuntu 18.04上进行了测试,并且各个命令的样式与此操作系统相匹配。 但是,这里的大多数命令都可以应用于其他Linux发行版,几乎不需要进行任何更改,而像kubectl
这样的命令与平台无关。
首先,在本地创建一个新的Git存储库,稍后您将推送到GitHub。 在您的主目录中创建一个名为do-sample-app
的空文件夹并将其cd
入其中:
mkdir ~/do-sample-appcd ~/do-sample-app
现在使用以下命令在此文件夹中创建一个新的Git存储库:
git init .
在此存储库中,创建一个名为kube
的空文件夹:
mkdir ~/do-sample-app/kube/
这将是您要存储与将部署到群集的示例应用程序相关的Kubernetes资源清单的位置。
现在,创建另一个名为kube-general
文件夹,但这次是在刚刚创建的Git存储库之外。 将它放在您的主目录中:
mkdir ~/kube-general/
此文件夹位于Git存储库之外,因为它将用于存储非特定于群集上的单个部署的清单,但对多个清单是通用的。 这将允许您为不同的部署重用这些常规清单。
创建文件夹并准备好示例应用程序的Git存储库后,就可以安排DOKS集群的身份验证和授权了。
第3步 – 创建服务帐户
通常不建议使用默认管理员用户从Kubernetes集群中的其他服务进行身份验证。 如果外部提供商上的密钥遭到破坏,整个群集将会受到损害。
相反,您将使用具有特定角色的单个服务帐户 ,这是RBAC Kubernetes授权模型的所有部分。
此授权模型基于角色和资源 。 首先创建一个服务帐户 (基本上是群集中的用户),然后创建一个角色,在其中指定它可以在群集上访问的资源。 最后,创建一个角色绑定 ,用于在角色和先前创建的服务帐户之间建立连接,授予服务帐户访问角色有权访问的所有资源的权限。
您要创建的第一个Kubernetes资源是CI / CD用户的服务帐户,本教程将命名为cicd
。
在~/kube-general
文件夹中创建文件cicd-service-account.yml
,并使用您喜欢的文本编辑器打开它:
nano ~/kube-general/cicd-service-account.yml
在上面写下以下内容:
apiVersion: v1kind: ServiceAccountmetadata: name: cicd namespace: default
这是一个YAML文件; 所有Kubernetes资源都使用一个来表示。 在这种情况下,您说这个资源来自Kubernetes API版本v1
(内部kubectl
通过调用Kubernetes HTTP API创建资源),它是一个ServiceAccount
。
metadata
字段用于添加有关此资源的更多信息。 在这种情况下,您将为此ServiceAccount
指定名称cicd
,并在default
名称空间上创建它。
您现在可以通过运行kubectl apply
在集群上创建此服务帐户,如下所示:
kubectl apply -f ~/kube-general/
您将收到类似于以下内容的输出:
serviceaccount/cicd created
要确保您的服务帐户正常运行,请尝试使用它登录您的群集。 要做到这一点,首先需要获取各自的访问令牌并将其存储在环境变量中。 每个服务帐户都有一个访问令牌,Kubernetes将其存储为秘密 。
您可以使用以下命令检索此秘密:
TOKEN=$(kubectl get secret $(kubectl get secret | grep cicd-token | awk '{print $1}') -o jsonpath='{.data.token}' | base64 --decode)
关于此命令正在做什么的一些解释:
$(kubectl get secret | grep cicd-token | awk '{print $1}')
这用于检索与我们的cicd
服务帐户相关的秘密名称。 kubectl get secret
返回默认命名空间上的秘密列表,然后使用grep
搜索与您的cicd
服务帐户相关的行。 然后返回名称,因为它是从grep
返回的单行上的第一个东西。
kubectl get secret preceding-command -o jsonpath='{.data.token}' | base64 --decode
这将仅检索您的服务帐户令牌的密码。 然后使用jsonpath
访问令牌字段,并将结果传递给base64 --decode
。 这是必要的,因为令牌存储为Base64字符串。 令牌本身是一个JSON Web令牌 。
您现在可以尝试使用cicd
服务帐户检索您的cicd
。 运行以下命令,将server-from-kubeconfig-file
替换为server-from-kubeconfig-file
in ~kube/config
后可以找到的服务器URL。 此命令将提供您将在本教程后面学习的特定错误:
kubectl --insecure-skip-tls-verify --kubeconfig="/dev/null" --server=server-from-kubeconfig-file --token=$TOKEN get pods
--insecure-skip-tls-verify
跳过验证服务器证书的步骤,因为您只是在测试而不需要验证这一点。 --kubeconfig="/dev/null"
是为了确保kubectl
不会读取您的配置文件和凭据,而是使用提供的令牌。
输出应该类似于:
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:cicd" cannot list resource "pods" in API group "" in the namespace "default"
这是一个错误,但它向我们显示令牌有效。 您收到的错误是您的服务帐户没有列出资源secrets
的必要授权,但您可以访问服务器本身。 如果您的令牌无效,则错误可能是以下错误:
error: You must be logged in to the server (Unauthorized)
既然身份验证成功了,下一步就是修复服务帐户的授权错误。 您将通过创建具有必要权限的角色并将其绑定到您的服务帐户来完成此操作。
第4步 – 创建角色和角色绑定
Kubernetes有两种定义角色的方法:使用Role
或ClusterRole
资源。 前者和后者之间的区别在于,第一个适用于单个命名空间,而另一个适用于整个群集。
在本教程中使用单个命名空间时,您将使用Role
。
创建文件~/kube-general/cicd-role.yml
并使用您喜欢的文本编辑器打开它:
nano ~/kube-general/cicd-role.yml
基本思想是授予访问权限以执行与default
命名空间中的大多数Kubernetes资源相关的所有操作。 您的Role
将如下所示:
kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: cicd namespace: defaultrules: - apiGroups: ["", "apps", "batch", "extensions"] resources: ["deployments", "services", "replicasets", "pods", "jobs", "cronjobs"] verbs: ["*"]
这个YAML与你之前创建的有一些相似之处,但是你在这里说这个资源是一个Role
,它来自Kubernetes API rbac.authorization.k8s.io/v1
。 您正在命名您的角色cicd
,并在您创建ServiceAccount
的同一名称空间(即default
名称)上创建它。
然后,您有rules
字段,该字段是此角色可以访问的资源列表。 在Kubernetes中,资源是根据它们所属的API组,资源种类本身以及您可以在其上执行的操作(由动词表示)来定义的。 这些动词类似于HTTP动词 。
在我们的例子中,您说允许您的Role
在以下资源上执行所有操作*
: deployments
, services
, replicasets
, pods
, jobs
和cronjobs
。 这也适用于属于以下API组的那些资源: ""
(空字符串), apps
, batch
和extensions
。 空字符串表示根API组。 如果在创建资源时使用apiVersion: v1
,则表示此资源是此API组的一部分。
Role
本身什么都不做; 您还必须创建一个RoleBinding
,它将Role
绑定到某个东西,在本例中为ServiceAccount
。
创建文件~/kube-general/cicd-role-binding.yml
并打开它:
nano ~/kube-general/cicd-role-binding.yml
将以下行添加到文件中:
kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: cicd namespace: defaultsubjects: - kind: ServiceAccount name: cicd namespace: defaultroleRef: kind: Role name: cicd apiGroup: rbac.authorization.k8s.io
您的RoleBinding
包含一些本教程尚未涉及的特定字段。 roleRef
是您要绑定到某个Role
; 在这种情况下,它是您之前创建的cicd
角色。 subjects
是您绑定角色的资源列表; 在这种情况下,它是一个名为cicd
ServiceAccount
。
注意:如果您使用了ClusterRole
,则必须创建ClusterRoleBinding
而不是RoleBinding
。 该文件几乎相同。 唯一的区别是它在metadata
没有namespace
字段。
创建这些文件后,您将能够再次使用kubectl apply
。 通过运行以下命令在Kubernetes集群上创建这些新资源:
kubectl apply -f ~/kube-general/
您将收到类似于以下内容的输出:
rolebinding.rbac.authorization.k8s.io/cicd createdrole.rbac.authorization.k8s.io/cicd createdserviceaccount/cicd created
现在,尝试先前运行的命令:
kubectl --insecure-skip-tls-verify --kubeconfig="/dev/null" --server=server-from-kubeconfig-file --token=$TOKEN get pods
由于您没有pod,因此将产生以下输出:
No resources found.
在此步骤中,您将为要在CircleCI上使用的服务帐户提供必要的授权,以便对您的群集执行有意义的操作,例如列出,创建和更新资源。 现在是时候创建示例应用程序了。
第5步 – 创建示例应用程序
注意:从现在开始创建的所有命令和文件都将从您之前创建的文件夹~/do-sample-app
。 这是因为您现在正在创建特定于要部署到群集的示例应用程序的文件。
您要创建的Kubernetes Deployment
将使用Nginx映像作为基础,您的应用程序将是一个简单的静态HTML页面。 这是一个很好的开始,因为它允许您通过直接从Nginx提供简单的HTML来测试您的部署是否有效。 正如您稍后将看到的,您可以将所有流量转发到本地address:port
到群集上的部署以测试它是否正常工作。
在您之前设置的存储库中,创建一个新的Dockerfile
文件并使用您选择的文本编辑器打开它:
nano ~/do-sample-app/Dockerfile
在上面写下:
FROM nginx:1.14COPY index.html /usr/share/nginx/html/index.html
这将告诉Docker从nginx
映像构建应用程序容器。
现在创建一个新的index.html
文件并打开它:
nano ~/do-sample-app/index.html
编写以下HTML内容:
<!DOCTYPE html><title>DigitalOcean</title><body> Kubernetes Sample Application</body>
此HTML将显示一条简单的消息,告诉您应用程序是否正常工作。
您可以通过构建然后运行它来测试图像是否正确。
首先,使用以下命令构建映像,将dockerhub-username
替换为您自己的Docker Hub用户名。 您必须在此处指定您的用户名,以便稍后将其推送到Docker Hub时它将起作用:
docker build ~/do-sample-app/ -t dockerhub-username/do-kubernetes-sample-app
现在运行图像。 使用以下命令启动映像并将端口8080
上的任何本地流量转发到映像内的端口80
,默认情况下Nginx监听端口:
docker run --rm -it -p 8080:80 dockerhub-username/do-kubernetes-sample-app
在命令运行时,命令提示符将停止交互。 相反,您将看到Nginx访问日志。 如果您在任何浏览器上打开localhost:8080
,它应该显示一个内容为~/do-sample-app/index.html
的HTML页面。 如果您没有可用的浏览器,可以打开一个新的终端窗口并使用以下curl
命令从网页中获取HTML:
curl localhost:8080
您将收到以下输出:
<!DOCTYPE html><title>DigitalOcean</title><body> Kubernetes Sample Application</body>
停止容器(运行它的终端上的CTRL
+ C
),并将此映像提交到Docker Hub帐户。 要执行此操作,请首先登录Docker Hub:
docker login
填写有关Docker Hub帐户的必要信息,然后使用以下命令推送映像(不要忘记将dockerhub-username
替换为您自己dockerhub-username
):
docker push dockerhub-username/do-kubernetes-sample-app
您现在已将示例应用程序映像推送到Docker Hub帐户。 在下一步中,您将从此映像在DOKS群集上创建部署。
第6步 – 创建Kubernetes部署和服务
创建并运行Docker镜像后,您现在将创建一个清单,告知Kubernetes如何在群集上创建部署 。
创建YAML部署文件~/do-sample-app/kube/do-sample-deployment.yml
并使用文本编辑器打开它:
nano ~/do-sample-app/kube/do-sample-deployment.yml
在文件上写下以下内容,确保将dockerhub-username
替换dockerhub-username
Docker Hub用户名:
apiVersion: apps/v1kind: Deploymentmetadata: name: do-kubernetes-sample-app namespace: default labels: app: do-kubernetes-sample-appspec: replicas: 1 selector: matchLabels: app: do-kubernetes-sample-app template: metadata: labels: app: do-kubernetes-sample-app spec: containers: - name: do-kubernetes-sample-app image: dockerhub-username/do-kubernetes-sample-app:latest ports: - containerPort: 80 name: http
Kubernetes部署来自API组apps
,因此清单的apiVersion
设置为apps/v1
。 在metadata
您添加了一个之前未使用过的新字段,称为metadata.labels
。 这对于组织部署非常有用。 字段spec
表示部署的行为规范。 部署负责管理一个或多个pod; 在这种情况下, spec.replicas
字段将有一个副本。 也就是说,它将创建和管理单个pod。
要管理容器,您的部署必须知道它负责哪些容器。 spec.selector
字段是spec.selector
提供该信息的字段。 在这种情况下,部署将负责所有标签app=do-kubernetes-sample-app
。 spec.template
字段包含此部署将创建的Pod
的详细信息。 在模板内部,您还有一个spec.template.metadata
字段。 此字段内的labels
必须与spec.selector
使用的labels
匹配。 spec.template.spec
是pod本身的规范。 在这种情况下,它包含一个名为do-kubernetes-sample-app
容器。 该容器的图像是您之前构建的图像并推送到Docker Hub。
这个YAML文件还告诉Kubernetes这个容器暴露了端口80
,并给这个端口命名为http
。
要访问Deployment
公开的端口,请创建服务。 创建一个名为~/do-sample-app/kube/do-sample-service.yml
,并使用您喜欢的编辑器打开它:
nano ~/do-sample-app/kube/do-sample-service.yml
接下来,将以下行添加到文件中:
apiVersion: v1kind: Servicemetadata: name: do-kubernetes-sample-app namespace: default labels: app: do-kubernetes-sample-appspec: type: ClusterIP ports: - port: 80 targetPort: http name: http selector: app: do-kubernetes-sample-app
此文件为您的Service
提供与部署中使用的标签相同的标签。 这不是必需的,但它有助于在Kubernetes上组织您的应用程序。
服务资源还具有spec
字段。 spec.type
字段负责服务的行为。 在这种情况下,它是一个ClusterIP
,这意味着该服务在集群内部IP上公开,并且只能从集群内部访问。 这是服务的默认spec.type
。 spec.selector
是选择要由此服务公开的pod时应使用的标签选择器条件。 由于你的pod有标签app: do-kubernetes-sample-app
,你在这里使用它。 spec.ports
是您要从此服务公开的pod容器所公开的端口。 您的pod有一个容器,它暴露了名为http
端口80
,因此您在此处使用它作为targetPort
。 该服务也使用相同的名称在端口80
上公开该端口,但您可以使用与容器中的端口/名称组合不同的端口/名称组合。
创建Service
和Deployment
清单文件后,您现在可以使用kubectl
在Kubernetes集群上创建这些资源:
kubectl apply -f ~/do-sample-app/kube/
您将收到以下输出:
deployment.apps/do-kubernetes-sample-app createdservice/do-kubernetes-sample-app created
通过将计算机上的一个端口转发到服务在Kubernetes集群中公开的端口来测试它是否正常工作。 你可以使用kubectl port-forward
做到这kubectl port-forward
:
kubectl port-forward $(kubectl get pod --selector="app=do-kubernetes-sample-app" --output jsonpath='{.items[0].metadata.name}') 8080:80
subshell命令$(kubectl get pod --selector="app=do-kubernetes-sample-app" --output jsonpath='{.items[0].metadata.name}')
检索$(kubectl get pod --selector="app=do-kubernetes-sample-app" --output jsonpath='{.items[0].metadata.name}')
匹配的pod的名称你用的标签。 否则,您可以使用kubectl get pods
列表中检索它。
在运行port-forward
,shell将停止交互,并将输出重定向到您的集群的请求:
Forwarding from 127.0.0.1:8080 -> 80Forwarding from [::1]:8080 -> 80
在任何浏览器上打开localhost:8080
应该呈现您在本地运行容器时看到的相同页面,但它现在来自您的Kubernetes集群! 和以前一样,您也可以在新的终端窗口中使用curl
来检查它是否正常工作:
curl localhost:8080
您将收到以下输出:
<!DOCTYPE html><title>DigitalOcean</title><body> Kubernetes Sample Application</body>
接下来,是时候将您创建的所有文件推送到GitHub存储库。 为此,您必须首先在GitHub上创建一个名为digital-ocean-kubernetes-deploy
的存储库 。
为了使此存储库简单以用于演示目的,请不要在GitHub UI上询问时使用README
, license
或.gitignore
文件初始化新存储库。 您可以稍后添加这些文件。
创建存储库后,将本地存储库指向GitHub上的存储库。 要执行此操作,请按CTRL
+ C
以停止kubectl port-forward
并返回命令行,然后运行以下命令以添加名为origin
的新远程:
cd ~/do-sample-app/git remote add origin https://github.com/your-github-account-username/digital-ocean-kubernetes-deploy.git
上一个命令不应该有输出。
接下来,将您创建的所有文件提交到GitHub存储库。 首先,添加文件:
git add --all
接下来,使用引号中的提交消息将文件提交到存储库:
git commit -m "initial commit"
这将产生类似于以下的输出:
[master (root-commit) db321ad] initial commit 4 files changed, 47 insertions(+) create mode 100644 Dockerfile create mode 100644 index.html create mode 100644 kube/do-sample-deployment.yml create mode 100644 kube/do-sample-service.yml
最后,将文件推送到GitHub:
git push -u origin master
系统将提示您输入用户名和密码。 输入之后,您将看到如下输出:
Counting objects: 7, done.Delta compression using up to 8 threads.Compressing objects: 100% (7/7), done.Writing objects: 100% (7/7), 907 bytes | 0 bytes/s, done.Total 7 (delta 0), reused 0 (delta 0)To github.com:your-github-account-username/digital-ocean-kubernetes-deploy.git * [new branch] master -> masterBranch master set up to track remote branch master from origin.
如果您转到GitHub存储库页面,您现在将看到所有文件。 通过GitHub上的项目,您现在可以将CircleCI设置为CI / CD工具。
第7步 – 配置CircleCI
对于本教程,您将使用CircleCI在代码更新时自动部署应用程序,因此您需要使用GitHub帐户登录CircleCI并设置存储库。
首先,访问他们的主页https://circleci.com
,然后按“ 注册” 。
您正在使用GitHub,因此请单击绿色注册GitHub按钮。
CircleCI将重定向到GitHub上的授权页面。 CircleCI需要您的帐户的一些权限才能开始构建您的项目。 这允许CircleCI获取您的电子邮件,部署密钥和在您的存储库上创建挂钩的权限,并将SSH密钥添加到您的帐户。 如果您需要有关CircleCI将如何处理数据的更多信息,请查看有关GitHub集成的文档 。
授权CircleCI后,您将被重定向到他们的仪表板。
接下来,在CircleCI中设置GitHub存储库。 单击CircleCI仪表板中的设置新项目 ,或作为快捷方式,打开以下链接,使用您自己的GitHub用户名更改突出显示的文本: https://circleci.com/setup-project/gh/ your-github-username /digital-ocean-kubernetes-deploy
: https://circleci.com/setup-project/gh/ your-github-username /digital-ocean-kubernetes-deploy
。
之后按Start Building 。 暂时不要在存储库中创建配置文件,如果第一次构建失败,请不要担心。
接下来,在CircleCI设置中指定一些环境变量。 您可以通过单击页面右上角的cog图标的小按钮然后选择Environment Variables来查找项目的设置,或者您可以使用以下URL直接转到环境变量页面(记得填写)在您的用户名中): https://circleci.com/gh/ your-github-username /digital-ocean-kubernetes-deploy/edit#env-vars
: https://circleci.com/gh/ your-github-username /digital-ocean-kubernetes-deploy/edit#env-vars
。 按添加变量以创建新的环境变量。
首先,添加两个名为DOCKERHUB_USERNAME
和DOCKERHUB_PASS
环境变量, DOCKERHUB_PASS
将需要将其推送到Docker Hub。 分别将值设置为Docker Hub用户名和密码。
然后再添加三个: KUBERNETES_TOKEN
, KUBERNETES_SERVER
和KUBERNETES_CLUSTER_CERTIFICATE
。
KUBERNETES_TOKEN
的值将是您之前使用服务帐户用户在Kubernetes集群上进行身份验证时使用的本地环境变量的值。 如果已关闭终端,则可以始终运行以下命令以再次检索它:
kubectl get secret $(kubectl get secret | grep cicd-token | awk '{print $1}') -o jsonpath='{.data.token}' | base64 --decode
当您使用cicd
服务帐户登录时, KUBERNETES_SERVER
将作为--server
标志传递给kubectl
的字符串。 您可以在server:
之后找到此文件server:
在~/.kube/config
文件中,或者在您对Kubernetes集群进行初始设置时从DigitalOcean仪表板下载的文件kubernetes-deployment-tutorial-kubeconfig.yaml
。
您的~/.kube/config
文件中也应该有~/.kube/config
。 它是与clusters
相关的clusters
项上的certificate-authority-data
字段。 它应该是一个长串; 确保复制所有内容。
这里必须定义这些环境变量,因为它们中的大多数都包含敏感信息,将它们直接放在CircleCI YAML配置文件上是不安全的。
随着CircleCI监听存储库的更改以及配置的环境变量,是时候创建配置文件了。
在示例应用程序存储库中.circleci
一个名为.circleci
的目录:
mkdir ~/do-sample-app/.circleci/
在此目录中,创建一个名为config.yml
的文件,并使用您喜欢的编辑器打开它:
nano ~/do-sample-app/.circleci/config.yml
将以下内容添加到文件中,确保将dockerhub-username
替换为您的Docker Hub用户名:
version: 2.1jobs: build: docker: - image: circleci/buildpack-deps:stretch environment: IMAGE_NAME: dockerhub-username/do-kubernetes-sample-app working_directory: ~/app steps: - checkout - setup_remote_docker - run: name: Build Docker image command: | docker build -t $IMAGE_NAME:latest . - run: name: Push Docker Image command: | echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin docker push $IMAGE_NAME:latestworkflows: version: 2 build-master: jobs: - build: filters: branches: only: master
这将使用单个作业(称为build
设置工作流,该作业针对master
分支的每次提交运行。 这个工作是使用图像circleci/buildpack-deps:stretch
来运行它的步骤,这是来自CircleCI的图像,基于官方buildpack-deps
Docker镜像,但安装了一些额外的工具,比如Docker二进制文件本身。
工作流程有四个步骤:
-
checkout
从GitHub检索代码。 -
setup_remote_docker
为每个构建设置一个远程隔离环境。 在作业步骤中使用任何docker
命令之前,这是必需的。 这是必要的,因为当步骤在setup_remote_docker
镜像内运行时,setup_remote_docker
分配另一台机器来运行那里的命令。 - 第一个
run
步骤构建图像,就像您之前在本地执行的那样。 为此,您正在使用在环境中声明的环境变量environment:
,IMAGE_NAME
(请记住使用您自己的信息更改突出显示的部分)。 - 最后一个
run
步骤使用您在项目设置上配置的环境变量进行身份验证,将映像推送到Dockerhub。
将新文件提交到存储库并将更改推送到上游:
cd ~/do-sample-app/git add .circleci/git commit -m "add CircleCI config"git push
这将触发CircleCI的新构建。 CircleCI工作流程将正确构建并将您的图像推送到Docker Hub。
现在您已经创建并测试了CircleCI工作流,您可以将DOKS集群设置为从Docker Hub检索最新映像,并在进行更改时自动部署它。
第8步 – 更新Kubernetes群集上的部署
现在,每当您将更改推送到GitHub上的master
分支时,您的应用程序映像正在构建并发送到Docker Hub,现在是时候更新Kubernetes集群上的部署,以便它检索新映像并将其用作部署的基础。
To do that, first fix one issue with your deployment: it’s currently depending on an image with the latest
tag. This tag does not tell us which version of the image you are using. You cannot easily lock your deployment to that tag because it’s overwritten everytime you push a new image to Docker Hub, and by using it like that you lose one of the best things about having containerized applications: Reproducibility.
You can read more about that on this article about why depending on Docker latest tag is a anti-pattern .
To correct this, you first must make some changes to your Push Docker Image
build step in the ~/do-sample-app/.circleci/config.yml
file. Open up the file:
nano ~/do-sample-app/.circleci/config.yml
Then add the highlighted lines to your Push Docker Image
step:
... - run: name: Push Docker Image command: | echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin docker tag $IMAGE_NAME:latest $IMAGE_NAME:$CIRCLE_SHA1 docker push $IMAGE_NAME:latest docker push $IMAGE_NAME:$CIRCLE_SHA1...
保存并退出该文件。
CircleCI has some special environment variables set by default. One of them is CIRCLE_SHA1
, which contains the hash of the commit it’s building. The changes you made to ~/do-sample-app/.circleci/config.yml
will use this environment variable to tag your image with the commit it was built from, always tagging the most recent build with the latest tag. That way, you always have specific images available, without overwriting them when you push something new to your repository.
Next, change your deployment manifest file to point to that file. This would be simple if inside ~/do-sample-app/kube/do-sample-deployment.yml
you could set your image as dockerhub-username /do-kubernetes-sample-app:$COMMIT_SHA1
, but kubectl
doesn’t do variable substitution inside the manifests when you use kubectl apply
. To account for this, you can use envsubst
. envsubst
is a cli tool, part of the GNU gettext project. It allows you to pass some text to it, and if it finds any variable inside the text that has a matching environment variable, it’s replaced by the respective value. The resulting text is then returned as their output.
To use this, you will create a simple bash script which will be responsible for your deployment. Make a new folder called scripts
inside ~/do-sample-app/
:
mkdir ~/do-sample-app/scripts/
Inside that folder create a new bash script called ci-deploy.sh
and open it with your favorite text editor:
nano ~/do-sample-app/scripts/ci-deploy.sh
Inside it write the following bash script:
#! /bin/bash# exit script when any command ran here returns with non-zero exit codeset -eCOMMIT_SHA1=$CIRCLE_SHA1# We must export it so it's available for envsubstexport COMMIT_SHA1=$COMMIT_SHA1# since the only way for envsubst to work on files is using input/output redirection,# it's not possible to do in-place substitution, so we need to save the output to another file# and overwrite the original with that one.envsubst <./kube/do-sample-deployment.yml >./kube/do-sample-deployment.yml.outmv ./kube/do-sample-deployment.yml.out ./kube/do-sample-deployment.ymlecho "$KUBERNETES_CLUSTER_CERTIFICATE" | base64 --decode > cert.crt./kubectl \ --kubeconfig=/dev/null \ --server=$KUBERNETES_SERVER \ --certificate-authority=cert.crt \ --token=$KUBERNETES_TOKEN \ apply -f ./kube/
Let’s go through this script, using the comments in the file. First, there is the following:
set -e
This line makes sure any failed command stops the execution of the bash script. That way if one command fails, the next ones are not executed.
COMMIT_SHA1=$CIRCLE_SHA1export COMMIT_SHA1=$COMMIT_SHA1
These lines export the CircleCI $CIRCLE_SHA1
environment variable with a new name. If you had just declared the variable without exporting it using export
, it would not be visible for the envsubst
command.
envsubst <./kube/do-sample-deployment.yml >./kube/do-sample-deployment.yml.outmv ./kube/do-sample-deployment.yml.out ./kube/do-sample-deployment.yml
envsubst
cannot do in-place substitution. That is, it cannot read the content of a file, replace the variables with their respective values, and write the output back to the same file. Therefore, you will redirect the output to another file and then overwrite the original file with the new one.
echo "$KUBERNETES_CLUSTER_CERTIFICATE" | base64 --decode > cert.crt
The environment variable $KUBERNETES_CLUSTER_CERTIFICATE
you created earlier on CircleCI’s project settings is in reality a Base64 encoded string. To use it with kubectl
you must decode its contents and save it to a file. In this case you are saving it to a file named cert.crt
inside the current working directory.
./kubectl \ --kubeconfig=/dev/null \ --server=$KUBERNETES_SERVER \ --certificate-authority=cert.crt \ --token=$KUBERNETES_TOKEN \ apply -f ./kube/
Finally, you are running kubectl
. The command has similar arguments to the one you ran when you were testing your Service Account. You are calling apply -f ./kube/
, since on CircleCI the current working directory is the root folder of your project. ./kube/
here is your ~/do-sample-app/kube
folder.
Save the file and make sure it’s executable:
chmod +x ~/do-sample-app/scripts/ci-deploy.sh
Now, edit ~/do-sample-app/kube/do-sample-deployment.yml
:
nano ~/do-sample-app/kube/do-sample-deployment.yml
Change the tag of the container image value to look like the following one:
# ... containers: - name: do-kubernetes-sample-app image: dockerhub-username/do-kubernetes-sample-app:$COMMIT_SHA1 ports: - containerPort: 80 name: http
保存并关闭文件。 You must now add some new steps to your CI configuration file to update the deployment on Kubernetes.
Open ~/do-sample-app/.circleci/config.yml
on your favorite text editor:
nano ~/do-sample-app/.circleci/config.yml
Write the following new steps, right below the Push Docker Image
one you had before:
... - run: name: Install envsubst command: | sudo apt-get update && sudo apt-get -y install gettext-base - run: name: Install kubectl command: | 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 u+x ./kubectl - run: name: Deploy Code command: ./scripts/ci-deploy.sh
The first two steps are installing some dependencies, first envsubst
, and then kubectl
. The Deploy Code
step is responsible for running our deploy script.
To make sure the changes are really going to be reflected on your Kubernetes deployment, edit your index.html
. Change the HTML to something else, like:
<!DOCTYPE html><title>DigitalOcean</title><body> Automatic Deployment is Working!</body>
Once you have saved the above change, commit all the modified files to the repository, and push the changes upstream:
cd ~/do-sample-app/git add --allgit commit -m "add deploy script and add new steps to circleci config"git push
You will see the new build running on CircleCI, and successfully deploying the changes to your Kubernetes cluster.
Wait for the build to finish, then run the same command you ran previously:
kubectl port-forward $(kubectl get pod --selector="app=do-kubernetes-sample-app" --output jsonpath='{.items[0].metadata.name}') 8080:80
Make sure everything is working by opening your browser on the URL localhost:8080
or by making a curl
request to it. It should show the updated HTML:
curl localhost:8080
您将收到以下输出:
<!DOCTYPE html><title>DigitalOcean</title><body> Automatic Deployment is Working!</body>
Congratulations, you have set up automated deployment with CircleCI!
结论
This was a basic tutorial on how to do deployments to DigitalOcean Kubernetes using CircleCI. From here, you can improve your pipeline in many ways. The first thing you can do is create a single build
job for multiple deployments, each one deploying to different Kubernetes clusters or different namespaces. This can be extremely useful when you have different Git branches for development/staging/production environments, ensuring that the deployments are always separated.
You could also build your own image to be used on CircleCI, instead of using buildpack-deps
. This image could be based on it, but could already have kubectl
and envsubst
dependencies installed.
If you would like to learn more about CI/CD on Kubernetes, check out the tutorials for our CI/CD on Kubernetes Webinar Series , or for more information about apps on Kubernetes, see Modernizing Applications for Kubernetes .