将配置文件和秘密文件挂载到Kubernetes Pods中作为卷轴的指南

299 阅读5分钟

在这个例子中,我们将把秘密和非秘密的配置文件装入Pod。对于秘密,我们将使用两种方法。使用secret.yaml 文件和在Kubernetes集群中手动创建秘密。第一种方法的问题是,如果你提交了secret.yaml 文件,你就会暴露你的秘密,所以使用手动选项会更安全。只要确保秘密已经在Kubernetes中,否则Pod会崩溃。

我们要去挂载。

  • 一个特定文件夹中的单个文件的名称。 非秘密的

  • 特定文件夹中的所有文件,不对其命名。 秘密

  • 应用根中的单个文件。 秘密

结构

secret 文件夹和.env 文件下的文件不被提交,因为它们有开发环境的秘密。

├── Makefile
├── config
│   └── finders.yaml
├── deploy
│   └── k8s
│       ├── configmap.yaml
│       ├── deployment.yaml
│       └── secret.yaml
├── docker
│   └── dev
│       └── Dockerfile
├── .env
├── .env.dist
├── .gitignore
├── main.go
└── secret
    ├── credentials.conf
    └── keys.yaml

文件

制作文件

## Build application binary.
.PHONY: build
build:
   go build -race -ldflags "-s -w" -o bin/main main.go

## Build application binary and run it.
.PHONY: run
run: build
   bin/main

## -----------------------------------------------------------------------------------

## Build, tag and push application image to registry then clean up.
.PHONY: push
push:
   docker build -t you/address-finder:latest -f docker/dev/Dockerfile .
   docker push you/address-finder:latest
   docker rmi you/address-finder:latest
   docker system prune --volumes --force

## Deploy application to kubernetes cluster.
.PHONY: deploy
deploy:
   # This secret command is not needed for manual secret interactions (preferred).
   kubectl apply -f deploy/k8s/secret.yaml
   kubectl apply -f deploy/k8s/configmap.yaml
   kubectl apply -f deploy/k8s/deployment.yaml

.env

ENV_VAR_X=x_secret_from_repo_non_dist
ENV_VAR_Y=y_secret_from_repo_non_dist
ENV_VAR_Z=z_non_secret_from_repo_non_dist

.env.dist

ENV_VAR_X=x_secret_from_repo_dist
ENV_VAR_Y=y_secret_from_repo_dist
ENV_VAR_Z=z_non_secret_from_repo_dist

.gitignore

secret/
.env

.main.go

package main

import (
   "fmt"
   "io/ioutil"
   "log"
   "os"
   "time"

   "github.com/joho/godotenv"
)

func main() {
   fmt.Println("--- Secret credentials file ---------------------")
   scrConf, err := ioutil.ReadFile("secret/credentials.conf")
   if err != nil {
      log.Fatalln(err)
   }
   fmt.Println(string(scrConf))

   fmt.Println("--- Secret keys file ----------------------------")
   kysConf, err := ioutil.ReadFile("secret/keys.yaml")
   if err != nil {
      log.Fatalln(err)
   }
   fmt.Println(string(kysConf))

   fmt.Println("--- Public finders file -------------------------")
   finConf, err := ioutil.ReadFile("config/finders.yaml")
   if err != nil {
      log.Fatalln(err)
   }
   fmt.Println(string(finConf))

   fmt.Println("--- System environment variables ----------------")
   _ = godotenv.Load()
   fmt.Println(value("ENV_VAR_X"))
   fmt.Println(value("ENV_VAR_Y"))
   fmt.Println(value("ENV_VAR_Z"))

   time.Sleep(time.Hour)
}

func value(key string) string {
   v, ok := os.LookupEnv(key)
   if !ok {
      log.Fatalf("the environment var %s was not found", key)
   }

   return v
}

Docker文件

#
# STAGE 1: build
#
FROM golang:1.15-alpine3.12 as build

WORKDIR /source
COPY . .

RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/main main.go

#
# STAGE 2: run
#
FROM alpine:3.12 as run

COPY --from=build /source/bin/main /main

ENTRYPOINT ["/main"]

finders.yaml

finders:
  - name: postcode
    api: https://api.postcodes.io/postcodes
    method: GET
  - name: ip
    api: http://ip-api.com/json
    method: GET

credentials.yaml

title: this is credentials.conf secret content (local)
line_1: hello (local)

line_2: world (local)

keys.yaml

KEY_1: key 1 (local)
KEY_2: key 2 (local)

configmap.yaml

apiVersion: v1
kind: ConfigMap

metadata:
  name: address-finder-configmap

data:
  finders.yaml: |
    finders:
      - name: address
        api: https://api.getAddress.io/find
        method: GET
      - name: postcode
        api: https://api.postcodes.io/postcodes
        method: GET
      - name: ip
        api: http://ip-api.com/json
        method: GET

