HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

每天部署数千个容器实例,扩缩容复杂性该如何管理?

  • 2020-12-29
  • 本文字数:6048 字

    阅读完需:约 20 分钟

每天部署数千个容器实例,扩缩容复杂性该如何管理?

无论是大型软件公司还是小型软件公司,现在每天都要部署数千个容器实例,这种扩缩容的复杂性是他们必须要管理的。本文介绍了如何将 Kubernetes 纳入到现有的传统 CI/CD 管道中,并实现服务的高可用性,以及随时在生产环境中进行代码变更。


基于容器的微服务架构改变了开发和运维团队测试和部署现代应用程序 / 服务的方式。容器通过简化应用程序的扩缩容和部署来帮助公司实现现代化,但容器也创建了一个全新的基础设施生态系统,从而引入了新的挑战和更多的复杂性。


无论是大型软件公司还是小型软件公司,现在每天都要部署数千个容器实例,这种扩缩容的复杂性是他们必须要管理的。那么他们是怎么做的呢?



秘诀就是 Kubernetes


Kubernetes 最初由 Google 开发,是一个开源的容器编排平台,旨在自动化容器化应用程序的部署、扩缩容和管理。


在本教程中,我们将介绍如何将 Kubernetes 纳入到现有的传统 CI/CD 管道中,并实现服务的高可用性,以及随时(是的,任何时候都不会影响服务)在生产环境中进行代码变更。


使用到的工具


本文是基于你对如下主题有基本 / 良好的理解的基础上的。


  • Kubernetes(我们的服务在 Kubernetes 上运行)

  • Jenkins 以及 Jenkins 共享库(CI/CD 工具)

  • GIT(SCM 工具)

  • HAProxy(网络负载均衡器)

  • Ansible(配置管理工具)


CI/CD 安装


  • Jenkins 服务器——与 Docker、Ansible 和 Kubectl 一起安装(将 Kube 管理配置.kube 从 K8s master 复制到 Jenkins 服务器的主目录中)

  • 具有一主两从三个节点的 K8s 集群

  • HAProxy 服务器


使用 K8s 的 CI/CD


GIT & Jenkins


让我们来详细介绍一下如何配置所有这些不同的工具,并使其能够完美地运行。


我们的示例应用程序,名为 shoppingapp,在 GitHub 中有三个存储库,每个库都具有特定的微服务。


  • shoppingapp-home:首页微服务

  • shoppingapp-kids :儿童版页面微服务

  • shoppingapp-mens:男士版页面微服务


类似地,针对这三个库我们有三个 Jenkins 管道作业,并在存储库中设置了 GitHub webhook,以便在有新提交时自动启动构建。



Jenkins 管道作业


我们来看下 shoppingapp-home 作业的配置。




Jenkins 管道配置


在 shoppingapp-home 的管道作业中,仓库 URL 指向 shoppingapp-home 的 GitHub 存储库。类似地,另外两个管道作业也指向各自的存储库。


下面是 shoppingapp-home 存储库中的 Jenkinsfile。


@Library('jenkins-shared-library') _pipeline {   agent any   environment {       app = 'shoppingapp'       service = 'shoppingapp-home'       registryCredential = 'dockerhub'       dockerImage = ''       imageid = "deepanmurugan/shoppingapp-home:$BUILD_NUMBER"   }   stages {       stage('Build') {            steps {        script {            dockerImage = dockerbuild(imageid)        }        }         }       stage('Test') {           steps {                        testcase()           }       }       stage('Publish') {           steps{               script {            imagepush(imageid)                   }           }       }       stage('Pull Playbook Repo') {        steps {        dir('/tmp/ansible-playbooks/') {            gitcheckout(                branch: "master",                repoUrl: "https://github.com/deepanmurugan/Ansible_Playbook.git"            )        }    }       }       stage ('Deploy') {           steps {           dir('/tmp/ansible-playbooks/') {               script{            deploytok8s(imageid,app,service)               }           }           }       }   }}
复制代码


