原文:Octopus Blog
避免重复部署步骤的提示- Octopus 部署
原文:https://octopus.com/blog/tips-to-avoid-duplicating-deployment-steps
在 Octopus 中,我们强烈认为部署应该是可重复的。当您准备使用 Octopus 部署一个项目时,您需要花一些时间定义您的部署过程。在 Octopus 中,这由您在步骤选项卡上定义的一组部署步骤来表示。
我注意到人们在定义步骤时会犯一些常见的错误。为了有所帮助,您可以做两件事来减少重复部署步骤的需要。
技巧 1:在所有环境中使用相同的角色
创建包或脚本步骤时,可以指定该步骤部署到的角色:
人们有时会认为,当我选择上面的web-server
角色时,该步骤将针对该角色中的所有机器运行,而不管我部署到哪个环境。事实并非如此。当我部署到我的开发环境中时,这个步骤将只在开发环境中的web-server
机器上运行。当我部署到生产环境时,这个步骤将只在生产环境中的web-server
角色的机器上运行。
如果您的角色名称以“prod”或“uat”或“dev”结尾,很可能您没有正确使用角色。
有一种理论认为,当谈到软件时,如果用户认为它必须以某种方式工作,而事实并非如此,那么是软件而不是用户有问题。我认为这在很大程度上是正确的。在这种情况下,我怀疑这是因为从 UX 的角度来看,我们没有做好明确部署期间会发生什么的工作。如果你对我们如何使这变得更清楚有任何建议,请在下面留下评论。
技巧 2:使用变量来说明环境之间的差异
定义步骤时,许多字段允许您引用变量。您可以通过查找字段旁边的哈希按钮来判断字段是否支持它。
假设我需要将一个 Azure 云服务包部署到 Azure 中的两个不同的云服务上。我可以创建两个不同的步骤,并为每个步骤选择不同的环境。然而,更好的方法是定义适用于每个环境的变量。
然后,我可以在定义步骤时引用该变量:
希望这两个技巧能够帮助您减少需要定义的部署步骤的数量。
章鱼 TL;灾难恢复-性能改进- Octopus 部署
每周三早上,我们会召开一个简短的全公司会议,我们称之为“TL;“博士。这是一个公开邀请,任何团队成员都可以展示他们认为相关或有趣的东西,以及他们希望公司其他人知道的东西。它们涵盖了各种各样的主题,从特性设计到从支持事件中吸取的经验教训,再到开发人员在构建软件时可能会用到的模式。
作为一项实验,我们将把这些演示文稿每周公之于众——至少是那些不太机密的部分😉我希望它能让你对 Octopus 的内部有一点“幕后”的了解。
今天的 TL;DR 展示了两个关于性能的演示。Octopus 3.12 具有一些非常显著的性能改进,这要感谢一些报告问题的客户,他们能够提供跟踪和其他信息来帮助我们重现问题。
首先,Rob Erez 分享了一些即将到来的与浏览器缓存相关的改进,并减少了我们的 web 请求总数。然后,迈克尔·诺南分享了他最近使用 JetBrains dotMemory 来诊断一些内存问题的过程。我希望你喜欢!
https://www.youtube.com/embed/eByv1uuum88
VIDEO
我很想知道这是否是你感兴趣的事情,以及是否有你想看到我们讨论的话题。请在下面的评论中留言,别忘了订阅 YouTube 频道!
使用 YAML 和 XML 配置文件替换将 Java 部署到 Tomcat
原文:https://octopus.com/blog/tomcat-deployments-with-variable-replacements
将应用程序部署到不同的环境时,一个常见的挑战是确保应用程序针对特定的环境进行了正确的配置。典型的例子是用每个环境所需的凭证配置数据库连接字符串。这些类型的敏感凭证不应该存储在源代码控制中,因此必须做一些工作来获取由在本地开发环境中工作的开发人员创建的应用程序包,以创建可以部署到共享但也受限制的环境中的包。
Octopus 多年来一直有能力将值注入到 JSON 文件中,但是 Java 应用程序通常没有将 JSON 作为一种配置格式。旧的 Java 应用程序严重依赖 XML,而像 Spring 这样的新库已经采用了 YAML 和属性文件作为它们的配置。
在本文和截屏中,我将向您展示如何将 Spring 应用程序部署到 Tomcat,利用 Octopus 2020.4 中的新功能将值注入 XML、YAML 和属性文件。
截屏
https://www.youtube.com/embed/x1u2iAr_BQ4
VIDEO
准备 Linux web 服务器
对于这个例子,我使用的是 Ubuntu 20.04。为了安装 Tomcat 和管理器应用程序,我们运行:
sudo apt-get install tomcat9 tomcat9-admin
然后,我们需要通过修改/var/lib/tomcat9/conf/tomcat-users.xml
文件来创建一个可以访问管理器应用程序的用户。该文件的示例如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<role rolename="manager-script"/>
<role rolename="manager-gui"/>
<user username="admin" password="Password01!" roles="manager-gui,manager-script"/>
</tomcat-users>
如果您在访问管理器应用程序时遇到问题,可能是因为安全设置只限制从本地主机 IP 地址进行访问。该限制在文件/usr/share/tomcat9-admin/manager/META-INF/context.xml
中定义。以下是实施过滤的阀被注释掉的例子:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="false" privileged="true" >
<!-- Comment out the Valve below to remove the address filtering -->
<!--<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />-->
<Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>
生成自签名证书
要生成自签名证书,请运行以下命令:
openssl genrsa 2048 > private.pem
openssl req -x509 -new -key private.pem -out public.pem
openssl pkcs12 -export -in public.pem -inkey private.pem -out mycert.pfx
由此产生的 PFX 文件可以上传到八达通证书商店。
示例应用程序
我们正在部署的应用程序名为 Random Quotes,源代码可从 GitHub 获得。这个应用程序有两个我们想要在部署期间修改的配置文件: application.yml 和 web.xml 。这些文件混合了 Spring 使用的较新的 YAML 配置风格和 servlet 应用程序使用的较旧的 XML 风格。
具体来说,我们将在 YAML 文件中设置活动的 Spring 概要文件,并在 XML 文件中设置应用程序显示名称。
要构建应用程序,请运行命令:
mvn package
然后将target
目录下的 WAR 文件上传到 Octopus 内置的 feed 中。
使用文件副本部署应用程序
使用 Octopus 可以以两种不同的方式部署 Java 应用程序。
一般的解决方案是将 WAR 文件复制到目标机器上的一个目录中。大多数应用程序服务器都有一个目录,用于监控新文件,然后自动部署应用程序。对于 Tomcat,该目录是/var/lib/tomcat9/webapps
。
要将 Java 档案部署到目录,请使用部署 Java 档案步骤。
通过必须手动启用的结构化配置变量功能将值注入配置文件:
然后,该步骤被配置为将 Java WAR 文件部署到/var/lib/tomcat9/webapps
目录:
然后将application.yml
和web.xml
文件定义为属性替换的目标:
通过管理器部署应用程序
将应用程序部署到 Tomcat 的第二种方式是通过管理器步骤将部署到 Tomcat。这一步利用了管理器应用程序公开的 API。
为了配置这个步骤,我们需要将它指向位于 http://localhost:8080/manager 的管理器 API,并定义我们添加到tomcat-users.xml
文件中的凭证:
定义变量
我们需要定义两个变量来匹配我们想要替换的 YAML 和 XML 文件中的属性。
对于 YAML 文件,语法是一个冒号分隔的属性层次结构,产生一个变量名 spring:profiles:active 。对于 XML 文件,XPath 用作语法,这导致变量名为 //*:display-name :
配置 HTTPS
配置 HTTPS 证书是通过将证书部署到 Tomcat 步骤完成的。
它要求定义 CATALINA_HOME 和 CATALINA_BASE 目录。这些在系统服务文件中定义在/lib/systemd/system/tomcat9.service
,在我们的例子中定义为/usr/share/tomcat9
和/var/lib/tomcat9
:
然后,我们通过端口 8443 暴露 HTTPS 访问:
结论
在这个例子中,我们使用支持本地开发的通用配置文件部署了一个 web 应用程序,并在部署期间注入了在 Octopus 中定义的变量,以便为特定环境配置最终的应用程序。这演示了如何在将应用程序归档部署到共享和受限环境时,使用没有特定环境设置的应用程序归档并对其进行定制。
我们还部署了一个证书来配置 Tomcat 服务器上的 HTTPS 访问。
最终结果是一个为共享环境定制的 Spring Boot 应用程序,并安全地在 HTTPS 上公开。
愉快的部署!
将证书部署到 Tomcat - Octopus 部署
在之前的一篇文章中,我向您展示了如何将由 Octopus Deploy 管理的证书导出到 Java 密钥库中,然后配置 WildFly 来利用密钥库提供对 web 应用程序和管理端口的 HTTPS 访问。
在这篇文章中,我将向你展示如何在 Windows 中为 Tomcat 做同样的事情。
先决条件
要运行这篇博文中的命令和脚本,您需要安装一些工具。
第一个是 OpenSSL 客户机。我使用了来自 Shining Light Productions 的 Windows OpenSSL 二进制文件。
第二个是 Groovy SDK。你可以从 Groovy 下载页面下载安装程序。
这些步骤已经用 Tomcat 8 测试过了,但是应该适用于 Tomcat 6 以上的所有版本。
最后,你还需要安装 Java 8 JDK。
创建和分发证书
因为 WildFly 和 Tomcat 都使用 Java 密钥库,所以 WildFly 博文中的创建证书库和导出证书库下的指令可以完全相同的方式应用于 Tomcat。
如果您遵循这些步骤,您的 Tomcat 实例将有一个名为C:\keystore.jks
的文件。
在 Tomcat 中配置 HTTPS
为 Tomcat 配置 HTTPS 支持实际上非常简单。我们需要做两件事:
- 向引用密钥库的
server.xml
文件添加一个<Connector>
元素,如下所示:
<Connector SSLEnabled="true"
keystoreFile="C:\keystore.jks"
keystorePass="Password01"
port="8443"
protocol="org.apache.coyote.http11.Http11NioProtocol"
scheme="https"
secure="true"
sslProtocol="TLS"/>
- 确保现有的 HTTP 连接器
redirectPort
属性指向 HTTPS 端口,如下所示:
<Connector
connectionTimeout="20000"
port="8080"
protocol="HTTP/1.1"
redirectPort="8443"/>
为了促进这些改变,我们写了一个 Groovy 脚本,它将使用必要的改变来更新server.xml
文件。
运行脚本
要用 HTTPS 支持更新 Tomcat,运行如下脚本:
groovy tomcat-deploy-certificate.groovy --tomcat-dir C:\apache-tomcat-8.5.15 --https-port 8443 --http-port 8080 --keystore-file C:\keystore.jks --keystore-password Password01
就是这样!重新启动 Tomcat,您将能够通过安全连接访问它。
后续步骤
这些 Groovy 脚本正在被开发,作为最终将被移植到 Octopus Deploy 中直接提供的步骤中的概念验证。
如果你对剧本有任何问题,请留下评论。如果有一些 Java 特性你希望 Octopus 在未来部署支持,请加入 Java RFC 帖子的讨论。
通过管理器部署 Tomcat 应用程序
在上一篇文章中,我向您展示了如何通过将一个 WAR 文件提取到 Tomcat webapps
文件夹中,从 Octopus Deploy 部署到 Tomcat。除了文件副本,Tomcat 还支持通过与 Tomcat 发行版捆绑在一起的管理器应用程序部署 WAR 文件。
在本文中,我将向您展示如何通过 Tomcat 管理器在 Tomcat 中部署 WAR 文件。
配置 Tomcat
在开始上传 WAR 文件之前,我们需要做一些初始配置。
配置用户
我们需要定义与管理器交互的凭证。经理区分两种类型的用户,一种是通过浏览器使用 web 界面的用户,另一种是将使用 API 的用户。
为了定义这些用户,我们需要编辑conf/tomcat-users.xml
文件。
基于浏览器的用户被分配到manager-gui
角色,而 API 用户被分配到manager-script
角色:
<user username="manager" password="s3cret" roles="manager-gui"/>
<user username="deployer" password="s3cret" roles="manager-script"/>
定义最大文件上传大小
默认情况下,您只能通过管理器上传大约 50MB 的文件。这对于大的包来说通常不够大,所以您可以通过编辑webapps/manager/WEB-INF/web.xml
文件来设置一个更高的限制。在该文件中,您会发现一个<multipart-config>
元素,它的子元素定义了最大文件上传大小。
在这里,我将最大文件上传大小设置为 250MB:
<multipart-config>
<max-file-size>262144000</max-file-size>
<max-request-size>262144000</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>
上传战争文件
要将 WAR 文件放入 Octopus Deploy,请按照前一篇博文中的打包 WAR 文件和推送包中的说明进行操作。
然后,您可以使用中的指令将 WAR 文件提取到目标服务器上,创建一个 Octopus 部署项目。
但是这次有一个重要的不同。我们将把 WAR 文件提取到一个临时位置,而不是直接提取到webapps
文件夹。在这种情况下,我将文件提取到了C:\staging
文件夹:
为了部署文件,我们需要对http://localhost:8080/manager/text/deploy
URL 进行 HTTP PUT,在请求体中提供要部署的 WAR 文件。path
查询参数定义了将分配给应用程序的上下文路径。请注意,在路径名中有前导斜杠是很重要的。
下面的 PowerShell 命令使用 curl 来发出 HTTP 请求:
& "C:\curl.exe" -u deployer:s3cret -X PUT -F "file=@C:\staging\demo.war" http://localhost:8080/manager/text/deploy?path=/demo
确认部署
打开 http://localhost:8080/manager,并为我们之前创建的管理员用户提供凭据。您将看到应用程序部署在demo
路径下:
后续步骤
正在开发这些过程,作为最终将迁移到 Octopus Deploy 中直接提供的步骤中的概念验证。
如果您对此流程有任何疑问,请留下评论。如果有一些 Java 特性你希望 Octopus 在未来部署支持,请加入 Java RFC 帖子的讨论。
使用 Octopus Deploy - Octopus Deploy 在 Tomcat 中启用并行部署
在 Octopus,我们正在努力构建对 Java 和 Tomcat 等服务器的支持。虽然这项工作仍在进行中,但我想分享 Tomcat 的一个简洁的特性,当这项 Java 工作发布时,您将能够通过 Octopus Deploy 使用它。
博文中的截图来自 Octopus Deploy 的开发版本,在最终版本中会有变化。
并行部署
考虑这个相当典型的场景。您有一个为 web 应用程序提供服务的 Tomcat 实例,并且您想要部署一个新版本。目前使用该应用程序的用户会发生什么情况?通常,部署新版本的应用程序会使为旧版本的应用程序建立的会话失效。这对用户体验意味着什么在很大程度上取决于所涉及的应用程序,但成为那些会话刚刚终止的客户之一几乎不会有什么乐趣。
Tomcat 附带了一个很好的特性,叫做并行部署,这使得这些升级对于管理员来说更加容易,对于客户来说更加愉快。并行部署允许应用程序的两个或更多版本并行运行,允许现有会话自然完成,同时将新流量导向新部署的应用程序版本。
并行部署不能解决数据库等共享资源的变更问题。如果应用程序的新版本改变了数据库模式,旧版本的应用程序可能不再按预期工作。在决定实现并行部署时,请记住这些限制。
并行部署很容易配置,所以让我们看一个简单的例子。
通过 Octopus Deploy 部署 Tomcat
这里是我们正在构建到 Octopus Deploy 中的工作进展步骤的截图。
任何通过 Octopus Deploy 将应用程序部署到 IIS 的人都应该对此很熟悉。您选择要部署的包,并定义您要部署到的服务器的一些细节。
为了利用并行部署,您只需要为Deployment Version
字段定义一个值。唯一的要求是更新的部署要有更高的版本。在这个例子中,我们使用 Octopus 版本号作为版本,这满足了每次部署都增加版本号的要求。
部署第一个版本
一旦部署完成,您将在 Tomcat 管理器中看到类似这样的内容。在本例中,演示应用程序已经部署了版本0.0.50
。另请注意,它还没有会话。这是因为没有人开始使用该应用程序。
这是刚刚部署的演示应用程序的屏幕截图。请注意屏幕底部的版本号。这是构建应用程序时生成的时间戳。
应用程序中的时间戳和它在 Tomcat 中被分配的版本不需要相同。使这些版本相同是有益的,但是对于这个例子,我们只使用这个时间戳来区分并行部署的应用程序的两个版本。
既然已经使用了这个应用程序,Tomcat 将显示它有一个与之相关联的会话。你可以在Sessions
栏下看到这个。
部署第二个版本
现在我们有一个新版本的应用程序要部署。部署由 Octopus Deploy 处理,这一次给了我们同一个应用程序的第二个版本,现在是版本0.0.51
。
在这里,我在一个隐姓埋名的窗口中打开了应用程序。这样做意味着我没有共享在常规 Chrome 窗口中开始的会话,这意味着 Tomcat 将这个请求视为一个新的会话。从截图中可以看到,应用程序显示的时间戳是不同的,因为这是后来构建的不同的 WAR 文件。
现在,Tomcat 管理器向我们展示了演示应用程序的两个版本:0.0.50
和0.0.51
。两者都有活动会话,并且都将独立运行。现有用户继续使用版本0.0.50
,而新流量被导向版本0.0.51
。
删除旧部署
在某个时刻,针对旧版本应用程序的所有会话都将结束。当这种情况发生时,我们可以利用 Tomcat 中的一个特性,该特性会在没有用户的情况下自动取消部署旧的应用程序版本。
要启用这个特性,您需要编辑server.xml
文件,并在<Host>
元素中设置undeployOldVersions="true"
。
<Host
name="localhost"
appBase="webapps"
unpackWARs="true"
autoDeploy="true"
undeployOldVersions="true">
通常会话会在大约 30 分钟后过期,但是您可以通过使用commands
列中的Expire sessions
按钮终止空闲了一分钟的会话,并将空闲时间减少到一分钟,从而加快进程。
随着会话手动过期,应用程序版本0.0.50
不再有任何活动用户。
几秒钟后,这个旧版本将自动取消部署。
结论
如果您正在寻找一种简单的方法来管理 Java 应用程序的滚动部署,Tomcat 中的并行部署特性是一种非常方便的方法,可以将用户定向到新版本,同时在不再使用旧版本时最终清理旧版本。
八大集装箱登记处-部署八达通
容器注册中心经常与它们的存储库副本混淆,尽管它们服务于不同的目的。
容器存储库是容器化应用程序映像的存储。如今,大多数图像存储库都专注于“OCI”格式,基于 Docker 普及并向所有人开放的容器格式。事实上,“OCI 形象”和“码头形象”在注册服务商的营销中经常互换使用。
OCI 主张开放集装箱倡议。该计划是一个容器结构,旨在充当行业标准格式。技术、开发和云服务领域的大多数主要参与者都支持这一倡议,并支持 OCI 格式。在开放容器倡议网站上了解更多信息。
然后,容器注册中心既作为容器存储库的集合,又作为管理和部署映像的可搜索目录。
市场上有许多容器注册选项,为不同类型、大小和需求的客户提供服务。让我们看看我们的 8 强,以及他们为什么会有吸引力。
坞站集线器
鉴于 Docker 发明了用于容器交付的标准 OCI 格式,并被所有主要操作系统采用,因此 Docker Hub 也是图像管理的标准注册中心是有道理的。如果你正在开发,很可能你已经使用过 Docker Hub,尤其是如果你曾经关注过我们的技术指南或博客。
虽然这个列表中的所有注册表服务都可以帮助你管理 Docker 的格式,但 Docker Hub 作为一个注册表仍然值得在这里占有一席之地。不仅仅是因为它提供了一个巨大的公共注册表,你可以用它来部署来自大大小小的供应商的应用,也可以交付你自己的应用。
亚马逊 ECR
由于其集成和团队管理选项,如果您已经在使用亚马逊网络服务(AWS)来托管应用程序,亚马逊的弹性容器注册中心(ECR) 会很有用。
Amazon 的 ECR 融入了他们所有的容器托管服务,因此您可以轻松地管理和推送您的图像到:
- 弹性集装箱服务
- 弹性立方结构服务(EKS)
- 哦,舔舔
(当然,前提是你能理解所有这些相似的缩写。)
像 Docker Hub 一样,他们的公共注册中心和市场也是物有所值的,允许部署许多产品、自由软件和开源项目。您永远不会缺少可以构建的现有环境。
海港
Harbor 是一个开源注册表,你几乎可以安装在任何地方,但特别适合 Kubernetes。
在某些服务将它们的注册中心与它们自己的服务紧密结合的情况下,Harbor 的自由使它成为一个多用途的选择。它与大多数云服务以及持续集成和持续交付(CI/CD)平台兼容,也是一个很好的内部解决方案。
Azure 容器注册表
在向 AWS 提供类似服务的同时,微软的Azure Container Registry(ACR)也支持 Docker 和 OCI 图像,以及 Helm charts。
然而,Azure 最大的卖点是它的注册地理复制。这确保了每个人都能以他们习惯的速度访问图像,无论他们身在何处。
GitHub 容器注册表
鉴于 GitHub 的影响力,并且它已经对所有用户可用, GitHub 的容器注册表(称为 GitHub 包的更大功能的一部分)是最容易接近的选项之一。
考虑将 GitHub 包用于容器管理的好处包括:
- 简化的用户管理-您的大多数用户已经拥有帐户
- GitHub Actions 集成推送、发布和部署图像
- 相对于其他服务的成本
谷歌容器注册
当其他服务将他们的承诺集中在对潜在客户子集重要的某些功能上时,谷歌云的容器注册(GCR) 是一个可靠的多面手。它做了你想从容器注册表中得到的一切,而且做得非常好。
就像云服务的其他大牌一样,你可能会被锁定在 GCR,这取决于你使用的谷歌云产品。例如,Google Cloud Run 将只使用存储在 GCR 的图像,所以在选择注册服务时要记住这一点。
它不像 AWS 或 Azure 那样吹嘘自己的功能,但谷歌云的 GCR 是云提供商“三巨头”之一的一个有价值的产品。
JFrog 容器注册表
基于另一个 JFrog 产品 Artifactory, JFrog 容器注册表支持 Docker 图像和舵图。JFrog Container Registry 还提供了存储任何包类型的选项,这要归功于它的通用存储库。
JFrog 既有云和自托管两种选择(或者混合,如果你愿意的话),并承诺很好的可伸缩性。
红帽码头
与其他选择不同,红帽码头只提供私人集装箱注册。这使得它特别适合企业级客户。
Quay 不受云提供商限制,可轻松连接到 DevOps 管道两端的系统。像 Azure 一样,Quay 也包括地理位置选项,有趣的是,它支持 BitTorrent 进行集装箱配送。
Red Hat 还在其 Kubernetes 平台 OpenShift 中包含了一个精简的注册表解决方案。然而,他们确实建议大型团队和组织使用 Quay。
下一步是什么
在我们最近的一些帖子中,我们更多地讨论了容器化和云流程编排,包括:
愉快的部署!
跟踪 CI/CD 渠道中的吉拉问题- Octopus Deploy
原文:https://octopus.com/blog/track-jira-issues-across-your-ci-cd-pipeline
随着人们认识到 DevOps 提供的好处,近年来 devo PS 的采用大幅增加。许多解决方案提供集成在一起的持续集成和持续交付(CI/CD ),我们之前已经讨论过 CI 和 CD 之间的差异,但是这些解决方案很少充分利用在 DevOps 中至关重要的持续反馈循环。在这篇文章中,我将向您展示如何集成 Jenkins、Octopus Deploy 和吉拉来提供一个解决方案,使您可以轻松地跟踪 CI/CD 渠道中的问题。
安装詹金斯章鱼部署插件
Octopus Deploy 为 Jenkins 提供了一个插件,实现了其他构建平台上可用的相同功能。
安装 Octopus Deploy 插件与为 Jenkins 安装任何其他插件是一样的。当你打开 Jenkins,从登陆页面点击管理 Jenkins ,然后管理插件:
点击可用的选项卡,按Octo
过滤。勾选 Octopus Deploy 旁边的方框,选择安装而不重启或立即下载并重启后安装。
安装插件后,您将可以访问以下构建任务:
- Octopus 部署:打包应用程序
- Octopus 部署:推送包
- Octopus 部署:推送构建信息
除了构建任务之外,您还将有以下构建后任务:
- Octopus 部署:创建发布
- 章鱼部署:部署释放
Jenkins 插件与 Azure DevOps、TeamCity 和 Bamboo 的不同之处在于,创建版本和部署版本只能作为构建后操作使用。Jenkins 只允许一个 post build 动作类型,这意味着每个构建定义不能有一个以上的 create release 动作。
配置 Octopus 服务器连接
许多 Octopus 部署步骤需要连接到 Octopus 服务器。要配置连接,点击管理 Jenkins ,然后配置系统,然后向下滚动到 Octopus Deploy 插件,点击添加 Octopus Deploy 服务器:
添加您的 Octopus 服务器详情并点击保存。
Octopus 部署 CLI
Octopus Deploy 插件包含了执行这些操作所需的所有命令,但是它仍然依赖于构建代理上存在的 Octopus Deploy CLI 。下载 Octopus CLI 并将其解压缩到一个文件夹中,然后配置 Jenkins 知道它在那里。
点击管理 Jenkins ,然后点击全局工具配置。滚动到 Octopus Deploy CLI 部分,点击添加 Octopus 工具。添加工具的名称和 Octopus CLI 的路径,例如c:\octopuscli\octo.exe
。
示例构建
在这篇文章中,我构建了 PetClinic 应用程序,这是一个使用 MySQL 作为后端的 Java 应用程序。
构建设置
首先,从 Jenkins 菜单中选择一个新项目:
给你的项目命名,选择 Maven 项目。单击 OK ,您将看到构建定义的配置屏幕。
我将我的构建配置为基于下面定义的参数创建一个惟一的版本号。这个版本号将被印在构建的工件上,这些工件随后将被推送到 Octopus Deploy。我安装了几个 Jenkins 插件来实现这个功能:
- 构建名称和描述设置器
- 日期参数插件
在通用选项卡下,勾选框,该项目被参数化。
我们构建所需的参数是(所有String
参数):
- 数据库名称:
#{Project.MySql.Database.Name}
- 数据库服务器名称:
#{MySql.Database.Server.Name}
- 数据库用户名:
#{Project.MySql.Database.User.Name}
- 数据库用户密码:
#{Project.MySql.Database.User.Password}
我添加了一些可选参数来构造版本号,格式如下:1.0.2098.101603
。
可选参数:
- 专业(字符串):1
- 次要(字符串):0
- 年份(日期):
- 日期格式:yy
- 默认值:local date . now();
- 年月日:
- 日期格式:D
- 默认值:local date . now();
- 时间(日期):
- 日期格式:HHmmss
- local date . now();
定义好参数后,让我们将构建与源代码控制挂钩。我在这个构建中使用 PetClinic 公共 bit bucket repo:https://twerthi@bitbucket.org/octopussamples/petclinic.git
。点击源代码管理,选择 Git,输入 repo 的 URL。
如果您使用了上面的可选参数,单击构建环境选项卡,选中复选框设置构建名称,输入${MAJOR}.${MINOR}.${YEAR}${DAYOFYEAR}.${TIME}
作为构建名称,将构建名称设置为我们之前配置的版本号。
构建步骤
由于我们选择了 Maven 构建,Jenkins 为我们创建了构建步骤。我们需要做的就是导航到 build 选项卡,并为目标和选项输入以下内容:
clean package -Dproject.versionNumber=${BUILD_DISPLAY_NAME} -DdatabaseServerName=${DatabaseServerName} -DdatabaseName=${DatabaseName} -DskipTests -DdatabaseUserName=${DatabaseUserName} -DdatabaseUserPassword=${DatabaseUserPassword}
命令的分解:
- 清理:清理项目并移除由之前的构建生成的所有文件。
- 打包:将编译好的源代码打包成可分发的格式(jar,war,…)。
- -D:传递到构建中的参数。
这一步构建一个名为petclinic.web.Version.war
的. war 文件。在这种情况下,包 ID 是petclinic.web
。
发布步骤
我们剩下的步骤在构建定义的 post 步骤部分。这是我们为 MySQL 数据库后端打包 Flyway 项目的地方,将包和构建信息推送到 Octopus Deploy,然后创建我们的版本。
在后期步骤页签中,点击添加后期构建步骤,选择Octopus:package application,输入任务详情:
- 包装标识:
petclinic.mysql.flyway
- 版本号:
${BUILD_DISPLAY_NAME}
这是我们通过参数配置的版本号,通过上面的设置构建名选项设置。 - 包格式:
zip|nuget
- 包基础文件夹:
${WORKSPACE}\flyway
。忽略警告,它工作正常。 - 包包含路径:这里没有这个项目。
- 包输出文件夹:
${WORKSPACE}
接下来,我们定义推送步骤。点击 Post 步骤选项卡,点击 Add post-build step 下拉菜单,选择Octopus Deploy:push packages:
选择我们之前配置的 Octopus Deploy 服务器连接,然后选择您想要推送的空间(如果没有指定空间,将使用默认空间)。最后,添加要推送的包的路径。这一步接受通配符格式。路径的起始文件夹是${WORKSPACE}
,所以没有理由指定它(事实上,如果您这样做,它将失败)。
在上面我们为 Flyway 定义的Octopus Deploy:package application步骤中,我们告诉该步骤将包放在${WORKSPACE}
文件夹中。Maven build 放置 build。war 文件放在/target/
文件夹中,所以我们的包路径文件夹值是:
/*.nupkg
/target/*.war
负责推动包裹。接下来,我们将推送一些构建信息。点击添加后期构建步骤并选择Octopus Deploy:Push build information。这一步是吉拉发布说明的地方。
填写以下详细信息:
- Octopus 服务器:前面定义的服务器连接。
- 空间:你推动包裹的空间。
- 包 id:
petclinic.web
petclinic.mysql.flyway
- 版本号:
${BUILD_DISPLAY_NAME}
构建定义完成
在这个构建定义中,我们将 Jenkins 与 Octopus Deploy 集成在一起,并将 Jenkins 构建配置为从 Bitbucket 中检索发行说明,因此它们出现在 Octopus Deploy 中。让我们前往吉拉,进行集成配置。
部署吉拉与八达通集成
Octopus Deploy 已经开发了与吉拉软件的集成,以便在部署发生时,提交消息所引用的任何问题都可以回调到吉拉,并提供关于问题修复程序已部署在管道中的哪个位置的更新。
在吉拉添加八达通部署应用程序
Octopus Deploy 在吉拉市场创建了一个应用程序,便于集成。在吉拉软件登陆页面,点击吉拉设置、应用,点击查找新应用,按Octo
过滤,选择八达通部署吉拉:
点击获取 app ,点击立即获取。
看到应用程序已成功安装的消息后,点击开始。
配置吉拉和 Octopus 部署集成
在本节中,我们需要在 Octopus Deploy 和吉拉之间切换。
在继续之前,让我们打开 Octopus Deploy 并进入右侧屏幕来完成这一集成。在 Octopus Deploy 中,点击配置,然后点击设置,再点击吉拉问题跟踪器。
复制 Octopus 安装 ID 并将该值粘贴到吉拉的 Octopus 安装 ID 中(但不要单击保存):
复制吉拉基本 URL 并将该值粘贴到 Octopus Deploy 中的吉拉基本 Url 字段和吉拉问题跟踪器屏幕上。
现在,回到吉拉,复制吉拉连接应用程序密码,并将其粘贴到 Octopus Deploy 中的吉拉连接应用程序密码字段。
注意在 Octopus Deploy 中的测试按钮不会起作用直到你先点击保存在吉拉:
现在,让我们回到 Octopus Deploy,点击测试按钮测试连接工作。如果连接正常,您将看到以下屏幕:
至此,我们已经完成了吉拉的工作,但是在 Octopus Deploy 中我们还有一些事情要做。首先,让我们通过单击已启用复选框来启用 Octopus Deploy 中的集成。
要配置发行说明,请向下滚动并输入吉拉用户名和密码。
Jira Username
是你的电子邮件地址,Jira Password
是你的 API 密匙。
确保单击“测试”以确保凭据有效。
环境测绘
作为与吉拉集成的一部分,您需要将 Octopus 部署环境映射到吉拉环境类型。这是必要的,这样吉拉可以了解八达通的环境和跟踪问题的进展。请注意,吉拉环境类型是不能编辑的固定列表。为此,单击基础架构选项卡,然后单击环境,并单击该环境的省略号和编辑:
使用吉拉环境类型部分中的下拉菜单将 Octopus 部署环境与吉拉环境类型相关联。
对要映射的任何其他环境重复此过程。
Octopus 部署项目自动发布说明创建
要配置自动发行说明创建,从您的 Octopus 项目中,单击设置,并为发行说明模板输入以下内容:
#{each package in Octopus.Release.Package}
- #{package.PackageId} #{package.Version}
#{each workItem in package.WorkItems}
- [#{workItem.Id}](#{workItem.LinkUrl}) - #{workItem.Description}
#{/each}
#{/each}
(可选)您可以为部署更改模板输入以下内容:
#{each release in Octopus.Deployment.Changes}
**Release #{release.Version}**
#{release.ReleaseNotes}
#{/each}
反馈回路
随着我们的集成完成,是时候看到所有这些一起工作了。
在吉拉软件中创建一个问题
让我们在吉拉创建一个问题,点击左侧的 + :
填写问题表格,点击创建。
记下为该问题创建的 ID,因为您稍后会需要它。对于这个帖子,是PET-3
。
承诺回购
提交在 Octopus Deploy 中显示为发行说明,因此您可以看到正在部署什么。此外,如果您在提交消息中引用吉拉问题,提交将与吉拉的问题相关联。当部署发生时,八达通将更新吉拉的状态。
向您的回购协议添加一些提交;在这篇文章中,我添加了以下内容:
- 更新 pom.xml 以使用 SSL 版本的https://repo.spring.io/milestone回购
- PET-3 -更新了数据源 bean 属性,以防止数据库连接超时
- 将 bin 文件夹添加到 Flyway 项目中,以包含 JRE 的内置版本
构建项目
提交完成后,我们就可以构建项目了。构建定义中的推送构建信息步骤将包含我们的提交消息。让我们在詹金斯排队建设。
在 Jenkins 中,点击用参数构建,点击构建。
查看 Octopus Deploy 中的构建信息
构建完成后,应该可以在 Octopus Deploy 中获得这些信息。导航到库标签,点击构建信息,点击最高版本链接查看提交和工作项。
这里我们看到构建来自 Jenkins 构建服务器,并且包括我们对相关工作项(PET-3)进行的三次提交。点击 PET-3 链接将我们带到吉拉问题。
部署版本
到目前为止,这种整合看起来相当不错!部署一个版本会用状态更新吉拉。让我们开始开发部署。本文假设您已经知道如何创建项目,所以我们将跳过项目创建和部署过程的步骤。
在您的 Octopus Deploy 项目中,单击 CREATE RELEASE ,在下一个屏幕中单击 Save 之后,您将看到发布的详细信息。在这个屏幕上,我们可以看到我们的发行说明和相关的构建信息:
当该版本开始部署时,它将向吉拉发送信息。在吉拉,我们可以看到这个问题目前正在发展中。
结论
詹金斯,吉拉和章鱼部署都是强大的 DevOps 工具。当您将这三者集成在一起时,您将获得一个强大的 DevOps 解决方案,它向开发人员、操作人员和业务团队等提供持续的反馈。
观看我们最近的网络研讨会,了解如何将 Atlassian 云管道与 Octopus Deploy 集成。我们涵盖了这篇文章中的许多概念,所以请查看:
https://www.youtube.com/embed/yPjooXDJUA0
VIDEO
为什么您应该在部署后跟踪漏洞- Octopus Deploy
原文:https://octopus.com/blog/track-vulnerabilities-after-deployment
处理漏洞是软件开发中不幸但重要的一部分。有一些引人注目的例子强调了主动风险管理的重要性,证明了漏洞的责任不仅限于部署。
在这篇文章中,我探索了:
- 为什么您应该在部署软件后跟踪漏洞
- 跟踪漏洞的方法以及如何保护您的用户和企业的安全
在部署后发现漏洞
一个简单的事实是,软件提供商是在部署之后发现漏洞的,而不是之前。
这有两个语义原因:
- 如果在部署之前测试发现了潜在的漏洞,那么代码永远不会成为环境中的漏洞
- 一个未被检测到的、尚未部署的漏洞并不是真正的漏洞,除非它遇到一个有人可以利用它的环境
撇开语义不谈,知道测试不能捕捉到代码中的每个问题是至关重要的。不管你的内部测试有多严格,不管是自动化的还是其他的,问题都会悄悄出现,因为你并不总是知道要寻找什么。
因此,您不应该仅仅依赖内部部署前测试。如果你没有在部署后跟踪漏洞*,那么你将会错过一些。*
你想成为发现弱点的人
如果你发布了一个漏洞,最好在心怀不轨的人之前找到它。黑客在不受监控的应用程序和基础设施上茁壮成长,不检查漏洞让他们的生活更轻松。
未能检查、发现和解决漏洞会导致:
- 停工期
- 公司、员工和客户数据面临的风险
- 对贵公司声誉和收益的损害
你有责任保护你的用户的利益。部署后不跟踪漏洞会对您的用户造成伤害,使他们和您自己的商业利益面临风险。
你需要担心的不仅仅是那些心怀不轨的人。通常,安全研究人员和库作者会在代码依赖关系首次部署后很久才发现其中的漏洞。在发现之前经过了很长时间,这意味着其他人很有可能将漏洞包含在他们自己的软件中,并可能对其进行迭代。
这很好地把我们带到了下一点。
你不想意外地重复一个问题
DevOps 过程的短冲刺有助于限制迭代未发现问题的可能性。短距离冲刺还可以更容易地查明引入问题的更新。
如果您不跟踪部署后发生的事情,您会再次引入漏洞迭代的风险。这可能会使漏洞更难发现、故障排除和修复,以免为时过晚。
部署后检查漏洞可以节省您自己(和他人)的时间,并减轻未来的压力。
管理漏洞
现在你明白了为什么你应该在部署后跟踪漏洞,让我们看看如何。
这里有一些你可以做的积极的事情来帮助保护你的利益。
虽然您对自己产品的安全性负责,但是您所使用的工具和基础设施的提供商也是容易犯错的。即使是最大的软件和服务提供商,如微软,也需要修复漏洞。
作为另一个例子,亚马逊使用了共享责任模式。该模型概述了他们提供的 AWS 服务和他们的客户之间的期望。简单来说,AWS 负责云的安全*,客户负责云中的安全。这意味着您要对在 AWS 基础设施上运行的操作系统和软件的安全性负责。*
但是,不管您的提供商是谁,您都必须确保用于交付软件的产品是:
- 在贵公司政策允许的情况下尽可能保持最新
- 对存在紧急漏洞的地方进行修补和热修复
- 仍然遵守您对客户的承诺
您应该定期查看服务提供商的支持和安全页面,以获取保护您产品的更新信息。
跟踪行业安全新闻
在查看供应商的支持页面时,您还应该关注最新的网络安全新闻。
以下网站报告了新的漏洞和其他网络安全问题,以及安全管理建议:
使用漏洞扫描器
您还可以使用漏洞扫描器,它可以主动检查代码和工具中的已知漏洞。
代码漏洞扫描器示例:
基础设施漏洞扫描器示例:
结论
在这篇文章中,我探讨了为什么您应该在部署后检查漏洞,并研究了一些有助于管理漏洞的策略。
愉快的部署!
传统运行手册与 Octopus 运行手册- Octopus 部署
原文:https://octopus.com/blog/traditional-runbooks-vs-octopus-runbooks
我们谈论了很多关于 Octopus 如何能够完成您的持续集成和持续部署(CI/CD)管道,但是您知道 Octopus Deploy 的 Runbooks 功能也可以帮助您完成运营任务吗?
在这篇文章中,我们探索了作为 DevOps 一部分的 Octopus Runbooks 的好处,它们解决的问题,以及如何设置它们。
什么是 runbook?
简而言之,操作手册是完成常规或紧急计算机程序的一步一步的指南。
在过去的 IT 运营中,团队将这些指南整理成实体书籍或文件夹,仅在需要时才从书架上取下。如今,它们通常存在于共享网络驱动器、维基或知识库的文档中。
例如,假设一个应用服务器有一个由偶尔挂起的 Windows 服务引起的已知问题。支持团队可能有一本操作手册(或知识文章)来指导支持成员重新启动该服务。其他典型的操作手册包括:
- 数据库备份和恢复
- 服务器维护,如升级、修补或文件整理
- 服务器、服务或 web 应用程序重新启动
- 决策树的故障诊断步骤
无论是物理的还是其他的,手动操作手册都有一些问题,因为它们可能是:
- 耗时——手动完成指南和分支步骤需要时间,对于那些不熟悉系统的人来说更是如此。
- 容易出现人为错误——即使是最简单的指令,我们中最优秀的人也会犯错误。毕竟 pobody 的 nerfect!
- 过时——在繁忙的团队中,内部文档的维护经常会降低优先级。这使得操作手册过时或缺乏一致性。
- 难以管理的访问——您的运营团队可能需要访问无数的系统。风险自然会随着能接触到某样东西的人数的增加而增加,或者一个人接触到的东西越多。但是,您也不希望支持成员在真正需要时发现他们没有访问权限。
如果使用 Octopus Deploy,这些都是可以避免的问题。
八达通手册的好处
让我们来看看 Octopus 如何帮助解决传统 runbooks 的问题,以及其他一些好处。
运行手册自动化
作为一个产品,我们开发 Octopus 是因为我们相信可重复的部署意味着健壮的部署。章鱼手册遵循同样的信念。
这降低了人为错误的风险,并加快了流程。只需设置您的步骤一次,并触发一个点击操作手册。
章鱼手册已经了解你的环境
如果您是客户,您的 Octopus 实例已经连接到您部署的基础设施。您在 Octopus 中创建的任何 runbooks 也使用这些集成,因此没有什么新的操作任务需要设置。
Octopus 操作手册也不依赖于您环境的部署生命周期。您可以在需要时针对任何环境或部署目标运行它们。
Octopus Runbooks 有助于安全和节省网络管理
在我们之前的示例中,当支持人员需要在 Windows 服务器上重新启动服务时,您需要通过以下方式之一授予他们访问服务器的权限:
- 权限组
- 共享网络帐户
- 本地管理员权限
如果同一个支持团队负责许多系统(在运营中就是这种情况),那么弄清楚他们拥有或需要访问什么可能会令人困惑。
鉴于 Octopus 已经连接到您的部署目标,我们的操作手册意味着您不需要为操作任务分配对基础架构的直接访问。因此,如果随叫随到的支持人员需要重启某些东西,他们只需要访问 Octopus 和操作手册。章鱼管理其余的。
八达通有详细的审计跟踪
Octopus 还提供每个触发的运行手册的完整审计日志,因此您可以随时看到:
- 谁做了什么,什么时候,为什么
- 操作手册的成功和失败——知道什么时候以及为什么是时候更新你的操作手册了
- 事故响应报告所需的信息
如果您可以创建一个部署流程,那么您可以创建一个操作手册
创建部署和操作手册的过程是相似的。通过这两种方式,您可以使用预定义操作的组合或您喜欢的任何脚本语言来设置步骤。
让我们通过一个简单的操作手册来完成创建过程。
用 Octopus 创建一个简单的操作手册
这个例子向您展示了如何创建一个基本的 runbook,它不会影响您的任何项目或环境。如果你是现有用户,你可以跟进,或者通过注册免费试用。
如果您不想跟随,但仍然想看到最终结果,我们用这个 runbook 设置了一个示例实例,可供来宾访问。
本指南假设您已经:
本操作手册将:
- 运行“Hello World”脚本
- 当 runbook 成功完成时,通知松弛通道
请遵循以下步骤:
- 打开并登录 Octopus。
- 点击项目,从列表中选择一个已有的项目。
- 从左侧菜单中点击操作。
- 点击进入运行手册。
- 点击添加运行手册。
- 给你的新 runbook 起一个合适的名字和描述,然后点击保存。
- 点击定义您的 RUNBOOK 流程。
- 点击添加步骤。
- 选择脚本,悬停在上,从结果中运行脚本,点击添加。
- 更改以下设置,保留其他所有设置为默认设置,并点击保存:
* 步骤名称 -给步骤起一个描述性的名称。
* 执行位置——根据你的 Octopus 设置,选择在 Octopus 服务器上运行或者在 worker 上运行一次。
* 内联源代码——选择 PowerShell 单选按钮,并在代码框中输入以下内容:Write-Host 'Hello, World!'
- 现在,您可以添加在 runbook 成功完成时向 Slack 发送消息的步骤。再次点击添加步骤。
- 搜索
slack
,悬停在 Slack -从结果中发送简单通知并点击添加。如果提示将步骤保存到实例的模板中,单击保存。 - 完成以下设置,保留其他所有设置为默认,并点击保存:
* 步骤名称 -给步骤起一个描述性的名称。
* 执行位置 -根据您的 Octopus 设置,选择在 Octopus 服务器上运行或在 worker 上运行一次。
* Hook URL -复制你的 Slack Webhook URL。如果其他人可以看到你的 Octopus 实例或者你正在创建一个真正的 runbook,考虑把你的 webhook 作为一个项目变量添加进来。然后,您可以使用下面的语法#{variable-name}
安全地调用 webhook。
* 频道句柄 -输入您想要发布消息的确切空闲频道。
* 消息 -输入你希望 Octopus 发送给 Slack 的成功消息。 - 点击运行…测试运行手册。根据您的 Octopus 设置,您可能需要选择环境。如果是,选择任何环境(这并不重要,因为这个 runbook 不会改变任何东西)并再次点击运行。
- 等待运行手册完成,并检查您之前设置的消息的剩余时间。
当创建一本真正的 runbook 时,你必须点击发布以使它对其他团队成员和 Octopus 触发事件可用。
下一步是什么?
这只是一个尝试者,向你展示 Octopus Runbooks 的好处以及设置它们是多么容易。
在即将发布的系列文章中,我们将带您浏览一些真实的用例,包括如何使用 Octopus Runbooks:
- 应对漏洞
- 创建标准化的支持电子邮件
- 对您的基础设施进行冒烟测试
- 通过实例调度管理云成本
- 管理影子 IT 资源
- 计算 DORA 指标
- 用 Bash 脚本设置 Linux 服务器
同时,参见我们的 Octopus Runbook 文档获取更多的例子。
阅读我们的 Runbooks 系列的其余部分。
愉快的部署!
硒系列:特拉维斯 CI -章鱼部署
这篇文章是关于创建 Selenium WebDriver 测试框架的系列文章的一部分。
既然我们已经在一个公共的 GitHub 存储库中有了我们的代码,我们可以将它与 Travis CI 链接起来,以允许签入触发我们代码的构建和测试。
首先打开https://travis-ci.com/并点击Sign in with GitHub
按钮。
GitHub 会要求你授权 Travis CI。点击Authorize travis-pro
按钮。
你需要重新输入你的 GitHub 密码,然后点击Confirm password
按钮。
几秒钟后,您将被带到一个屏幕,在这里您可以激活 GitHub 集成。点击Active
按钮。
选择All repositories
选项,点击Approve & Install
按钮。
几秒钟后,您将看到之前创建的公共 GitHub 存储库。
单击存储库,进入构建列表。这个列表将是空的,因为我们还没有将所需的配置文件添加到存储库中,以允许 Travis CI 构建它。然而,我们现在已经成功地将 Travis CI 和 GitHub 链接在一起,这意味着 Travis CI 将监控 GitHub 存储库的变化。这是创建持续集成管道的第一步。
Travis CI 和 GitHub 现在链接在一起了,Travis CI 正在监控保存我们的 Java 应用程序的存储库的任何签入。签入将触发 Travis CI 构建我们的代码并运行我们的测试,但是为了让 Travis CI 知道如何构建我们的项目,我们需要向我们的存储库添加一个名为.travis.yml
的特殊文件。
.travis.yml
文件是 Travis CI 在其监控的任何存储库中查找的配置文件。该文件包含 Travis CI 构建代码和运行测试所需的配置。
Travis CI 在 Linux 或 MacOS 实例上执行构建。我们将使用 Linux 来进行构建,因为 Linux 有许多有用的工具,我们可以在测试中加以利用。
让我们来看看完整的.travis.yml
档案:
sudo: required
dist: trusty
language: java
jdk:
- oraclejdk8
addons:
firefox: "60.0"
before_install:
- sudo apt-get update
- sudo apt-get install dbus-x11
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- export CHROME_BIN=/usr/bin/google-chrome
- sudo apt-get install -y libappindicator1 fonts-liberation
- wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- sudo dpkg -i google-chrome*.deb
- wget https://chromedriver.storage.googleapis.com/2.38/chromedriver_linux64.zip
- unzip chromedriver_linux64.zip
- sudo cp chromedriver /usr/bin
- wget https://github.com/mozilla/geckodriver/releases/download/v0.20.1/geckodriver-v0.20.1-linux64.tar.gz
- tar -xzf geckodriver-v0.20.1-linux64.tar.gz
- sudo cp geckodriver /usr/bin
现在让我们来分解这个文件。
sudo
选项用于指示构建是否应该在可以运行sudo
命令的环境中进行。通过将该选项设置为required
,我们已经表明我们需要能够运行sudo
命令,这意味着 Travis CI 将在虚拟机内部运行该构建。如果我们将这个选项设置为false
,Travis CI 就会创建一个容器来运行构建。
容器比虚拟机快,但是因为我们需要在构建环境中安装一些额外的软件来支持运行 WebDriver 测试,所以我们必须使用虚拟机选项:
sudo: required
dist
选项配置我们的构建将运行的 Ubuntu 版本。Ubuntu 版本有一些押韵的名字,比如“精确的穿山甲”和“可靠的塔尔”。dist
选项接受这些版本的简写,这里我们已经表明我们希望使用可信的 Tahr 版本的 Ubuntu(也称为版本 14.04)。
dist: trusty
language
选项定义了存储库中代码的编程语言。我们用 Java 编写代码,所以我们将这个选项设置为java
:
language: java
jdk
选项配置用于构建代码的 JDK。您可以选择使用 OpenJDK,它是 Java 的开源实现,或者使用 Oracle JDK,它是 Oracle 提供的 Java 发行版。对于我们的代码来说,这两种选择都可以,但是我们将使用 Oracle JDK。
jdk:
- oraclejdk8
Travis CI 提供了许多常见的应用程序,这些应用程序可以通过addons
选项包含在构建环境中,Firefox 就是提供的应用程序之一。在这里,我们已经配置了要安装的 Firefox 60:
addons:
firefox: "60.0"
before_install
选项为我们提供了运行原始脚本命令的能力,以便在构建代码之前进一步定制我们的构建环境。该选项下的每个项目都作为单独的命令运行,很像脚本文件:
before_install:
apt-get
命令是在 Ubuntu 中安装包的方式。大多数 Linux 发行版都有庞大的软件库,可以用包管理器安装,Ubuntu 也不例外。像这样用一个命令就能下载、安装和更新软件的能力是 Linux 如此受开发人员欢迎的原因之一。
在我们安装任何额外的软件包之前,我们使用update
命令来刷新可用软件包的列表。这可确保我们在稍后调用apt-get
时安装任何应用程序的最新版本:
- sudo apt-get update
当从 Travis CI 环境中运行 Firefox 时,许多类似于(firefox:9067): GConf-WARNING **: Client failed to connect to the D-BUS daemon
的警告被添加到日志文件中。这些可以忽略,但是很烦。问题https://github.com/travis-ci/travis-ci/issues/8520,中指出的解决方案是安装dbus-x11
包:
- sudo apt-get install dbus-x11
接下来的两个命令配置并启动 Xvfb。
在以前的帖子中,我们讨论了一些系统是如何无头的,这仅仅意味着它们没有连接监视器。Travis CI 使用的构建环境就是一个无头环境的例子。
然而,在某些情况下,比如在 web 浏览器上运行自动化测试,拥有一个即使没有监视器也能运行桌面应用程序的环境是很有用的。Xvfb 是 X 虚拟帧缓冲区的缩写,它允许这样的桌面应用程序在无头环境中运行。Xvfb 在内存中创建一个虚拟监视器,桌面应用程序将自己“画”到这个虚拟监视器上。
Xvfb 中的 X 来自名称 X Window System,这是可以在 Travis CI 中运行的 Linux 版本所使用的窗口系统。
通过使用 Xvfb,我们可以测试没有在 headless 环境中运行的本机支持的浏览器,或者运行像 Chrome 和 Firefox 这样的老版本浏览器,这些浏览器最近才获得本机 headless 支持。
导出DISPLAY
环境变量将应用程序配置为将自己绘制到屏幕99
,这是 Xvfb 默认提供的屏幕:
- export DISPLAY=:99.0
然后我们手动启动xvbf
服务:
- sh -e /etc/init.d/xvfb start
导出CHROME_BIN
环境变量可以确保 Chrome 二进制驱动程序可以在测试中定位并启动 Chrome:
- export CHROME_BIN=/usr/bin/google-chrome
这两个命令安装 Chrome 所需的一些依赖项:
- sudo apt-get install -y libappindicator1 fonts-liberation
与 Firefox 不同,Chrome 在 Travis CI 中不作为插件提供,所以我们必须自己手动安装。这里我们用 wget(Linux 下下载文件的工具)下载 Ubuntu 的 Chrome 包,用dpkg
安装:
- wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- sudo dpkg -i google-chrome*.deb
正如我们在本地将 Chrome 二进制驱动程序安装到PATH
上的一个目录中一样,我们为 Travis CI 构建环境做了同样的事情。在这里,我们下载 Chrome 二进制驱动程序,将其解压缩,并将可执行文件复制到/usr/bin
目录。/usr/bin
目录已经在PATH
上了,这意味着任何复制到那里的可执行文件都可供我们的代码运行:
- wget https://chromedriver.storage.googleapis.com/2.38/chromedriver_linux64.zip
- unzip chromedriver_linux64.zip
- sudo cp chromedriver /usr/bin
我们对 Firefox 二进制驱动程序做同样的事情:
- wget https://github.com/mozilla/geckodriver/releases/download/v0.20.1/geckodriver-v0.20.1-linux64.tar.gz
- tar -xzf geckodriver-v0.20.1-linux64.tar.gz
- sudo cp geckodriver /usr/bin
要创建.travis.yml
文件,右击项目根文件夹并选择新➜文件。
输入文件名并点击OK
按钮。
填充.travis.yml
文件并保存更改。
我们需要将变更推送到或者签入到远程存储库中。为此,右击项目根目录并选择 Git ➜提交目录。
输入提交消息,点击Commit
按钮旁边的下拉箭头,然后点击Commit and Push
。
点击Push
按钮,将变更登记到远程存储库中。
推送完成后,新文件将显示在 GitHub 存储库中。
更重要的是,Travis CI 已经检测到了对 GitHub 存储库的推送,并使用了.travis.yml
文件中的配置来构建项目。
Travis CI 认识到我们的项目是使用 Maven 构建的,因为 pom.xml 文件的存在。然后,它将通过运行以下命令自动安装 Maven 依赖项:
mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
然后通过运行以下命令来运行测试:
mvn test -B
这一切都是自动发生的,无需任何额外的配置。这意味着当我们的代码被签入 GitHub 时,Travis CI 将获得代码的副本,并运行我们编写的所有测试。
然而有一个问题。如果我们查看日志文件的末尾,我们会看到一些测试失败了:
Results :
Tests in error:
browserStackAndroidTest(academy.learnprogramming.FormTest): Invalid username or password (WARNING: The server did not provide any stacktrace information)(..)
browserStackEdgeTest(academy.learnprogramming.FormTest): Invalid username or password (WARNING: The server did not provide any stacktrace information)(..)
Tests run: 19, Failures: 0, Errors: 2, Skipped: 1
这些测试失败了,因为 BrowserStack 测试要求将用户名和密码存储在环境变量中。幸运的是,Travis CI 提供了一种为构建定义环境变量的简单方法。
点击More Options
菜单,选择Settings
选项。
在环境变量下添加BROWSERSTACK_USERNAME
和BROWSERSTACK_KEY
的值。您可以禁用构建日志中的Display value
,这意味着这些值将从 Travis CI 生成的日志中隐藏。这是防止秘密值泄露到日志文件中的一个有用的方法。
单击左侧菜单中的 build,然后单击Restart build
。这将重新构建代码,但是这次使用新的环境变量。
这一次,构建和相关的测试成功完成。
您可能会看到如下日志消息:
GLib-GObject-CRITICAL **: g_object_ref: assertion 'object->ref_count > 0' failed
这些可以忽略,因为它们不影响测试的结果。
我们现在已经成功地将代码签入到 GitHub 中托管的中央 Git 存储库中,Travis CI 已经检测到新代码,并自动构建它并运行所有测试。这是持续集成的核心思想,这意味着每次新代码被检入时,它都会被我们的测试自动验证。
您可以在此查看同一项目的 Travis CI 构建。
这篇文章是关于创建 Selenium WebDriver 测试框架的系列文章的一部分。
尝试生章鱼-章鱼部署
卡拉马里&为你的 SSH 目标部署 Mono-free。
原始脚本
如果你已经阅读了一些我们很棒的文档,你可能会遇到下面这个简单的架构图。
这描述了一个标准的 Windows 安装,其中 Octopus 服务器使用 Calamari 通过安装的触手执行部署,触手提供通信通道。有了 SSH 部署,SSH 连接消除了对触手的需求,因此 Octopus 可以直接向远程目标上的 Calamari 发出命令
对于某些场景,这似乎还不够远。甚至运行 Calamari(及其核心依赖 mono )的需求有时也是用户没有或不想要的奢侈品。也许你正试图在无法安装 mono 的硬件负载平衡器或 IOT 设备上简单地执行一些脚本。也许 mono 在 80 年代被分回来的那个不知名的 Unix 发行版上不被支持,bash shell 也还没有被移植。出于这个原因,我们已经发布了一个隐藏的特性,叫做原始脚本,它通过一个特殊的项目变量,允许你的脚本步骤通过打开的 SSH 连接直接执行*,而不需要 Octopus 进行任何额外的包装或引导。结合 3.7.12 中新的传输包步骤,您现在可以将您的应用程序部署到您的 Linux 服务器上,甚至不知道什么是 mono!*
我们真的只是打开一个 SSH 连接,按原样传递您的脚本。
转移包裹
对于某些部署,您可能只是希望将包传输到远程目标,而不是实际解压缩。也许它需要通过脚本进一步处理或上传到另一个服务器。新的
Transfer A Package
步骤的工作方式与普通的Deploy A Package
步骤非常相似,除了没有真正提取包内容,也没有任何额外的脚本可以作为该步骤的一部分运行。在正常部署(非原始)中使用时,获取阶段可以利用增量压缩和保留策略,然后将包从其暂存位置复制到您提供的目标路径。如果在原始脚本编写期间发生包传输,则上述采集优化都不会发生(因为它们是由 Calamari 执行的),而是将包从上传暂存位置移动到目的地。
设置它
为原始脚本设置您的项目只是配置一个标准的 SSH 目标,然后添加项目级变量OctopusUseRawScript
。这个标志只对项目中的包传输和脚本步骤有影响。
我们在文档中包含了一个指南,概述了部署和运行一个简单打包脚本的简短步骤,而无需在目标上做任何额外的工作。
关于原始脚本,有一件事怎么强调都不为过,那就是我们实际上只是打开了一个 SSH 连接,并按原样传递您的脚本。这意味着您需要了解与典型 SSH 部署的一些行为差异:
- 标准的 SSH 健康检查测试各种依赖项的存在,比如 Mono 和 Calamari。如果打算在原始脚本中专门使用端点,您可能希望配置它们的机器策略来仅执行连接性测试,以避免它们的健康检查失败。该设置现在从版本
3.10.1
开始可用。
-
如果您的默认 shell 不是 bash,那么您的脚本将不会在 bash 中运行。确保您相应地配置了您的登录 shell 或编写了您的脚本。
-
get_octopusvariable
函数在你的脚本中将不再可用,因为它是由一个 Calamari 引导程序脚本提供的。相反,您可以编写脚本来利用变量替换语法 (#{MyVariable}
),该语法将在传递给远程目标并被远程目标调用之前被替换。这显然排除了只能在远程目标上解析的环境变量。 -
set_outputvariable
和new_artifact
功能也不可用。因为 Octopus Deploy 仍然会监听和响应这些服务消息,所以您可以自己编写这些服务消息来解决这个问题。查看 Calamari 源代码中的引导脚本以获取详细信息。
生吃
最终,这个特性为那些无法安装 Octopus 在 SSH 端点上运行 Calamari 所需的元素的用户提供了一种机制。虽然我们可以为 Windows 机器上的原始脚本提供额外的支持(默认情况下在 PowerShell 会话中运行,sans-Calamari ),但我们认为这是一个不太可能需要的场景,因为存在安装和运行整个触手服务的不可避免的需求。对于那些有这种需求的人(或者只是想在 SSH 上获得更简洁、更快速的部署体验),我们鼓励您尝试一下,并让我们知道您认为哪里有改进的空间。
浏览器 UI 测试的注意事项- Octopus Deploy
通过网络浏览器模拟用户交互的端到端测试已经变得越来越普遍。WebDriver 现在是一个开放的 W3 标准,所有主流的 web 浏览器都提供了 WebDriver 接口。最近的项目如 Cypress ,已经实现了他们自己的与浏览器交互的方法,专门用于编写测试。有了这样一系列得到良好支持的成熟测试平台,编写基于浏览器的测试变得前所未有的简单。
但是,在自动化 CI/CD 环境中运行基于浏览器的测试时,有一些注意事项需要考虑。在这篇博文中,我们将探讨如何运行这些测试以及测试环境的局限性。
Windows 交互式服务
在我们开始使用 Windows 进行交互式测试之前,有必要了解一下交互式服务的历史。
如果您曾经配置过 Windows 服务,您可能已经注意到了Allow service to interact with the desktop
选项:
回到 Windows XP,该选项允许 Windows 服务在第一个登录用户的桌面上绘图,该用户被分配了会话 0。从安全角度来看,这并不理想,导致了像粉碎攻击这样的攻击。
为了解决此漏洞,Windows Vista 引入了一项更改,即在会话 0 中运行服务,在会话 1 及更高版本中运行用户。这意味着用户不再暴露于服务所绘制的任何界面,这为期望用户与对话框提示交互的服务带来了向后兼容性问题。
创建 Interactive Services 检测服务是为了方便与这些提示进行交互,显示如下消息:
【T2
也可以用命令rundll32 winsta.dll,WinStationSwitchToServicesSession
切换到会话 0。
然而,最新版本的 Windows 10 已经移除了互动服务检测服务并禁止任何对会话 0 的访问。这意味着用户现在完全脱离了交互式服务,没有第三方解决方案。
此外,允许服务与桌面交互的选项在默认情况下通过NoInteractiveServices
注册表键被禁用。
底线是,虽然微软从未正式表示运行交互式服务不受支持或不被重视,但所有影响交互式服务的 Windows 更改都是为了限制或禁用它们的使用。即使您今天设法实现了一个交互式服务,假设它在将来会继续工作也是不明智的。
拯救无头浏览器
PhantomJS 推广了无头浏览器的概念。这使得自动化测试可以在非交互式环境中运行。
此后,Chrome/Chromium 和 Firefox 增加了对无头模式的支持。随着主流浏览器中的无头支持,PhantomJS 变得多余,并且不再被维护。
尽管如此,仍有许多浏览器,如 Internet Explorer 和 Edge,不支持(也可能永远不会支持)无头模式。headless 模式也没有提供测试桌面应用程序用户界面或捕获屏幕记录的解决方案。那么,当你需要一个真正的桌面来进行测试时,有什么选择呢?
Windows 交互式代理
对于 Windows,我们可以依靠 Azure DevOps 交互代理来指导微软如何支持在真实桌面上运行测试。微软的解决方案是给 Windows 机器配置自动登录,然后在启动时运行代理启动 UI 测试。这样,被测试的应用程序出现在普通用户的桌面上。
但是这个解决方案并非没有风险:
当您启用自动登录或禁用屏幕保护程序时,会有安全风险,因为您允许其他用户走近计算机并使用自动登录的帐户。如果将代理配置为以这种方式运行,则必须确保计算机受到物理保护;例如,位于安全设施中。
Octopus 的一个类似解决方案是在启动时用下面的命令运行触手(用本地触手的名称替换instance
名称):
"C:\Program Files\Octopus Deploy\Tentacle\Tentacle.exe" run --instance="Tentacle"
Linux 无头桌面
使用 xvfb ,Linux 用户可以更加灵活地在无头环境中运行应用程序。Xvfb 使用虚拟内存模拟一个哑帧缓冲区,这意味着所有应用程序都被渲染到内存中,而不需要任何显示硬件。
我们已经使用 xvfb 通过配置一个完整的 Linux 桌面环境来创建 Octopus Guides ,浏览器在这个环境中启动。这个 webdriver 脚本展示了 xvfb 是如何启动的,并且定义了DISPLAY
环境变量来实现这一点。
场外测试
有许多像 Browserstack 或 Sause Labs 这样的服务提供自动化测试服务。通过控制远程浏览器,您不再负责配置底层操作系统,大多数浏览器将提供视频录制等功能。
结论
在远程系统上运行自动化测试时,无头浏览器应该是您的首选。Linux 用户可以利用 xvfb 在无头桌面上运行应用程序。但是,如果你需要一个真正的桌面来测试 Windows 中的应用程序,启用自动登录并在启动时运行代理或触角是最好的解决方案。
使用交互式服务你可能会有一些运气,但这种选择是脆弱的,因为微软在每次更新 Windows 时都会限制和禁用交互式服务。
Kubernetes 微服务部署终极指南- Octopus Deploy
原文:https://octopus.com/blog/ultimate-guide-to-k8s-microservice-deployments
对于希望快速可靠地发布复杂系统的团队来说,微服务已经成为一种流行的开发实践。Kubernetes 是微服务的天然平台,因为它可以处理部署许多单个微服务的许多实例所需的编排。此外,服务网格技术将常见的网络问题从应用层提升到基础设施层,使路由、保护、记录和测试网络流量变得更加容易。
使用微服务、Kubernetes 和服务网格技术创建持续集成和持续交付(CI/CD)管道需要一些工作,因为健壮的 CI/CD 管道必须解决许多问题:
- 高可用性(HA)
- 多重环境
- 零停机部署
- HTTPS 和证书管理
- 功能分支部署
- 烟雾测试
- 回滚策略
在本文中,我将介绍如何通过将 Google 创建的名为 Online Boutique 的示例微服务应用程序部署到亚马逊 EKS Kubernetes 集群,配置 Istio 服务网格以处理网络路由,并深入 HTTP 和 gRPC 网络以通过集群路由租用的网络流量来测试功能分支,从而创建 CI/CD 管道的持续交付(或部署)部分。
创建 EKS 集群
即使我将微服务应用程序部署到亚马逊 EKS 托管的 Kubernetes 集群,我也不依赖 EKS 提供的任何特殊功能,因此任何 Kubernetes 集群都可以用于遵循该流程。
开始使用 EKS 最简单的方法是使用 ekscli 工具。这个 CLI 工具抽象出了与创建和管理 EKS 集群相关的大部分细节,并提供了合理的默认值来帮助您快速入门。
创建 AWS 帐户
Octopus 对通过 AWS 帐户向 EKS 集群进行身份验证提供了本机支持。该账户在基础设施➜账户下定义。
AWS 账户
创建 Kubernetes 目标
通过导航到基础设施➜部署目标,在 Octopus 中创建 Kubernetes 目标。在部署目标屏幕上,选择身份验证部分中的 AWS 帐户选项,并添加 EKS 集群的名称。
Kubernetes 目标根据 AWS 帐户进行身份验证。
在 Kubernetes Details 部分,添加 EKS 集群的 URL,并选择集群证书或选中 Skip TLS 验证选项。
该目标运行的默认名称空间在 Kubernetes 名称空间字段中定义。
部署过程中的每一步都可以覆盖名称空间,因此可以将该字段留空,并在多个名称空间中重用一个目标。但是,当在单个集群中共享多个环境时,最好设置默认的名称空间。
EKS 星团详情。
执行这些步骤的 Octopus 服务器或工作者必须在路径上有可用的kubectl
和 AWS aws-iam-authenticator
可执行文件。更多细节见文件。
安装 Istio
Istio 提供了许多安装选项,但我发现istioctl
工具是最简单的。
从 GitHub 下载一份istioctl
的副本。文件名将类似于istioctl-1.5.2-win.zip
,我们将其重命名为istioctl.1.5.2.zip
,然后上传到 Octopus。将可执行文件放在 Octopus 内置提要中,可以在脚本步骤中使用它。
向 runbook 添加一个运行 kubectl CLI 脚本步骤,并引用istioctl
包:
参考 Istio CLI 工具的附加包。
在脚本体中,执行如下所示的istioctl
,将 Istio 安装到 EKS 集群中。您可以从 Istio 文档中找到关于这些命令的更多信息。然后将istio-injection
标签添加到包含我们的应用程序的名称空间中,以启用自动 Istio sidecar 注入:
istioctl/istioctl manifest apply --skip-confirmation
kubectl label namespace dev istio-injection=enabled
您必须使用--skip-confirmation
参数来防止istioctl
永远等待工具通过 Octopus 运行时无法提供的输入。
安装 Istio 并在名称空间中启用自动边车注入的脚本。
创建 Docker 提要
构成我们的微服务应用程序的 Docker 映像将托管在 Docker Hub 中。Google 确实提供了来自他们自己的 Google Container Registry 的图像,但是这些图像没有 SemVer 兼容标签,Octopus 需要这些标签在发布创建期间对图像进行排序。我们还将创建一些特性分支映像来部署到集群,因此需要一个我们可以发布到的公共存储库。在下面的截图中,您可以看到在 Library ➜外部提要下创建的一个新的 Docker 提要:
Docker Hub Docker feed。
部署微服务
在线精品示例应用程序提供了一个 Kubernetes YAML 文件,该文件定义了运行应用程序所需的所有部署和服务。
每项服务都将作为单独的项目部署在 Octopus 中。微服务的优势之一是每个服务都有独立的生命周期,允许独立于任何其他服务进行测试和部署。为每个微服务创建单独的 Octopus 项目允许我们为该服务创建和部署版本。
YAML 文件中提到的第一个微服务叫做emailservice
。我们将在一个名为01\. Online Boutique - Email service
的项目中部署emailservice
。这个微服务有两个 Kubernetes 资源:一个部署和一个服务。这些资源的 YAML 如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: emailservice
spec:
selector:
matchLabels:
app: emailservice
template:
metadata:
labels:
app: emailservice
spec:
terminationGracePeriodSeconds: 5
containers:
- name: server
image: gcr.io/google-samples/microservices-demo/emailservice:v0.2.0
ports:
- containerPort: 8080
env:
- name: PORT
value: "8080"
# - name: DISABLE_TRACING
# value: "1"
- name: DISABLE_PROFILER
value: "1"
readinessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"]
livenessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"]
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: emailservice
spec:
type: ClusterIP
selector:
app: emailservice
ports:
- name: grpc
port: 5000
targetPort: 8080
部署和服务资源的这种配对是我们将在 YAML 文件中看到的重复模式。部署用于部署和管理实现微服务的容器。服务资源向其他微服务和前端应用程序公开这些容器,前端应用程序向最终用户公开微服务。
通过部署 Kubernetes 容器步骤,在 Octopus 中公开了组合公共 Kubernetes 资源的模式。这个固执己见的步骤为 Kubernetes 部署、服务、入口、秘密和配置映射提供了丰富的用户界面,使这个步骤成为部署我们在线精品微服务的自然选择。
从历史上看,使用部署 Kubernetes 容器步骤的一个缺点是将现有 YAML 文件中的属性翻译到用户界面中所花费的时间。每个设置都必须手动复制,这是一项艰巨的任务。
Octopus 2020.2.4 中最近添加的一个功能允许您直接编辑由该步骤生成的 YAML。单击每个 Kubernetes 资源的编辑 YAML 按钮,在一个操作中将 YAML 复制到步骤中:
编辑 YAML 按钮导入和导出 YAML。
在下面的截图中,您可以看到我粘贴了组成emailservice
部署资源的 YAML:
从微服务项目导入 YAML。
导入所提供的 YAML 中与表单公开的字段匹配的任何属性。在下面的屏幕截图中,您可以看到server
容器已经导入,包括环境设置、健康检查、资源限制和端口:
从导入的 YAML 得到的容器定义。
并不是每个可能的部署属性都被Deploy Kubernetes containers步骤所识别,未被识别的属性在导入过程中会被忽略。Deploy raw Kubernetes YAML
步骤提供了一种将通用 YAML 部署到 Kubernetes 集群的方法。然而,组成在线精品示例应用程序的微服务所使用的所有属性都是由部署 Kubernetes 容器步骤公开的。
接下来,我将服务 YAML 导入到步骤的服务部分:
服务区编辑 YAML 按钮。
导入 YAML 定义的服务资源。
服务将流量定向到与在selector
属性下定义的标签相匹配的 pod。Deploy Kubernetes containers步骤忽略了导入的服务 YAML 中的selector
属性,取而代之的是,假设部署中的 pod 都将由服务公开。以这种方式耦合部署和服务是由部署 Kubernetes 容器步骤实施的观点之一。
我们的微服务不会部署入口、秘密或配置映射资源,因此我们可以通过单击配置功能按钮从步骤中删除这些功能,并删除对未使用功能的检查:
删除未使用的配置特征。
最后一步是引用我们构建并上传到 Docker Hub 的容器。导入过程引用了部署 YAML 中定义的容器microservices-demo/emailservice
。我们需要将其更改为octopussamples/microservicedemo-emailservice
,以引用由octopus samplesDocker Hub 用户上传的容器:
更新 Docker 图像。
据此,我们创建了一个部署emailservice
的 Octopus 项目。emailservice
是组成在线精品示例应用程序的 11 个微服务之一,其他微服务称为:
checkoutservice
recommendationservice
frontend
paymentservice
productcatalogservice
cartservice
loadgenerator
currencyservice
shippingservice
redis-cart
adservice
这些微服务中的大多数都部署了与我们在emailservice
中看到的相同的部署和服务对。例外情况是loadgenerator
,它没有服务,以及frontend
,它包括一个额外的负载平衡器服务,将微服务暴露给公共流量。
额外的负载平衡器服务可以通过部署 Kubernetes 服务资源步骤进行部署。这个独立的步骤具有与部署 Kubernetes 容器步骤中相同的编辑 YAML 按钮,因此可以直接导入frontend-external
服务 YAML:
导入独立服务 YAML 定义。
与部署 Kubernetes 容器步骤不同,独立的部署 Kubernetes 服务资源步骤与部署无关,因此它导入并公开标签选择器来定义流量被发送到的 pod。
高可用性
由于 Kubernetes 负责集群中的配置单元,并内置了对单元和节点健康状况的跟踪支持,我们获得了开箱即用的合理程度的高可用性。此外,我们通常可以依靠云提供商来监控节点运行状况,重新创建故障节点,并跨可用性区域对节点进行物理配置,以降低停机的影响。
然而,我们导入的部署的默认设置需要一些调整,以使它们更有弹性。
首先,我们需要增加部署副本数量,这决定了一个部署将创建多少个单元。默认值为 1,表示任何一个 pod 出现故障都会导致微服务不可用。增加这个值意味着我们的应用程序可以承受单个 pod 的丢失。在下面的屏幕截图中,您可以看到我已经将广告服务的副本数量增加到了 2:
增加 pod 副本数量。
拥有两个 pod 是一个好的开始,但是如果这两个 pod 都是在单个节点上创建的,我们仍然会有单点故障。为了解决这个问题,我们在 Kubernetes 中使用了一个名为 pod anti-affinity 的功能。这允许我们指示 Kubernetes 更喜欢将某些 pod 部署在单独的节点上。
在下面的截图中,您可以看到我创建了一个首选的反相似性规则,该规则指示 Kubernetes 尝试将带有标签app
和值adservice
(这是该部署分配给 pod 的一个标签)的 pod 放置在不同的节点上。
拓扑关键字是指定给节点的标签名称,用于定义节点所属的拓扑组。在更复杂的部署中,拓扑关键字将用于指示细节,如节点所在的物理区域或网络注意事项。然而,在这个例子中,我们选择了一个标签来唯一地标识每个节点,这个标签叫做alpha.eksctl.io/instance-id
,有效地创建了只包含一个节点的拓扑。
最终结果是,Kubernetes 将尝试将属于同一部署的 pod 放在不同的节点上,这意味着我们的集群更有可能在失去单个节点的情况下不受影响:
定义 pod 反亲和性。
零停机部署
Kubernetes 中的部署资源为部署更新提供了两种内置策略。
首先是再造策略。该策略首先删除任何现有的 pod,然后再部署新的 pod。recreate 策略消除了两个 pod 版本共存的需要,这在诸如不兼容的数据库更改被合并到新版本中的情况下可能很重要。然而,当旧的吊舱被关闭时,它确实引入了停机时间,该停机时间持续到新的吊舱完全可操作为止。
第二种也是默认的策略是滚动更新策略。这种策略以增量方式用新的 pods 替换旧的 pods,并且可以以这样一种方式进行配置,以确保在更新期间总是有健康的 pods 可用于服务流量。滚动更新策略意味着新旧 pod 在短时间内并行运行,因此您必须特别注意确保客户端和数据存储能够支持两种 pod 版本。这种方法的好处是没有停机时间,因为一些 pod 仍然可以用来处理任何请求。
Octopus 引入了第三种部署策略,称为蓝/绿。蓝/绿策略是通过创建一个不同的新部署资源来实现的,也就是说,每个部署都有一个具有唯一名称的新部署资源。如果在Deploy Kubernetes containers步骤中定义了一个 configmap 或 secret,那么也会创建这些资源的不同的新实例。在新部署成功且所有运行状况检查都通过后,服务将更新,以将流量从旧部署切换到新部署。这允许在没有停机时间的情况下进行完全切换,并确保流量仅发送到旧的 pod 或新的 pod。
选择滚动或蓝/绿部署策略意味着我们可以零停机部署微服务:
【T2
启用滚动更新。
真正的零停机部署需要一些额外的工作,才能在更新过程中不丢失任何请求。博客文章用 Kubernetes 实现零停机滚动更新提供了一些在更新期间最小化网络中断的技巧。
功能分支部署
对于单一应用程序,功能分支部署通常是直接的;整个应用程序被构建、捆绑和部署为单个工件,并且可能由特定的数据库实例提供支持。
微服务呈现了一个非常不同的场景。当一个微服务的所有上游和下游依赖项都可以用来处理请求时,这个微服务才可能以一种有意义的方式运行。
博客文章为什么我们在优步的微服务架构中利用多租户讨论了在微服务架构中执行集成测试的两种方法:
- 平行测试
- 生产中的测试
这篇博文详细介绍了这两种策略的实施,但总结起来:
- 并行测试包括在一个共享的临时环境中测试微服务,该环境的配置类似于生产环境,但与生产环境相隔离。
- 生产中的测试包括将测试中的微服务部署到生产中,使用安全策略将其隔离,将所有静态数据分类为测试或生产数据,并将不同的流量子集定向到测试微服务。
这篇博客文章继续提倡在生产中进行测试,列举了并行测试的这些局限性:
- 额外硬件成本
- 同步问题(或试运行和生产环境之间的偏差)
- 不可靠的测试
- 不准确的容量测试
很少有开发团队会像优步那样接受微服务,所以我怀疑对于大多数团队来说,部署微服务功能分支将涉及到介于优步描述的并行测试和生产测试之间的解决方案。具体来说,在这里我们将了解如何在暂存环境中将微服务功能分支与现有版本一起部署,并将一部分流量定向到该分支。
通过利用硬件和网络隔离,将功能分支部署到临时环境中消除了干扰生产服务的风险,从而消除了在生产环境中实施这种隔离的需要。它还消除了划分或识别测试和生产数据的需要,因为在试运行环境中创建的所有数据都是测试数据。
在开始之前,我们需要简要回顾一下什么是服务网格,以及我们如何利用 Istio 服务网格来独立于微服务引导流量。
什么是服务网格?
在服务网格出现之前,网络功能很像老式的电话交换机。应用程序类似于个人打电话;他们知道他们需要与谁通信,像 NGINX 这样的反向代理将作为总机操作员来连接双方。只要交易中的所有各方都是众所周知的,并且他们交流的方式是相对静态和非专业化的,这种基础设施就可以工作。
微服务类似于手机的兴起。有更多的设备需要连接在一起,以不可预测的方式在网络中漫游,每个设备通常都需要自己独特的配置。
服务网格旨在适应大量服务相互通信的日益复杂和动态的需求。在服务网格中,每个服务负责定义它将如何接受请求;它需要哪些常见的网络功能,如重试、断路、重定向和重写。这避免了所有流量都必须通过的中央交换机,并且在大多数情况下,单个应用程序不需要知道它们的网络请求是如何被处理的。
服务网格是提供大量功能的丰富平台,但为了部署微服务功能分支,我们最感兴趣的是检查和路由网络流量的能力。
我们在路由什么流量?
下面是架构图,显示了组成在线精品的各种微服务,以及它们如何通信:
微服务应用架构。
请注意,在此图中,来自互联网的公共流量通过前端进入应用程序。这个流量是普通的 HTTP。
微服务之间的通信然后通过 gRPC 执行,它是:
一个高性能、开源的通用 RPC 框架
重要的是,gRPC 使用 HTTP2。因此,要将流量路由到微服务功能分支部署,我们需要检查和路由 HTTP 流量。
路由 HTTP 流量
Istio HTTPMatchRequest 定义了可用于匹配 HTTP 流量的请求的属性。这些属性相当全面,包括 URI、方案、方法、头、查询参数、端口等等。
为了将流量子集路由到功能分支部署,我们需要能够以不干扰请求中包含的数据的方式将附加信息作为 HTTP 请求的一部分进行传播。方案(即 HTTP 或 HTTPS)、方法(GET、PUT、POST 等)。)、端口、查询参数(问号后面的 URI 部分)和 URI 本身都包含特定于所做请求的信息,不能修改这些信息。这样就剩下头了,头是键值对,经常被修改以支持请求跟踪、代理和其他元数据。
查看浏览器在与在线精品前端交互时提交的网络流量,我们可以看到,Cookie
报头可能包含一个有用的值,我们可以检查该值以做出路由决策。该应用程序保存了一个 cookie,它带有一个标识浏览器会话的 GUID,事实证明,这就是该示例应用程序用来标识用户的方法。显然,在现实世界的例子中,身份验证服务用于识别用户,但是对于我们的目的,随机生成的 GUID 就足够了。
从浏览器捕获网络流量。
有了 HTTP 头,我们可以检查和路由。下一步是部署特性分支。
创建特征分支 Docker 图像
在线精品已经用多种语言编写,前端组件用 Go 编写。我们将对标题模板做一个小小的修改,以包含文本 MyFeature ,使其清楚地表示我们的特性分支。
我们将从这个分支构建一个 Docker 映像,并将其发布为octopussamples/microservicedemo-frontend:0.1.4-myfeature
。注意,0.1.4-myfeature
的标签是一个 SemVer 字符串,它允许这个图像被用作 Octopus 部署的一部分。
部署第一个功能分支
我们在部署前端应用程序的 Octopus 项目中定义了两个通道。
默认通道有一个版本规则,要求 SemVer 预发布标签为空,正则表达式为^$
。这个规则确保这个频道只匹配版本(或者在我们的例子中是 Docker 标签),比如0.1.4
。
特征分支通道有一个版本规则,要求 SemVer 预发布标签不为空,正则表达式为.+
。这个频道会匹配0.1.4-myfeature
这样的版本。
然后,我们向部署添加一个名为FeatureBranch
的变量,其值为#{Octopus.Action.Package[server].PackageVersion | Replace "^([0-9\.]+)((?:-[A-Za-z0-9]+)?)(.*)$" "$2"}
。该变量获取名为server
的 Docker 图像版本,在正则表达式中捕获预发布和前导破折号作为 group 2,然后只打印 group 2。如果没有预发布,变量将解析为空字符串。
用于提取 SemVer 预发布的变量。
然后,将该变量附加到部署名称、部署标签和服务名称之后。更改部署和服务的名称可以确保特性分支部署在名为frontend
的现有资源旁边创建名为frontend-myfeature
的新资源:
摘要文本显示部署的名称和标签
摘要文本显示服务的名称
通过 Istio 暴露前端
到目前为止,我们还没有部署任何 Istio 资源。包含我们的应用程序的名称空间上的istio-injection
标签意味着由我们的部署创建的 pod 包括 Istio sidecar,准备拦截和路由流量,但正是普通的旧 Kubernetes 服务将我们的 pod 相互公开。
为了开始使用 Istio 路由我们的内部流量,我们需要创建一个虚拟服务:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: frontend
spec:
gateways:
- istio-system/ingressgateway
hosts:
- '*'
http:
- match:
- headers:
Cookie:
exact: shop_session-id=4f9e715d-fe56-4a1e-964b-b00b607e7695
route:
- destination:
host: frontend-myfeature
- route:
- destination:
host: frontend
这项虚拟服务有几个重要部分:
gateway
被设置为istio-system/ingressgateway
,这是安装 Istio 时创建的网关。该网关依次接受来自负载平衡器服务的流量,该服务也是在istio-system
名称空间中创建的,这意味着要访问我们的应用程序并通过该虚拟服务路由流量,我们需要通过 Istio 负载平衡器服务的公共主机名来访问应用程序。http
属性下的第一项指定其Cookie
报头与指定值匹配的传入流量将被定向到frontend-myfeature
服务。- 任何其他流量都被发送到
frontend
服务。
有了这些规则,我们可以重新打开应用程序,我们的请求被重定向到 feature 分支,如标题所示:
Istio 检查了 Cookie 头并将请求定向到特性分支。
然而,这种重定向只是挑战的一半。我们已经成功检查了应用程序已经添加的 HTTP 头,并将 web 流量定向到面向公众的前端应用程序的功能分支。但是重定向内部 gRPC 调用呢?
gRPC 路由
就像普通的 HTTP 请求一样,gRPC 请求也可以公开用于路由的 HTTP 头。任何与 gRPC 请求相关联的元数据都被公开为 HTTP 头,然后可以被 Istio 检查。
前端应用程序向许多其他微服务发出 gRPC 请求,包括广告服务。为了启用 ad 服务的特性分支部署,我们需要将用户 ID (实际上只是会话 ID,但是我们将这两个值视为同一事物)与 gRPC 请求一起从前端传播到 ad 服务。
为此,我们向getAd()
方法添加一个名为userID
的属性,创建一个名为metactx
的新上下文,通过userid
元数据属性公开用户 ID,并使用上下文metactx
发出 gRPC 请求:
func (fe *frontendServer) getAd(ctx context.Context, userID string, ctxKeys []string) ([]*pb.Ad, error) {
ctx, cancel := context.WithTimeout(ctx, time.Millisecond*100)
defer cancel()
// This is where we add the metadata, which in turn is exposed as HTTP headers
metactx := metadata.AppendToOutgoingContext(ctx, "userid", userID)
resp, err := pb.NewAdServiceClient(fe.adSvcConn).GetAds(metactx, &pb.AdRequest{
ContextKeys: ctxKeys,
})
return resp.GetAds(), errors.Wrap(err, "failed to get ads")
}
元数据包导入时带有:
metadata "google.golang.org/grpc/metadata"
注意,调用getAd()
函数的函数链也必须更新以传递新的参数,在顶层,通过调用sessionID(r)
找到用户 ID,其中r
是 HTTP 请求对象。
我们现在可以创建一个虚拟服务,根据userid
HTTP 头将前端应用程序发出的请求路由到广告服务,这就是 gRPC 元数据键值对的公开方式:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: adservice
spec:
hosts:
- adservice
http:
- match:
- headers:
userid:
exact: 4f9e715d-fe56-4a1e-964b-b00b607e7695
route:
- destination:
host: adservice-myfeature
- route:
- destination:
host: adservice
手动编辑虚拟服务可能很繁琐,随着更多分支的部署或更多测试用户的配置,我们将需要一个更加自动化的解决方案来编辑虚拟服务。
下面是一个 PowerShell 脚本,它读取当前虚拟服务,为给定用户添加或替换重定向规则,并将更改保存回 Kubernetes。该代码可以保存为操作手册(因为流量切换是一个独立于部署的过程),并且变量SessionId
和Service
可以在每次运行之前被提示:
# Get the current virtual service
$virtService = kubectl get virtualservice adservice -o json | ConvertFrom-JSON
# Clean up the generated metadata properties, as we do not want to send these back
$virtService.metadata = $virtService.metadata |
Select-Object * -ExcludeProperty uid, selfLink, resourceVersion, generation, creationTimestamp, annotations
# Get the routes not associated with the new session id
$otherMatch = $virtService.spec.http |
? {$_.match.headers.userid.exact -ne "#{SessionId}"}
# Create a new route for the session
$thisMatch = @(
@{
match = @(
@{
headers = @{
userid = @{
exact = "#{SessionId}"
}
}
}
)
route = @(
@{
destination = @{
host = "#{Service}"
}
}
)
}
)
# Append the other routes
$thisMatch += $otherMatch
# Apply the new route collection
$virtService.spec.http = $thisMatch
# Save the virtual service to disk
$virtService | ConvertTo-JSON -Depth 100 | Set-Content -Path vs.json
# Print the contents of the file
Get-Content vs.json
# Send the new virtual service back to Kubernetes
kubectl apply -f vs.json
一个更健壮的解决方案可能包括编写一个定制的 Kubernetes 操作符来保持虚拟服务资源与定义测试流量的外部数据源同步。这篇文章不会深入讨论操作符的细节,但是你可以从文章中找到更多的信息,用 Kotlin 创建一个 Kubernetes 操作符。
部署内部功能分支
就像我们对前端应用程序所做的一样,广告服务的一个分支已经被创建并作为octopussamples/microservicedemo-adservice:0.1.4-myfeature
被推送。Octopus 中的广告服务项目获得了设置为#{Octopus.Action.Package[server].PackageVersion | Replace "^([0-9\.]+)((?:-[A-Za-z0-9]+)?)(.*)$" "$2"}
的新的FeatureBranch
变量,部署和服务的名称也更改为adservice#{FeatureBranch}
。
特性分支本身被更新为将字符串MyFeature
附加到由服务提供的广告上,以允许我们看到特性分支部署何时被调用。部署虚拟服务后,我们再次打开 web 应用程序,发现我们收到的广告确实包含字符串MyFeature
:
Istio 根据 userid 报头将内部 gRPC 请求路由到广告服务功能分支。
摘要
为了在微服务环境中部署用于集成测试的功能分支,在不干扰其他流量的情况下测试特定请求是有用的。通过在 HTTP 头和 gRPC 元数据中公开路由信息(进而公开为 HTTP 头),Istio 可以将特定流量路由到功能分支部署,而所有其他流量则通过常规主线微服务部署。
这可能足以在测试环境中部署微服务功能分支。如果您的特性分支发生故障,并在测试数据库中保存无效数据,这是不方便的,但不会导致生产中断。同样,将无效消息放在消息队列中可能会破坏测试环境,但是您的生产环境是隔离的。
优步的博客文章提供了一个诱人的视角,展示了如何将部署特性分支的想法扩展到生产环境中进行测试。但是,请注意,这篇文章明确指出,租用信息必须随所有请求一起传播,与所有静态数据一起保存,并且可选地将测试数据隔离在单独的数据库和消息队列中。此外,安全策略需要到位,以确保测试微服务不会失控,不会与它们不应该交互的服务进行交互。这篇博文不会涉及这些额外的需求,但是部署和与微服务特性分支交互的能力是一个很好的基础。
HTTPS 和证书管理
为了允许通过 HTTPS 安全地访问我们的应用程序,我们将配置秘密发现服务。
第一步是部署入口网关代理,这是通过使用istioctl
工具生成 YAML 文档并应用它来实现的。正如我们在安装 Istio 本身时所做的那样,istioctl
包引用被添加到作为 runbook 的一部分运行 kubectl CLI 脚本的运行步骤中。部署代理的脚本如下所示:
istioctl\istioctl manifest generate `
--set values.gateways.istio-egressgateway.enabled=false `
--set values.gateways.istio-ingressgateway.sds.enabled=true > `
istio-ingressgateway.yaml
kubectl apply -f istio-ingressgateway.yaml
使用 istioctl 启用代理。
下一步是将 HTTPS 证书和私钥的内容保存为秘密。在这篇文章中,我使用了由 Let’s Encrypt 通过我的 DNS 提供商 dnsimple 生成的证书,并下载了 PFX 证书包。该软件包在将证书部署到 IIS 的说明下提供,但是 PFX 文件本身是通用的。我们使用 PFX 文件,因为它是独立的,很容易上传到 Octopus。
让我们加密由 DNS 提供商生成的证书。
PFX 文件作为新证书上传到库➜证书下:
把咱们的加密证书上传到八达通。
要将证书导入 Kubernetes,我们需要创建一个秘密。我们从引用名为Certificate
的变量中的证书开始:
作为变量引用的证书。
然后,证书的内容被保存到两个文件中,一个保存证书,另一个保存私钥。证书变量在 Octopus 中很特殊,因为它们公开了许多由生成的属性,包括PrivateKeyPem
和CertificatePem
。
这两个文件的内容依次保存到一个名为octopus.tech
的秘密中:
Set-Content -path octopus.tech.key -Value "#{Certificate.PrivateKeyPem}"
Set-Content -path octopus.tech.crt -Value "#{Certificate.CertificatePem}"
kubectl delete -n istio-system secret octopus.tech
kubectl create -n istio-system secret generic octopus.tech `
--from-file=key=octopus.tech.key `
--from-file=cert=octopus.tech.crt
kubectl get -n istio-system secret octopus.tech -o yaml
然后ingressgateway
网关被更新以暴露端口 443 上的 HTTPS 流量。credentialName
属性与我们上面创建的秘密的名称相匹配,并且mode
被设置为SIMPLE
以启用标准 HTTPS(与MUTUAL
相反,它配置相互 TLS,这通常对公共网站没有用):
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ingressgateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: octopus.tech
hosts:
- "*"
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
做出这一更改后,我们可以通过 HTTPS 和 HTTP:
通过 HTTPS 访问网站。
烟雾测试
Kubernetes 提供了内置支持,用于在 pod 被标记为健康并投入使用之前检查其状态。就绪探测器确保在首次创建 pod 时,该 pod 处于健康状态并准备好接收流量,而活动探测器在其生命周期内不断验证 pod 的健康状态。
我们从示例应用程序中部署的微服务包括这些检查。下面显示的 YAML 是应用于内部 gRPC 微服务的就绪性和活性检查的一个片段(略有变化):
readinessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"]
livenessProbe:
periodSeconds: 5
exec:
command: ["/bin/grpc_health_probe", "-addr=:8080"]
grpc_health_probe
是一个可执行文件,专门用于验证公开 gRPC 服务的应用程序的健康状况。这个项目可以在 GitHub 上找到。
因为前端是通过 HTTP 公开的,所以它使用不同的检查来利用 Kubernetes 的能力来验证带有特殊格式的 HTTP 请求的 pod。有趣的是,这些检查使用会话 ID cookie 值来识别测试请求,就像我们将流量路由到功能分支一样:
readinessProbe:
initialDelaySeconds: 10
httpGet:
path: "/_healthz"
port: 8080
httpHeaders:
- name: "Cookie"
value: "shop_session-id=x-readiness-probe"
livenessProbe:
initialDelaySeconds: 10
httpGet:
path: "/_healthz"
port: 8080
httpHeaders:
- name: "Cookie"
value: "shop_session-id=x-liveness-probe"
如果就绪性探测在部署过程中失败,部署将认为自己不健康。如果就绪检查失败,我们可以通过选择等待部署成功选项来使 Octopus 部署失败,这将在成功完成以下步骤之前等待 Kubernetes 部署成功:
【T2
等待部署成功可确保在成功完成该步骤之前通过所有准备情况检查。
回滚策略
Kubernetes 支持对部署资源的本地回滚,命令如下:
kubectl rollout undo deployment.v1.apps/yourdeployment
但是,这种回滚过程有一些限制:
- 它仅回滚部署,而不考虑部署所依赖的任何资源,如机密或配置映射。将特定于环境的配置存储在应用程序之外是十二因素应用程序所鼓励的实践之一,这意味着代码和配置通常会并排部署。
- 它不适用于蓝/绿部署,因为此过程创建了全新的部署资源,没有可回滚的配置历史。
- 八达通仪表板不会准确反映系统的状态。
Octopus 中的部署流程旨在捕获在给定版本中部署应用程序所需的所有步骤。通过重新部署旧的版本,我们可以确保代表可部署版本的所有资源和配置都被考虑在内。
请注意,必须特别注意持久存储数据的微服务,因为回滚到以前的版本并不能确保任何持久存储的数据都与以前的代码兼容。如果您使用滚动部署,情况也是如此,因为这种策略实现了增量升级,导致应用程序的旧版本和新版本在短时间内并行运行。
多重环境
CNCF 2019 年调查强调了在 Kubernetes 中分离团队的两种主要方法:分离名称空间和分离集群:
【T2
图表显示了 2019 年 CNCF 调查的团队分离策略。
Kubernetes 提供了对资源限制(CPU、内存和临时磁盘空间)、通过网络策略的防火墙隔离和 RBAC 授权的现成支持,可以根据名称空间限制对 Kubernetes 资源的访问。
Istio 可以用来实现网络速率限制,尽管它不像那么容易。
然而,名称空间并不是完全相互隔离的。例如,自定义资源定义不能限定在名称空间的范围内。
这意味着 Kubernetes 支持软多租户,其中名称空间大部分(但不是完全)相互隔离。硬多租户,其中名称空间可以用来隔离不受信任的邻居,是一个积极讨论的主题但目前还不可用。
这可能意味着您将拥有多个共享一个 Kubernetes 集群的测试环境,以及一个单独的生产集群。
好消息是,无论您的环境是通过名称空间还是集群实现的,都被 Octopus Kubernetes 目标抽象掉了。
正如我们在本文开头看到的,Octopus 中的 Kubernetes 目标捕获了默认名称空间、用户帐户和集群 API URL。这三个字段的组合代表了执行部署的安全边界。
Kubernetes 目标的范围是环境,部署过程的范围是目标角色,将部署过程从底层集群或名称空间中分离出来。在下面的截图中,您可以看到第二个 Kubernetes 目标,其作用域为Test
环境,默认为test
名称空间:
默认为测试名称空间的目标,范围为测试环境。
为了更好地分隔目标,我们可以为每个目标创建服务帐户,其范围为名称空间。下面的 YAML 显示了一个服务帐户、角色和角色绑定的示例,该示例仅授予对dev
名称空间的访问权限:
apiVersion: v1
kind: ServiceAccount
metadata:
name: dev-deployer
namespace: dev
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: dev
name: dev-deployer-role
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: dev-deployer-binding
namespace: dev
subjects:
- kind: ServiceAccount
name: dev-deployer
apiGroup: ""
roleRef:
kind: Role
name: dev-deployer-role
apiGroup: ""
创建服务帐户会导致创建密码。这个秘密包含一个 base64 编码的令牌,我们可以使用以下命令来访问它:
kubectl get secret $(kubectl get serviceaccount dev-deployer -o jsonpath="{.secrets[0].name}" --namespace=dev) -o jsonpath="{.data.token}" --namespace=dev
该值随后被解码并保存为 Octopus 中的令牌帐户:
【T2
八达通中保存的服务账户令牌。
Kubernetes 目标可以使用令牌帐户:
Kubernetes 目标使用的令牌。
这个目标现在可以用来将资源部署到dev
名称空间,并且禁止修改其他名称空间中的资源,从而有效地通过名称空间对 Kubernetes 集群进行分区。
结论
如果你已经做到了这一步,祝贺你!配置具有高可用性、多环境、零停机部署、HTTPS 访问、功能分支部署、冒烟测试和回滚的微服务部署并非易事,但有了本指南,您将拥有在 Kubernetes 中构建世界一流部署的坚实基础。
滚动部署的终极指南- Octopus Deploy
原文:https://octopus.com/blog/ultimate-guide-to-rolling-deployments
当部署现代软件的新版本时,比如 web 应用程序和服务,一些团队在进行部署时关闭整个网站的情况仍然很常见。
如果您的大多数客户只在工作时间使用您的应用程序,那么这种大爆炸式的方法可能是可以接受的,但是如果您的客户全天候使用您的应用程序会怎么样呢?
如今,用户希望应用程序始终可用,有一些部署模式可以用来实现零停机。在这篇文章中,我将更深入地讨论其中一种模式;滚动部署。我还将为您提供一些如何使用不同工具实现滚动部署的实际例子。
在这篇文章中
什么是滚动部署?
滚动部署是一种部署模式(也称为增量部署、批量部署或斜坡部署),在这种模式下,通常一次向一个或多个部署目标交付新软件,直到所有目标都部署了软件的更新版本。
典型的流程如下所示:
- 在应用程序的两个节点运行
v1.0
的情况下,清空要更新的第一个节点,将其从负载平衡器池中取出,并让剩余的节点保持在线以服务流量:
- 停止在耗尽的节点上运行
v1.0
应用程序,然后部署新的v1.1
版本。可选地,通过在新部署的应用程序上运行测试来验证部署是否成功。同时,维护至少一个运行应用程序v1.0
的节点:
- 在第一个节点成功更新后,继续清空仍在运行应用程序
v1.0
的剩余节点,同时您的新v1.1
版本现在在线提供流量:
- 停止剩余节点上的
v1.0
应用程序的运行,部署新的v1.1
版本。同样,也可以验证部署是否成功:
- 最后,在应用程序的
v1.1
成功部署到所有节点之后,您的滚动部署就完成了!
如果您想要加速您的滚动部署,并同时向多个节点交付一个新版本,比如说两个节点,那么它看起来应该是这样的:
这种增量方法通常在位于负载平衡器之后的 web 应用程序中更受青睐,因为大多数负载平衡器支持一种称为连接排出的概念。这允许到服务的连接自然完成,并防止建立新的连接。
通过执行该动作,被选择更新的实例可以在它们完成它们的工作之后从可用池中移除,而其他实例保持在线服务流量。
尽管上面的场景描述了 web 应用程序滚动部署,但是也可以实现其他类型应用程序的滚动部署,只要它们是以支持安全地结束其过程的方式构建的。
例如,Octopus Deploy 的高可用性配置也有一个消耗选项,它可以阻止任何新任务的执行,并完成它当前正在执行的任何任务,直到空闲。排水等功能允许安全终止进程,然后可以更新并重新联机。
它们为什么有用?
那么,为什么要使用滚动部署而不是其他模式,比如 canary 或 blue/green?滚动部署具有以下优势:
增量更新
应用程序的新版本是逐步推出的。这使您可以验证它的工作,例如,在进行下一批更新之前运行健康检查或测试。
如果您需要启动回滚,您也可以用一种安全且可控的方式来完成。
保持灯亮着
当您着手更新少量的应用程序实例时,其余的继续为请求服务。这意味着您的应用程序不会停机,并且在整个部署过程中您的用户都可以使用它。
平行
您通常可以控制一次部署到的并发实例的数量。在之前的部署完成之前,不会开始进一步的部署。
您可以在 Octopus 滚动部署中使用窗口大小选项来控制一次可以部署多少个部署目标。
实践中的滚动部署模式
为了演示滚动部署的不同方法,我们有一个非常简单的。将显示网页的 NET Core 3.1 应用程序。
我感兴趣的部分的 HTML 如下所示:
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>If you are seeing this, then <strong>Congratulations!</strong> <br/> You've got the example application running. </p>
@if(Settings.Value.AppVersion == "0.0.2") {
<p>v0.0.2 of the application comes with this text </p>
}
@if(Settings.Value.AppVersion == "0.0.3") {
<p>But don't miss out on v0.0.3 of the application which comes with this text! </p>
}
</div>
该应用程序的代码可在 GitHub 上获得,并有一个与三个不同的AppVersion
值相对应的标签。一张 Docker 图片也已经发布为octopus deploy/rolling-deploy-web-example。
我想看看使用一些流行的技术和工具来执行此应用程序的滚动部署有多简单,因此我将使用以下工具来演示滚动部署:
Docker 滚动应用程序更新
在过去几年中,Docker 已经成为事实上的容器技术。因此,它以 Docker 服务的概念支持滚动部署也就不足为奇了。通常,服务是更大的架构图中的一小部分,在微服务中很受欢迎。
服务支持许多不同的选项,包括滚动更新策略和回滚能力。
码头集装箱应用
我在一个 Ubuntu 服务器上运行 Docker,并使用我们预先构建的容器映像。在 Ubuntu 上安装 Docker 有几种方法:
- 通过运行
sudo apt-get install docker.io
来使用 Ubuntu 存储库。 - 使用官方的对接指南。
我选择了 Ubuntu 库,因为它似乎更快更容易,但你的里程可能会有所不同。无论您选择哪种方法,都有必要确保您满足安装的先决条件。
为了简单起见,我将在我的 Linux 机器的 SSH 终端会话中与 Docker 交互。有一些生产就绪的设置可以实现这一点,这些设置的特点是在一个 Docker Compose 文件中定义您的服务,包括控制自动更新和回滚设置的部分。
权限要求:
本演示中的大多数命令都使用了 sudo 。默认情况下,Docker 守护程序作为根用户运行,需要提升权限才能执行命令。如果你不想在执行命令时使用sudo
,请确保遵循 Docker 安装后说明。
首先,为了查看这个独立运行的 Docker 映像,我们将使用以下命令运行它:
markh@ubuntu01:~$ sudo docker run -d -p 5001:5001 octopusdeploy/rolling-deploy-web-example:0.0.1
不出所料,运行这个 Docker 图像会显示网页:
在这篇文章中,我不解释如何建立一个容器图像。如果您是 Docker 的新手,我的同事 Shawn 已经写了一个很好的系列文章,介绍如何将一个真实世界的应用程序容器化,而不是另一个“Hello World”的例子。
集装箱清理
接下来需要快速整理。要删除我们使用上面的run
命令创建的容器,我们需要停止它,然后使用rm
命令删除它。我们可以用简洁的一行程序来实现这一点:
sudo docker rm $(sudo docker stop $(sudo docker ps -a -q --filter ancestor=octopusdeploy/rolling-deploy-web-example:0.0.1 --format="{{.ID}}"))
这通过图像名octopusdeploy/rolling-deploy-web-example:0.0.1
定位我们的容器,并将它传递给stop
命令,最后将它传递给rm
命令。
为了部署容器的多个实例,我们需要创建 Docker 服务。这使用码头工人群作为其幕后指挥者。
Docker Kubernetes orchestrator
Docker 在使用 Docker stack 命令部署容器时也支持 Kubernetes 作为 orchestrator,但是在使用service create
时无法指定 orchestrator。
让我们看看创建服务的命令是什么样子的:
markh@ubuntu01:~$ sudo docker service create --name rolling-deploy-svc --replicas 3 --publish published=5001,target=5001 --update-delay 10s --update-parallelism 1 octopusdeploy/rolling-deploy-web-example:0.0.1
这个命令中有很多内容,所以让我们来分析一下我们对 Docker 的要求:
--name
不言自明。--replicas
标志控制我们想要的容器数量(3)。--publish published=5001,target=5001
使用 Swarm 的路由网格指定要在端口 5001 上访问的服务,该路由网格本质上就像一个软件负载平衡器。--update-delay
配置服务任务更新之间的时间延迟(10 秒)。--update-parallelism
控制 Docker 将同时调度的最大任务数(1)。- 最后,我们指定要使用的图像:
octopusdeploy/rolling-deploy-web-example:0.0.1
。
提示:
第一次运行service create
时,可能会收到警告,就像我一样:This node is not a swarm manager
。要解决此问题,请运行以下命令之一:
- 这将把你当前的节点初始化为群组管理器。
sudo docker swarm join
:这将把你的本地节点连接到 swarm。
执行此操作将导致我们的服务被部署到 Docker Swarm,其中包含三个实例:
wxi1w4m7crknaz1f800kr9ztt
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
我们还可以通过运行service inspect
命令来检查我们的服务是否有正确的更新配置:
markh@ubuntu01:~$ sudo docker service inspect rolling-deploy-svc --pretty
ID: bh03s0yjzkevzkkwvu8q2h0jj
Name: rolling-deploy-svc
Service Mode: Replicated
Replicas: 3
Placement:
UpdateConfig:
Parallelism: 1
Delay: 10s
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Update order: stop-first
RollbackConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Rollback order: stop-first
ContainerSpec:
Image: octopusdeploy/rolling-deploy-web-example:0.0.1@sha256:4da10d630025bf268b855b0b4afafa7334769ab6d0b3e75e11a3f11949708552
Init: false
Resources:
Endpoint Mode: vip
Ports:
PublishedPort = 5001
Protocol = tcp
TargetPort = 5001
PublishMode = ingress
这个结果表明我们有了我们想要的UpdateConfig
,它将一次更新一个任务。
Docker 服务更新
现在我们可以通过运行service update
命令将octopusdeploy/rolling-deploy-web-example
的容器图像更新为v0.0.2
:
markh@ubuntu01:~$ sudo docker service update rolling-deploy-svc --image octopusdeploy/rolling-deploy-web-example:0.0.2
Docker 对每个容器运行更新,一次一个任务,就像我们配置的那样:
overall progress: 0 out of 3 tasks
1/3: running [=============================================> ]
2/3:
3/3:
第一个任务完成后,它进入第二个任务:
overall progress: 1 out of 3 tasks
1/3: starting [==================================================>]
2/3: ready [=====================================> ]
3/3:
直到更新容器到v0.0.2
的所有任务完成:
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
现在浏览网站显示申请v0.0.2
的文本:
Docker 服务回滚
正如直接推出一样,也可以在 Docker 中用一个简单的命令手动回滚。
首先,我们将通过运行以下命令来更新应用程序的最终版本v0.0.3
:
markh@ubuntu01:~$ sudo docker service update rolling-deploy-svc --image octopusdeploy/rolling-deploy-web-example:0.0.3
我们通过运行service inspect
命令来验证新的v0.0.3
版本,只为我们希望看到的输出传入一个--format
参数:
markh@ubuntu01:~$ sudo docker service inspect --format='{.Spec.TaskTemplate.ContainerSpec.Image}}' rolling-deploy-svc
octopusdeploy/rolling-deploy-web-example:0.0.3@sha256:151a8f2aaed0192bf9f22eaeff487d546e6ff8fec4d0691e6697dede743b187c
因为 Docker Swarm 知道我们部署的版本,所以我们可以使用rollback
命令恢复到之前的版本(v0.0.2
):
markh@ubuntu01:~$ sudo docker service rollback rolling-deploy-svc
rolling-deploy-svc
rollback: manually requested rollback
overall progress: rolling back update: 3 out of 3 tasks
1/3: running [> ]
2/3: running [> ]
3/3: running [> ]
verify: Service converged
一旦成功回滚,它就确认服务正在运行。
提示:
由于我没有给rollback
命令指定任何参数,Docker 将默认一次回滚一个任务,每个任务之间没有延迟。您可以通过将以下内容传递给命令来指定不同的值:
--rollback-parallelism
--rollback-delay
Docker 文档中有您可以使用的参数的完整列表。
我们可以使用与之前相同的命令来检查服务,以验证回滚是否成功:
markh@ubuntu01:~$ sudo docker service inspect --format='{.Spec.TaskTemplate.ContainerSpec.Image}}' rolling-deploy-svc
octopusdeploy/rolling-deploy-web-example:0.0.2@sha256:4843a91ba84ace97cb11a6e3f68827a8c28a628d509159910c868f9ad02c3053
这导致输出中出现预期的v0.0.2
版本。
数据库回滚
当 Docker 中的服务利用数据库进行存储时,有一个适当的策略来处理服务回滚是很重要的,特别是当数据库在一个容器中时。Docker 不会自动回滚数据库更改,这可能会使您的数据库和应用程序处于不兼容状态。Docker 存储文档提供了一些关于在容器中存储不同选项的指导。
码头服务清理
最后,要删除 Docker 服务,我们只需运行rm
命令:
markh@ubuntu01:~$ sudo docker service rm rolling-deploy-svc
Docker 摘要
如您所见,在 Docker 中进行滚动部署并不需要太多的设置。加上它对回滚的支持,使它成为一个值得考虑的有吸引力的选择。
Kubernetes 滚动更新
Kubernetes 中的滚动部署被称为滚动更新。
pod 的实例将使用新实例进行增量更新。它支持在更新期间不可用的 pod 的最大数量或百分比,以及可以创建的新 pod 的最大数量。除此之外,Kubernetes 还有一个方便的内置特性,允许更新恢复到以前的版本。
为了找到更多关于 Kubernetes 的信息,我的同事 Shawn 继续他的容器系列,主要关注 Kubernetes。
Kubernetes 关于更新的教程包括一个展示其工作原理的漂亮图表:
Kubernetes 集群设置
和以前一样,我将在这个演示中重用我们预先构建的容器映像,这次使用的是 MicroK8s 。我还将主要在 SSH 终端会话中与它进行交互。
作者 Canonical 将 MicroK8s 描述为:
适用于 42 种 Linux 的 k8s 软件包。专为开发人员打造,非常适合 edge、IoT 和设备。
对于像我这样想尝试 Kubernetes 或者用它做一些开发的人来说,这很有用。
MicroK8s 的一个好处是它不像其他 Kubernetes 选择的那样需要任何类型的虚拟机(比如 Minikube )。
在安装 MicroK8s 之前,值得注意的是有一些先决条件:
- Ubuntu 18.04 LTS 或 16.04 LTS(或其他支持
snapd
的 OS)。 - 至少 20G 可用磁盘空间。
- 4GB 内存。
- 互联网连接。
为了安装 MicroK8s,我们运行snap命令:
markh@ubuntu01:~$ sudo snap install microk8s --classic
microk8s v1.17.0 from Canonical✓ installed
完整安装输出
如果您想查看来自snap
的完整安装输出,运行snap changes
命令:
markh@ubuntu01:~$ snap changes
ID Status Spawn Ready Summary
1 Done today at 10:17 UTC today at 10:17 UTC Initialize system state
2 Done today at 10:17 UTC today at 10:17 UTC Initialize device
3 Done today at 14:38 UTC today at 14:38 UTC Install "microk8s" snap
由此,您可以运行命令snap change 3
,其中3
是上面ID
列中用于安装 MicroK8s 的值。这将为您提供安装步骤的明细行。
Kubernetes 部署
现在我们已经安装并运行了 MicroK8s,让我们继续使用我们现有的映像rolling-deploy-web-example
创建一个 Kubernetes 部署,并将其设置为在端口 5001 上侦听。
Google 将 Kubernetes 部署描述为:
表示一组没有唯一标识的多个相同的 pod。部署运行应用程序的多个副本,并自动替换任何失败或无响应的实例。通过这种方式,部署有助于确保应用程序的一个或多个实例可用于服务用户请求。部署由 Kubernetes 部署控制器管理。
这听起来非常适合滚动部署。
Kubernetes 容器化应用程序设置
为了设置我们的应用程序的部署,我们将使用与 MicroK8s 打包在一起的 kubectl 二进制文件。这是用于管理 Kubernetes 的命令行界面(CLI)。它特别添加了前缀microk8s.
,以避免与您可能运行的任何其他 Kubernetes 实例发生命名冲突。如果我们运行create deployment
命令:
markh@ubuntu01:~$ sudo microk8s.kubectl create deployment rollingdeploy-microk8s --image=octopusdeploy/rolling-deploy-web-example:0.0.1
deployment.apps/rollingdeploy-microk8s created
MicroK8s 将创建我们的部署,并确认它已经成功创建。
接下来,我们将设置应用程序窗格监听端口5001
。为此,我们运行 expose 命令:
markh@ubuntu01:~$ sudo microk8s.kubectl expose deployment rollingdeploy-microk8s --type=NodePort --port=5001
service/rollingdeploy-microk8s exposed
Kubernetes 仪表板
虽然已经创建了rollingdeploy-microk8s
pod,但它可能不会立即可用。我们可以通过使用 Kubernetes 仪表板查看我们的服务来检查它的状态,该仪表板作为插件包含在 MicroK8s 中。
试图远程访问仪表板需要您经历一些困难。启用附加组件后,我发现最简单的方法是通过运行kubectl proxy
命令创建一个从我的机器到服务器的代理:
markh@ubuntu01:~$ sudo microk8s.kubectl proxy --accept-hosts=.* --address=0.0.0.0
Starting to serve on [::]:8001
从那里你可以访问端口8001
上的仪表板,但是你需要一个Kubeconfig
文件或者Token
来登录。更多详情请参见 MicroK8s 仪表板附件。
注意:您可以通过为仪表板容器设置--enable-skip-login
参数来跳过登录,但是不建议这样做,因为这违反了安全性最佳实践。
一旦打开,您就可以使用仪表板部署容器化的应用程序,管理集群资源并与之交互。
为了执行滚动更新,首先我们需要应用程序的多个副本。我们可以通过单击部署部分右侧的三个省略号,直接从仪表板扩展我们的部署:
对于我们的 Kubernetes 部署,我将所需副本更新为 3,这样我就可以执行滚动更新,然后点击缩放:
等价的 kubectl 命令
您可能已经注意到仪表板为我们的操作提供了运行的等价命令。来扩展我们的资源,那就是:
sudo microk8s.kubectl scale -n default deployment rollingdeploy-microk8s --replicas=3
配置完 pod 后,我们可以通过运行get pod
命令(名称可能不同)直接查询 pod 的状态来确认这一点:
markh@ubuntu01:~$ sudo microk8s.kubectl get pod
NAME READY STATUS RESTARTS AGE
rollingdeploy-microk8s-794bdc64c4-fv7zt 1/1 Running 0 76s
rollingdeploy-microk8s-794bdc64c4-t6mh5 1/1 Running 0 76s
rollingdeploy-microk8s-794bdc64c4-trr6f 1/1 Running 0 76s
为了验证我们的应用程序正在工作,我们需要通过运行get service
找到 Kubernetes 向我们在开始时创建的部署公开的端口:
markh@ubuntu01:~$ sudo microk8s.kubectl get service rollingdeploy-microk8s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
rollingdeploy-microk8s NodePort 10.152.183.39 <none> 5001:32334/TCP 1m
在我的例子中,端口是32334
,所以我访问服务的 URL 是:
http://ubuntu01.octopusdemos.com:32334
注意:
在您自己的机器上运行时,端口可能会有所不同。Kubernetes 将在 30000-32767(默认)范围内随机分配一个端口,因为我们在前面运行expose
命令时选择了NodePort
类型。
在浏览器中打开 URL,我们可以看到在 Microk8s 中运行的应用程序有v0.0.1
:
Kubernetes 滚动更新
让我们继续,通过运行set image
命令,指示 Kubernetes 用我们的图像octopusdeploy/rolling-deploy-web-example
的v0.0.2
更新我们的三个 pod:
markh@ubuntu01:~$ sudo microk8s.kubectl set image deployment/rollingdeploy-microk8s rolling-deploy-web-example=octopusdeploy/rolling-deploy-web-example:0.0.2 --record
deployment.apps/rollingdeploy-microk8s image updated
接下来,我们可以通过运行rollout status
命令来观察我们的首次展示的实时进度,直到它完成:
markh@ubuntu01:~$ sudo microk8s.kubectl rollout status deployment.v1.apps/rollingdeploy-microk8s
Waiting for deployment "rollingdeploy-microk8s" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "rollingdeploy-microk8s" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "rollingdeploy-microk8s" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "rollingdeploy-microk8s" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "rollingdeploy-microk8s" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "rollingdeploy-microk8s" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "rollingdeploy-microk8s" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "rollingdeploy-microk8s" rollout to finish: 1 old replicas are pending termination...
deployment "rollingdeploy-microk8s" successfully rolled out
您可以看到它表示一次更新一个 Pod。
Kubernetes 部署确保只有一定数量的 pod 在更新时关闭。它通过创建一个新的 pod 并在完成后销毁旧的 pod 来实现这一点。
默认 pod 更新控制
默认情况下,Kubernetes 确保至少有 75%的期望数量的 pod 可用。此外,另一个默认情况是创建不超过 25%的总体期望数量的 pod。
在浏览器中打开 URL,我们可以看到我们的应用程序的v0.0.2
在 Microk8s 中运行:
这里触发了部署的首次展示,因为set image
导致了对底层部署 pod 的模板的更新。模板是描述复制控制器创建实际 pod 的方式的规范文档。
通过运行edit
,我们可以看到我们的应用程序的模板是什么样子的:
markh@ubuntu01:~$ sudo microk8s.kubectl edit deployment.v1.apps/rollingdeploy-microk8s
这将在文本编辑器中打开模板文件。对我来说,那是在终端本身。您可以交互编辑该文件。更改部署窗格的模板(.spec.template
中的部分)将导致触发部署的首次展示:
提示:
对部署的其他更新,如我们之前所做的缩放,不会导致部署被触发。
Kubernetes 部署回滚
成功的滚动部署显然是我们所有人都希望的,但是不可避免的是,在某个时候,您需要启动回滚,要么是在部署过程的一部分,要么是在一段时间之后。
使用 Kubernetes,默认情况下,所有部署的部署历史都保存在系统中。这意味着,您可以随时回滚。
注意:
可以更改为部署的首次部署存储的历史量(通过修改修订历史限制),但通常不建议这样做,因为这会限制您回滚部署的能力。
为了查看我们部署的首次展示历史,我们运行rollout history
命令:
markh@ubuntu01:~$ sudo microk8s.kubectl rollout history deployment.v1.apps/rollingdeploy-microk8s
deployment.apps/rollingdeploy-microk8s
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment/rollingdeploy-microk8s rolling-deploy-web-example=octopusdeploy/rolling-deploy-web-example:0.0.2 --kubeconfig=/var/snap/microk8s/1107/credentials/client.config --record=true
我们可以选择通过运行rollout undo
恢复到之前部署的v0.0.1
版本:
markh@ubuntu01:~$ sudo microk8s.kubectl rollout undo deployment.v1.apps/rollingdeploy-microk8s
deployment.apps/rollingdeploy-microk8s rolled back
提示:
markh@ubuntu01:~$ sudo microk8s.kubectl rollout undo deployment.v1.apps/rollingdeploy-microk8s --to-revision=1
其中--to-revision
参数包含您希望返回的版本。
Kubernetes 文档中有您可以使用的参数的完整列表。
我们可以确认我们已经回滚,或者通过查看仪表板,在浏览器中查看应用程序,或者通过运行describe
命令:
markh@ubuntu01:~$ sudo microk8s.kubectl describe deployment
Name: rollingdeploy-microk8s
Namespace: default
CreationTimestamp: Wed, 22 Jan 2020 13:19:30 +0000
Labels: app=rollingdeploy-microk8s
Annotations: deployment.kubernetes.io/revision: 3
Selector: app=rollingdeploy-microk8s
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=rollingdeploy-microk8s
Containers:
rolling-deploy-web-example:
Image: octopusdeploy/rolling-deploy-web-example:0.0.1
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: rollingdeploy-microk8s-794bdc64c4 (3/3 replicas created)
Events:
这表明Image
被设置为octopusdeploy/rolling-deploy-web-example:0.0.1
,正如我们所预期的。
数据库回滚
与 Docker 一样,重要的是要了解当您启动 Kubernetes 回滚时,您拥有的任何数据库将会发生什么。谷歌有一篇很棒的文章更深入地讨论了在 Kubernetes 上运行数据库。
Kubernetes 部署清理
为了删除与我们的 Kubernetes 部署相关的所有资源,我们使用delete
命令:
markh@ubuntu01:~$ sudo microk8s.kubectl delete services,deployment rollingdeploy-microk8s -n default
service "rollingdeploy-microk8s" deleted
deployment.apps "rollingdeploy-microk8s" deleted
Kubernetes 摘要
与 Docker 相比,Kubernetes 的滚动部署设置似乎要多一点,特别是在访问仪表板方面,但是在完成所有配置之后,它运行得非常好。
使用 Octopus 进行滚动部署
Octopus 从 Octopus 2.0 开始就支持滚动部署的概念。
通过使用子步骤,我们可以为 Octopus 中的rolling-deploy-web-example
应用程序设置部署流程。
创建新项目后,我们通过三个步骤配置滚动部署:
- 从负载平衡器中删除节点的脚本。
- web 应用程序的部署。
- 将节点添加回负载平衡器的脚本。
为了在 Octopus 中实现增量发布,我们需要使我们的窗口大小低于部署目标的总数。在我的例子中,我将其设置为1
,如下所示:
我有两个配置了目标角色 : rolling-deploy-webapp
的部署目标。
当我将这个版本部署到Test
环境时,Octopus 一次部署到一个部署目标,正如我在前面的部署过程中配置的那样:
这就是全部了!查看我们的文档以获得关于 Octopus 中滚动部署的完整参考。
样本 Octopus 项目
您可以在我们的样本实例中查看这个 Octopus 项目设置。
关于数据库的一句话
通常滚动部署的主要症结之一是数据库。执行涉及某种持久存储的滚动部署可能很棘手,但并非不可能。魔鬼总是在细节中。
如果您希望执行包含数据库更改的滚动部署,我建议首先部署数据库。您还希望确保对数据库所做的任何更改都与您已部署的代码的以前版本向后兼容。
最后,经常测试你的回滚策略绝对是个好主意*。*
*## 结论
无论您使用哪种工具,滚动部署只是优化软件部署的一种模式。但是使用增量方法,它允许您保持应用程序在线,同时以受控的方式推出软件的新版本,通常具有对回滚的本机支持,这使它成为我最喜欢的最小化中断的方法。
CI/CD 管道指南
如果您需要任何其他帮助来配置您的 CI/CD 管道,我们已经创建了 Octopus 指南,其中包含为各种技术堆栈(包括 Docker 和 Kubernetes)设置 CI/CD 管道的分步说明。
欢迎发表评论,让我们知道您对滚动部署的想法!*