Hibernate, Spring и имена таблиц, полей базы данных.

Эх, давно не брал я в руки шашку. (с) Василий Иванович

Пришлось и мне встать на тропу микросервисов. Поскольку java основной язык программирования, то в качестве платформы был выбран Spring. Начинаю потихоньку копать этот фреймворк.

Досталась мне в наследство базёнка, в которой имена таблиц и полей начинаются с большой буквы. Типа: Notes, NotesIndex и т.п.

Настраиваю spring JPA, все как в примере работы с MySQL. Только в отличии от примера использую готовую таблицу. Проверку опять же:

spring.jpa.hibernate.ddl-auto=validate

Описываю сущности. Причем понимаю, что название таблиц и полей нестандартное, явно указываю имена:

@Data
@Entity
@Table(name = "Notes")
public class Notes {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name="NoteIndex")
  private int noteIndex;

Запускаю приложение и ловлю ошибку:

nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [notes]

Вот засада! Я ведь явно указал имя таблицы. Долго копался в интернетах и нашел решение: необходимо явно определить стратегию именования, иначе не реагирует оно на мои имена таблиц.

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Kubernetes, включение RBAC на namespace

Создадим namespace artur, доступ к которому будем разрешать пользователю artur:

# kubectl create namespace artur

Как создавать пользователя было описано тут.

Создаём файл с описанием role и rolebindig — 01. yaml следующего содержания:

---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      namespace: artur # namespace к которому применяются огранияения
      name: artur-role
    rules:
    - apiGroups: [""] # "" indicates the core API group
      resources: ["pods", "services", "replicationcontrollers"]
      verbs: ["create", "get", "update", "list", "delete"]
    - apiGroups: [""]
      resources: ["pods/log"]
      verbs: ["get", "list"]
    - apiGroups: [""]
      resources: ["pods/exec"]
      verbs: ["create"]
    - apiGroups: ["apps"]
      resources: ["deployments","daemonsets","replicasets","statefulsets"]
      verbs: ["create", "get", "update", "patch", "list", "delete", "deploy"]
    - apiGroups: ["autoscaling"]
      resources: ["horizontalpodautoscalers"]
      verbs: ["create", "get", "update", "list", "delete"]
   - apiGroups: ["batch"]
     resources: ["jobs","cronjobs"]
     verbs: ["create", "get", "update", "list", "delete"]
--- apiVersion: rbac.authorization.k8s.io/v1 
kind: RoleBinding 
metadata:
 name: artur-rb
 namespace: artur
subjects: 
- kind: User
  name: artur
  apiGroup: rbac.authorization.k8s.io 
roleRef:
 kind: Role
 name: artur-role
 apiGroup: rbac.authorization.k8s.io

Создаем роли и биндинги в кубернетес.

# cubectl apply -f 01.yaml

Всё, что тут написано — это художественная обработка материалов доброго человека.

Kubernetes, доступ по сертификату пользователя

В принципе — это шпаргалка, что бы не забыть как все на самом деле происходит 🙂

Создание SSL ключа и сертификата пользователя

Создаём временную директорию:

# mkdir tmp
# cd tmp

Генерируем SSL ключ пользователя:

# openssl genrsa -out artur.key 4096

Создаём конфигурационный файл для openssl — csr.cnf следующего содержания:

[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[ dn ]
CN = artur
O = dev

[ v3_ext ]
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth,clientAuth

Генерируем запрос на сертификат:

# openssl req -config csr.cnf -new -key artur.key -nodes -out artur.csr

Формируем запрос на сертификат в формате base64 и помещаем его в переменную среды окружения:

# export BASE64_CSR=$(cat artur.csr | base64 | tr -d '\n')

Создаём yaml файл csr.yaml с запросом на подпись следующего содержания:

    apiVersion: certificates.k8s.io/v1beta1
    kind: CertificateSigningRequest
    metadata:
      name: artur_csr
    spec:
      groups:
      - system:authenticated
      request: ${BASE64_CSR}
      usages:
      - digital signature
      - key encipherment
      - server auth
      - client auth

Помещаем запрос в кубернетес:

# cat csr.yaml | envsubst | kubectl apply -f -

Контролируем наличие запроса:

# kubectl get csr

Подписываем и генерируем сертификат:

# kubectl certificate approve artur_csr

Копируем во временную директорию файл ~/.kube/config и в разделе users удаляем из него строки client-certificate-data и client-key-data.

Добавляем в config строку с сертификатом клиента в формате base64:

# echo "client-certificate-data: $(kubectl get csr artur_csr -o jsonpath={.status.certificate})" >> config

В редакторе делаем правильное выравнивание строки. Также везде меняем пользователя на artur.

Добавляем в config файл ключ пользователя в формате base64:

# kubectl --kubeconfig ./config config set-credentials artur --client-key=artur.key --embed-certs=true

Смотрим получившийся конфигурационный файл:

# kubectl --kubeconfig ./config config view
apiVersion: v1
    clusters:
    - cluster:
        certificate-authority-data: DATA+OMITTED
        server: https://192.168.0.45:6443
      name: cluster.local
    contexts:
    - context:
        cluster: cluster.local
        user: artur
      name: artur@cluster.local
    current-context: artur@cluster.local
    kind: Config
    preferences: {}
    users:
    - name: artur
      user:
        client-certificate-data: REDACTED
        client-key-data: REDACTED

Передаём файл config пользователю или копируем его в директорию ~/.kube пользователя.

Шпаргалка сделана по мотивам записок доброго человека.

Чудеса случаются.

«Налетай, торопись, покупай живопись!»

New Always Free Services

https://www.oracle.com/cloud/free/#always-free

Oracle + халява — два несовместимых слова. Однако… мне кажется, что Оракл достаточно поздно включился в гонку облачных сервисов. Поэтому им приходится наступать на горло своей патологической жадности, что бы привлекать новых клиентов.

Fluent-bit и PodSecurityPolicy

Жили мы жили. И вроде не плохо. Но на тесте решили поставить Kubernetes при помощи kubespray v2.11.

Накатили в новый кластер fluent-bit, с отлаженным ранее деплоем. И… обломс.

Ругается при деплое DaemonSet на:

securityContext:
          privileged: true
volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true

В итоге оказалось, что в новом облачке включена PodSecurityPolicy. И поскольку наш DaemonSet не использует (подключен) к существующим политикам случился облом.

Решение проблемы – Добавить PodSecurityPolicy и применить её к DaemonSet.

Определяем новую политику:

---
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
  name: psp.td-agent
spec:
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  allowedUnsafeSysctls:
  - '*'
  fsGroup:
    rule: RunAsAny
  hostIPC: true
  hostNetwork: true
  hostPID: true
  hostPorts:
  - max: 65535
    min: 0
  privileged: true
  runAsUser:
    rule: RunAsAny
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  volumes:
  - 'hostPath'
  - 'configMap'
  - 'emptyDir'
  - 'secret'

Стандартно создаем ServiceAccount:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluent-bit
  namespace: logging

Определяем ClusterRole и в ней определяем используемый PodSecurityPolicy (выделено болдом):

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: psp:fluent-bit-read
rules:
- apiGroups:
  - '*'
#  - policy
  resources:
#  - podsecuritypolicies
  - '*'
  resourceNames:
  - psp.td-agent
  verbs:
#  - use
  - '*'

Я еще не до конца разобрался какие apiGroups, rules и resources необходимо использовать. Поэтому написал все (что разумеется не правильно). Если вы имеете, что сказать по этому поводу, скажите это в комментах.

И в конце ClusterRoleBinding:

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: psp:fluent-bit-read
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: psp:fluent-bit-read
subjects:
- kind: ServiceAccount
  name: fluent-bit
  namespace: logging

И естественно все собираем вместе:

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: psp:fluent-bit-read
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: psp:fluent-bit-read
subjects:
- kind: ServiceAccount
  name: fluent-bit
  namespace: logging

В DaemonSet указываем созданный нами serviceAccountName:

serviceAccountName: fluent-bit

Самая деревянная служба поддержки

Я видел и общался с различными службами поддержки. И могу с уверенностью сказать, что служба поддержки mos.ru — самая идиотская. Я имею в виду не только качество ответов, а в первую очередь техническую сторону организации «общения» с клиентом.

В ответ на их ответ можно послать только одно письмо. На второе письмо, в котором находятся, например уточняющие данные, приходится открывать новый запрос. Переписка превращается в несвязанное общение с несколькими операторами службы поддержки. Которые и так не «горят» желанием решить твою проблему, так еще и не удосуживаются почитать предыдущие запросы.

Вобщем, категория дерево.

Как удалить report в kibana

Сформированные по вашему запросу или автоматически отчёты, как и многое другое, хранятся непосредственно в индексах elasticsearch.

Удалить конкретный report из индекса нельзя. Несмотря на запросы от пользователей, разработчики эластика отказываются предоставить такой инструмент и предлагают удалять индекс целиком.

Индексы с отчетами начинаются с .reporting и удаляются либо в kibana в разделе Index Management либо при помощи утилиты curator.

Пример action удаления индекса для curator:

actions:
  1:
    action: delete_indices
    options:
      ignore_empty_list: True
      disable_action: True
    filters:
    - filtertype: pattern
      kind: prefix
      value: .reporting-
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 30

Elasticsearch переполнение файловой системы

После переполнения файловой системы, elastic автоматически переводит индекс в режим read only.

После увеличения свободного пространства на диске отключить это режим для конкретного индекса можно при помощи следующей команды:

PUT app-index-2019.08.12/_settings
{
  "index": {
    "blocks": {
      "read_only_allow_delete": "false"
    }
  }
}

Если в режим только для чтения переведены и системные индексы, например .kibana. Тогда команда будет выглядеть так:

PUT _settings
{
  "index": {
    "blocks": {
      "read_only_allow_delete": "false"
    }
  }
}

Эти команды можно ввести в Kibana в разделе Dev Tools

Или в командной строке:

curl -XPUT -H "Content-Type: application/json" http://localhost:9200/_all/_settings -d '{"index.blocks.read_only_allow_delete": false}'

Fluent Bit — особенности использования multiline в плагине tail

Input plugin tail имеет возможность обрабатывать многострочные лог файлы. Достаточно создать PARSER, при помощи которого можно определить первую строку такого сообщения. Все вроде бы работает, но мне захотелось не просто разобрать сообщение по тегам, но и в отдельном теге сохранить всё исходное сообщение.

По идее регулярное выражение, используемое в парсере для лога openfire

2018.04.16 10:48:15 ERROR [pool-2-thread-171]: org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager - Error processing file transfer proxy connection
java.io.IOException: Only SOCKS5 supported
        at org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager.processConnection(ProxyConnectionManager.java:156) ~[xmppserver-4.3.2.jar:4.3.2]
        at org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager.access$200(ProxyConnectionManager.java:53) ~[xmppserver-4.3.2.jar:4.3.2]
        at org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager$1$1.run(ProxyConnectionManager.java:125) [xmppserver-4.3.2.jar:4.3.2]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_202]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_202]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_202]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_202]
        at java.lang.Thread.run(Thread.java:748) [?:1.8.0_202]