shoppingapp-home 存储库的 Jenkinsfile 以下是管道作业中的不同步骤。作业声明了几个变量,app——指我们的应用程序名,service——指我们的服务名,registryCredentials——是我们保存在 Jenkins 中的 dockerhub 用户名和密码,imageid——带有标签的 Docker 容器镜像名,这里的标签将是 Jenkins 的内部版本号。


在上面的 Jenkinsfile 中,我使用了共享库(如果你不熟悉共享库的话,请参考 Jenkins 网站)。


在“Build”(构建)阶段,我在共享库中调用了函数dockerbuild,并将 imageid 作为参数进行传递。以下是dockerbuild函数的定义。


def call(String dockerImage) {  script {    docker.build "${dockerImage}"  }}
复制代码


Build 阶段


它仅使用我们传递的 imageid 作为参数调用 docker.build,就会触发docker build -t imageid, 并基于 Dockerfile 创建一个 Docker 镜像。我们稍后再看 Dockerfile。


下一步是“Test”(测试),此时我们可以运行测试用例。我目前不执行任何测试用例,因为这是一个非常基础的应用程序。


def call() {    sh """        echo "Testing the docker built image"    """}
复制代码


Test 阶段


接下来,我们进入“Publish”(发布)阶段,该阶段将再次调用imagepush函数,其中 imageid 作为共享库中的参数。该函数基本上要登录到 Docker Hub,并将镜像推送到 Docker Hub 中。


def call(String dockerImage) {  echo "${dockerImage}"  withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'hubUsername', passwordVariable: 'hubPassword')]) {        sh """            docker login --username="${hubUsername}" --password="${hubPassword}"            docker push "${dockerImage}"        """    }}
复制代码


Publish 阶段


下一步是“Pull Playbook Repo”,仅克隆存储库,该库中包含了我们所有的 playbook。


def call(Map stageParams){  checkout([$class: 'GitSCM', branches: [[name: stageParams.branch]], userRemoteConfigs: [[credentialsId: 'github_repo', url: stageParams.repoUrl]]])}
复制代码


Pull Playbook Repo 阶段


接下来是“Deploy”(部署)阶段,该阶段触发 playbook 将容器部署到 Kubernetes 集群中。


def call(String dockerImage, String app, String service) {    sh """        ansible-playbook deploy_k8s.yml --extra-vars \"image_id=${dockerImage} app_name=${app} service_name=${service}\"    """}
复制代码


Deploy 阶段


现在,我们对如何开发和维护每个微服务的存储库、如何配置 Jenkins 管道来创建 Docker 镜像并将镜像推送到 Docker Hub 以及触发 Ansible Playbook 将容器部署到 K8s 集群都已经相当清楚了。那我们来看一下它的 Dockerfile 是什么样的。


Dockerize 应用程序


FROM python:3.7.3-alpine3.9RUN mkdir -p /appWORKDIR /appCOPY ./src/requirements.txt /app/requirements.txtRUN pip install -r requirements.txtCOPY ./src/ /appENV FLASK_APP=server.pyCMD flask run -h 0.0.0.0 -p 5000
复制代码


Dockerfile


Dockerfile 使用 python:3.7.3-alpine3.9 作为基础镜像,我们安装了一个 flask 应用程序来打印某些文本。该应用程序运行在端口 5000 上。


使用 Ansible Playbook 来进一步接管


下面是 Ansible Playbook 的 deploy_k8s.yml,它是由 Jenkins 管道作业触发的。


