Skip to main content

· 3 min read

Решали с коллегами задачу по генерации манифестов Provisioner в цикле, при таком создании есть один деструктивный момент - нужно выбрать Container Runtime Interface (CRI), иначе provisioner.spec.kubeletConfiguration.containerRuntime в манифесте Provisioner в паре с AWSNodeTemplate karpenter по дефолту выставят нам containerd (подтверждение в тесте)

Собрали ряд требований:

  • CRI не указан в провиженере, значит выбираем дефолтный для нас (в нашем случае dockerd)
  • CRI указан в провиженере, выбираем его
  • CRI не указан в провиженере, но в provisioner.spec.kubeletConfiguration есть другие параметры

Получился следующий код валидации:

helm

  # Kubelet
# If Provisioner.kubeletConfiguration is not empty
{{- if .kubeletConfiguration }}
kubeletConfiguration:
# If containerRuntime has been configured in Provisioner.kubeletConfiguration
{{- if hasKey .kubeletConfiguration "containerRuntime" -}}
{{- toYaml .kubeletConfiguration | nindent 4 }}
# ElseIf containerRuntime has not been configured in Provisioner.kubeletConfiguration
{{- else }}
# Pick default CRI from karpenter.default.kubeletConfiguration and add it to current .kubeletConfiguration
{{- $CRI := dict "containerRuntime" $.Values.karpenter.default.kubeletConfiguration.containerRuntime -}}
{{- $kubeletConfiguration := merge $CRI .kubeletConfiguration }}
{{- toYaml $kubeletConfiguration | nindent 4 }}
{{- end }}
# ElseIf Provisioner.kubeletConfiguration is empty
{{- else }}
kubeletConfiguration:
{{- toYaml $.Values.karpenter.default.kubeletConfiguration | nindent 4 }}
{{- end }}
Тесты

Test cases:

  • 1) CRI не указан в провиженере values.yml
karpenter:
payload:
ahorbach:
foo: bar

result:

spec:
# Kubelet
# If Provisioner.kubeletConfiguration is not empty
kubeletConfiguration:
containerRuntime: dockerd
  • 2) CRI указан в провиженере

values.yml

karpenter:
payload:
ahorbach:
kubeletConfiguration:
bar: baz
containerRuntime: rocket

result:

spec:
# Kubelet
# If Provisioner.kubeletConfiguration is not empty
kubeletConfiguration:
# If containerRuntime has been configured in Provisioner.kubeletConfiguration
bar: baz
containerRuntime: rocket
# ElseIf containerRuntime has not been configured in Provisioner.kubeletConfiguration
# ElseIf Provisioner.kubeletConfiguration is empty
  • 3) CRI не указан в провиженере, но есть конфиг

values.yml

  payload:
ahorbach:
kubeletConfiguration:
spam: eggs

result:

spec:
# Kubelet
# If Provisioner.kubeletConfiguration is not empty
kubeletConfiguration:
# If containerRuntime has been configured in Provisioner.kubeletConfiguration
# Pick default CRI from karpenter.default.kubeletConfiguration and add it to current .kubeletConfiguration
containerRuntime: dockerd
spam: eggs

python

from pydantic import BaseModel, validator

DEFAULT_CRI = {"containerRuntime": "dockerd"}

class karpenterPayloadProvisioner(BaseModel):
name: str
kubelet_configuration: dict = DEFAULT_CRI

@validator('kubelet_configuration')
def kubelet_container_runtime(cls, v):
cri = v.get("containerRuntime")
# option two - Mutating
if cri not in ["dockerd", "containerd"]:
v.update(DEFAULT_CRI)
return v
Тесты
import typing as t

import pytest

from karpenter import karpenterPayloadProvisioner

# without selected CRI in Provisioner
case1 = ("foo", {}, "dockerd")

# with containerd as a CRI for Provisioner

case2 = ("bar", {"kubeReserved": "testMe", "containerRuntime": "containerd"}, "containerd")

# with typo in CRI for Provisioner
case3 = ("baz", {"kubeReserved": "testMe", "containerRuntime": "qwerty"}, "dockerd")

