K8S容器编排之ConfigMap 用于动态应用程序的实践

导言:在 Kubernetes 中,ConfigMap 是允许管理员将配置组件与镜像内容解耦,使容器化应用程序产生可移植性的一种资源。ConfigMap 可以与 Kubernetes Pod 一起使用,用于动态添加或更改容器中的使用文件。本文将阐述 Kubernetes ConfigMap 如何利用动态应用程序的方法来解决轻量级文件服务器部署到 Kubernetes 集群中的问题。

ConfigMap 概览

生产环境中很多应用程序的配置可能需要通过配置文件、命令行参数和环境变量的组合来完成。这些配置应该从镜像中解耦,通过这种方式来保持容器化应用程序的可移植性。在 Kubernetes 1.2 版本以后,研发人员引入 ConfigMap 来处理这种类型的配置数据。

简单来说,ConfigMap 是容器的配置管理。在容器运行时,ConfigMap 把配置文件、命令行参数、环境变量、端口号和其他配置组件绑定到 Pod 的容器和系统组件上,同时将应用的代码和配置区分开。从数据角度来看,ConfigMap 的类型只是键值对。从应用角度来看,管理员可以从不同角度来配置它。

在 Pod 中使用 ConfigMap 大致有以下三种方式:

  • 将 ConfigMap 中的数据设置为环境变量;
  • 将 ConfigMap 中的数据设置为命令行参数;
  • 将 ConfigMap 作为文件或目录挂载。
    另外,由于应用会从环境变量和包含配置数据的文件中读取配置信息,所以 ConfigMap 是可以支持这两种读取方式的。

创配置 ConfigMap 的注意事项

众所周知,ConfigMapSecret 很相似。但是,ConfigMap 主要用来存储和共享非敏感、未加密的配置信息。Secret 是用来存储敏感信息(例如:密码)。除了这个大家都了解的注意事项外,在配置 ConfigMap 时还要注意以下 4 点:

  • ConfigMap 必须在被 Pod 使用之前创建;
  • Pod 只能使用在同一 Namespace 中的 ConfigMap;
  • ConfigMap 大小的配额是一个已经设置好的功能;
  • Kubelet 只支持 API 服务器中的 Pod 使用 ConfigMap。
    注:API 服务器中的 Pod 包括用 Kubectl 创建的 Pod、间接通过 replication controller 创建的 Pod,不包括通过 Kubelet 的 –manifest-url 标志创建的 Pod,也不包括从它的 REST API 创建的 Pod。

ConfigMap 用于动态应用程序的实践

需要解决的问题

作为 Kubernetes 安装程序的一部分,很多人希望可以将轻量级文件服务器部署到 Kubernetes 集群中以此处理默认(root - path)入口请求。并且,我认为如果我们可以编辑 index.html 和 CSS 文件而不必重新部署应用程序。

为了解决这个用例,我们决定构建一个 Golang 应用程序,将其部分文件系统映射到 Kubernetes ConfigMap 资源中。

Golang Fileserver

文件服务器应用程序的设计非常简单,它仅用于提供静态内容。这种方式可以帮助 Kubernetes 用户使用入口功能。

package main
import (
“log”
“net/http”
)
func main() {
fs := http.FileServer(http.Dir(“html”))
http.Handle(“/”, fs)
log.Println(“Listening…”)
http.ListenAndServe(“:8080”, nil)
}

应用程序使用以下 Dockerfile 内容构建容器镜像。它是一个两阶段的 Dockerfile,首先在 Alpine 容器中执行 Golang 构建,然后将已编译的二进制和空 helm 目录复制到最终的 scratch-based 镜像上。

# build stage  
FROM golang:alpine AS builder  
WORKDIR /usr/local/go/src  
COPY  main.go .  
RUN CGO_ENABLED=0 GOOS=linux go build -o main .  


# final stage  
FROM scratch  
WORKDIR /  
COPY --from=builder /usr/local/go/src/main main  
COPY html html  
EXPOSE 8080  
ENTRYPOINT ["/main"]