deployment.yaml

apiVersion: apps/v1
kind: Deployment

metadata:
  name: address-finder-deployment
  labels:
    app: address-finder

spec:
  replicas: 1
  selector:
    matchLabels:
      app: address-finder
  template:
    metadata:
      labels:
        app: address-finder
    spec:
      containers:
        - name: golang
          image: you/address-finder:latest
          volumeMounts:
            # Refers to a single file
            - name: config
              mountPath: ./config/finders.yaml
              subPath: finders.yaml
              readOnly: true
            # Refers to all the secret files in a folder
            - name: secret
              mountPath: ./secret
              readOnly: true
            # Refers to single secret file to app root
            - name: dotenv
              mountPath: ./.env
              subPath: .env # Without this, .env will be created as a folder, not a file!
              readOnly: true
      volumes:
        - name: config
          configMap:
            name: address-finder-configmap
        - name: secret
          secret:
            secretName: address-finder-secret
        - name: dotenv
          secret:
            secretName: address-finder-secret
            items:
              - key: .env
                path: ./.env

secret.yaml

记住这不是处理秘密的首选方式。你将删除这个文件并在选项2中用kubectl命令在Kubernetes集群中手动创建address-finder-secret 组件。你在这里看到的是选项1。

apiVersion: v1
kind: Secret

metadata:
  name: address-finder-secret

type: Opaque

data:
  credentials.conf: | # cat secret/credentials.conf | base64
    dGl0bGU6IHRoaXMgaXMgY3JlZGVudGlhbHMuY29uZiBzZWNyZXQgY29udGVudCAobG9jYWwpCmxpbmVfMTogaGVsbG8gKGxvY2FsKQoKbGluZV8yOiB3b3JsZCAobG9jYWwpCg==
  keys.yaml: |        # cat secret/keys.yaml | base64
    S0VZXzE6IGtleSAxIChsb2NhbCkKS0VZXzI6IGtleSAyIChsb2NhbCkK
  .env: |             # cat .env | base64
    RU5WX1ZBUl9YPXhfc2VjcmV0X2Zyb21fcmVwb19ub25fZGlzdApFTlZfVkFSX1k9eV9zZWNyZXRfZnJvbV9yZXBvX25vbl9kaXN0CkVOVl9WQVJfWj16X25vbl9zZWNyZXRfZnJvbV9yZXBvX25vbl9kaXN0Cg==

本地测试

这是我们在本地环境下运行应用程序时终端输出的情况。正如你所看到的,内容与我们上面列出的文件相符。

$ make run
go build -race -ldflags "-s -w" -o bin/main main.go
bin/main
--- Secret credentials file ---------------------
title: this is credentials.conf secret content (local)
line_1: hello (local)

line_2: world (local)

--- Secret keys file ----------------------------
KEY_1: key 1 (local)
KEY_2: key 2 (local)

--- Public finders file -------------------------
finders:
  - name: postcode
    api: https://api.postcodes.io/postcodes
    method: GET
  - name: ip
    api: http://ip-api.com/json
    method: GET

--- System environment variables ----------------
x_secret_from_repo_non_dist
y_secret_from_repo_non_dist
z_non_secret_from_repo_non_dist

部署

选项1

在这种情况下,我们将secret.yaml ,并依靠它来自动创建秘密。**这不是首选!**部署下面的应用程序:

$ make deploy
# This secret command is not needed for manual secret interactions (preferred).
kubectl apply -f deploy/k8s/secret.yaml
secret/address-finder-secret created
kubectl apply -f deploy/k8s/configmap.yaml
configmap/address-finder-configmap created
kubectl apply -f deploy/k8s/deployment.yaml
deployment.apps/address-finder-deployment created

检查下面的应用程序日志。

$ kubectl logs pod/address-finder-deployment-7f6d6959db-w57gn
--- Secret credentials file ---------------------
title: this is credentials.conf secret content (local)
line_1: hello (local)

line_2: world (local)

--- Secret keys file ----------------------------
KEY_1: key 1 (local)
KEY_2: key 2 (local)

--- Public finders file -------------------------
finders:
  - name: address
    api: https://api.getAddress.io/find
    method: GET
  - name: postcode
    api: https://api.postcodes.io/postcodes
    method: GET
  - name: ip
    api: http://ip-api.com/json
    method: GET

--- System environment variables ----------------
x_secret_from_repo_non_dist
y_secret_from_repo_non_dist
z_non_secret_from_repo_non_dist

正如你所看到的,秘密已经被解码(它们被编码为本地副本)并被安装在Pod中。另外,查找器的配置来自configmap.yaml 文件,而不是应用程序仓库。环境变量来自于.env 文件而不是环境,因为我们没有把它们作为普通的环境变量来创建。让我们检查一下Pod的内容:

