# generic

这里将分作两部分讨论，第一部分是将数据传入generic类型的Secret，即创建Secret，第二部分是将数据从Secret中传入容器中（以环境变量的方式、以文件的形式）。

该类型的Secret的`secret.type=Opaque`。

## 配置传入Secret

数据传入Secret的过程，即**创建Secret**的过程，在创建时，可以将需要传给Pod中容器的信息以**键/值对**的形式写入Secret中。

创建Secret与ConfigMap同样有两种方式：

1. 使用指令创建（优选）；
2. 使用配置文件创建；

### 使用命令创建

在使用`kubectl create secret generic`创建generic类型的Secret时，可以通过不同的参数，以不同的方式向其中写入数据：

1. `--from-literal`：最普通的写入方式，值为简单的字面量：

   例如，创建一个名为`mysecret`的Secret，其包含`username=admin`、`password=123456`两个条目：

   ```bash
    kubectl create secret generic mysecret --from-literal=username=admin --from-literal=password=123456
   ```
2. `--from-env-file`：后面跟的是文件名，文件中存放的是**键/值对**形式的条目，它们会被批量传入Secret中：

   ```bash
    # 先通过key=value的方式创建环境变量的文件
    cat << EOF > env.txt
    > username=admin
    > password=123456
    > EOF
    # 使用k8s命令创建
    kubectl create secret generic mysecret --from-env-file=env.txt
   ```
3. `--from-file`：将文件内容作为值写入：

   例如，创建一个名为`mysecret`的generic类型的Secret，其包含的文件内容为`username.conf`中的内容，其**键**为`username.conf`（因为不自定义的关系，所以默认与文件名一致）：

   ```bash
    kubectl create secret generic mysecret --from-file=username.conf
   ```

   当然，也可以手动指定键。例如，指定键名为`username`：

   ```bash
    kubectl create configmap myconfigmap --from-file=username=username.conf
   ```

{% hint style="info" %}
**Tips:** `--from-file`后面除了跟单个文件名外，同样可以跟**目录名**，其会将该目录下所有文件名合法的文件都传入Secret中，即批量传入多个条目，但是这种情况下，无法单独指定键名。
{% endhint %}

### 使用配置文件创建

Secret的配置文件非常简单，需要写入的数据都被存放在了`secret.data`中，下面展示一个案例：

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysecret    # 该Secret的名字
type: Opaque    # Secret的默认类型，可以不指定
stringData:
  foo: bar    # stringData中存放非二进制数据，可以不用手动Base64编码，但是在创建后，会自动编码
data:
  username: YWRtaW4=    # 敏感数据需手动base64编码，使用echo -n admin | base64得到，使用base64 --decode可以反编码
  password: MTIzNDU2    # password是文件名，在容器中，内容会自动反编码再存入文件
```

{% hint style="info" %}
**Tips:** 在Secret的配置文件中

1. 向`secret.data`中写入键/值对时，值需要经过Base64编码后再放入配置文件中；
2. 向`secret.stringData`中写入键/值对时，可以不经过Base64编码，就可以将值放入配置文件中；
   {% endhint %}

## 从Secret中读取数据

Pod中的容器想要从Secret中读取数据，与ConfigMap类似，同样有两种方式：

1. 读取为环境变量（不推荐）：分为读取单个条目和读取所有条目两种方式；
2. 读取为文件：将Secret以卷的方式挂载，进而从中读取数据；

### 读取为环境变量

{% hint style="danger" %}
再次申明，将Secret中的数据读取为环境变量的方式，不推荐！！！若想读取为环境变量，还是推荐使用ConfigMap。
{% endhint %}

与[**从ConfigMap读取配置为环境变量**](https://yangsijie151104.gitbook.io/k8s-note/configmap-he-secret/configmap/cong-configmap-zhong-du-qu-pei-zhi/configmap-du-qu-wei-huan-jing-bian-liang)类似，可以从Secret中读取数据作为环境变量，可以读取单个条目，或者一次性读取所有条目。

#### 读取单个条目

读取单个条目，是通过Pod的配置文件中的`spec.env.valueFrom.secretKeyRef`进行配置，从而从Secret中读取指定的条目，作为环境变量。

例如，为容器创建一个键为`FOO_SECRET`的环境变量，其值引用的是名为`fortune-https`的Secret中的键为`foo`的条目的值：

```yaml
env:    # 配置容器的环境变量
- name: FOO_SECRET    # 环境变量的Key
  valueFrom:    # 配置环境变量的Value
    secretKeyRef:    # 从Secret中读取数据
      name: fortune-https    # Secret的名字
      key: foo    # （必须）Secret中的条目的key，将key为foo的条目的value，作为容器的Key为FOO_SECRET的环境变量的Value