test_cases = [
case1,
case2,
case3,
]

@pytest.mark.parametrize("name, kubelet_configuration, expected_CRI", test_cases)
def test_cri_provisioner(name: str, kubelet_configuration: t.Optional[dict], expected_CRI: str):

provisioner = karpenterPayloadProvisioner(name=name, kubelet_configuration=kubelet_configuration)
assert provisioner.kubelet_configuration["containerRuntime"] == expected_CRI

pytest .
=========================================================== test session starts ============================================================
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/a.horbach/repository-self/python-monorepo/pydantic-karpenter
plugins: django-4.4.0, cov-3.0.0
collected 3 items

test_karpenter.py ... [100%]

============================================================ 3 passed in 0.08s =============================================================

· One min read

Описание

https://github.com/GoogleCloudPlatform/terraformer

Terraformer - тулза преобразующая существующие облачные ресурсы в terraform код. Может быть полезна в различных кейсах, например: при обучении aws-у и terraform-у - создали ресурсы через console по курсу, а потом дампнули это в terraform код. Либо более классический кейс - описываете существующую инфраструктуру в IaC.

Использование

info

запустить получилось вот так, но это вряд ли best practices

Экспортируем креды доступа к aws-apo

export AWS_DEFAULT_PROFILE=my-profile  # ~/.aws/credentials
export AWS_ACCESS_KEY_ID=id
export AWS_SECRET_ACCESS_KEY=key

Так же нам нужен tf провайдер aws:

terraform {
required_version = ">= 1.3.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = "4.42.0"
}
}
}

Инициализируем провайдер, запускаем terraformer и натравливаем на нужный нам aws ресурс

terraform init # инициализируем провайдер в директорию ./.terraform
terraformer import aws --resources=route53 --regions=us-east-1 --profile my-profile
terraformer import aws --resources=route53 --regions=us-east-1 --profile my-profile --filter="Type=route53_zone;Name=tags.Name;Value=my.example.com"

· 7 min read

История, Архитектура

1) aws account mc-legacy

img

Имеется legacy приложение в aws аккаунте:

  1. В route53 зарегистрирован домен <my-company>.io и аналогичная hosted zone
  2. В зоне есть запись <my-service>.<my-company>.io , смотрит на <lb>.<my-company>.io
  3. Балансировщик ведет на ASG
  4. ASG управляет EC2 инсансами на которых работает приложение <my-service>

2) aws account mc-project

img

Принимается решение перевезти приложение в kubernetes, для удобства под каждую команду создается свой aws аккаунт и свой k8s кластер:

  1. В aws поднимается NLB балансировщик <lb-my-project>
  2. Через target groups он смотрит на EC2 инстансы, на которых запущен pod с ingress controller-ом.
  3. Попадая в pod ингресса, запрос через Ingress перенаправляется на pod-ы за <my-service> с помощью k8s service.

3) combine

img

r53 запись <my-service>.<my-company>.io начинает смотреть на CNAME <lb-my-project>, все работает, но SSL не терминируется.

Зона <my-company>.io слишком критична для бизнеса, и передавать в нее управление контроллерам типа external-dns, cert-manager не хочется.

В тоже время aws аккаунтов типа mc-project может быть много, как и кластеров k8s, вручную доставлять секрет с tls сертификатом не хочется.

4) Concept

img

  1. Создается специальный management aws аккаунт и k8s кластер.
  2. В него каким-то образом доставляется секрет с tls сертификатом *.<my-company>.io
  3. Специальный контроллер kubed синхронизирует секрет *.<my-company>.io в другие кластеры k8s с помощью аннотаций.
  4. Секрет *.<my-company>.io используется в Ingress, SSL траффик терминируется.

Конфигурации

info

В последующих конфигурационных файлах роли для mc-mgmt будут называться kubed-master, а mc-project-* - kubed-follower

AWS IAM RBAC

info

На схеме mc-mgmt = CI, mc-project - Target

img

