成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

技術(shù)實(shí)踐 | Mesos 全方位“烹飪”指南

archieyang / 2373人閱讀

摘要:之前提到的文件即可利用以下模板生成請注意,其中的與就是占位符。如將某一特定部署至生產(chǎn)環(huán)境并運(yùn)行個實(shí)例。而另一種方式則是使用等負(fù)載均衡器即服務(wù)器端發(fā)現(xiàn)??芍嘏渲们夷軌蛟谧兏l(fā)生后立即將請求路由至新實(shí)例。

如今與Mesos相關(guān)的文章可謂層出不窮,不過展示能夠直接用于生產(chǎn)的完整基礎(chǔ)設(shè)施的資料卻相當(dāng)少見。在今天的文章中,我將介紹各組件的配置與使用方式,旨在幫助大家利用Mesos構(gòu)建起持續(xù)交付且擁有容錯能力的運(yùn)行時平臺。不過設(shè)備配置腳本屬于基礎(chǔ)設(shè)施當(dāng)中的主體部分,因此在文章中無法完全公布——但值得大家了解的內(nèi)容已經(jīng)全部到位。

概述

我們不會具體探討設(shè)備的實(shí)際安裝與配置,不過大家可以通過下圖了解我們已經(jīng)安裝的軟件:

目前,我們運(yùn)行有3臺主節(jié)點(diǎn)與3臺從節(jié)點(diǎn),并利用Saltstack進(jìn)行配置。

下面讓我們從宏觀角度審視代碼構(gòu)建與配置及執(zhí)行的完整部署流程。


在以下章節(jié)中,我們將探討各組件是如何交互及運(yùn)作的。

準(zhǔn)備Docker鏡像

雖然Mesos能夠利用自身默認(rèn)容器完美處理各類可執(zhí)行文件,但我們?nèi)匀煌扑]運(yùn)行Docker化應(yīng)用。我們需要向每套Docker鏡像中添加一點(diǎn)內(nèi)容,從而保證其能夠輕松同這套運(yùn)行時平臺相集成。

首先,我們需要了解Docker宿主主機(jī)的實(shí)際IP地址。雖然人們一直要求支持特殊的Docker標(biāo)記,可以很容易的通過定制化腳本輕松實(shí)現(xiàn):

#!/bin/sh

set -e

DOCKERHOST=$(ip route show 0.0.0.0/0 | grep -Eo "via S+" | awk "{ print $2 }")
echo "$DOCKERHOST dockerhost" >> /etc/hosts

exec "$@"

現(xiàn)在容器化中的應(yīng)用程序已經(jīng)能夠可靠地引用“dockerhost”主機(jī)名稱,在必要時接入該Docker宿主主機(jī)。

接下來是服務(wù)配置管理(我們將在后文中詳盡討論),我們需要向Docker鏡像中添加一套名為service-wrapper的腳本。另外,我們還需要安裝其關(guān)聯(lián)性元素——jq。如果大家使用精簡化Linux鏡像(例如Alpine),請確保其中包含curl命令工具。這一切匯總起來,我們就擁有了以下面向Node.js應(yīng)用程序的Docker鏡像:

FROM node:4

