开工福利|免费学 2200+ 精品线上课,企业成员人人可得! 了解详情
写点什么

Step by Step!Kubernetes 持续部署指南

  • 2020-05-18
  • 本文字数:6936 字

    阅读完需:约 23 分钟

Step by Step!Kubernetes持续部署指南

在很久很久以前的一份工作中,我的任务是将老式的 LAMP 堆栈切换到 Kubernetes 上。那会儿我的老板总是追逐新技术,认为只需要几天时间就能够完成新旧技术的迭代——鉴于那时我们甚至对容器的工作机制一无所知,所以不得不说老板的想法真的很大胆。


在阅读了官方文档并且搜索了很多信息之后,我们开始感到不知所措——有许多新的概念需要学习:pod、容器以及 replica 等。对我而言,Kubernetes 似乎只是为一群聪明的开发者而设计的。


然后我做了我在这一状况下常做的事:通过实践来学习。通过一个简单的例子可以很好地理解错综复杂的问题,所以我自己一步一步完成了整个部署过程。


最后,我们做到了,虽然远未达到规定的一周时间——我们花了将近一个月的时间来创建三个集群,包括它们的开发、测试和生产。


本文我将详细介绍如何将应用程序部署到 Kubernetes。阅读完本文之后,你将拥有一个高效的 Kubernetes 部署和持续交付工作流程。

持续集成与交付

持续集成是在每次应用程序更新时构建和测试的实践。通过以少量的工作,更早地检测到错误并立即解决。


集成完成并且所有测试都通过之后,我们就能够添加持续交付到自动化发布和部署的流程中。使用 CI/CD 的项目可以更频繁、更可靠地发布。


我们将使用 Semaphore,这是一个快速、强大且易用地持续集成和交付(CI/CD)平台,它能够自动执行所有流程:


1、 安装项目依赖项


2、 运行单元测试


3、 构建一个 Docker 镜像


4、 Push 镜像到 Docker Hub


5、 一键 Kubernetes 部署


对于应用程序,我们有一个 Ruby Sinatra 微服务,它暴露一些 HTTP 端点。该项目已包含部署所需的所有内容,但仍需要一些组件。

准备工作

在开始操作之前,你需要登录 Github 和 Semaphore 账号。此外,为后续方便拉取或 push Docker 镜像,你需要登录 Docker Hub。


接下来,你需要在计算机上安装一些工具:


  • Git:处理代码

  • curl:网络的“瑞士军刀”

  • kubectl:远程控制你的集群


当然,千万不要忘了 Kubernetes。大部分的云供应商都以各种形式提供此服务,选择适合你的需求的即可。最低端的机器配置和集群大小足以运行我们示例的 app。我喜欢从 3 个节点的集群开始,但你可以只用 1 个节点的集群。


集群准备好之后,从你的供应商中下载 kubeconfig 文件。有些允许你直接从其 web 控制台下载,有些则需要帮助程序。我们需要此文件才能连接到集群。


有了这个,我们已经可以开始了。首先要做的是 fork 存储库。

Fork 存储库

在这篇文章中 fork 我们将使用的演示应用程序。


  1. 访问 semaphore-demo-ruby-kubernetes 存储库,并且点击右上方的 Fork 按钮

  2. 点击 Clone or download 按钮并且复制地址

  3. 复制存储库:


$ git clone https://github.com/your_repository_path…
复制代码


使用 Semaphore 连接新的存储库


1、 登录到你的 Semaphore


2、 点击侧边栏的链接,创建一个新项目


3、 点击你的存储库旁【 Add Repository 】按钮

使用 Semaphore 测试

持续集成让测试变得有趣并且高效。一个完善的 CI 流水线能够创建一个快速反馈回路以在造成任何损失之前发现错误。我们的项目附带一些现成的测试。