- hosts: localhost  user: ubuntu  tasks:  - name: Deploy the service    k8s:      state: present      definition: "{{ lookup('template', 'k8s/{{app_name}}/{{service_name}}/deployment.yml') | from_yaml }}"      validate_certs: no      namespace: default  - name: Deploy the application    k8s:      state: present      validate_certs: no      namespace: default      definition: "{{ lookup('template', 'k8s/{{app_name}}/{{service_name}}/service.yml') | from_yaml }}"  - name: Deploy the Ingress    k8s:      state: present      validate_certs: no      namespace: default      definition: "{{ lookup('template', 'k8s/{{app_name}}/common/ingress.yml') | from_yaml }}"
复制代码


使用 Ansible 部署 K8s 组件


Kubernetes 集群


现在我们来看一下 K8s 集群。集群中有一个主节点和两个工作节点。


ubuntu@kube-master:~$ kubectl get nodesNAME STATUS ROLES AGE VERSIONkube-master Ready master 10d v1.19.4kube-worker1 Ready <none> 10d v1.19.4kube-worker2 Ready <none> 9d v1.19.4
复制代码


此外,我们还安装了 Traefik Ingress 控制器,作为集群中两个副本的部署(deployment)。


ubuntu@kube-master:~$ kubectl get pods -n kube-system|grep ingresstraefik-ingress-controller-6b7f594d46–5jqzq 1/1 Running 0 7d12htraefik-ingress-controller-6b7f594d46-vvfch 1/1 Running 0 8d
复制代码


为了无缝地部署 / 扩展我们的服务,我们以特定的格式组织了 Ansible Playbook 库。



K8s 服务定义的目录结构


每个服务都有两个特定于该服务的文件 deployment 和 service,common 目录中则包含了 ingress 配置定义。


apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app: "{{ app_name }}"  name: {{ service_name }}-deloymentspec:  replicas: 2  selector:    matchLabels:      app: "{{ app_name }}"      task: "{{ service_name }}"  template:    metadata:      labels:        app: "{{ app_name }}"        task: "{{ service_name }}"    spec:      containers:      - name: {{ service_name }}-pod        image: "{{ image_id }}"        imagePullPolicy: Always        ports:        - containerPort: 5000      imagePullSecrets:        - name: dockerhubsecret  strategy:    type: RollingUpdate    rollingUpdate:      maxSurge: 1      maxUnavailable: 0
复制代码


deployment.yml


上面的 deployment 文件是通用的,它从 Jenkins 管道作业中获取所有传输到 deploy_k8s.yml 的变量,并将其传输到该 K8s 的 deployment.yml 中。因此,若要用更多的微服务来扩展应用程序,只需在 Ansible Playbook 库中创建适当的目录结构以及具有 ingress 配置的 Jenkins 管道,即可实现完美地运行。


在创建部署之后,我们需要创建一个服务来公开该部署。该服务将端口 80 映射到 pod 的端口 5000。


apiVersion: v1kind: Servicemetadata:  name: {{ service_name }}-servicespec:  selector:    app: "{{ app_name }}"    task: "{{ service_name }}"  ports:    - protocol: TCP      port: 80      targetPort: 5000      name: http
复制代码


service.yml


Deployment.yml 和 Service.yml 文件对于 shoppingapp 服务下的所有微服务都是通用的。


另外,我们看一下 common/ingress.yml 中 ingress 配置。ingress 的主机(host)名是 app.shoppingapp.com,它具有基于路径的规则,可将流量重定向到特定的服务。


apiVersion: extensions/v1beta1kind: Ingressmetadata:  name: {{ app_name }}-ingress  annotations:    kubernetes.io/ingress.class: traefik    traefik.frontend.rule.type: PathPrefixStripspec:  rules:  - host: app.shoppingapp.com    http:      paths:      - path: /home        backend:          serviceName: shoppingapp-home-service          servicePort: http      - path: /kids        backend:          serviceName: shoppingapp-kids-service          servicePort: http      - path: /mens        backend:          serviceName: shoppingapp-mens-service          servicePort: http
复制代码


ingress.yml


引入 HAProxy