在 Golang 应用程序中使用 scratch 容器来部署 Golang 容器是一种更安全、更轻量级的方法。

部署和运行

我使用 make 来自动化 Docker 操作。以下是此应用程序的 Makefile 。

VERSION?= 0.0.1   
NAME?=“ingress-default”   
AUTHOR?=“Jimmy Ray”   
PORT_EXT?= 8080   
PORT_INT?= 8080    
NO_CACHE?= true   


.PHONY:build run stop clean   


build:   
docker build -f scratch.dockerfile.-t $(NAME)\:$(VERSION) -  no-cache = $(NO_CACHE)


run:   docker   
run --name $(NAME)-d -p $(PORT_EXT):$(PORT_INT)$(NAME) \:$(VERSION)&& docker ps -a --format“{{.ID}} \ t {{.Names}}”| grep $(NAME)   


stop:   
docker rm $$(docker stop $$(docker ps) -a -q --filter“ancestor = $(NAME):$(VERSION)” -  format =“{{.ID}}”))   

clean:   
@rm -f main   

DEFAULT:build

我们可以使用 make 消除重复任务之间的可变性。有了上述的 Makefile,在将测试的应用程序部署到 Kubernetes 之前,我们可以在 Docker 中构建和运行应用程序。

配置 Kubernetes

对于此解决方案,我们需要配置 Kubernetes Namespace、ConfigMap、Deployment、Service 和 Ingress。我们通过使用 kubectl apply -f 的方法来完成此操作(这是对 Kubernetes 集群资源应用更改的声明式方法)。

下面是我们将 munge 的 Kubernetes 资源的 YAML 文件。

apiVersion: v1  
kind: Namespace  
metadata:  
  name: ingress-default 
  labels:  
    app: ingress-default  
---  
kind: ConfigMap  
apiVersion: v1  
metadata:  
  name: ingress-default-static-files  
  namespace: ingress-default   
  labels:  
    app: ingress-default   
data:  
  index.html: |  
    <!doctype html>  
    <html>  
      <head>  
        <meta charset="utf-8">  
        <title>Cluster Ingress Index</title>  
        <link rel="stylesheet" href="main.css">  
      </head>  
      <body>  
        <table class="class1">  
          <tr>  
            <td class="class2">Kubernetes Platform</td>  
          </tr>  
          <tr>  
            <td class="class1">  
              <table class="class3">  
                <tr><td><h1>Cluster Ingress Index</h1></td></tr>  
              </table>  
            </td>  
          </tr>  
          <tr>  
            <td>  
              <table class="class3">  
                <tr>  
                <td>  
                   <h2>The following are links to this cluster's ingress resources:</h2>  
                  </td>  
                </tr>  
                <tr>  
                <td class="class4">  
                    <a href="https://<ROOT_INGRESS_PATH>" target="_blank">Root Ingress</a><br/>  
                    <a href="https://<OTHER_INGRESS_PATH>" target="_blank">Other Ingress</a><br/>   
                 </td>  
               </tr>  
              </table>  
            </td>  
          </tr>  
         </table>  
      </body>  
    </html>  
  main.css: |  
    body {  
      background-color: rgb(224,224,224);  
      font-family: Verdana, Arial, Helvetica, sans-serif;  
      font-size: 100%;  
    }  
    .class1 {  
  ... 
    }  
    .class2 {  
  ... 
    }  
    .class3 {  
  ... 
    }  
    .class4 {  
     ...
    }   
---  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:  
    app: ingress-default   
  name: ingress-default  
  namespace: ingress-default 
spec:  
  selector:  
    matchLabels:  
      app: ingress-default  
  replicas: 1  
  template:  
    metadata:  
      labels:  
        app: ingress-default    
      name: ingress-default  
    spec:  
      containers:  
        - name: ingress-default  
          image: <IMAGE_REGISTRY_REPO_TAG>  
          imagePullPolicy: Always  
          resources:  
            limits:  
              cpu: 100m  
              memory: 10Mi  
            requests:  
              cpu: 100m  
              memory: 10Mi  
          volumeMounts:  
            - readOnly: true  
              mountPath: html  
              name: html-files  
      volumes:  
        - name: html-files  
          configMap:  
            name: ingress-default-static-files  
