ConfigMap
В kubernetes есть объект ConfigMap, который хранит конфигурации для других объектов куба. ConfigMap может быть использован в случаях, когда мы хотим:
- Во внутрь контейнера прокинуть файл с конфигурацией для нашего приложения, через read-only volume;
- Добавить переменные окружения во внутрь контейнера;
- Передать в контейнер агрументы командной строки.
Мы можем хранить любую информацию в манифесте конфигмапы (кроме паролей и etc..), в кластере создаем новый configmap-объект и описываем его конфигурацию, в поле data
. Далее включаем объект в деплоймент, поле template: -> containers:
. При запуске пода, кубернетес примонтирует конфигмап как volume.
Теперь на практике, рассмотрим каждый из этих кейсов.
ConfigMap, для прокидывания файла.
Создадим новый манифест, где пропишем конфигурации для nginx. Далее примонтируем объект с конфигой в наш deployment.
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
default.conf: |
server {
listen 80 default_server;
server_name _;
default_type text/plain;
location / {
return 200 '$hostname\n';
}
}
Первые директивы нам уже знакомы, мы указываем версию api и тип создаваемого объекта - ConfigMap
. В метаданных указываем поле - name
c именем конфигмапы.
Добавляется новое поле - data
. В этом поле в качестве ключа указывается имя файла - default.conf
, в значение через пайп (|) прописываем конфиг для nginx сервера.
Применяем манифест, добавляем конфигурацию:
$ kubectl apply -f nginx-config.yml
---
configmap/nginx-config created
Напишем новый деплоймент с добавлением configmap:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-config
spec:
replicas: 2
selector:
matchLabels:
depl: nginx-conf
strategy:
rollingUpdate:
maxSurge: 2
maxUnavailable: 2
type: RollingUpdate
template:
metadata:
labels:
depl: nginx-conf
spec:
containers:
- image: nginx:1.17
name: nginx
ports:
- containerPort: 80
readinessProbe:
failureThreshold: 3
httpGet:
path: /
port: 80
successThreshold: 1
periodSeconds: 10
timeoutSeconds: 3
livenessProbe:
failureThreshold: 3
httpGet:
path: /
port: 80
successThreshold: 1
periodSeconds: 10
timeoutSeconds: 3
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 150m
memory: 150Mi
volumeMounts:
- name: nginx-configs
mountPath: /etc/nginx/conf.d
volumes:
- name: nginx-configs
configMap:
name: nginx-config
Сам deployment манифест разбирался в предыдущих главах, обратим внимание на конец yml-файла.
На одном уровне с полем containers
добавилось поле volumes
, это отдельная сушность которая описывает все тома в этой деплойменте.
Далее идет название тома - name: nginx-configs
, созданный из объекта configMap
, а имя configmap - name: nginx-config
.
Подключаем этот том к контейнеру, в конце описания контейнера добавилось новое поле volumeMounts
, в котором перечисляются подключаемые тома. В этом поле мы указываем имя volume - name: nginx-configs
. И путь куда монтируется раздел внутри контейнера - mountPath: /etc/nginx/conf.d
.
Запускаем деплоймет и заходим во внутрь контейнера, что бы пролистить примонтированную конфигурацию:
$ kubectl apply -f deployment-config.yml
$ kubectl exec -ti deployment-config-84b475d9f5-rb2vp bash
Находясь внутри контейнера, дергаем примапленую конфигу nginx:
root@deployment-config-84b475d9f5-rb2vp:/# cat /etc/nginx/conf.d/default.conf
server {
listen 80 default_server;
server_name _;
default_type text/plain;
location / {
return 200 '$hostname\n';
}
}
Когда для запуска пода требуется configmap, kubernetes берет значения ключей configmap-манифеста из своей базы и создает файлик с содержимым на узле, где запланирован запуск контейнера, далее монтирует этот файл во внутрь контейнера нашего пода.
Поменяем конфигмапу, что бы проверить как новый конфиг применится к подам. Для этого юзаем команду (cm - сокращенно ConfigMap):
$ kubectl edit cm nginx-config
--
return 200 '$hostname\\nOKE\\n';
В локейшене nginx, в конец строки добавил слово.
Теперь вновь подключается к контейнеру, и смотрим появились ли изменения.
$ kubectl exec -ti deployment-config-84b475d9f5-rb2vp bash
---
root@deployment-config-84b475d9f5-rb2vp:/# cat /etc/nginx/conf.d/default.conf
server {
listen 80 default_server;
server_name _;
default_type text/plain;
location / {
return 200 '$hostname\nOKE\n';
}
}
Как видно из вывода изменения вступили в силу.
Мы можем постучаться курлом на наш под, но для начала нужно пробросить порт:
$ kubectl port-worward my-deployment-6f968d7c9c-dwbwr 20001:80
$ curl localhost:20001
---
my-deployment-6f968d7c9c-dwbwr
Есть один момент, для того что бы nginx увидел изменившийся конфиг нужно перезапустить контейнер. Работает это так, по причине того что nginx не может динамически перечитывать свою конфигурацию и нужно перезапустить процесс.
ConfigMap, для передачи переменных окружения.
В принципе, для передачи переменных в окружение контейнера, мы можем описать наши переменные в блоке env
, в контексте описания контейнера. И это будет работать.
$ vim my-pod-env.yml
---
apiVersion: v1
kind: Pod
metadata:
name: my-pod-env
spec:
containers:
- image: nginx:1.20
name: nginx-env
ports:
- containerPort: 80
env:
- name: NGINX_VERSION
value: "1.20"
- name: NGINX_PROXY
value: "backend01"
Проникаем во внутрь контейнера, и выводим список наших переменных.
$ kubectl exec -ti my-pod-env bash
---
root@my-pod-env:/# printenv | grep -i nginx
NGINX_PROXY=backend01
NGINX_VERSION=1.20
Данный подход хоть и работает, но не совсем применим когда у нас имется несколько сред (prod, dev, stage) и нужно придерживаться одинакового окружения для всех сред. Данный кейс решается, использованием ConfigMap, одну конфигурацию мы применяем для каждой из сред.
Такой тип конфигмапы можем создать через описания манифеста, или с использованием литералов:
$ kubectl create configmap nginx-enc-lit --from-literal=NGX_PROXY=backend1 --from-literal=NGX_VERSION=1.20
--
$ kubectl get cm nginx-enc-lit -o yaml
apiVersion: v1
data:
NGX_PROXY: backend1
NGX_VERSION: "1.20"
kind: ConfigMap
metadata:
creationTimestamp: "2022-12-07T08:35:08Z"
name: nginx-enc-lit
namespace: default
resourceVersion: "51791"
uid: 902cbb9c-5ca7-464b-9825-812ba0f55edf
Теперь создаем манифест для пода, и включаем конфигмапу:
$ cat my-pod-envfile.yml
---
apiVersion: v1
kind: Pod
metadata:
name: my-pod-envfile
spec:
containers:
- image: nginx:1.18
name: nginx
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: nginx-enc-lit
В контексте описания контейнера, перечисляем конфигмапы в поле - envFrom:
, через директиву configMapRef
с указанием имени конфигмапы, подключаем нашу конфигурацию - nginx-enc-lit
.
Запускаем под, и смотрим переменные:
$ kubectl exec -ti my-pod-envfile printenv | grep -i ngx
---
NGX_PROXY=backend1
NGX_VERSION=1.20
Стоит отметить, что для явного указание ключей и конфигмепов, мы можем использовать такую конструкцию:
env:
- name: MY_VAR
valueFrom:
configMapRef:
name: my-configmap-app
key: var_key
В поле env
, явно указываем имя переменной для окружения. Через valueFrom:
- указываем откуда берем данные. В нашем случае это - configMapRef:
, далее указывает имя конфигмапы и ключ.
Secret’s
В Kubernetes есть аналогичный ConfigMap
объект - Secrets (Секреты). Отличительной разницей является то, что в секретах содержимое ключей, из поля data
шифруются кодировкой base64. Теперь пароли, токены и секретные файлы для наших приложений мы можем изолировать в секретах, и не хранить их в гите или же не каждый раз добавлять в image. Ну и стоит отметить, что любой секрет можно раскодировать командой - base64
.
С помошью секретов, мы также можем прокинуть во внутрь контейнера в файлы с конфидециальной информацией, файлы монтируются в read-only tmpfs-каталог.
Или же передать записи секретов в качестве переменных среды.
Итак, на практике посмотрим два кейса использования:
Secrets, добавление секретов в виде файлов в томе.
Произведем ситуацию, условно мы хотим что бы клиента нашего веб-сервера обслуживались поверх HTTPS. Для этого создадим сертификат и закрытый ключ. Закрытый ключ должен быть зашищен, поэтому положим его в секреты.
Для начала локально создаем сертификат и прикатный ключ.
$ openssl genrsa -out mydev.key 2048
$ openssl req -new -x509 -key mydev.key -out mydev.crt -days 365 -subj /CN=mydev.local
Сертификат и ключ есть, создаем новый secretmap, создавать будем через утилиту kubectl
, далее посмотрим как бы выглядил манифест.
$ kubectl create secret generic mydev-https-certs --from-file=mydev.key --from-file=mydev.crt
---
secret/mydev-https-certs created
Пролистим содержимое созданного секрета:
tony@i3Arch:~/Documents/minikube-sandbox/secrets-configmaps » kubectl get secrets mydev-https-certs -o yaml apiVersion: v1
data:
mydev.crt: LS0tLS1CRUdJTiBDRVJUSUZJ......
mydev.key: LS0tLS1CRUdJTiBQUklWQVKN......
kind: Secret
metadata:
creationTimestamp: "2022-12-11T14:47:06Z"
name: mydev-https-certs
namespace: default
resourceVersion: "240752"
uid: 2e0dc781-317b-41bf-ab99-028618803b0b
type: Opaque
В поле data
появилось два ключа - имена файлов сертификата и ключа. В значении ключей - mydev.crt
, mydev.key
закодированное base64 содержимое, одноименных файлов.
Из за большого количества строк в файлах сертифика и ключа, удобно создавать секрет через kubectl
c использованием ключа --from-file
. Иначе мы могли бы ручным способом создать манифест, предварительно закодировав содержимое исходных файлов.
Сразу же отметим, что объект secrets
бывает нескольких типов:
generic
- используется для хранения настроек в формате ключ:значение. Здесь храним пароли, токены, etc…;docker-registry
- это специальный тип секретов, в которых храняться параметры подключения к приватный docker-репозиториям;tls
- здесь хранятся tls сертификаты, обычно для использования в ingress.
Создадим новый nginx конфиг, с указанием порты и путей к ключу и сертификату.
server {
listen 80;
listen 443 ssl;
server_name _;
ssl_certificate certs/mydev.crt;
ssl_certificate_key certs/mydev.key;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
default_type text/plain;
location / {
return 200 'Hello, I am $hostname\n';
}
}
Теперь нужно создать новый конфиг мапу для нашего приложения:
$ kubectl create cm mydev-https-conf --from-file=nginx-mydev.conf
Подготовка завершена, теперь можно перейти к созданию деплойнемта, и запуска nginx. Пишем новый деплоймент:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mydev-nginx
spec:
replicas: 2
selector:
matchLabels:
app: mydev
strategy:
rollingUpdate:
maxSurge: 2
maxUnavailable: 2
type: RollingUpdate
template:
metadata:
labels:
app: mydev
spec:
containers:
- image: nginx:1.20
name: nginx
ports:
- containerPort: 80
- containerPort: 443
readinessProbe:
failureThreshold: 3
successThreshold: 1
periodSeconds: 15
timeoutSeconds: 5
httpGet:
port: 80
path: /
livenessProbe:
failureThreshold: 3
successThreshold: 1
periodSeconds: 15
timeoutSeconds: 5
httpGet:
port: 80
path: /
resources:
requests:
cpu: 200m
memory: 250Mi
limits:
cpu: 200m
memory: 250Mi
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d/
- name: nginx-certs
mountPath: /etc/nginx/certs/
volumes:
- name: nginx-certs
secret:
secretName: mydev-https-certs
- name: nginx-conf
configMap:
name: mydev-https-conf
Так как манифест деплоймента достаточно тривиальный, мы заострим внимание только на монтирование каталогов.
В поле volumes
определяем два раздела:
-
Раздел с сертификатом и ключем:
- name: nginx-certs secret: secretName: mydev-https-certs
десь указываем имя раздела, далее говорится что используемый тип раздела - `secret`. В конце указывается имя используемого секрета.
-
Раздел с конфигом:
- name: nginx-conf configMap: name: mydev-https-conf
о аналогии с прошлой темой, указываем имя раздела - `name: nginx-conf`. Говорим, что тип раздела - `configMap:` и указываем имя конфигмапы.
В блоке описания контейнера создаем точки монтирования - mountPoints
. Указываем имя volume, и путь к какому каталогу монтируем внутри контейнера.
Запускаем деплоймент, проваливаемcя во внутрь контейнера и смотрим напичие примонтированных каталогов.
$ kubectl exec -ti mydev-nginx-847b7679d5-hrj4g bash
--
root@mydev-nginx-847b7679d5-hrj4g:/# ls -l /etc/nginx/certs/
lrwxrwxrwx 1 root root 16 Dec 11 16:06 mydev.crt -> ..data/mydev.crt
lrwxrwxrwx 1 root root 16 Dec 11 16:06 mydev.key -> ..data/mydev.key
--
root@mydev-nginx-847b7679d5-hrj4g:/# cat /etc/nginx/conf.d/nginx-mydev.conf
server {
listen 80;
listen 443 ssl;
server_name _;
ssl_certificate certs/mydev.crt;
ssl_certificate_key certs/mydev.key;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
default_type text/plain;
location / {
return 200 'Hello, I am $hostname\n';
}
}
Давайте пробросим порт до контейнера и попробуем выдернуть самоподписной сертификат:
$ kubectl port-forward mydev-nginx-847b7679d5-hrj4g 8443:443
$ curl https://localhost:8443 -k -v
---
* Server certificate:
* subject: CN=mydev.local
* start date: Dec 11 14:41:41 2022 GMT
* expire date: Dec 11 14:41:41 2023 GMT
* issuer: CN=mydev.local
* SSL certificate verify result: self-signed certificate (18), continuing anyway.
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
...
< HTTP/1.1 200 OK
Secrets, добавление переменных окружения.
Для реализации это примера, создадим новый секрет с указанием наших переменных. Далее обновим deployment.
Через утилиту kubectl
, создаем новый секрет с указанием литералов:
$ kubectl create secret generic nginx-env --from-literal user=user1
---
secret/nginx-env created
Обновляем манифест деплоймента. В контексте описания контейнера добавляем поле env
:
env:
- name: USER
valueFrom:
secretKeyRef:
name: nginx-env
key: user
Здесь указываем имя переменной name: USER
, и источник - valueFrom
. В последующем указывается из какого объекта берется значение - secretKeyRef
, имя секрета - name: nginx-env
и ключ - key: user
.
Применяем обновленный деплоймент, и смотрим результат:
tony@i3Arch:~/Documents/minikube-sandbox/secrets-configmaps » kubectl apply -f mydev-deployment.yml
tony@i3Arch:~/Documents/minikube-sandbox/secrets-configmaps » kubectl exec -ti mydev-nginx-5bb8ffd7d6-jhqw6 printenv | grep -i user
--
USER=user1