另外,我还使用了 HAProxy 负载均衡器来平衡 K8s 集群中两个节点之间的流量。我已经将前端和后端添加到现有的 HAProxy 配置文件中。后端服务器是 K8s 集群中的工作节点。


frontend http_frontbind *:80mode httpdefault_backend http_back
复制代码


backend http_backbalance roundrobinserver kube 172.31.35.122:32365 checkserver kube 172.31.40.13:32365 check
复制代码


端口 32365 是 traefik-ingress-service 服务公开的端口。DNS 是为 ingress.yml 中提到的主机名 app.shoppingapp.com 创建的,该主机名会被解析为 HAProxy IP(我使用的是 AWS Route 53 内部域名来创建的域名和 DNS 记录)


ubuntu@jenkins_ansible:~$ kubectl describe svc traefik-ingress-service -n kube-systemName: traefik-ingress-serviceNamespace: kube-systemLabels: <none>Annotations: <none>Selector: k8s-app=traefik-ingress-lbType: NodePortIP: 10.102.149.216Port: web 80/TCPTargetPort: 80/TCPNodePort: web 32365/TCPEndpoints: 10.244.1.99:80,10.244.3.19:80Port: admin 8080/TCPTargetPort: 8080/TCPNodePort: admin 31387/TCPEndpoints: 10.244.1.99:8080,10.244.3.19:8080Session Affinity: NoneExternal Traffic Policy: ClusterEvents: <none>
复制代码


真实的流程


对于这个为我们的微服务应用程序而创建的 CI/CD 管道,其所需的所有组件我们都已经了解了。现在,让我们将这些组件组装起来,以了解该流程的工作原理。


  • 开发人员提交代码到微服务仓库(shoppingapp-kids)

  • GitHub 中配置的 Webhook 通知 Jenkins 并触发相应的管道作业(shoppingapp kids)

  • 管道作业克隆 shoppingapp-kids 存储库,并开始执行 Jenkinsfile。

  • Jenkinsfile 中的构建阶段使用克隆的 shoppingapp-kids 存储库中的 Dockerfile 创建 Docker 镜像(deepanmurugan/shoppingapp-kids:21)。

  • 测试 Docker 镜像,如果测试通过,则将镜像推送到 Docker Hub。

  • 触发 Ansible Playbook,使用 app_name、service_name 以及 image_id 变量将 Docker 镜像部署到 K8s 集群。

  • Ansible Playbook 将读取所有与 deployment/servive/ingress 相关的 K8s 定义,并在集群中创建所需的组件。


向管道中添加新的微服务


假设我们有一个请求,要求在该架构中再添加一个名为 shoppingapp-ladies 的微服务。开发人员创建了一个名为 shoppingapp-ladies 的存储库,并使用相同的 Docker 和 Jenkinsfile 提交了代码,唯一的变化是 Jenkinsfile 中的 service_name=shoppingapp-ladies 变量。



修改文件夹结构后,添加新的微服务


修改 common/ingress.yml,并添加一个名为 /ladies 的新路径,端点是 shoppingapp-ladies-service。


- path: /ladiesbackend:serviceName: shoppingapp-ladies-serviceservicePort: http
复制代码


拷贝现有的 Jenkins 管道作业,并创建一个名为 shoppingapp-ladies 的新管道作业。



Jenkins 管道作业列表


在新作业 shoppingapp-ladies 的管道中,只需添加新的存储库 URL,然后执行管道作业,仅此而已。它将创建一个新的部署和服务并修改 ingress。


ubuntu@jenkins_ansible:/opt/python$ curl app.shoppingapp.com/ladiesWelcome to shoppingapp — Ladies section — shoppingapp-ladies-deloyment-656f6f9d9f-dms8w
复制代码


原文链接


https://medium.com/awsblogs/ci-cd-with-kubernetes-3c29e8073c38

2020-12-29 17:073974

评论 1 条评论

发布
用户头像
部署图画的很形象
2020-12-30 12:44
回复
没有更多了
发现更多内容