---  
kind: Service  
apiVersion: v1  
metadata:  
  name: ingress-default  
  namespace: ingress-default  
  labels:  
    app: ingress-default  
spec:  
  selector:  
    app: ingress-default  
  ports:  
  - name: http  
    protocol: TCP  
    port: 80  
    targetPort: 8080  
---  
apiVersion: extensions/v1beta1  
kind: Ingress  
metadata:  
  name: default-ingress  
  namespace: ingress-default  
  annotations:   
    nginx.ingress.kubernetes.io/rewrite-target: /  
    kubernetes.io/ingress.class: "nginx"    
  labels:  
    app: ingress-default  
spec:  
  rules:  
  - http:  
      paths:  
      - path: /  
        backend:  
          serviceName: ingress-default  
          servicePort: 80

正如在 YAML 中的:

ingress-default-static-files

我们可以知道,ConfigMap 包含index.html 和 main.css 文件的内容。通过编辑或替换此 ConfigMap,我们可以更改在 Golang 文件服务器应用程序中的文件。

使用 ConfigMap 作为卷

在 Docker 和 Kubernetes 的中,卷用于解决两个问题:

  • 需要持久化的文件系统;
  • 需要在容器之间共享的文件系统。
    现在,我们将已部署在容器中的卷映射到 ConfigMap 资源中。在下面的代码段中,被配置的 html-files 卷可能被 Pod 中的所有容器使用。

卷会将数据配置映射到 ConfigMap 中的 ingress-default-static-files 上。

...volumes:  
     - name: html-files  
       configMap:  
         name: ingress-default-static-files…

在 Pod 级别配置卷后,我们将配置的卷装入容器中。将此卷的挂载映射到在 Pod 中配置的 html-files 卷上。通过此映射,应用程序容器现在可以访问 ConfigMap 中的两个文件:html/index.html 和 html/mian.css。

...volumeMounts:  
     - readOnly: true  
       mountPath: html  
       name: html-files 

当在 Kubernetes 集群中启动 Golang 应用程序时,ingress-default 会在 NGINX 入口控制器中配置上游规则。生成的路径将通过 NGINX 入口控制器将集群边缘连接到ingress-default 服务上。此服务指向 Golang 文件服务的 app Pod 中。在运行时,它为 ingress 控制器的根路径上的默认 Web 应用程序提供服务。如果需要更改此网页,我们只需要 edit/replace ConfigMap。

结语

容器编排的一个关键好处是,它承诺消除多个容器工作负载所需的“无差异的繁重工作”。通过使用 Kubernetes 声明性配置功能(如 ConfigMap),可以提高应用程序部署和更改集群状态的效率与速度。我们通过将 ConfigMap 资源作为已安装的卷,使用正在运行的容器,可以从容器中抽象配置和内容,减少对镜像重构和重新部署容器的需求。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容

  • 1、基础架构 1.1 Master Master节点上面主要由四个模块组成:APIServer、scheduler...
    阿斯蒂芬2阅读 10,870评论 0 44
  • 一、 K8s 是什么? Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署,调度和节点集群...
    loveroot阅读 6,643评论 1 21
  • 我只愿与朋友如水般相处 朋友袁的父亲去世,我知道的第一时间打了电话。不久一个通过我认识袁,并与袁有一些人情来往的静...
    天边微云阅读 382评论 4 6
  • 从来没有如此无辜, 落寞中还添酸楚。 初起的那刻花飞舞, 花城偶遇的我们, 和声阳春白雪, 在岁月里同步。 几季雨...
    狐落云端阅读 1,504评论 41 35
  • “我在我的绘画中创造一切。”—— 塔西娜·亚玛瑞 她是谁? 她的《食人者》之于巴西的意义,不亚于《蒙娜丽莎》...
    泡芙兮阅读 908评论 0 1