灵雀云

灵雀云

2019企业IT现状和趋势调研报告:70.7%的企业有云原生相关计划

灵雀云 发表了文章 • 0 个评论 • 129 次浏览 • 2019-06-03 11:00 • 来自相关话题

2019年第一季度,灵雀云发起了“企业IT应用现状和云原生技术落地情况”的调研,通过定向邀请,3个月内共收集了400余份有效调研问卷,这些调研问卷80%以上都来自于国内政府、金融、能源、制造、汽车等传统行业的IT从业者。 发起本次调研 ...查看全部
2019年第一季度,灵雀云发起了“企业IT应用现状和云原生技术落地情况”的调研,通过定向邀请,3个月内共收集了400余份有效调研问卷,这些调研问卷80%以上都来自于国内政府、金融、能源、制造、汽车等传统行业的IT从业者。

发起本次调研的初衷,是我们希望了解当前企业,尤其是传统企业目前IT应用开发现状、以及以DevOps、Kubernetes、微服务等为代表的云原生技术在企业的应用情况,从而勾勒出传统行业IT发展趋势,并对于判断国内用户对云原生相关技术的认知度提供一个有价值的参考。
核心要点解读:
1、 约70%的参与调研者所在企业2019年IT预算有上浮;

2、 24.4%的参与调研者表示公司IT系统基本全靠自研,企业开始自建软件研发团队,主导IT应用的研发;

3、 70.7%的参与调研者所在企业表示在2019年有容器、DevOps和微服务方面的规划;

4、 11.4%的参与调研者所在企业已经试点了具有标杆意义的云原生实践,如精英团队的DevOps实践,小范围非核心应用的微服务拆分改造实践等。


pic2.jpg



本次调研的400多位调研对象中,80%以上来自金融、能源、制造、汽车等传统行业,其中17.3%来自基础架构部门, 22.5%来自运维部门,34.1%来自研发部门,还有约10%的被调研对象为企业的CIO/CTO等高级IT管理者。


pic3.jpg



被调研企业中,服务器规模在100-500台的比例为26.8%,500-1000台的企业占比22%,1000台服务器以上规模的14.6%。



IT系统自研还是外包



pic4.jpg




在数字化转型的背景下,传统外包的做法在被逐渐改变。在此次调查中,70.7%的参与调研者表示目前IT系统是自研外包兼而有之,其中核心业务系统以自己开发为主,24.4%的参与调研者表示公司IT系统基本全靠自研,只有4.9%的参与调研者选择了纯外包选项。这表明,企业开始不再将大部分业务系统,尤其是核心业务需求开发外包,开始自建软件研发团队,主导IT应用的研发。只有企业自己主导IT研发,才能够打造IT核心竞争力。

软件能力成为企业的核心竞争力,这恰好是数字化转型的本质之一。何谓成功的数字化转型?灵雀云认为,有三大衡量标志:IT部门由成本中心转为收入中心;企业自己主导IT产品的研发;改进工具、流程、文化来提高交付速度和质量。最终,实现客户满意度的提升、打造差异化竞争优势、加速产品上市。



IT系统更新频率




PIC5.jpg



在IT系统更新频率方面,每月都要更新、升级的比例达到了51.2%的高占比。同时,每3-6个月更新一次的比例达22%。每个传统领域,都受到了来自Fintech金融科技、车联网、物联网、新零售等新技术驱动的创新业务的挑战,传统企业只有借助IT手段才能实现持续发展,在速度和规模上保持竞争力。



IT系统和研发团队TOP 3挑战



pic6.jpg




本次参与调研的企业以中大型企业为主,其中研发团队规模达到100人以上的比例高达44.3%,20-100人规模的占32.4%。

PIC7.jpg




今天,许多企业都经过了大量IT建设,从分散到集中,造成IT系统越来越复杂,信息孤岛林立,架构臃肿等问题突出。调研中企业IT系统支撑所面临的压力位列前三的挑战分别是:系统复杂性越来越高(65.9%);应用交付压力大,交付速度无法满足业务需求(61.4%);运维管理复杂度提升,IT部门很难构建一支全功能团队(53.7%)。


PIC8.jpg



同时,研发团队所面临的挑战前三甲分别是:部署和运维复杂,运维成本高(74.6%);研发、测试、运维等角色之间相互孤立(62.3%);升级和变更流程复杂,IT服务和应用交付周期长(45.7%)。此外,比较突出的挑战还有,工具链无法完整集成,工具使用困难(32.3%),单体应用过于庞大,迭代效率低下(20.4%)。


pic9.jpg



上述结果充分表明,面对高度创新、快速变化和充满不确定性的新型业务需求,传统开发模式和IT架构已经成为掣肘。70.7%的参与调研企业表示2019年有容器、DevOps和微服务方面的规划和实施计划。

只有朝着持续交付、敏捷部署、快速迭代,通过敏捷IT赋予业务足够的敏捷,才能够满足不断变化的业务需求,重塑自身的生产力,形成竞争优势,带来更好的用户体验,这最终落到以Kubernetes/容器、DevOps和微服务为核心的云原生技术的落地上。云原生架构和理念与数字化转型一脉相承,帮助企业更加顺畅地实施数字化转型。



业务上云需求最强烈,开源、数字化转型受追捧



PIC10.jpg




在企业最关注的新兴技术趋势方面,云计算占比82.9%,企业将业务上云,提升IT资源效率作为首要关注对象。大数据和人工智能紧随其后,占比分别为73.2%和46.3%。其中开源解决方案在调研对象中的关注程度达到24.4%。

当前开源技术正在进入快速发展阶段,向着企业应用的方方面面深入。开源及开源社区不断将新的工具、方法和最佳实践用于云原生的实际业务用例,解决云原生用户的关键问题。借助许多开源解决方案,云原生部署的复杂性和难度也在得以降低。

此外,数字化转型的关注度为33.6%。如今每位IT从业者言必称数字化转型,IT能力也直接指向助力数字化转型。CIO和其他IT管理者已将企业的数字化计划置于新的高度,希望通过数字化来改变企业的商业和业务模式,数字化业务将从初步试验走向大规模应用。伴随企业数字化业务的不断成熟,预计未来几年,数字化转型将进入爆发阶段。



传统企业2019年IT预算稳中有升


PIC11.jpg





本次调研中,被调研企业今年IT工作的重点包括业务上云(56.1%),云原生、大数据、人工智能等新技术采用(53.7%),打造数字化团队,引领企业的数字化创新(43.9%),选择传统业务应用的比例不足20%。越来越多的企业将工作负载放在云端,将正在开发的应用或服务托管在云平台上,云市场不断增长。


PIC12.jpg



在IT预算方面,比客观经济形势略显乐观,和2018年IT预算相比,接近70%参与调研企业2019年的IT预算略有上浮,其中增长5%以内的企业占比37.5%,增长5-10%的企业占比21.2%,增长10%以上的企业达到12.7%。

此外,调研结果显示,数字化转型是一项需要通盘考虑的工作,需要项目管理部门、技术管理部门、开发部门、运维部门共同参与,制定统一的数字化转型方案和决策并推进。有些参与调研的企业特别强调2018年已经在全公司范围内试点了具有标杆意义的云原生实践,如精英团队的DevOps实践,小范围非核心应用的微服务拆分改造实践等,并且这些都将在2019年进行大范围推广。

浅析 Kubernetes原生NetworkPolicy 网络策略,让更安全的容器运行环境唾手可得

灵雀云 发表了文章 • 0 个评论 • 146 次浏览 • 2019-05-29 10:33 • 来自相关话题

k8s中的网络策略主要分为原生 NetworkPolicy 和第三方网络插件提供的网络策略。本文将主要分析原生Networkpolicy的网络策略。 什么是网络策略 ...查看全部

k8s中的网络策略主要分为原生 NetworkPolicy 和第三方网络插件提供的网络策略。本文将主要分析原生Networkpolicy的网络策略。



什么是网络策略



网络策略(NetworkPolicy)是一种关于 Pod 间及 Pod 与其他网络端点间所允许的通信规则的规范。NetworkPolicy 资源使用标签选择 Pod,并定义选定 Pod 所允许的通信规则。

k8s中的网络策略由实现了CNI接口的网络插件提供,网络插件监听集群中 NetworkPolicy 资源的创建/删除/更新事件生成对应的规则来控制 Pod 的流量是否放行。

常见的支持 NetworkPolicy 的网络插件有:

  • Calico
  • Cilium
  • Kube-router
  • Romana
  • Weave Net