должно выглядеть как то так:

^(?<message>(?<time>\d{4}\.\d{2}\.\d{2} \d{2}:\d{2}:\d{2}) (?<loglevel>\w+) {1,2}(?<process>\[[^ ]*\]): .*)

Оказалось, что всё не так просто. Fluent bit находит первую строку, разбирает её, формирует теги. Вроде всё нормально.

Но остальные строки он тупо дописывает к последнему тегу регулярного выражения. И вместо того, что бы поместить их (как я ожидал) в тег message, он добавляет их к тегу process.

Поэтому, после обработки приходится «терять» теги time, loglevel и process используя следующее выражение вместо первого:

^(?<time>\d{4}\.\d{2}\.\d{2} \d{2}:\d{2}:\d{2}) (?<loglevel>\w+) {1,2}(?<process>\[[^ ]*\]): (?<message>.*)

Остальные строки многострочного лога будут добавляться к тегу message, но он не будет содержать время, loglevel и процесс.

Итого, первое регулярное выражение отлично работает с однострочными логами, но не будет работать с многострочными. Вот такая фича.

Не ожидал я такой засады от Java

Я, не то чтобы часто, но как минимум раз в полгода пишу всякие утилитки и приложения на java. В основном клиенты к BD на десктоп. В качестве основного инструмента пользуюсь JavaFX.

И вот, поменял себе железо, начал ставить софт, в том числе и IDE для разработки. Ну и заодно решил попробовать 11-ю (да и 12-ю) версию SDK. Открываю старый проект, дописать пару фишек. А он не работает. IDE подчеркивает красным все что связано с JavaFX.

Вобщем, пока я был в танке, они выпилили JavaFX из java! Не, ну С… (Стабильность).

Оно конечно есть проект Open JavaFX, продвигаемый достойной компанией. Но мне теперь придется кучу проектов переделывать, что бы все заработало! Ну не уроды эти Ораклы?

Так что, пока остаюсь на 8-й жабе. Но уже как то не уютно стало мне в этой среде.