Нужно сделать кросс-аккаунт aws iam rbac роли, для того, чтобы kubed из mc-mgmt мог управлять секретами в k8s кластерах других aws аккаунтов, в данном случае mc-project.

mc-mgmt aka kubed-master

locals {
kubed_followers_arns = [
for _, id in local.aws_accounts_map : "arn:aws:iam::${id}:role/kubed-follower"
]
}

module "iam_assumable_role_kubed_master" {
source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc"
version = "4.2.0"

create_role = true
role_name = "kubed-master"

provider_url = replace(local.cluster_oidc_issuer_url, "https://", "")

role_policy_arns = [
aws_iam_policy.kubed_master.arn,
aws_iam_policy.kubed_eks.arn,
]

oidc_fully_qualified_subjects = [
"system:serviceaccount:kube-system:kubed"
]
}

data "aws_iam_policy_document" "kubed_master" {
statement {
sid = "kubedMaster"
effect = "Allow"
actions = [
"sts:AssumeRole",
]
resources = local.kubed_followers_arns
}
}

resource "aws_iam_policy" "kubed_master" {
name = "kubed-master"
policy = data.aws_iam_policy_document.kubed_master.json
}

data "aws_iam_policy_document" "kubed_eks" {
statement {
sid = "kubedEks"
effect = "Allow"
actions = [
"eks:DescribeCluster",
"eks:ListClusters"
]
resources = ["*"]
}
}

resource "aws_iam_policy" "kubed_eks" {
name = "kubed-eks"
policy = data.aws_iam_policy_document.kubed_eks.json
}

mc-project aka kubed-follower

locals {
mc_mgmt_id = "<mc-mgmt_id>"
}

data "aws_iam_policy_document" "kubed_follower_assume" {
statement {
sid = "kubedFollowerAssume"
effect = "Allow"
principals {
identifiers = toset(["arn:aws:iam::${local.mc_mgmt_id}:role/kubed-master"])
type = "AWS"
}
actions = [
"sts:AssumeRole",
]
}
}

data "aws_iam_policy_document" "kubed_follower" {
statement {
sid = "kubedEks"
effect = "Allow"
actions = [
"eks:DescribeCluster",
"eks:ListClusters"
]
resources = ["*"]
}
}


resource "aws_iam_policy" "kubed_follower" {
name = "kubed-follower"
policy = data.aws_iam_policy_document.kubed_follower.json
}


resource "aws_iam_role" "kubed_follower" {
name = "kubed-follower"
description = "IAM role which allows kubed sync secrets from mc-mgmt to this account"
assume_role_policy = data.aws_iam_policy_document.kubed_follower_assume.json
managed_policy_arns = [aws_iam_policy.kubed_follower.arn]
}

ServiceAccount kubed

На ServiceAccount kubed-master нужно повесить аннотацию:

annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<mc-mgmt_id>:role/kubed-master

ConfgiMap aws-auth

apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- "groups":
- "system:masters"
"rolearn": "arn:aws:iam::<mc-mgmt_id>:role/kubed-master"
"username": ""
...

Docker image

Образ от разработчиков scratch, в нем есть только бинарник kubed, нам же нужны утилиты aws для генерирования kubeconfig.

FROM appscode/kubed:v0.13.2 as downloader

FROM alpine:3.17

COPY --from=downloader /kubed /usr/bin/kubed

RUN apk add --no-cache \
curl~=7.86 \
py3-pip~=22.3 \
&& pip install --no-cache-dir \
awscli~=1.27 \
&& curl -Lo aws-iam-authenticator https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/download/v0.5.9/aws-iam-authenticator_0.5.9_linux_amd64 \
&& chmod +x aws-iam-authenticator \
&& mv aws-iam-authenticator /usr/bin/

RUN addgroup --gid 1950 app \
&& addgroup --gid 1000 app \
&& adduser \
--uid 1000 \
--home /home/app \
--shell /bin/ash \
--ingroup app \
--disabled-password \
app \
&& addgroup app app

USER 1000
WORKDIR /home/app

ENTRYPOINT ["/usr/bin/kubed"]

K8s controller