观测云产品更新 | 监控、数据脱敏、快照分享等优化

观测云

监控 快照 数据脱敏

Todolist工具哪个好?九款最优秀的待办事项管理工具

爱吃小舅的鱼

项目管理 待办事项管理

自建大数据平台迁移腾讯云EMR最佳实践

腾讯云大数据

EMR

ThreadPoolExecutor线程池内部处理浅析

快乐非自愿限量之名

Python 内部处理

软件测试/人工智能|Python函数与调用:解放编程力量的关键

霍格沃兹测试开发学社

软件测试/人工智能|Python关键字:代码掌中利器的关键之处

霍格沃兹测试开发学社

Kosmos实现无状态工作负载跨集群平滑扩展

畅聊云原生

软件测试/人工智能|Python标识符:代码世界中的命名之道

霍格沃兹测试开发学社

用爱发电,分享快乐,真·免费GM公益服游戏!

echeverra

公益服

告别盲目制作,5分钟教你完全理解用户旅程图!

职场工具箱

模板 用户旅程图

袋鼠云产品功能更新报告08期|近百项全新功能和优化,你要的都在这里!

袋鼠云数栈

大数据 数据中台 产品更新 产品功能

Kosmos介绍

畅聊云原生

掌握HarmonyOS框架的ArkTs如何管理和共享状态数据

不在线第一只蜗牛

数据库 HarmonyOS 鸿蒙系统

单元测试|如何编写更安全、更可靠的代码?

秃头小帅oi

低代码 单元测试

Inklet for Mac(触控板绘画工具) v2.2.5永久激活版

mac

苹果mac Windows软件 Inklet 触控板绘画工具

交互式白板软件有哪些?全球顶级的7款白板软件盘点!

彭宏豪95

在线白板 办公软件 团队协作工具 效率软件 数字白板

Debian12编译安装R软件教程。

百度搜索:蓝易云

云计算 Linux 运维 云服务器 Debian

Milvus 再上新!支持 Upsert、Kafka Connector、集成 Airbyte,助力高效数据流处理

Zilliz

kafka Milvus Zilliz airbyte

Illustrator 2023 for mac(Ai2023矢量设计软件)v27.9.0中文激活版

mac

AI 苹果mac Windows软件 矢量图软件 llustrator 2023

基于AI的架构优化:创新数据集构造法提升Feature envy坏味道检测与重构准确率

华为云开发者联盟

人工智能 华为云 华为云开发者联盟 智能检测

小程序开发实战案例之如何获取用户信息(一)

盐焗代码虾

支付宝小程序 经验分享 小程序开发 用户信息

分布式基础概念-分布式缓存[2]

派大星

Java 面试题

产品待办列表和冲刺待办列表的区别

爱吃小舅的鱼

产品经理 产品待办列表

拿来就用!6款详细的用户旅程图模板,一键下载!

职场工具箱

模板 用户旅程图

淘宝店铺所有商品数据接口|淘宝API接口

tbapi

淘宝API接口 淘宝店铺所有商品数据接口 淘宝整店商品数据接口

如何利用item_get接口提供的数据进行商品比较和筛选?

技术冰糖葫芦

API 文档

Ubuntu安装Anaconda详细步骤

百度搜索:蓝易云

Linux ubuntu 运维 Anaconda 云服务器

开发体育赛事直播APP平台实现“流量”向“增量”的成功转化

软件开发-梦幻运营部

从 Oracle 到 TiDB,全链路数据迁移平台核心能力和杭州银行迁移实践

PingCAP

数据库 数据库迁移 TiDB 银行业

Compressor for Mac(视频转码编辑工具)v4.7中文激活版

iMac小白

音视频FAQ(二)视频直播延时高

ZEGO即构

每天部署数千个容器实例,扩缩容复杂性该如何管理?_服务革新_Deep_InfoQ精选文章