Деплой контейнера в кубернетес: Pod — ReplicaSet — Deployment
Как использовать Service (ClusterIP, NodePort)
Что бы понять всю прелесть автоматизации, сначала делаем все руками.
Деплой контейнера в кубернетес: Pod — ReplicaSet — Deployment
Как использовать Service (ClusterIP, NodePort)
Что бы понять всю прелесть автоматизации, сначала делаем все руками.
Dashboard в kubernetes. Изначально планировал показать подключение дашборда в рамках предыдущего видео. И обычно, для доступа к нему достаточно двух команд в командной строке, но не в случае kubespray 2.12.
Установка последнего дашборда, права доступа и прочие мелочи.
Добавление, удаление ноды в кубернетес.
Добавляем при помощи kubespray. Удаляем руками.
В начале видео было заявлено включение дашборда. Но «в жизни все не так как на самом деле». Пришлось разбираться с глючным дашбордом, ставить новый и править доступы. Все это заняло достаточный кусок времени. Пришлось выносить в отдельное видео.
Вспомнил давно забытые преподские дела 🙂 да.
Записал начало видео курса по Кубернетес.
А что? Самоизоляция, она кого угодно достанет…
В дружественной компании открыты вакансии Сисадминов Linux. Компания надежная. Работа удаленная, т.е. кандидат может проживать в любом регионе России. Работы много, кризис на работу не влияет, даже увеличил её (работы) количество.
Обязанности:
Требования:
Условия:
Обязанности:
Требования:
Условия:
Так же есть вакансии:
Ссылки на резюме (желательно на hh.ru) присылайте на мыло arturkryukov@krlb.ru
В один прекрасный день, elastic может сойти с ума. Перестать принимать информацию в индекс и начать кидать в логи ошибки типа:
failed to parse field [time] of type [date]
Проблема в автоматическом распознавании содержимого полей. Особенно это касается поля, в котором содержится дата и время. Тот самый прекрасный день настал, когда наши программеры немножечко изменили формат даты. И этот формат не стал совпадать с форматом, который эластик использует по умолчанию.
Какой формат используется можно увидеть в mapping интересующего вас индекса:
GET /index-name/_mapping
Следующая засада, вы не можете на лету поменять тип поля в индексе. Только путем пересоздания индекса с нуля.
И самое противное. Если вы используете группу индексов в IndexPattern, сбойный индекс вылетит из поиска по проблемному полю.
Вобщем проблем с такими «прыгающими» типами полей будет чертовски много. Поэтому, рекомендую «гвоздями» прибивать тип поля в индексе.
Например, в индексе есть поле, содержащее дату. В Elastic такие поля могут попасть под шаблон (mapping) по умолчанию для даты и в индексе появится поле типа date. Потом программеры поменяют формат даты и поле перестает подходить под шаблон. Эластик будет считать что это поле типа text и тупо перестает добавлять информацию в индекс.
Если вы не планируете в дальнейшем использовать поле для поиска, самое простое решение привести его к типу text. В дальнейшем, смена формата даты никоим образом не повлияет на работу индекса.
Создание шаблона для индекса или группы индексов.
Посмотреть все шаблоны можно так:
GET _template
Посмотреть конкретный шаблон:
GET _template/template_name
Создать шаблон:
PUT _template/template_name { "order" : 0, "index_patterns" : [ "index-name-*" ], "settings" : { "index" : { "number_of_shards" : "1" } }, "mappings" : { "properties": { "time": { "type" : "text", "fields" : { "keyword" : { "ignore_above" : 256, "type" : "keyword" } } } } }, "aliases" : { } }
Параметр index-patterns определяет шаблон именён индексов к которым будет применяться данный template. Т.е. если будет создан индекс с , например, именем index-name-2020.04.01, то шаблон будет к нему применен. И в дальнейшем в списке mappings вы увидите, что поле time будет иметь тип text.
Так же в этом шаблоне ограничивается количество шардов единицей. По умолчанию у вновь создаваемого шаблона значение этого параметра равно 3. Такое ограничение имеет смысл для небольших индексов. Максимальное количество шардов на сервер ограничено и при большом количестве индексов имеет смысл на один индекс давать один шард.
Собственно коронавирус, пользователи стаями потянулись на удалёнку. Пришла пора VPN серверов.
Достаточно стандартное решение OpenVPN + OpenLDAP позволяет управлять доступом пользователей к vpn серверу. Конфигурируется незамысловато, в интернет есть куча готовых советов как это делать. У меня в старой wiki была статейка как приготовить OpenVPN. Предупреждаю, статейка старинная, но еще рабочая.
Но в процессе настройки нашел забавную особенность:
Если в OpenLDAP для хеширования пароля используется не MD5, то плагин openvpn-auth-ldap будет давать ошибку.
А в случае MD5 — все работает как часы.
Попытался было заглянуть в исходники модуля. И понял, что современный С (или что они там использовали) далеко ушел от классического Керниган&Ритчи 🙂 Поэтому вот, пришлось возвращаться к MD5.
Если кто разбирается в современном С и норм владеет английским, скажите авторам, что: «Хьюстон, у нас проблемы».
З.Ы. Странно как то. Но оно заработало с разными типами хешей. И ведь ничего не трогал.
Создадим 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
Всё, что тут написано — это художественная обработка материалов доброго человека.
В принципе — это шпаргалка, что бы не забыть как все на самом деле происходит 🙂
Создаём временную директорию:
# 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 пользователя.
Шпаргалка сделана по мотивам записок доброго человека.
Жили мы жили. И вроде не плохо. Но на тесте решили поставить 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