В mc-mgmt кластере так же работает обычный интанс kubed-а, установленный helm чартом, из него мы и берем все необходимые k8s RBAC-и.

danger

Этот способ деструктивен, неверная конфигурация kubed-master может реверсировать синхронизацию секретов между namespace-ами. Рекомендация держать специальный кластер для kubed-master

---
apiVersion: v1
kind: ConfigMap
metadata:
name: kubed-master-aws
namespace: kube-system
data:
credentials: |-
[mc-mgmt]
role_arn = arn:aws:iam::<mc-mgmt_id>:role/kubed-master
web_identity_token_file = /var/run/secrets/eks.amazonaws.com/serviceaccount/token
[mc-project]
role_arn = arn:aws:iam::<mc-project_id>:role/kubed-follower
source_profile = mc-mgmt
role_session_name = mc-project
generate-kubeconfig.sh: |-
#!/bin/bash
set -eux

aws eks update-kubeconfig --name mc-project-k8s --alias mc-project-k8s --profile mc-project

# NOTE: mc-mgmt-k8s should the last due to current-context
aws eks update-kubeconfig --name mc-mgmt-k8s --alias mc-mgmt-k8s --profile mc-mgmt

Синхронизация конфигураций

---

apiVersion: v1
kind: ConfigMap
metadata:
name: cross-acc-sync-demo
namespace: prod
annotations:
kubed.appscode.com/sync-contexts: "mc-project-k8s"
data:
foo: bar
spam: eggs

Нюансы работы

info

конфигурация: это ConfigMap, Secret

  • Синхронизация возможно только из namespace-а указанного во флагах контроллера в namespace с таким же названием в follower кластерах. При это annotations и labels на конфигурациях в follower кластерах не сохраняется.
  • Поводом для синхронизации конфигураций является: перезапуск pod-а контроллера, обновление .data конфигурации. Т.е. если конфигурация будет удалена в follower кластере, она не синхронизуется автоматом.
  • Удаление конфигурации в master аккаунте, удалит конфигурацию во всех follower-ах.
  • В случае, если в follower кластере есть свой kubed для синхронизации между namespace-ами, то можно добавить annotation-ию вручную и конфигурация разбежится по namespace-ам. Причем обновление .data в master кластере синхронизирует секрет в follower, но не перетрет эту аннотацию.

Добавляем аннотацию в follower кластере

kubectl --context mc-project annotate cm cross-acc-sync-demo "kubed.appscode.com/sync=sync/kubed-master=true"

Добавляем label на другой namespace в follower кластере, чтобы местный kubed засинхронизировал cm между namespace-ами.

---

kind: Namespace
apiVersion: v1
metadata:
name: env-ahorbach
labels:
name: env-ahorbach
sync/kubed-master: "true"

Ссылки

· One min read

История

Не запускался Lens 6, быстро проблему решить не получилось, поэтому решил откатиться на предыдущие версии.

DEBUG=true /Applications/Lens.app/Contents/MacOS/Lens

Логи дебага выдавали ошибки, по которым в issues в репозитории на GitHub https://github.com/lensapp/lens не удалось найти полезной информации.

Как выяснилось - легкого доступа к предыдущим релизам нет, единственный из вариантов это собраться из сорцов.

Сборка

$ git clone https://github.com/lensapp/lens.git
$ git checkout v5.5.4 # последний из стабильных релизов 5.x.x
$ make build

Тут и начинаются грабли, в большей степени связанные с nodejs и несовместимостью версий самой ноды или пакетов.

Мне для корректировки версии помогла утилита nvm и эта статья

nvm ls-remote  # смотрим какие варианты нам может предложить менеджер ноды
nvm install lts/fermium # node 15.x
nvm use lts/fermium # пиним версию ноды в системе

В моем случае пина версии ноды хватило, чтобы успешно собрать .dmg пакет.

· One min read

Long Story Short

Уже пару месяцев использую Notion для заметок, но делал все относительно топорно. У меня было много страничек древовидной структуры и канбан доска с тасками на неделю. Правда недавно взглянул в темплейты от самого Notin-а и скопировал оттуда engineering page (ниже пример под спойлером).