打开位于.semaphore/semaphore.yml 的初始流水线文件,并快速查看。这个流水线描述了 Semaphore 构建和测试应用程序所应遵循的所有步骤。它从版本和名称开始。


version: v1.0name: 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
复制代码


工作流程继续执行下一个流水线。

构建 Docker 镜像

我们可以在 Kubernetes 上运行任何东西,只要它打包在 Docker 镜像中。在这一部分,我们将学习如何构建镜像。


我们的 Docker 镜像将包含应用程序的代码、Ruby 以及所有的库。让我们先来看一下 Dockerfile:


FROM ruby:2.5 RUN apt-get update -qq && apt-get install -y build-essential ENV APP_HOME /appRUN mkdir $APP_HOMEWORKDIR $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 就像一个详细的菜谱,包含所有构建容器镜像所需要的步骤和命令:


1、 从预构建的 ruby 镜像开始


2、 使用 apt-get 安装构建工具


3、 复制 Gemfile,因为它具有所有的依赖项


4、 用 bundle 安装它们


5、 复制 app 的源代码


6、 定义监听端口和启动命令


我们将在 Semaphore 环境中 bake 我们的生产镜像。然而,如果你想要在计算机上进行一个快速的测试,那么请输入:


$ docker build . -t test-image
复制代码


使用 Docker 运行和暴露内部端口 4567 以在本地启动服务器:


$ docker run -p 4567:4567 test-image
复制代码


你现在可以测试一个可用的 HTTP 端点:


$ curl -w "\n" localhost:4567hello world :))
复制代码

添加 Docker Hub 账户到 Semaphore

Semaphore 有一个安全的机制以存储敏感信息,如密码、令牌或密钥等。为了能够 push 镜像到你的 Docker Hub 镜像仓库中,你需要使用你的用户名和密码来创建一个 Secret:


  1. 打开你的 Semaphore

  2. 在左侧导航栏中,点击【 Secret

  3. 点击【 Creat New Secret

  4. Secret 的名字应该是 Dockerhub,键入登录信息(如下图所示),并保存。


构建 Docker 流水线

这个流水线开始构建并且 push 镜像到 Docker Hub,它仅仅有 1 个 block 和 1 个 job:



这次,我们需要使用更好的性能,因为 Docker 往往更加耗费资源。我们选择具有四个 CPU,8GB RAM 和 35GB 磁盘空间的中端机器 e1-standard-4:


version: v1.0name: Docker buildagent:  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

自动部署是 Kubernetes 的强项。我们所需要做的就是告诉集群我们最终的期望状态,剩下的将由它来负责。


然而,在部署之前,你必须将 kubeconfig 文件上传到 Semaphore。

上传 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/v1kind: Deploymentmetadata:  name: semaphore-demo-ruby-kubernetesspec:  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: v1kind: Servicemetadata:  name: semaphore-demo-ruby-kubernetes-lbspec:  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.0name: Deploy to Kubernetesagent:  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 deploymentsNAME                             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGEsemaphore-demo-ruby-kubernetes   3         0         0            0           15m
复制代码


几秒之后,pod 已经启动,reconciliation 已经完成:


$ kubectl get deploymentsNAME                             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGEsemaphore-demo-ruby-kubernetes   3         3         3            3           15m
复制代码


使用 get all 获得集群的通用状态,它显示了 pod、服务、部署以及 replica:


$ kubectl get allNAME                                                  READY   STATUS    RESTARTS   AGEpod/semaphore-demo-ruby-kubernetes-7d985f8b7c-454dh   1/1     Running   0          2mpod/semaphore-demo-ruby-kubernetes-7d985f8b7c-4pdqp   1/1     Running   0          119spod/semaphore-demo-ruby-kubernetes-7d985f8b7c-9wsgk   1/1     Running   0          2m34s  NAME                                        TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)        AGEservice/kubernetes                          ClusterIP      10.12.0.1              443/TCP        24mservice/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   AGEdeployment.apps/semaphore-demo-ruby-kubernetes   3         3         3            3           17m NAME                                                        DESIRED   CURRENT   READY   AGEreplicaset.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 集群

  • 构建一个部署容器并且在里面运行测试

  • 使用更多微服务扩展项目