```

#### 读取所有条目

读取所有条目，是通过Pod的配置文件中的`spec.containers.envFrom.secretRef`进行配置，从而从Secret中读取所有的条目，作为环境变量。

如果Secret中某键名不符合环境变量的键名格式，那么在创建环境变量时，会自动忽略对应的条目。

例如，容器从名为`fortune-https`的Secret中读取所有条目作为环境变量：

```yaml
envFrom:    # 配置容器的环境变量
- secretRef:    # 从Secret中读取数据
  name: fortune-https    # Secret的名字
```

通过指定`spec.containers.envFrom.prefix`可以为读入的所有环境变量添加前缀，比如Secret中包含键为`FOO`、`BAR`的两个条目，那么可以通过下面这种方式添加前缀`CONFIG_`，令读入容器中的环境变量变成`CONFIG_FOO`、`CONFIG_BAR`：

```yaml
envFrom:
- prefix: CONFIG_    # 为读入的环境变量添加前缀CONFIG_
  configMapKeyRef:
    name: fortune-config
```

### 读取为文件

与[**从ConfigMap读取配置为文件**](https://yangsijie151104.gitbook.io/k8s-note/configmap-he-secret/configmap/cong-configmap-zhong-du-qu-pei-zhi/configmap-du-qu-wei-wen-jian)类似，Secret中的条目可以被容器以文件的形式读入，这种情况下，需要将Secret作为卷挂载至Pod中的容器中。

在挂载至容器后，默认情况下，容器会将Secret中的条目的**键**作为文件名，将条目的**值**作为文件内容，且在读取内容时会自动将其Base64解码。

因为Secret中具有多个条目，因此可以通过在`pod.spec.volumes.secret.items`中进行指定单独挂载的文件。

#### 挂载所有文件

将Secret作为卷挂载，需要下面两步：

1. 定义卷：在`spec.volumes.secret`中指定`secretName`为预先创建好的Secret资源的名字，即与Secret资源相关联；
2. 挂载卷：在Pod的`spec.containers.volumeMounts`中指定挂载点和挂载的卷名；

下面举一个例子，在容器的`/etc/nginx/certs/`下挂载一个名为`certs`的Secret卷，该卷与名为`fortune-https`的Secret相关联，容器会将该Secret中的所有条目，以条目的键为文件名，值的Base64解码作为文件内容，在挂载点中创建这些文件：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: fortune-https
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:    # 定义挂载点
    - name: certs    # （必须）挂载的卷名
      mountPath: /etc/nginx/certs/    # （必须）容器中的挂载点，该目录下的原文件都会被隐藏
      readOnly: true
    ports:
    - containerPort: 80
    - containerPort: 443
  volumes:    # 定义卷
  - name: certs    # （必须）卷名
    secret:    # 创建的是Secret卷
      secretName: fortune-https    # 需要挂载的Secret的名字
      defaultMode: 0660    # 为挂载的文件设置权限，默认是0644，取值范围为0~0777
```

#### 挂载指定文件

同样，挂载卷的方式可以只挂载部分文件，而不是将Secret中的所有文件都挂载进Pod。有两种方法可以实现：

1. 利用`pod.spec.volumeMounts.subPath`将卷中的指定文件挂载至容器中，具体方法可以参考[**卷**](https://yangsijie151104.gitbook.io/k8s-note/juan/juan-lei-xing)这章节；

   优点：挂载点中原本存在的文件不会被隐藏；

   缺点：应用于Secret卷时，被挂载的文件不会随着Secret中条目的更新而更新；
2. 利用`spec.volumes.secret.items`在**定义卷**时，指定需要被挂载至卷中的条目；

   优点：被挂载的文件会随着Secret中条目的更新而更新，[**更新原理与ConfigMap的同步更新一样**](https://yangsijie151104.gitbook.io/k8s-note/configmap-he-secret/configmap/cong-configmap-zhong-du-qu-pei-zhi/configmap-du-qu-wei-wen-jian)；

   缺点：挂载点中原本存在的文件会被隐藏；

这里针对第二种方法进行详细介绍。

下面举一个例子，在容器的`/etc/nginx/certs/`下挂载一个名为`certs`的Secret卷，该卷与名为`fortune-https`的Secret相关联，容器会将该Secret中的键为`username.conf`的条目，更名为`username`，将条目的值Base64解码后，在挂载点中创建该文件：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: fortune-https
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:    # 定义挂载点
    - name: certs    # （必须）挂载的卷名
      mountPath: /etc/nginx/certs/    # （必须）容器中的挂载点，该目录下的源文件都会被隐藏
      readOnly: true
    ports:
    - containerPort: 80
    - containerPort: 443
  volumes:    # 定义卷
  - name: certs    # （必须）卷名
    secret:    # 创建的是Secret卷
      name: fortune-https    # 需要挂载的ConfigMap的名字
      items:    # 仅挂载ConfigMap中的部分条目
      - key: username.conf    # （必须）Secret中条目的键名
        path: username    # （必须）挂载至容器中挂载点的相对路径
        mode: 0660    # 为挂载的文件设置权限，默认是0644，取值范围为0~0777
```
