cloudfoundry dea 之 app staging

来源:互联网 发布:大数据学什么专业 编辑:程序博客网 时间:2024/06/10 21:43

最近撸cloud dea 代码,略作总结。分享开来,抛砖引玉。

下图是cloudfoundry 官方文档上一张app stage 图。原图地址:http://docs.cloudfoundry.org/concepts/how-applications-are-staged.html


从上图可以看出,从step 7 之后才是dea 真正进行staging 的过程。注意,其中cc并不是和dea直接交互的,而是通过nats 的消息机制进行交互。

在看具体的staging过程之前,先大致说下dea,dea 最主要的两个模块是staging 和starting,从名字就可以看出来,staging主要负责app 的staging过程,当dea 收到cc 发送的stage 消息时,主要由这个模块相应,staging过程的最重要的任务就是生成一个droplet,并将该droplet 以及相应的buildpack 发送到blobstore上。starting主要处理app start 的消息,具体的start 过程以后再细说。

staging 部分模块如下:

  1. StagingTask: 这是staging中最主要的部分,负责完成staging 过程的大部分工作。
  2. StagingMessage: 负责分析处理从nats接收到的staging 消息
  3. BuildpackManager: 主要负责buildpack的管理,包括buildpack 的download 以及clean。
  4. StagingTaskRegistry: 主要维护一个<task_id, task>表,所有正在进行的staging task 都会在这个表中。
  5. StagingTaskWorkspace: 维护了一个tmp 文件夹和一个staging文件夹,staging文件夹下主要存放了droplet, app package等信息,tmp下主要存放了一些cache和warden log 等信息。
  6. droplet_registry: 在dea的base dir下有众多的包含droplet 的子目录,形式为/tmp/dea_ng/[sha1]/droplet.tgz, 通过droplets来seed registry.

接下来看下staging的具体的过程。

1. 接收staging 相关消息

在dea 启动过程中,会启动一个nats client,以监听nats 中的信息,

    def create_nats_client      clean_servers = URICleaner.clean(config["nats_servers"])      logger.info("nats.connecting", servers: clean_servers)      ::NATS.connect(        :servers => config["nats_servers"],        :max_reconnect_attempts => -1,        :dont_randomize_servers => false,      )    end


主要监听的有关staging 的消息主要有三种:staging、staging.dea_id.start、staging.stop。其中staging、staging.dea_id.start是启动一个staging 过程的消息,staging.stop是停止staging 过程的消息。

2.消息的处理

当收到一个staging 消息时,StagingMessage 会分析这个消息从而形成一个message, 在整个staging过程中,这些信息都是很重要的,message 大致可以包含以下几个主要的信息:

    Message = {app_id,properties,task_id,download_uri,#保存app files 的blosstore upload_uri,buildpack_cache_upload_uri,# 保存buildpack cache的 路径(一般为blobstore)buildpack_cache_download_uri,start_message,admin_buildpacks}

3. staging task 开始前的工作

staging 真正开始之前有一些预备工作需要处理,主要包括:

  1. 利用StagingMessage 新建一个staging task
  2. 判断dea 的memory 和disk 是否能满足需求
  3. 在StagingTaskRegistry中注册task
  4. 保存snapshot。

接下来就是staging的具体的过程了。

4.staging setup

1. 准备工作空间(StagingTaskwWorkspace),这里主要做了一下几个事情。

a). 在base dir(在bootstrap时通过config来确定)下建立一个tmp 文件夹,这个文件夹下主要存放了 warden_unstaged_buildpack_cache, warden_cache, warden_unstaged_dir 等。

b). 根据StagingMessage 中的buildpack信息下载buildpack

c). 删除本地buildpack中无用的buildpack,只保留StagingMessage 指定的buildpack以及在staging_task_registry中其他staging_task使用的buildpack。

d). 记录log