默认情况下 Pod 间及 Pod 与其他网络端点间的访问是没有限制的。
pic1.png
如下是一个 NetworkPolicy 定义的例子,该策略的含义是阻止所有流量访问有`app=web`这个 label 的 Pod。经常有人会问网络策略要怎么写,或者是这个网络策略代表了什么含义。笔者认为这个问题主要是因为使用者不了解网络策略的省缺行为。NetworkPolicy 字段含义NetworkPolicy 这个资源属于命名空间级别的,因此metadata 中的 namespace 不可省略,否则只会对default 命名空间下的满足条件的 Pod 生效。下面介绍下 NetworkPolicy 中各字段的含义,并说明各字段省缺值及其含义,主要看 (http://www.alauda.cn]Spec.io/docs/reference/generated/kubernetes-api/v1.14/#networkpolicyspec-v1-networking-k8s-io) 中的字段,podSelector: 必填字段,Pod 的标签选择器,表示该网络策略作用于哪些 Pod。如果为空`{}`则表示选中本命名空间下所有 Pod。policyTypes: 可选字段,字符串,策略规则类型, 表示该网络策略中包含哪些类型的策略,可选为"Ingress", "Egress", 或 "Ingress,Egress"。未填时,这个值依据下面的 ingress 和 egress 来定。如果该字段未设置且下面只出现了 ingress,则对 egress 不做限制。如果填了这个值,同时后续没有设定对应的规则,则认为设定的规则对应的流量全部禁止。例如:
pic2.png
该规则就限制了所有 Pod 的出流量。ingress: 可选字段,数组,入站规则。互相间为或的关系,满足其中一条则放行。ports: 可选字段,数组,放行端口信息。互相间为或的关系,如果为空表示端口不受约束,如果非空,则表示除了出现的端口放行,其他未指定的端口都禁止。 -port: 可选字段,数字,协议端口号。如果不写,表示协议所有端口。 -protocol: 可选字段,字符串,协议。允许取值为 TCP,UDP,SCTP。省缺为 TCP。from: 可选字段,数组,放行源地址信息。互相间为或的关系,如果为空表示不约束源地址,如果非空,则表示除了出现的源地址放行外,其他源地址都禁止。 -ipBlock: 可选字段,放行 ip 段。 cidr: 标准 cidr,除了指定的cidr放行外其他都禁止。 except: 标准 cidr 字符串数组,表示前面cidr 中的放行的 cidr 段需要再排除掉 except 中指定的。 -namespaceSelector: 可选字段,namespace 的标签选择器,表示放行集群中的哪些命名空间中过来的流量。如果为空`{}`或未出现则表示选中所有命名空间。 -podSelector: 可选字段,Pod 的标签选择器,表示放行哪些 Pod 过来的流量,默认情况下从NetworkPolicy 同命名空间下的 Pod 中做筛选,如果前面设定了`namespaceSelector`则从符合条件的命名空间中的 Pod 中做筛选。如果为空`{}`则表示选中满足`namespaceSelector` 条件的所有 Pod。egress: 可选字段,数组,出站规则。互相间为或的关系,满足其中一条就放行。ports: 可选字段,数组,放行端口信息。互相间为或的关系,如果为空表示端口不受约束,如果非空,则表示除了出现的端口放行,其他未指定的端口都禁止。(详细字段同 ingress 中的 ports)to: 可选字段,数组,放行目的地址信息。互相间为或的关系,如果为空表示不约束目的,如果非空,则表示除了出现的目的地址放行外,其他目的地址都禁止。(详细字段同ingress 中的 from)介绍完 Spec 中各字段的含义及其默认行为后,做个简单的小结,NetworkPolicy 定义了放行规则,规则间是或的关系,只要命中其中一条规则就认为流量可以放行。下面以一个kubernetes官网中的例子来回顾下前面的知识。
pic3.png
首先该规则指定了命名空间为 default, 选择了其中所有包含 `role=db` 这个 label 的 Pod,定义了入站流量规则与出站流量规则。对于入站流量,放行源地址来自 cidr 172.17.0.0/16 除了 172.17.1.0/24 之外的流量,放行来自有`project=myproject` 这个label的namespace中的流量,放行 default 命名空间下有 label `role=frontend` 的 Pod 的流量,并限定这些流量只能访问到 `role=db` 这个 label 的 Pod 的 TCP 6379端口。对于出站流量,只放行其访问目的地址属于 cidr 10.0.0.0/24 中,且端口为 TCP 5978的流量。需要注意的是 NetworkPolicy 选中的 Pod 只能是与 NetworkPolicy 同处一个 namespace 中的 Pod,因此对于有些规则可能需要在多个命名空间中分别设置。或者使用非原生的网络策略定义,例如 Calico 中的 GlobalNetworkPolicyNetworkPolicy 变更历史v1.6 以及以前的版本需要在 kube-apiserver 中开启 extensions/v1beta1/networkpolicies;v1.7 版本 Network Policy 已经 GA,API 版本为 networking.k8s.io/v1;v1.8 版本新增 Egress 和 IPBlock 的支持;附录

使用 Jenkins + Ansible 实现 Spring Boot 自动化部署101

灵雀云 发表了文章 • 0 个评论 • 227 次浏览 • 2019-05-22 10:43 • 来自相关话题

本文要点: 设计一条 Spring Boot 最基本的流水线:包括构建、制品上传、部署。 使用 Docker 容器运行构建逻辑。 自动化整个实验环境:包括 Jenkins 的配置,Jenkins agent 的配置等。 ...查看全部
本文要点:
设计一条 Spring Boot 最基本的流水线:包括构建、制品上传、部署。
使用 Docker 容器运行构建逻辑。
自动化整个实验环境:包括 Jenkins 的配置,Jenkins agent 的配置等。

1. 代码仓库安排

本次实验涉及以下多个代码仓库:
% tree -L 1
├── 1-cd-platform # 实验环境相关代码
├── 1-env-conf # 环境配置代码-实现配置独立
└── 1-springboot # Spring Boot 应用的代码及其部署代码

1-springboot 的目录结构如下:
% cd 1-springboot
% tree -L 1
├── Jenkinsfile # 流水线代码
├── README.md
├── deploy # 部署代码
├── pom.xml
└── src # 业务代码

所有代码,均放在 GitHub:https://github.com/cd-in-practice

2. 实验环境准备

笔者使用 Docker Compose + Vagrant 进行实验。环境包括以下几个系统:
Jenkins * 1 Jenkins master,全自动安装插件、默认用户名密码:admin/admin。
Jenkins agent * 2 Jenkins agent 运行在 Docker 容器中,共启动两个。
Artifactory * 1 一个商业版的制品库。笔者申请了一个 30 天的商业版。
使用 Vagrant 是为了启动虚拟机,用于部署 Spring Boot 应用。如果你的开发机器无法使用 Vagrant,使用 VirtualBox 也可以达到同样的效果。但是有一点需要注意,那就是网络。如果在虚拟机中要访问 Docker 容器内提供的服务,需要在 DNS 上或者 hosts 上做相应的调整。所有的虚拟机的镜像使用 Centos7。
另,接下来笔者的所有教程都将使用 Artifactory 作为制品库。在此申明,笔者没有收 JFrog——研发 Artifactory 产品的公司——任何广告费。 笔者只是想试用商业产品,以便了解商业产品是如何应对制品管理问题的。
启动 Artifactory 后,需要添加 “Virtual Repository” 及 “Local Repository”。具体请查看 Artifactory 的官方文档。如果你当前使用的是 Nexus,参考本教程,做一些调整,问题也不大。
如果想使用已有制品库,可以修改 1-cd-platform 仓库中的 settings-docker.xml 文件,指向自己的制品库。
实验环境近期的总体结构图如下:

pic1.jpg



之所以说是“近期的”,是因为上图与本篇介绍的结构有小差异。本篇文章还没有介绍 Nginx 与 Springboot 配置共用,但是总体不影响读者理解。如果你想和更多Jenkins技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

3. Springboot 应用流水线介绍

Springboot 流水线有两个阶段:


构建并上传制品
部署应用
流水线的所有逻辑都写在 Jenkinsfile 文件。接下来,分别介绍这两个阶段。
3.1 构建并上传制品
此阶段核心代码:


docker.image('jenkins-docker-maven:3.6.1-jdk8')
.inside("--network 1-cd-platform_cd-in-practice -v $HOME/.m2:/root/.m2") {
sh """
mvn versions:set -DnewVersion=${APP_VERSION}
mvn clean test package
mvn deploy
"""
}


它首先启动一个装有 Maven 的容器,然后在容器内执行编译、单元测试、发布制品的操作。
而 mvn versions:set -DnewVersion=${APP_VERSION} 的作用是更改 pom.xml 文件中的版本。这样就可以实现每次提交对应一个版本的效果。
3.2 部署应用
注意: 这部分需要一些 Ansible 的知识。
首先看部署脚本的入口 1-springboot/deploy/playbook.yaml:

---


[list]
[*]hosts: "springboot"[/*]
[/list] become: yes
roles:
- {"role": "ansible-role-java", "java_home": "{{JAVA_HOME}}"}
- springboot



先安装 JDK,再安装 Spring Boot。JDK 的安装,使用了现成 Ansible role: https://github.com/geerlingguy/ansible-role-java。
重点在 Spring Boot 部署的核心逻辑。它主要包含以下几部分:
创建应用目录。
从制品库下载指定版本的制品。
生成 Systemd service 文件(实现服务化)。
启动服务。
以上步骤实现在 1-springboot/deploy/roles/springboot 中。
流水线的部署阶段的核心代码如下:


docker.image('williamyeh/ansible:centos7').inside("--network 1-cd-platform_cd-in-practice") {

checkout([$class: 'GitSCM', branches: [[name: "master"]], doGenerateSubmoduleConfigurations: false,
extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "env-conf"]], submoduleCfg: [],
userRemoteConfigs: [[url: "https://github.com/cd-in-practice/1-env-conf.git"]]])

sh "ls -al"

sh """
ansible-playbook --syntax-check deploy/playbook.yaml -i env-conf/dev
ansible-playbook deploy/playbook.yaml -i env-conf/dev --extra-vars '{"app_version": "${APP_VERSION}"}'
"""
}


它首先将配置变量仓库的代码 clone 下来,然后对 playbook 进行语法上的检查,最后执行 ansible-playbook 命令进行部署。--extra-vars 参数的 app_version 用于指定将要部署的应用的版本。

3.3 实现简易指定版本部署

在 1-springboot/Jenkinsfile 中实现了简易的指定版本部署。核心代码如下:
流水线接受参数
parameters { string(name: 'SPECIFIC_APP_VERSION',
defaultValue: '', description: '') }
如果指定了版本,则跳过构建阶段,直接执行部署阶段


stage("build and upload"){
// 如果不指定部署版本,则执行构建
when {
expression{ return params.SPECIFIC_APP_VERSION == "" }
}
// 构建并上传制品的逻辑
steps{...}
}


之所以说是“简易”,是因为部署时只指定了制品的版本,并没有指定的部署逻辑和配置的版本。这三者的版本要同步,部署才真正做到准确。

4. 配置管理

所有的配置项都放在 1-env-conf 仓库中。Ansible 执行部署时会读取此仓库的配置。
将配置放在 Git 仓库中有两个好处:
配置版本化。
任何配置的更改都可以被审查。
有好处并不代表没有成本。那就是开发人员必须开始关心软件的配置(笔者发现不少开发者忽视配置项管理的重要性。)。
本文重点不在配置管理,后面会有文章重点介绍。

5. 实验环境详细介绍

事实上,整个实验,工作量大的地方有两处:一是 Spring Boot 流水线本身的设计;二是整个实验环境的自动化。读者朋友之所以能一两条简单的命令就能启动整个实验环境,是因为笔者做了很多自动化的工作。笔者认为有必要在本篇介绍这些工作。接下来的文章将不再详细介绍。

5.1 解决流水线中启动的 Docker 容器无法访问 http://artifactory

流水线中,我们需要将制品上传到 artifactory(settings.xml 配置的仓库地址是 http://artifactory:8081),但是发现无法解析 host。这是因为流水线中的 Docker 容器所在网络与 Docker compose 创建的网络不同。所以,解决办法就是让流水线中的 Docker 容器加入到 Docker compose 的网络。
具体解决办法就是在启动容器时,加入参数:--network 1-cd-platform_cd-in-practice

5.2 Jenkins 初次启动初始化

在没有做任何设置的情况启动 Jenkins,会出现一个配置向导。这个过程必须是手工的。笔者希望这一步也是自动化的。Jenkins 启动时会执行 init.groovy.d/目录下的 Groovy 脚本。
5.3 虚拟机中如何能访问到 http://artifactory ?
http://artifactory 部署在 Docker 容器中。Spring Boot 应用的制品要部署到虚拟机中,需要从 http://artifactory 中拉取制品,也就是要在虚拟机里访问容器里提供的服务。虚拟机与容器之间的网络是不通的。那怎么办呢?笔者的解决方案是使用宿主机的 IP 做中转。具体做法就是在虚拟机中加一条 host 记录:


machine.vm.provision "shell" do |s|
s.inline = "echo '192.168.52.1 artifactory' >> /etc/hosts"
end


以上是使用了 Vagrant 的 provision 技术,在执行命令 vagrant up 启动虚拟机时,就自动执行那段内联 shell。192.168.52.1 是虚拟宿主机的 IP。所以,虚拟机里访问 http://artifactory:8081 时,实际上访问的是 http://192.168.52.1:8081。
网络结构可以总结为下图:

pic2.jpg


后记

目前遗留问题:
部署时制品版本、配置版本、部署代码版本没有同步。
Springboot 的配置是写死在制品中的,没有实现制品与配置项的分离。
这些遗留问题在后期会逐个解决。就像现实一样,经常需要面对各种遗留项目的遗留问题。
附录
使用 Jenkins + Ansible 实现自动化部署 Nginx:https://jenkins-zh.cn/wechat/articles/2019/04/2019-04-25-jenkins-ansible-nginx/
简单易懂 Ansible 系列 —— 解决了什么:https://showme.codes/2017-06-12/ansible-introduce/


本文转自公众号 jenkins
作者 翟志军

基于 Jenkins 的 DevOps 平台应该如何设计凭证管理

灵雀云 发表了文章 • 0 个评论 • 211 次浏览 • 2019-05-20 11:06 • 来自相关话题

背景 了解到行业内有些团队是基于 Jenkins 开发 DevOps 平台。而基于 Jenkins 实现的 DevOps 平台,就不得不考虑凭证的管理问题。 本文就此问题进行讨论,尝试找出相对合理的管理凭证的方案。 ...查看全部
背景

了解到行业内有些团队是基于 Jenkins 开发 DevOps 平台。而基于 Jenkins 实现的 DevOps 平台,就不得不考虑凭证的管理问题。
本文就此问题进行讨论,尝试找出相对合理的管理凭证的方案。
一开始我们想到的方案可能是这样的:用户在 DevOps 平台增加凭证后,DevOps 再将凭证同步到 Jenkins 上。Jenkins 任务在使用凭证时,使用的是存储在 Jenkins 上的凭证,而不是 DevOps 平台上的。
但是,仔细想想,这样做会存在以下问题:
Jenkins 与 DevOps 平台之间的凭证数据会存在不一致问题。
存在一定的安全隐患。通过 Jenkins 脚本命令行很容易就把所有密码的明文拿到。哪天 Jenkins 被注入了,所有的凭证一下子就被扒走。
无法实现 Jenkins 高可用,因为凭证存在 Jenkins master 机器上。
那么,有没有更好的办法呢?

期望实现的目标

先定我们觉得更合理的目标,然后讨论如何实现。以下是笔者觉得合理的目标:
用户还是在 DevOps 管理自己的凭证。但是 DevOps 不需要将自己凭证同步到 Jenkins 上。Jenkins 任务在使用凭证时,从 DevOps 上取。

实现方式

Jenkins 有一个 Credentials Binding Plugin 插件,在 Jenkins pipeline 中的用法如下:


withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) {
sh '''
curl -u "$USERPASS" https://private.server/ > output
'''
}


withCredentials 方法做的事情就是从 Jenkins 的凭证列表中取出 id 为 mylogin 的凭证,并将值赋到变量名为 USERPASS 的变量中。接下来,你就可以在闭包中使用该变量了。
说到这里,不知道读者朋友是否已经有思路了?
思路就是实现一个和 Credentials Binding Plugin 插件类似功能的方法,比如叫 zWithCredentials(后文还会提到)。与 withCredentials 不同的是,zWithCredentials 根据凭证 id 获取凭证时,不是从 Jenkins 上获取,而是从 DevOps 平台获取。

会遇到的坑

需要适配只认 Jenkins 凭证的插件
withCredentials 方法是将凭证的内容存到变量中,这可以满足一大部分场景。但是有一种场景是无法满足的。就是某些 Jenkins 插件的步骤接收参数时,参数值必须是 Jenkins 凭证管理系统中的 id。比如 git 步骤中 credentialsId 参数:


git branch: 'master',
credentialsId: '12345-1234-4696-af25-123455',
url: 'ssh://git@bitbucket.org:company/repo.git'


这种情况,我们不可能修改现有的插件。因为那样做的成本太高了。
那怎么办呢?
笔者想到的办法是在 zWithCredentials 中做一些 hack 操作。也就是 zWithCredentials 除了从 DevOps 平台获取凭证,还在 Jenkins 中创建一个 Jenkins 凭证。在 Jenkins 任务执行完成后,再将这个临时凭证删除。这样就可以适配那些只认 Jenkins 凭证 id 的插件了。
对凭证本身的加密
DevOps 平台在存储凭证、传输凭证给 Jenkins 时,都需要对凭证进行加密。至于使用何种加密方式,交给读者思考了。

小结

以上解决方案对 Jenkins 本身的改造几乎没有,我们只通过一个插件就解耦了 Jenkins 的凭证管理和 DevOps 平台的凭证管理。
思路已经有了。具体怎么实现,由于一些原因不能开源,虽然实现起来不算难。还请读者见谅。
最后,希望能和遇到同样问题的同学进行交流。看看是否还可以有更好的设计思路。

本文转自公众号 Jenkins
作者:翟志军

深度解析Kubernetes核心原理之Scheduler

灵雀云 发表了文章 • 0 个评论 • 227 次浏览 • 2019-05-17 10:56 • 来自相关话题

Kubernetes是一个容器编排引擎,它被设计为在被称为集群的节点上运行容器化应用。通过系统建模的方法,本系列文章的目的是为了能够深入了解Kubernetes以及它的深层概念。 Kubernetes Scheduler ...查看全部
Kubernetes是一个容器编排引擎,它被设计为在被称为集群的节点上运行容器化应用。通过系统建模的方法,本系列文章的目的是为了能够深入了解Kubernetes以及它的深层概念。


Kubernetes Scheduler是Kubernetes的一个核心组件:在用户或者控制器创建一个Pod后,Scheduler在对象存储数据里监控未被分配的Pod,并将Pod分配到某个节点。然后Kubelet在对象存储数据里监控已分配的Pod,并运行该Pod。

本文提供了一个Kubernetes Scheduler的更简洁、更详细的模型表述。该模型部分基于TLA+规范。

pic1.jpg


图 1. Pod处理流程




调度



Kubernetes Scheduler的任务是选择一个placement(位置)。一个placement是一个部分的,非内射的Pod集合到节点集合的分配。

pic2.png



图 2. 调度示例

调度是一个最优化问题:首先,Scheduler确定feasible placements(可用的位置),这些是满足给定约束的placement集合。然后,Scheduler确定viable placements(可行的位置),这些是得分最高的feasible placements集合。

pi3.jpg


图 3. Possible(可能), Feasible(可用)和Viable(可行)的调度

Kubernetes Scheduler是一个保证局部最优解的多步调度器,而不是一个保证全局最优解的单步调度器。


pic4.jpg


图 4. 多步 vs. 单步



Kubernetes Scheduler




pi5.jpg



图 5. Kubernetes Pod对象和Node对象

图5描述了Kubernetes Scheduler所感兴趣的Kubernetes对象和属性。在Kubernetes里: 一个Pod表示为一个Kubernetes Pod对象 一个Node表示为一个Kubernetes Node对象 * 一个Pod分配给一个Node表示为Pod的Spec.NodeName属性












BoundTo(Pod, Node, Snapshot)≝
∧ Pod ∈Snapshot
∧Pod.Kind = "Pod"
∧ Node∈ Snapshot
∧Node.Kind = "Node"
∧Pod.Spec.NodeName = Node.Name

Bound(Pod, Snapshot) ≝
∃ Node∈ Snapshot:
BoundTo(Pod, Node, Snapshot)


如果一个Pod的Spec.NodeName等于一个Node的Name,则表示这个Pod对象绑定到了这个Node对象。

Kubernetes Scheduler的任务现在可以更规范地表述为:对于一个Pod p,Kubernetes Scheduler选择一个Node n,且更新(*)这个Pod的Spec.NodeName使得BoundTo(p, n)为true。



控制循环逻辑










Scheduler ≝
LETUnbound ≝ {Pod \in Objects : Pod.Kind = "Pod" ∧ ~ Bound(Pod,Objects)} IN
∃ Pod∈ { Pod ∈ Unbound : ∀ Other ∈ Unbound : Other.Spec.Priority ≤ Pod.Spec.Priority}:
CASE SchedulingEnabled(Pod) ⟶ Scheduling(Pod)
[] PreemptionEnabled(Pod) ⟶ Preemption(Pod)
[] OTHER ⟶ UNCHANGED(Objects)



Kubernetes Scheduler监控Kubernetes对象存储并且选择一个未绑定的最高优先级的Pod来执行调度流程或者抢占流程。



调度流程

















SchedulingEnabled(Pod) ≝
∃ Node∈ {Node ∈ Objects : Node.Kind = "Node"}:
Feasibility(Pod, Node, Objects)

Scheduling(Pod) ≝
LETFeasibile ≝ {Node ∈ Objects : n.Kind = "Node" ∧ Feasibility(Pod, n,Objects)} IN
∃Node ∈ Feasibile :
∧ ∀Other ∈ Feasibile : Viability(Pod, Other, Objects) ≤ Viability(Pod, Node,Objects)
∧Objects' = {
IF Pod = Object THEN
[Pod EXCEPT !["Spec"] = [Pod.Spec EXCEPT!["NodeName"] = Node.Name]]
ELSE
Object : Object ∈ Objects}



对于一个给定的Pod,如果存在至少一个Node可以运行该Pod,则启用调度流程。

如果调度流程启用,Scheduler将绑定该Pod到一个可选的Node,使得绑定能达到最优的可行性。

如果调度流程未启用,则Sheduler将尝试执行抢占流程。



抢占流程

















PreemptionEnabled(Pod) ≝
∃ Node∈ {Node \in Objects : Node.Kind = "Node"}:
∃Pods ∈ SUBSET(Jeopardy(Pod, Node, Objects)):
Feasibility(Pod, Node, Objects \ Pods)

Preemption(Pod) ==
LETPreemptable == {Node ∈ Objects : Node.Kind = "Node" ∧ ∃ Pods ∈SUBSET(Jeopardy(p, Node, Objects)): Feasibility(Pod, Node, Objects \ Pods)} IN
∃Node ∈ Preemptable:
∃Pods ∈ SUBSET(Jeopardy(Pod, Node, Objects)):
∀OtherNode ∈ qualified:
∀ OtherPods ∈ SUBSET(Jeopardy(Pod, OtherNode, Objects)):
∧ Casualty(Pods) ≤ Casualty(OtherPods)
∧ Objects' = (Objects \ Pods)



对于一个给定的Pod,如果存在至少一个Node,在删除绑定到该Node的较低优先级Pod子集后可以运行该Pod,则启用抢占流程。

如果抢占流程启用,Scheduler将触发绑定到Node的低优先级Pod子集的删除操作,使得抢占流程造成的损害最小。

(抢占损害是用Pod Disruption Budget来评估的,超出了本文的主题)

注意的是Scheduler不保证触发抢占流程的Pod在后续的调度流程中能绑定到Node。



1. 可用性(Feaisbility)



对于每一个Pod,Kubernetes Scheduler确定可用的Node集合,这些Node满足了该Pod的约束。

从概念上讲,Kubernetes Scheduler定义了一个过滤函数集合。给定一个Pod和一个Node,过滤函数决定该Node是否满足该Pod的约束。所有过滤函数都必须返回true才表示该Node可以运行该Pod。




Feasibility(Pod, Node,Snapshot) ==
(Filter_1(Pod, Node, Snapshot) ∧ Filter_2(Pod, Node, Snapshot) ∧ ...)


下面小节详细描述了目前一些可用的过滤函数:

1.1 可调度性和生命周期阶段(Schedulability and LifecyclePhase)

该过滤函数基于Node的可调度性和生命周期阶段来确定Node的可用。Nodeconditions通过taints和tolerations来说明(如下所示)。



pic7.jpg



图 1.1 可调度性和生命周期阶段






Filter(Pod, Node) ≝
\* Onlyconsider Nodes that accept new Pods
∧Node.Spec.Unschedulable = False
\* Onlyconsider Nodes that are ready to accept new Pods (Lifecycle Phase)
∧Node.Status.Phase = "Running"


1.2 资源需求和资源可用性

该过滤函数基于Pod的资源需求和Node的资源可用性来确定Node的可用。


pic8.jpg




图 1.2 资源需求和资源可用性











Resources(Pod, Node) ≝
∧ 1 ≤Node.Status.Allocatable["pods"]
\* Usethe maximum resource requirements of init containers
∧ Max({i \in DOMAIN p.Spec.InitContainer :p.Spec.InitContainer[i].Resources.Required["cpu"] }) ≤Node.Status.Allocatable["cpu"]
∧ Max({i \in DOMAIN p.Spec.InitContainer :p.Spec.InitContainer[i].Resources.Required["mem"] }) ≤Node.Status.Allocatable["mem"]
∧ ...
\* Usethe sum of resource requirements of main containers
∧ Sum({i \in DOMAIN p.Spec.Container :p.Spec.Container[i].Resources.Required["cpu"] }) ≤Node.Status.Allocatable["cpu"]
∧ Sum({i \in DOMAIN p.Spec.Container :p.Spec.Container[i].Resources.Required["mem"] }) ≤Node.Status.Allocatable["mem"]
∧ ...


1.3 Node Selector

该过滤函数基于Pod的node selector值和Node的label值来确定Node的可用。



pic9.jpg



图 1.3 Node Selector





Filter(Pod, Node) ==
∀ Label∈ DOMAIN(Pod.Spec.NodeSelector):
∧Label ∈ DOMAIN(Node.Labels)
∧Pod.Spec.NodeSelector[Label] = Node.Labels[Label]


1.4 Node Taints和Pod Tolerations

该过滤函数基于Pod的taints键值对和Node的tolerations键值对来确定Node的可用。



pic10.jpg



图 1.4 Node Taints和Pod Tolerations













Filter(Pod, Node) ==
∀Taint ∈ Node.Spec.Taints:
∃Toleration ∈ Pod.Spec.Tolerations: Match(Toleration, Taint)

Match(Toleration, Taint) ==
∧CASE Toleration.Operator = "Exists"
⟶ Toleration.key = Taint.key
[] Toleration.Operator = "Equal"
⟶ Toleration.key = Taint.key ∧ Toleration.value = Taint.value
[] OTHER
⟶ FALSE
∧Toleration.Effect = Taint.Effect


如果某个Node的taints匹配Pod的tolerations, 一个Pod可能被绑定到该Node。如果某个Node的taints不匹配Pod的tolerations, 一个Pod不能被绑定该Node。

1.5 亲和性

该过滤函数基于Pod需要的Node亲和项,Pod亲和项和Pod反亲和项来确定Node的可用。


pic11.jpg


图 1.4 Node Taints和Pod Tolerations


















































Filter(Pod, Node) ≝
\*Node, Affinity
∧ ∃NodeSelectorTerm ∈ Pod.Spec.Affinity.NodeAffinity.Required.NodeSelectorTerms :
Match_NS(NodeSelectorTerm, Node)
\*Pod, Affinity
∧ ∀PodAffinityTerm ∈ Pod.Spec.Affinity.PodAffinity.Required :
P_Affinity(PodAffinityTerm, Node)
\*Pod, Anit-Affinity
∧ ∀PodAffinityTerm ∈ Pod.Spec.Affinity.AntiPodAffinity.Required :
¬ P_Affinity(PodAffinityTerm, Node)

\* Node, Affinity, Match Node Selector Term
Match_NS(NodeSelectorRequirement, Node) ≝
CASENodeSelectorRequirement.Operator = "In"
⟶ (NodeSelectorRequirement.Key ∈DOMAIN(Node.Labels) ∧ Node.Labels[NodeSelectorRequirement.Key] ∈NodeSelectorRequirement.Value)
[]NodeSelectorRequirement.Operator = "NotIn"
⟶¬ (NodeSelectorRequirement.Key ∈ DOMAIN(Node.Labels) ∧Node.Labels[NodeSelectorRequirement.Key] ∈ NodeSelectorRequirement.Value)
[]NodeSelectorRequirement.Operator = "Exits"
⟶ (NodeSelectorRequirement.Key ∈DOMAIN(Node.Labels))
[]NodeSelectorRequirement.Operator = "DoesNotExist"
⟶¬ (NodeSelectorRequirement.Key ∈ DOMAIN(Node.Labels))
[]_NodeSelectorRequirement.Operator = "Gt" ∧ ∀ Value ∈NodeSelectorRequirement.Value: Value ∈ Int
⟶ (NodeSelectorRequirement.Key ∈DOMAIN(Node.Labels) ∧ Node.Labels[NodeSelectorRequirement.Key] ∈ Int ∧Node.Labels[NodeSelectorRequirement.Key] >Max(NodeSelectorRequirement.Value))
[]_NodeSelectorRequirement.Operator = "Lt" ∧ ∀ Value ∈NodeSelectorRequirement.Value: Value ∈ Int
⟶ (NodeSelectorRequirement.Key ∈DOMAIN(Node.Labels) ∧ Node.Labels[NodeSelectorRequirement.Key] ∈ Int ∧Node.Labels[NodeSelectorRequirement.Key] []OTHER
⟶FALSE

\* Pod, (Anti)Affinity, Match Pod Affinity Term
P_Affinity(PodAffinityTerm, Node) ==
IFPodAffinityTerm.TopologyKey \in DOMAIN(Node.Labels) THEN
∃Other ∈ {Other ∈ Objects : Other.Kind = "Node" ∧PodAffinityTerm.TopologyKey ∈ DOMAIN(Other.Labels) ∧Other.Labels[PodAffinityTerm.TopologyKey] =Node.Labels[PodAffinityTerm.TopologyKey]}:
∃ Pod ∈ {Pod ∈ objects : Pod.kind = "Pod" ∧ BoundTo(Pod, Node)∧ Pod.Namespace ∈ PodAffinityTerm.Namespaces}:
Match_LS(PodAffinityTerm.LabelSelector, Pod.Labels)
ELSE
FALSE

\* Pod, (Anti)Affinity, Match Label Selector
Match_LS(LabelSelector, Labels) ≝
∧ ∀Key ∈ DOMAIN(LabelSelector) : Key ∈ DOMAIN(Labels) ∧ LabelSelector[Key] =Labels[Key]
∧ ∀LabelSelectorRequirement ∈ LabelSelector.MatchExpression:
CASE LabelSelectorRequirement.Operator = "In"
⟶ (LabelSelectorRequirement.Key∈ DOMAIN(Labels) ∧ Labels[LabelSelectorRequirement.Key] ∈LabelSelectorRequirement.Values)
[] _LabelSelectorRequirement.Operator = "NotIn"
⟶ ¬ (LabelSelectorRequirement.Key ∈ DOMAIN(Labels) ∧Labels[LabelSelectorRequirement.key] ∈ LabelSelectorRequirement.Values)
[] _LabelSelectorRequirement.Operator = "Exists"
⟶ (LabelSelectorRequirement.Key∈ DOMAIN(Labels))
[] _LabelSelectorRequirement.Operator = "DoesNotExist"
⟶ ¬ (LabelSelectorRequirement.Key ∈ DOMAIN(Labels))


Node亲和
一个Pod必须分配给label匹配Pod的Node亲和需求的Node。另外,一个Pod不能分配给label不匹配Pod的Node亲和需求的Node。

Pod亲和
一个Pod必须分配给匹配TopologyKey的Node, 且该Node上至少有一个Pod匹配Pod的亲和需求。

Pod反亲和
一个Pod必须分配给匹配TopologyKey的Node, 且该Node上没有Pod匹配Pod的反亲和需求。



  1. 可行性(Viability)



对于每一个Pod,Kubernetes Scheduler确定可用的Node集合,这些Node满足了该Pod的约束。然后,Kubernetes Scheduler从可用Node集合中确定最高可行性的Node。

从概念上讲,Kubernetes Scheduler定义了一个评分函数集合。给定一个Pod和一个Node,评分函数确定Pod和Node配对的可行性。这些结果最后相加。




Viability(Pod, Node,Snapshot) ==
Sum(<>)


下面小节详细描述了目前一些可用的过滤函数:

2.1 亲和偏好

这些过滤函数基于Pod的偏好Node亲和项,Pod亲和项和Pod反亲和项,对Node的可行性进行评分。


pic12.jpg


图 1.4 Node Taints和Pod Tolerations







Rating(Pod, Node) ≝
Sum(<<
Sum(LAMBDA Term: Term.Weight, {NodeSelectorTerm ∈Pod.Spec.Affinity.NodeAffinity.Preferred.NodeSelectorTerms :Match_NS(NodeSelectorTerm, Node) }),
Sum(LAMBDA Term: Term.Weight, {PodAffinityTerm ∈Pod.Spec.Affinity.PodAffinity.Preferred : P_Affinity(PodAffinityTerm, Node) }),
Sum(LAMBDA Term: Term.Weight, {PodAffinityTerm ∈Pod.Spec.Affinity.AntiPodAffinity.Preferred : ~ P_Affinity(PodAffinityTerm,Node)})
>>)



最终评分是下列项的总和: 对于每一个匹配的Node Selector项的权重的总和 对于每一个匹配的Pod亲和项的权重的总和 * 对于每一个匹配的Pod反亲和项的权重的总和



用例分析
图6描述了包含2个不同类型的节点和2个不同类型的Pod的例子: 没有GPU资源的9个节点 有GPU资源的6个节点

这个用例的目标是保证: 不需要GPU的Pod被分配到没有GPU的节点 需要GPU的Pod被分配到有GPU的节点
pic13.jpg

灵雀云Kube-OVN:基于OVN的开源Kubernetes网络实践

灵雀云 发表了文章 • 0 个评论 • 446 次浏览 • 2019-05-09 14:34 • 来自相关话题

近日,灵雀云发布了基于OVN的Kubernetes网络组件Kube-OVN,并正式将其在Github上开源。Kube-OVN提供了大量目前Kubernetes不具备的网络功能,并在原有基础上进行增强。通过将OpenStack领域成熟的网络功能平移到Kubern ...查看全部

近日,灵雀云发布了基于OVN的Kubernetes网络组件Kube-OVN,并正式将其在Github上开源。Kube-OVN提供了大量目前Kubernetes不具备的网络功能,并在原有基础上进行增强。通过将OpenStack领域成熟的网络功能平移到Kubernetes,来应对更加复杂的基础环境和应用合规性要求。
目前Kube-OVN项目代码已经在Github 上开源,项目地址为:https://github.com/alauda/kube-ovn。项目使用宽松的Apache 2.0 协议,欢迎更多技术开发者和爱好者前去试用和使用。

网络插件那么多,为什么还需要Kube-OVN?

网络插件千千万,为什么还要开发Kube-OVN?从当前Kubernetes网络现状来看,Kubernetes 网络相关的组件非常分散。例如,CNI 负责基础容器网络,它本身只是个接口标准,社区和市场上都有很多各自的实现;集群内的服务发现网络需要依赖 kube-proxy,而 kube-proxy 又有 iptables 和 ipvs 两种实现;集群内的 DNS 需要依赖额外组件kube-dns 或coredns;集群对外访问的负载均衡器服务需要依赖各个云厂商提供的Cloud-Provider;网络策略的 NetworkPolicy 本身只是一个标准接口,社区中也有各自不同的实现。此外还有 ingress,Kubernetes提供的只是一个标准接口,社区中同样有各自的实现。
分散的网络组件导致容器网络流量被分散到了不同的网络组件上,一旦出现问题需要在多个组件间游走逐个排查。在实际运维过程中网络问题通常是最难排查的,需要维护人员掌握全链路上所有组件的使用、原理以及排查方式。因此,如果有一个网络方案能够将所有数据平面统一,那么出现问题时只需要排查一个组件即可。
其次,现有网络插件种类繁多,但是在落地时会发现,每个网络插件由于覆盖的功能集合不同,很难在所有场景使用同一套方案。为了满足不同的客户需求,很多厂商一度同时支持多种网络方案,给自身带来很大负担。在落地过程中,还发现很多传统的网络方案在容器网络中是缺失的。一些高级功能是所有网络插件都无法满足的,比如:子网划分、vlan 绑定、nat、qos、固定 IP、基于acl的网络策略等等。
现有 Kubernetes网络能力是否足够?答案很明显,如果真的已经做够强大落地的时候就不会出现这么多的问题。从更大格局来看,Kubernetes本质上是提供了一层虚拟化网络。而虚拟化网络并不是一个新问题。在OpenStack社区,虚拟网络已经有了长足的发展,方案成熟,OVS 基本已经成为网络虚拟化的标准。于是,灵雀云开始把目光投向OVS 以及 OVS 的控制器OVN。

OVN 简介

OVS 是一个单机的虚拟网络交换机,同时支持 OpenFlow 可以实现复杂的网络流量编程,这也是网络虚拟化的基础。通过OVS 和 OpenFlow 可以实现细粒度的流量控制。如果做个类比,OVS 相当于网络虚拟化里的 Docker。
OVS 只是个单机程序,想生成一个集群规模的虚拟网络就需要一个控制器,这就是 OVN。OVN 和 OVS 的关系就好比 Kubernetes 和 Docker 的关系。OVN 会将高层次的网络抽象转换成具体的网络配置和流表,下发到各个节点的OVS上,实现集群网络的管理。
由于 OVN 最初是为 OpenStack 网络功能设计的,提供了大量 Kubernetes 网络目前不存在的功能:
L2/L3 网络虚拟化包括:
• 分布式交换机,分布式路由器
• L2到L4的ACL
• 内部和外部负载均衡器
• QoS,NAT,分布式DNS
• Gateway
• IPv4/IPv6 支持

此外 OVN 支持多平台,可以在Linux,Windows,KVM,XEN,Hyper-V 以及 DPDK 的环境下运行。
综上可以看出 OVN 可以覆盖 CNI, Kube-Proxy, LoadBalancer, NetworkPolicy, DNS 等在内的所有 Kubernetes 网络功能,并在原有基础上有所增强。将之前在OpenStack领域内成熟的网络功能往 Kubernetes 平移,也就诞生了灵雀云的开源项目 Kube-OVN.

Kube-OVN 重要功能及实现

目前大部分网络插件的网络拓扑都是一个大二层网络,通过防火墙做隔离,这种形式非常类似公有云早期的经典网络。Kube-OVN在设计网络支出时同时把多租户的场景考虑进来,方便之后更好的扩展高级功能,因此采用了不同的网络拓扑。

PIC1.png


Kube-OVN 网络拓扑

在Kube-OVN的网络拓扑里,不同 Namespace 对应着不同的子网。子网是其中最为重要的概念,之后的内置负载均衡器,防火墙,路由策略以及DNS的网络功能都是和子网绑定的。我们做到了同一台机器的不同 pod 可以使用不同的子网,多个子网之间使用一个虚拟路由器进行网络的联通。为了满足Kubernetes中主机可以和容器互通的网络要求,Kube-OVN在每个主机新增一块虚拟网卡,并接入一个单独的虚拟交换机和集群的虚拟路由器相连,来实现主机和容器网络的互通。
在容器访问外部网络的策略中,Kube-OVN设计了两种方案:一种是分布式 Gateway,每台主机都可以作为当前主机上 Pod 的出网节点,做到出网的分布式。针对企业需要对流量进行审计,希望使用固定IP出网的场景,还做了和 namespace 绑定的 Gateway,可以做到一个 namespace 下的 Pod 使用一个集中式的主机出网。在整个网络拓扑中,除了集中式网关,交换机,路由器,防火墙,负载均衡器,DNS都是分布在每个节点上的,不存在网络的单点。

PIC2.png



Kube-OVN架构图

在实现的过程中,Kube-OVN对组件架构进行了大幅的简化,整个流程中只需要一个全局Controller 和分布在每台机器上的 CNI 插件,就可以组建网络,整体安装十分简单。其中全局Controller 负责监听 APIServer 事件,针对 Namespace,Pod,Service,Endpoint 等和网络相关的资源变化对 OVN 进行操作。同时 Controller 会把操作 OVN 的结果,例如 IP ,Mac,网关,路由等信息回写到对应资源的 annotation 中。而 CNI 插件则根据回写的 annotation 信息操作本机的 OVS 以及主机网络配置。通过这种架构Kube-OVN将 CNI 插件和、Controller 和 OVN 解耦,通过 Apiserver 传递所需的网络信息,方便快速开发迭代。

Kube-OVN选择使用最基础的 annotation ,而不是 CRD、API Aggregation 或者 Operator,也是出于降低复杂度的考虑,使用户和开发者都能够快速上手。

Kube-OVN主要具备五大主要功能:
1. Namespace 和子网的绑定,以及子网间访问控制;
2. 静态IP分配;
3. 动态QoS;
4. 分布式和集中式网关;
5. 内嵌 LoadBalancer;
子网是Kube-OVN中最重要的概念,由于子网和 namespace 关联,因此只需要在 namespace 中设置对应的 annotation 就可以完成子网的功能。Kube-OVN支持子网cidr,gateway,exclude_ips 以及 switch_name 的设置。同时支持基于子网的IP隔离,用户可以轻松实施基本的网络隔离策略。在后台实现上,Kube-OVN会监听 namespace 的变化,并根据变化在 ovn 中创建并设置虚拟交换机,将其和集群路由器关联,设置对应的 acl,dns 和 lb。

静态IP的使用可以直接在 Pod 中加入对应的 annotation,Controller 在发现相关 annotation 时会跳过自动分配阶段,直接使用用户指定的 IP/Mac。对应多 Pod 的工作负载,例如 Deployment、DaemonSet,可以指定一个 ip-pool,工作负载下的 Pod 会自动使用ip-pool中未使用的地址。

在QoS功能中,分别实现了 ingress 和 egress 的带宽限制,用户可以在 Pod 运行时通过动态调整 annotation 来实现 QoS 的动态调整,而无需重启 Pod。在后台的实现中, OVN 自带的 QoS 功能工作在 Tunnel 端口,无法对同主机间 Pod 的互访做 QoS 控制。因此Kube-OVN最终通过操作 OVS 的 ingress_policing_rate 和 port qos 字段来实现 QoS 的控制。

在网关设计中,OVN的网关功能有一些使用限制,需要单独的网卡来做 overlay 和 underlay 的流量交换,使用起来比较复杂,为了能够适应更广泛的网络条件并简化用户使用,Kube-OVN对网关部分进行了调整。使用策略路由的方式根据网络包的源 IP 选择下一跳的机器,通过这种方式将流量导入所希望的边界网关节点,然后在网关节点通过 SNAT 的方式对外进行访问。这种方式用户只需要在 namespace 中配置一个网关节点的 annotation 就可以配置对应的流量规则。此外,Kube-OVN也支持非SNAT将容器IP直接暴露给外网的场景,这种情况下只需要外部添加一条静态路由指向容器网络,就可以实现 Pod IP 直接和外部互通。
内嵌的 LoadBalancer 使用 OVN 内置的 L2 LB,这样集群内部的服务发现功能可以直接在 OVS 层面完成,不需要走到宿主机的 iptable 或者 ipvs 规则,可以将 kube-porxy 的功能整合到 Kube-OVN 中。

开源计划 & RoadMap

目前Kube-OVN已经在 github 上开源。OVN 安装比较繁琐,Kube-OVN特意做了安装的简化,现在只需要两个 yaml 就可以部署一个完整的 Kube-OVN。在使用方面也做了优化,通过直观的 annotation 即可对网络进行配置。此外还针对不使用任何 annotation的情况内置了一套默认配置。用户可以使用默认的子网,默认的IP分配策略,默认的分布式网关以及内嵌的负载均衡器,这些都不需要任何配置就可以默认启用。欢迎大家体验试用,多给我们提供反馈和意见。
关于Kube-OVN,近期灵雀云将主要着力于实现三大目标:第一,集中式网关的高可用,消灭整个架构中最后一个单点;第二,内嵌 DNS,去除 Kube-DNS/CoreDNS 的依赖,将整个数据平面用 OVN 进行统一;第三,NetworkPolicy实现。
长期来看,Kube-OVN未来将实现对DPDK 和 Hardware Offload 的支持,解决 Overlay 网络性能问题;将更多的 OVS 监控和链路追踪工具引入 Kubernetes;将OpenStack社区的网络功能向Kubernetes平移,打造更完整的网络体系。

Kubernetes 中的渐进式交付:蓝绿部署和金丝雀部署

灵雀云 发表了文章 • 0 个评论 • 360 次浏览 • 2019-05-07 15:27 • 来自相关话题

渐进式交付是持续交付的下一步, 它将新版本部署到用户的一个子集,并在将其滚动到全部用户之前对其正确性和性能进行评估, 如果不匹配某些关键指标,则进行回滚。 这里有一些有趣的项目,使得渐进式交付在 Kubernetes 中变得更简单。我 ...查看全部
渐进式交付是持续交付的下一步, 它将新版本部署到用户的一个子集,并在将其滚动到全部用户之前对其正确性和性能进行评估, 如果不匹配某些关键指标,则进行回滚。

这里有一些有趣的项目,使得渐进式交付在 Kubernetes 中变得更简单。我将使用一个 Jenkins X 示例项目 对它们之中的三个进行讨论:Shipper、Istio 以及 Flagger。

Shipper

shipper 是来自 booking.com 的一个项目, 它对 Kubernetes 进行了扩展,添加了复杂的部署策略和多集群编排(文档)。它支持从一个集群到多个集群的部署,允许多区域部署。
Shipper 通过一个 shipperctl 命令行进行安装。它增加不同集群的配置文件来进行管理。请注意这个与 GKE 上下文相关的问题。
Shipper 使用 Helm 包来部署,但是它们没有随着 Helm 一起安装,它们不会在 helm list 的输出显示。同样地,deployments 的版本必须是 apps/v1 , 否则 shipper 将不能编辑 deployment 来添加正确的标签和副本数量。
使用 shipper 部署都是与从旧版本(现有版本)过渡到新版本(竞争版本)相关。这是通过创建一个新的应用对象实现的, 它定义了部署需要通过的多个阶段。例如下面 3 个步骤过程:
Staging:部署新版本到一个 pod ,没有流量
50 / 50:部署新版本到 50% 的 pods,50% 的流量
Full on:部署新版本到全部的 pods,全部的流量


strategy:
steps:
- name: staging
capacity:
contender: 1
incumbent: 100
traffic:
contender: 0
incumbent: 100
- name: 50/50
capacity:
contender: 50
incumbent: 50
traffic:
contender: 50
incumbent: 50
- name: full on
capacity:
contender: 100
incumbent: 0
traffic:
contender: 100
incumbent: 0


如果发布的某个步骤没有将流量发送到 pods , 则可以使用 kubectl port-forward 访问它们,如:kubectl port-forward mypod 8080:8080, 这对于在用户看到新版本之前进行测试非常有用。
Shipper 支持多集群的概念,但是以相同的方式对待所有集群,仅使用区域并通过 capabilities (配置在集群对象中)进行筛选, 所有对一个应用对象来说,这里没有一个 dev, staging, prod 集群的选项。但是我们可以有两个应用对象:
myapp-staging 部署到 "staging" 区域
myapp 部署到其它区域
在 GKE 中,你可以轻松地配置多集群 ingress , 该入口将公开在多个集群中运行的服务,并从离你所在位置最近的集群提供服务。
局限性
Shipper 中的主要的局限性有:
Chart 限制:Chart 必须有一个部署对象。Deployment 的名称必须使用 {{.Release.Name}} 模板化。Deployment 对象应该有 apiVersion:apps/v1 。
基于 Pod 的流量切换:这里没有细粒度的流量路由,例如:发送 1% 的流量到新版本,它基于正在运行的 Pod 数量。
如果 Shipper 不工作了,新的 Pod 将获取不到流量。

Istio

Istio 不是一个部署工具,而是一个服务网格。然而,它很令人感兴趣,因为它已经变得非常流行,并且允许流量管理,例如,将一定比例的流量发送到不同的服务和其他高级网络。
在 GKE 中,只需在集群配置中选中复选框即可启用 Istio 。在其它集群中,可以通过 Helm 手动安装。
有了 Istio ,我们可以创建一个网关,通过 Ingress 网关处理所有外部流量,并创建虚拟服务来管理到我们服务的路由。为此,只需找到 ingress 网关的 ip 地址并为其配置通配符 DNS 。然后创建一个网关,通过 Ingress 网关路由所有外部流量。


apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: public-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"


Isito 不管理应用的生命周期,只管理网络。我们可以创建一个虚拟服务,为所有进入 ingress 网关的请求 向 pull request 或 master 分支中部署的服务发送 1% 的流量。


apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: croc-hunter-jenkinsx
namespace: jx-production
spec:
gateways:
- public-gateway.istio-system.svc.cluster.local
- mesh
hosts:
- croc-hunter.istio.example.org
http:
- route:
- destination:
host: croc-hunter-jenkinsx.jx-production.svc.cluster.local
port:
number: 80
weight: 99
- destination:
host: croc-hunter-jenkinsx.jx-staging.svc.cluster.local
port:
number: 80
weight: 1


Flagger
Flagger 是一个由 Weaveworks 赞助的使用了 Istio 的项目, 该项目使用 Prometheus 的指标进行自动化金丝雀发布和回滚。它超越了 Isito 提供了基于指标的自动化渐进式发布和回滚。
Flager 需要将 Istio与 Prometheus、Servicegraph 和某些系统的配置一起安装, 另外还要安装 Flager 控制器本身。它也提供了一个 Grfana 面板来监控部署进度。


pic1.jpg


部署 rollout 通过 Canary 对象定义, 它会生成主要的和金丝雀 Deployment 对象。编辑 Deployment 时,例如要使用新的镜像版本, Flagger 控制器将负载从 0% 切换到 50% ,每分钟增加 10% ,然后它将切换到新的 deployment 或者如果响应错误和请求持续时间等指标失败则进行回滚。

比较

此表总结了 Shipper 和 Flagger 在几个渐进式交付特性方面的优势和劣势。

pic2.jpg



pic3.jpg




综上所述,我看到了 Shipper 在多集群管理和简单性方面的价值,它不需要 Kubernetes 以外的任何东西,但是它有一些严重的局限性。
Flager 确实在自动部署和回滚以及对流量进行细粒度控制的过程中付出了额外的努力,它以更高的复杂性成本提供了所需的所有额外服务( Isito、Prometheus )。
这里可以查看 Shipper、Isito 和 Flager 的示例代码。

本文转自微信公众号 Jenkins

使用 Jenkins + Ansible 实现自动化部署 Nginx

灵雀云 发表了文章 • 0 个评论 • 426 次浏览 • 2019-04-29 11:00 • 来自相关话题

本文介绍如何使用 Jenkins + Ansible 实现对 Nginx 的自动化部署。最终达到的效果有如下几点: 只要你将 Nginx 的配置推送到 GitHub 中,Jenkins 就会自动执行部署,然后目标服务器的 Nginx 配置自动生效。 ...查看全部
本文介绍如何使用 Jenkins + Ansible 实现对 Nginx 的自动化部署。最终达到的效果有如下几点:
只要你将 Nginx 的配置推送到 GitHub 中,Jenkins 就会自动执行部署,然后目标服务器的 Nginx 配置自动生效。这个过程是幂等(idempotent)的,只要代码不变,执行多少遍,最终效果不变。
如果目标机器没有安装 Nginx,则会自动安装 Nginx。
自动设置服务器防火墙规则。

1. 实验环境介绍

本次实验使用 Docker Compose 搭建 Jenkins 及 Jenkins agent。使用 Vagrant 启动一台虚拟机,用于部署 Nginx。使用 Vagrant 是可选的,读者可以使用 VirtualBox 启动一个虚拟机。使用 Vagrant 完全是为了自动化搭建实验环境。
以下是整个实验环境的架构图

pic1.jpg



注意,图中的 5123 <-> 80 代表将宿主机的 5123 端口请求转发到虚拟机中的 80 端口。
Vagrant:虚拟机管理工具,通过它,我们可以使用文本来定义、管理虚拟机。
Ansible:自动化运维工具
Docker Compose:它是一个用于定义和运行多容器 Docker 应用程序的工具。可以使用 YAML 文件来配置应用程序的服务。

2. 启动实验环境

克隆代码并进入文件夹


git clone https://github.com/zacker330/jenkins-ansible-nginx.git
cd jenkins-ansible-nginx


构建 Jenkins agent 的镜像 需要自定义 Jenkins agent 镜像有两个原因:


docker build -f JenkinsSlaveAnsibleDockerfile -t jenkins-swarm-ansible .


本次实验,使用 Swarm 插件实现 Jenkins master 与 agent 之间的通信,所以 Jenkins agent 需要启动 swarm 客户端。
Jenkins agent 必须支持 Ansible。
启动 Jenkins master 及 Jenkins agent


docker-compose up -d


通过 http://localhost:8080 访问 Jenkins master,如果出现“解锁密码”页面,如下图,则执行命令 docker-compose logs jenkins 查看 Jenkins master 启动日志。将日志中的解锁密码输入到表单中。然后就一步步按提示安装即可。

pic2.jpg


安装 Jenkins 插件 本次实验需要安装以下插件:
Pipeline 2.6:https://plugins.jenkins.io/workflow-aggregator
Swarm 3.15:https://plugins.jenkins.io/swarm 用于 实现 Jenkins master 与 Jenkins agent 自动连接
Git 3.9.3:https://plugins.jenkins.io/git
配置 Jenkins master 不执行任务 进入页面:http://localhost:8080/computer/(master)/configure,如下图所示设置:

pic3.jpg


确认 Jenkins 安全配置有打开端口,以供 Jenkins agent 连接。我们设置 Jenkins master 开放的端口,端口可以是固定的 50000 ,也可以设置为随机。设置链接:http://localhost:8080/configureSecurity/。

pic4.jpg


启动目标机器,用于部署 Nginx 在命令行中执行以下命令:


vagrant up


注意,Vagrantfile 文件中的 config.vm.box 值必须改成你的 vagrant box 。
至此,实验环境已经搭建好了。接下来就可以新建 Jenkins 任务了。

3. 在 Jenkins 上创建部署任务

1、新建流水线任务

pic5.jpg


2、配置流水线 配置 Jenkins 任务从远程仓库拉取 Jenkinsfile,如下图所示:

pic5.jpg


除此之外,不需要其它配置了,是不是很简单?

4. 手工触发一次自动化构建

点击“立即构建”:

pic6.jpg


最终执行日志如下:

pic7.jpg


至此,部署已经完成。以后修改 Nginx 的配置,只需要修改代码,然后推送到远程仓库,就会自动化部署。不需要手工登录到目标机器手工修改了。
最后,我们可以通过访问 http://localhost:5123,如果出现如下页面说明部署成功:

pic8.jpg



5. 代码讲解

以上步骤并不能看出自动化部署真正做了什么。那是因为我们所有的逻辑都写在代码中。是的,可以说是 everything is code。
接下来我们介绍代码仓库。


% tree -L 2
├── JenkinsSlaveAnsibleDockerfile # Jenkins agent 镜像 Dockerfile
├── Jenkinsfile # 流水线逻辑
├── README.md
├── Vagrantfile # Vagrant 虚拟机定义文件
├── docker-compose.yml # Jenkins 实现环境
├── env-conf # 所有应用配置
│ └── dev # dev 环境的配置
├── deploy # Ansible 部署脚本所在文件夹
│ ├── playbook.yaml
│ └── roles
└── swarm-client.sh # Jenkins swarm 插件的客户端


5.1流水线逻辑

Jenkinsfile 文件用于描述整条流水线的逻辑。代码如下:


pipeline{
// 任务执行在具有 ansible 标签的 agent 上
agent { label "ansible"}
environment{
// 设置 Ansible 不检查 HOST_KEY
ANSIBLE_HOST_KEY_CHECKING = false
}
triggers {
pollSCM('H/1 [i] [/i] [i] [/i]')
}
stages{
stage("deploy nginx"){
steps{
sh "ansible-playbook -i env-conf/dev deploy/playbook.yaml"
}

}}}

environment 部分:用于定义流水线执行过程中的环境变量。
triggers 部分:用于定义流水线的触发机制。pollSCM 定义了每分钟判断一次代码是否有变化,如果有变化则自动执行流水线。
agent 部分:用于定义整条流水线的执行环境。
stages 部分:流水线的所有阶段,都被定义在这部分。
以上只是定义流水线是如何执行的,目前整条流水线只有一个 deploy nginx 阶段,并且只执行了一条 ansible-playbook 命令。但是它并没有告诉我们部署逻辑是怎么样的。

5.2 部署逻辑

所有的部署逻辑,包括 Nginx 的安装启动、配置的更新以及加载,都放在 Ansible 脚本中。对 Ansible 不熟的同学,可以在本文末尾找到介绍 Ansible 的文章。
整个部署逻辑的入口在 deploy/playbook.yaml,代码如下:


[list]
[*]hosts: "nginx"[/*]
[/list] become: true
roles:
# Nginx 的部署
- ansible-role-nginx
# 对防火墙的设置
- ansible-role-firewall



hosts:定义了 playbook 部署的目标主机分组名为 nginx。
roles:包含了两个执行具体部署动作的 role,至于 role 内部逻辑,不在本文讨论范围,有兴趣的同学阅读源码。

5.3 配置管理

谈到部署,就不得不谈配置管理。
回顾前文中流水线中执行的 shell 命令:ansible-playbook -i env-conf/dev deploy/playbook.yaml 我们通过 -i 参数指定部署时所使用的环境配置。通过这种方式实现环境配置与执行脚本的分离。这样带来以下几个好处:
新增环境时,只需要复制现有的环境,然后将里面的变量的值改成新环境的即可。比如,要对测试环境进行部署,只需要将 -i 参数值改成:env-conf/test。
对配置版本化控制。
本次实验中,各个环境的配置放在 env-conf 目录中,目前只有 dev 环境,以下是 env-conf/ 目录结构:


% cd env-conf/
% tree
└── dev
├── group_vars
│ └── nginx.yaml
├── host_vars
│ └── 192.168.52.10
└── hosts


hosts文件:Ansible 中通过“分组”来实现对主机的管理。hosts 文件内容如下:


[nginx]
192.168.52.10


host_vars 目录:用于存放主机级别的配置变量,本例中 192.168.52.10 是一个 YAML 格式文件。注意文件名是该主机的 IP。我们在文件中放主机相关的配置,比如 Ansible 连接主机时使用到的用户名和密码。
group_vars 目录:用于存放组级别的配置变量。比如 nginx.yaml 对应的就是

nginx



这个组的的配置变量。文件名与 hosts 中的组名对应

总结

到此,我们完整的自动化部署已经讲解完成。但是还遗留下一些问题:
本文只是安装了一个“空”的 Nginx,但是没有介绍 Nginx 真正配置。
目前主机的连接信息(SSH 密码)是明文写在` host_vars/192.168.52.10 `文件中的,存在安全风险。
没有介绍如何当 Java 应用部署时,如何自动更新 Nginx 的配置。
本文属于使用 Jenkins + Ansible 实现自动化部署的入门文章,笔者将根据读者的反馈决定是否写续集。
如果觉得本文讲的 Jenkins 流水线逻辑部分不够过瘾,可以考虑入手一本最近才出版的《Jenkins 2.x实践指南》。长按下图进行扫码购买。

本文转载自:微信公众号jenkins

关于 Jenkins master 共享 JENKINS_HOME 目录的实验

灵雀云 发表了文章 • 1 个评论 • 322 次浏览 • 2019-04-24 14:45 • 来自相关话题

Jenkins master 的高可用是个老大难的问题。和很多人一样,笔者也想过两个 Jenkins master 共享同一个 JENKINS_HOME 的方案。了解 Jenkins 原理的人,都会觉得这个方案不可行。但是真的不可行吗? 由于工作原 ...查看全部
Jenkins master 的高可用是个老大难的问题。和很多人一样,笔者也想过两个 Jenkins master 共享同一个 JENKINS_HOME 的方案。了解 Jenkins 原理的人,都会觉得这个方案不可行。但是真的不可行吗?
由于工作原因,笔者需要亲自验证以上猜想。

JENKINS_HOME 介绍

Jenkins 所有状态数据都存放文件系统的目录中,这个目录被称为 JENKINS_HOME 目录。
实验环境介绍
笔者通过 Docker compose 启动两个独立的 Jenkins master,分别为 jenkins-a 和 jenkins-b。它们共用同一个 JENKINS_HOME 目录。相应的代码仓库的链接放在文章底部。
将代码克隆到本地后,进入仓库,执行 docker-compose up -d 即可启动实验环境。启动完成,在浏览器中输入 http://localhost:7088 可访问 jenkins-a,jenkins-b 的地址是 http://localhost:7089 。但是你会发现它们启动后的界面显示是不一样的。

pic1.jpg


jenkins-b 的界面如下图所示:

pic2.jpg



而 jenkins-a 的界面如下图所示:

pic3.jpg



这时,将 jenkins-a 日志中的解锁密码(Unlock password)输入到 jenkins-b 的页面中,会得到报错信息:


ERROR: The password entered is incorrect, please check the file for the correct password


这时,再次 jenkins-b 日志中的解锁密码(Unlock password)输入到表单中即可进入下一步。接下来就是按照提示一步步完成了。在 jenkins-b 安装步骤的最后一步,我们设置了管理员的用户名密码:admin/admin。然后就算完成任务了。
然后我们再在 jenkins-a 使用 admin/admin 进行登录,登录是报错的:用户密码不正确。
接下来,执行 `docker-compose restart jenkins-a `docker-compose restart jenkins-a 命令重启 jenkins-a。再次使用 admin/admin 就可以登录成功了。
当两个 Jenkins 启动完成后,接下来开始做实验。

实验1:创建任务

在 jenkins-a 创建任务 x,刷新 jenkins-b 的页面,jenkins-b 上会不会显示出任务 x ?
结果:jenkins-b 不会出现任务 x。重启 jenkins-b 后,任务 x 出现在任务列表中。
实验2:任务结果可见性
jenkins-a 上任务执行,jenkins-b 上能否看到任务执行结果?
jenkins-a 执行任务 x,并且执行成功。刷新 jenkins-b 看不到任何执行记录。重启 jenkins-b 后,可看到执行记录。

实验3:两 master 同时执行同一任务

分别在两个 Jenkins master 上(几乎)开始同一个任务 x。其中一个任务的 build number 会更新,但是另一个不会。
其中 jenkins-a 任务 x 的 build number 会升到 2,而 jenkins-b 保持的是 1。这时,单独执行 jenkins-b 的任务 x,日志会出现错误:


jenkins-b_1 | WARNING: A new build could not be created in job x
jenkins-b_1 | java.lang.IllegalStateException: JENKINS-23152: /var/jenkins_home/jobs/x/builds/2 already existed; will not overwrite with x #2



实验4:编辑任务

jenkins-a 上设置任务 x 定时执行,刷新 jenkins-b 页面,任务 x 中并没有定时执行的设置。重启 jenkins-b 后,任务 x 更新。

实验5:定时任务的结果是什么?

如果 jenkins-a 和 jenkins-b 两个任务均为定时任务,而且都生效了。它们运行结果是什么的呢?
看到的现象是,两个任务都会按时执行,但是只有一个任务能将运行结果写入到磁盘中。界面如下图:

另,从日志中,可以确认 jenkins-a 和 jenkins-b 确实按时执行了。如下图日志中,看出 jenkins-a 定时执行 #6 次构建时报错,因为 jenkins-b 已经执行过 #6 次构建了:

pic4.jpg


小结
可以确认的是,当两个 Jenkins 进程共用同一个 JENKINS_HOME 目录时,其中一个 Jenkins 进程更新了 JENKINS_HOME 的内容,另一个是不会实时更新的。所以,同时启动两个 Jenkins master 共用同一个 JENKINS_HOME 的方案是不可行的。我们不能在 jenkins-a 挂了后,直接将流量切到 jenkins-b。因为 jenkins-b 必须重启。
最后结论:多个 Jenkins master 共享同一个 JENKINS_HOME 的方案是无法使用 Jenkins master 的高可用。

附录

Jenkins standby 实验环境:https://github.com/zacker330/jenkins-standby-experiment


本文转自微信公众号:Jenkins

Gartner容器市场指南中国语境:容器成为新常态,本地厂商在选择中占据优势

灵雀云 发表了文章 • 0 个评论 • 391 次浏览 • 2019-04-23 10:30 • 来自相关话题

在2019年2月“ China Summary Translation: 'Market Guide for Container Management Software'”的报告中,Gartner认为,在中国市场,容器技术的使用是近期的热点。本地厂商由于能够贴 ...查看全部
在2019年2月“ China Summary Translation: 'Market Guide for Container Management Software'”的报告中,Gartner认为,在中国市场,容器技术的使用是近期的热点。本地厂商由于能够贴近客户实际需求,而在选择中占据优势,例如阿里云、灵雀云等中国本地厂商。

关于容器的未来

为了解容器当前和未来的状态,国外研究分析师 Tom Smith近期收集了30余位积极使用容器技术的IT高管的见解。大家一致的观点认为:容器继续成熟,采用率上升,复杂度下降,Serverless兴起。

成熟

我们期待与AI,AR和VR一起使用更多的技术,随着人们使用AI轻松地开发、部署和管理容器,容器将会被大量采用和创新,会有更多的计算能力来更快地完成任务。

预计会有越来越多的人采用,容器在企业中已经被高度渗透。CNCF表示容器已经有60%-70%的部署,但是运行在Kubernetes上的计算工作负载占比要低得多。因此,Kubernetes还有巨大的增长机会。

越来越多的公司将会发现容器的好处,不仅因为可以使用容器来构建新的应用程序,而且真正开始重构现有的应用程序,并有效利用底层平台提供的水平可伸缩性等功能。企业从谈论云和容器转向在生产中使用容器,容器的使用成为主流。与此同时,围绕安全性和合规性的思考也将改变。

容器在容器编排和调度环境中提供了更好的状态管理,以及更好的执行时间,以支持无服务器的用例。

容器使用率将继续增长。推动新技术快速部署的能力不容忽视,容器的快速部署、管理和短生命周期的快速发展将推动新功能的开发。公司不得不跟上容器技术环境的快速变化,安全、编排和开发等领域都充斥着破坏的机会!

未来,容器将作为企业应用部署和管理的关键基础设施。随着技术的成熟,它会变得更加稳定、标准化和便携。希望成熟的容器技术能够带来更多的用例,诸如应用程序智能、性能表现等。

就像所有优秀的技术一样,容器变得“Boring”。解决方案提供商在包装和分销方面做得更好,将会有更多关于如何在容器周围加入信任,确保不是恶意以及防止臃肿的知识。

围绕Kubernetes的编排正在标准化,这将加速开源和商业生态系统的发展,并推动工具开发。还将看到,随着云供应商提供一致的产品,这个堆栈也将成熟起来。甚至微软、亚马逊和IBM都支持Kubernetes。五年后,不运行Kubernetes和Docker的企业将成为少数。

清晰

容器将像任何优秀的技术一样继续消失于背景中,工具使得利用技术变得更容易,容器的部署和使用将有更大的简化。

容器是一种在本地或云中构建类云应用程序的机制,容器变得更容易处理和扩展,没有单点故障,也没有单一供应商。

容器使事情变得不那么复杂,成为新的常态。开发人员希望在容器中构建所有新应用程序,人们需要改变构建的方式,首先分析应用程序,以便在发布时,可以监控从构建到生产到建设和扩展的全流程。

1)今天Kubernetes不是以app开发者为核心角色而构建的。需要让Kubernetes更易于开发人员快速启动和运行。

2)我们看到了在Knative和OpenFast等Kubernetes之上构建抽象的趋势,在Kubernetes之上部署了无服务器功能,抽象了the knobs of Kubernetes。随着越来越多的项目成熟并以原生方式运行,更多开发人员可以更轻松地使用该技术。只有28%的应用程序在容器上运行,它还处于早期阶段,我们有机会让这项技术变得更加平易近人和实用。

容器仍然太复杂。如果比较一下现在开发人员所需要的知识量,就会发现间接需要的能力比五六年前要复杂得多。五年前,如果想构建一个Python应用程序,有一些众所周知的标准。现在开发人员不仅要学习如何生成Docker镜像,还要学习如何在编排系统上部署,如何将配置传递到容器,以及所有关于安全性的细节。最终,开发人员将不必处理容器,因为更高级别的抽象是构建在容器之上的。

无服务器和FaaS已经在路上

在开发人员体验和开发速度方面,容器的价值得到坚实地证明。然而,在容器安全性方面肯定会有所改进。未来,我们设想一个更安全的容器,运行沙箱在Nano VMs,就像Kata容器或AWS Firecracker一样。Serverless函数将代替传统API应用程序的大量工作。

在运营框架和如何描述自动化之间有两个巨大的机会。Kubernetes已经成为标准。快速脚本工作。运营者有可能在非常强大的环境中实现这一目标。我们一直在寻找可以使用的80/20工具。当使用标准化的YAML语言进入Kubernetes时,标准化的应用程序自动化将为我们提供一个功能强大的地方,在这里我们可以看到一个真正的服务目录。无服务器FaaS也非常令人兴奋,因为它允许您只专注于应用程序的逻辑。

容器使每个人都可以轻松实现无服务器。没有必要依赖虚拟机,虚拟机正在消失。它更容易转向Serverless,容器随着时间的推移会有所改善。将有更多选项可以在容器内运行更多应用程序。他们将继续改变,改善,变得更加稳定,更快地从失败中恢复,同时省下大笔资金。

无服务器和FaaS已经在路上。更高级别的抽象有助于在系统中获得更小的组件。随着颗粒越来越小,必须弄清楚如何管理和知道在哪里运行,这时候 Istio作为一种服务网格产品,可以帮助跟踪所有组件。

1)在去年的KubeCon上,“Serverless”计算的概念是指向容器创新未来的一个重要话题和热点——即构建和部署几乎任何类型的应用程序,而无需配置或管理运行这些应用程序的服务器。此外,用户将根据使用模式付费,只支付所消耗的计算时间,不运行时不收费。

2) 容器最终将取代虚拟机。与vm相比,容器提供了显著的优势,如降低了部署成本、显著降低了启动性能、减少了机器占用空间,且具备易用性。随着越来越多的公司和IT组织使用容器,将会出现应用程序从虚拟机到容器的大规模迁移。

3) 容器的采用幅度将远远超出仅以Docker为主要容器类型的情况。竞争产品将被更广泛地接受和使用。Docker作为市场领导者,已经偏离了标准容器技术的开发,而是更加专注于开发和营销一个全面的应用程序开发平台。导致其他容器产品的普及和使用的大幅增长。

参考资料:

Gartner “China Summary Translation: 'Market Guide for Container Management Software'“,by Kevin Ji & Dennis Smith, Published on 18 February 2019, ID: G00382483.
2.The Future of Containers

https://dzone.com/articles/the ... ers-1
3.Cloud 2019 Predictions (Part 5)
https://dzone.com/articles/clo ... art-5

DockOne微信分享(二零五):基于OVN的Kubernetes网络架构解析

灵雀云 发表了文章 • 0 个评论 • 878 次浏览 • 2019-03-28 17:42 • 来自相关话题

【编者的话】Kubernetes经过了几年的发展,存在着很多的网络方案。然而网络虚拟化在Kubernetes出现前就一直在发展,其中基于OpenVswitch的方案在OpenStack中已经有了很成熟的方案。其中OVN作为OVS的控制器提供了构建分布式虚拟网络 ...查看全部
【编者的话】Kubernetes经过了几年的发展,存在着很多的网络方案。然而网络虚拟化在Kubernetes出现前就一直在发展,其中基于OpenVswitch的方案在OpenStack中已经有了很成熟的方案。其中OVN作为OVS的控制器提供了构建分布式虚拟网络的完整控制平面,并已经成为了最新的OpenStack网络标准。我们将OVN的网络架构和Kubernetes的容器平台进行结合,将业界成熟的网络架构引入Kubernetes大幅增强现有容器网络的能力。
#Kubernetes网络的局限性
Kubernetes提出了很多网络概念,很多开源项目都有自己的实现。然而由于各个网络功能都是在不同的项目中实现,功能和性能也各有千秋,缺乏统一的解决方案,在使用过程中经常会陷入到底该用哪个的抉择中。同时CNI、DNS、Service的实现又在不同的项目,一旦网络出现问题,排查也会在多个组件间游走,是一个十分痛苦的过程。

尽管Kubernetes提出了很多网络的概念,但是在真实应用中很多人会有这样的感觉:网络这块还是很薄弱,很多功能缺乏,方案也不够灵活。尤其是和搞传统基础设施网络的人沟通会发现,在他们眼里,Kubernetes的网络还很初级。我们熟悉的Kubernetes网络是CNI、Service、DNS、Ingress、Network Policy这样的模式。而做IaaS的视角完全不同,他们每次提起是VPC、Subnet、VNIC、 Floating IP,在此之上有DHCP,路由控制,安全组,QoS,负载均衡,域名解析这样的基础网络功能。

从IaaS的视角来看,Kubernetes的网络功能确实比较单薄。经常碰到来自传统网络部门的挑战,诸如子网划分VLAN隔离,集群内外网络打通,容器NAT设置,带宽动态调节等等。现有的开源网络方案很难完美支持,最简单的一个例子,比如提及容器的固定IP功能通常就要上升到意识形态斗争的层面去讨论。这本质上还是Kubernetes的网络功能不足,模型也不够灵活导致的。从更高层面来说,Kubernetes中抽象类一层网络虚拟化的内容,然而网络虚拟化或者SDN并不是Kubernetes带来的新东西,相关技术已经发展很久。尤其是在IaaS领域里已经有着比较成熟且完善的一整套网络方案。

传统网络部门的人都会问,为什么不用OVS来做网络方案,很多需求用只要容器网络接入OVS网络,剩下事情网络部门自己就知道怎么去做了,都不用我们实现太多额外的功能。也有很多人向我们推荐了OVN,用这个能很方便地实现这些功能。也正由此我们开始去关注OVS/OVN这种之前主要应用于OpenStack生态系统的网络工具。下面我就来介绍一下OVS和OVN。
#OVS和OVN网络方案的能力
网络的概念比较晦涩一些,但是好在大家都对Docker和Kubernetes比较熟悉,可以做个类比。如果说Docker是对单机计算资源的虚拟化,那么OVS就是对单机网络进行虚拟化的一个工具。它最基本的功能是实现了虚拟交换机,可以把虚拟网卡和虚拟交换机的端口连接,这样一个交换机下的多个网卡网络就打通了,类似Linux Bridge的功能。在此之上,OVS很重要的一点就是支持OpenFlow,这是一种可编程的流量控制语言,可以方便我们以编程的方式对流量进行控制,例如转发,拒绝,更改包信息,NAT,QoS 等等。此外OVS还支持多中网络流量监控的协议,方便我们可视化监控并跟踪整个虚拟网络的流量情况。

但是,OVS只是一个单机软件,它并没有集群的信息,自己无法了解整个集群的虚拟网络状况,也就无法只通过自己来构建集群规模的虚拟网络。这就好比是单机的Docker,而OVN就相当于是OVS的Kubernetes,它提供了一个集中式的OVS控制器。这样可以从集群角度对整个网络设施进行编排。同时OVN也是新版OpenStack中Neutron的后端实现,基本可以认为未来的OpenStack网络都是通过OVN来进行控制的。
01.jpeg

上图是一个OVN的架构,从下往上看:

ovs-vswitchd和ovsdb-server可以理解为单机的Docker负责单机虚拟网络的真实操作。

ovn-controller类似于kubelet,负责和中心控制节点通信获取整个集群的网络信息,并更新本机的流量规则。

Southbound DB类似于etcd(不太准确),存储集群视角下的逻辑规则。

Northbound DB类似apiserver,提供了一组高层次的网络抽象,这样在真正创建网络资源时无需关心负责的逻辑规则,只需要通过Northoboud DB的接口创建对应实体即可。

CMS可以理解为OpenStacke或者Kubernetes这样的云平台,而 CMS Plugin是云平台和OVN对接的部分。

下面我们具体介绍一下OVN提供的网络抽象,这样大家就会有比较清晰的认知了。

Logical_Switch最基础的分布式虚拟交换机,这样可以将多台机器上的容器组织在一个二层网络下,看上去就好像所有容器接在一台交换机上。之后可以在上面增加诸如ACL、LB、QoS、DNS、VLAN等等二层功能。

Logical_Router虚拟路由器,提供了交换机之间的路由,虚拟网络和外部网络连接,之后可以在路由器层面增加DHCP、NAT、Gateway等路由相关的功能。

Loadbalancer,L2和L3的Loadbalancer,可以类比公有云上的内部LB和外部LB的功能。

ACL基于L2到L4的所有控制信息进行管控的一组DSL,可以十分灵活,例如:outport == “port1” && ip4 && tcp && tcp.src >= 10000 && tcp.dst <= 1000。

QoS,可以基于和ACL同样的DSL进行带宽的控制。

NAT,同时提供DNAT和SNAT的控制方便内外网络通信。

DNS,内置的分布式DNS,可以在本机直接返回内部DNS的请求。

Gateway控制内部和外部之间的集中式通信。

了解了这些,大家应该就能发现,Kubernetes目前的网络从功能层面其实只是OVN支持的一个子集,基本上所有Kubernetes的网络需求都能在OVN中找到映射关系,我们简单来看下他们之间的映射。
#OVN和Kubernetes的结合
Switch/Router -> Kubernetes基本的东西向互通容器网络。这块OVN的能力其实是大大超出,毕竟OVN的这套模型是针对多租户进行设计的,而Kubernetes现在只需要一个简单的二层网络。

Loadbalancer → ClusterIP以及Loadbalancer类型的Service,可以取代kube-proxy的功能,OVN本身也可以实现云上ELB的功能。

ACL -> Networkpolicy本质上更灵活因为可以从L2控制到L4并且DSL也支持更多的语法规则。

DNS -> 可以取代Kube-DNS/CoreDNS,同时由于OVN实现的是分布式DNS,整体的健壮性会比现在的Kubernetes方案要好。

可以看到Kubernetes的CNI、kube-proxy、Kube-DNS、NetworkPolicy、Service等等概念OVN都有对应的方案,而且在功能或者稳定性上还有增强。更不要说还有QoS、NAT、Gateway等等现在Kubernetes中没有的概念。可以看到如果能把IaaS领域的网络能力往Kubernetes平移,我们还有很多的提升空间。
#Kubernetes网络未来增强的方向
最后来说说我认为的未来Kubernetes网络可能的增强方向。
##Kubernetes网络功能和IaaS网络功能打平
现在的Kubernetes网络模型很类似之前公有云上的经典网络,所有用户大二层打通,通过安全策略控制访问。我们现在也都知道公有云多租户不能这么做VPC肯定是标配。因此未来Kubernetes网络可能也会向着多租户方向前进,在VPC的基础上有更多的路由控制、NAT控制、带宽控制、浮动IP等等现在IaaS上很常见的功能。
##性能
现有的开源方案其实主要还是依赖原有的Linux网络栈,没有针对性的优化。理论上容器的密度比传统虚拟化高,网络压力会更大。OVS现在有DPDK等Kernel bypass的DataPath,绕过Linux内核栈来实现低延迟和大吞吐网络。未来随着容器的密度越来越大,我认为会出现这种针对容器架构专门优化的网络方案,而不是依旧依赖Linux网络栈。
##监控和问题排查
现有的网络问题排查十分困难,如果所有的数据平面都由一个项目完成,比如OVN,那么学习成本和排障都会容易一些。此外OVS社区已经有了很多成熟的监控,追踪,排障方案,随着容器的使用场景变多,我认为外围的工具也需要能够很好的支撑这种模式的网络运维问题。
#Q&A
Q:OVS方案与基于三层交换机方案对比,各有什么优缺点?

A:OVS最大的优点在于可编程,灵活性比较好。虚拟网络不用手动插网线,而且有OpenFlow加持,可以实现一些普通交换机无法实现的流量控制。物理交换机的主要有点就是性能好,而且比较稳定,不容易出问题。



Q:OVN不支持ECMP,貌似现在还是active-standby机制,你们怎么解决Gateway瓶颈问题?

A:有几种方式:1. Gateway用DPDK这样的高速DataPath;2. 多Gateway,用策略路不同的IP段走不同Gateway,可以分担流量;3. 不使用OVN概念的Gateway,自己做一些手脚从每台宿主机直接出去,也是分担流量降低单点的风险。



Q:OVN-Kubernetes好像只支持每个部署节点一个虚拟网络网段。如何避免IP池浪费和不均衡?

A:这个其实是这个项目实现的网络模型的一个局限性。在我们的实现里是以namespace为粒度划分子网,可以对每个namespace进行控制,情况会好很多。



Q:OVN如果有不同的Chassis,但是相同IP就会造成网络异常(比如物理机,VM装上OVN,注册到远端后,被重建,后面又注册到远端,但是Chassis已经改变),会导致大量节点Geneve网络异常。你们怎么解决这个问题?

A:暂时没碰到这个问题,但是我们在实现的一个原则就是尽可能保证所有的操作都是幂等的。向这种可能需要在重连前做一个检查,判断是否有过期的数据需要清理,再连接,或者复用旧的连接信息去连接。



Q:如何debug OVN逻辑拓扑是否配置有问题?

A:目前debug确实很多情况只能靠眼看,也可以使用ovn-trace这个工具可以打印数据包在逻辑流里的链路来排查。



Q:OVS跟Calico等有啥区别?

A:Calico主要依赖Linux的路由功能做网络打通,OVS是在软件交换机层面实现网络打通,并提供了更丰富的网络功能。



Q:OVS的封包支持有STT和Geneve,你们选用哪种,为什么?

A:其实还支持VXLAN,我们选的Geneve。原因比较简单,Geneve是第一个OVN支持的封包协议,而且看了一些评测,据说在搞内核开启UDP Checksum的情况下性能会比VXLAN要好一些。



Q:OVS如何实现固定容器IP?

A:这个其实OVS有对应的设置可以给每个端口设定IP和MACE,这样网卡启动时配置相同的信息就可以了,难点其实是如何控制OVN来分配 IP,感觉这个话题可以再开一场分享来讨论了。



Q:可以简单介绍下你们准备开源的网络功能吗?

A:每个namespace和一个logical_switch绑定,支持子网分配,支持固定 IP,支持 QoS,支持NetworkPolicy,内置的LB,内置的DNS,大致就是把OVN的概念映射到Kubernetes。



Q:想了解一下,如果采用OVN,是不是意味着使用OpenStack平台和Kubernetes网络可以直接互通?完成业务在虚拟机和Pod之间的全新负载方式?

A:是这样的,如果涉及的合理可以做到容器和VM使用同一个底层网络基础设施,VM和容器之间可以IP直达,所有的ACL、LB都是打通的。



Q:直接把OpenShift OVS抽出来做Kubernetes的网络插件和灵雀云做的这个区别在哪?

A:功能上有很多是相同的,因为底层都是OVS。如果关注Roadmap会发现OpenShift之后也会采用OVS的模式。从架构的角度来看,现在openshift-multitenant的实现很类似Neutron之前那一套,各种Agent,之后会统一到OVN。另一方面OpenShift的网络插件绑定的太死了,所以我们决定还是自己抽出来,顺便能实现我们的一些特殊功能,比如固定IP,子网共享,以及一些网关控制层面的功能。



Q:请问Geneve和VXLAN的区别有哪些?

A:Geneve可以理解为下一代VXLAN,VXLAN相对VLAN来说头部更长可以支持更多的VLAN,但是由于是固定长度的封装头,不能任意加控制信息。Geneve采用变长的封装头,可以加很多自定义的控制信息,方便之后做更复杂的网络管控。



Q:Docker CNM也支持固定IP,和你说的固定IP是一回事吗?另外,基于OVS建立的网络是CNI还是CNM的呢?

A:基于CNI,因为我们依赖Kubernetes的模型。不过话说回来我很喜欢Docker CNM那套模型,比CNI要实用很多。固定IP其实只是个功能,各种模型下都可以实现,效果就是可以给Pod指定IP启动,Workload下的多个Pod实用的是一组固定的地址。



Q:目前你们对企业的解决方案里会默认采用这种网络模式么?

A:这个方案是我们这几年需求和碰到坑的一个积累吧,现在还不会直接给企业用,我们还需要一些功能的开发和测试,但是之后Overlay的场景这个肯定是主推了,主要是取代原来的Flannel VXLAN网络。



Q:你了解Contiv网络方案吗,和贵公司的实现有哪些区别?

A:Contiv是思科做的,也是OVS实现的,不过它的实现比较早,自己手动实现了整个控制平面,可以认为自己写了个跟OVN类似的东西来控制 OVS。不过看它最近已经很少更新了。用OVN能用很少的代码就实现基本相同的功能。Contiv有个比较独特的功能就是支持BGP的网络间通信,这个是OVN暂时不支持的。



以上内容根据2019年3月26日晚微信群分享内容整理。分享人刘梦馨,灵雀云高级工程师。2014年加入灵雀云容器团队,长期参与容器调度平台和容器网络架构的产品研发和技术架构演进,参与自研容器网络和容器应用网关。目前主要专注于容器网络功能的拓展和架构优化。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesd,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

DockOne微信分享(一九七):etcd 集群运维实践

灵雀云 发表了文章 • 1 个评论 • 1417 次浏览 • 2019-01-04 19:21 • 来自相关话题

【编者的话】etcd 是 Kubernetes 集群的数据核心,最严重的情况是,当 etcd 出问题彻底无法恢复的时候,解决问题的办法可能只有重新搭建一个环境。因此围绕 etcd 相关的运维知识就比较重要,etcd 可以容器化部署,也可以在宿主机自行搭建,以下 ...查看全部
【编者的话】etcd 是 Kubernetes 集群的数据核心,最严重的情况是,当 etcd 出问题彻底无法恢复的时候,解决问题的办法可能只有重新搭建一个环境。因此围绕 etcd 相关的运维知识就比较重要,etcd 可以容器化部署,也可以在宿主机自行搭建,以下内容是通用的。
# 集群的备份和恢复
## 添加备份
#!/bin/bash
IP=123.123.123.123
BACKUP_DIR=/alauda/etcd_bak/
mkdir -p $BACKUP_DIR
export ETCDCTL_API=3
etcdctl --endpoints=http://$IP:2379 snapshot save $BACKUP/snap-$(date +%Y%m%d%H%M).db

# 备份一个节点的数据就可以恢复,实践中,为了防止定时任务配置的节点异常没有生成备份,建议多加几个

## 恢复集群
#!/bin/bash

# 使用 etcdctl snapshot restore 生成各个节点的数据

# 比较关键的变量是
# --data-dir 需要是实际 etcd 运行时的数据目录
# --name --initial-advertise-peer-urls 需要用各个节点的配置
# --initial-cluster initial-cluster-token 需要和原集群一致

ETCD_1=10.1.0.5
ETCD_2=10.1.0.6
ETCD_3=10.1.0.7

for i in ETCD_1 ETCD_2 ETCD_3
do

export ETCDCTL_API=3
etcdctl snapshot restore snapshot.db \
--data-dir=/var/lib/etcd \
--name $i \
--initial-cluster ${ETCD_1}=http://${ETCD_1}:2380,${ETCD_2}=http://${ETCD_2}:2380,${ETCD_3}=http://${ETCD_3}:2380 \
--initial-cluster-token k8s_etcd_token \
--initial-advertise-peer-urls http://$i:2380 && \
mv /var/lib/etcd/ etcd_$i

done

# 把 etcd_10.1.0.5 复制到 10.1.0.5节点,覆盖/var/lib/etcd(同--data-dir路径)
# 其他节点依次类推

## 用 etcd 自动创建的 SnapDb 恢复
#!/bin/bash 
export ETCDCTL_API=3
etcdctl snapshot restore snapshot.db \
--skip-hash-check \
--data-dir=/var/lib/etcd \
--name 10.1.0.5 \
--initial-cluster 10.1.0.5=http://10.1.0.5:2380,10.1.0.6=http://10.1.0.6:2380,10.1.0.7=http://10.1.0.7:2380 \
--initial-cluster-token k8s_etcd_token \
--initial-advertise-peer-urls http://10.1.0.5:2380

# 也是所有节点都需要生成自己的数据目录,参考上一条
# 和上一条命令唯一的差别是多了 --skip-hash-check (跳过完整性校验)
# 这种方式不能确保 100% 可恢复,建议还是自己加备份
# 通常恢复后需要做一下数据压缩和碎片整理,可参考相应章节

## 踩过的坑
[ 3.0.14 版 etcd restore 功能不可用 ] https://github.com/etcd-io/etcd/issues/7533

使用更新的 etcd 即可。

总结:恢复就是要拿 DB 去把 etcd 的数据生成一份,用同一个节点的,可以保证除了 restore 时候指定的参数外,所有数据都一样。这就是用一份 DB,操作三次(或者5次)的原因。
# 集群的扩容——从 1 到 3
##执行添加
#!/bin/bash
export ETCDCTL_API=2
etcdctl --endpoints=http://10.1.0.6:2379 member add 10.1.0.6 http://10.1.0.6:2380
etcdctl --endpoints=http://10.1.0.7:2379 member add 10.1.0.7 http://10.1.0.7:2380

# ETCD_NAME="etcd_10.1.0.6"
# ETCD_INITIAL_CLUSTER="10.1.0.6=http://10.1.0.6:2380,10.1.0.5=http://10.1.0.5:2380"
# ETCD_INITIAL_CLUSTER_STATE="existing"

##准备添加的节点 etcd 参数配置
#!/bin/bash
/usr/local/bin/etcd
--data-dir=/data.etcd
--name 10.1.0.6
--initial-advertise-peer-urls http://10.1.0.6:2380
--listen-peer-urls http://10.1.0.6:2380
--advertise-client-urls http://10.1.0.6:2379
--listen-client-urls http://10.1.0.6:2379
--initial-cluster 10.1.0.6=http://10.1.0.6:2380,10.1.0.5=http://10.1.0.5:2380
--initial-cluster-state exsiting
--initial-cluster-token k8s_etcd_token

# --initial-cluster 集群所有节点的 name=ip:peer_url
# --initial-cluster-state exsiting 告诉 etcd 自己归属一个已存在的集群,不要自立门户

##踩过的坑
从 1 到 3 期间,会经过集群是两节点的状态,这时候可能集群的表现就像挂了,endpoint status 这些命令都不能用,所以我们需要用 member add 先把集群扩到三节点,然后再依次启动 etcd 实例,这样做就能确保 etcd 就是健康的。

从 3 到更多,其实还是 member add 啦,就放心搞吧。
# 集群加证书
## 生成证书
curl -s -L -o /usr/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
curl -s -L -o /usr/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x /usr/bin/{cfssl,cfssljson}
cd /etc/kubernetes/pki/etcd

#  cat ca-config.json
{
"signing": {
"default": {
"expiry": "100000h"
},
"profiles": {
"server": {
"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "100000h"
},
"client": {
"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "100000h"
}
}
}
}

#  cat ca-csr.json
{
"CN": "etcd",
"key": {
"algo": "rsa",
"size": 4096
},
"names": [
{
"C": "CN",
"L": "Beijing",
"O": "Alauda",
"OU": "PaaS",
"ST": "Beijing"
}
]
}

#  cat server-csr.json
{
"CN": "etcd-server",
"hosts": [
"localhost",
"0.0.0.0",
"127.0.0.1",
"所有master 节点ip ",
"所有master 节点ip ",
"所有master 节点ip "
],
"key": {
"algo": "rsa",
"size": 4096
},
"names": [
{
"C": "CN",
"L": "Beijing",
"O": "Alauda",
"OU": "PaaS",
"ST": "Beijing"
}
]
}

# cat client-csr.json

{
"CN": "etcd-client",
"hosts": [
""
],
"key": {
"algo": "rsa",
"size": 4096
},
"names": [
{
"C": "CN",
"L": "Beijing",
"O": "Alauda",
"OU": "PaaS",
"ST": "Beijing"
}
]
}

cd /etc/kubernetes/pki/etcd

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server-csr.json | cfssljson -bare server

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client-csr.json | cfssljson -bare client

参考链接:https://lihaoquan.me/2017/3/29/etcd-https-setup.html
## 首先更新节点的peer-urls
export ETCDCTL_API=3
etcdctl --endpoints=http://x.x.x.x:2379 member list
# 1111111111 ..........
# 2222222222 ..........
# 3333333333 ..........
etcdctl --endpoints=http://172.30.0.123:2379 member update 1111111111 --peer-urls=https://x.x.x.x:2380
# 执行三次把三个节点的peer-urls都改成https

# 修改配置
#  vim /etc/kubernetes/main*/etcd.yaml

# etcd启动命令部分修改 http 为 https,启动状态改成 existing
- --advertise-client-urls=https://x.x.x.x:2379
- --initial-advertise-peer-urls=https://x.x.x.x:2380
- --initial-cluster=xxx=https://x.x.x.x:2380,xxx=https://x.x.x.x:2380,xxx=https://x.x.x.x:2380
- --listen-client-urls=https://x.x.x.x:2379
- --listen-peer-urls=https://x.x.x.x:2380
- --initial-cluster-state=existing

# etcd 启动命令部分插入
- --cert-file=/etc/kubernetes/pki/etcd/server.pem
- --key-file=/etc/kubernetes/pki/etcd/server-key.pem
- --peer-cert-file=/etc/kubernetes/pki/etcd/server.pem
- --peer-key-file=/etc/kubernetes/pki/etcd/server-key.pem
- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem
- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem
- --peer-client-cert-auth=true
- --client-cert-auth=true

# 检索hostPath在其后插入
- hostPath:
path: /etc/kubernetes/pki/etcd
type: DirectoryOrCreate
name: etcd-certs

# 检索mountPath在其后插入
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs

#  vim /etc/kubernetes/main*/kube-apiserver.yaml
# apiserver 启动部分插入,修改 http 为https
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.pem
- --etcd-certfile=/etc/kubernetes/pki/etcd/client.pem
- --etcd-keyfile=/etc/kubernetes/pki/etcd/client-key.pem
- --etcd-servers=https://x.x.x.x:2379,https://x.x.x.x:2379,https://x.x.x.x:2379

总结下就是,先准备一套证书。然后修改 etcd 内部通信地址为https,这时候etcd日志会报错(可以忽略),然后用etcd --带证书的参数启动,把所有链接etcd的地方都用上证书,即可。
## 遇到的坑
[ etcd 加证书后,apiserver 的健康检查还是 http 请求,etcd 会一直刷日志 ] https://github.com/etcd-io/etcd/issues/9285
2018-02-06 12:41:06.905234 I | embed: rejected connection from "127.0.0.1:35574" (error "EOF", ServerName "")

解决办法:直接去掉 apiserver 的健康检查,或者把默认的检查命令换成 curl(apiserver 的镜像里应该没有 curl,如果是刚需的话自己重新 build 一下吧)
# 集群升级
已经是 v3 的的集群不需要太多的配置,保留数据目录,替换镜像(或者二进制)即可;

v2 到 v3 的升级需要一个 merge 的操作,我并没有实际的实践过,也不太推荐这样做。
# 集群状态检查
其实上述所有步骤都需要这些命令的辅助——
#!/bin/bash
# 如果证书的话,去掉--cert --key --cacert 即可
# --endpoints= 需要写了几个节点的url,endpoint status就输出几条信息

export ETCDCTL_API=3

etcdctl \
--endpoints=https://x.x.x.x:2379 \
--cert=/etc/kubernetes/pki/etcd/client.pem \
--key=/etc/kubernetes/pki/etcd/client-key.pem \
--cacert=/etc/kubernetes/pki/etcd/ca.pem \
endpoint status -w table

etcdctl --endpoints=xxxx endpoint health

etcdctl --endpoints=xxxx member list

kubectl get cs

# 数据操作(删除、压缩、碎片整理)
## 删除
ETCDCTL_API=2 etcdctl rm --recursive            # v2 的 api 可以这样删除一个“目录”
ETCDCTL_API=3 etcdctl --endpoints=xxx del /xxxxx --prefix # v3 的版本

# 带证书的话,参考上一条添加 --cert --key --cacert 即可

遇到的坑:在一个客户环境里发现 Kubernetes 集群里的 “事件” 超级多,就是 kubectl describe xxx 看到的 events 部分信息,数据太大导致 etcd 跑的很累,我们就用这样的方式删掉没用的这些数据。
## 碎片整理
ETCDCTL_API=3 etcdctl --endpoints=xx:xx,xx:xx,xx:xx defrag
ETCDCTL_API=3 etcdctl --endpoints=xx:xx,xx:xx,xx:xx endpoint status # 看数据量

## 压缩
ETCDCTL_API=3 etcdctl --endpoints=xx:xx,xx:xx,xx:xx compact

# 这个在只有 K8s 用的 etcd 集群里作用不太大,可能具体场景我没遇到
# 可参考这个文档
# https://www.cnblogs.com/davygeek/p/8524477.html
# 不过跑一下不碍事

etcd --auto-compaction-retention=1

# 添加这个参数让 etcd 运行时自己去做压缩

# 常见问题

  1. etcd 对时间很依赖,所以集群里的节点时间一定要同步
  2. 磁盘空间不足,如果磁盘是被 etcd 自己吃完了,就需要考虑压缩和删数据啦
  3. 加证书后所有请求就都要带证书了,要不会提示 context deadline exceeded
  4. 做各个操作时 etcd 启动参数里标明节点状态的要小心,否则需要重新做一遍前面的步骤很麻烦

# 日志收集
etcd 的日志暂时只支持 syslog 和 stdout 两种——https://github.com/etcd-io/etcd/issues/7936

etcd 的日志在排查故障时很有用,如果我们用宿主机来部署 etcd,日志可以通过 systemd 检索到,但 kubeadm 方式启动的 etcd 在容器重启后就会丢失所有历史。我们可以用以下的方案来做——
##shell 的重定向
etcd --xxxx --xxxx   >  /var/log/etcd.log 
# 配合 logratate 来做日志切割
# 将日志通过 volume 挂载到宿主机

##supervisor
supervisor 从容器刚开始流行时,就是保持服务持续运行很有效的工具。
##sidecar 容器(后续我在 GitHub 上补充一个例子,github.com/jing2uo)
Sidecar 可以简单理解为一个 Pod 里有多个容器(比如 kubedns)他们彼此可以看到对方的进程,因此我们可以用传统的 strace 来捕捉 etcd 进程的输出,然后在 Sidecar 这个容器里和 shell 重定向一样操作。
strace  -e trace=write -s 200 -f -p 1

#Kubeadm 1.13 部署的集群
最近我们测试 Kubernetes 1.13 集群时发现了一些有趣的改变,诈一看我们上面的命令就没法用了——

https://kubernetes.io/docs/setup/independent/ha-topology/

区分了 Stacked etcd topology 和 External etcd topology,官方的链接了这个图很形象——
B70D000C-83B2-4B9C-A612-53925238D0DA.png

这种模式下的 etcd 集群,最明显的差别是容器内 etcd 的initial-cluster 启动参数只有自己的 IP,会有点懵挂了我这该怎么去恢复。其实基本原理没有变,Kubeadm 藏了个 ConfigMap,启动参数被放在了这里——
kubectl get cm  etcdcfg -n kube-system -o yaml

    etcd:
local:
serverCertSANs:
- "192.168.8.21"
peerCertSANs:
- "192.168.8.21"
extraArgs:
initial-cluster: 192.168.8.21=https://192.168.8.21:2380,192.168.8.22=https://192.168.8.22:2380,192.168.8.20=https://192.168.8.20:2380
initial-cluster-state: new
name: 192.168.8.21
listen-peer-urls: https://192.168.8.21:2380
listen-client-urls: https://192.168.8.21:2379
advertise-client-urls: https://192.168.8.21:2379
initial-advertise-peer-urls: https://192.168.8.21:2380

#Q&A
Q:请问 etcd 监控和告警如何做的?告警项都有哪些?

A:告警要看用的什么监控吧,和 Kubernetes 配套比较常见的是普罗米修思和 Grafana 了。告警项我没有具体配过,可以关注的点是:endpoint status -w table 里可以看到数据量,endpoints health 看到健康状态,还有内存使用这些,具体可以参考普罗米修思的 exporter 是怎么做的。



Q:使用 Kubeadm 部署高可用集群是不是相当于先部署三个独立的单点 Master,最后靠 etcd 添加节点操作把数据打通?

A:不是,Kubeadm 部署会在最开始就先建一个 etcd 集群,apiserver 启动之前就需要准备好 etcd,否则 apiserver 起不了,集群之间就没法通信。可以尝试手动搭一下集群,不用 Kubeadm,一个个把组件开起来,之后对Kubernetes的组件关系会理解更好的。



Q:etcd 跨机房高可用如何保证呢?管理 etcd 有好的 UI 工具推荐么?

A:etcd 对时间和网络要求很高,所以跨机房的网络不好的话性能很差,光在那边选请输入链接描述举去了。我分享忘了提一个 etcd 的 mirror,可以去参考下做法。跨机房的话,我觉得高速网络是个前提吧,不过还没做过。UI 工具没找过,都是命令行操作来着。



Q:Kubeadm 启动的集群内 etcd节 点,kubectl 操作 etcd 的备份恢复有尝试过吗?

A:没有用 kubectl 去处理过 etcd 的备份恢复。etcd 的恢复依赖用 SnapDb 生成数据目录,把 etcd 进程丢进容器里,类似的操作避免不了,还有启动的状态需要修改。kubeadm 启动的 etcd 可以通过 kubectl 查询和 exec,但是数据操作应该不可以,比如恢复 etcd ing 时,无法连接 etcd,kubectl 还怎么工作?



Q:kubeadm-ha 启动 3 个 Master,有 3 个 etcd 节点,怎么跟集群外的 3 个 etcd 做集群,做成 3 Master 6 etcd?

A:可以参考文档里的扩容部分,只要保证 etcd 的参数正确,即使一个集群一部分容器化,一部分宿主机,都是可以的(当然不建议这么做)。可以先用 kubeadm 搭一个集群,然后用扩容的方式把其他三个节点加进来,或者在 kubeadm 操作之前,先搭一个 etcd 集群。然后 kubeadm 调用它就可以。



Q:有没有试过 Kubeadm 的滚动升级,etcd 版本变更,各 Master 机分别重启,数据同步是否有异常等等?

A:做过。Kubeadm 的滚动升级公司内部有从 1.7 一步步升级到 1.11、1.12 的文档,或多或少有一点小坑,不过今天主题是 etcd 所以没提这部分。各个 Master 分别重启后数据的一致我们测试时没问题,还有比较极端的是直接把三 Master 停机一天,再启动后也能恢复。



以上内容根据2019年1月3日晚微信群分享内容整理。分享人郭靖,灵雀云运维开发工程师,有大规模集群运维经验,对自动化迷之热衷,精通Ansible,HashiCorp工具集,容器和Kubernetes鼓捣了三年,喜欢用Python和Go写小工具,DevOps推崇及践行者,近期关注和期待OpsMop。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesd,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

2015 年 Container 生态系统报告

灵雀云 发表了文章 • 0 个评论 • 4953 次浏览 • 2015-10-29 15:49 • 来自相关话题

编者按:成立于2013年,Docker迅速进入主流。超过8亿的Docker容器已经被pull到了公共的Docker Hub中,到底该如何使用Docker容器呢?目前在生产环境中的使用情况又是如何?2015年5月,O'Reilly Media和Ruxit针对这些 ...查看全部
编者按:成立于2013年,Docker迅速进入主流。超过8亿的Docker容器已经被pull到了公共的Docker Hub中,到底该如何使用Docker容器呢?目前在生产环境中的使用情况又是如何?2015年5月,O'Reilly Media和Ruxit针对这些问题,面向O'Reilly社区的用户,共同发起了一项调查。

docker-report-aa212d7caf6912a2a33115f4ad672da9.png


2015年5月,O'Reilly Media和Ruxit针对这些问题,面向O'Reilly社区的用户,共同发起了一项调查。

numbers.png


这138个调查对象(样本量偏小,请谨慎看待)来自软件行业,咨询行业,出版,媒体,教育,云服务,硬件,零售,政府等行业,50%以上来自500人以下的公司,13%来自10000以上的公司。

其中的一些关键信息:

  • 93%以上的调查对象都已经或计划在开发、测试或生产环境中采用Container技术;


future_adoption.png


  • 其中78%的调查对象选择了Docker;


adopt_env.png


  • 85%以上的调查对象在开发过程中使用了Container,65%左右的调查对象在测试环境中使用Container;


IaaS.png


  • 45%以上的调查对象选择的基础设施是AWS EC2,45%左右选择Ubuntu;


5why.png


  • 加快和简化开发是使用Container的最重要原因;


future.png


  • 未来的6-12个月里,53%的调查对象计划在生产环境中采用Container;

8_challenge.png


  • 56%的人认为Container技术在生产环境中使用的最大挑战,在于编排方面缺乏可靠的解决方案,46%的人认为在监控方面,40%的人认为在自动化方面。

Docker品玩会第二期:Docker加速开发和运维

灵雀云 发表了文章 • 3 个评论 • 2806 次浏览 • 2015-10-27 11:08 • 来自相关话题

♪ 愉悦的心情和舌头能让学习效率增加189.7% ♬ ♪ Docker是个严肃的事儿 ♩ ♬ 但学习Docker的过程可以很好玩儿 ♩ ♪ 灵雀云就是让Docker线下沙龙很好玩儿 ♬ ♫ 带大家轻轻松松认识Do ...查看全部
♪ 愉悦的心情和舌头能让学习效率增加189.7% ♬
♪ Docker是个严肃的事儿 ♩
♬ 但学习Docker的过程可以很好玩儿 ♩
♪ 灵雀云就是让Docker线下沙龙很好玩儿 ♬
♫ 带大家轻轻松松认识Docker ♪
alauda_beauty.jpg


继第一期有趣又有料的Docker品玩会之后,你一定等了很久!在这里告诉大家,第二期报名已经启动!

时间:11月1日 13 : 00
地点:北京市海淀区中关村大街11号 中关村e世界A座B2 P2联合创业办公社

点击此处报名

12:30-13:00 签到

13:00-13:45 Docker与容器云基础入门 灵雀云CEO 左玥

讲师介绍:左玥,2006年加入微软的Windows部门, 2009年,他加入了刚成立不久的Azure部门负责研发了多个Azure的关键组件,并成为了网络虚拟化组的开发经理。他设计的Azure vSwitch,获得了美国专利,成为了Azure SDN架构的基础。2012年底,他接管了轻量级虚拟化团队,负责Azure和Windows的Container技术的研发,成为微软Container技术第一人。在微软的8年期间,他获得了六次晋升,成为首席研发经理。他还拥有六项国际云计算领域专利。

13:45-14:00 Docker监控 云智慧资深技术顾问 王旭

分享内容:解密监控宝Docker监控实现原理:什么是Docker监控?监控原理是什么?如何部署?

讲师介绍:王旭,在IT领域有多年的工作经验,曾经分别就职于华为, 蓝汛等公司。在网络通讯项目管理和产品解决方案售前,CDN,云计算解决方案交付等领域拥有丰富经验。

14:00-14:20 茶歇

14:20-15:05 VMware容器云平台架构解析 VMware中国研发中心云原生应用资深架构师 张海宁

分享内容:本演讲将介绍容器云平台架构,涉及开发栈、生产栈和DevOps等方面,听众可了解到如何加速开发微服务架构应用,如何解决容器隔离性和安全方面的不足,以及如何进行容器应用的权限管理等方面的前沿技术。

讲师介绍:张海宁,VMware中国研发中心云原生应用资深架构师,CloudFoundry中国社区最早的技术布道师之一。 10年前在SUN公司时,已经开始研究和推广Solaris的容器技术应用。2012年加入VMware中国研发中心,先后负责开源平台Cloud Foundry、大数据虚拟化、软件定义存储等领域的技术布道和解决方案推广。目前着重关注云原生应用的研发工作,内容包括Container、PaaS、IaaS等方面。

15:05-15:50 利用容器技术快速打造 MVP 灵雀云高级软件工程师 刘梦馨

分享内容:都在说用容器运行一切,但是都没见过真实的例子,来一个实际的例子看看到底如何在容器平台中运行一个真实的服务,利用容器平台快速迭代部署,不断线上验证更新服务,两天内完成一个 demo 产品的实践经验。

讲师介绍:刘梦馨,灵雀云高级软件工程师,前阿里系统工程师,专注于容器、虚拟化技术,对一切新技术感兴趣,个人博客 oilbeater.com。

15:50-16:30 自由讨论

Docker Hub彻底放弃Registry V1,灵雀云镜像市场国内率先支持V2

灵雀云 发表了文章 • 2 个评论 • 3842 次浏览 • 2015-10-26 16:07 • 来自相关话题

10月17日,在给用户的Newsletter中,Docker 正式发表声明表示 Docker Hub 将不再支持版本1.5和更早版本的客户端: 截至2015年11月19日,版本 1.5 和更早版本的 Docker 客户端将无法将 i ...查看全部
10月17日,在给用户的Newsletter中,Docker 正式发表声明表示 Docker Hub 将不再支持版本1.5和更早版本的客户端

  • 截至2015年11月19日,版本 1.5 和更早版本的 Docker 客户端将无法将 image push 到 Docker Hub,仍然能够 pull image;
  • 截至2015年12月7日,版本 1.5 和更早版本的客户端 pull image 也将被禁用,只支持 1.6 或更高版本。


registry_alauda.jpg


在这之前,随着 Docker 官方宣布 v1 的 registry 不再进行开发,灵雀云的小伙伴们就积极的投入了 v2 版 registry 工作的对接中,经过几个月的相关开发调试以及小规模的内测后,灵雀云的镜像市场服务已经正式敞开怀抱拥抱 v2 版本的 registry, 来为大家提供更加优质的服务。

下面我们就来看下,新版本的 registry 会带来哪些体验提升:

安全

v1 版的 registry 一直存在着安全漏洞,存在着镜像造假的隐患,由于 v1 无法对镜像的内容和正确性进行校验,从 v1 pull 镜像会有着 pull 到伪造镜像的风险,可以类比一下之前下载到带木马的 Xcode 的事件。v2 版提供了服务器短内容校验的功能,可以杜绝这种客户端伪造镜像的欺骗方法,并且 1.6 之后版本的 docker 也可以利用这种方式在本地进行校验,保证了上传和下载到镜像的一致。这也是 docker 官方主推高版本 docker 以及 v2 registry 的原因。我们也建议大家升级到高版本的 docker 来使用更安全的 v2 服务,不过目前我们可能是国内第一个全面支持 v2 的公有镜像市场 :)
性能

新版本的 registry 在性能方面有了大幅提升,并且支持并行 pull,以后再 pull 镜像就是这个样子了,可以感受一下 pull 镜像飞起是一种怎样的体验了。

1.pic_.jpg


灵雀云的 CaaS 平台也已经全面对接了 v2 registry,相应服务的性能也会得到提升,可以进一步加速用户持续化集成和部署的速度,我们之后也会持续优化这部分的性能。

兼容性

Docker Hub 已经宣布即将停止对 v1 的支持,很快就无法通过 1.6 之前的版本从 Docker Hub 上传下载任何镜像。灵雀云没有那么任性,还会对各个版本的 docker 提供服务支持,并对不同的客户端做了透明的兼容。由于 docker 客户端的限制,只有 1.6 之后的版本可以使用 v2 registry,不过 1.6 版本之前的小伙伴也无需担心,我们在后端服务做了大量的工作,使得同一个 registry 地址兼容两套 registry,并会做两者之间的镜像实时同步,不管你使用哪个版本的 docker 或者升级或者降级版本都可以无感知的使用到对应版本registry,并找到自己对应的镜像。

相关的技术文章可以点击这里查看。当然我们还是推荐大家升级到更高版本的 docker,这样即能够获得更好的镜像市场使用体验,也可以享用到 docker 新版本的其他特性,何乐而不为呢?

DockOne技术分享(二十六):Docker Registry V1 to V2

oilbeater 发表了文章 • 1 个评论 • 12989 次浏览 • 2015-10-17 17:30 • 来自相关话题

【编者的话】Docker Registry 2.0版本在安全性和性能上做了诸多优化,并重新设计了镜像的存储的格式。我们将详细介绍Docker Registry V1与V2的区别,并在此基础上分享了灵雀云的实时同步迁移实践。 1. 相关概念 ...查看全部
【编者的话】Docker Registry 2.0版本在安全性和性能上做了诸多优化,并重新设计了镜像的存储的格式。我们将详细介绍Docker Registry V1与V2的区别,并在此基础上分享了灵雀云的实时同步迁移实践。
1. 相关概念
首先讲一下Registry相关的概念。大家对Docker应该比较了解了,就是容器技术使用了Cgroups、Namespaces、Union FS等一系列机制来保证隔离。但我们实际使用中可能并不会直接接触这些技术,更直接使用的是pull image、run image,再进阶一点会使用build image和push image。我们日常的使用主要还是围绕image展开的。

而image和Registry的关系可以想象成自己机器上的源码和远端SVN或者Git服务的关系。Registry是一个几种存放image并对外提供上传下载以及一系列API的服务。可以很容易和本地源代码以及远端Git服务的关系相对应。

然后再说一下Hub,很多人不太清楚Docker Registry和Docker Hub到底有什么区别。其实依然可以利用Git的概念来类比,Registry 和Hub的关系可以类比Git和GitHub的关系。Git是一个服务端程序,GitHub是全球最大的同性社交网站,那么 GitHub比Git多了些什么?首先是UI,然后就是用户鉴权,public-private partnerships各种组织机构服务,评论issue管理,Search webhook等工具的集合。Hub和Registry的关系也是类似的,大家在 DockerHub上看到的界面和各种功能就是Hub的一部分,而且处于商业的考量Hub是不开源的。
2. V1 Python Registry
下面来说一下V1的Registry以及为啥Docker官方不给他饭吃了。V1的项目地址在 https://github.com/docker/docker-registry 已经小半年没有更新了,很早就是废弃态。但说实话还是比较稳定的,API设计也比健全,使用和扩展也还算方便。至少在我使用的过程中没有碰到太多的坑,反而是V2把我坑的够呛。
1.jpg

这是一张V1 Registry存储镜像的目录树。可以看到最上面是两层结构images和repositories,关注一下images里面的内容,最叶子节点有一个layer和Ancestry。layer就是这一层文件系统的tar包,Ancestry中存储的是它父亲层ID,可以看到layer之间在V1是通过一个链表的形式进行关系组织的。大家学过数据结构应该知道,链表的特点是插入删除方便,随机读取性能差,而layer之间显然是没有插入删除这个需求的,所以这个设计在我看来是有些问题的。这个组织结构的另一个缺点就是pull layer只能单线程,下完一层才能知道父亲是谁,就只能按序下载,没有发挥多核的优势。

当然这只是一个很小的问题,最主要的问题出现在那一长串Image ID 上。不知道大家有没有想过这个ID是如何生成的。这个ID是在本地build时随机生成的,随机生成的,随机生成的。随机生成的意思就是ID和内容完全没有关系,同样的layer再次build生成的ID就是不同的。

这会造成很大的安全隐患。docker pull和push判断image layer存在都是根据这个ID所以在双向都存在造假的可能。恶意用户可以伪造ID直接 push 上去,这样以后再有别人同样的ID就上传不上去了,因为Registry会认为layer已经存在了,但内容其实是不一样的。这个倒是还好,因为碰撞概率没那么高。

另一方面恶意的 Registry 也可以根据ID伪造内容,反正你只校验ID也不知道是不是这个内容和ID是关联的。类似于前一阵xcode的木马,下下来一看名字是xcode就以为是真的。安全性的因素也是Docker官方想要重新设计Registry的主要理由。

同样这种随机ID也会造成push性能下降,因为可能重复内容被多次 push。其他原因还有V1是Python实现的与Golang的理念不符,尽管Golang写的也不咋地。

另一个比较重要的原因是tag的问题。Docker很多方面都学Git 也学得很成功,偏偏版本控制的东西学残了,最明显的就是tag。tag 的问题在于Docker images的tag是可变的,你没有办法通过tag来确定唯一的版本,这一点在latest上尤为明显。因为latest是可以自己定义的,如果你在 Dockerfile里from latest很可能你过一阵build出来的镜像和之前不一样了,这个问题还很难发现。其他tag也存在同样的问题,我们现在的做法是用代码 commit ID做tag,每次都是按照commit ID作部署,尽量远离latest。
3. V2 Golang Distribution
上面说了V1的一堆不好,下面来看一下V2。

项目地址是:https://github.com/docker/distribution,实话说目前的开发进度很缓慢,和Docker Engine的热度完全不是一个量级,很多基本的功能都很缺失。
2.jpg

他的存储目录树十分复杂,我就简单列一下,可以看到最顶层还是两个,只不过images改名为Blobs,最叶子节点变为了data,他的目录是一串长ID,需要注意的是这个ID和Image ID是没有关系的。由于一些兼容性的历史问题,Docker并没有取消Image ID的概念,这里的一长串ID是把data的内容经过sha256做hash得出的结果。

这样就很好的解决了之前V1所存在的随机ID的大问题。

通过这种方式ID和内容是一一对应的了,同样的内容总会生成同样的ID,而且这种key->value的形式也很利于缓存,为未来的优化也埋下伏笔。这样就可以变原来的链表顺序查找为数组的随机读取,这也是V2 pull可以并行的一个基础,并行pull大概也是客户端唯一能感受到的比较大的和V1的区别。大家可能发现这里没有Ancestry这种链表结构了,V2中会有一个新的文件,叫manifest,它会记录改镜像所有layer的信息。manifest中还有大量其他信息,感兴趣的可以看一下 spec https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md。

这一长串ID在Docker叫digest, 从Docker的想法来看他是希望通过digest来取代tag达到可以指定唯一版本的目的。但是由于Image ID历史遗留的问题太长远了,现在看改起来十分艰辛。还需要注意的是 这一串ID是服务器端计算得出的,这样也杜绝了客户端造假ID的行为,当然这个做法也带来其他的问题。

其他的新的地方还包括新的auth方式,notification机制,以及全新的API。这里全新的API的意思是和之前完全不兼容,如果现有系统想迁移需要考虑一下这点。然后他是go实现的,我自己的感觉吞吐量有两三倍的提升。

再说一下V2存在的问题。

首先是API的缺失,delete、search这种基本功能都没有,而且tag和digest的关系很难找,而所有API又都是基于digest的。然后push和pull的速度有了新瓶颈。之前V1是把文件系统做tar包V2变成了gzip包,只要一push镜像CPU就会打满,而且压缩解压都是单核的,如果是内网话很有可能push和pull都变慢了。最后一点就是V1和V2镜像格式不兼容,不兼容,不兼容。
4. V1 V2 共存及同步实践
我们做的就是让V1和V2两个版本共存,并且镜像在两个Registry中都存在。Docker目前1.6之后支持V2,但我们的用户从0.9到1.8都有使用,Docker自己给我们挖了坑然后提提裤子就跑了,我们需要让用户无感知。而且用户可能会升级降级版本,不能发现镜像不在了。

先说一下共存,这一点相对容易,需要一个域名支持两套Registry,由于V1和V2的后接url是不一样的,所以可以很容易的通过Nginx做一个转发。官方也给出了一个示例:https://github.com/docker/distribution/blob/master/docs/nginx.md,但是读的时候一定要仔细,并根据具体情况进行调整,直接不看拿过来就是坑。
3.jpg

我们搭好的一个网络拓扑大概是这样的,之所以把网络拓扑放出来是希望大家想一下,这种大文件传输的服务在网络拓扑上要考虑什么?最主要的就是超时,你需要考虑每条链路的超时设置。其次是body size ,buffer size之类的参数保证链路的通畅。

然后再讲一下V1和V2之间的同步,由于镜像是不兼容的,肯定要涉及到同步迁移。大思路上有两个方案,第一读懂两种镜像的格式,直接做文件级别的更新,把V1的文件翻译成V2,这个华为的马道长在做大家有兴趣可以找他。另一种是利用Docker 1.6之后的版本可以和两个Registry进行通信,我们从一个registry pull再push到另一个Registry才用Docker Engin 的Runtime来解决,我们当时合计了一下觉得第二个省劲就用了第二个方案。

之后我们发现官方也给给出了个迁移工具,大家也可以看一下: https://github.com/docker/migrator,思路基本也是一样的,但问题这个工具有很多缺陷。首先他是一个单一的shell脚本,只能做离线同步,我们想要的是实时,因为我们每天量还是很大,离线很可能不收敛,而且用户体验也不好.其次它只能做V1到V2的单向同步,扩展性,和性能也不好,并且也没有相关的统计监控功能,只是个玩具产品。

接下来看一下我们做的:
4.jpg

思路还是用1.6之后的特性。在两个Registry上分别加hook实时获取tag更新信息发送到消息队列,消息队列再把消息发送到分布式的worker集群上进行一个同步,这个过程中每一步都落数据库,方便我们之后的监控和错误恢复。

把上面的数据流反过来再画一遍,就是我们线上的工作流了。这里有许多细节的问题大家可以之后想一下。首先是如果我做双向同步,那么我的同步也是一个push事件,这样会再触发一个同步这样一直循环下去该怎么办?还有就是一个埋的比较深的问题,也是latest最容易引起,两次间隔很近的latest push,很有可能后一个latest同步先完成,第一个同步后完成,这样就会同步的结果就是一个旧的版本,如何避免这种情况?其实就是一个事务的问题,如何确定那些操作可以并行,那些必须串行,这两个问题大家可以想一想。
5. 同步海外镜像实践
我们有了这套同步工具其实可以干很多别的事情。理论上任意一个Registry只要我能爬到它的更新就可以同步过来。我们同步官方library和一些其他库现在也是基于这套工具,但是这里会碰到一个更头疼的问题,就是网络。墙的问题大家都是中国人。而且我们这种同步都是最新的镜像,mirror也帮不了什么忙,用VPN的话这种走流量的很快就会被封IP。最后我们发现七牛有个海外上传加速可以在海外很快的上传文件到国内,我们就写了一个七牛的driver来进行这种同步。架构图就变成这个样子了,相当于部署了两套同步节点,一套在海外负责Dockerhub同步到qiniu,一套在国内负责qiniu同步到我们自己。
5.jpg

这种方案解决了很多问题,但是问题依然很多。首先是只有上传文件走加速节点,所有的控制流比如mv rename等对象存储操作还是走国内,这样这一段依然高延时容易被墙,失败频率依然比较高。另一反面我们也好几次碰到了七牛服务不稳定的情况。所以我们虽说是按照一个实时同步进行设计的,现在的结果是大部分情况是分钟级别同步,故障时可能会到天级别,不过我们认为大部分情况下还OK。如果当初设计就是天同步或者周同步很可能最后就同步不完了,所以给大家的启发就是不妨把目标设置高点,反正目标再低也是完不成的。

额外想说的就是,尽管这套东西我做出来了,但我觉得这个东西是不该存在的。不知道大家有没有听过有个博士的论文写得是如何在奶粉中检测三氯氰胺,不能说这件事情没意义,但是这件事情有意义很可悲,我觉得我现在做的也是类似的事情。我们都把太多时间和精力花在了毫无意义的和网络作斗争上,我希望有一天可以把这套海外同步的机制干掉,或者只是做个简单的国内镜像站而已,而不是大费周折的可以画一个很花哨的图来讲。
Q&A
Q:想问下,那你们的layer数据是不是要存两份,V1、V2各一份?

A:是要分开存两份的,因为他们的格式其实都是不一样的一个是tar包一个是gzip包,但内容一样。



Q:为啥tar变为gzip会耗费CPU和网络,不就是不同的压缩格式么?

A:网络其实是节省的,但是压缩是很耗CPU的tar其实并不太消耗。



Q:V1如果做些优化,一次获取Ancestry,然后并行下载layer,是不是也可以提高吞吐量么?

A:理论上是这样的,我看1.8的代码在pull v1也一次会拿到所有的Image ID但是并没有去并行下载,估计Docker自己把这块放弃了吧。



Q:请问,您提到的利用Registry的hook,来获取image更新的信息,指的是利用Registry的notification API?

A:V2是这样,V1是自己在Registry那里做了个hook。



Q:请问关于镜像删除的问题,V2的删除感觉坑很多,如何删除,还有,如果同一个镜像名称及版本但是内容并不同的镜像重复push,有没有办法检测,以及同步?

A:我们用的AWS对象存储,存储还比较便宜,所以没太关注,GitHub上有一个v2 gc的项目可以删除无用镜像,官方叫着做停机gc,叫了好久了,目前还没实现,只能自己造轮子了;重复push和刚才提到的乱序类似,我们会保证这种情况是串行的。



Q:V2这么不成熟,眼下上还是不上,push到V2 registry的image能不能查询?

A:我们当初的想法是照着Docker这么任性的态度,没准1.8就不支持V1了,所以就赶紧调研用上了。查询没有直接的API,我们很多tar没有的API都是自己造轮子造出来的。



Q:V2.1后,Registry提供一个叫catalog API,具有一定image搜索的功能,但还不够完美?

A:catalog会遍历整个存储消耗还是蛮大的,可以通过catalog做离线,然后notify做实时更新来实现search的一个索引。



Q:请问,灵雀云的registry backend storage是什么类型,文件系统么,理由是什么?

A:直接AWS在中国的s3,目前官方支持的最好的,不用自己造轮子,就酱紫。



Q:针对V2的auth方式,有没有什么好的建议,对于平台类的开发?

A:我的建议是使用token auth的方式,虽然复杂一步到位,可以做一些复杂的权限认证。类似的项目还是:https://github.com/SUSE/Portus ,不过建议每次Docker版本更新都跟着测一遍。



Q:有没有类似docker_auth的项目?

A:https://github.com/SUSE/Portus 一个开源的 auth server,但是比较坑的是Docker Engine老变,一升级可能就不一样,我们自己的auth server也改了好几次。



Q:由V1升级到V2,为什么非得把旧仓库上的镜像迁移到新的V2这么折腾,直接两个版本并存一段时间不行吗,新上传用新的V2的url,如果要回退旧版本旧库上镜像url也还有吧,一段时间后旧库就能退役了?

A:因为我们有用户的push而用户很多还在用旧版本,也有用户发现新版本不合适回滚的,如果只顾一头用户一变就发现镜像没了。



Q:alauda云push的时候443端口拒绝连接怎么办?

A:这个应该不会吧……,可以先下再联系复现一下,我们的两个版本Registry都是走HTTPS的。



Q:V2好像仍然没有解决Registry最大的痛:单点,你们怎么对待这个问题的?

A:Registry一直都是可以水平扩展的,只是一个HTTP的服务器是无状态的不存在单点问题。



Q:企业私有云场景下用多个Registry实现HA该如何选择后端存储,京东的Speedy是否合适?

A:Registry有Swift的driver私有云可以考虑,或者根据已有的情况选择自己的存储自己写个driver也是可以的,写的难度其实不大,七牛那个一个下午就能写出来。要求不高的话还是不难的。京东的不是太了解,我觉得主要看现有的技术框架和产品选一个易上手的就行。把Registry水平扩展挂载lb后面就好了。



Q:V1已经被官方deprecated,V2仍然缺少一些基本的管理API,请问现在私有Registry升级到V2是否还为时过早?

A:看需求了吧,我觉得要是稳定考虑deprecated也没啥影响,V2的很多好处确实在私有云表现不出来,反而会有一些表现不如V1的地方。



Q:用七牛海外加速之前用哪种方案的?

A:我先答一下这个吧,这个挺有意思的,我们发现一个tcp的拥塞算法是用于卫星通信的,卫星这种高延迟高丢包的拥塞算法貌似还蛮合适国外往国内传数据。



===========================
以上内容根据2015年10月16日晚微信群分享内容整理。分享人刘梦馨,灵雀云软件工程师,前阿里系统保障部系统工程师,容器技术爱好者,个人博客:oilbeater.com。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesx,进群参与,您有想听的话题可以给我们留言。

基于Docker快速搭建(支持Docker管理的)单节点Mesos+Marathon集群开发环境

xianlubird 发表了文章 • 1 个评论 • 5209 次浏览 • 2015-09-28 17:38 • 来自相关话题

【编者的话】在进行Mesos+Marathon开发的时候,最麻烦的事情莫过于基础环境的搭建。Mesos还好有mesosphere帮忙build好了,但是你想使用最新版还得自己make,在install的时候,还会遇到fetch code.google.com的 ...查看全部
【编者的话】在进行Mesos+Marathon开发的时候,最麻烦的事情莫过于基础环境的搭建。Mesos还好有mesosphere帮忙build好了,但是你想使用最新版还得自己make,在install的时候,还会遇到fetch code.google.com的网络问题,折腾环境的过程早已经把一腔热血泼灭了。因此为了快速搭建Mesos+Marathon单节点开发环境,使用Docker弄了一套。

Mesos是集群资源管理系统,Marathon是运行在Mesos之上的集群计算架构。将Mesos和Marathon打包到Docker镜像中,开发者便可以在本机上快速搭建Mesos/Marathon集群,进行学习和测试。

本镜像非常简单。Docker容器运行在Ubuntu主机之上,Mesos和Marathon运行在该容器之中。具体来讲,Docker容器中运行了一个Mesos Master和一个Mesos Slave,以及Marathon和ZooKeeper。

由于很多同学搭建Mesos是为了管理Docker镜像,而且Mesos也原生支持了Docker,因此使用marathon管理Docker是一件非常amazing的事情。但是奈何默认marathon配置没有加上对Docker的支持,因此你需要自己去配置一下启动参数和两个文件,这样才能使用Marathon管理Docker容器。

已经有同学实现了Docker搭建单节点的Mesos+Marathon环境,但是经过试用,发现只能运行非容器化的应用,对于Docker是不支持的,在log里面一直报错。
 None of the enabled containerizers (mesos) could create a container for the provided TaskInfo/ExecutorInfo message

Google得知是因为没有在mesos-slave上配置对于Docker的支持,因此需要增加配置。

首先需要在mesos-slave这台机器上安装Docker,这个就不详细说了,安装Docker已经是必备的技能。

为了让Mesos知道我们要使用Docker作为虚拟化工具,需要增加两个文件如下:
echo 'docker,mesos' > /etc/mesos-slave/containerizers
echo '5mins' > /etc/mesos-slave/execu`enter code here`tor_registration_timeout

这两个文件的意思是,一个说我将要使用Docker作为我第一选择,然后如果不行的话再去使用Mesos。然后第二个文件是设置超时时间,因为每个image都需要pull下来,如果pull的时间太长就会被认为是超时。

这是完了这两个文件后,还需要在启动mesos-slave的时候增加一个参数。
/usr/sbin/mesos-slave --containerizers=docker,mesos --master=zk://127.0.0.1:2181/mesos --log_dir=/log/mesos >>/dev/null 2>&1 &  

这样slave就会可以支持Docker了。其实根据我最后的使用经验来看,这个slave仅仅是使用了docker-client,真正的docker-daemon是使用的host的,这样对于端口映射就会非常简单的完成,就像你在本地机器自己装了一个Docker一样。

最后启动容器就OK了。
docker run -p 8080:8080 -p 5050:5050 --name mesos -ti -w /root -v /sys/fs/cgroup:/sys/fs/cgroup -v /var/run/docker.sock:/var/run/docker.sock single-docker-mesos

这里比较奇怪的参数是那两个-v,一个是将本地的Docker Daemon绑定到容器内部,另外一个是将cgroup绑定到容器内部,因为Docker的运行需要cgroup的支持,这样Marathon就能在容器内部管理外部的容器了。
1.png

2.png

最后,其实上面这些东西只要明白大概意思就可以,真正使用灰常的简单。我把代码放到GitHub上,你可以克隆下来本地build,然后run一下就可以测试使用Marathon 管理Docker了,如果你还嫌麻烦,我把已经build好的image传到了灵雀云上,你可以直接pull下来使用,更加方便。https://hub.alauda.cn/repos/a352193394/single-docker-mesos

引用自
作者KiwenLau原文URL地址:
http://kiwenlau.com/2015/09/18/150918-single-mesos-docker/

灵雀云还欠你一个头条,让你红到违反新广告法

灵雀云 发表了文章 • 2 个评论 • 3011 次浏览 • 2015-09-08 14:46 • 来自相关话题

第二期“我要上头条”活动,火热上线啦! 在玩转Docker的道路上,我们继续前行…… 如果你热心肠,Zui爱雕琢镜像,Zui享受你的镜像被下载、被点赞的那一刻…… 灵雀云将给你一个舞 ...查看全部
第二期“我要上头条”活动,火热上线啦!

在玩转Docker的道路上,我们继续前行……

如果你热心肠,Zui爱雕琢镜像,Zui享受你的镜像被下载、被点赞的那一刻……

灵雀云将给你一个舞台,让你有机会学以致用,有机会和高手切磋,更有机会在Docker社区红到违反新广告法!

也希望更多的,不同类型的用户从这些镜像中获益,让Docker技术在中国的发展好到不能说!‌‌

第二期报名方式

如果您已经在灵雀云上传了镜像,并有兴趣参与比赛,请发邮件到xiaolu@mathildetech.com报名参与当期比赛。

注意:请将邮件命名为“我要上头条”+ 您的灵雀云用户名。并将您参赛镜像的URL附在邮件正文中。

评奖规则

是不是受热爱,用户说了算!

在一个活动周期内,综合计算每个报名镜像被其他用户pull的数量,选出zui受欢迎的前6个镜像,将分别获得价值100-500元不等的京东卡一张。

第一期获奖作品展示

#Zui受热爱的镜像

作者:王俊涛,做过美工,扛过摄像机,做过影视后期,但最终无爱,2004年转行正式投身程序员的队伍。主攻Python与Java,坚信“想象力比知识更重要”。2015年开始创业,将ToughRADIUS作为第一个开源探路产品推向大众。

作品简介:ToughRADIUS是由国人开发和维护的开源的Radius服务软件,支持标准RADIUS协议,致力于为中小微运营商,提供基于TOUGHRADIUS的宽带认证计费服务,让宽带运营变的更简单、高效、可靠。ToughRADIUS提供了完整的Docker支持,通过Docker模式更轻松的交付整个应用,实现了自动化部署、测试、发布和升级,极大地提高了效率。

镜像下载命令:docker pull index.alauda.cn/toughstruct/toughradius

作品展示:开源宽带认证计费系统

#Zui受欢迎的App

作者:高正炎,某大学升大三的孩子,专注于计算机技术,学习过PHP和android编程,努力往安全方向发展的单身汪。

作品简介:1983年7月15日,任天堂FC正式发售,国内俗称红白机。因为众多兼容机型的存在,可以说是国内影响力Zui大的游戏主机。游戏画面虽然现在看起来简陋粗糙,当时却带给我们无数快乐的回忆!
Zui受欢迎的App,就是高正炎同学集合了几个红白机游戏所做的Docker容器。

作品展示:最难忘的红白机游戏

#Zui有深度的文章

作者:杜航,灵雀云资深用户,Websense云基础架构组开发经理,专注于Openstack和Docker。

作品简介:Docker公司在2014年12月发布了Docker集群管理三剑客。
Docker Machine可实现Docker主机的一键部署;Docker Compose是一个编排多容器分布式部署的工具;Docker Swarm是Docker原生的集群管理工具,把多个Docker主机抽象成单一的虚拟主机。
杜航的三篇文章通过对Docker Machine,Compose以及Swarm的源码分析,深度解析了其工作原理及使用方法。

作品展示:
我要上头条:Docker集群管理之Docker Machine
我要上头条:Docker集群管理之Docker Compose
我要上头条:Docker集群管理之Docker Swarm

Docker Hub最受欢迎的10大镜像

灵雀云 发表了文章 • 2 个评论 • 40606 次浏览 • 2015-09-06 15:04 • 来自相关话题

【编者的话】DockerHub上有多少个镜像?哪些基础镜像被引用的最多?哪些镜像最受欢迎?来自CenturyLink实验室博客的一篇文章将为你解答这些问题,文中所有数据均来自2015年8月6日,Docker Registry API的调用。 ...查看全部
【编者的话】DockerHub上有多少个镜像?哪些基础镜像被引用的最多?哪些镜像最受欢迎?来自CenturyLink实验室博客的一篇文章将为你解答这些问题,文中所有数据均来自2015年8月6日,Docker Registry API的调用。
docker-top-ten.jpg

DockerHub上有多少个镜像?

哪些基础镜像被引用最多?

哪些镜像最受欢迎?

来自CenturyLink实验室博客的一篇文章将为你解答这些问题,文中所有数据均来自2015年8月6日,Docker Registry API的调用。
‌‌
灵雀云正在举办“我要上头条:Docker镜像征集”活动,参与有大奖,看看哪些镜像最受国内最用户的欢迎!
FullSizeRender1-e1438038233747.jpg

Docker CEO Ben Golub在2014年的DockerCon上发布Docker Hub,并指出已经有超过14,000个Docker化的应用存储在他们的publc registry中。而根据2015年的DockerCon上,Docker SVP Marianna Tessel公布的数据,Docker Hub上的repos已经超过150,000个,仅一年就有了十倍的增长!

看到这些疯狂增长的数字,笔者不禁想去探索这些数据背后的故事。所以决定从Docker Hub中挖掘数据,通过调用Docker Registry API,研究这150,000个repos。

需要注意的是:Docker Hub中的repos和tag在不断地被添加和删除,所以文中的数据只是在某个特定时间点的数据,当你读到这篇文章时可能它已经发生了变化,但这些数字背后体现的规模还是基本准确的。

镜像仓库(Repositories)

最新统计显示Docker Hub上有125,289个公共库。这比在六月的DockerCon上提及的150,000略少(的确增长到过那个数字),可能因为有一些私有库。

在这些公有库中,39,441(31%)是自动构建(automate build),也就是说这个镜像是由Docker Hub,构建一个已经发布的Dockerfile生成的镜像。剩下85,848个库镜像是通过docker push命令上传的。

Docker Hub上目前有84个官方镜像仓库,涵盖了Mysql,MongoDB和Redis等常用的服务。相比在14年DockerCon上公布的13个,这也是相当大的增幅。

鉴于国内访问Docker Hub的诸多不便,灵雀云镜像中心也是一个容器化服务发布和共享的社区平台,其中汇集了数以万计的来自平台、社区和第三方的优质镜像,让用户组合、复用容器化服务,轻松搭建新一代云端应用。

灵雀云正在举办“我要上头条:Docker镜像征集”活动,参与有大奖,看看哪些镜像最受国内最用户的欢迎!

机构(Organizations)

以下是发布公共镜像仓库最多的10大机构或个人:
Organization.png

不知道datdocker用那8261个库做什么,看起来像是CI过程中生成的新库,因为很多库名都加了数字后缀。而imiell的拥有者是Ian Miell,曾写过一本Docker的书,许多库看起来都是书中的范例。

收藏数(Stars)

通过Docker registry API获取不了镜像被pull的个数,只能通过镜像的star数量,来衡量镜像的流行度。
毫无疑问,拥有最高star数量的库都是官方库。排名最高的10个官方库是:
nonofficial.png

排名最高的十个非官方库是:
Stars.png

这张列表上最令人惊讶的是aspnet镜像(ASP.NET的Docker镜像),显然有许多微软开发人员对Docker很感兴趣。

在灵雀云,aspnet也是一个比较受欢迎的镜像:
镜像地址:https://hub.alauda.cn/repos/microsoft/aspnet
pull命令:docker pull index.alauda.cn/microsoft/aspnet

在刚刚结束的“我要上头条”活动数据显示,这一个月来灵雀云最受欢迎的非官方镜像是ToughRADIUS开源宽带认证计费系统的Docker镜像。

Tags

在这125,289个公共库中,有243,966个被加了tag。平均每一个库有1.9个tag。超过100,000个库(82%)有唯一的tag,有少量库拥有上千个tag。

tag最多的是rstiller/jetty,有2104个tag(他们似乎为每个可能组合的OS,JDK和Jetty版本都创建了镜像tag)。所有库中最常使用的tag:
Tag.png

最有趣的tag是”kitten”,73个不同的镜像都使用了它,可能和这个Kubernetes Demo App有关。

排第1的是“lastest”,如果你不明确指定一个tag,默认情况下就会是“lastest”。

有247个库没有tag,—它们都是没有镜像的空库。

基础镜像(base image)

Docker的优雅之处在于其分层镜像格式,你不必从无到有构建你的镜像——你能使用任何公共镜像作为基础,构建自己的镜像。那么哪些镜像是最经常被引用的基础镜像呢?以下是排名前十的基础镜像:
Missing_Tag.png

显然,Ubuntu是极其受大家欢迎的基础镜像,前10中占了6个,因为很多开发人员都对Ubuntu很熟悉。但建议关注一下alpine,特别是出于精简镜像的考虑。 scratch镜像是当之无愧的No.1.Docker 1.5以前,几乎每个镜像都将scratch作为基础镜像。

Alpine镜像的大小是5MB(ubuntu镜像大小是188MB),有一个很棒的包管理器。CenturyLink已经将很多的基础镜像从Ubuntu迁移到了Alpine,显著缩小了镜像的大小。
docker-images-alpine-vs-ubuntu.png

灵雀云用户alexwhen,在《精简Docker镜像》一文中,也曾提到过这个观点。

Missing Tags

10个最经常被引用的基础镜像中,6个没有tag(被标记为lastest),可能之前它们也被标记为某一个值,但又变了。

比如,之前bf84c1d84a8f层被标记为debian:latest(也是debian:jessie)。当其它镜像以其为基础镜像时,会在Dockerfile中写FROM debian或FROM debian:jessie。

如果Debian镜像被重新构建,tag会被指向到另外的层。所以,镜像tag不是静态的,它们可能指向到另外的镜像。

如果你希望你的镜像始终基于最新版本的Debian Jessie或者Ubuntu构建而成,你需要追踪这些tag的变更,并根据情况重构镜像。Docker Hub的repository link特性可以帮你实现这一点。

本文为王家隆的投稿文章,译自:https://labs.ctl.io/docker-hub-top-10/

我要上头条:Docker集群管理之Docker Machine

灵雀云 发表了文章 • 3 个评论 • 6027 次浏览 • 2015-08-13 15:23 • 来自相关话题

“我要上头条”活动发出的第一天,就收到了灵雀云(www.alauda.cn)资深用户杜航的投稿文章,感谢大家的支持,也希望能通过这个活动将Docker技术普及给更多的技术人员。 前言:以Docker为代表的容器技术正在成为应用交付运 ...查看全部
“我要上头条”活动发出的第一天,就收到了灵雀云(www.alauda.cn)资深用户杜航的投稿文章,感谢大家的支持,也希望能通过这个活动将Docker技术普及给更多的技术人员。

前言:以Docker为代表的容器技术正在成为应用交付运维的全新标准,围绕其周围的整个生态圈也在发生着天翻地覆的改变,我们可以期待以其为代表的新一轮云技术革命的到来。‌‌CaaS(Container as a Service)技术是为了解决Docker容器的最佳应用场景 – 云端而生,如何在云端管理部署Docker容器便成为CaaS技术的核心所在。做为Docker容器集群管理三剑客之一的Docker Machine 大大简化了Docker主机部署的复杂度,极大的方便了开发者管理分布式Docker主机。本文通过对Docker Machine的源码分析,讲述了Docker Machine的工作原理。本文为灵雀云“我要上头条”活动的投稿。

应用软件的部署方式主要可以分为部署在用户环境的主机上(On-Premise),部署在云端(SaaS)以及两者混合模式(Hybird)。部署在云端的软件又可根据软件可访问的范围分为公有云部署,私有云部署以及其两者的混合模式。如何在以上各种环境中创建Docker主机便成为DevOps关注的一个问题。Docker Machine的产生简化了这一过程,让你可以使用一条命令在你的计算机,公有云平台以及私有数据中心创建及管理Docker主机。

在Docker Machine发布之前,你可能会遇到以下问题:
  1. 你需要登录主机,按照主机及操作系统特有的安装以及配置步骤安装Docker,使其能运行Docker容器;
  2. 你需要研发一套工具管理多个Docker主机并监控其状态;
  3. 你在本地开发,产品部署在公有云平台,你希望能尽可能的减小两个环境的差异性

Docker Machine的出现解决了以上问题:
  1. Docker Machine简化了部署的复杂度,无论是在本机的虚拟机上还是在公有云平台,只需要一条命令便可搭建好Docker主机;
  2. Docker Machine提供了多平台多Docker主机的集中管理;
  3. Docker Machine 使应用由本地迁移到云端变得简单,只需要修改一下环境变量即可和任意Docker主机通信部署应用。

综合来说Docker Machine让下图这种开发模式得到了大大的简化。

Docker_Machine.png


#Docker Machine的安装
可以通过下载二进制可执行文件的方式安装Docker Machine,本文以Linux系统为例。

code.png


#Docker Machine的运行原理

本文通过两个例子讲述了Docker Machine的工作原理及工作流程:在本机安装Virtualbox虚拟机作为Docker主机,以及在AWS创建Docker主机。

create命令用来创建docker主机,运行create命令需要指明驱动的名称,目前支持在本机运行virtualbox虚拟主机,Hyper-V虚拟主机,VMware虚拟主机,AWS EC2,Azure,DigitalOcean,Google等公有云主机,以及使用Openstack搭建的私有数据中心。

新的虚拟化(Xen,KVM)支持以及新的云平台支持可以通过开发驱动的方式支持。

#在本机安装Virtualbox虚拟机作为Docker主机

Create Docker Machine主要包括三个Create过程。

code1.png


  1. 首先是Provider Create(libmachine/provider.go),此函数主要是在当前运行docker-machine命令主机上创建以machine name命名的文件夹,并将根证书,服务器证书以及用户证书拷贝到此文件夹;
  2. 其次是driver create(例如drivers/virtualbox/virtualbox.go)用来创建主机;
  3. 最后是运行host create(libmachine/host.go)通过SSH安装并配置Docker。

目前在本地环境中使用的是boot2docker镜像,云端环境使用的是Ubuntu镜像。

运行在Virtualbox虚拟主机上的docker machine创建过程如下:
  1. Docker Machine首先生成一个自签名的Root CA,然后用这个Root CA签发客户端证书,此证书在Docker客户端连接远程Docker服务器的时候做认证使用;
  2. 配置Docker主机的运行参数,参数包括Docker客户端与远程Docker服务器之间认证参数,远程Docker daemon的运行参数以及Docker Swarm的参数。

code2.png


  1. Docker Machine使用boot2docker作为virtualbox的镜像 – boot2docker是一个运行Docker容器的轻量级Linux系统,完全在内存中运行。
  2. 创建virtualbox虚拟机
a. 生成ssh key – 部署过程中使用次ssh key认证实现远程操作
b. 创建一个VMDK文件作为虚拟机的hdd,大小可以在运行时指定参数–virtualbox-disk-size控制

code6.png


c. 创建虚拟机

code7.png


修改虚拟机参数,可通过参数–virtualbox-memory和–virtualbox-cpu-count控制内存大小和CPU个数。

code3.png


d. 配置虚拟机网络 – NIC1为NAT形式使虚拟机能够访问外网,NIC2为hostonly模式用于内部通信,子网可以通过参数–virtualbox-hostonly-cidr修改

code4.png


e. 配置存储 – 将boot2docker iso文件挂载在虚拟dvd光驱,步骤b中创建的文件作为hdd
  1. 启动虚拟机,并设置端口映射,将本地随机端口转发到虚拟机22端口(此时还不知道虚拟机Hostonly网卡的IP地址,所以只能通过NAT网卡进行端口映射的方法访问虚拟机)
  2. 使用默认用户名docker,默认密码docker登录虚拟机将第一步生成的ssh key导入到虚拟机/home/docker/.ssh/authorized_keys。获取hostonly网卡的IP地址(通过DHCP获得)
  3. 部署虚拟机的Docker运行环境(所有命令都是通过SSH远程执行)
a. 配置虚拟机hostname
b. 安装Docker
c. 配置docker daemon使用TLS –tlsverify。使用此选项之后docker daemon只接受来自第1步中自签名的根证书签发的证书,docker客户端只相信第1步中自签名的更证书签发的服务器证书
d. 签发服务器证书

自此为止一个基于virtualbox的Docker运行环境就创建好了,使用者需要将本地的docker客户端配置到远程的docker daemon。

code5.png


Docker Machine的其他命令都是通过drivers/virtualbox/virtualbox.go驱动实现的,在此就不累述了。

#在AWS创建Docker主机

Docker Machine通过AWS REST API创建一个Docker主机所需要的资源,需要使用AWS访问密钥(–amazonec2-access-key)和AWS密钥(–amazonec2-secret-key)进行认证。所创建的EC2主机需要在一个提前创建好的VPC(–amazonec2-vpc-id)里面。

  1. 检查环境 – 是否已经存在重名的密钥对,检查指定区域(–amazonec2-zone)的指定VPC中是否存在子网,使用默认的子网;
  2. 创建密钥对并通过AWS REST API上传到AWS服务器;
  3. 创建一个安全组允许访问22(SSH),2376(Docker)或3376(指定Swarm),可通过–amazonec2-security-group指定名称;
  4. 创建一个块设备;
  5. 运行EC2实例 – 指定AMI,区域,安全组,子网等参数;
  6. 部署虚拟机的Docker运行环境 -使用默认用户ubuntu以及之前生成的密钥SSH到AWS EC2实例进行远程操作
a. 配置虚拟机hostname
b. 安装Docker
c. 配置docker daemon使用TLS –tlsverify。使用此选项之后docker daemon只接受来自第1步中自签名的根证书签发的证书,docker客户端只相信第1步中自签名的更证书签发的服务器证
d. 签发服务器证书

至此一个运行在AWS公有云上的Docker主机就创建成功了。

Docker Machine的其他命令也是在drivers/amazonec2/amazonec2.go中实现的

关于Docker Machine与Docker Swarm的集成,将在之后Docker Swarm的章节中介绍。

结语:本文通过两个典型实例分析力Docker Machine在本地虚拟环境的工作流程以及在远程公有云平台的工作流程。通过以上步骤更能说明Docker Machine为开发者带来的一步式部署,免去了烦琐的虚拟机搭建以及公有云IaaS的配置,使开发者能专注于应用开发。Docker Machine目前还处在初级阶段,在其Roadmap我们可以看到包括Mac/Windows支持,多种发行版支持,更广泛的支持Docker engine以及Swarm的配置,我们有理由期待Docker Machine成为一个更加通用的Docker主机部署管理工具。

作者简介:杜航,灵雀云资深用户,Websense云基础架构组开发经理,专注于Openstack和Docker。
转载请注明来源。

【精贴】(神速)5分钟内拉取Dockerhub镜像 附带Mesos中文版下载链接

回复

挑灯看剑 回复了问题 • 10 人关注 • 5 个回复 • 6337 次浏览 • 2016-04-08 17:13 • 来自相关话题

请教下灵雀云是如何做到自动构建镜像的?

回复

oilbeater 回复了问题 • 4 人关注 • 2 个回复 • 4202 次浏览 • 2015-10-13 14:24 • 来自相关话题

灵雀云的mongodb经常会变密码么?

回复

苦逼少侠 回复了问题 • 5 人关注 • 4 个回复 • 2856 次浏览 • 2015-10-11 22:06 • 来自相关话题

像灵雀云和时速云这些平台内部署的服务器镜像支持cron么

回复

萧遥吟 回复了问题 • 5 人关注 • 2 个回复 • 3506 次浏览 • 2015-09-15 09:49 • 来自相关话题

一键发布tomcat应用到 灵雀云 的Dockerfile怎么写啊

回复

oilbeater 回复了问题 • 2 人关注 • 1 个回复 • 4453 次浏览 • 2015-07-10 09:57 • 来自相关话题

2019企业IT现状和趋势调研报告:70.7%的企业有云原生相关计划

灵雀云 发表了文章 • 0 个评论 • 129 次浏览 • 2019-06-03 11:00 • 来自相关话题

2019年第一季度,灵雀云发起了“企业IT应用现状和云原生技术落地情况”的调研,通过定向邀请,3个月内共收集了400余份有效调研问卷,这些调研问卷80%以上都来自于国内政府、金融、能源、制造、汽车等传统行业的IT从业者。 发起本次调研 ...查看全部
2019年第一季度,灵雀云发起了“企业IT应用现状和云原生技术落地情况”的调研,通过定向邀请,3个月内共收集了400余份有效调研问卷,这些调研问卷80%以上都来自于国内政府、金融、能源、制造、汽车等传统行业的IT从业者。

发起本次调研的初衷,是我们希望了解当前企业,尤其是传统企业目前IT应用开发现状、以及以DevOps、Kubernetes、微服务等为代表的云原生技术在企业的应用情况,从而勾勒出传统行业IT发展趋势,并对于判断国内用户对云原生相关技术的认知度提供一个有价值的参考。
核心要点解读:
1、 约70%的参与调研者所在企业2019年IT预算有上浮;

2、 24.4%的参与调研者表示公司IT系统基本全靠自研,企业开始自建软件研发团队,主导IT应用的研发;

3、 70.7%的参与调研者所在企业表示在2019年有容器、DevOps和微服务方面的规划;

4、 11.4%的参与调研者所在企业已经试点了具有标杆意义的云原生实践,如精英团队的DevOps实践,小范围非核心应用的微服务拆分改造实践等。


pic2.jpg



本次调研的400多位调研对象中,80%以上来自金融、能源、制造、汽车等传统行业,其中17.3%来自基础架构部门, 22.5%来自运维部门,34.1%来自研发部门,还有约10%的被调研对象为企业的CIO/CTO等高级IT管理者。


pic3.jpg



被调研企业中,服务器规模在100-500台的比例为26.8%,500-1000台的企业占比22%,1000台服务器以上规模的14.6%。



IT系统自研还是外包



pic4.jpg




在数字化转型的背景下,传统外包的做法在被逐渐改变。在此次调查中,70.7%的参与调研者表示目前IT系统是自研外包兼而有之,其中核心业务系统以自己开发为主,24.4%的参与调研者表示公司IT系统基本全靠自研,只有4.9%的参与调研者选择了纯外包选项。这表明,企业开始不再将大部分业务系统,尤其是核心业务需求开发外包,开始自建软件研发团队,主导IT应用的研发。只有企业自己主导IT研发,才能够打造IT核心竞争力。

软件能力成为企业的核心竞争力,这恰好是数字化转型的本质之一。何谓成功的数字化转型?灵雀云认为,有三大衡量标志:IT部门由成本中心转为收入中心;企业自己主导IT产品的研发;改进工具、流程、文化来提高交付速度和质量。最终,实现客户满意度的提升、打造差异化竞争优势、加速产品上市。



IT系统更新频率




PIC5.jpg



在IT系统更新频率方面,每月都要更新、升级的比例达到了51.2%的高占比。同时,每3-6个月更新一次的比例达22%。每个传统领域,都受到了来自Fintech金融科技、车联网、物联网、新零售等新技术驱动的创新业务的挑战,传统企业只有借助IT手段才能实现持续发展,在速度和规模上保持竞争力。



IT系统和研发团队TOP 3挑战



pic6.jpg




本次参与调研的企业以中大型企业为主,其中研发团队规模达到100人以上的比例高达44.3%,20-100人规模的占32.4%。

PIC7.jpg




今天,许多企业都经过了大量IT建设,从分散到集中,造成IT系统越来越复杂,信息孤岛林立,架构臃肿等问题突出。调研中企业IT系统支撑所面临的压力位列前三的挑战分别是:系统复杂性越来越高(65.9%);应用交付压力大,交付速度无法满足业务需求(61.4%);运维管理复杂度提升,IT部门很难构建一支全功能团队(53.7%)。


PIC8.jpg



同时,研发团队所面临的挑战前三甲分别是:部署和运维复杂,运维成本高(74.6%);研发、测试、运维等角色之间相互孤立(62.3%);升级和变更流程复杂,IT服务和应用交付周期长(45.7%)。此外,比较突出的挑战还有,工具链无法完整集成,工具使用困难(32.3%),单体应用过于庞大,迭代效率低下(20.4%)。


pic9.jpg



上述结果充分表明,面对高度创新、快速变化和充满不确定性的新型业务需求,传统开发模式和IT架构已经成为掣肘。70.7%的参与调研企业表示2019年有容器、DevOps和微服务方面的规划和实施计划。

只有朝着持续交付、敏捷部署、快速迭代,通过敏捷IT赋予业务足够的敏捷,才能够满足不断变化的业务需求,重塑自身的生产力,形成竞争优势,带来更好的用户体验,这最终落到以Kubernetes/容器、DevOps和微服务为核心的云原生技术的落地上。云原生架构和理念与数字化转型一脉相承,帮助企业更加顺畅地实施数字化转型。



业务上云需求最强烈,开源、数字化转型受追捧



PIC10.jpg




在企业最关注的新兴技术趋势方面,云计算占比82.9%,企业将业务上云,提升IT资源效率作为首要关注对象。大数据和人工智能紧随其后,占比分别为73.2%和46.3%。其中开源解决方案在调研对象中的关注程度达到24.4%。

当前开源技术正在进入快速发展阶段,向着企业应用的方方面面深入。开源及开源社区不断将新的工具、方法和最佳实践用于云原生的实际业务用例,解决云原生用户的关键问题。借助许多开源解决方案,云原生部署的复杂性和难度也在得以降低。

此外,数字化转型的关注度为33.6%。如今每位IT从业者言必称数字化转型,IT能力也直接指向助力数字化转型。CIO和其他IT管理者已将企业的数字化计划置于新的高度,希望通过数字化来改变企业的商业和业务模式,数字化业务将从初步试验走向大规模应用。伴随企业数字化业务的不断成熟,预计未来几年,数字化转型将进入爆发阶段。



传统企业2019年IT预算稳中有升


PIC11.jpg





本次调研中,被调研企业今年IT工作的重点包括业务上云(56.1%),云原生、大数据、人工智能等新技术采用(53.7%),打造数字化团队,引领企业的数字化创新(43.9%),选择传统业务应用的比例不足20%。越来越多的企业将工作负载放在云端,将正在开发的应用或服务托管在云平台上,云市场不断增长。


PIC12.jpg



在IT预算方面,比客观经济形势略显乐观,和2018年IT预算相比,接近70%参与调研企业2019年的IT预算略有上浮,其中增长5%以内的企业占比37.5%,增长5-10%的企业占比21.2%,增长10%以上的企业达到12.7%。

此外,调研结果显示,数字化转型是一项需要通盘考虑的工作,需要项目管理部门、技术管理部门、开发部门、运维部门共同参与,制定统一的数字化转型方案和决策并推进。有些参与调研的企业特别强调2018年已经在全公司范围内试点了具有标杆意义的云原生实践,如精英团队的DevOps实践,小范围非核心应用的微服务拆分改造实践等,并且这些都将在2019年进行大范围推广。

浅析 Kubernetes原生NetworkPolicy 网络策略,让更安全的容器运行环境唾手可得

灵雀云 发表了文章 • 0 个评论 • 146 次浏览 • 2019-05-29 10:33 • 来自相关话题

k8s中的网络策略主要分为原生 NetworkPolicy 和第三方网络插件提供的网络策略。本文将主要分析原生Networkpolicy的网络策略。 什么是网络策略 ...查看全部

k8s中的网络策略主要分为原生 NetworkPolicy 和第三方网络插件提供的网络策略。本文将主要分析原生Networkpolicy的网络策略。



什么是网络策略



网络策略(NetworkPolicy)是一种关于 Pod 间及 Pod 与其他网络端点间所允许的通信规则的规范。NetworkPolicy 资源使用标签选择 Pod,并定义选定 Pod 所允许的通信规则。

k8s中的网络策略由实现了CNI接口的网络插件提供,网络插件监听集群中 NetworkPolicy 资源的创建/删除/更新事件生成对应的规则来控制 Pod 的流量是否放行。

常见的支持 NetworkPolicy 的网络插件有:

  • Calico
  • Cilium
  • Kube-router
  • Romana
  • Weave Net
默认情况下 Pod 间及 Pod 与其他网络端点间的访问是没有限制的。
pic1.png
如下是一个 NetworkPolicy 定义的例子,该策略的含义是阻止所有流量访问有`app=web`这个 label 的 Pod。经常有人会问网络策略要怎么写,或者是这个网络策略代表了什么含义。笔者认为这个问题主要是因为使用者不了解网络策略的省缺行为。NetworkPolicy 字段含义NetworkPolicy 这个资源属于命名空间级别的,因此metadata 中的 namespace 不可省略,否则只会对default 命名空间下的满足条件的 Pod 生效。下面介绍下 NetworkPolicy 中各字段的含义,并说明各字段省缺值及其含义,主要看 (http://www.alauda.cn]Spec.io/docs/reference/generated/kubernetes-api/v1.14/#networkpolicyspec-v1-networking-k8s-io) 中的字段,podSelector: 必填字段,Pod 的标签选择器,表示该网络策略作用于哪些 Pod。如果为空`{}`则表示选中本命名空间下所有 Pod。policyTypes: 可选字段,字符串,策略规则类型, 表示该网络策略中包含哪些类型的策略,可选为"Ingress", "Egress", 或 "Ingress,Egress"。未填时,这个值依据下面的 ingress 和 egress 来定。如果该字段未设置且下面只出现了 ingress,则对 egress 不做限制。如果填了这个值,同时后续没有设定对应的规则,则认为设定的规则对应的流量全部禁止。例如:
pic2.png
该规则就限制了所有 Pod 的出流量。ingress: 可选字段,数组,入站规则。互相间为或的关系,满足其中一条则放行。ports: 可选字段,数组,放行端口信息。互相间为或的关系,如果为空表示端口不受约束,如果非空,则表示除了出现的端口放行,其他未指定的端口都禁止。 -port: 可选字段,数字,协议端口号。如果不写,表示协议所有端口。 -protocol: 可选字段,字符串,协议。允许取值为 TCP,UDP,SCTP。省缺为 TCP。from: 可选字段,数组,放行源地址信息。互相间为或的关系,如果为空表示不约束源地址,如果非空,则表示除了出现的源地址放行外,其他源地址都禁止。 -ipBlock: 可选字段,放行 ip 段。 cidr: 标准 cidr,除了指定的cidr放行外其他都禁止。 except: 标准 cidr 字符串数组,表示前面cidr 中的放行的 cidr 段需要再排除掉 except 中指定的。 -namespaceSelector: 可选字段,namespace 的标签选择器,表示放行集群中的哪些命名空间中过来的流量。如果为空`{}`或未出现则表示选中所有命名空间。 -podSelector: 可选字段,Pod 的标签选择器,表示放行哪些 Pod 过来的流量,默认情况下从NetworkPolicy 同命名空间下的 Pod 中做筛选,如果前面设定了`namespaceSelector`则从符合条件的命名空间中的 Pod 中做筛选。如果为空`{}`则表示选中满足`namespaceSelector` 条件的所有 Pod。egress: 可选字段,数组,出站规则。互相间为或的关系,满足其中一条就放行。ports: 可选字段,数组,放行端口信息。互相间为或的关系,如果为空表示端口不受约束,如果非空,则表示除了出现的端口放行,其他未指定的端口都禁止。(详细字段同 ingress 中的 ports)to: 可选字段,数组,放行目的地址信息。互相间为或的关系,如果为空表示不约束目的,如果非空,则表示除了出现的目的地址放行外,其他目的地址都禁止。(详细字段同ingress 中的 from)介绍完 Spec 中各字段的含义及其默认行为后,做个简单的小结,NetworkPolicy 定义了放行规则,规则间是或的关系,只要命中其中一条规则就认为流量可以放行。下面以一个kubernetes官网中的例子来回顾下前面的知识。
pic3.png
首先该规则指定了命名空间为 default, 选择了其中所有包含 `role=db` 这个 label 的 Pod,定义了入站流量规则与出站流量规则。对于入站流量,放行源地址来自 cidr 172.17.0.0/16 除了 172.17.1.0/24 之外的流量,放行来自有`project=myproject` 这个label的namespace中的流量,放行 default 命名空间下有 label `role=frontend` 的 Pod 的流量,并限定这些流量只能访问到 `role=db` 这个 label 的 Pod 的 TCP 6379端口。对于出站流量,只放行其访问目的地址属于 cidr 10.0.0.0/24 中,且端口为 TCP 5978的流量。需要注意的是 NetworkPolicy 选中的 Pod 只能是与 NetworkPolicy 同处一个 namespace 中的 Pod,因此对于有些规则可能需要在多个命名空间中分别设置。或者使用非原生的网络策略定义,例如 Calico 中的 GlobalNetworkPolicyNetworkPolicy 变更历史v1.6 以及以前的版本需要在 kube-apiserver 中开启 extensions/v1beta1/networkpolicies;v1.7 版本 Network Policy 已经 GA,API 版本为 networking.k8s.io/v1;v1.8 版本新增 Egress 和 IPBlock 的支持;附录

使用 Jenkins + Ansible 实现 Spring Boot 自动化部署101

灵雀云 发表了文章 • 0 个评论 • 227 次浏览 • 2019-05-22 10:43 • 来自相关话题

本文要点: 设计一条 Spring Boot 最基本的流水线:包括构建、制品上传、部署。 使用 Docker 容器运行构建逻辑。 自动化整个实验环境:包括 Jenkins 的配置,Jenkins agent 的配置等。 ...查看全部
本文要点:
设计一条 Spring Boot 最基本的流水线:包括构建、制品上传、部署。
使用 Docker 容器运行构建逻辑。
自动化整个实验环境:包括 Jenkins 的配置,Jenkins agent 的配置等。

1. 代码仓库安排

本次实验涉及以下多个代码仓库:
% tree -L 1
├── 1-cd-platform # 实验环境相关代码
├── 1-env-conf # 环境配置代码-实现配置独立
└── 1-springboot # Spring Boot 应用的代码及其部署代码

1-springboot 的目录结构如下:
% cd 1-springboot
% tree -L 1
├── Jenkinsfile # 流水线代码
├── README.md
├── deploy # 部署代码
├── pom.xml
└── src # 业务代码

所有代码,均放在 GitHub:https://github.com/cd-in-practice

2. 实验环境准备

笔者使用 Docker Compose + Vagrant 进行实验。环境包括以下几个系统:
Jenkins * 1 Jenkins master,全自动安装插件、默认用户名密码:admin/admin。
Jenkins agent * 2 Jenkins agent 运行在 Docker 容器中,共启动两个。
Artifactory * 1 一个商业版的制品库。笔者申请了一个 30 天的商业版。
使用 Vagrant 是为了启动虚拟机,用于部署 Spring Boot 应用。如果你的开发机器无法使用 Vagrant,使用 VirtualBox 也可以达到同样的效果。但是有一点需要注意,那就是网络。如果在虚拟机中要访问 Docker 容器内提供的服务,需要在 DNS 上或者 hosts 上做相应的调整。所有的虚拟机的镜像使用 Centos7。
另,接下来笔者的所有教程都将使用 Artifactory 作为制品库。在此申明,笔者没有收 JFrog——研发 Artifactory 产品的公司——任何广告费。 笔者只是想试用商业产品,以便了解商业产品是如何应对制品管理问题的。
启动 Artifactory 后,需要添加 “Virtual Repository” 及 “Local Repository”。具体请查看 Artifactory 的官方文档。如果你当前使用的是 Nexus,参考本教程,做一些调整,问题也不大。
如果想使用已有制品库,可以修改 1-cd-platform 仓库中的 settings-docker.xml 文件,指向自己的制品库。
实验环境近期的总体结构图如下:

pic1.jpg



之所以说是“近期的”,是因为上图与本篇介绍的结构有小差异。本篇文章还没有介绍 Nginx 与 Springboot 配置共用,但是总体不影响读者理解。如果你想和更多Jenkins技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

3. Springboot 应用流水线介绍

Springboot 流水线有两个阶段:


构建并上传制品
部署应用
流水线的所有逻辑都写在 Jenkinsfile 文件。接下来,分别介绍这两个阶段。
3.1 构建并上传制品
此阶段核心代码:


docker.image('jenkins-docker-maven:3.6.1-jdk8')
.inside("--network 1-cd-platform_cd-in-practice -v $HOME/.m2:/root/.m2") {
sh """
mvn versions:set -DnewVersion=${APP_VERSION}
mvn clean test package
mvn deploy
"""
}


它首先启动一个装有 Maven 的容器,然后在容器内执行编译、单元测试、发布制品的操作。
而 mvn versions:set -DnewVersion=${APP_VERSION} 的作用是更改 pom.xml 文件中的版本。这样就可以实现每次提交对应一个版本的效果。
3.2 部署应用
注意: 这部分需要一些 Ansible 的知识。
首先看部署脚本的入口 1-springboot/deploy/playbook.yaml:

---


[list]
[*]hosts: "springboot"[/*]
[/list] become: yes
roles:
- {"role": "ansible-role-java", "java_home": "{{JAVA_HOME}}"}
- springboot



先安装 JDK,再安装 Spring Boot。JDK 的安装,使用了现成 Ansible role: https://github.com/geerlingguy/ansible-role-java。
重点在 Spring Boot 部署的核心逻辑。它主要包含以下几部分:
创建应用目录。
从制品库下载指定版本的制品。
生成 Systemd service 文件(实现服务化)。
启动服务。
以上步骤实现在 1-springboot/deploy/roles/springboot 中。
流水线的部署阶段的核心代码如下:


docker.image('williamyeh/ansible:centos7').inside("--network 1-cd-platform_cd-in-practice") {

checkout([$class: 'GitSCM', branches: [[name: "master"]], doGenerateSubmoduleConfigurations: false,
extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "env-conf"]], submoduleCfg: [],
userRemoteConfigs: [[url: "https://github.com/cd-in-practice/1-env-conf.git"]]])

sh "ls -al"

sh """
ansible-playbook --syntax-check deploy/playbook.yaml -i env-conf/dev
ansible-playbook deploy/playbook.yaml -i env-conf/dev --extra-vars '{"app_version": "${APP_VERSION}"}'
"""
}


它首先将配置变量仓库的代码 clone 下来,然后对 playbook 进行语法上的检查,最后执行 ansible-playbook 命令进行部署。--extra-vars 参数的 app_version 用于指定将要部署的应用的版本。

3.3 实现简易指定版本部署

在 1-springboot/Jenkinsfile 中实现了简易的指定版本部署。核心代码如下:
流水线接受参数
parameters { string(name: 'SPECIFIC_APP_VERSION',
defaultValue: '', description: '') }
如果指定了版本,则跳过构建阶段,直接执行部署阶段


stage("build and upload"){
// 如果不指定部署版本,则执行构建
when {
expression{ return params.SPECIFIC_APP_VERSION == "" }
}
// 构建并上传制品的逻辑
steps{...}
}


之所以说是“简易”,是因为部署时只指定了制品的版本,并没有指定的部署逻辑和配置的版本。这三者的版本要同步,部署才真正做到准确。

4. 配置管理

所有的配置项都放在 1-env-conf 仓库中。Ansible 执行部署时会读取此仓库的配置。
将配置放在 Git 仓库中有两个好处:
配置版本化。
任何配置的更改都可以被审查。
有好处并不代表没有成本。那就是开发人员必须开始关心软件的配置(笔者发现不少开发者忽视配置项管理的重要性。)。
本文重点不在配置管理,后面会有文章重点介绍。

5. 实验环境详细介绍

事实上,整个实验,工作量大的地方有两处:一是 Spring Boot 流水线本身的设计;二是整个实验环境的自动化。读者朋友之所以能一两条简单的命令就能启动整个实验环境,是因为笔者做了很多自动化的工作。笔者认为有必要在本篇介绍这些工作。接下来的文章将不再详细介绍。

5.1 解决流水线中启动的 Docker 容器无法访问 http://artifactory

流水线中,我们需要将制品上传到 artifactory(settings.xml 配置的仓库地址是 http://artifactory:8081),但是发现无法解析 host。这是因为流水线中的 Docker 容器所在网络与 Docker compose 创建的网络不同。所以,解决办法就是让流水线中的 Docker 容器加入到 Docker compose 的网络。
具体解决办法就是在启动容器时,加入参数:--network 1-cd-platform_cd-in-practice

5.2 Jenkins 初次启动初始化

在没有做任何设置的情况启动 Jenkins,会出现一个配置向导。这个过程必须是手工的。笔者希望这一步也是自动化的。Jenkins 启动时会执行 init.groovy.d/目录下的 Groovy 脚本。
5.3 虚拟机中如何能访问到 http://artifactory ?
http://artifactory 部署在 Docker 容器中。Spring Boot 应用的制品要部署到虚拟机中,需要从 http://artifactory 中拉取制品,也就是要在虚拟机里访问容器里提供的服务。虚拟机与容器之间的网络是不通的。那怎么办呢?笔者的解决方案是使用宿主机的 IP 做中转。具体做法就是在虚拟机中加一条 host 记录:


machine.vm.provision "shell" do |s|
s.inline = "echo '192.168.52.1 artifactory' >> /etc/hosts"
end


以上是使用了 Vagrant 的 provision 技术,在执行命令 vagrant up 启动虚拟机时,就自动执行那段内联 shell。192.168.52.1 是虚拟宿主机的 IP。所以,虚拟机里访问 http://artifactory:8081 时,实际上访问的是 http://192.168.52.1:8081。
网络结构可以总结为下图:

pic2.jpg


后记

目前遗留问题:
部署时制品版本、配置版本、部署代码版本没有同步。
Springboot 的配置是写死在制品中的,没有实现制品与配置项的分离。
这些遗留问题在后期会逐个解决。就像现实一样,经常需要面对各种遗留项目的遗留问题。
附录
使用 Jenkins + Ansible 实现自动化部署 Nginx:https://jenkins-zh.cn/wechat/articles/2019/04/2019-04-25-jenkins-ansible-nginx/
简单易懂 Ansible 系列 —— 解决了什么:https://showme.codes/2017-06-12/ansible-introduce/


本文转自公众号 jenkins
作者 翟志军

基于 Jenkins 的 DevOps 平台应该如何设计凭证管理

灵雀云 发表了文章 • 0 个评论 • 211 次浏览 • 2019-05-20 11:06 • 来自相关话题

背景 了解到行业内有些团队是基于 Jenkins 开发 DevOps 平台。而基于 Jenkins 实现的 DevOps 平台,就不得不考虑凭证的管理问题。 本文就此问题进行讨论,尝试找出相对合理的管理凭证的方案。 ...查看全部
背景

了解到行业内有些团队是基于 Jenkins 开发 DevOps 平台。而基于 Jenkins 实现的 DevOps 平台,就不得不考虑凭证的管理问题。
本文就此问题进行讨论,尝试找出相对合理的管理凭证的方案。
一开始我们想到的方案可能是这样的:用户在 DevOps 平台增加凭证后,DevOps 再将凭证同步到 Jenkins 上。Jenkins 任务在使用凭证时,使用的是存储在 Jenkins 上的凭证,而不是 DevOps 平台上的。
但是,仔细想想,这样做会存在以下问题:
Jenkins 与 DevOps 平台之间的凭证数据会存在不一致问题。
存在一定的安全隐患。通过 Jenkins 脚本命令行很容易就把所有密码的明文拿到。哪天 Jenkins 被注入了,所有的凭证一下子就被扒走。
无法实现 Jenkins 高可用,因为凭证存在 Jenkins master 机器上。
那么,有没有更好的办法呢?

期望实现的目标

先定我们觉得更合理的目标,然后讨论如何实现。以下是笔者觉得合理的目标:
用户还是在 DevOps 管理自己的凭证。但是 DevOps 不需要将自己凭证同步到 Jenkins 上。Jenkins 任务在使用凭证时,从 DevOps 上取。

实现方式

Jenkins 有一个 Credentials Binding Plugin 插件,在 Jenkins pipeline 中的用法如下:


withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) {
sh '''
curl -u "$USERPASS" https://private.server/ > output
'''
}


withCredentials 方法做的事情就是从 Jenkins 的凭证列表中取出 id 为 mylogin 的凭证,并将值赋到变量名为 USERPASS 的变量中。接下来,你就可以在闭包中使用该变量了。
说到这里,不知道读者朋友是否已经有思路了?
思路就是实现一个和 Credentials Binding Plugin 插件类似功能的方法,比如叫 zWithCredentials(后文还会提到)。与 withCredentials 不同的是,zWithCredentials 根据凭证 id 获取凭证时,不是从 Jenkins 上获取,而是从 DevOps 平台获取。

会遇到的坑

需要适配只认 Jenkins 凭证的插件
withCredentials 方法是将凭证的内容存到变量中,这可以满足一大部分场景。但是有一种场景是无法满足的。就是某些 Jenkins 插件的步骤接收参数时,参数值必须是 Jenkins 凭证管理系统中的 id。比如 git 步骤中 credentialsId 参数:


git branch: 'master',
credentialsId: '12345-1234-4696-af25-123455',
url: 'ssh://git@bitbucket.org:company/repo.git'


这种情况,我们不可能修改现有的插件。因为那样做的成本太高了。
那怎么办呢?
笔者想到的办法是在 zWithCredentials 中做一些 hack 操作。也就是 zWithCredentials 除了从 DevOps 平台获取凭证,还在 Jenkins 中创建一个 Jenkins 凭证。在 Jenkins 任务执行完成后,再将这个临时凭证删除。这样就可以适配那些只认 Jenkins 凭证 id 的插件了。
对凭证本身的加密
DevOps 平台在存储凭证、传输凭证给 Jenkins 时,都需要对凭证进行加密。至于使用何种加密方式,交给读者思考了。

小结

以上解决方案对 Jenkins 本身的改造几乎没有,我们只通过一个插件就解耦了 Jenkins 的凭证管理和 DevOps 平台的凭证管理。
思路已经有了。具体怎么实现,由于一些原因不能开源,虽然实现起来不算难。还请读者见谅。
最后,希望能和遇到同样问题的同学进行交流。看看是否还可以有更好的设计思路。

本文转自公众号 Jenkins
作者:翟志军

深度解析Kubernetes核心原理之Scheduler

灵雀云 发表了文章 • 0 个评论 • 227 次浏览 • 2019-05-17 10:56 • 来自相关话题

Kubernetes是一个容器编排引擎,它被设计为在被称为集群的节点上运行容器化应用。通过系统建模的方法,本系列文章的目的是为了能够深入了解Kubernetes以及它的深层概念。 Kubernetes Scheduler ...查看全部
Kubernetes是一个容器编排引擎,它被设计为在被称为集群的节点上运行容器化应用。通过系统建模的方法,本系列文章的目的是为了能够深入了解Kubernetes以及它的深层概念。


Kubernetes Scheduler是Kubernetes的一个核心组件:在用户或者控制器创建一个Pod后,Scheduler在对象存储数据里监控未被分配的Pod,并将Pod分配到某个节点。然后Kubelet在对象存储数据里监控已分配的Pod,并运行该Pod。

本文提供了一个Kubernetes Scheduler的更简洁、更详细的模型表述。该模型部分基于TLA+规范。

pic1.jpg


图 1. Pod处理流程




调度



Kubernetes Scheduler的任务是选择一个placement(位置)。一个placement是一个部分的,非内射的Pod集合到节点集合的分配。

pic2.png



图 2. 调度示例

调度是一个最优化问题:首先,Scheduler确定feasible placements(可用的位置),这些是满足给定约束的placement集合。然后,Scheduler确定viable placements(可行的位置),这些是得分最高的feasible placements集合。

pi3.jpg


图 3. Possible(可能), Feasible(可用)和Viable(可行)的调度

Kubernetes Scheduler是一个保证局部最优解的多步调度器,而不是一个保证全局最优解的单步调度器。


pic4.jpg


图 4. 多步 vs. 单步



Kubernetes Scheduler




pi5.jpg



图 5. Kubernetes Pod对象和Node对象

图5描述了Kubernetes Scheduler所感兴趣的Kubernetes对象和属性。在Kubernetes里: 一个Pod表示为一个Kubernetes Pod对象 一个Node表示为一个Kubernetes Node对象 * 一个Pod分配给一个Node表示为Pod的Spec.NodeName属性












BoundTo(Pod, Node, Snapshot)≝
∧ Pod ∈Snapshot
∧Pod.Kind = "Pod"
∧ Node∈ Snapshot
∧Node.Kind = "Node"
∧Pod.Spec.NodeName = Node.Name

Bound(Pod, Snapshot) ≝
∃ Node∈ Snapshot:
BoundTo(Pod, Node, Snapshot)


如果一个Pod的Spec.NodeName等于一个Node的Name,则表示这个Pod对象绑定到了这个Node对象。

Kubernetes Scheduler的任务现在可以更规范地表述为:对于一个Pod p,Kubernetes Scheduler选择一个Node n,且更新(*)这个Pod的Spec.NodeName使得BoundTo(p, n)为true。



控制循环逻辑










Scheduler ≝
LETUnbound ≝ {Pod \in Objects : Pod.Kind = "Pod" ∧ ~ Bound(Pod,Objects)} IN
∃ Pod∈ { Pod ∈ Unbound : ∀ Other ∈ Unbound : Other.Spec.Priority ≤ Pod.Spec.Priority}:
CASE SchedulingEnabled(Pod) ⟶ Scheduling(Pod)
[] PreemptionEnabled(Pod) ⟶ Preemption(Pod)
[] OTHER ⟶ UNCHANGED(Objects)



Kubernetes Scheduler监控Kubernetes对象存储并且选择一个未绑定的最高优先级的Pod来执行调度流程或者抢占流程。



调度流程

















SchedulingEnabled(Pod) ≝
∃ Node∈ {Node ∈ Objects : Node.Kind = "Node"}:
Feasibility(Pod, Node, Objects)

Scheduling(Pod) ≝
LETFeasibile ≝ {Node ∈ Objects : n.Kind = "Node" ∧ Feasibility(Pod, n,Objects)} IN
∃Node ∈ Feasibile :
∧ ∀Other ∈ Feasibile : Viability(Pod, Other, Objects) ≤ Viability(Pod, Node,Objects)
∧Objects' = {
IF Pod = Object THEN
[Pod EXCEPT !["Spec"] = [Pod.Spec EXCEPT!["NodeName"] = Node.Name]]
ELSE
Object : Object ∈ Objects}



对于一个给定的Pod,如果存在至少一个Node可以运行该Pod,则启用调度流程。

如果调度流程启用,Scheduler将绑定该Pod到一个可选的Node,使得绑定能达到最优的可行性。

如果调度流程未启用,则Sheduler将尝试执行抢占流程。



抢占流程

















PreemptionEnabled(Pod) ≝
∃ Node∈ {Node \in Objects : Node.Kind = "Node"}:
∃Pods ∈ SUBSET(Jeopardy(Pod, Node, Objects)):
Feasibility(Pod, Node, Objects \ Pods)

Preemption(Pod) ==
LETPreemptable == {Node ∈ Objects : Node.Kind = "Node" ∧ ∃ Pods ∈SUBSET(Jeopardy(p, Node, Objects)): Feasibility(Pod, Node, Objects \ Pods)} IN
∃Node ∈ Preemptable:
∃Pods ∈ SUBSET(Jeopardy(Pod, Node, Objects)):
∀OtherNode ∈ qualified:
∀ OtherPods ∈ SUBSET(Jeopardy(Pod, OtherNode, Objects)):
∧ Casualty(Pods) ≤ Casualty(OtherPods)
∧ Objects' = (Objects \ Pods)



对于一个给定的Pod,如果存在至少一个Node,在删除绑定到该Node的较低优先级Pod子集后可以运行该Pod,则启用抢占流程。

如果抢占流程启用,Scheduler将触发绑定到Node的低优先级Pod子集的删除操作,使得抢占流程造成的损害最小。

(抢占损害是用Pod Disruption Budget来评估的,超出了本文的主题)

注意的是Scheduler不保证触发抢占流程的Pod在后续的调度流程中能绑定到Node。



1. 可用性(Feaisbility)



对于每一个Pod,Kubernetes Scheduler确定可用的Node集合,这些Node满足了该Pod的约束。

从概念上讲,Kubernetes Scheduler定义了一个过滤函数集合。给定一个Pod和一个Node,过滤函数决定该Node是否满足该Pod的约束。所有过滤函数都必须返回true才表示该Node可以运行该Pod。




Feasibility(Pod, Node,Snapshot) ==
(Filter_1(Pod, Node, Snapshot) ∧ Filter_2(Pod, Node, Snapshot) ∧ ...)


下面小节详细描述了目前一些可用的过滤函数:

1.1 可调度性和生命周期阶段(Schedulability and LifecyclePhase)

该过滤函数基于Node的可调度性和生命周期阶段来确定Node的可用。Nodeconditions通过taints和tolerations来说明(如下所示)。



pic7.jpg



图 1.1 可调度性和生命周期阶段






Filter(Pod, Node) ≝
\* Onlyconsider Nodes that accept new Pods
∧Node.Spec.Unschedulable = False
\* Onlyconsider Nodes that are ready to accept new Pods (Lifecycle Phase)
∧Node.Status.Phase = "Running"


1.2 资源需求和资源可用性

该过滤函数基于Pod的资源需求和Node的资源可用性来确定Node的可用。


pic8.jpg




图 1.2 资源需求和资源可用性











Resources(Pod, Node) ≝
∧ 1 ≤Node.Status.Allocatable["pods"]
\* Usethe maximum resource requirements of init containers
∧ Max({i \in DOMAIN p.Spec.InitContainer :p.Spec.InitContainer[i].Resources.Required["cpu"] }) ≤Node.Status.Allocatable["cpu"]
∧ Max({i \in DOMAIN p.Spec.InitContainer :p.Spec.InitContainer[i].Resources.Required["mem"] }) ≤Node.Status.Allocatable["mem"]
∧ ...
\* Usethe sum of resource requirements of main containers
∧ Sum({i \in DOMAIN p.Spec.Container :p.Spec.Container[i].Resources.Required["cpu"] }) ≤Node.Status.Allocatable["cpu"]
∧ Sum({i \in DOMAIN p.Spec.Container :p.Spec.Container[i].Resources.Required["mem"] }) ≤Node.Status.Allocatable["mem"]
∧ ...


1.3 Node Selector

该过滤函数基于Pod的node selector值和Node的label值来确定Node的可用。



pic9.jpg



图 1.3 Node Selector





Filter(Pod, Node) ==
∀ Label∈ DOMAIN(Pod.Spec.NodeSelector):
∧Label ∈ DOMAIN(Node.Labels)
∧Pod.Spec.NodeSelector[Label] = Node.Labels[Label]


1.4 Node Taints和Pod Tolerations

该过滤函数基于Pod的taints键值对和Node的tolerations键值对来确定Node的可用。



pic10.jpg



图 1.4 Node Taints和Pod Tolerations













Filter(Pod, Node) ==
∀Taint ∈ Node.Spec.Taints:
∃Toleration ∈ Pod.Spec.Tolerations: Match(Toleration, Taint)

Match(Toleration, Taint) ==
∧CASE Toleration.Operator = "Exists"
⟶ Toleration.key = Taint.key
[] Toleration.Operator = "Equal"
⟶ Toleration.key = Taint.key ∧ Toleration.value = Taint.value
[] OTHER
⟶ FALSE
∧Toleration.Effect = Taint.Effect


如果某个Node的taints匹配Pod的tolerations, 一个Pod可能被绑定到该Node。如果某个Node的taints不匹配Pod的tolerations, 一个Pod不能被绑定该Node。

1.5 亲和性

该过滤函数基于Pod需要的Node亲和项,Pod亲和项和Pod反亲和项来确定Node的可用。


pic11.jpg


图 1.4 Node Taints和Pod Tolerations


















































Filter(Pod, Node) ≝
\*Node, Affinity
∧ ∃NodeSelectorTerm ∈ Pod.Spec.Affinity.NodeAffinity.Required.NodeSelectorTerms :
Match_NS(NodeSelectorTerm, Node)
\*Pod, Affinity
∧ ∀PodAffinityTerm ∈ Pod.Spec.Affinity.PodAffinity.Required :
P_Affinity(PodAffinityTerm, Node)
\*Pod, Anit-Affinity
∧ ∀PodAffinityTerm ∈ Pod.Spec.Affinity.AntiPodAffinity.Required :
¬ P_Affinity(PodAffinityTerm, Node)

\* Node, Affinity, Match Node Selector Term
Match_NS(NodeSelectorRequirement, Node) ≝
CASENodeSelectorRequirement.Operator = "In"
⟶ (NodeSelectorRequirement.Key ∈DOMAIN(Node.Labels) ∧ Node.Labels[NodeSelectorRequirement.Key] ∈NodeSelectorRequirement.Value)
[]NodeSelectorRequirement.Operator = "NotIn"
⟶¬ (NodeSelectorRequirement.Key ∈ DOMAIN(Node.Labels) ∧Node.Labels[NodeSelectorRequirement.Key] ∈ NodeSelectorRequirement.Value)
[]NodeSelectorRequirement.Operator = "Exits"
⟶ (NodeSelectorRequirement.Key ∈DOMAIN(Node.Labels))
[]NodeSelectorRequirement.Operator = "DoesNotExist"
⟶¬ (NodeSelectorRequirement.Key ∈ DOMAIN(Node.Labels))
[]_NodeSelectorRequirement.Operator = "Gt" ∧ ∀ Value ∈NodeSelectorRequirement.Value: Value ∈ Int
⟶ (NodeSelectorRequirement.Key ∈DOMAIN(Node.Labels) ∧ Node.Labels[NodeSelectorRequirement.Key] ∈ Int ∧Node.Labels[NodeSelectorRequirement.Key] >Max(NodeSelectorRequirement.Value))
[]_NodeSelectorRequirement.Operator = "Lt" ∧ ∀ Value ∈NodeSelectorRequirement.Value: Value ∈ Int
⟶ (NodeSelectorRequirement.Key ∈DOMAIN(Node.Labels) ∧ Node.Labels[NodeSelectorRequirement.Key] ∈ Int ∧Node.Labels[NodeSelectorRequirement.Key] []OTHER
⟶FALSE

\* Pod, (Anti)Affinity, Match Pod Affinity Term
P_Affinity(PodAffinityTerm, Node) ==
IFPodAffinityTerm.TopologyKey \in DOMAIN(Node.Labels) THEN
∃Other ∈ {Other ∈ Objects : Other.Kind = "Node" ∧PodAffinityTerm.TopologyKey ∈ DOMAIN(Other.Labels) ∧Other.Labels[PodAffinityTerm.TopologyKey] =Node.Labels[PodAffinityTerm.TopologyKey]}:
∃ Pod ∈ {Pod ∈ objects : Pod.kind = "Pod" ∧ BoundTo(Pod, Node)∧ Pod.Namespace ∈ PodAffinityTerm.Namespaces}:
Match_LS(PodAffinityTerm.LabelSelector, Pod.Labels)
ELSE
FALSE

\* Pod, (Anti)Affinity, Match Label Selector
Match_LS(LabelSelector, Labels) ≝
∧ ∀Key ∈ DOMAIN(LabelSelector) : Key ∈ DOMAIN(Labels) ∧ LabelSelector[Key] =Labels[Key]
∧ ∀LabelSelectorRequirement ∈ LabelSelector.MatchExpression:
CASE LabelSelectorRequirement.Operator = "In"
⟶ (LabelSelectorRequirement.Key∈ DOMAIN(Labels) ∧ Labels[LabelSelectorRequirement.Key] ∈LabelSelectorRequirement.Values)
[] _LabelSelectorRequirement.Operator = "NotIn"
⟶ ¬ (LabelSelectorRequirement.Key ∈ DOMAIN(Labels) ∧Labels[LabelSelectorRequirement.key] ∈ LabelSelectorRequirement.Values)
[] _LabelSelectorRequirement.Operator = "Exists"
⟶ (LabelSelectorRequirement.Key∈ DOMAIN(Labels))
[] _LabelSelectorRequirement.Operator = "DoesNotExist"
⟶ ¬ (LabelSelectorRequirement.Key ∈ DOMAIN(Labels))


Node亲和
一个Pod必须分配给label匹配Pod的Node亲和需求的Node。另外,一个Pod不能分配给label不匹配Pod的Node亲和需求的Node。

Pod亲和
一个Pod必须分配给匹配TopologyKey的Node, 且该Node上至少有一个Pod匹配Pod的亲和需求。

Pod反亲和
一个Pod必须分配给匹配TopologyKey的Node, 且该Node上没有Pod匹配Pod的反亲和需求。



  1. 可行性(Viability)



对于每一个Pod,Kubernetes Scheduler确定可用的Node集合,这些Node满足了该Pod的约束。然后,Kubernetes Scheduler从可用Node集合中确定最高可行性的Node。

从概念上讲,Kubernetes Scheduler定义了一个评分函数集合。给定一个Pod和一个Node,评分函数确定Pod和Node配对的可行性。这些结果最后相加。




Viability(Pod, Node,Snapshot) ==
Sum(<>)


下面小节详细描述了目前一些可用的过滤函数:

2.1 亲和偏好

这些过滤函数基于Pod的偏好Node亲和项,Pod亲和项和Pod反亲和项,对Node的可行性进行评分。


pic12.jpg


图 1.4 Node Taints和Pod Tolerations







Rating(Pod, Node) ≝
Sum(<<
Sum(LAMBDA Term: Term.Weight, {NodeSelectorTerm ∈Pod.Spec.Affinity.NodeAffinity.Preferred.NodeSelectorTerms :Match_NS(NodeSelectorTerm, Node) }),
Sum(LAMBDA Term: Term.Weight, {PodAffinityTerm ∈Pod.Spec.Affinity.PodAffinity.Preferred : P_Affinity(PodAffinityTerm, Node) }),
Sum(LAMBDA Term: Term.Weight, {PodAffinityTerm ∈Pod.Spec.Affinity.AntiPodAffinity.Preferred : ~ P_Affinity(PodAffinityTerm,Node)})
>>)



最终评分是下列项的总和: 对于每一个匹配的Node Selector项的权重的总和 对于每一个匹配的Pod亲和项的权重的总和 * 对于每一个匹配的Pod反亲和项的权重的总和



用例分析
图6描述了包含2个不同类型的节点和2个不同类型的Pod的例子: 没有GPU资源的9个节点 有GPU资源的6个节点

这个用例的目标是保证: 不需要GPU的Pod被分配到没有GPU的节点 需要GPU的Pod被分配到有GPU的节点
pic13.jpg

灵雀云Kube-OVN:基于OVN的开源Kubernetes网络实践

灵雀云 发表了文章 • 0 个评论 • 446 次浏览 • 2019-05-09 14:34 • 来自相关话题

近日,灵雀云发布了基于OVN的Kubernetes网络组件Kube-OVN,并正式将其在Github上开源。Kube-OVN提供了大量目前Kubernetes不具备的网络功能,并在原有基础上进行增强。通过将OpenStack领域成熟的网络功能平移到Kubern ...查看全部

近日,灵雀云发布了基于OVN的Kubernetes网络组件Kube-OVN,并正式将其在Github上开源。Kube-OVN提供了大量目前Kubernetes不具备的网络功能,并在原有基础上进行增强。通过将OpenStack领域成熟的网络功能平移到Kubernetes,来应对更加复杂的基础环境和应用合规性要求。
目前Kube-OVN项目代码已经在Github 上开源,项目地址为:https://github.com/alauda/kube-ovn。项目使用宽松的Apache 2.0 协议,欢迎更多技术开发者和爱好者前去试用和使用。

网络插件那么多,为什么还需要Kube-OVN?

网络插件千千万,为什么还要开发Kube-OVN?从当前Kubernetes网络现状来看,Kubernetes 网络相关的组件非常分散。例如,CNI 负责基础容器网络,它本身只是个接口标准,社区和市场上都有很多各自的实现;集群内的服务发现网络需要依赖 kube-proxy,而 kube-proxy 又有 iptables 和 ipvs 两种实现;集群内的 DNS 需要依赖额外组件kube-dns 或coredns;集群对外访问的负载均衡器服务需要依赖各个云厂商提供的Cloud-Provider;网络策略的 NetworkPolicy 本身只是一个标准接口,社区中也有各自不同的实现。此外还有 ingress,Kubernetes提供的只是一个标准接口,社区中同样有各自的实现。
分散的网络组件导致容器网络流量被分散到了不同的网络组件上,一旦出现问题需要在多个组件间游走逐个排查。在实际运维过程中网络问题通常是最难排查的,需要维护人员掌握全链路上所有组件的使用、原理以及排查方式。因此,如果有一个网络方案能够将所有数据平面统一,那么出现问题时只需要排查一个组件即可。
其次,现有网络插件种类繁多,但是在落地时会发现,每个网络插件由于覆盖的功能集合不同,很难在所有场景使用同一套方案。为了满足不同的客户需求,很多厂商一度同时支持多种网络方案,给自身带来很大负担。在落地过程中,还发现很多传统的网络方案在容器网络中是缺失的。一些高级功能是所有网络插件都无法满足的,比如:子网划分、vlan 绑定、nat、qos、固定 IP、基于acl的网络策略等等。
现有 Kubernetes网络能力是否足够?答案很明显,如果真的已经做够强大落地的时候就不会出现这么多的问题。从更大格局来看,Kubernetes本质上是提供了一层虚拟化网络。而虚拟化网络并不是一个新问题。在OpenStack社区,虚拟网络已经有了长足的发展,方案成熟,OVS 基本已经成为网络虚拟化的标准。于是,灵雀云开始把目光投向OVS 以及 OVS 的控制器OVN。

OVN 简介

OVS 是一个单机的虚拟网络交换机,同时支持 OpenFlow 可以实现复杂的网络流量编程,这也是网络虚拟化的基础。通过OVS 和 OpenFlow 可以实现细粒度的流量控制。如果做个类比,OVS 相当于网络虚拟化里的 Docker。
OVS 只是个单机程序,想生成一个集群规模的虚拟网络就需要一个控制器,这就是 OVN。OVN 和 OVS 的关系就好比 Kubernetes 和 Docker 的关系。OVN 会将高层次的网络抽象转换成具体的网络配置和流表,下发到各个节点的OVS上,实现集群网络的管理。
由于 OVN 最初是为 OpenStack 网络功能设计的,提供了大量 Kubernetes 网络目前不存在的功能:
L2/L3 网络虚拟化包括:
• 分布式交换机,分布式路由器
• L2到L4的ACL
• 内部和外部负载均衡器
• QoS,NAT,分布式DNS
• Gateway
• IPv4/IPv6 支持

此外 OVN 支持多平台,可以在Linux,Windows,KVM,XEN,Hyper-V 以及 DPDK 的环境下运行。
综上可以看出 OVN 可以覆盖 CNI, Kube-Proxy, LoadBalancer, NetworkPolicy, DNS 等在内的所有 Kubernetes 网络功能,并在原有基础上有所增强。将之前在OpenStack领域内成熟的网络功能往 Kubernetes 平移,也就诞生了灵雀云的开源项目 Kube-OVN.

Kube-OVN 重要功能及实现

目前大部分网络插件的网络拓扑都是一个大二层网络,通过防火墙做隔离,这种形式非常类似公有云早期的经典网络。Kube-OVN在设计网络支出时同时把多租户的场景考虑进来,方便之后更好的扩展高级功能,因此采用了不同的网络拓扑。

PIC1.png


Kube-OVN 网络拓扑

在Kube-OVN的网络拓扑里,不同 Namespace 对应着不同的子网。子网是其中最为重要的概念,之后的内置负载均衡器,防火墙,路由策略以及DNS的网络功能都是和子网绑定的。我们做到了同一台机器的不同 pod 可以使用不同的子网,多个子网之间使用一个虚拟路由器进行网络的联通。为了满足Kubernetes中主机可以和容器互通的网络要求,Kube-OVN在每个主机新增一块虚拟网卡,并接入一个单独的虚拟交换机和集群的虚拟路由器相连,来实现主机和容器网络的互通。
在容器访问外部网络的策略中,Kube-OVN设计了两种方案:一种是分布式 Gateway,每台主机都可以作为当前主机上 Pod 的出网节点,做到出网的分布式。针对企业需要对流量进行审计,希望使用固定IP出网的场景,还做了和 namespace 绑定的 Gateway,可以做到一个 namespace 下的 Pod 使用一个集中式的主机出网。在整个网络拓扑中,除了集中式网关,交换机,路由器,防火墙,负载均衡器,DNS都是分布在每个节点上的,不存在网络的单点。

PIC2.png



Kube-OVN架构图

在实现的过程中,Kube-OVN对组件架构进行了大幅的简化,整个流程中只需要一个全局Controller 和分布在每台机器上的 CNI 插件,就可以组建网络,整体安装十分简单。其中全局Controller 负责监听 APIServer 事件,针对 Namespace,Pod,Service,Endpoint 等和网络相关的资源变化对 OVN 进行操作。同时 Controller 会把操作 OVN 的结果,例如 IP ,Mac,网关,路由等信息回写到对应资源的 annotation 中。而 CNI 插件则根据回写的 annotation 信息操作本机的 OVS 以及主机网络配置。通过这种架构Kube-OVN将 CNI 插件和、Controller 和 OVN 解耦,通过 Apiserver 传递所需的网络信息,方便快速开发迭代。

Kube-OVN选择使用最基础的 annotation ,而不是 CRD、API Aggregation 或者 Operator,也是出于降低复杂度的考虑,使用户和开发者都能够快速上手。

Kube-OVN主要具备五大主要功能:
1. Namespace 和子网的绑定,以及子网间访问控制;
2. 静态IP分配;
3. 动态QoS;
4. 分布式和集中式网关;
5. 内嵌 LoadBalancer;
子网是Kube-OVN中最重要的概念,由于子网和 namespace 关联,因此只需要在 namespace 中设置对应的 annotation 就可以完成子网的功能。Kube-OVN支持子网cidr,gateway,exclude_ips 以及 switch_name 的设置。同时支持基于子网的IP隔离,用户可以轻松实施基本的网络隔离策略。在后台实现上,Kube-OVN会监听 namespace 的变化,并根据变化在 ovn 中创建并设置虚拟交换机,将其和集群路由器关联,设置对应的 acl,dns 和 lb。

静态IP的使用可以直接在 Pod 中加入对应的 annotation,Controller 在发现相关 annotation 时会跳过自动分配阶段,直接使用用户指定的 IP/Mac。对应多 Pod 的工作负载,例如 Deployment、DaemonSet,可以指定一个 ip-pool,工作负载下的 Pod 会自动使用ip-pool中未使用的地址。

在QoS功能中,分别实现了 ingress 和 egress 的带宽限制,用户可以在 Pod 运行时通过动态调整 annotation 来实现 QoS 的动态调整,而无需重启 Pod。在后台的实现中, OVN 自带的 QoS 功能工作在 Tunnel 端口,无法对同主机间 Pod 的互访做 QoS 控制。因此Kube-OVN最终通过操作 OVS 的 ingress_policing_rate 和 port qos 字段来实现 QoS 的控制。

在网关设计中,OVN的网关功能有一些使用限制,需要单独的网卡来做 overlay 和 underlay 的流量交换,使用起来比较复杂,为了能够适应更广泛的网络条件并简化用户使用,Kube-OVN对网关部分进行了调整。使用策略路由的方式根据网络包的源 IP 选择下一跳的机器,通过这种方式将流量导入所希望的边界网关节点,然后在网关节点通过 SNAT 的方式对外进行访问。这种方式用户只需要在 namespace 中配置一个网关节点的 annotation 就可以配置对应的流量规则。此外,Kube-OVN也支持非SNAT将容器IP直接暴露给外网的场景,这种情况下只需要外部添加一条静态路由指向容器网络,就可以实现 Pod IP 直接和外部互通。
内嵌的 LoadBalancer 使用 OVN 内置的 L2 LB,这样集群内部的服务发现功能可以直接在 OVS 层面完成,不需要走到宿主机的 iptable 或者 ipvs 规则,可以将 kube-porxy 的功能整合到 Kube-OVN 中。

开源计划 & RoadMap

目前Kube-OVN已经在 github 上开源。OVN 安装比较繁琐,Kube-OVN特意做了安装的简化,现在只需要两个 yaml 就可以部署一个完整的 Kube-OVN。在使用方面也做了优化,通过直观的 annotation 即可对网络进行配置。此外还针对不使用任何 annotation的情况内置了一套默认配置。用户可以使用默认的子网,默认的IP分配策略,默认的分布式网关以及内嵌的负载均衡器,这些都不需要任何配置就可以默认启用。欢迎大家体验试用,多给我们提供反馈和意见。
关于Kube-OVN,近期灵雀云将主要着力于实现三大目标:第一,集中式网关的高可用,消灭整个架构中最后一个单点;第二,内嵌 DNS,去除 Kube-DNS/CoreDNS 的依赖,将整个数据平面用 OVN 进行统一;第三,NetworkPolicy实现。
长期来看,Kube-OVN未来将实现对DPDK 和 Hardware Offload 的支持,解决 Overlay 网络性能问题;将更多的 OVS 监控和链路追踪工具引入 Kubernetes;将OpenStack社区的网络功能向Kubernetes平移,打造更完整的网络体系。

Kubernetes 中的渐进式交付:蓝绿部署和金丝雀部署

灵雀云 发表了文章 • 0 个评论 • 360 次浏览 • 2019-05-07 15:27 • 来自相关话题

渐进式交付是持续交付的下一步, 它将新版本部署到用户的一个子集,并在将其滚动到全部用户之前对其正确性和性能进行评估, 如果不匹配某些关键指标,则进行回滚。 这里有一些有趣的项目,使得渐进式交付在 Kubernetes 中变得更简单。我 ...查看全部
渐进式交付是持续交付的下一步, 它将新版本部署到用户的一个子集,并在将其滚动到全部用户之前对其正确性和性能进行评估, 如果不匹配某些关键指标,则进行回滚。

这里有一些有趣的项目,使得渐进式交付在 Kubernetes 中变得更简单。我将使用一个 Jenkins X 示例项目 对它们之中的三个进行讨论:Shipper、Istio 以及 Flagger。

Shipper

shipper 是来自 booking.com 的一个项目, 它对 Kubernetes 进行了扩展,添加了复杂的部署策略和多集群编排(文档)。它支持从一个集群到多个集群的部署,允许多区域部署。
Shipper 通过一个 shipperctl 命令行进行安装。它增加不同集群的配置文件来进行管理。请注意这个与 GKE 上下文相关的问题。
Shipper 使用 Helm 包来部署,但是它们没有随着 Helm 一起安装,它们不会在 helm list 的输出显示。同样地,deployments 的版本必须是 apps/v1 , 否则 shipper 将不能编辑 deployment 来添加正确的标签和副本数量。
使用 shipper 部署都是与从旧版本(现有版本)过渡到新版本(竞争版本)相关。这是通过创建一个新的应用对象实现的, 它定义了部署需要通过的多个阶段。例如下面 3 个步骤过程:
Staging:部署新版本到一个 pod ,没有流量
50 / 50:部署新版本到 50% 的 pods,50% 的流量
Full on:部署新版本到全部的 pods,全部的流量


strategy:
steps:
- name: staging
capacity:
contender: 1
incumbent: 100
traffic:
contender: 0
incumbent: 100
- name: 50/50
capacity:
contender: 50
incumbent: 50
traffic:
contender: 50
incumbent: 50
- name: full on
capacity:
contender: 100
incumbent: 0
traffic:
contender: 100
incumbent: 0


如果发布的某个步骤没有将流量发送到 pods , 则可以使用 kubectl port-forward 访问它们,如:kubectl port-forward mypod 8080:8080, 这对于在用户看到新版本之前进行测试非常有用。
Shipper 支持多集群的概念,但是以相同的方式对待所有集群,仅使用区域并通过 capabilities (配置在集群对象中)进行筛选, 所有对一个应用对象来说,这里没有一个 dev, staging, prod 集群的选项。但是我们可以有两个应用对象:
myapp-staging 部署到 "staging" 区域
myapp 部署到其它区域
在 GKE 中,你可以轻松地配置多集群 ingress , 该入口将公开在多个集群中运行的服务,并从离你所在位置最近的集群提供服务。
局限性
Shipper 中的主要的局限性有:
Chart 限制:Chart 必须有一个部署对象。Deployment 的名称必须使用 {{.Release.Name}} 模板化。Deployment 对象应该有 apiVersion:apps/v1 。
基于 Pod 的流量切换:这里没有细粒度的流量路由,例如:发送 1% 的流量到新版本,它基于正在运行的 Pod 数量。
如果 Shipper 不工作了,新的 Pod 将获取不到流量。

Istio

Istio 不是一个部署工具,而是一个服务网格。然而,它很令人感兴趣,因为它已经变得非常流行,并且允许流量管理,例如,将一定比例的流量发送到不同的服务和其他高级网络。
在 GKE 中,只需在集群配置中选中复选框即可启用 Istio 。在其它集群中,可以通过 Helm 手动安装。
有了 Istio ,我们可以创建一个网关,通过 Ingress 网关处理所有外部流量,并创建虚拟服务来管理到我们服务的路由。为此,只需找到 ingress 网关的 ip 地址并为其配置通配符 DNS 。然后创建一个网关,通过 Ingress 网关路由所有外部流量。


apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: public-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"


Isito 不管理应用的生命周期,只管理网络。我们可以创建一个虚拟服务,为所有进入 ingress 网关的请求 向 pull request 或 master 分支中部署的服务发送 1% 的流量。


apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: croc-hunter-jenkinsx
namespace: jx-production
spec:
gateways:
- public-gateway.istio-system.svc.cluster.local
- mesh
hosts:
- croc-hunter.istio.example.org
http:
- route:
- destination:
host: croc-hunter-jenkinsx.jx-production.svc.cluster.local
port:
number: 80
weight: 99
- destination:
host: croc-hunter-jenkinsx.jx-staging.svc.cluster.local
port:
number: 80
weight: 1


Flagger
Flagger 是一个由 Weaveworks 赞助的使用了 Istio 的项目, 该项目使用 Prometheus 的指标进行自动化金丝雀发布和回滚。它超越了 Isito 提供了基于指标的自动化渐进式发布和回滚。
Flager 需要将 Istio与 Prometheus、Servicegraph 和某些系统的配置一起安装, 另外还要安装 Flager 控制器本身。它也提供了一个 Grfana 面板来监控部署进度。


pic1.jpg


部署 rollout 通过 Canary 对象定义, 它会生成主要的和金丝雀 Deployment 对象。编辑 Deployment 时,例如要使用新的镜像版本, Flagger 控制器将负载从 0% 切换到 50% ,每分钟增加 10% ,然后它将切换到新的 deployment 或者如果响应错误和请求持续时间等指标失败则进行回滚。

比较

此表总结了 Shipper 和 Flagger 在几个渐进式交付特性方面的优势和劣势。

pic2.jpg



pic3.jpg




综上所述,我看到了 Shipper 在多集群管理和简单性方面的价值,它不需要 Kubernetes 以外的任何东西,但是它有一些严重的局限性。
Flager 确实在自动部署和回滚以及对流量进行细粒度控制的过程中付出了额外的努力,它以更高的复杂性成本提供了所需的所有额外服务( Isito、Prometheus )。
这里可以查看 Shipper、Isito 和 Flager 的示例代码。

本文转自微信公众号 Jenkins

使用 Jenkins + Ansible 实现自动化部署 Nginx

灵雀云 发表了文章 • 0 个评论 • 426 次浏览 • 2019-04-29 11:00 • 来自相关话题

本文介绍如何使用 Jenkins + Ansible 实现对 Nginx 的自动化部署。最终达到的效果有如下几点: 只要你将 Nginx 的配置推送到 GitHub 中,Jenkins 就会自动执行部署,然后目标服务器的 Nginx 配置自动生效。 ...查看全部
本文介绍如何使用 Jenkins + Ansible 实现对 Nginx 的自动化部署。最终达到的效果有如下几点:
只要你将 Nginx 的配置推送到 GitHub 中,Jenkins 就会自动执行部署,然后目标服务器的 Nginx 配置自动生效。这个过程是幂等(idempotent)的,只要代码不变,执行多少遍,最终效果不变。
如果目标机器没有安装 Nginx,则会自动安装 Nginx。
自动设置服务器防火墙规则。

1. 实验环境介绍

本次实验使用 Docker Compose 搭建 Jenkins 及 Jenkins agent。使用 Vagrant 启动一台虚拟机,用于部署 Nginx。使用 Vagrant 是可选的,读者可以使用 VirtualBox 启动一个虚拟机。使用 Vagrant 完全是为了自动化搭建实验环境。
以下是整个实验环境的架构图

pic1.jpg



注意,图中的 5123 <-> 80 代表将宿主机的 5123 端口请求转发到虚拟机中的 80 端口。
Vagrant:虚拟机管理工具,通过它,我们可以使用文本来定义、管理虚拟机。
Ansible:自动化运维工具
Docker Compose:它是一个用于定义和运行多容器 Docker 应用程序的工具。可以使用 YAML 文件来配置应用程序的服务。

2. 启动实验环境

克隆代码并进入文件夹


git clone https://github.com/zacker330/jenkins-ansible-nginx.git
cd jenkins-ansible-nginx


构建 Jenkins agent 的镜像 需要自定义 Jenkins agent 镜像有两个原因:


docker build -f JenkinsSlaveAnsibleDockerfile -t jenkins-swarm-ansible .


本次实验,使用 Swarm 插件实现 Jenkins master 与 agent 之间的通信,所以 Jenkins agent 需要启动 swarm 客户端。
Jenkins agent 必须支持 Ansible。
启动 Jenkins master 及 Jenkins agent


docker-compose up -d


通过 http://localhost:8080 访问 Jenkins master,如果出现“解锁密码”页面,如下图,则执行命令 docker-compose logs jenkins 查看 Jenkins master 启动日志。将日志中的解锁密码输入到表单中。然后就一步步按提示安装即可。

pic2.jpg


安装 Jenkins 插件 本次实验需要安装以下插件:
Pipeline 2.6:https://plugins.jenkins.io/workflow-aggregator
Swarm 3.15:https://plugins.jenkins.io/swarm 用于 实现 Jenkins master 与 Jenkins agent 自动连接
Git 3.9.3:https://plugins.jenkins.io/git
配置 Jenkins master 不执行任务 进入页面:http://localhost:8080/computer/(master)/configure,如下图所示设置:

pic3.jpg


确认 Jenkins 安全配置有打开端口,以供 Jenkins agent 连接。我们设置 Jenkins master 开放的端口,端口可以是固定的 50000 ,也可以设置为随机。设置链接:http://localhost:8080/configureSecurity/。

pic4.jpg


启动目标机器,用于部署 Nginx 在命令行中执行以下命令:


vagrant up


注意,Vagrantfile 文件中的 config.vm.box 值必须改成你的 vagrant box 。
至此,实验环境已经搭建好了。接下来就可以新建 Jenkins 任务了。

3. 在 Jenkins 上创建部署任务

1、新建流水线任务

pic5.jpg


2、配置流水线 配置 Jenkins 任务从远程仓库拉取 Jenkinsfile,如下图所示:

pic5.jpg


除此之外,不需要其它配置了,是不是很简单?

4. 手工触发一次自动化构建

点击“立即构建”:

pic6.jpg


最终执行日志如下:

pic7.jpg


至此,部署已经完成。以后修改 Nginx 的配置,只需要修改代码,然后推送到远程仓库,就会自动化部署。不需要手工登录到目标机器手工修改了。
最后,我们可以通过访问 http://localhost:5123,如果出现如下页面说明部署成功:

pic8.jpg



5. 代码讲解

以上步骤并不能看出自动化部署真正做了什么。那是因为我们所有的逻辑都写在代码中。是的,可以说是 everything is code。
接下来我们介绍代码仓库。


% tree -L 2
├── JenkinsSlaveAnsibleDockerfile # Jenkins agent 镜像 Dockerfile
├── Jenkinsfile # 流水线逻辑
├── README.md
├── Vagrantfile # Vagrant 虚拟机定义文件
├── docker-compose.yml # Jenkins 实现环境
├── env-conf # 所有应用配置
│ └── dev # dev 环境的配置
├── deploy # Ansible 部署脚本所在文件夹
│ ├── playbook.yaml
│ └── roles
└── swarm-client.sh # Jenkins swarm 插件的客户端


5.1流水线逻辑

Jenkinsfile 文件用于描述整条流水线的逻辑。代码如下:


pipeline{
// 任务执行在具有 ansible 标签的 agent 上
agent { label "ansible"}
environment{
// 设置 Ansible 不检查 HOST_KEY
ANSIBLE_HOST_KEY_CHECKING = false
}
triggers {
pollSCM('H/1 [i] [/i] [i] [/i]')
}
stages{
stage("deploy nginx"){
steps{
sh "ansible-playbook -i env-conf/dev deploy/playbook.yaml"
}

}}}

environment 部分:用于定义流水线执行过程中的环境变量。
triggers 部分:用于定义流水线的触发机制。pollSCM 定义了每分钟判断一次代码是否有变化,如果有变化则自动执行流水线。
agent 部分:用于定义整条流水线的执行环境。
stages 部分:流水线的所有阶段,都被定义在这部分。
以上只是定义流水线是如何执行的,目前整条流水线只有一个 deploy nginx 阶段,并且只执行了一条 ansible-playbook 命令。但是它并没有告诉我们部署逻辑是怎么样的。

5.2 部署逻辑

所有的部署逻辑,包括 Nginx 的安装启动、配置的更新以及加载,都放在 Ansible 脚本中。对 Ansible 不熟的同学,可以在本文末尾找到介绍 Ansible 的文章。
整个部署逻辑的入口在 deploy/playbook.yaml,代码如下:


[list]
[*]hosts: "nginx"[/*]
[/list] become: true
roles:
# Nginx 的部署
- ansible-role-nginx
# 对防火墙的设置
- ansible-role-firewall



hosts:定义了 playbook 部署的目标主机分组名为 nginx。
roles:包含了两个执行具体部署动作的 role,至于 role 内部逻辑,不在本文讨论范围,有兴趣的同学阅读源码。

5.3 配置管理

谈到部署,就不得不谈配置管理。
回顾前文中流水线中执行的 shell 命令:ansible-playbook -i env-conf/dev deploy/playbook.yaml 我们通过 -i 参数指定部署时所使用的环境配置。通过这种方式实现环境配置与执行脚本的分离。这样带来以下几个好处:
新增环境时,只需要复制现有的环境,然后将里面的变量的值改成新环境的即可。比如,要对测试环境进行部署,只需要将 -i 参数值改成:env-conf/test。
对配置版本化控制。
本次实验中,各个环境的配置放在 env-conf 目录中,目前只有 dev 环境,以下是 env-conf/ 目录结构:


% cd env-conf/
% tree
└── dev
├── group_vars
│ └── nginx.yaml
├── host_vars
│ └── 192.168.52.10
└── hosts


hosts文件:Ansible 中通过“分组”来实现对主机的管理。hosts 文件内容如下:


[nginx]
192.168.52.10


host_vars 目录:用于存放主机级别的配置变量,本例中 192.168.52.10 是一个 YAML 格式文件。注意文件名是该主机的 IP。我们在文件中放主机相关的配置,比如 Ansible 连接主机时使用到的用户名和密码。
group_vars 目录:用于存放组级别的配置变量。比如 nginx.yaml 对应的就是

nginx



这个组的的配置变量。文件名与 hosts 中的组名对应

总结

到此,我们完整的自动化部署已经讲解完成。但是还遗留下一些问题:
本文只是安装了一个“空”的 Nginx,但是没有介绍 Nginx 真正配置。
目前主机的连接信息(SSH 密码)是明文写在` host_vars/192.168.52.10 `文件中的,存在安全风险。
没有介绍如何当 Java 应用部署时,如何自动更新 Nginx 的配置。
本文属于使用 Jenkins + Ansible 实现自动化部署的入门文章,笔者将根据读者的反馈决定是否写续集。
如果觉得本文讲的 Jenkins 流水线逻辑部分不够过瘾,可以考虑入手一本最近才出版的《Jenkins 2.x实践指南》。长按下图进行扫码购买。

本文转载自:微信公众号jenkins

关于 Jenkins master 共享 JENKINS_HOME 目录的实验

灵雀云 发表了文章 • 1 个评论 • 322 次浏览 • 2019-04-24 14:45 • 来自相关话题

Jenkins master 的高可用是个老大难的问题。和很多人一样,笔者也想过两个 Jenkins master 共享同一个 JENKINS_HOME 的方案。了解 Jenkins 原理的人,都会觉得这个方案不可行。但是真的不可行吗? 由于工作原 ...查看全部
Jenkins master 的高可用是个老大难的问题。和很多人一样,笔者也想过两个 Jenkins master 共享同一个 JENKINS_HOME 的方案。了解 Jenkins 原理的人,都会觉得这个方案不可行。但是真的不可行吗?
由于工作原因,笔者需要亲自验证以上猜想。

JENKINS_HOME 介绍

Jenkins 所有状态数据都存放文件系统的目录中,这个目录被称为 JENKINS_HOME 目录。
实验环境介绍
笔者通过 Docker compose 启动两个独立的 Jenkins master,分别为 jenkins-a 和 jenkins-b。它们共用同一个 JENKINS_HOME 目录。相应的代码仓库的链接放在文章底部。
将代码克隆到本地后,进入仓库,执行 docker-compose up -d 即可启动实验环境。启动完成,在浏览器中输入 http://localhost:7088 可访问 jenkins-a,jenkins-b 的地址是 http://localhost:7089 。但是你会发现它们启动后的界面显示是不一样的。

pic1.jpg


jenkins-b 的界面如下图所示:

pic2.jpg



而 jenkins-a 的界面如下图所示:

pic3.jpg



这时,将 jenkins-a 日志中的解锁密码(Unlock password)输入到 jenkins-b 的页面中,会得到报错信息:


ERROR: The password entered is incorrect, please check the file for the correct password


这时,再次 jenkins-b 日志中的解锁密码(Unlock password)输入到表单中即可进入下一步。接下来就是按照提示一步步完成了。在 jenkins-b 安装步骤的最后一步,我们设置了管理员的用户名密码:admin/admin。然后就算完成任务了。
然后我们再在 jenkins-a 使用 admin/admin 进行登录,登录是报错的:用户密码不正确。
接下来,执行 `docker-compose restart jenkins-a `docker-compose restart jenkins-a 命令重启 jenkins-a。再次使用 admin/admin 就可以登录成功了。
当两个 Jenkins 启动完成后,接下来开始做实验。

实验1:创建任务

在 jenkins-a 创建任务 x,刷新 jenkins-b 的页面,jenkins-b 上会不会显示出任务 x ?
结果:jenkins-b 不会出现任务 x。重启 jenkins-b 后,任务 x 出现在任务列表中。
实验2:任务结果可见性
jenkins-a 上任务执行,jenkins-b 上能否看到任务执行结果?
jenkins-a 执行任务 x,并且执行成功。刷新 jenkins-b 看不到任何执行记录。重启 jenkins-b 后,可看到执行记录。

实验3:两 master 同时执行同一任务

分别在两个 Jenkins master 上(几乎)开始同一个任务 x。其中一个任务的 build number 会更新,但是另一个不会。
其中 jenkins-a 任务 x 的 build number 会升到 2,而 jenkins-b 保持的是 1。这时,单独执行 jenkins-b 的任务 x,日志会出现错误:


jenkins-b_1 | WARNING: A new build could not be created in job x
jenkins-b_1 | java.lang.IllegalStateException: JENKINS-23152: /var/jenkins_home/jobs/x/builds/2 already existed; will not overwrite with x #2



实验4:编辑任务

jenkins-a 上设置任务 x 定时执行,刷新 jenkins-b 页面,任务 x 中并没有定时执行的设置。重启 jenkins-b 后,任务 x 更新。

实验5:定时任务的结果是什么?

如果 jenkins-a 和 jenkins-b 两个任务均为定时任务,而且都生效了。它们运行结果是什么的呢?
看到的现象是,两个任务都会按时执行,但是只有一个任务能将运行结果写入到磁盘中。界面如下图:

另,从日志中,可以确认 jenkins-a 和 jenkins-b 确实按时执行了。如下图日志中,看出 jenkins-a 定时执行 #6 次构建时报错,因为 jenkins-b 已经执行过 #6 次构建了:

pic4.jpg


小结
可以确认的是,当两个 Jenkins 进程共用同一个 JENKINS_HOME 目录时,其中一个 Jenkins 进程更新了 JENKINS_HOME 的内容,另一个是不会实时更新的。所以,同时启动两个 Jenkins master 共用同一个 JENKINS_HOME 的方案是不可行的。我们不能在 jenkins-a 挂了后,直接将流量切到 jenkins-b。因为 jenkins-b 必须重启。
最后结论:多个 Jenkins master 共享同一个 JENKINS_HOME 的方案是无法使用 Jenkins master 的高可用。

附录

Jenkins standby 实验环境:https://github.com/zacker330/jenkins-standby-experiment


本文转自微信公众号:Jenkins

Gartner容器市场指南中国语境:容器成为新常态,本地厂商在选择中占据优势

灵雀云 发表了文章 • 0 个评论 • 391 次浏览 • 2019-04-23 10:30 • 来自相关话题

在2019年2月“ China Summary Translation: 'Market Guide for Container Management Software'”的报告中,Gartner认为,在中国市场,容器技术的使用是近期的热点。本地厂商由于能够贴 ...查看全部
在2019年2月“ China Summary Translation: 'Market Guide for Container Management Software'”的报告中,Gartner认为,在中国市场,容器技术的使用是近期的热点。本地厂商由于能够贴近客户实际需求,而在选择中占据优势,例如阿里云、灵雀云等中国本地厂商。

关于容器的未来

为了解容器当前和未来的状态,国外研究分析师 Tom Smith近期收集了30余位积极使用容器技术的IT高管的见解。大家一致的观点认为:容器继续成熟,采用率上升,复杂度下降,Serverless兴起。

成熟

我们期待与AI,AR和VR一起使用更多的技术,随着人们使用AI轻松地开发、部署和管理容器,容器将会被大量采用和创新,会有更多的计算能力来更快地完成任务。

预计会有越来越多的人采用,容器在企业中已经被高度渗透。CNCF表示容器已经有60%-70%的部署,但是运行在Kubernetes上的计算工作负载占比要低得多。因此,Kubernetes还有巨大的增长机会。

越来越多的公司将会发现容器的好处,不仅因为可以使用容器来构建新的应用程序,而且真正开始重构现有的应用程序,并有效利用底层平台提供的水平可伸缩性等功能。企业从谈论云和容器转向在生产中使用容器,容器的使用成为主流。与此同时,围绕安全性和合规性的思考也将改变。

容器在容器编排和调度环境中提供了更好的状态管理,以及更好的执行时间,以支持无服务器的用例。

容器使用率将继续增长。推动新技术快速部署的能力不容忽视,容器的快速部署、管理和短生命周期的快速发展将推动新功能的开发。公司不得不跟上容器技术环境的快速变化,安全、编排和开发等领域都充斥着破坏的机会!

未来,容器将作为企业应用部署和管理的关键基础设施。随着技术的成熟,它会变得更加稳定、标准化和便携。希望成熟的容器技术能够带来更多的用例,诸如应用程序智能、性能表现等。

就像所有优秀的技术一样,容器变得“Boring”。解决方案提供商在包装和分销方面做得更好,将会有更多关于如何在容器周围加入信任,确保不是恶意以及防止臃肿的知识。

围绕Kubernetes的编排正在标准化,这将加速开源和商业生态系统的发展,并推动工具开发。还将看到,随着云供应商提供一致的产品,这个堆栈也将成熟起来。甚至微软、亚马逊和IBM都支持Kubernetes。五年后,不运行Kubernetes和Docker的企业将成为少数。

清晰

容器将像任何优秀的技术一样继续消失于背景中,工具使得利用技术变得更容易,容器的部署和使用将有更大的简化。

容器是一种在本地或云中构建类云应用程序的机制,容器变得更容易处理和扩展,没有单点故障,也没有单一供应商。

容器使事情变得不那么复杂,成为新的常态。开发人员希望在容器中构建所有新应用程序,人们需要改变构建的方式,首先分析应用程序,以便在发布时,可以监控从构建到生产到建设和扩展的全流程。

1)今天Kubernetes不是以app开发者为核心角色而构建的。需要让Kubernetes更易于开发人员快速启动和运行。

2)我们看到了在Knative和OpenFast等Kubernetes之上构建抽象的趋势,在Kubernetes之上部署了无服务器功能,抽象了the knobs of Kubernetes。随着越来越多的项目成熟并以原生方式运行,更多开发人员可以更轻松地使用该技术。只有28%的应用程序在容器上运行,它还处于早期阶段,我们有机会让这项技术变得更加平易近人和实用。

容器仍然太复杂。如果比较一下现在开发人员所需要的知识量,就会发现间接需要的能力比五六年前要复杂得多。五年前,如果想构建一个Python应用程序,有一些众所周知的标准。现在开发人员不仅要学习如何生成Docker镜像,还要学习如何在编排系统上部署,如何将配置传递到容器,以及所有关于安全性的细节。最终,开发人员将不必处理容器,因为更高级别的抽象是构建在容器之上的。

无服务器和FaaS已经在路上

在开发人员体验和开发速度方面,容器的价值得到坚实地证明。然而,在容器安全性方面肯定会有所改进。未来,我们设想一个更安全的容器,运行沙箱在Nano VMs,就像Kata容器或AWS Firecracker一样。Serverless函数将代替传统API应用程序的大量工作。

在运营框架和如何描述自动化之间有两个巨大的机会。Kubernetes已经成为标准。快速脚本工作。运营者有可能在非常强大的环境中实现这一目标。我们一直在寻找可以使用的80/20工具。当使用标准化的YAML语言进入Kubernetes时,标准化的应用程序自动化将为我们提供一个功能强大的地方,在这里我们可以看到一个真正的服务目录。无服务器FaaS也非常令人兴奋,因为它允许您只专注于应用程序的逻辑。

容器使每个人都可以轻松实现无服务器。没有必要依赖虚拟机,虚拟机正在消失。它更容易转向Serverless,容器随着时间的推移会有所改善。将有更多选项可以在容器内运行更多应用程序。他们将继续改变,改善,变得更加稳定,更快地从失败中恢复,同时省下大笔资金。

无服务器和FaaS已经在路上。更高级别的抽象有助于在系统中获得更小的组件。随着颗粒越来越小,必须弄清楚如何管理和知道在哪里运行,这时候 Istio作为一种服务网格产品,可以帮助跟踪所有组件。

1)在去年的KubeCon上,“Serverless”计算的概念是指向容器创新未来的一个重要话题和热点——即构建和部署几乎任何类型的应用程序,而无需配置或管理运行这些应用程序的服务器。此外,用户将根据使用模式付费,只支付所消耗的计算时间,不运行时不收费。

2) 容器最终将取代虚拟机。与vm相比,容器提供了显著的优势,如降低了部署成本、显著降低了启动性能、减少了机器占用空间,且具备易用性。随着越来越多的公司和IT组织使用容器,将会出现应用程序从虚拟机到容器的大规模迁移。

3) 容器的采用幅度将远远超出仅以Docker为主要容器类型的情况。竞争产品将被更广泛地接受和使用。Docker作为市场领导者,已经偏离了标准容器技术的开发,而是更加专注于开发和营销一个全面的应用程序开发平台。导致其他容器产品的普及和使用的大幅增长。

参考资料:

Gartner “China Summary Translation: 'Market Guide for Container Management Software'“,by Kevin Ji & Dennis Smith, Published on 18 February 2019, ID: G00382483.
2.The Future of Containers

https://dzone.com/articles/the ... ers-1
3.Cloud 2019 Predictions (Part 5)
https://dzone.com/articles/clo ... art-5
云雀科技成立于2014年10月,是中国第一家基于容器技术、服务于开发者及企业的的云计算平台。云雀科技产品线以容器(Docker)这个新一代应用交付件为中心,全方位支持云端应用创建、编译、集成、部署、运行的每一个环节。