CNCF 会重蹈 OpenStack 的覆辙吗?|航海日志 Vol.25

552 阅读8分钟


➤ CNCF 会重蹈 OpenStack 的覆辙吗?


CNCF(Cloud Native Computing Foundation),即云原生计算基金会,于 2015 年 7 月成立,隶属于 Linux 基金会,初衷围绕“云原生”服务云计算,致力于维护和集成开源技术,支持编排容器化微服务架构应用。


由于最近大佬级别的云提供商的加入,云原生计算基金会(CNCF)很快就站在了开源容器世界的中心。在过去的几个星期里, CNCF 吸引了微软和亚马逊的 Web 服务 (AWS)的加入。他们的加入对于这个本来就隶属于 Linux 基金会的组织来说,无疑是如虎添翼。

而伴随着 Kubernetes 的成功,和市场对于容器技术的需求逐渐扩大,CNCF 的声势也日渐浩大。微软和 AWS 相继加入 CNCF,对于 CNCF、开源和 Kubernetes 来说, 也是一个巨大的胜利。


尽管这些会给 CNCF 带来规模和潜在的影响,但组织仍然面临挑战。


CNCF 执行董事 Dan Kohn 表示:


CNCF 一直非常专注于确保所有成员在迅速扩张的组织中都有各自的代表性。CNCF 的最大优势是他们足够新兴,所以他们能够向前人学习,其的愿景是只犯新的错误, 而不是照搬过去的错误。所以推断 CNCF 会重蹈 OpenStack 的覆辙,这样的论断是错误的。


而对 OpenStack 来说,许多运营商早期的时候使用 OpenStack 来实现他们的 SDN 计划,而现在供应商社区则无法提供所需的解决方案。同时,OpenStack 存在的一些问题还归咎于它无法处理一些较大成员的不同需求。


而为了规避这样的问题,CNCF 留出了在供应商之上添加更多产品的余地,让社区来决定其有用性。这样他们的生存和死亡由自身的优势决定,但 OpenStack 社区人为地支撑着将死的平台。


同时,Dan Kohn 也承认 Kubernetes 一直处于爆炸性增长的状态。如果能管理这一增长,则能实现组织目前最大的成就。

➤ Kubernetes 1.7.4 版本发布


8 月 17 日, Kubernetes 1.7.4 版本发布,相比 1.7.3 版本共有 17 处明显变化,例如:


  • 修复创建或更新 ELB 会修改全局定义的 Security Group Bug

  • 修复 kubefed 在不同版本 RBAC 创建问题

  • 修复 API Server Watch Cache 中一个 Bug

  • Azure:允许 VNet 在一个单独的资源组中

  • Cluster Autoscaler -修复了与 taints 相关的问题,并更新了 kube – proxy cpu请求

  • 以 Stackdriver 模式收集来自 Heapster 的 Metrics

  • GCE:Bump GLBC 版本更新到 0.9.6

  • 更新 Heapster 版本 1.4.1


➤ Docker Tips:将容器的文件重定向到您的 Docker 主机


每隔一段时间,我都需要将容器的文件转存到我的 Docker 主机上。在这里向大家提供一种简单的方法。

有时为了调试,您可能想将容器内部的配置文件的内容复制到 Docker 主机,以便您在自己喜欢的代码编辑器中打开它,或将其发送给别人。这对于已经运行的 Docker 容器来说非常方便,并且您不希望用 volume 重新启动它,因为你想要立刻就获取这个文件。

完成以下两点你就可以达成目的:

# 重写那个镜像的 Dockerfile 的 CMD, 来cat到你想要的文件
docker run --rm alpine cat /etc/hosts

以上步骤将打印出容器的 /etc/hosts 文件的内容

# 修改命令将该输出重定向到 Docker 主机上的新文件。
docker run --rm alpine cat /etc/hosts > /tmp/alpinehosts

你可以运行命令ls -la /tmp | grep alpinehosts来进行验证。