2020-05-18 17:59729

评论

发布
暂无评论
发现更多内容

软件架构思考

轻口味

android 架构设计原则 10月月更

引航计划|大前端|优质合集手把手带你玩转移动开发

三掌柜

大前端 引航计划 引航

用时三个月,阿里P8大佬总结出来的最深《Java性能优化全攻略》限时分享!

Java 架构 面试 程序人生 编程语言

偷瞄大佬书签栏,16个C++必备学习网站,建议收藏

奔着腾讯去

c++

重置不良的编程陋习

devpoint

编码习惯 10月月更

还在郁郁寡欢?啃完666页Java面试高频宝典,十月保你定级腾讯T3-2

Java 架构 面试 程序人生 编程语言

自我提升:高效能人士的 7 个习惯学习笔记(二)

程序员架构进阶

效率 自我管理 自我提升 10月月更

看山聊 Java:检查日期字符串是否合法

看山

Java 10月月更

“区块链+农业”推动产业“提档升级”

CECBC

记不住源码?掌握这套方法,Alibaba必有你一席之地!

Java 架构 面试 程序人生 编程语言

认识流媒体协议,从 RTSP 协议解析开始!

声网

协议 RTSP

代码检查规则:Java语言案例详解

百度开发者中心

Java 最佳实践 案例分享 代码检查规则

内网域渗透分析(实战总结)

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 漏洞挖掘

手把手带你玩转 JS | 引航计划|大前端

三掌柜

大前端 引航计划 引航 技术专题合集

都2021年了,还在算法上折跟头?这本《字节跳动面试必问算法秘籍》送给你!

Java 架构 面试 程序人生 编程语言

如何区分封闭图形的内部和外部

Changing Lin

10月月更

看完阿里大牛的数据结构学习笔记,我直接手撕了500道Leetcode题

Java 程序员 架构 面试 算法

新一代容器平台ACK Anywhere,来了

阿里巴巴中间件

阿里云 云原生 中间件 ACK ACK Anywhere

接棒运动赛事!工厂里也有热血竞技?

白洞计划

Serverless 工程实践 | Serverless 应用优化与调试秘诀

阿里巴巴云原生

阿里云 Serverless 云原生

虚拟化管理软件比较(Eucalyptus, OpenNebula, OpenStack, OpenQRM,XenServer, Oracle VM, CloudStack,ConVirt)

hanaper

Hold the Door!区块链底层平台流控分析

趣链科技

区块链 流量控制 底层平台

机器人自主学习新进展,百度飞桨发布四足机器人控制强化学习新算法

百度大脑

人工智能 飞桨

架构实战营模块九作业 - 毕业设计

王晓宇

架构实战营

携程 x StarRocks:高效支持高并发查询,大幅降低人力和硬件成本

StarRocks

大数据 数据分析 高并发 StarRocks

中国移动5G消息开发者社区 | 主题沙龙直播预约:5G消息在文旅场景的应用探讨

5G消息

5G 5G消息 中国移动5G rcs

自动驾驶混战,剑气二宗谁能笑傲江湖?

脑极体

分享一套golang实现的 IM 系统,一键部署服务端,客户端SDK全平台支持,可以替代IM云服务

OpenIM

阿里P8最全总结PDF:Tomcat+虚拟机+Spring全家桶+MyBatis源码等

Java 架构 面试 程序人生 编程语言

MyBatis 批量插入数据的 3 种方法!

王磊

Java mybatis springboot

使用 Ansible 部署 Elasticsearch 集群

Se7en

Step by Step!Kubernetes持续部署指南_文化 & 方法_Rancher_InfoQ精选文章