本文是做者经过亲身实践,从零基础开始,一步一步总结出来的Kubernetes持续部署工做流程。文章从前期的工具准备开始,到复刻存储库、测试、构建镜像、构建流水线最后进行部署,全部的工做流程都一一展示在文章中,对于想要拥有全自动持续交付流水线的用户将有很大的借鉴意义。node
在好久好久之前的一份工做中,个人任务是将老式的LAMP堆栈切换到Kubernetes上。那会儿个人老板老是追逐新技术,认为只须要几天时间就可以完成新旧技术的迭代——鉴于那时咱们甚至对容器的工做机制一无所知,因此不得不说老板的想法真的很大胆。git
在阅读了官方文档而且搜索了不少信息以后,咱们开始感到不知所措——有许多新的概念须要学习:pod、容器以及replica等。对我而言,Kubernetes彷佛只是为一群聪明的开发者而设计的。github
而后我作了我在这一情况下常作的事:经过实践来学习。经过一个简单的例子能够很好地理解错综复杂的问题,因此我本身一步一步完成了整个部署过程。web
最后,咱们作到了,虽然远未达到规定的一周时间——咱们花了将近一个月的时间来建立三个集群,包括它们的开发、测试和生产。docker
本文我将详细介绍如何将应用程序部署到Kubernetes。阅读完本文以后,你将拥有一个高效的Kubernetes部署和持续交付工做流程。ubuntu
持续集成是在每次应用程序更新时构建和测试的实践。经过以少许的工做,更早地检测到错误并当即解决。api
集成完成而且全部测试都经过以后,咱们就可以添加持续交付到自动化发布和部署的流程中。使用CI/CD的项目能够更频繁、更可靠地发布。缓存
咱们将使用Semaphore,这是一个快速、强大且易用地持续集成和交付(CI/CD)平台,它可以自动执行全部流程:安全
一、 安装项目依赖项ruby
二、 运行单元测试
三、 构建一个Docker镜像
四、 Push镜像到Docker Hub
五、 一键Kubernetes部署
对于应用程序,咱们有一个Ruby Sinatra微服务,它暴露一些HTTP端点。该项目已包含部署所需的全部内容,但仍须要一些组件。
在开始操做以前,你须要登陆Github和Semaphore帐号。此外,为后续方便拉取或push Docker镜像,你须要登陆Docker Hub。
接下来,你须要在计算机上安装一些工具:
Git:处理代码
curl:网络的“瑞士军刀”
kubectl:远程控制你的集群
固然,千万不要忘了Kubernetes。大部分的云供应商都以各类形式提供此服务,选择适合你的需求的便可。最低端的机器配置和集群大小足以运行咱们示例的app。我喜欢从3个节点的集群开始,但你能够只用1个节点的集群。
集群准备好以后,从你的供应商中下载kubeconfig文件。有些容许你直接从其web控制台下载,有些则须要帮助程序。咱们须要此文件才能链接到集群。
有了这个,咱们已经能够开始了。首先要作的是fork存储库。
在这篇文章中fork咱们将使用的演示应用程序。
访问semaphore-demo-ruby-kubernetes存储库,而且点击右上方的Fork按钮
点击Clone or download按钮而且复制地址
复制存储库:$ git clone https://github.com/your_repository_path…
使用Semaphore链接新的存储库
一、 登陆到你的Semaphore
二、 点击侧边栏的连接,建立一个新项目
三、 点击你的存储库旁【Add Repository】按钮
持续集成让测试变得有趣而且高效。一个完善的CI 流水线可以建立一个快速反馈回路以在形成任何损失以前发现错误。咱们的项目附带一些现成的测试。
打开位于.semaphore/semaphore.yml的初始流水线文件,并快速查看。这个流水线描述了Semaphore构建和测试应用程序所应遵循的全部步骤。它从版本和名称开始。
version: v1.0 name: CI
接下来是agent,它是为job提供动力的虚拟机。咱们能够从3种类型中选择:
agent: machine: type: e1-standard-2 os_image: ubuntu1804
Block(块)、任务以及job定义了在流水线的每一个步骤中要执行的操做。在Semaphore,block按照顺序运行,与此同时,在block中的job也会并行运行。流水线包含2个block,一个是用于库安装,一个用于运行测试。
第一个block下载并安装了Ruby gems。
- name: Install dependencies task: jobs: - name: bundle install commands: - checkout - cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH,gems-master - bundle install --deployment --path .bundle - cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) .bundle
Checkout复制了Github里的代码。既然每一个job都在彻底隔离的机器里运行,那么咱们必须依赖缓存(cache)来在job运行之间存储和检索文件。
blocks: - name: Install dependencies task: jobs: - name: bundle install commands: - checkout - cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH,gems-master - bundle install --deployment --path .bundle - cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) .bundle
第二个block进行测试。请注意咱们重复使用了checkout和cache的代码以将初始文件放入job中。最后一个命令用于启动RSpec测试套件。
- name: Tests task: jobs: - name: rspec commands: - checkout - cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH,gems-master - bundle install --deployment --path .bundle - bundle exec rspec
最后一个部分咱们来看看Promotion。Promotion可以在必定条件下链接流水线以建立复杂的工做流程。全部job完成以后,咱们使用 auto_promote_on来启动下一个流水线。
promotions: - name: Dockerize pipeline_file: docker-build.yml auto_promote_on: - result: passed
工做流程继续执行下一个流水线。
咱们能够在Kubernetes上运行任何东西,只要它打包在Docker镜像中。在这一部分,咱们将学习如何构建镜像。
咱们的Docker镜像将包含应用程序的代码、Ruby以及全部的库。让咱们先来看一下Dockerfile:
FROM ruby:2.5 RUN apt-get update -qq && apt-get install -y build-essential ENV APP_HOME /app RUN mkdir $APP_HOME WORKDIR $APP_HOME ADD Gemfile* $APP_HOME/ RUN bundle install --without development test ADD . $APP_HOME EXPOSE 4567 CMD ["bundle", "exec", "rackup", "--host", "0.0.0.0", "-p", "4567"]
Dockerfile就像一个详细的菜谱,包含全部构建容器镜像所须要的步骤和命令:
一、 从预构建的ruby镜像开始
二、 使用apt-get安装构建工具
三、 复制Gemfile,由于它具备全部的依赖项
四、 用bundle安装它们
五、 复制app的源代码
六、 定义监听端口和启动命令
咱们将在Semaphore环境中bake咱们的生产镜像。然而,若是你想要在计算机上进行一个快速的测试,那么请输入:
$ docker build . -t test-image
使用Docker运行和暴露内部端口4567以在本地启动服务器:
$ docker run -p 4567:4567 test-image
你如今能够测试一个可用的HTTP端点:
$ curl -w "\n" localhost:4567 hello world :))
Semaphore有一个安全的机制以存储敏感信息,如密码、令牌或密钥等。为了可以push镜像到你的Docker Hub镜像仓库中,你须要使用你的用户名和密码来建立一个Secret:
打开你的Semaphore
在左侧导航栏中,点击【Secret】
点击【Creat New Secret】
Secret的名字应该是Dockerhub,键入登陆信息(以下图所示),并保存。
这个流水线开始构建而且push镜像到Docker Hub,它仅仅有1个block和1个job:
此次,咱们须要使用更好的性能,由于Docker每每更加耗费资源。咱们选择具备四个CPU,8GB RAM和35GB磁盘空间的中端机器e1-standard-4:
version: v1.0 name: Docker build agent: machine: type: e1-standard-4 os_image: ubuntu1804
构建block经过登陆到Docker Hub启动,用户名和密码能够从咱们刚建立的secret导入。登陆以后,Docker能够直接访问镜像仓库。
下一个命令是docker pull,它试图拉取最新镜像。若是找到镜像,那么Docker可能可以从新使用其中的一些层,以加速构建过程。若是没有最新镜像,也无需担忧,只是须要花费长一点的时间来构建。
最后,咱们push新的镜像。注意,这里咱们使用SEMAPHORE_WORKFLOW_ID 变量来标记镜像。
blocks: - name: Build task: secrets: - name: dockerhub jobs: - name: Docker build commands: - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin - checkout - docker pull "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:latest || true - docker build --cache-from "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:latest -t "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID . - docker images - docker push "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID
当镜像准备完毕,咱们进入项目的交付阶段。咱们将用手动promotion来扩展咱们的Semaphore 流水线。
promotions: - name: Deploy to Kubernetes pipeline_file: deploy-k8s.yml
要进行第一次自动构建,请进行push:
$ touch test-build $ git add test-build $ git commit -m "initial run on Semaphore“ $ git push origin master
镜像准备完成以后,咱们就能够进入部署阶段。
自动部署是Kubernetes的强项。咱们所须要作的就是告诉集群咱们最终的指望状态,剩下的将由它来负责。
然而,在部署以前,你必须将kubeconfig文件上传到Semaphore。
咱们须要第二个secret:集群的kubeconfig。这个文件授予能够对它的管理访问权限。所以,咱们不但愿将文件签入存储库。
建立一个名为do-k8s的secret而且将kubeconfig文件上传到/home/semaphore/.kube/dok8s.yaml中:
尽管Kubernetes已是容器编排平台,可是咱们不直接管理容器。实际上,部署的最小单元是pod。一个pod就好像一群如影随行的朋友,老是一块儿去同一个地方。所以要保证在pod中的容器运行在同一个节点上而且有相同的IP。它们能够同步启动和中止,而且因为它们在同一台机器上运行,所以它们能够共享资源。
pod的问题在于它们能够随时启动和中止,咱们没办法肯定它们会被分配到的pod IP。要把用户的http流量转发,还须要提供一个公共IP和一个负载均衡器,它负责跟踪pod和转发客户端的流量。
打开位于deploymente.yml的文件。这是一个部署咱们应用程序的清单,它被3个dash分离成两个资源。第一个,部署资源:
apiVersion: apps/v1 kind: Deployment metadata: name: semaphore-demo-ruby-kubernetes spec: replicas: 1 selector: matchLabels: app: semaphore-demo-ruby-kubernetes template: metadata: labels: app: semaphore-demo-ruby-kubernetes spec: containers: - name: semaphore-demo-ruby-kubernetes image: $DOCKER_USERNAME/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID
这里有几个概念须要厘清:
资源都有一个名称和几个标签,以便组织
Spec定义了最终指望的状态,template是用于建立Pod的模型。
Replica设置要建立的pod的副本数。咱们常常将其设置为集群中的节点数。既然咱们使用了3个节点,我将这一命令行更改成replicas:3
第二个资源是服务。它绑定到端口80而且将HTTP流量转发到部署中的pod:
--- apiVersion: v1 kind: Service metadata: name: semaphore-demo-ruby-kubernetes-lb spec: selector: app: semaphore-demo-ruby-kubernetes type: LoadBalancer ports: - port: 80 targetPort: 4567
Kubernetes将selector与标签相匹配以便将服务与pod链接起来。所以,咱们在同一个集群中有许多服务和部署而且根据须要链接他们。
咱们如今进入CI/CD配置的最后一个阶段。这时,咱们有一个定义在semaphore.yml的CI流水线,以及定义在docker-build.yml的Docker流水线。在这一步中,咱们将部署到Kubernetes。
打开位于.semaphore/deploy-k8s.yml的部署流水线:
version: v1.0 name: Deploy to Kubernetes agent: machine: type: e1-standard-2 os_image: ubuntu1804
两个job组成最后的流水线:
Job 1开始部署。导入kubeconfig文件以后,envsubst将deployment.yaml中的占位符变量替换为其实际值。而后,kubectl apply将清单发送到集群。
blocks: - name: Deploy to Kubernetes task: secrets: - name: do-k8s - name: dockerhub env_vars: - name: KUBECONFIG value: /home/semaphore/.kube/dok8s.yaml jobs: - name: Deploy commands: - checkout - kubectl get nodes - kubectl get pods - envsubst < deployment.yml | tee deployment.yml - kubectl apply -f deployment.yml
Job 2将镜像标记为最新,以让咱们可以在下一次运行中将其做为缓存使用。
- name: Tag latest release task: secrets: - name: dockerhub jobs: - name: docker tag latest commands: - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin - docker pull "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID - docker tag "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:$SEMAPHORE_WORKFLOW_ID "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:latest - docker push "${DOCKER_USERNAME}"/semaphore-demo-ruby-kubernetes:latest
这是工做流程的最后一步了。
让咱们教咱们的Sinatra应用程序唱歌。在app.rb中的App类中添加如下代码:
get "/sing" do "And now, the end is near And so I face the final curtain..." end
推送修改的文件到Github:
$ git add .semaphore/* $ git add deployment.yml $ git add app.rb $ git commit -m "test deployment” $ git push origin master
等到docker构建流水线完成,你能够查看Semaphore的进度:
是时候进行部署了,点击Promote按钮,看它是否工做:
咱们已经有了一个好的开始,如今就看Kubernetes的了。咱们可使用kubectl检查部署状态,初始状态是三个所需的pod而且零可用:
$ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE semaphore-demo-ruby-kubernetes 3 0 0 0 15m
几秒以后,pod已经启动,reconciliation已经完成:
$ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE semaphore-demo-ruby-kubernetes 3 3 3 3 15m
使用get all得到集群的通用状态,它显示了pod、服务、部署以及replica:
$ kubectl get all NAME READY STATUS RESTARTS AGE pod/semaphore-demo-ruby-kubernetes-7d985f8b7c-454dh 1/1 Running 0 2m pod/semaphore-demo-ruby-kubernetes-7d985f8b7c-4pdqp 1/1 Running 0 119s pod/semaphore-demo-ruby-kubernetes-7d985f8b7c-9wsgk 1/1 Running 0 2m34s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.12.0.1 443/TCP 24m service/semaphore-demo-ruby-kubernetes-lb LoadBalancer 10.12.15.50 35.232.70.45 80:31354/TCP 17m NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deployment.apps/semaphore-demo-ruby-kubernetes 3 3 3 3 17m NAME DESIRED CURRENT READY AGE replicaset.apps/semaphore-demo-ruby-kubernetes-7d985f8b7c 3 3 3 2m3
Service IP在pod以后展现。对于我来讲,负载均衡器被分配到外部IP 35.232.70.45。须要将其更改成你的提供商分配给你的那个,而后咱们来试试新的服务器。
$ curl -w "\n" http://YOUR_EXTERNAL_IP/sing
如今,离结束已经不远了。
当你使用了正确的CI/CD解决方案以后,部署到Kubernetes并非那么困难。你如今拥有一个Kubernetes的彻底自动的持续交付流水线啦。
这里有几个建议可让你在Kubernetes上随意fork并玩转semaphore-demo-ruby-kubernetes:
建立一个staging集群
构建一个部署容器而且在里面运行测试
使用更多微服务扩展项目