当然,如果您在 Docker 主机上运行 Windows 而不是 MacOS 或 Linux,则你的命令需要进行一些小的调整。例如,在 Windows 上不起作用。如果您使用 PowerShell 等,您将需要 Google 一下如何将输出重定向到文件。

另外,在这两种情况下,您都需要将Cat指令安装在 Docker 镜像中,但所有主要的 Linux 版本都已经默认安装了(包括 Alpine)。


➤ 从环境变量到 Docker secrets

12 Factor app

12 Factor app 中的第三项告诉我们要将配置存储在环境中。

它还提供了以下内容的示例:

  • 资源处理数据库,Memcached 和其他后台服务

  • 对外部服务的认证,如 Amazon S3 或 Twitter

  • 部署的规范主机名

我们想知道如今是否仍然推荐这种方法,并且使用它的风险程度。在这篇文章中,我们将一个简单的应用程序为例,看看如何修改它以更安全的方式来处理这些敏感的信息。

在 Docker 世界运行的应用

在过去的几年中,我们看到了许多应用在开发和部署方面都产生了变化。这主要是因为Docker 平台的流行。应用程序现在主要采用微服务体系结构:它们由多个隔离式服务组成。使用 Docker Compose 文件格式定义微服务应用程序现在非常普遍。此格式定义了服务及其使用的组件(网络,卷,...)。以下是用于定义由以下组成的 Web 应用程序的 Docker Compose 文件(其默认名称为 docker-compose.yml)的简单示例:

version: "3.3"
services:
  db:
    image: mongo:3.4
    network:
      - backend
    volumes:
      — mongo-data:/data/db
    deploy:
      restart_policy:
        condition: on-failure
  api:
    image: lucj/api:1.0
    networks:
      - backend    
    deploy:
      restart_policy:
        condition: on-failure
  web:
    image: lucj/web:1.0
    networks:
      - frontend
      - backend    
    deploy:
      restart_policy:
        condition: on-failure    
volumes:
  mongo-data:
networks:
  frontend:  
  backend:

使用环境变量处理 AWS 凭据

当我们深入了解 api 服务,假设这需要 AWS S3 的一些凭据。api 服务是在 Node.js 中编写的。使用 aws-sdk npm 模块连接到 Amazon API 的代码类似于以下内容。

// Middleware handling user's profile images
const AWS = require('aws-sdk'),
      config = require(‘../config’),
      aws_config = config.amazon;