info
мой канбан борд

img

Посмотрел видео Владлена, нашел много полезного (ниже будет pdf с конспектом)

Конспект:

Ссылка на файл

· 2 min read

iPad. Long Story Short

Относительно недавно заимел iPad + Pencil + Logitech Combo Touch для рисования рисунков, заметок и конспектов, легковесной замене ноута в случае инцидентов.

  • рисование -> кул с procreate
  • заметки / конспект -> нравиться с goodnotes
  • замена ноута -> в Logitech отвратный клик на тач и куча проблем самого iPad. Но используя проект code-server - я могу работать за iPad. Однако считаю, что поэтому пункту скорее неуд.

GoodNotes

Софтина как бы примитивная, но можно создавать вот такие конспекты from scratch

img

Либо загружать в эту софтину PDF-ы и редактировать уже их:

img

note

✏️ Мне сложно это объяснить, но есть что-то тактильно приятное в том, чтобы писать что-то от руки, при этом конспект не превратиться в мусор.

Хочется снова пойти в универ 📚

TechWorld with Nana about prometheus:

Go United

· 2 min read

На VagrantCloud не нашел нормальных box-ов с GUI.

Процесс

info

Буду делать аналогии с docker

config file

Описываем конфигурацию нашего VagrantBox (DockerImage) в формате .hcl (terraform)

source "vagrant" "ubuntu" {
add_force = true
communicator = "ssh"
provider = "virtualbox"
source_path = "ubuntu/focal64"
}

build {
name = "learn-packer"
sources = [
"source.vagrant.ubuntu"
]
}
tip

Полезные команды:

packer validate packer.hcl  # валидирует файл
packer fmt . # форматирует файлы, и кажется, выполняет валидацию

build

С помощью packer (docker engine) собираем образ.

packer build packer.hcl

Через некоторое время в директории learn-packer появится файл .box (docker_image)

push

Заливаем получившийся файл на VagrantCloud. Понадобиться собрать хэш-сумму файла, например через md5

provisioner

note

Любимый провиженер - ansible, но можно использовать shell

source "vagrant" "ubuntu" {
add_force = true
communicator = "ssh"
provider = "virtualbox"
source_path = "ubuntu/focal64"
}

build {
name = "learn-packer"
sources = [
"source.vagrant.ubuntu"
]
provisioner "ansible" {
playbook_file = "./ansible/packer-playbook.yml"
}
}

post-processors

caution

Не протестировал

Штуки которые выполняются после сборки, например публикация в VagrantCloud (docker push)

GitHub repo

· 2 min read

Буду краток, у вас мало времени: Certified Kubernetes Administrator (CKA) один из крутейших сертификатов.

Articles

Нашел пару хороших ресурсов, чтобы с чего начать:

  • devopscube about CKA - очень много хороших советов и кросс-ссылок, добавил сайт в закладки
  • medium - на контрасте с предыдущей статьей как-то блекло
  • udemy - для визуалов, учитель из 🇮🇳 , курс как всегда лучше брать за 999р, но можно найти и на пиратских ресурсах

About Exam

Можно использовать:

Говорят проходить the hard way не обязательно.

Themepercentagecomment
Cluster Architecture, Installation & Configuration25 %kubeadm, Container Runtime Interface (CRI)
Workloads & Scheduling15 %workloads (configure po,deploy,sts,job,cj,ds and etc.); nodes - (drain,cordon,nodeselector,affinity,taint)
Services & Networking20 %Container Network Interface (CNI) (networking, connectivity between pods - policy, CoreDNS, etc.)
Storage10 %Container Storage Interface (CSI) - sc,pvc,pv - extend pv and etc (?ceph)
RBACX %role based access
Troubleshooting30 %see spoiler
Toggle me!
  • What if a node is not ready?

  • What if a pod is frequently restarting, and you need to figure out why?

  • What if all CPU resource is used up and you need to find out which pod consumes the most and why?

  • How to monitor certain resources?

  • How to troubleshoot a failed component?

    info

    For example, if you want to monitor the CPU resource each pod uses or each node uses, do you know what keyword to search in the official documentation?

