Файлы, упомянутые в главе

  • .helm/templates/deployment.yaml
  • .helm/secret-values.yaml
  • .helm/values.yaml
  • src/server/server.js

В этой главе мы настроим в нашем базовом приложении работу с пользовательскими файлами. Для этого потребуется персистентное (постоянное) хранилище.

В идеале — нужно добиться, чтобы приложение было stateless, а данные хранились в S3-совместимом хранилище — например, MinIO или AWS S3. Это обеспечивает простое масштабирование, работу в HA-режиме и высокую доступность.

А есть какие-то способы кроме S3?

Первый и более общий способ — это использовать как volume хранилище NFS, CephFS или hostPath.

Мы не рекомендуем этот способ, потому что при возникновении неполадок с такими типами volume’ов они влияют на работоспособность контейнера и всего демона Docker в целом. Тогда могут пострадать приложения, не имеющие никакого отношения к вашему.

Более надёжный путь — пользоваться S3. Так мы используем отдельный сервис, который имеет возможность масштабироваться, работать в HA-режиме и иметь высокую доступность. Можно воспользоваться облачным решением вроде AWS S3, Google Cloud Storage, Microsoft Blobs Storage и т.д.

Природа Kubernetes (и оркестровки контейнеров в целом) такова, что если мы будем сохранять файлы в какой-либо директории у приложения (запущенного в Kubernetes), то после перезапуска контейнера все изменения пропадут.

Данная настройка производится полностью в рамках приложения. Рассмотрим подключение к S3 на примере MinIO.

Для этого подключим пакет minio в npm:

$ npm install minio --save

… и настроим работу с MinIO S3 в приложении:

const Minio = require("minio");
const S3_ENDPOINT = process.env.S3_ENDPOINT || "127.0.0.1";
const S3_PORT = Number(process.env.S3_PORT) || 9000;
const TMP_S3_SSL = process.env.S3_SSL || "true";
const S3_SSL = TMP_S3_SSL.toLowerCase() == "true";
const S3_ACCESS_KEY = process.env.S3_ACCESS_KEY || "SECRET";
const S3_SECRET_KEY = process.env.S3_SECRET_KEY || "SECRET";
const S3_BUCKET = process.env.S3_BUCKET || "avatars";
const CDN_PREFIX = process.env.CDN_PREFIX || "http://127.0.0.1:9000";

// S3 client
var s3Client = new Minio.Client({
  endPoint: S3_ENDPOINT,
  port: S3_PORT,
  useSSL: S3_SSL,
  accessKey: S3_ACCESS_KEY,
  secretKey: S3_SECRET_KEY,
});
const Minio = require("minio"); const S3_ENDPOINT = process.env.S3_ENDPOINT || "127.0.0.1"; const S3_PORT = Number(process.env.S3_PORT) || 9000; const TMP_S3_SSL = process.env.S3_SSL || "true"; const S3_SSL = TMP_S3_SSL.toLowerCase() == "true"; const S3_ACCESS_KEY = process.env.S3_ACCESS_KEY || "SECRET"; const S3_SECRET_KEY = process.env.S3_SECRET_KEY || "SECRET"; const S3_BUCKET = process.env.S3_BUCKET || "avatars"; const CDN_PREFIX = process.env.CDN_PREFIX || "http://127.0.0.1:9000"; // S3 client var s3Client = new Minio.Client({ endPoint: S3_ENDPOINT, port: S3_PORT, useSSL: S3_SSL, accessKey: S3_ACCESS_KEY, secretKey: S3_SECRET_KEY, });

Для работы с S3 необходимо пробросить ключи доступа в приложение. Для этого стоит использовать механизм секретных переменных. Вопрос работы с секретными переменными рассматривался подробнее, когда мы делали базовое приложение.

.helm/secret-values.yaml (расшифрованный) копировать имя копировать текст
app:
  s3:
    access_key:
      _default: bNGXXCF1GF
    secret_key:
      _default: zpThy4kGeqMNSuF2gyw48cOKJMvZqtrTswAQ
app: s3: access_key: _default: bNGXXCF1GF secret_key: _default: zpThy4kGeqMNSuF2gyw48cOKJMvZqtrTswAQ

Несекретные значения — храним в values.yaml:

app:
<...>
  s3:
    host:
      _default: minio
    port:
      _default: 9000
    bucket:
      _default: 'avatars'
    ssl:
      _default: 'false'
app: <...> s3: host: _default: minio port: _default: 9000 bucket: _default: 'avatars' ssl: _default: 'false'

После того, как значения корректно прописаны и зашифрованы, можно пробросить соответствующие значения в Deployment:

        - name: CDN_PREFIX
          value: {{ printf "%s%s" (pluck .Values.global.env .Values.app.cdn_prefix | first | default .Values.app.cdn_prefix._default) (pluck .Values.global.env .Values.app.s3.bucket | first | default .Values.app.s3.bucket._default) | quote }}
        - name: S3_SSL
          value: {{ pluck .Values.global.env .Values.app.s3.ssl | first | default .Values.app.s3.ssl._default | quote }}
        - name: S3_ENDPOINT
          value: {{ pluck .Values.global.env .Values.app.s3.host | first | default .Values.app.s3.host._default | quote  }}
        - name: S3_PORT
          value: {{ pluck .Values.global.env .Values.app.s3.port | first | default .Values.app.s3.port._default | quote }}
        - name: S3_ACCESS_KEY
          value: {{ pluck .Values.global.env .Values.app.s3.access_key | first | default .Values.app.s3.access_key._default | quote }}
        - name: S3_SECRET_KEY
          value: {{ pluck .Values.global.env .Values.app.s3.secret_key | first | default .Values.app.s3.secret_key._default | quote }}
        - name: S3_BUCKET
          value: {{ pluck .Values.global.env .Values.app.s3.bucket | first | default .Values.app.s3.bucket._default | quote }}
- name: CDN_PREFIX value: {{ printf "%s%s" (pluck .Values.global.env .Values.app.cdn_prefix | first | default .Values.app.cdn_prefix._default) (pluck .Values.global.env .Values.app.s3.bucket | first | default .Values.app.s3.bucket._default) | quote }} - name: S3_SSL value: {{ pluck .Values.global.env .Values.app.s3.ssl | first | default .Values.app.s3.ssl._default | quote }} - name: S3_ENDPOINT value: {{ pluck .Values.global.env .Values.app.s3.host | first | default .Values.app.s3.host._default | quote }} - name: S3_PORT value: {{ pluck .Values.global.env .Values.app.s3.port | first | default .Values.app.s3.port._default | quote }} - name: S3_ACCESS_KEY value: {{ pluck .Values.global.env .Values.app.s3.access_key | first | default .Values.app.s3.access_key._default | quote }} - name: S3_SECRET_KEY value: {{ pluck .Values.global.env .Values.app.s3.secret_key | first | default .Values.app.s3.secret_key._default | quote }} - name: S3_BUCKET value: {{ pluck .Values.global.env .Values.app.s3.bucket | first | default .Values.app.s3.bucket._default | quote }}