2. create 一个container,并做一些限制。这里需要特别指出的是,在创建container 时候,bind mounts 是StagingTaskWorkspace中的workspace dir。

  def create_container(params)    [:bind_mounts, :limit_cpu, :byte, :inode, :limit_memory, :setup_inbound_network, :rootfs].each do |param|      raise ArgumentError, "expecting #{param.to_s} parameter to create container" if params[param].nil?    end    with_em do      new_container_with_bind_mounts_and_rootfs(params[:bind_mounts], params[:rootfs])      limit_cpu(params[:limit_cpu])      limit_disk(byte: params[:byte], inode: params[:inode])      limit_memory(params[:limit_memory])      setup_inbound_network if params[:setup_inbound_network]      setup_egress_rules(params[:egress_rules])    end  end
其实,创建container的过程是dea 根据warden 的protocol来提一些request,例如:创建一个container其实就是向warden发出一个 :Warden::Protocol::CreateRequest
3.下载app的package,根据StagingMessage的"download_uri"来下载,app的package 在是由cc将用户的程序上传到blobstore上的,下载dea需要将package下载到本地。

4.下载buildpack,根据StagingMessage 的“buildpack_cache_upload_uri” 来下载。

5. 在warden中运行脚本,建立log dir

6. 在warden中运行脚本,建立app dir.

在warden 中运行脚本,其实也是通过warden request来完成的。

  def run_script(name, script, privileged=false, discard_output=false, log_tag=nil)    request = ::Warden::Protocol::RunRequest.new(handle: handle,                                                 script: script,                                                 privileged: privileged,                                                 discard_output: discard_output,                                                 log_tag: log_tag)    response = call(name, request)    if response.exit_status > 0      data = {          script: script,          exit_status: response.exit_status,          stdout: response.stdout,          stderr: response.stderr,      }      logger.warn('%s exited with status %d with data %s' % [script.inspect, response.exit_status, data.inspect])      raise WardenError.new("Script exited with status #{response.exit_status}", response)    else      response    end  end

5.staging 

真正的staging的执行,是在warden中进行,dea 通过向warden发送一些请求和命令完成staging过程。一些主要的过程如下

1.unpack_app: 在warden中执行脚本,将app解压。

2.unpack_buildpack_cache :在warden中执行脚本,将下载的buildpack解压

3.staging: 这是在warden中正真执行staging 过程的一步,主要分为两块:a). 在warden中运行staging commond b).link warden job

    def staging_command      env = Env.new(staging_message, self)      [        'set -o pipefail;',        env.exported_environment_variables,#系统环境变量和用户环境变量        config['dea_ruby'], #ruby 路径 /usr/bin/ruby        run_plugin_path,# /dea/buildpacks/bin/run        workspace.plugin_config_path,# 进行staging 的一些配置        "| tee -a #{workspace.warden_staging_log}"      ].join(' ')    end

4.pack app: 打包droplet

5.copy out: 从/tmp/droplet.tgz 拷贝到/tmp/dea_ng/staging/staged 目录下

6. save droplet: 将/tmp/dea_ng/staging/staged 目录下的droplet拷贝到 droplet registry(也就是到/tmp/dea_ng/[sha1]/droplet.tgz)。是不多很奇怪,为什么上一步中已经将droplet.tgz拷贝一次了,还要再次拷贝呢?主要有两方面的原因,第一方面是将droplet 注册下来,另一方面是因为,在整个staging完成之后,/tmp/dea_ng/staging整个目录是被完全删除掉的。


6. upload app

在staging结束之后,接下来要做的则是将staging 产生的droplet 以及buildpack上传到blobstore上。

1.upload droplet :将/tmp/dea_ng/staging/staged/droplet.tgz 上传到blobstore上

2.upload buildpack cache

a).首先见buildpack 打包

b).将打包好的buildpack 拷贝到/tmp/dea_ng/staging/staged下

c).将buildpack 上传到blobstore

至此,staging的过程基本上已经完成,但还有些收尾的工作要处理,主要有以下的几个工作
7.善后工作~
1.销毁为了staging而创建的warden
2. close warden socket connection.
3. 删除worspace_dir ,也就是/tmp/dea_next/staging 目录。

至此,整个staging过程基本上完成了,限于个人水平问题,难免有些错误或者遗漏,请怕砖~

1 0
原创粉丝点击