RUN apt-get update && apt-get install -y jq && 
  rm -rf /var/lib/apt/lists/*

ADD service-wrapper.sh /usr/bin/
ADD entrypoint.sh /usr/bin/

此鏡像應(yīng)被推送至Docker 倉庫并由CI服務(wù)器進(jìn)行訪問。在我們的示例中,將是可用的私有Docker倉庫在registry.local這臺宿主主機(jī)上。

構(gòu)建與部署

我們利用Drone實(shí)現(xiàn)持續(xù)集成,其使用方式非常簡單但卻能夠在Docker容器當(dāng)中很好地執(zhí)行構(gòu)建任務(wù)。另外,其全部插件也都是普通的Docker容器,其能夠獲取build信息載荷并能夠以獨(dú)特的插件方式加以處理。而且沒錯,甚至是克隆代碼庫也能夠通過多帶帶的Docker容器實(shí)現(xiàn)。

由于我們使用的技術(shù)相當(dāng)多元化,因此需要利用Drone簡化很多我們的服務(wù)器構(gòu)件設(shè)置——我們只需要為每種不同技術(shù)堆棧準(zhǔn)備多帶帶的Docker鏡像,并要求其利用此鏡像進(jìn)行構(gòu)建。舉例來說,這些鏡像分別用于運(yùn)行Node.js、Java以及Mono的構(gòu)建。當(dāng)然,它們同樣由Drone負(fù)責(zé)構(gòu)建。

為了便于理解,我創(chuàng)建一款名為test-server的Node.js Web應(yīng)用,其只負(fù)責(zé)顯示各項(xiàng)環(huán)境變量——大家可以 點(diǎn)擊此處在GitHub上查看源代碼。我們在后文中還將多次提到這款示例應(yīng)用,了解如何利用test-server創(chuàng)建一套Docker鏡像并將其部署至Marathon。需要強(qiáng)調(diào)的是,該服務(wù)應(yīng)當(dāng)盡可能遵循十二因素應(yīng)用原則,從而更好地與這套運(yùn)行時平臺相集成。

在進(jìn)行構(gòu)建之前,需要注意的是,我們會利用Marathon調(diào)度自己的長期運(yùn)行服務(wù)。它正是整套運(yùn)行時平臺的基石,其負(fù)責(zé)控制服務(wù)可用性并用于實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)。其擁有非常直觀的“可curl”API,我們可以利用它實(shí)現(xiàn)自動部署。為了利用Marathon部署服務(wù),大家需要調(diào)用其API并發(fā)送JSON文件以進(jìn)行服務(wù)描述,具體如下所示:

{
  "id": "/app/production/test-server",
  "labels": {
    "version": "master-ef5a154e25b268c611006d08a78a3ec0a451e7ed-56",
"environment": "production"
  },
  "env": {
"SERVICE_TAGS": "production,internal-listen-http-3000",
"SERVICE_NAME": "test-server"
  },
  "cpus": 0.05,
  "mem": 64.0,
  "instances": 4,
  "args": [
    "service-wrapper.sh", "dockerhost:18080", "dockerhost:18500", "app/production/test-server",
"node", "/app/server.js"
  ],
  "container": {
    "type": "DOCKER",
    "docker": {
      "image": "registry.local/test-server:master-ef5a154e25b268c611006d08a78a3ec0a451e7ed-56",
      "forcePullImage": true,
      "network": "BRIDGE",
      "portMappings": [{
        "containerPort": 3000
      }]
    }
      },
  "healthChecks": [{
    "protocol": "HTTP",
    "path": "/healthcheck",
    "gracePeriodSeconds": 2,
    "intervalSeconds": 10,
    "maxConsecutiveFailures": 3
  }],
  "upgradeStrategy": {
    "minimumHealthCapacity": 0.5,
    "maximumOverCapacity": 0.5
  }
}

它會指示Marathon調(diào)用4個Docker服務(wù)實(shí)例,來源則為來自registry.local/test-server且擁有master-ef5a154e25b268c611006d08a78a3ec0a451e7ed-56標(biāo)簽的鏡像。我們將其稱為服務(wù)定義。我們不會對其規(guī)范進(jìn)行具體討論,感興趣的朋友可以點(diǎn)擊此處查閱Marathon API參考。更重要的是,此文件能夠在構(gòu)建過程中自動生成。對于每一項(xiàng)可部署服務(wù),其服務(wù)定義模板中都包含一個以“$”開頭的占位符。之前提到的文件即可利用以下模板生成:

{
  "id": "/app/$environment/test-server",
  "labels": {
    "version": "$tag",
    "environment": "$environment"
  },
  "env": {
    "SERVICE_TAGS": "$environment,internal-listen-http-3000",
    "SERVICE_NAME": "test-server"
  },
  "cpus": 0.05,
  "mem": 64.0,
  "instances": $instances,
  "args": [
    "service-wrapper.sh", "dockerhost:18080",     "dockerhost:18500", "app/$environment/test-server",
    "node", "/app/server.js"
  ],
  "container": {
    "type": "DOCKER",
    "docker": {
      "image": "registry.local/test-server:$tag",
      "forcePullImage": true,
      "network": "BRIDGE",
      "portMappings": [{
        "containerPort": 3000
      }]
    }
  },
  "healthChecks": [{
    "protocol": "HTTP",
    "path": "/healthcheck",
    "gracePeriodSeconds": 2,
    "intervalSeconds": 10,
    "maxConsecutiveFailures": 3
  }],
  "upgradeStrategy": {
    "minimumHealthCapacity": 0.5,
    "maximumOverCapacity": 0.5
  }
}

請注意,其中的$tag、$environment與$instances就是占位符。$tag值會在構(gòu)建過程中生成并被以下簡單腳本添加至service.json當(dāng)中:

#!/bin/bash

set -e

VERSION="$CI_BRANCH-$CI_COMMIT-$CI_BUILD_NUMBER"
declare -A TARGETS=(
    [test-server]="."
)

# Set version in service.json
for i in "${!TARGETS[@]}"
do
    SERVICE=${TARGETS[$i]}/service.json
    TARGET=service-defs/${TARGETS[$i]}
    mkdir -p $TARGET
    echo "Setting version $VERSION for $SERVICE"
    sed "s/$tag/$VERSION/g" $SERVICE > $TARGET/service-$CI_BRANCH.json
    cp $TARGET/service-$CI_BRANCH.json $TARGET/service-$VERSION.json
done

它創(chuàng)建兩條服務(wù)定義:其一以完整版本命名,其二以分支版本命名,即service-master-ef5a154e25b268c611006d08a78a3ec0a451e7ed-56.json與service-master.json。在這兩個文件當(dāng)中,$tag會被替換為master-ef5a154e25b268c611006d08a78a3ec0a451e7ed-56,但$environment與$instances則仍然保持不變,我們隨后還需要在部署階段使用它們。下面來看看Drone的build配置(其完整語法可點(diǎn)擊此處查看):

build:
  image: registry.local/buildimage-nodejs:latest
  commands:

tools/gen-service-defs.sh

publish:

 docker:
   image: plugins/drone-docker
   repo: registry.local/test-server
   tag:

latest

$$BRANCH

$$BRANCH-$$COMMIT-$$BUILD_NUMBER

 s3:
   image: plugins/drone-s3
   acl: public-read
   region: eu-west-1
   bucket: build-artifacts
   access_key: $$S3_ACCESS_KEY
   secret_key: $$S3_SECRET_KEY
   source: service-defs/
   target: test-server/
   recursive: true

而后,CI根據(jù)指令發(fā)布build構(gòu)件:

創(chuàng)建一套Docker鏡像并將其推送至Docker倉庫

將生成的服務(wù)定義發(fā)送至S3

我們完全可以將一部分最新發(fā)布的Docker鏡像標(biāo)簽存儲在Docker 倉庫當(dāng)中,甚至可以在磁盤空間比較充裕時全部加以存儲。通過這種方式,我們就能在新近部署的版本出現(xiàn)問題時利用Marathon輕松回滾至原有版本。

現(xiàn)在我們已經(jīng)可以部署自己的全新服務(wù)版本了。為了完成這項(xiàng)任務(wù),我們需要將服務(wù)定義JSON發(fā)送至Marathon的/v2/apps API。不過首先,我們需要替換現(xiàn)有占位符,即$environment與$instances。盡管相關(guān)操作非常簡單,但我們?nèi)匀灰胢arathon-deploy工具實(shí)現(xiàn)自動化處理。其能夠自動下載服務(wù)定義模板,利用對應(yīng)值替換點(diǎn)位符并創(chuàng)建/更新Marathon中的應(yīng)用。具體方式如下:

`marathon-deploy.sh   [instances-count]`

其中service-template-url為被發(fā)布至S3的服務(wù)定義構(gòu)件的URL,而environment則可為任意其它運(yùn)行時環(huán)境(例如分段、生產(chǎn)、A/B分組測試等),instances-count則允許指定需要啟動的服務(wù)實(shí)例數(shù)量,默認(rèn)情況下為1。例如:

`marathon-deploy.sh https://build-artifacts.s3.amazonaws.com/test-server/service-master.json staging`

其將主分支的最新build部署至分段環(huán)境并運(yùn)行單一實(shí)例。如:

`marathon-deploy.sh https://build-artifacts.s3.amazonaws.com/test-server/service-master-ef5a154e25b268c611006d08a78a3ec0a451e7ed-56.json production 4`

將某一特定build部署至生產(chǎn)環(huán)境并運(yùn)行4個實(shí)例。

在本示例中,我們將marathon-deploy腳本復(fù)制到全部Mesos-slave 的宿主機(jī)當(dāng)中,并通過Chronos進(jìn)行配置與部署。因?yàn)槲覀円呀?jīng)將各關(guān)鍵服務(wù)預(yù)部署到了不同環(huán)境當(dāng)中,因此這一部署流程只需要一次點(diǎn)擊即可完成。Chronos的最大優(yōu)勢在于,它能夠?qū)⒋罅咳蝿?wù)串聯(lián)起來,意味著大家能夠通過配置在實(shí)際部署前、后執(zhí)行一次性任務(wù)以準(zhǔn)備運(yùn)行時環(huán)境。

服務(wù)發(fā)現(xiàn)與負(fù)載均衡

分布式系統(tǒng)中的服務(wù)總是出于種種原因而不斷上線、下線,例如服務(wù)啟動/停止,規(guī)模伸縮或者服務(wù)故障。與利用已知IP地址及主機(jī)名稱同服務(wù)器協(xié)作的靜態(tài)負(fù)載均衡器不同,Marathon中的負(fù)載均衡機(jī)制的實(shí)時性使得其需要以更為復(fù)雜的方式進(jìn)行服務(wù)注冊與注銷。有鑒于此,我們需要利用某種服務(wù)注冊表以容納已注冊服務(wù)信息并將此信息提供給客戶端。這一概念也就是服務(wù)發(fā)現(xiàn),且成為大多數(shù)分布式系統(tǒng)中的核心組件。

這里就要用到Consul了。正如其官方網(wǎng)站上所言,“Consul能夠輕松幫助服務(wù)進(jìn)行自我注冊,同時通過DNS或者HTTP接口發(fā)現(xiàn)其它服務(wù)”。除此之外,它還擁有其它一些非常實(shí)用的功能,我們將在后文提到?,F(xiàn)在我們已經(jīng)擁有了服務(wù)注冊表,接下來要做的就是告知其哪些服務(wù)需要啟動、這些服務(wù)的具體位置(主機(jī)名稱與端口)并可選擇提供其它相關(guān)元信息。實(shí)現(xiàn)這一目標(biāo)的方法之一在于讓服務(wù)本身直接使用Consul API,但這種作法會導(dǎo)致各項(xiàng)服務(wù)必須擁有內(nèi)置通信邏輯。如果服務(wù)數(shù)量不多還好,面對大規(guī)模服務(wù)集時這將成為一場災(zāi)難,特別是當(dāng)它們由不同編程語言編寫而成時。另一種方式在于利用某些第三方工具對服務(wù)進(jìn)行監(jiān)控,并將服務(wù)報(bào)告給Consul。我們使用的程序名為marathon-registrator,它能夠與Marathon緊密集成并注冊Marathon所運(yùn)行的任何服務(wù)類型。另一種選項(xiàng)則是使用Gliderlabs registrator,當(dāng)然前提是大家的服務(wù)全部存在于Docker容器當(dāng)中。選擇一種工具,將其實(shí)例運(yùn)行在Mesos-slave宿主機(jī)上即可。

服務(wù)注冊完成之后,其它服務(wù)就能夠?qū)ζ溥M(jìn)行定位了。如此一來,它們就能夠利用Consul API或者DNS進(jìn)行直接通信并獲取此類元信息(即客戶端發(fā)現(xiàn))。而另一種方式則是使用HAProxy等負(fù)載均衡器(即服務(wù)器端發(fā)現(xiàn))。

HAProxy服務(wù)發(fā)現(xiàn)較客戶端發(fā)現(xiàn)擁有多種優(yōu)勢:

免費(fèi)負(fù)載均衡機(jī)制。

能夠?qū)⒎?wù)注冊表中的變更即時傳播至消費(fèi)方。HAProxy可重配置且能夠在變更發(fā)生后立即將請求路由至新實(shí)例。

配置更為靈活,例如:負(fù)載均衡策略、運(yùn)行狀態(tài)檢查、ACL、A/B測試、日志記錄以及統(tǒng)計(jì)等等。

不需要由服務(wù)實(shí)現(xiàn)額外的發(fā)現(xiàn)邏輯。

不過HAProxy是如何在Consul中追蹤已注冊服務(wù)的?一般來講,其配置會利用全部已知后端以靜態(tài)方式完成。不過我們也可以例如consul-template等外部工具對其進(jìn)行動態(tài)構(gòu)建。這款工具能夠監(jiān)控Consul中的變更并生成任意文本文件,因此其并不只限于配置HAProxy,亦可用于其它能夠利用文本文件實(shí)現(xiàn)配置的工具(例如nginx、varnish以及apache等等)。該模板語言的說明文檔非常全面,感興趣的朋友可以點(diǎn)擊此處查看。

大家可能已經(jīng)注意到,我們在概覽圖中運(yùn)行有兩套不同的HAProxy配置:一套用于內(nèi)部負(fù)載均衡,另一套用于外部負(fù)載均衡。內(nèi)部實(shí)例跨越各后端服務(wù)提供實(shí)際服務(wù)發(fā)現(xiàn)與負(fù)載流量。外部實(shí)例則額外將服務(wù)發(fā)現(xiàn)聲明至TCP 80端口并接收來自外部的請求,以此實(shí)現(xiàn)前端服務(wù)負(fù)載均衡。

在這些不同的HAProxy實(shí)例中,我們需要管理兩套獨(dú)立的consul-template模板文件——這兩個文件本身則由另一套模板引擎(即Jinja2)在Saltstack進(jìn)行設(shè)備配置時構(gòu)建完成。這種作法主要是為了保證流程清晰,同時將來自設(shè)備配置軟件的數(shù)據(jù)填充至其中的特定部分。讓我們首先來看外部負(fù)載均衡器配置模板。請注意,raw/endraw標(biāo)記負(fù)責(zé)讓Jinja引擎忽略Go模板的大括號并按原樣使用初始內(nèi)容:

global
  log /dev/log  local0
  log /dev/log  local1 notice
  chroot /var/lib/haproxy
  user haproxy
  group haproxy
  daemon
  maxconn {% raw %}{{key "service/haproxy/maxconn"}}{% endraw %}

  # Default SSL material locations
  ca-base /etc/ssl/certs
  crt-base /etc/ssl/private

  ssl-default-bind-options no-sslv3 no-tls-tickets
  ssl-default-bind-ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH

  tune.ssl.default-dh-param 2048

{% include "mesos/files/haproxy-defaults.ctmpl.jinja" %}
{% include "mesos/files/haproxy-internal-frontend.ctmpl.jinja" %}
{% include "mesos/files/haproxy-external-frontend.ctmpl.jinja" %}
{% include "mesos/files/haproxy-wellknown-services.ctmpl.jinja" %}
{% include "mesos/files/haproxy-backends.ctmpl.jinja" %}

其中還包含其它一些關(guān)聯(lián)性。haproxy-defaults.ctmpl.jinja屬于常見于各類HAProxy配置示例中的靜態(tài)部分,而haproxy-internal-frontend.ctmpl.jinja則比較有趣——它負(fù)責(zé)指定內(nèi)部服務(wù)發(fā)現(xiàn)配置的實(shí)現(xiàn)位置。

其基本思路在于為每一項(xiàng)可發(fā)現(xiàn)服務(wù)匹配一條已知端口編號,同時創(chuàng)建一套HAProxy前端以監(jiān)聽該端口。我們將使用與每項(xiàng)已注冊服務(wù)一同存儲的元信息。Consul允許用戶為服務(wù)指定一套關(guān)聯(lián)標(biāo)簽列表,而marathon-registrator會通過名為SERVICE_TAGS的服務(wù)環(huán)境變量讀取這些標(biāo)簽。查看test-server的service.json模板,其中包含兩個以逗號隔開的標(biāo)簽,即$environment與internal-listen-http-3000。后者在consul-template模板中用于標(biāo)記已聲明端口以實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)的服務(wù)(在本示例中為3000)。以下代碼片段會自動生成必要的HTTP前端:

{% raw %}

{{range $service := services}}{{range $tag := $service.Tags}}
{{$servicePort := $service.Name | regexReplaceAll "^[w-]+?(d*)$" "$1"}}{{$tagPort := $tag | regexReplaceAll "^[w-]+?(d*)$" "$1"}}
{{if or (not $servicePort) (eq $servicePort $tagPort)}}
{{if $tag | regexMatch "internal-listen-http-d+"}}
##
# Production internal http frontend for {{$service.Name}}
##

frontend internal_http_in_production:{{$service.Name}}
  bind :{{$tag | replaceAll "internal-listen-http-" ""}}
  use_backend cluster_production:{{$service.Name}}

##
# Staging internal http frontend for {{$service.Name}}
##

frontend internal_http_in_staging:{{$service.Name}}
  bind :1{{$tag | replaceAll "internal-listen-http-" ""}}
  use_backend cluster_staging:{{$service.Name}}
{{end}}
{{end}}
{{end}}{{end}}

{% endraw %}

它的外環(huán)會列出全部Consul服務(wù),內(nèi)環(huán)則列出每項(xiàng)服務(wù)的標(biāo)簽并嘗試找到與internal-listen-http-匹配的條目。每一次匹配成功,其都會創(chuàng)建對應(yīng)的HTTP前端。這里的每項(xiàng)服務(wù)都擁有兩種硬編碼環(huán)境:production與staging,用于進(jìn)行區(qū)分。其中分段環(huán)境的端口編號會以“1”開頭,因此如果生產(chǎn)前端監(jiān)聽端口3000,那么分段則監(jiān)聽端口13000。

另外,if語句允許我們?yōu)閱我环?wù)指定多個可發(fā)現(xiàn)端口。要實(shí)現(xiàn)這一效果,我們只需要在標(biāo)簽列表當(dāng)中額外添加internal-listen-http-標(biāo)記,例如:

`$environment,internal-listen-http-3000,internal-listen-http-3010`

現(xiàn)在我們需要添加一個指向container.docker.portMappings服務(wù)定義文件組的聲明端口,從而保證Marathon能夠正確配置我們的容器網(wǎng)絡(luò)。請注意,本示例中的marathon-registrator將分別注冊兩項(xiàng)服務(wù):test-server-3000與test-server-3010,對二者進(jìn)行分別解析并避免名稱混淆。

大家也可以利用其它預(yù)定義標(biāo)記在模板中實(shí)現(xiàn)其它邏輯類型,例如引入internal-listen-tcp-以生成TCP前端,或者利用balance-roundrobin或balance-leastconn控制均衡策略。

這套模板允許我們配置HAProxy,從而指定每臺設(shè)備通過接入localhost:以訪問每項(xiàng)Consul已知服務(wù),最終解決服務(wù)發(fā)現(xiàn)問題。
在haproxy-wellknown-services.ctmpl.jinja當(dāng)中,我們可以指定多種靜態(tài)管理服務(wù),例如Marathon、Consul以及Chronos,這是因?yàn)樗鼈冚^易于發(fā)現(xiàn)。它們會在設(shè)備配置過程中由systemd/upstart/etc啟動。舉例來說,以下代碼片段允許來自集群內(nèi)任意設(shè)備的請求通過聯(lián)系localhost:18080 輕松訪問Marathon實(shí)例,而localhost:14400 與localhost:18500 則分別對應(yīng)Chronos與Consul(在本示例中,集來自配置管理軟件):

frontend internal_http_in:marathon
  bind :18080
  use_backend cluster:marathon

frontend internal_http_in:chronos
  bind :14400
  use_backend cluster:chronos

listen internal_http_in:consul
  bind :18500
  timeout client 600000
  timeout server 600000
  server local 127.0.0.1:8500

backend cluster:marathon
  option forwardfor
  option httpchk GET /ping
  balance roundrobin
{%- for host, ip in master_nodes.iteritems() %}
  server {{ host }} {{ ip }}:8080 check inter 10s
{% endfor -%}

backend cluster:chronos
  option forwardfor
  option httpchk GET /ping
  balance roundrobin
{%- for host, ip in master_nodes.iteritems() %}
  server {{ host }} {{ ip }}:4400 check inter 10s
{% endfor -%}

haproxy-external-frontend.ctmpl.jinja用于描述HTTP與HTTPS前端。其中包含多套Jinja宏,用于為域名匹配定義ACL規(guī)則并將后端與這些規(guī)則相綁定:

{% macro hosts(environment, domain_prefix="") -%}
  # {{ environment }} hosts
  acl host_{{ environment }}:test-server  hdr_dom(host) -i -m str {{ domain_prefix }}mesos-test.domain.com
{%- endmacro %}

{% macro bind(service, environment) -%}
  use_backend cluster_{{ environment }}:{{ service }} if host_{{ environment }}:{{ service }}
{%- endmacro %}

frontend external_https_in
  bind :443 ssl crt domain_com.pem

  http-response set-header Strict-Transport-Security max-age=31536000; preload
  http-response set-header X-Frame-Options DENY
  http-response set-header X-Content-Type-Options nosniff

  {{ hosts("staging", "staging-") }}
  {{ hosts("production") }}

  # Staging bindings
  {{ bind("test-server", "staging") }}

  # Production bindings
  {{ bind("test-server", "production") }}

frontend external_http_in
  bind :80

  {{ hosts("staging", "staging-") }}
  {{ hosts("production") }}

  # Staging bindings
  {{ bind("test-server", "staging") }}

  # Production bindings
  {{ bind("test-server", "production") }}

最后,還有haproxy-backends.ctmpl.jinja文件。它會列出之前章節(jié)中提到的全部可用服務(wù)。所有后端都可以進(jìn)行手動調(diào)整,因?yàn)橛脩艨赡苄枰獫M足運(yùn)行狀態(tài)檢查或者負(fù)載均衡方面的特殊要求:

{% macro backends(environment) -%}

##
# {{ environment }} backends
##
{{ "{{" }}$environment := "{{ environment }}"{{ "}}" }}
backend cluster_{{ environment }}:test-server
  option forwardfor
  option httpchk GET /healthcheck
  balance roundrobin{% raw %}{{range $i, $s := service (print $environment ".test-server")}}
  server {{$s.Node}}-{{$i}} {{$s.Address}}:{{$s.Port}} check inter 10s fall 1 rise 1{{end}}{% endraw %}

{%- endmacro %}

{{ backends("production") }}
{{ backends("staging") }}

內(nèi)部負(fù)載均衡配置文件要稍稍簡單一點(diǎn),其只需要將連接路由至內(nèi)部可訪問服務(wù):

global
  log /dev/log  local0
  log /dev/log  local1 notice
  chroot /var/lib/haproxy
  user haproxy
  group haproxy
  daemon
  maxconn {% raw %}{{key "service/haproxy/maxconn"}}{% endraw %}

{% include "mesos/files/haproxy-defaults.ctmpl.jinja" %}
{% include "mesos/files/haproxy-internal-frontend.ctmpl.jinja" %}
{% include "mesos/files/haproxy-wellknown-services.ctmpl.jinja" %}
{% include "mesos/files/haproxy-backends.ctmpl.jinja" %}
零停機(jī)時間部署

動態(tài)運(yùn)行時系統(tǒng)的一大共通優(yōu)勢在于,其能夠以零停機(jī)時間為前提的實(shí)現(xiàn)服務(wù)部署。在實(shí)際部署過程中,我們往往希望確保服務(wù)的可用性不受影響。下面來看如何在自己的系統(tǒng)中實(shí)現(xiàn)這一效果。

其基本思路在于運(yùn)行多個服務(wù)實(shí)例以支撐輸入請求,并在部署期間一一替換這些實(shí)例:只有新實(shí)例頂替上之后,我們才會關(guān)停舊實(shí)例。Marathon能夠完全搞定這樣的滾動重啟方式,但為了保證恒定的正常運(yùn)行時間,我們還需要確保負(fù)載均衡器停止向已經(jīng)被關(guān)停的實(shí)例路由流量。

下面要討論Mesos如何停止基于Docker的服務(wù)實(shí)例。與其它Unix進(jìn)程控制系統(tǒng)一樣,它會嘗試以正常方式停止服務(wù)。首先,SIGTERM會被發(fā)送至目標(biāo)進(jìn)程,而后該進(jìn)程預(yù)計(jì)將在掛起請求并清空后自行退出。如果其在指定時間內(nèi)仍未退出,SIGKILL會強(qiáng)制將其關(guān)閉。Mesos的具體執(zhí)行方式如下:

`docker stop -t= `

(詳見:https://docs.docker.com/engine/reference...

現(xiàn)在,我們的服務(wù)應(yīng)該能夠正確處理終止信號了(其同樣也是十二因子應(yīng)用原則之一)。在接收到SIGTERM后,其應(yīng)該告知負(fù)載均衡器不再向其路由流量。要實(shí)現(xiàn)這一效果,最簡單的方式就是拒絕全部后續(xù)運(yùn)行狀態(tài)檢查,但仍然正常處理其它潛在請求。一旦負(fù)載均衡器發(fā)現(xiàn)該實(shí)例運(yùn)行狀態(tài)不正常,它將停止將流量路由至該處。為了確保這一流程正常實(shí)現(xiàn),我們需要確保關(guān)停超時足夠長,使得負(fù)載均衡器能夠自行發(fā)現(xiàn)運(yùn)行狀態(tài)變化并停止對掛起請求的處理。默認(rèn)情況下,Mesos的配置超時為5秒,不過大家也可以利用以下命令行參數(shù)變更該值:

`--executor_shutdown_grace_period=60secs --docker_stop_timeout=30secs`

感興趣的朋友也可以參閱https://github.com/x-cray/test-server/bl... 示例以了解如何處理SIGTERM。

需要強(qiáng)調(diào)的是,docker化服務(wù)的處理方式有所不同。為了允許信號傳入Docker容器進(jìn)程,我們應(yīng)當(dāng)在Dockerfile中使用ENTRYPOINT而非CMD以指定執(zhí)行權(quán)限。在運(yùn)行時,CMD執(zhí)行會被打包至shell進(jìn)程當(dāng)中,而其不會轉(zhuǎn)發(fā)任何信號(詳見Docker說明文檔)。

基本上,到這里我們已經(jīng)實(shí)現(xiàn)了零停機(jī)時間部署。不過對于HAProxy而言,這樣的配置只適用于BSD系統(tǒng)。在Linux系統(tǒng)當(dāng)中還存在一個小問題:當(dāng)其進(jìn)行配置重載時,服務(wù)器會在一小段時間內(nèi)(約20到50毫秒)無法監(jiān)聽輸入連接。此時段內(nèi)的全部輸入請求自然也將失敗。其根本原因在于Linux內(nèi)核處理SO_REUSEPORT套接選項(xiàng)的方式身上(詳情可參閱http://lwn.net/Articles/542629/ )。要解決這個問題,大家不妨閱讀由Yelp發(fā)布的一篇文章。我們最終選擇了簡單的SYN數(shù)據(jù)包丟棄方案。這種作法對于我們目前的使用場景來講已經(jīng)足夠了。以下為我們的consul-template如何重載HAProxy實(shí)例:

#!/bin/bash

iptables -I INPUT -p tcp --dport 80 --syn -j DROP
sleep 0.2
systemctl reload haproxy
iptables -D INPUT -p tcp --dport 80 --syn -j DROP
服務(wù)配置管理

我們運(yùn)行的每項(xiàng)服務(wù)都需要擁有自己的配置,包括連接字符串以及API密鑰等等。一般來講,這些信息不應(yīng)被硬編碼,也應(yīng)該從構(gòu)建來提供(調(diào)試/發(fā)布/其它),因?yàn)榇蠹铱赡芟M诓煌h(huán)境(生產(chǎn)/分段/分組測試)下通過不同的配置實(shí)現(xiàn)同樣的執(zhí)行效果。

服務(wù)配置通常有著不同的來源(各來源亦擁有自己的優(yōu)先級排序):默認(rèn)、文件、環(huán)境變量、argv等。如果該服務(wù)可通過環(huán)境變更進(jìn)行配置,那么我們就能通過多種途徑以集中方式對其進(jìn)行管理——我們要做的就是在服務(wù)正式啟動前為其準(zhǔn)備正確的環(huán)境。好消息是,現(xiàn)在我們已經(jīng)擁有了免費(fèi)的集中化配置存儲方案。Consul提供內(nèi)置鍵-值存儲(Consul KV)機(jī)制,可用于保存配置值——但我們還需要另一種手段將這些值轉(zhuǎn)發(fā)至服務(wù)環(huán)境。

專門用于解決這類需求的工具為envconsul,它會讀取Consul KV數(shù)據(jù)并將其傳遞至服務(wù)環(huán)境。而在KV數(shù)據(jù)更新時,它會重啟對應(yīng)服務(wù)實(shí)例。不過,它在服務(wù)滾動重啟以實(shí)現(xiàn)服務(wù)可用性方面的表現(xiàn)并不好——它會一次性重啟全部實(shí)例,并因此造成請求失敗。作為后備選項(xiàng),我們可以要求Marathon在KV數(shù)據(jù)更新時,確保一切已經(jīng)部署到位并由調(diào)度器完成相關(guān)重啟工作。正因?yàn)槿绱耍覀冞x擇了一套小型Shell腳本service-wrapper來代替envconsul,其能夠從Consul KV處讀取數(shù)據(jù),設(shè)置服務(wù)環(huán)境并在KV變更發(fā)生時通過Marathon進(jìn)行重啟。另外,它還能夠?qū)⒔邮盏降腟IGTERM轉(zhuǎn)發(fā)至底層進(jìn)程以實(shí)現(xiàn)正常關(guān)閉。大家應(yīng)該將這套腳本內(nèi)置于Docker鏡像當(dāng)中。它需要使用curl與jq,因此請確保二者同樣存在于Docker鏡像內(nèi)。以下為具體使用方法:

`service-wrapper.sh    `

其中marathon-host:port 與consul-host:port 擁有自描述特性,prefix值按照慣例應(yīng)為Marathon應(yīng)用ID,而command則為包含相關(guān)參數(shù)的實(shí)際服務(wù)可執(zhí)行文件。為了設(shè)定各服務(wù)環(huán)境變量,我們需要將值添加至Consul KV存儲內(nèi)的prefix路徑下。以下為service-wrapper運(yùn)行示例:

`service-wrapper.sh dockerhost:18080 dockerhost:18500 app/staging/test-server node /app/server.js`

請注意dockerhost:18080 與dockerhost:18500 ——二者為之前提到的服務(wù)器端內(nèi)部服務(wù)發(fā)現(xiàn)示例。

現(xiàn)在如果我們添加/移除/修改prefix之下的任意值,該服務(wù)都將根據(jù)新配置進(jìn)行正常重啟。

總結(jié)

基于Mesos的架構(gòu)能夠幫助我們將多種不同組件加以混合與匹配,從而擁有一套符合實(shí)際需要的完整工作系統(tǒng)。舉例來說,要實(shí)現(xiàn)負(fù)載均衡,我們可以使用HAProxy或者Nginx并配合由consul-template生成的動態(tài)配置,甚至可以利用其它一些既能夠與Consul通信、又能夠配置IPVS負(fù)載均衡的其它方案替代consul-template。我們還可以利用Netflix Eureka替換整個服務(wù)發(fā)現(xiàn)/負(fù)載均衡層,或者基于Etcd乃至ZooKeeper編寫自己的定制化解決方案。構(gòu)建這樣一套系統(tǒng)能夠幫助大家更好地了解分布式運(yùn)行時平臺背后的各個進(jìn)程及其核心組件。

這套平臺還具備良好的可移植性,其不僅可被托管至特定云/IaaS之上,也可以通過內(nèi)部甚至是混合(云加內(nèi)部)環(huán)境實(shí)現(xiàn)。目前甚至有一些項(xiàng)目能夠?qū)esos移植至Windows平臺。

原文鏈接:
https://medium.com/@x_cray/how-we-cook-m...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/26575.html

相關(guān)文章

  • Container容器技術(shù)大會PPT分享

    摘要:大會是由國內(nèi)容器社區(qū)組織的專為一線開發(fā)者和運(yùn)維工程師設(shè)計(jì)的頂級容器技術(shù)會議,會議強(qiáng)調(diào)實(shí)踐和交流,話題設(shè)置圍繞容器運(yùn)維云計(jì)算等技術(shù)領(lǐng)域,力求全方位多角度為參會者解讀容器技術(shù)。 @Container大會是由國內(nèi)容器社區(qū) DockOne 組織的專為一線開發(fā)者和運(yùn)維工程師設(shè)計(jì)的頂級容器技術(shù)會議,會議強(qiáng)調(diào)實(shí)踐和交流,話題設(shè)置圍繞容器、運(yùn)維、云計(jì)算等技術(shù)領(lǐng)域,力求全方位、多角度為參會者解讀容器技術(shù)...

    Zachary 評論0 收藏0
  • GIAC 2017全球互聯(lián)網(wǎng)架構(gòu)大會最新日程

    摘要:月日至日,高可用架構(gòu)和聯(lián)合主辦的全球互聯(lián)網(wǎng)架構(gòu)大會將于上海光大會展中心舉行。全球互聯(lián)網(wǎng)架構(gòu)大會是高可用架構(gòu)技術(shù)社區(qū)推廣的面向架構(gòu)師技術(shù)負(fù)責(zé)人及高端技術(shù)從業(yè)人員的技術(shù)架構(gòu)大會。本次大會共有大板塊方向,場技術(shù)專題,個互聯(lián)網(wǎng)架構(gòu)案例。 showImg(https://segmentfault.com/img/bVZ3Vh?w=600&h=375);12月22日至23日,高可用架構(gòu)和msup聯(lián)...

    617035918 評論0 收藏0
  • GIAC 2017全球互聯(lián)網(wǎng)架構(gòu)大會最新日程

    摘要:月日至日,高可用架構(gòu)和聯(lián)合主辦的全球互聯(lián)網(wǎng)架構(gòu)大會將于上海光大會展中心舉行。全球互聯(lián)網(wǎng)架構(gòu)大會是高可用架構(gòu)技術(shù)社區(qū)推廣的面向架構(gòu)師技術(shù)負(fù)責(zé)人及高端技術(shù)從業(yè)人員的技術(shù)架構(gòu)大會。本次大會共有大板塊方向,場技術(shù)專題,個互聯(lián)網(wǎng)架構(gòu)案例。 showImg(https://segmentfault.com/img/bVZ3Vh?w=600&h=375);12月22日至23日,高可用架構(gòu)和msup聯(lián)...

    Imfan 評論0 收藏0
  • 周末“干活”之 Mesos Meetup

    摘要:愛奇藝歷程采用的軟件棧服務(wù)現(xiàn)狀集群建設(shè)自動化部署經(jīng)驗(yàn)沒有采用嵌入式管理服務(wù)降低風(fēng)險,對紅帽有一點(diǎn)擔(dān)憂。再次感謝和數(shù)人科技共同組織的,非常期待下一次的周末相聚。 周末兩天都是大霧霾天,作為運(yùn)營也不能在家宅,告別了技術(shù)就得腿兒勤點(diǎn)兒。 非常感謝 Linker 的 Sam Chen 和 數(shù)人科技 的 CTO 共同組織的Mesos Meetup,OneAPM 最帥的 Docker 工程獅~陳亮...

    warmcheng 評論0 收藏0
  • 從小白程序員一路晉升為大廠高級技術(shù)專家我看過哪些書籍?(建議收藏)

    摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報(bào)率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術(shù)專家我看過哪些技術(shù)類書籍。 大家好,我是...

    sf_wangchong 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<