Cluster Architecture, Installation & Configuration

Завел у себя дома кластер, мастер с одним воркером бегут поверх ubuntu-desktop моего старого ноутбука (4vcpu, 8gb ram), использую vagrant+virtualbox, containerd в качестве рантайма (CRI).

Мысль в том, что в любой момент могу с относительно минимальными телодвижениями докинуть воркеров с других компьютеров в домашней сети.

Вот ссылка на код и схемка.

img

· 2 min read

Inspiration

Буквально недавно писал про сборку Vagrant-а, доделалъ 🎉, в двух версиях:

  • в серверной работаем через ssh
  • и gui, установлены разные desktop приложения 💻

asciicast

Или схематично:

sequenceDiagram participant Packer participant Vagrant participant Ansible Packer->>Vagrant: Launch tmp VM Note right of Vagrant: !NOTE: at low level Vagrant uses Virtualbox API Vagrant->>Ansible: Configure tmp VM Ansible-->>Vagrant: Done! Vagrant-->>Packer: Done! %% loop Artifact %% Ansible->>Packer: Create Vagrant box from current VM state. %% end note over Packer: Creates Vagrant box from current VM state. note over Packer: Releases the Vagrant box on Vagrant Cloud.

Установка на Windows

Проверьте ресурсы вашей host OS:

CPU

WMIC CPU Get DeviceID,NumberOfCores,NumberOfLogicalProcessors

Версию OS и общий объем RAM:

systeminfo |findstr /c:"OS Name" /c:"Total Physical Memory"

img

"Рекомедованные ресурсы"
  • 2 vpcu
  • 4Gi RAM

Вам потребуется:

VirtualBox Guest Additions

6.4. Installing the VirtualBox Guest Additions

info

Это, пожалуй, самое больное в использовании virtualbox - открыть гую на весь экран. Тут я не буду вам давать никаких обещаний, могу лишь накинуть идеи как вам с этим справиться:

vbguest vagrant plugin

danger

У меня сработало лишь один раз 💀

vagrant plugin uninstall vagrant-vbguest
vagrant destroy -f
vagrant up
vagrant plugin install vagrant-vbguest
vagrant vbguest --do install

ansible galaxy

Используйте мой пример - Guest Additions установит ansible роль.

caution

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

Секретный вариант

Всегда можно погуглить / посмотреть ютуб на тему как сделать Virtualbox на весь экран 😉

Удачи и да прибудет с вами сила!

tip

Если не смотрели, исправьтесь:

Toggle me!
🦖 🦕

· 2 min read

Предыстория

Когда я только знакомился с AWS по youtube плейлисту, я смастерил VPC в двух AZ с несколькими подсетями. Один из типов подсетей был private - с nat-gateway, в курсах забыли сказать, что они не входят во фритир. За пару дней за 2 гейта накапало около $6. Было обидно, я поставил биллинг аларм.

#!NOTE

Тот самый курс от Курс ADV-IT

История

Совсем недавно я учился (на котиках) созданию артефактов с помощью packer. После тренировки в AWS я со спокойной душой в console нужного региона убил все AMI и пошел дальше.

Неделю спустя в почте заметил очередное письмо от AWS (мне иногда на личный gmail аккаунт приходят от них billing репорты, всегда пустые, и приглашения не re:invent).

alert

Увидел что я потратил 85% "фри-тирного" места под снэпшоты ebs дисков.

info

AWS Free Tier - тут можно прочитать сколько вам в месяц выделяется того или иного ресурса бесплатно в рамках фри-тира.

Ага - из-за негодяя packer-а помимо AMI-шек создаются еще и такие сущности.

snapshots

Мораль
  • пермым делом во фри-тире сделайте не рутового пользователя, вторым настройте биллинг алерты (я бы поставил трешхолд на 50% - в моем случае бюджет таял быстро).
  • Авторам видео / гайдлайнов, конечно, стоило бы указывать, что это не входит во фри-тир - или входит, но слабые лимиты.