// Configure AWS SDK
AWS.config.update(aws_config.credentials);
// Define S3 bucket
var s3Bucket = new AWS.S3( { params: {Bucket: aws_config.bucket} } )
...
// Upload image object
s3Bucket.putObject(obj, function(err){
    if (err) {
        log.error(err);
        return next(err);
    } else {
          return next();
    }
}

以上代码中所需的配置模块在一些其他配置内容中定义了 AWS 凭据。我们在这里看到,每个元素从一个环境变量获取它的值。

// config.js
module.exports = {
...
  "amazon":{
    "credentials": {
      "accessKeyID": process.env.AWS_ACCESS_KEY_ID,
      "secretAccessKey": process.env.AWS_SECRET_ACCESS_KEY,
    },
    "bucketName": process.env.AWS_BUCKET_NAME
  }
};

然后,当通过 Docker Compose 运行应用程序时,我们通过环境键指定这些环境变量。

api:
 image: lucj/api:1.0
networks:
      - backend    
    deploy:
      restart_policy:
        condition: on-failure
 environment:
   — AWS_BUCKET_NAME=BucketName
   — AWS_ACCESS_KEY_ID=AccessKeyID
   — AWS_SECRET_ACCESS_KEY=SecretAccessKey

这的确是处理这个问题的一个方式,但是,将这些敏感信息以纯文本格式化是非常危险的。

处理具有 Docker secrets 的 AWS 凭据

有几种方式可以以安全的方式处理这些信息。使用 Docker secrets 就是其中之一。

我们不再在环境变量中以纯文本定义凭据信息,而是从中创建 docker secrets。

$ echo "BucketName"| docker secret create AWS_BUCKET_NAME -
vjp5zh8hwb9dqkvohtyvtifl1
$ echo "AccessKeyID" | docker secret create AWS_ACCESS_KEY_ID -
5txxg3fslf9g5z1o4i19vvmcr
$echo "SecretAccessKey"|docker secret create AWS_SECRET_ACCESS_KEY -
v8g65iwcx1eb6uuwsjzknyi7g

secrets 创建成功。使用docker secret ls

$ docker secret ls
ID NAME CREATED UPDATED
5x..vm AWS_ACCESS_KEY_ID About a minute ago About a minute ago
v8..7g AWS_SECRET_ACCESS_KEY About a minute ago About a minute ago
vj..l1 AWS_BUCKET_NAME About a minute ago About a minute ago

但他们的内容无法被检索。例如,如果我们检查与关键字 AWS

ACCESS
KEY_ID 相关联的 secret,我们只会获取元数据,而不是其实际内容。

$ docker secret inspect 5txxg3fslf9g5z1o4i19vvmcr
[
  {
    "ID": "5txxg3fslf9g5z1o4i19vvmcr",
    "Version": {
      "Index": 12
    },
    "CreatedAt": "2017–08–13T12:58:50.54021338Z",
    "UpdatedAt": "2017–08–13T12:58:50.54021338Z",
    "Spec": {
      "Name": "AWS_ACCESS_KEY_ID",
      "Labels": {}
    }
  }
]

创建了 secret 以后,我们就可以在 Docker Compose 文件中引用它们。

secrets:
  AWS_BUCKET_NAME:
    external: true
  AWS_ACCESS_KEY_ID:
    external: true
  AWS_SECRET_ACCESS_KEY:
    external: true 
 

在 Docker Compose 文件中,我们还需要修改 api 服务的描述,以便使用这些 secrets。

api:
 image: lucj/api:2.0
 secrets:
   — AWS_BUCKET_NAME
   — AWS_ACCESS_KEY_ID
   — AWS_SECRET_ACCESS_KEY
 networks:
   — backend
 deploy:
   restart_policy:
     condition: on-failure

当一个服务需要访问一个 secret 时,默认情况下,它被安装在该服务的每个容器中的临时文件系统中。


由于我们的应用程序仅在此阶段检查环境变量,因此需要进行更新。


这可以用一个简单的模块来实现,只需要从`/run/secrets`中读取一个 secret。这在以下代码中说明。

// secrets.js
const fs = require("fs"),
      util = require("util");
module.exports = {
  // Get a secret from its name
  get(secret){
    try{
      // Swarm secret are accessible within tmpfs /run/secrets dir
      return fs.readFileSync(util.format(“/run/secrets/%s”, secret), "utf8").trim();
     }
     catch(e){
       return false;
     }
  }
};

然后,我们可以修改配置文件,以便它使用 secrets.js 模块的 get 函数:

...
"amazon":{
  "credentials": {
    "accessKeyId": secrets.get(“AWS_ACCESS_KEY_ID”) || process.env.AWS_ACCESS_KEY_ID,
    "secretAccessKey": secrets.get(“AWS_SECRET_ACCESS_KEY”) || process.env.AWS_SECRET_ACCESS_KEY,
  },
 "bucket": secrets.get("AWS_BUCKET_NAME") || process.env.AWS_BUCKET_NAME
 }

对于每个 key,我们首先检查它是否作为 secret 存在。如果没有的话,我们仍然使用环境变量。

这一期的『航海日志』就到这里,下期再浪~


参考链接


作者介绍

莫非 Beck:DaoCloud 微服务攻城狮,吃饱了就困的一流段子手。

刘玺元 Boring:DaoCloud 市场部门(伪)程序猿。


Discussion | 你对今天的哪条新闻最感兴趣?

  • 你对今天的哪条新闻最感兴趣?你有什么独到的见解?

  • 本周你还有什么更具爆炸性的容器圈新闻吗?欢迎在留言区爆料!


点这儿,回顾一下船长的过去