$ kubectl exec -it pod/address-finder-deployment-7f6d6959db-w57gn sh 
/ # ls -la
total 1816
-rw-r--r--    1 root     root           118 Nov 26 19:26 .env
drwxr-xr-x    2 root     root          4096 Nov 26 19:26 config
drwxrwxrwt    3 root     root           140 Nov 26 19:26 secret
...

/ # cat .env
ENV_VAR_X=x_secret_from_repo_non_dist
ENV_VAR_Y=y_secret_from_repo_non_dist
ENV_VAR_Z=z_non_secret_from_repo_non_dist

/ # ls -l config/
total 4
-rw-r--r--    1 root     root           223 Nov 26 19:26 finders.yaml
/ # cat config/finders.yaml 
finders:
  - name: address
    api: https://api.getAddress.io/find
    method: GET
  - name: postcode
    api: https://api.postcodes.io/postcodes
    method: GET
  - name: ip
    api: http://ip-api.com/json
    method: GET

/ # ls -l secret/
total 0
lrwxrwxrwx    1 root     root            23 Nov 26 19:26 credentials.conf -> ..data/credentials.conf
lrwxrwxrwx    1 root     root            16 Nov 26 19:26 keys.yaml -> ..data/keys.yaml
/ # cat secret/credentials.conf 
title: this is credentials.conf secret content (local)
line_1: hello (local)

line_2: world (local)
/ # cat secret/keys.yaml 
KEY_1: key 1 (local)
KEY_2: key 2 (local)

让我们检查一下秘密组件是什么样子的。我们将在后面的选项2中手动创建:

$ kubectl get secrets
NAME                    TYPE                                  DATA   AGE
address-finder-secret   Opaque                                3      10m

$ kubectl describe secret address-finder-secret
Name:         address-finder-secret
Namespace:    default
Labels:       
Annotations:  

Type:  Opaque

Data
====
keys.yaml:         42 bytes
.env:              118 bytes
credentials.conf:  100 bytes

方案2

这就是我们根本没有secret.yaml ,而是依靠在部署前在Kubernetes中手动创建秘密--**首选!**这里我们手动创建秘密文件。我已经有了它们,所以只是展示了内容:

$ cat ./Desktop/credentials.conf 
title: this is credentials.conf secret content (desktop)
line_1: hello (desktop)

line_2: world (dektop)

$ cat ./Desktop/keys.yaml 
KEY_1: key 1 (desktop)
KEY_2: key 2 (desktop)

$ cat ./Desktop/.env 
ENV_VAR_X=x_secret_from_desktop
ENV_VAR_Y=y_secret_from_desktop
ENV_VAR_Z=z_non_secret_from_desktop

让我们把这些文件变成Kubernetes的秘密组件:

$ kubectl create secret generic address-finder-secret --save-config \
> --from-file=./Desktop/credentials.conf \
> --from-file=./Desktop/keys.yaml \
> --from-file=./Desktop/.env

正如你在下面看到的,这个命令与之前在选项1中创建的secret.yaml :

$ kubectl get secrets
NAME                    TYPE                                  DATA   AGE
address-finder-secret   Opaque                                3      21s

$ kubectl describe secret address-finder-secret
Name:         address-finder-secret
Namespace:    default
Labels:       
Annotations:  

Type:  Opaque

Data
====
.env:              101 bytes
credentials.conf:  105 bytes
keys.yaml:         47 bytes

现在我们来部署这个应用程序。我从make命令中删除了secret.yaml 命令:

$ make deploy
kubectl apply -f deploy/k8s/configmap.yaml
configmap/address-finder-configmap created
kubectl apply -f deploy/k8s/deployment.yaml

让我们看看应用程序的日志/输出,重点是秘密:

$ kubectl logs pod/address-finder-deployment-7f6d6959db-f4rcr
--- Secret credentials file ---------------------
title: this is credentials.conf secret content (desktop)
line_1: hello (desktop)

line_2: world (dektop)

--- Secret keys file ----------------------------
KEY_1: key 1 (desktop)
KEY_2: key 2 (desktop)


--- Public finders file -------------------------
finders:
  - name: address
    api: https://api.getAddress.io/find
    method: GET
  - name: postcode
    api: https://api.postcodes.io/postcodes
    method: GET
  - name: ip
    api: http://ip-api.com/json
    method: GET

--- System environment variables ----------------
x_secret_from_desktop
y_secret_from_desktop
z_non_secret_from_desktop

正如你在上面看到的,它工作得很好。

如果你想在以后更新秘密,你可以使用kubectl edit secret address-finder-secret 命令。只要记住它需要base64编码的数据。

你可以使用下面的命令进行base64编码/解码:

// encode
$ cat {file-path} | base64

// decode
$ echo -n {plain-secret-goes-here} | base64