aasdhajkshd microservices repository
INFO Информация на картинках, как IP адреса, порты или время, может отличаться от приводимой в тексте.
- docker-2 Технология контейнеризации. Введение в Docker
- docker-3 Docker-образы Микросервисы
- docker-4 Docker сети, docker-compose
- gitlab-ci-1 Устройство Gitlab CI. Построение процесса непрерывной поставки
- monitoring-1 Введение в мониторинг. Системы мониторинга
- kubernetes-1 Введение в kubernetes
- kubernetes-3 Kubernetes. Networks, Storages
- kubernetes-4 CI/CD в Kubernetes
- kubernetes-2 Kubernetes. Запуск кластера и приложения. Модель безопасности
- logging-1 Логирование и распределенная трассировка
Подготовка окружения docker-хост с именем logging в Yandex.Cloud и настройка локального окружение на работу с ним
YC_HOSTNAME="docker-host"
yc compute instance create \
--name ${YC_HOSTNAME} \
--zone ru-central1-a \
--core-fraction 50 \
--cores 4 \
--memory 4GB \
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 \
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-2004-lts,size=40,type=network-ssd \
--ssh-key ~/.ssh/id_rsa-appuser.pub \
| awk '/nat:/ { getline; print $2}'
YC_HOST_IP=$(yc compute instance list --format json | jq ".[] | select (.name == \"${YC_HOSTNAME}\") | .network_interfaces[0].primary_v4_address.one_to_one_nat.address" | tr -d '"')
cat << EOF > docker/ansible/environment/stage/inventory.json
{
"all": {
"hosts": {
"docker-host" : {
"ansible_host": "$YC_HOST_IP"
}
}
}
}
EOF
# docker-machine rm -f ${YC_HOSTNAME}
docker-machine create \
--driver generic \
--generic-ip-address=$YC_HOST_IP \
--generic-ssh-user yc-user \
--generic-ssh-key ~/.ssh/id_rsa-appuser \
${YC_HOSTNAME}
docker-machine ls
eval $(docker-machine env ${YC_HOSTNAME})
docker-machine ssh ${YC_HOSTNAME}
sudo add-apt-repository ppa:longsleep/golang-backports
sudo apt update
sudo apt install golang-go
go mod init github.com/yandex-cloud/docker-machine-driver-yandex
go install github.com/yandex-cloud/docker-machine-driver-yandex@latest
curl -L https://github.com/docker/machine/releases/download/v0.8.2/docker-machine-`uname -s`-`uname -m` >/usr/local/bin/docker-machine && \
chmod +x /usr/local/bin/docker-machine
export PATH="$PATH:$HOME/go/bin"
Создадим сервисную учетную запись
export YC_FOLDER_ID='b1g0da3u1gqk0nansi59'
export SA_KEY_PATH="$HOME/key.json"
yc iam service-account create --name=sa-default --folder-id=$YC_FOLDER_ID
yc iam key create --service-account-name sa-default --output $SA_KEY_PATH --folder-id $YC_FOLDER_ID
yc resource-manager folder add-access-binding \
--name=infra \
--id=$YC_FOLDER_ID \
--service-account-id=$(yc iam service-account get sa-default | grep ^id | awk '{print $2}') \
--role=editor
В папке подготовил docker/ansbile ansible playbook для установки docker-machine ansible-playbook playbooks/docker-machine_install.yml
и установку go Yandex.Cloud Docker machine driver
docker-machine -D create \
--driver yandex \
--yandex-image-family "ubuntu-2004-lts" \
--yandex-platform-id "standard-v1" \
--yandex-folder-id $YC_FOLDER_ID \
--yandex-sa-key-file $SA_KEY_PATH \
--yandex-memory "4" \
--yandex-nat=true \
logging
eval $(docker-machine env logging)
docker-machine ip logging
Установка завершается ошибкой, разбираться нет смысла, так как docker-machine не является актуальным и более не поддерживается Deprecate Docker Machine.
Результат:
Error creating machine: Error running provisioning: Something went wrong running an SSH command!
command : DEBIAN_FRONTEND=noninteractive sudo -E apt-get install -y curl
err : exit status 100
output : E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarily unavailable)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
...
Error running SSH command: Something went wrong running an SSH command!
command : netstat -tln
err : exit status 127
output : bash: netstat: command not found
Error creating machine: Error running provisioning: Unable to verify the Docker daemon is listening: Maximum number of retries (10) exceeded
Дальнейшее разворачивание было выполнено через terraform
и ansible
. Файлы установки располагаются в папке logging/infra/terraform
и logging/infra/ansible
. Terraform terraform -chdir=logging/infra/terraform/stage apply
создает машины с именами docker-host-X и docker-logging-X, запускает logging/infra/ansible/playbooks/docker_logging.yml
, где выполняется установка docker-compose и запуск контейнеров. Причём адрес fluent сервера указывается изначально при установке из динамического inventory.json
файла.
Dockerfile для fluentd в итоге с различными версиями не взлетел с плагином fluent-plugin-elasticsearch
mkdir -p logging/fluentd
cat << EOF > logging/fluentd/Dockerfile
FROM fluent/fluentd:v0.12
RUN gem install faraday-net_http -v 2.1.0
RUN gem install faraday -v 1.10.3
RUN gem install fluent-plugin-elasticsearch --no-rdoc --no-ri --version 1.9.5
RUN gem install fluent-plugin-grok-parser --no-rdoc --no-ri --version 1.0.0
COPY --chmod=644 fluent.conf /fluentd/etc
EOF
Результат:
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: temporarily failed to flush the buffer. next_retry=2023-11-17 21:07:32 +0000 error_class="NameError" error="uninitialized constant Elasticsearch::Transport" plugin_id="object:2abd066abb50"
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluent-plugin-elasticsearch-1.9.5/lib/fluent/plugin/out_elasticsearch.rb:147:in `client'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluent-plugin-elasticsearch-1.9.5/lib/fluent/plugin/out_elasticsearch.rb:359:in `rescue in send_bulk'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluent-plugin-elasticsearch-1.9.5/lib/fluent/plugin/out_elasticsearch.rb:354:in `send_bulk'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluent-plugin-elasticsearch-1.9.5/lib/fluent/plugin/out_elasticsearch.rb:341:in `write_objects'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluentd-0.12.43/lib/fluent/output.rb:490:in `write'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluentd-0.12.43/lib/fluent/buffer.rb:354:in `write_chunk'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluentd-0.12.43/lib/fluent/buffer.rb:333:in `pop'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluentd-0.12.43/lib/fluent/output.rb:342:in `try_flush'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 [warn]: /usr/lib/ruby/gems/2.5.0/gems/fluentd-0.12.43/lib/fluent/output.rb:149:in `run'
logging-fluentd-1 | 2023-11-17 21:07:32 +0000 fluent.warn: {"next_retry":"2023-11-17 21:07:32 +0000","error_class":"NameError","error":"uninitialized constant Elasticsearch::Transport","plugin_id":"object:2abd066abb50","message":"temporarily failed to flush the buffer. next_retry=2023-11-17 21:07:32 +0000 error_class=\"NameError\" error=\"uninitialized constant Elasticsearch::Transport\" plugin_id=\"object:2abd066abb50\""}
Принято решение отказаться от fluentd и использовать fluent-bit Подсмотрев установку в keubernetes, использовал эти же версии Kubernetes Observability: логгинг с EFK из блога Блог компании OTUS, Kubernetes
В итоге:
mkdir -p logging/fluent-bit
cat << EOF > logging/fluent-bit/fluent-bit.conf
[INPUT]
name elasticsearch
listen 0.0.0.0
port 9200
[OUTPUT]
name stdout
match *
[OUTPUT]
Name es
Match service.*
Host elasticsearch
Logstash_Format On
Logstash_Prefix fluentd
Logstash_Dateformat %Y%m%d
Include_Tag_Key true
Retry_Limit False
tls Off
tls.verify Off
HTTP_User elastic
HTTP_Passwd elastic
Suppress_Type_Name On
Index fluentbit
Type docker
[INPUT]
Name forward
Listen 0.0.0.0
Port 24224
Buffer_Chunk_Size 1M
Buffer_Max_Size 6M
[SERVICE]
Flush 5
Daemon Off
Log_Level debug
EOF
cat << EOF > logging/fluent-bit/Dockerfile
FROM cr.fluentbit.io/fluent/fluent-bit
COPY --chmod=644 fluent-bit.conf /fluent-bit/etc/fluent-bit.conf
EOF
docker buildx build --push -t ${USERNAME}/fluent-bit:logging .
cat << EOF > docker/docker-compose-logging.yml
version: '3'
services:
fluent-bit:
image: ${USERNAME}/fluent-bit:${FLUENTD_VERSION}
ports:
- "24224:24224"
- "24224:24224/udp"
depends_on:
- elasticsearch
elasticsearch:
image: elasticsearch:7.17.3
environment:
- discovery.type=single-node
expose:
- "9200"
ports:
- "9200:9200"
kibana:
image: kibana:7.17.3
ports:
- "5601:5601"
EOF
Выполнена пересборка образов ui, comment, post c tag'ом - logging и доустановленными пакетами gcc и musl-dev
for i in post ui comment; do cd $i && docker buildx build --push -t $USERNAME/$i:logging . && cd - ; done
Результат см.
logging/README.md
:
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
docker_host_instance = [
[
"fhma1onfpi80lu9p769r",
],
[
"docker-host-0",
],
]
docker_host_ip_address = "158.160.125.139"
docker_image_id = "fd853sqaosrb2anl1uve"
docker_logging_instance = [
[
"fhmm27372bjc4i5elva1",
],
[
"docker-logging-0",
],
]
docker_logging_ip_address = "158.160.106.143"
ubuntu@fhma1onfpi80lu9p769r:~/docker$ docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
logging-comment-1 23f03013e37f/comment:logging "puma" comment 7 minutes ago Up 7 minutes
logging-post-1 23f03013e37f/post:logging "python3 post_app.py" post 7 minutes ago Up 7 minutes
logging-post_db-1 mongo:4.4.24 "docker-entrypoint.s…" post_db 7 minutes ago Up 7 minutes 27017/tcp
logging-ui-1 23f03013e37f/ui:latest "puma --debug -w 2" ui 7 minutes ago Up 7 minutes 0.0.0.0:9292->9292/tcp, :::9292->9292/tcp
ubuntu@fhmm27372bjc4i5elva1:~/docker$ docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
logging-elasticsearch-1 elasticsearch:7.17.3 "/bin/tini -- /usr/l…" elasticsearch 9 minutes ago Up 9 minutes 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp
logging-fluentd-1 23f03013e37f/fluent-bit:logging "/fluent-bit/bin/flu…" fluentd 9 minutes ago Up 9 minutes 2020/tcp, 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp, :::24224->24224/tcp, :::24224->24224/udp
logging-kibana-1 kibana:7.17.3 "/bin/tini -- /usr/l…" kibana 9 minutes ago Up 9 minutes 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp
mkdir -p logging/fluent-bit
cat << EOF >> logging/fluent-bit/fluent-bit.conf
[SERVICE]
flush 1
log_level info
parsers_file parsers.conf
[FILTER]
Name parser
Match service.post
Key_Name log
Parser json_parser
[FILTER]
Name parser
Match service.ui
Key_Name log
Parser ui_parser
EOF
cat << EOF >> logging/fluent-bit/parsers.conf
[PARSER]
Name json_parser
Format json
Key_Name log
[PARSER]
Name ui_parser
Format regex
Regex /\[(?<time>[^\]]*)\] (?<level>\S+) (?<user>\S+)[\W]*service=(?<service>\S+)[\W]*event=(?<event>\S+)[\W]*(?:path=(?<path>\S+)[\W]*)?request_id=(?<request_id>\S+)[\W]*(?:remote_addr=(?<remote_addr>\S+)[\W]*)?(?:method= (?<method>\S+)[\W]*)?(?:response_status=(?<response_status>\S+)[\W]*)?(?:message='(?<message>[^\']*)[\W]*)?/
Key_Name log
EOF
cat << EOF >> logging/fluent-bit/Dockerfile
COPY --chmod=644 parsers.conf /fluent-bit/etc/parsers.conf
EOF
Рузультат:
Grok не поддерживается в fluent-bit - Is there any grok support?
logging-fluentd-1 | [2023/11/18 08:52:46] [ info] ___________
logging-fluentd-1 | [2023/11/18 08:52:46] [ info] filters:
logging-fluentd-1 | [2023/11/18 08:52:46] [ info] parser.0
logging-fluentd-1 | [2023/11/18 08:52:46] [ info] parser.1
logging-fluentd-1 | [2023/11/18 08:52:46] [ info] ___________
Загружено приложение, собрано с tag'ом bugged. Для тестирования подготовлен docker/docker-commpse-apps.yml
.
Выполнен запуск и проведен анализ запросов.
Если взять значения по умолчанию, то задржка из-за неверного порта по-умолчанию: 4567
Как выясняется, проблема подключении на порт сервиса post отдается ошибка 500, если в UI указывается ENV на порт 5000 для POST.
Список литературы и статей:
- Менеджер пакетов для Kubernetes
- Установка minikube
- yq: Command-line YAML/XML/TOML processor - jq wrapper for YAML, XML, TOML documents
- Creating sample user
- Установка выполнена
sudo pacman -S minikube
Результат:
minikube version: v1.31.2
commit: fd7ecd9c4599bef9f04c0986c4a0187f98a4396e-dirty
Так как уже есть существующий кластер на актуальной версии v1.27.4, предлагается поднять еще один кластер командой minikube start -p minikube2 --kubernetes-version=v1.19.7
.
Результат:
😄 [minikube2] minikube v1.31.2 on Arch "rolling"
🎉 minikube 1.32.0 is available! Download it: https://github.com/kubernetes/minikube/releases/tag/v1.32.0
💡 To disable this notice, run: 'minikube config set WantUpdateNotification false'
✨ Automatically selected the docker driver. Other choices: virtualbox, none, ssh
📌 Using Docker driver with root privileges
👍 Starting control plane node minikube2 in cluster minikube2
🚜 Pulling base image ...
💾 Downloading Kubernetes v1.19.7 preload ...
> preloaded-images-k8s-v18-v1...: 379.21 MiB / 379.21 MiB 100.00% 21.39 M
🔥 Creating docker container (CPUs=2, Memory=4096MB) ...
🐳 Preparing Kubernetes v1.19.7 on Docker 24.0.4 ...
❌ Unable to load cached images: loading cached images: stat /home/elnone/.minikube/cache/images/amd64/registry.k8s.io/etcd_3.4.13-0: no such file or directory
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🔎 Verifying Kubernetes components...
🌟 Enabled addons: storage-provisioner, default-storageclass
❗ /usr/bin/kubectl is version 1.28.2, which may have incompatibilities with Kubernetes 1.19.7.
▪ Want kubectl v1.19.7? Try 'minikube kubectl -- get pods -A'
🏄 Done! kubectl is now configured to use "minikube2" cluster and "default" namespace by default
Выполним команду kubectl get nodes
Результат:
NAME STATUS ROLES AGE VERSION
minikube2 Ready master 2m17s v1.19.7
Информацию о контекстах kubectl в файле ~/.kube/config cat ~/.kube/config | head -n 12
Результат:
apiVersion: v1
clusters:
- cluster:
certificate-authority: /home/elnone/.minikube/ca.crt
extensions:
- extension:
last-update: Wed, 15 Nov 2023 01:04:25 MSK
provider: minikube.sigs.k8s.io
version: v1.31.2
name: cluster_info
server: https://192.168.58.2:8443
name: minikube2
kubectl apply -f ui-deployment.yml
kubectl get deployment
Результат:
NAME READY UP-TO-DATE AVAILABLE AGE
ui 3/3 3 3 61s
kubectl get pods --selector component=ui
Результат:
NAME READY STATUS RESTARTS AGE
ui-cf9f76bc-2pblk 1/1 Running 0 2m14s
ui-cf9f76bc-78dgh 1/1 Running 0 2m14s
ui-cf9f76bc-vcxk2 1/1 Running 0 2m14s
kubectl port-forward ui-cf9f76bc-78dgh 8080:9292
kubectl describe service comment | grep Endpoints
Результат:
Endpoints: 10.244.0.248:9292
kubectl exec -ti comment-5678bb9fd5-lnjjh nslookup comment
Результат:
nslookup: can't resolve '(null)': Name does not resolve
Name: comment
Address 1: 10.111.209.21 comment.default.svc.cluster.local
kubectl apply -f post-service.yml -f ui-service.yml
kubectl port-forward ui-cf9f76bc-2pblk 9292:9292
Результат:
kubectl get svc -n default
Результат:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
comment NodePort 10.111.209.21 <none> 9292:30634/TCP 10m
comment-db ExternalName <none> mongo.dev.svc.cluster.local <none> 10m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11d
mongo NodePort 10.101.112.128 <none> 27017:32434/TCP 10m
post ClusterIP 10.101.181.98 <none> 5000/TCP 10m
post-db ExternalName <none> mongo.dev.svc.cluster.local <none> 10m
ui NodePort 10.103.216.210 <none> 80:30294/TCP 3s
minikube service list
Результат:
|----------------------|---------------------------|--------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|----------------------|---------------------------|--------------|---------------------------|
| default | comment | 9292 | http://192.168.49.2:30634 |
| default | comment-db | No node port | |
| default | kubernetes | No node port | |
| default | mongo | 27017 | http://192.168.49.2:32434 |
| default | post | No node port | |
| default | post-db | No node port | |
| default | ui | 80 | http://192.168.49.2:30294 |
| kube-system | kube-dns | No node port | |
| kube-system | metrics-server | No node port | |
| kubernetes-dashboard | dashboard-metrics-scraper | No node port | |
| kubernetes-dashboard | kubernetes-dashboard | No node port | |
|----------------------|---------------------------|--------------|---------------------------|
minikube addons list
Результат:
|-----------------------------|----------|--------------|--------------------------------|
| ADDON NAME | PROFILE | STATUS | MAINTAINER |
|-----------------------------|----------|--------------|--------------------------------|
| ambassador | minikube | disabled | 3rd party (Ambassador) |
| auto-pause | minikube | disabled | minikube |
| cloud-spanner | minikube | disabled | Google |
| csi-hostpath-driver | minikube | disabled | Kubernetes |
| dashboard | minikube | enabled | Kubernetes |
minikube dashboard
Результат:
🤔 Verifying dashboard health ...
🚀 Launching proxy ...
🤔 Verifying proxy health ...
🎉 Opening http://127.0.0.1:43019/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
kubectl get all -n kubernetes-dashboard --selector k8s-app=kubernetes-dashboard
Результат:
NAME READY STATUS RESTARTS AGE
pod/kubernetes-dashboard-5c5cfc8747-5m2m8 1/1 Running 9 (38m ago) 11d
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes-dashboard ClusterIP 10.100.181.221 <none> 80/TCP 11d
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/kubernetes-dashboard 1/1 1 1 11d
NAME DESIRED CURRENT READY AGE
replicaset.apps/kubernetes-dashboard-5c5cfc8747 1 1 1 11d
kubectl apply -n dev -f ui-deployment.yml
Результат:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ui-57f876ddf5-4cxgb 0/1 Pending 0 111s <none> <none> <none> <none>
ui-57f876ddf5-k5bd8 1/1 Running 0 111s 10.244.0.250 minikube <none> <none>
ui-57f876ddf5-z2mlm 1/1 Running 0 111s 10.244.0.249 minikube <none> <none>
Для разворачивания Managed Kubernetes воспользуемся шагами из домашнего задания kubernetes-3 Kubernetes. Networks, Storages
-
Разворачивание Managed Service for Kubernetes было выполнено в рамках проектной работы. Настройка terraform манифестов выполнялась по статье Развертывание и управление организацией и правами доступа через IaC terraform и Создайте кластер Managed Service for Kubernetes
-
dashboard для kubernetes доступен скачивания на основном сайте Kubernetes Dashboard
в папке kubernetes/infra/dashboard манифесты для создания и скрипт запуска или остановки dashboard и Deploying the Dashboard UI, актуальный файл kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
Самой простой способ - это установка из helm'ом Installing the Chart
helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --create-namespace --namespace kubernetes-dashboard
Результат:
Release "kubernetes-dashboard" does not exist. Installing it now.
NAME: kubernetes-dashboard
LAST DEPLOYED: Wed Nov 15 02:54:51 2023
NAMESPACE: kubernetes-dashboard
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
*********************************************************************************
*** PLEASE BE PATIENT: kubernetes-dashboard may take a few minutes to install ***
*********************************************************************************
Get the Kubernetes Dashboard URL by running:
export POD_NAME=$(kubectl get pods -n kubernetes-dashboard -l "app.kubernetes.io/name=kubernetes-dashboard,app.kubernetes.io/instance=kubernetes-dashboard" -o jsonpath="{.items[0].metadata.name}")
echo https://127.0.0.1:8443/
kubectl -n kubernetes-dashboard port-forward $POD_NAME 8443:8443
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin
namespace: kubernetes-dashboard
EOF
export POD_NAME=$(kubectl get pods -n kubernetes-dashboard -l "app.kubernetes.io/name=kubernetes-dashboard,app.kubernetes.io/instance=kubernetes-dashboard" -o jsonpath="{.items[0].metadata.name}")
echo https://127.0.0.1:8443/
kubectl -n kubernetes-dashboard port-forward $POD_NAME 8443:8443
kubectl create token admin -n kubernetes-dashboard
Список литературы и статей:
- Менеджер пакетов для Kubernetes
- Setup a Kubernetes Cluster
- Helm 2 vs Helm 3
- helm/helm
- The Chart Template
- Charts
- Метки и селекторы
- Аннотации
- Ingress.yaml template is throwing nil pointer evaluating interface {}.enabled
- Основы работы с Helm чартами и темплейтами — Часть 2
- Удаление helm 2
- K9s
- Docker container build driver
- docker buildx build
- overview of Linux capabilities
- Установка GitLab Runner
- Running privileged containers for the runners
- skopeo
- docker buildx imagetools create
- What kubernetes permissions does GitLab runner kubernetes executor need?
- Use tags to control which jobs a runner can run
- Непрерывное развертывание контейнеризованных приложений с помощью GitLab
- Установка GitLab Agent
- Environments and deployments
- Установка
Выполнена установка helm пакета
helm version
Результат:
version.BuildInfo{Version:"v3.12.0", GitCommit:"c9f554d75773799f72ceef38c51210f1842a1dea", GitTreeState:"clean", GoVersion:"go1.20.4"}
yc managed-kubernetes cluster start kube-infra
yc managed-kubernetes cluster get-credentials --name=kube-infra --force --external
kubectl config get-contexts
kubectl config use-context yc-kube-infra
WARNING Ниже нет необходимости выполнять установку tiller, так как helm 3 is tiller less. Tiller окончательно и безвозвратно удален. Helm 3 использует Kubernetes API напрямую. Но если версия 2-я, то можно и доустановить, ссылка выше в списке статей. Ниже из методички.
kubectl apply -f - << EOF > tiller.yml
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
EOF
helm init --service-account tiller
kubectl get pods -n kube-system --selector app=helm
INFO Пространство имен (namespace) в Kubernetes должно существовать до того, как приступить что-то деплоить helm'ом.
- Charts
mkdir -p kubernetes/Charts/{comment,post,reddit,ui}/templates
cat << EOF > kubernetes/Charts/ui/Chart.yaml
name: ui
version: 1.0.0
description: OTUS reddit application UI
maintainers:
- name: OTUS
email: [email protected]
appVersion: 1.0
EOF
- Templates
Подготовлена необходимая шаблонная структура Charts
Файлы deployment.yaml, service.yaml фактически не различаются и клонировать становится их проще. Далее в CI/CD нужно удалить namespace из yaml файлов. Здесь используются как переменные для изучения.
helm lint ui/ comment/ ui/
Результат:
==> Linting ui/
==> Linting comment/
==> Linting ui/
3 chart(s) linted, 0 chart(s) failed
- Управление зависимостями
При выполнении команды:
helm dep update reddit --debug
Результат:
Repository from local path: file://../ui
Repository from local path: file://../post
Repository from local path: file://../comment
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "ingress-nginx" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 3 charts
Archiving ui from repo file://../ui
Archiving post from repo file://../post
Archiving comment from repo file://../comment
Deleting outdated charts
tree reddit; \
cat reddit/requirements.lock
Появится файл requirements.lock с фиксацией зависимостей:
reddit/
├── charts
│ ├── comment-1.0.0.tgz
│ ├── post-1.0.0.tgz
│ └── ui-1.0.0.tgz
├── Chart.yaml
├── requirements.lock
├── requirements.yaml
└── values.yaml
2 directories, 7 files
dependencies:
- name: ui
repository: file://../ui
version: 1.0.0
- name: post
repository: file://../post
version: 1.0.0
- name: comment
repository: file://../comment
version: 1.0.0
digest: sha256:c509eeb70b9d2c7cb6aa1ec88f12d6ce386a0abda19490e59ff71dd893e45553
generated: "2023-10-25T11:41:23.171523839+03:00"
Поиск репозитория выполняется в ArtifactHub
helm search hub mongo
https://artifacthub.io/packages/helm/bitnami/mongodb или https://artifacthub.io/packages/helm/microfunctions/mongodb
В учебной документации предлагается установить https://kubernetes-charts.storage.googleapis.com верию 0.4.18.
Если же поиск выполнять в helm search repo
, то нужно добавить репозиторий charts
Здесь предлагаются актуальные версии, но для приложения reddit подходит или необходима старая версия - 4.2.4.
helm repo list
helm repo add stable https://charts.helm.sh/stable
helm repo update
helm search repo stable/mongodb
Результат:
stable/mongodb 7.8.10 4.2.4 DEPRECATED NoSQL document-oriented database tha...
...
В конфигурационном файле указываем версию:
cat << EOF >> reddit/requirements.yaml
- name: mongodb
version: 7.8.10
repository: https://charts.helm.sh/stable
EOF
helm dep update reddit --debug
Резльтат
reddit/charts/mongodb-7.8.10.tgz
:
mongodb/Chart.yaml
mongodb/values.yaml
mongodb/templates/NOTES.txt
mongodb/templates/_helpers.tpl
mongodb/templates/configmap.yaml
mongodb/templates/deployment-standalone.yaml
mongodb/templates/ingress.yaml
...
mongodb/.helmignore
mongodb/OWNERS
mongodb/README.md
mongodb/files/docker-entrypoint-initdb.d/README.md
mongodb/values-production.yaml
mongodb/values.schema.json
В некоторых атрибутах нельзя использовать переменные, как например, а app... оставляем аьрибуты без использования переменных.
Error: INSTALLATION FAILED: YAML parse error on reddit/charts/comment/templates/deployment.yml: error converting YAML to JSON: yaml: line 22: mapping values are not allowed in this context
Чтобы включить 443 порт, к ingress.yaml можно добавить через переменные такие услования:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
{{- end }}
secretName: {{ .Values.ingress.secretName }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.ingress.secretName }}
...
type: kubernetes.io/tls
{{- end }}
А в файле переменных values.yaml описываем их так:
...
ingress:
enabled: true
secretName: ui-ingress-tls
hosts:
- host: reddit.infranet.dev
paths:
- path: /
backend:
service:
name: ui
tls:
- hosts:
- reddit.infranet.dev
...
Раскатка в облако Kubernetes:
rm -fR reddit/charts/*; \
helm uninstall reddit-test; \
helm dep update reddit && \
helm install reddit --namespace=dev --debug --name-template reddit-test
Результат, если выполнилась успешно сборка:
client.go:134: [debug] creating 12 resource(s)
NAME: reddit-test
LAST DEPLOYED: Wed Oct 25 13:31:16 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
...
# Source: post/charts/ui/templates/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: reddit-test-ui
namespace: dev
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.allow-http: "false"
app.kubernetes.io/instance: reddit-test
app.kubernetes.io/managemt-by: Helm
spec:
ingressClassName: nginx
rules:
- host: "reddit.infranet.dev"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ui
port:
number: 80
tls:
- hosts:
- "reddit.infranet.dev"
secretName: ui-ingress-tls
Для добавления дополнительных имен (как обращение по FQDN или другим именам) можно использовать repository:alias или externalNames в шаблоне reddit/templates/externalnames.yaml
:
{{- $values := .Values -}}
{{- $release := .Release -}}
{{- range $key, $value := .Values.service.names }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $value }}
labels:
app: reddit
spec:
type: "ExternalName"
externalName: {{ printf "%s-mongodb.dev.svc.cluster.local" $release.Name }}
{{- end}}
А в файле переменных:
---
service:
names:
- post-db
- comment-db
comment:
service:
externalPort: 9292
post:
service:
externalPort: 5000
Результат команды:
helm install reddit/ -n dev --atomic --replace --debug --name-template reddit-test --set auth.enabled=false
:
kubectl get service -A --selector=app=reddit
Результат:
default comment-db ExternalName <none> mongo.dev.svc.cluster.local <none> 104s
default post-db ExternalName <none> mongo.dev.svc.cluster.local <none> 104s
И видно, что добавлены переменные для указания называний узлов в файл ui/templates/deployment.yaml:
spec:
...
env:
- name: POST_SERVICE_HOST
value: {{ .Values.postHost | default (printf "%s-post" .Release.Name) }}
- name: POST_SERVICE_PORT
value: {{ .Values.postPort | default "5000" | quote }}
- name: COMMENT_SERVICE_HOST
value: {{ .Values.commentHost | default (printf "%s-comment" .Release.Name) }}
- name: COMMENT_SERVICE_PORT
value: {{ .Values.commentPort | default "9292" | quote }}
- name: ENV
valueFrom:
fieldRef:
fieldPath: metadata.namespace
По заданию без указания значений добавляются переменные в ui/values.yaml:
...
postHost:
postPort:
commentHost:
commentPort:
При тестировании выяснилось, что mongodb от bitnami требует аутентификацию. Добавление этих параметров в values.yml для тестирования микросервисов обходит требование авторизоваться.
mongodb:
auth:
enabled: false
usePassword: false
Результат:
- Установка и внесение изменений в конфигурационные файлы
helm repo add gitlab https://charts.gitlab.io
helm fetch gitlab/gitlab-omnibus --version 0.1.37 --untar
cd gitlab-omnibus
helm install --name-template gitlab . -f values.yaml
Данная версия устарела для текущей версии Kubernetes:
Most likely the problem is not related to missing CRDs but to the kubernetes version.
WARNING: This chart is deprecated
Error: INSTALLATION FAILED: unable to build kubernetes objects from release manifest: [resource mapping not found for name: "nginx" namespace: "nginx-ingress" from "": no matches for kind "DaemonSet" in version "extensions/v1beta1"
ensure CRDs are installed first, resource mapping not found for name: "gitlab-gitlab-runner" namespace: "" from "": no matches for kind "Deployment" in version "extensions/v1beta1"
ensure CRDs are installed first, resource mapping not found for name: "gitlab-gitlab" namespace: "" from "": no matches for kind "Deployment" in version "extensions/v1beta1"
ensure CRDs are installed first, resource mapping not found for name: "gitlab-gitlab-postgresql" namespace: "" from "": no matches for kind "Deployment" in version "extensions/v1beta1"
ensure CRDs are installed first, resource mapping not found for name: "gitlab-gitlab-redis" namespace: "" from "": no matches for kind "Deployment" in version "extensions/v1beta1"
ensure CRDs are installed first, resource mapping not found for name: "kube-lego" namespace: "kube-lego" from "": no matches for kind "Deployment" in version "extensions/v1beta1"
ensure CRDs are installed first, resource mapping not found for name: "default-http-backend" namespace: "nginx-ingress" from "": no matches for kind "Deployment" in version "extensions/v1beta1"
ensure CRDs are installed first, resource mapping not found for name: "gitlab-gitlab" namespace: "" from "": no matches for kind "Ingress" in version "extensions/v1beta1"
ensure CRDs are installed first]
# DEPRECATION NOTICE
This chart is DEPRECATED.
### Replacement
We have built a set of fully cloud native charts in [gitlab/gitlab](https://gitlab.com/charts/gitlab).
Пробуем на актуальных версиях ПО...
helm install gitlab gitlab/gitlab \
--set global.hosts.domain=infranet.dev \
--set [email protected]
NAME: gitlab
LAST DEPLOYED: Wed Oct 25 21:49:44 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
...
- Ingress objects must be in group/version `networking.k8s.io/v1`.
Получим внешний IP адрес:
kubectl get ingress -lrelease=gitlab
Результат:
NAME CLASS HOSTS ADDRESS PORTS AGE
gitlab-kas gitlab-nginx kas.infranet.dev 158.160.124.2 80, 443 13m
gitlab-minio gitlab-nginx minio.infranet.dev 158.160.124.2 80, 443 13m
gitlab-registry gitlab-nginx registry.infranet.dev 158.160.124.2 80, 443 13m
gitlab-webservice-default gitlab-nginx gitlab.infranet.dev 158.160.124.2 80, 443 13m
Получить пароль для входа:
kubectl get secret gitlab-gitlab-initial-root-password -ojsonpath='{.data.password}' | base64 --decode ; echo
Удаление gitlab
helm uninstall gitlab
Сброс пароля root'а
kubectl get pods -lrelease=gitlab
kubectl exec <Webservice pod name> -it -- bash
/srv/gitlab/bin/rails runner "user = User.first; user.password='#{password}'; user.password_confirmation='#{password}'; user.save!"
Запущен проект https://gitlab.infranet.dev/23f03013e37f
Инициализация проекта в репозиторий Gitlab:
Отмечю, что предлагается использовать не master, а main как репозиторий по-умолчанию...
cd Gitlab_ci/ui
git init
git remote add origin [email protected]:23f03013e37f/$(basename $(pwd)).git
git branch -M main
git add .
git commit -m "init"
git push -uf origin main
Аналогичные действия выполнены для других папок: comment, post, reddit-deploy
Настройка Gitlab Runner'а для запуска Docker-in-Docker (dind).
Загрузка Chart'а gitlab-runner с сайта:
export HELM_EXPERIMENTAL_OCI=1 && \
helm pull oci://cr.yandex/yc-marketplace/yandex-cloud/gitlab-org/gitlab-runner/chart/gitlab-runner \
--version 0.54.0-8 \
--untar && \
В файле values.yaml в папке gitlab-runner нужно rbac.create либо false или добавить правила rbac.rules, см. stackoverflow и runners.privileged установить значение true
...
rbac:
clusterWideAccess: false
create: true
podSecurityPolicy:
enabled: false
resourceNames:
- gitlab-runner
rules:
- apiGroups: [""]
resources: ["pods", "secrets", "configmaps"]
verbs: ["get", "list", "watch", "create", "patch", "delete", "update"]
- apiGroups: [""]
resources: ["pods/exec", "pods/attach"]
verbs: ["create", "patch", "delete"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
resources: {}
runnerRegistrationToken: "glrt-rCoCNbEHSJPux9PXGemx"
runners:
cache: {}
config: |
[[runners]]
[runners.kubernetes]
namespace = "{{.Release.Namespace}}"
image = "ubuntu:20.04"
{{- if .Values.runners.privileged }}
privileged = true
[[runners.kubernetes.volumes.empty_dir]]
name = "docker-certs"
mount_path = "/certs/client"
medium = "Memory"
{{- end }}
locked: false
privileged: true
tags: ""
secrets: []
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- CAP_NET_ADMIN
- CAP_SYS_ADMIN
privileged: true
readOnlyRootFilesystem: false
runAsNonRoot: true
...
Установка helm'ом Gitlab Runner'а (не забываем создать в проекте Gitlab - Runner, а Shared можно отключить, так как нет поддержки dind)
Нужно прописать полученный token и в файле выше runnerRegistrationToken и при установке runnerToken.
После получения token'а выполняется установка в свой namespace:
helm install --namespace gitlab-runner --create-namespace -f ./gitlab-runner/values.yaml --set gitlabDomain=infranet.dev --set gitlabUrl=https://gitlab.infranet.dev --set runnerToken=glrt-rCoCNbEHSJPux9PXGemx gitlab-runner ./gitlab-runner/
Удаление runner'а:
helm uninstall -n gitlab-runner gitlab-runner
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: gitlab-runner-role-default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: gitlab-runner
subjects:
- kind: ServiceAccount
name: default
namespace: gitlab-runner
EOF
В настройках Gitlab, где указываются переменные CI_REGISTRY_USER, CI_REGISTRY_PASSWORD - это учетные записи docker.io
В версии Gitlab Runner выше 12.7 нужно указывать docker вместо localhost
# If you're using GitLab Runner 12.7 or earlier with the Kubernetes executor and Kubernetes 1.6 or earlier,
# the variable must be set to tcp://localhost:2375 because of how the
# Kubernetes executor connects services to the job container
# DOCKER_HOST: tcp://localhost:2375
#
DOCKER_HOST: tcp://docker:2375
При сборке post в Gitlab CI/CD было следующее уведомление:
"No output specified with docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load"
Далее процесс отправки образа в docker.io
завершался ошибкой:
docker push index.docker.io/23f03013e37f/post:test
The push refers to repository [docker.io/23f03013e37f/post]
An image does not exist locally with the tag: 23f03013e37f/post
Cleaning up project directory and file based variables
00:00
ERROR: Job failed: command terminated with exit code 1
Проблему удалось решить путем настройки аутентификации докера раньше сборки, а при сборке использовать ключ --push в .gitlab-ci.yml:
docker buildx build -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" --platform=linux/amd64 --push .
Так как образ уже собран в Registry docker.io, чтобы не "гонять" еще раз после build'а для указания версии, есть возмоджность просто изменить манифест командой:
docker buildx imagetools create "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" --tag "$CI_APPLICATION_REPOSITORY:$(cat VERSION)"
Результат:
Для заруска задачи (Job) в отдельном Kubernetes POD-е доавлены атрибуты tags в CI/CD Runner и в .gitlab-ci.yml
Пайплайн здорового человека для ui можно посмотреть здесь, рабочий вариант... для reddit-deploy.
Чтобы запустить CI/CD в Environment: staging, production нужно подключить кластер Kubernetes к Gitlab,
Поэтому выполнить настройку аутентификацию Kubernetes в GitLab необходимо.
По документации нужно создать подключение к кластеру, прямо в строке поиска агента указываем имя и создать:
Установка Gitlab Agent с помощью Helm-чарта (версия должна быть совместима с Gitlab, можно взять с картинки):
export HELM_EXPERIMENTAL_OCI=1 && \
helm pull oci://cr.yandex/yc-marketplace/yandex-cloud/gitlab-org/gitlab-agent/chart/gitlab-agent \
--version 1.16.0-1 \
--untar && \
helm upgrade --install \
--namespace gitlab-agent-infra \
--create-namespace \
--set serviceAccount.create=false \
--set rbac.create=false \
--set rbac.useExistingRole=gitlab-runner-role-default \
--set config.kasAddress='wss://kas.infranet.dev/-/kubernetes-agent/' \
--set config.token='glagent-JyfC-qffZHonby8YTCykmzFDz8shGXaJxX9g2bpDL3c17QFyLA' \
gitlab-agent ./gitlab-agent/
Если RBAC указывается true, то, возможно, нужно добавить роль (ранее нужно было такое же делать для Runner'а):
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: gitlab-agent
namespace: gitlab-agent-infra
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list", "get", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
EOF
В меню Operation/Environments, там же есть подсказка по review:
deploy_review:
stage: deploy
script:
- echo "Add script here that deploys the code to your infrastructure"
environment:
name: review/$CI_COMMIT_REF_NAME
url: https://$CI_ENVIRONMENT_SLUG.infranet.dev
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
Чтобы заработала связка Gitlab и Kubernetes, полезно воспользоваться этой статьей, в Envrironment добавил KUBE_CONFIG из ~/.kube/config и при выполнении docker'ом в CI Pipeline'е можно было увидеть context, который был выставлен:
Далее выяснилось, что используемый helm-3 имеет проблему:
manager.go:107: warning: a valid Helm v3 hash was not found. Checking against Helm v2 hash... Error: the lock file (requirements.lock) is out of sync with the dependencies file (requirements.yaml). Please update the dependencies
Возможно, нужно не публиковать requirements.lock в .gitignore и еще актуальная версия helm'а, если использовать просто image: alpine - 3.13.1
Но переделал способ - это использовать уже alpine/helm образ (взято из лекционного материала):
test:
stage: test
image:
name: alpine/helm
entrypoint: ["/bin/sh","-c"]
variables:
KUBE_CONTEXT: 23f03013e37f/reddit-deploy:reddit-deploy
KUBE_NAMESPACE: staging
environment: test
script:
- install_dependencies
- ensure_namespace
- deploy
only:
- branches
Опытном путём выяснилось, что всё же не нужен KUBE_CONFIG/KUBECONFIG - yaml-файл указывать в переменных в Gitlab-Project-Settings-CI/CD-Variables, KUBECONFIG - файл, будет виден в set
из преременной KUBECONFIG=/builds/23f03013e37f/reddit-deploy.tmp/KUBECONFIG
Сложно было разобраться, какой-же всё же контекст: /:, где agent-folder-name, берется из .gitlab/agents//config.yaml
user_access:
access_as:
agent: {}
projects:
- id: 23f03013e37f/reddit-deploy
ci_access:
projects:
- id: 23f03013e37f/reddit-deploy
groups:
- id: 23f03013e37f
observability:
logging:
level: debug
В gitlab-ci.yml в ui.image.tag подправлен путь в команде helm'а - добавлен '/-/' в /ui/-/raw/main/VERSION
Далее задать своего агента можно и через переменную:
Все остальные действия уже чисто дело техники, сам процесс уже понятен... :-)
Альтернатива агенту по схеме pull - Flux
Файлы .gitlab-ci.yml
скопированы из Gitlab_ci в папку src/{ui,post-py,comment} и для reddit-deploy - в charts.
Список литературы и статей:
- Минимально жизнеспособный Kubernetes
- Kubernetes The Hard Way
- Kubernetes и другие оркестраторы
- Yandex Managed Service для Kubernetes
- Настройка групп безопасности
- Сервисные аккаунты
- Обеспечение доступа к приложению, запущенному в кластере Kubernetes
- Как начать работать с Network Load Balancer
- Проверка доступности ресурсов
- Kubernetes DNS for Services and Pods
- Сетевые политики кластера Kubernetes
- Использование объектов API Kubernetes
- Create MongoDB server on Kubernetes with PersistentVolume
- Динамическая подготовка тома
- Управление классами хранилищ
- kubectl Cheat Sheet
- A visual guide on troubleshooting Kubernetes deployments
Абстракции над подами
- Deployment — инструмент контроля за состоянием подов
- ReplicaSet — запускает несколько подов
- DaemonSet — запускает строго один под на каждом узле кластера
- StatefulSet — запускает нумерованные поды для stateful-приложений
- Job — запускает под один раз, пока он не завершится успешно
- CronJob — запускает Job по крону (расписанию)
Так как на странице 24 домашнего задания представлена картинка управлеяемого кластера, выполнение пунктов выполнялась с установкой Yandex Managed Service для Kubernetes кластере infra-kube.
- Настройка групп безопасности
yc vpc security-group create --name yc-security-group --network-name default \
--rule 'direction=ingress,port=443,protocol=tcp,v4-cidrs=0.0.0.0/0' \
--rule 'direction=ingress,port=80,protocol=tcp,v4-cidrs=0.0.0.0/0' \
--rule 'direction=ingress,from-port=0,to-port=65535,protocol=any,predefined=self_security_group' \
--rule 'direction=ingress,from-port=0,to-port=65535,protocol=any,v4-cidrs=[10.96.0.0/16,10.112.0.0/16,10.128.0.0/16]' \
--rule 'direction=ingress,from-port=0,to-port=65535,protocol=tcp,v4-cidrs=[198.18.235.0/24,198.18.248.0/24]' \
--rule 'direction=egress,from-port=0,to-port=65535,protocol=any,v4-cidrs=0.0.0.0/0' \
--rule 'direction=ingress,protocol=icmp,v4-cidrs=[10.0.0.0/8,192.168.0.0/16,172.16.0.0/12]' \
--rule 'direction=ingress,from-port=30000,to-port=32670,protocol=tcp,v4-cidrs=0.0.0.0/0' \
--rule 'direction=ingress,from-port=0,to-port=65535,protocol=tcp,predefined=loadbalancer_healthchecks'
- Запишем в переменную
<id группы безопасности>
:
export SG_ID=$(yc vpc security-group get --name yc-security-group | head -1 | awk '{print $2}')
echo $SG_ID
- Создадим сервисный аккаунт для кластера Kubernetes:
YC_SVC_ACCT="kube-infra"
YC_FOLDER_ID=$(yc config get folder-id)
YC_SUBNET_ID=$(yc vpc subnet get default-ru-central1-a | head -1 | awk -F ': ' '{print $2}')
YC_FOLDER_NAME="infra"
yc iam service-account create kube-infra
yc iam service-account create --name=$YC_SVC_ACCT --folder-id=$YC_FOLDER_ID
YC_ACCT_ID=$(yc iam service-account get $YC_SVC_ACCT | grep ^id | awk '{print $2}')
yc resource-manager folder add-access-binding \
--name=default \
--id=$YC_FOLDER_ID \
--folder_id=$YC_FOLDER_ID \
--service-account-id=$YC_ACCT_ID \
--role=editor
yc iam key create --service-account-id=$YC_ACCT_ID \
--output=../../kubernetes/infra/.secrets/$YC_SVC_ACCT.json
- Создаём публичный зональный кластер в зоне ru-central1-a:
yc managed-kubernetes cluster create \
--name=kube-infra \
--public-ip \
--network-name=default \
--service-account-name=$YC_SVC_ACCT \
--node-service-account-name=$YC_SVC_ACCT \
--release-channel=rapid \
--zone=ru-central1-a \
--version=1.27 \
--security-group-ids=$SG_ID \
--enable-network-policy \
--folder-name=$YC_FOLDER_NAME
- Создаём рабочую группу из одного узла:
С помощью флага --preemptible будут создаваться прерываемые инстансы, которые намного дешевле обычных. О таких инстансах
yc managed-kubernetes node-group create \
--name=kube-group \
--cluster-name=kube-infra \
--cores=2 \
--memory=4G \
--preemptible \
--auto-scale=initial=1,min=1,max=2 \
--network-interface=subnets=default-ru-central1-a,ipv4-address=nat,security-group-ids=$SG_ID \
--folder-name=$YC_FOLDER_NAME \
--metadata="ssh-keys=yc-user:~/.ssh/id_rsa-appuser.pub"
- Получаем конфигигурацию и выполняем подключение:
yc managed-kubernetes cluster get-credentials --name=kube-infra --force --external
В результате в файл
~/.kube/config
будут добавлены user, cluster, и context для подключения к кластеру в Yandex Cloud.
Данную команду нужно выполнять каждый раз после при запуска при динамическом внешнем адресе кластера:
yc managed-kubernetes cluster start kube-infra
yc application-load-balancer load-balancer list --format json | jq -r '.[].id' | xargs -n1 yc application-load-balancer load-balancer start
yc load-balancer network-load-balancer list --format json| jq -r '.[].id' | xargs -n1 yc load-balancer network-load-balancer start
- Проверяем, что доступ есть и нода создалась:
kubectl get nodes
Результат:
NAME STATUS ROLES AGE VERSION
cl1vhjhn7vldqi2v6ucs-avys Ready <none> 37h v1.27.3
cl1vhjhn7vldqi2v6ucs-ytiw Ready <none> 37h v1.27.3
kubectl cluster-info
Результат:
Kubernetes control plane is running at https://158.160.100.194
CoreDNS is running at https://158.160.100.194/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
- Конфигурация контекста
kubectl config current-context
Результат:
yc-kube-infra
- Запустим reddit проект в
namespace dev
:
в манифестах используется из предыдущего домашнего задания gitlab-ci ранее подготовленные образы из docker hub'а
for i in `/usr/bin/ls dev-namespace.yml comment* mongo* post* ui*`; do kubectl apply -n dev -f $i; done
Но, так как ранее сервис базы данных и сервис фронтенда были разнесены по разным сетям, mongo ДБ недоступна по именам post-db и comment-db. В лог сервиса post наблюдаем такую ошибку:
{"event": "find_all_posts", "level": "info", "message": "Successfully retrieved all posts from the database", "params": {}, "request_id": "93e56309-2e21-434f-a7e0-542b9c95ae85", "service": "post", "timestamp": "2023-10-20 21:14:24"}
{"event": "internal_error", "level": "error", "method": "GET", "path": "/posts?", "remote_addr": "10.112.129.13", "request_id": "93e56309-2e21-434f-a7e0-542b9c95ae85", "service": "post", "timestamp": "2023-10-20 21:14:54", "traceback": "Traceback (most recent call last):
...
File "/usr/local/lib/python3.6/site-packages/pymongo/topology.py", line 189, in select_servers
self._error_message(selector))
pymongo.errors.ServerSelectionTimeoutError: post_db:27017: [Errno -2] Name does not resolve
"}
В Kubernetes так добавить дополнительные имена: post-db и comment-db к Deployment mongo нет возможности, чтобы запись появилась в coredns Pod'е. Найдено два решения проблемы:
- использовать переменные у comment и post
spec:
env:
- name: COMMENT_DATABASE_HOST
value: mongo
- использовать сервис ExternalName
---
apiVersion: v1
kind: Service
metadata:
name: post-db
spec:
type: ExternalName
externalName: "mongo.dev.svc.cluster.local"
---
apiVersion: v1
kind: Service
metadata:
name: comment-db
spec:
type: ExternalName
externalName: "mongo.dev.svc.cluster.local"
До применения, имя post-db недоступно
kubectl exec -ti -n dev ui-cf9f76bc-8qwr4 -- /bin/bash
root@ui-cf9f76bc-8qwr4:/app# ping post-db
ping: unknown host post-db
После:
Результат:
root@ui-cf9f76bc-8qwr4:/app# ping mongo
PING mongo.dev.svc.cluster.local (10.96.241.0) 56(84) bytes of data.
^C
root@ui-cf9f76bc-8qwr4:/app# ping post-db
PING mongo.dev.svc.cluster.local (10.96.241.0) 56(84) bytes of data.
^C
root@ui-cf9f76bc-8qwr4:/app# ping comment-db
PING mongo.dev.svc.cluster.local (10.96.241.0) 56(84) bytes of data.
^C
In Kubernetes, you can set up a DNS system with two well-supported add-ons: CoreDNS and Kube-DNS. CoreDNS is a newer add-on that became a default DNS server as of Kubernetes v1.12. However, Kube-DNS may still be installed as a default DNS system by certain Kubernetes installer tools.
- "Отключение" службы coredns
kubectl scale deployment --replicas 0 -n kube-system kube-dns-autoscaler
kubectl scale deployment --replicas 0 -n kube-system coredns
kubectl get deployment -n kube-system
Результат:
NAME READY UP-TO-DATE AVAILABLE AGE
coredns 0/0 0 0 6h13m
kube-dns-autoscaler 0/0 0 0 6h13m
metrics-server 0/1 1 0 6h13m
- Проверка недоступности DNS:
kubectl exec ui-764fc4f97d-vr2pj -ti -n default -- /bin/bash
root@ui-764fc4f97d-vr2pj:/app# ping post
ping: unknown host post
root@ui-764fc4f97d-vr2pj:/app# telnet post 5000
telnet: could not resolve post/5000: Temporary failure in name resolution
- Проверка с доступностью DNS:
kubectl exec ui-764fc4f97d-vr2pj -ti -n default -- telnet post 5000
Trying 10.96.239.150...
Connected to post.default.svc.cluster.local.
Escape character is '^]'.
kubectl scale deployment --replicas 1 -n kube-system coredns kube-dns-autoscaler
kubectl exec ui-764fc4f97d-vr2pj -ti -n default -- ping -c 4 ya.ru
PING ya.ru (77.88.55.242) 56(84) bytes of data.
64 bytes from ya.ru (77.88.55.242): icmp_seq=1 ttl=248 time=3.58 ms
Типы сервисов: ClusterIP, NodePort, LoadBalancer, ExternalName.
selector — атрибут, который связывает сервис с подами. Связь происходит через совпадения лейблов. То есть в данном примере наш сервис post будет связан с подами, имеющими лейблы reddit: post. ports — атрибут, позволяющий объявить порты сервиса и связать их с портами подов. Получается, что обратившись на порт 443 сервиса metrics-server, мы попадём на порт с именем https у подов. В нашем кластере этот порт также равен 443.
Service объединяет несколько подов в пул и даёт единый сетевой доступ к этому пулу. Это решает проблему непостоянства адреса одиночных подов
- NodePort
Тип NodePort хоть и предоставляет доступ к сервису, но получить доступ к нашему приложению мы можем через проброс портов утилиты kubectl:
---
apiVersion: v1
kind: Service
metadata:
name: ui
labels:
app: reddit
component: ui
spec:
type: NodePort
externalTrafficPolicy: Cluster
ports:
- port: 80
nodePort: 30294
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: ui
kubectl
в отличии от docker команды позволяет воспользоваться табуляцией для выбора нужного Deployment'а контейнера
kubectl port-forward -n dev ui-cf9f76bc-8qwr4 8888:9292
Forwarding from 127.0.0.1:8888 -> 9292
Handling connection for 8888
- LoadBalancer
spec:
type: LoadBalancer
INFO При создании сервиса типа LoadBalancer, контроллер Yandex Cloud создает и настраивает сетевой балансировщик нагрузки в вашем каталоге с публичным IP-адресом.
kubectl get services -n dev -o wide
Результат:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
comment NodePort 10.96.211.236 <none> 9292:30263/TCP 2d3h app=reddit,component=comment
comment-db ExternalName <none> mongo.dev.svc.cluster.local <none> 31h <none>
mongo NodePort 10.96.241.0 <none> 27017:31944/TCP 2d3h app=reddit,component=mongo
post ClusterIP 10.96.238.60 <none> 5000/TCP 2d3h app=reddit,component=post
post-db ExternalName <none> mongo.dev.svc.cluster.local <none> 31h <none>
ui LoadBalancer 10.96.232.43 51.250.39.254 80:30294/TCP 11s app=reddit,component=ui
kubectl get service -n dev --selector component=ui
Результат:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
comment NodePort 10.96.211.236 <none> 9292:30263/TCP 2d3h
ui LoadBalancer 10.96.232.43 51.250.39.254 80:30294/TCP 34s
- Ingress и TLS Termination
Ingress Сontroller — один из важнейших компонентов кластера Kubernetes. Через него проходят практически все внешние запросы к кластеру. Для стандартного Nginx Ingress Controller входящий трафик обрабатывается Nginx, который развёрнут внутри кластера и распределяет запросы по сервисам типа ClusterIP.
Для выполнения домашнего задания были рассмотрены два Ingress Сontroller:
- Yandex ALB Ingress Controller - ALB-балансировщик
- Ingress NGINX Controller
Для настройки Yandex ALB - можно воспользоваться материалом курса Деплой инфраструктуры по модели gitops
Пример манифеста ingress.yml
для Pod'а httpbin:
cat << EOF > httpbin-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin
annotations:
ingress.alb.yc.io/subnets: $(yc vpc subnet get default-ru-central1-a | head -1 | awk -F ': ' '{print $2}')
ingress.alb.yc.io/external-ipv4-address: $(yc vpc address get infra-alb --format json | jq -r .external_ipv4_address.address)
ingress.alb.yc.io/group-name: infra-alb
ingress.alb.yc.io/security-groups: $(yc vpc security-group get --name yc-security-group | head -1 | awk '{print $2}')
spec:
tls:
- hosts:
- "httpbin.infranet.dev"
secretName: yc-certmgr-cert-id-$(yc certificate-manager certificate list --format json | jq -r '.[] | select(.name == "kube-infra") | .id')
rules:
- host: httpbin.infranet.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: httpbin
port:
number: 80
EOF
kubectl apply -n httpbin --force=true -f manifests/ingress.yaml
curl https://httpbin.infra.net.ru
INFO Здесь же сразу для получения сертификата letsencrypt заведен домен infranet.dev, изучен и настроен Cloud DNS, Certificate Manager.
Создадим сертификат на доменное имя:
yc certificate-manager certificate request \
--name kube-infra \
--domains "*.infranet.dev" \
--challenge dns
Результат:
id: fpqglql78os3ujgdao91
folder_id: b1g0da3u1gqk0nansi59
created_at: "2023-10-18T15:51:36.931798407Z"
name: kube-infra
type: MANAGED
domains:
- '*.infranet.dev'
status: VALIDATING
updated_at: "2023-10-18T15:51:36.931798407Z"
В настройках домена указываются nameservers Яндекса, чтобы и доменными записями управлять через Yandex Cloud:
- ns1.yandexcloud.net
- ns2.yandexcloud.net
Для автоматической проверки владения доменом требуется создать специальную CNAME-запись, ведущую на certificate-manager:
YC_CERT_ID=$(yc certificate-manager certificate list --format json | jq -r '.[] | select(.name == "kube-infra") | .id')
yc dns zone add-records --name yc-courses --record \
"_acme-challenge.infranet.dev. 600 CNAME $YC_CERT_ID.cm.yandexcloud.net."
yc certificate-manager certificate content --name kube-infra
Статус сертификата должен перейти в состояние ISSUED:
yc certificate-manager certificate list --format json | jq -r '.[] | select(.name == "kube-infra") | .status'
Создание wildcard A-записи, указывающую на IP балансировщика:
yc dns zone add-records --name yc-courses \
--record "*.infranet.dev. 600 A $INFRA_ALB_ADDRESS"
Результат:
+--------+-----------------+------+----------------+-----+
| ACTION | NAME | TYPE | DATA | TTL |
+--------+-----------------+------+----------------+-----+
| + | *.infranet.dev. | A | 158.160.81.102 | 600 |
+--------+-----------------+------+----------------+-----+
Получить сертификат и ключ из облака
yc certificate-manager certificate content --name kube-infra
Или же загрузить можно свой в кластер:
kubectl create secret tls ui-ingress --key privkey.pem --cert cert.pem -n dev
kubectl describe secret ui-ingress -n dev
Результат:
Name: ui-ingress-tls
Namespace: dev
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1762 bytes
tls.key: 1676 bytes
Ниже приводится настройка Ingress NGINX Controller'а
helm upgrade \
--create-namespace \
--namespace ingress-nginx \
--set folderId=$YC_FOLDER_ID \
--set clusterId=$YC_CLUSTER_ID \
--install ingress-nginx ingress-nginx \
--set-file saKeySecretKey=../../kubernetes/infra/.secrets/$YC_SVC_ACCT.json \
--repo https://kubernetes.github.io/ingress-nginx \
--atomic
Результат:
Release "ingress-nginx" does not exist. Installing it now.
NAME: ingress-nginx
LAST DEPLOYED: Fri Oct 20 15:56:45 2023
NAMESPACE: ingress-nginx
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl --namespace ingress-nginx get services -o wide -w ingress-nginx-controller'
...
Как результат приводится пример yaml файла, который был адаптирован для приложения reddit - ui-ingress.yml с добавляем в Ingress использование TLS-сертификата
cat << EOF > ui-ingress.yml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ui
namespace: dev
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.allow-http: "false"
spec:
ingressClassName: nginx
rules:
- host: reddit.infranet.dev
http:
paths:
- pathType: Prefix
backend:
service:
name: ui
port:
number: 80
path: /*
tls:
- hosts:
- reddit.infranet.dev
secretName: ui-ingress-tls
---
apiVersion: v1
kind: Secret
metadata:
name: ui-ingress-tls
namespace: dev
data:
tls.crt: $(cat certs/cert.pem | base64 -w 0)
tls.key: $(cat certs/key.pem | base64 -w 0)
type: kubernetes.io/tls
EOF
kubectl apply -n dev -f ui-service.yml -f ui-ingress.yml
Применяем и ui-service.yml, так как поменялся тип на ClusterIP
kubectl get ingress -n dev -o wide
Результат:
NAME CLASS HOSTS ADDRESS PORTS AGE
ui nginx reddit.infranet.dev 158.160.81.102 80, 443 28h
---
apiVersion: v1
kind: Secret
metadata:
name: ui-ingress-tls
namespace: dev
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
type: kubernetes.io/tls
yc container clusters list
Результат:
В самом начале рассматривались группы безопасности, которые можно применить к самому кластеру managed-kubernetes cluster
и managed-kubernetes node-group
.
Пример настройки в web-консоли:
INFOМожно включить использование сетевых политик только при создании кластера.
Чтобы включить контроллер сетевых политик Calico, передайте в команде создания кластера Managed Service for Kubernetes параметр --enable-network-policy:
yc managed-kubernetes cluster create \
...
--enable-network-policy
- Применён файл mongo-network-policy.yml
kubectl get networkpolicies.networking.k8s.io -n dev
Результат:
NAME POD-SELECTOR AGE
deny-db-traffic app=reddit,component=mongo 25m
- Для проверки использовался pod ui
kubectl exec -ti -n dev ui-cf9f76bc-8qwr4 -- telnet mongo 27017
Trying 10.96.241.0...
^C
- Для обеспечения доступности:
- podSelector:
matchLabels:
app: reddit
component: post
- podSelector:
matchLabels:
app: reddit
component: ui
ports:
- protocol: TCP
port: 27017
Результат:
Trying 10.96.241.0...
Connected to mongo.dev.svc.cluster.local.
Escape character is '^]'.
^]
- тип Volume emptyDir
volumeMounts:
- name: mongo-persistent-storage
mountPath: /data/db
volumes:
- name: mongo-ps
emptyDir: {}
Сообщения удаляются при перезагрузке или удалении pod'а с ДБ
- Динамическая подготовка тома
В большинстве случаев нет необходимости вручную создавать объекты PersistentVolumes и диски Compute Cloud. Вместо этого можно создать объекты PersistentVolumeClaim, и Managed Service for Kubernetes автоматически подготовит необходимый объект PersistentVolume и создаст диск.
При удалении PVC и ReclaimPolicy Delete
kubectl get storageclass
диски удаляются
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongo-pvc
labels:
app: reddit
component: pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: yc-network-hdd
- Статический том
При использовании статического диска, необходимо к PVC добавить атрибут volumeName с названием PV. PVC
cat << EOF > mongo-volume.yml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongo-pv
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
csi:
driver: disk-csi-driver.mks.ycloud.io
fsType: ext4
volumeHandle: $(yc compute disk show --name mongo-disk --format json | jq .id | tr -d '"')
storageClassName: yc-network-hdd
EOF
cat << EOF >> mongo-claim.yml
volumeName: mongo-pv
EOF
kubectl create -n dev -f mongo-claim.yml
kubectl describe -n dev persistentvolumeclaim mongo-pvc
kubectl describe -n dev persistentvolume mongo-pv
Результат:
Name: mongo-pvc
Namespace: dev
StorageClass: yc-network-hdd
Status: Bound
Volume: mongo-pv
Labels: app=reddit
component=pvc
Annotations: pv.kubernetes.io/bind-completed: yes
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 2Gi
Access Modes: RWO
VolumeMode: Filesystem
Used By: mongo-6568865f78-drqvs
Events: <none>
kubectl describe -n dev persistentvolume mongo-pv
Результат:
Name: mongo-pv
Labels: <none>
Annotations: pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pv-protection external-attacher/disk-csi-driver-mks-ycloud-io]
StorageClass: yc-network-hdd
Status: Bound
Claim: dev/mongo-pvc
Reclaim Policy: Retain
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 2Gi
Node Affinity: <none>
Message:
Source:
Type: CSI (a Container Storage Interface (CSI) volume source)
Driver: disk-csi-driver.mks.ycloud.io
FSType: ext4
VolumeHandle: fhmq2jmvqv725chbkker
ReadOnly: false
VolumeAttributes: <none>
Events: <none>
Важно, чтобы Status перешел из Pending в Bound
- Тестирование
Удаляем диск и создаем новый, после создаем новый, прописываем в PV id диска volumeHandle
yc compute disk delete --name mongo-disk; \
yc compute disk create --name mongo-disk --size 2 --description "Disk for K8s"; \
cat << EOF > mongo-volume.yml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongo-pv
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
csi:
driver: disk-csi-driver.mks.ycloud.io
fsType: ext4
volumeHandle: $(yc compute disk show --name mongo-disk --format json | jq .id | tr -d '"')
storageClassName: yc-network-hdd
EOF
kubectl apply -n dev -f mongo-volume.yml -f mongo-claim.yml -f mongo-deployment.yml
kubectl describe -n dev pod mongo
Результат:
После удаления и создания mongo Deployment все сообщения сохранены:
kubectl delete -n dev -f mongo-deployment.yml
kubectl apply -n dev -f mongo-deployment.yml
Реузльтат:
На картинках ниже будет видно статус использования диска
Полезные команды kubectl, которые могут пригодиться при работы с кластером:
kubectl apply -f
— применить манифестыkubectl get <kind>
— получить список объектовkubectl get <kind> <name> -o wide
— выдает больше информации, в зависимости от kindkubectl get <kind> <name> -o yaml
— в виде yamlkubectl describe <kind> <name>
— текстовое описание + событияkubectl edit
— редактирование прямо в терминале любого ресурсаkubectl logs <pod_name>
— посмотреть логи подаkubectl port-forward
— пробросить порт из Kubernetes на локальный хостkubectl exec
— выполнить команду внутри запущенного контейнера
Список литературы и статей:
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
- https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/configure-cgroup-driver/
- https://kubernetes.io/docs/reference/kubectl/cheatsheet/
- https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet
- https://h963z57.com/?option=view&id_article=63
- https://github.com/geerlingguy/ansible-role-kubernetes/tree/master
- https://github.com/kubernetes-sigs/kubespray/blob/master/docs/ansible.md
- https://habr.com/ru/articles/508762
- https://habr.com/ru/companies/domclick/articles/682364
- https://www.linuxtechi.com/install-kubernetes-on-ubuntu-22-04
- https://www.linuxsysadmins.com/install-kubernetes-cluster-with-ansible
-
Выполнены изучение и разбор на практике всех компонентов Kubernetes
-
Установка k8s на двух узлах при помощи утилиты kubeadm
Если docker устанавливается для версии ОС Ununtu 16.04, то будет несовместимость с актуальной версией kubernetes 1.28.
[init] Using Kubernetes version: v1.28.2
[preflight] Running pre-flight checks
error execution phase preflight: [preflight] Some fatal errors occurred:
[ERROR CRI]: container runtime is not running: output: time="2023-10-03T11:35:19Z" level=fatal msg="validate service connection: CRI v1 runtime API is not implemented for endpoint \"unix:///var/run/containerd/containerd.sock\": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"
, error: exit status 1
[preflight] If you know what you are doing, you can make a check non-fatal with --ignore-preflight-errors=...
To see the stack trace of this error execute with --v=5 or higher
Если взять версию kube* бинарных файлов версий 1.19.04 и запустить на 16.04, возникает другая ошибка при инициализации с containerd сервисом, если добавить SystemdCgroups. Старая версия containerd docker'а для xenial не поддерживает этот параметр.
Несмотря на то, что в репозитории google https://packages.cloud.google.com/apt/dists/kubernetes-xenial доступны пакеты для Ubuntu 16.04, установка выполняется успешно и на Focal 20.04 и работает с версией 1.6.24 пакета containerd
Версии kubernetes 1.28 не зависят от dockershirm'а и поэтому можно только устанавливать containerd.io. Но для удобства использовался docker-machine, то отдельно роль docker'а не менялась, перенесена из gitlab'а, но является основной для проверки и установки зависимых компонентов для ВМ. А роль kubernetes требует роль docker (для проверки зависимостей).
https://kubernetes.io/docs/setup/production-environment/container-runtimes/
The Dockershim is the CRI compliant layer between the Kubelet and the Docker daemon. As part of the Kubernetes 1.20 release, the deprecation of the in-tree Dockershim was announced. For more information on the deprecation and its timelines, see the Kubernetes Dockershim Deprecation FAQ.
Note: Dockershim has been removed from the Kubernetes project as of release 1.24. Read the Dockershim Removal FAQ for further details.
Вариант с установкой ВМ для master и worker узлов аналогичная
YC_HOSTNAME="k8s-master"
yc compute instance create \
--name ${YC_HOSTNAME} \
--zone ru-central1-a \
--core-fraction 100 \
--cores 4 \
--memory 4GB \
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 \
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-2004-lts,size=40,type=network-ssd \
--ssh-key ~/.ssh/id_rsa-appuser.pub \
| awk '/nat:/ { getline; print $2}'
YC_HOST_IP=$(yc compute instance list --format json | jq ".[] | select (.name == \"${YC_HOSTNAME}\") | .network_interfaces[0].primary_v4_address.one_to_one_nat.address" | tr -d '"')
docker-machine rm -f ${YC_HOSTNAME}
docker-machine create \
--driver generic \
--generic-ip-address=$YC_HOST_IP \
--generic-ssh-user yc-user \
--generic-ssh-key ~/.ssh/id_rsa-appuser \
${YC_HOSTNAME}
docker-machine ls
eval $(docker-machine env ${YC_HOSTNAME})
docker-machine ssh ${YC_HOSTNAME}
containerd -version
tee "/etc/docker/daemon.json"<<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
apt-get update
apt-get install -y apt-transport-https ca-certificates curl bridge-utils wget vim net-tools
modprobe overlay
modprobe br_netfilter
tee /etc/modules-load.d/modules.conf << EOF
overlay
br_netfilter
EOF
tee /etc/sysctl.d/99-kubernetes.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
Если установка выполнялась через docker-machine, то добавление docker репозитория нет необходимости
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y containerd.io
mkdir -p /etc/containerd
Здесь необходимо продолжить
containerd config default | sudo tee /etc/containerd/config.toml
sed -i -r -e 's/(SystemdCgroup = )false/\1true/g' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable containerd
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update -y
sudo apt-get -y install kubelet=1.28.* kubeadm=1.28.* kubectl=1.28.*
sudo apt-mark hold kubelet kubeadm kubectl
sudo kubeadm config images pull --kubernetes-version v1.28.0
Если ранее была какая-то конфигурация, то можно сбросить настройки командой kubeadm
kubeadm reset -f
- Настройка kubernetes на master узле
curl 'https://api.ipify.org'
kubeadm init --apiserver-cert-extra-sans=158.160.126.171 --apiserver-advertise-address=0.0.0.0 --control-plane-endpoint=158.160.126.171 --pod-network-cidr=10.244.0.0/16
На актуальной версии kubernetes --apiserver-advertise-address вызвал ошибку
unknown flag: --apiserver-advertiseaddress
To see the stack trace of this error execute with --v=5 or higher
Подсмотрено здесь https://github.com/torgeirl/kubernetes-playbooks/blob/master/playbooks/master.yml и здесь https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/
cat > /etc/kubernetes/kubeadm-config.yaml << EOF
kind: ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta3
networking:
podSubnet: "10.244.0.0/16"
controlPlaneEndpoint: "158.160.126.171"
apiServer:
certSANs:
- "158.160.126.171"
---
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
runtimeRequestTimeout: "15m"
cgroupDriver: "systemd"
systemReserved:
cpu: 100m
memory: 350M
kubeReserved:
cpu: 100m
memory: 50M
enforceNodeAllocatable:
- pods
EOF
kubeadm init --config /etc/kubernetes/kubeadm-config.yaml
- Вывод команды выше: kubeadm init...
[init] Using Kubernetes version: v1.28.2
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
W1004 16:07:13.187465 11637 checks.go:835] detected that the sandbox image "registry.k8s.io/pause:3.6" of the container runtime is inconsistent with that used by kubeadm. It is recommended that using "registry.k8s.io/pause:3.9" as the CRI sandbox image.
...
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:
kubeadm join 158.160.126.171:6443 --token 1762id.yjr59ik6xf3oquog \
--discovery-token-ca-cert-hash sha256:3342eb8b2ae26622f8d2971d0686bcfe867ed6e82b24f5a9b21d9121c1c22ef2 \
--control-plane
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 158.160.126.171:6443 --token 1762id.yjr59ik6xf3oquog \
--discovery-token-ca-cert-hash sha256:3342eb8b2ae26622f8d2971d0686bcfe867ed6e82b24f5a9b21d9121c1c22ef2
- Копируется файл с ключами, чтобы от пользователя можно было выполнять команды
mkdir -p /home/yc-user/.kube
cp /etc/kubernetes/admin.conf /home/yc-user/.kube/config
chown yc-user:yc-user /home/yc-user/.kube/config
- Далее настройки выполняются от пользователя yc-user
su - yc-user
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
kubectl get nodes
- Необходимо повторить пункт 2 и для worker узла в другой консоли
YC_HOSTNAME="k8s-worker"
...
- По-умолчанию сети нет, поэтому необходимо установить pod flannel или calico
NAME STATUS ROLES AGE VERSION
k8s-master NotReady control-plane 12m v1.28.2
- Добавление манифеста pod сети flannel или ниже calico
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
- После установки на worker узел всех программ, выполнить необходимо добавление узла.
kubeadm join 158.160.126.171:6443 --token 1762id.yjr59ik6xf3oquog \
--discovery-token-ca-cert-hash sha256:3342eb8b2ae26622f8d2971d0686bcfe867ed6e82b24f5a9b21d9121c1c22ef2
Если ключ истек (24 часа), нужно воспользоваться командой
kubeadm token create --print-join-command
Вывод команды добавления:
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
- Добавление плагина сети calico
https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/onpremises
export CALICO_IPV4POOL_CIDR=10.244.0.0/16
kubectl apply -f https://projectcalico.docs.tigera.io/manifests/calico.yaml
по заданию нужно изменить параметр в ранее скачанном файле calico.yaml
wget https://projectcalico.docs.tigera.io/manifests/calico.yaml
sed -i -r -e 's/^([ ]+)# (- name: CALICO_IPV4POOL_CIDR)$\n/\1\2\n\1 value: "10.244.0.0\/16"/g' calico.yaml
kubectl apply -f calico.yaml
Вывод команды: kubectl apply -f calico.yaml
poddisruptionbudget.policy/calico-kube-controllers created
serviceaccount/calico-kube-controllers created
serviceaccount/calico-node created
serviceaccount/calico-cni-plugin created
configmap/calico-config created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
...
daemonset.apps/calico-node created
deployment.apps/calico-kube-controllers created
Вывод команды: kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 69m v1.28.2
k8s-worker Ready <none> 2m6s v1.28.2
Вывод команды: kubectl get pods -A -o custom-columns=NAME:.metadata.name,IP:.status.podIP,NAME:.spec.nodeName
NAME IP NAME
kube-flannel-ds-k8g9s 10.128.0.21 k8s-worker
kube-flannel-ds-vk856 10.128.0.24 k8s-master
calico-kube-controllers-7ddc4f45bc-lrdcn 10.244.0.4 k8s-master
calico-node-j84f7 10.128.0.21 k8s-worker
calico-node-m8nsg 10.128.0.24 k8s-master
coredns-5dd5756b68-29zxq 10.244.0.2 k8s-master
coredns-5dd5756b68-cl7ct 10.244.0.3 k8s-master
etcd-k8s-master 10.128.0.24 k8s-master
kube-apiserver-k8s-master 10.128.0.24 k8s-master
kube-controller-manager-k8s-master 10.128.0.24 k8s-master
kube-proxy-l8m4v 10.128.0.24 k8s-master
kube-proxy-xhqs7 10.128.0.21 k8s-worker
kube-scheduler-k8s-master 10.128.0.24 k8s-master
- Созданные ранее манифесты применяются kubectl get pods
NAME READY STATUS RESTARTS AGE
post-deployment-68db465f9c-lmcv7 1/1 Running 0 2m11s
...
- Выполнены различные варианты автоматической установки кластера k8s с помощью terraform и ansible.
Packer и ansible: в директории infra/packer располагается конфигуарция в hcl формате для подготовки "золотого" образа, а для установки ПО используется playbook infra/ansible/playbooks/k8s_install.yml. Данный playbook использует ранее уже созданный из предыдущего ДЗ установку docker'а и вторым шагом установку после kubernetes. Packer создает в последствии manifest файл infra/terraform/stage/packer.auto.tfvars.json, в котором есть id артификта образа. В terraform при создании ВМ можно его использовать, сделав замену в infra/terraform/modules/kubernetes/main.tf:
image_id = data.yandex_compute_image.img.id
Замечено, что если выполнять в облаке разворачивание ВМ с подготовленным диском, то необходмо в terraform внести изменения timeouts, так время занимает для network-ssd - 8 минут, для network-hdd - 15 минут, что превышает время по-умолчанию 5 минут. При использовании общедоступных образов - время составляет не более минуты, но на установку ПО ansible уходит то же время в итоге...
timeouts {
create = "30m"
delete = "2h"
}
boot_disk {
initialize_params {
image_id = var.image_id
...
}
}
Запустить packer можно из директории kuternetes/infra:
packer build packer/
Вывод выполнения создания образа можно найти в kubernetes/README.md
--> yandex.kubernetes: A disk image was created: k8s-base-20231004140023 (id: fd8vps8tcsm1qcec71f5) with family name k8s-base
+----------------------+-------------------------+----------+----------------------+--------+
| ID | NAME | FAMILY | PRODUCT IDS | STATUS |
+----------------------+-------------------------+----------+----------------------+--------+
| fd85atnjqrc498e0kgd3 | k8s-base-20231001095239 | k8s-base | f2em05j9ahdca5i8iltd | READY |
| fd8vps8tcsm1qcec71f5 | k8s-base-20231004140023 | k8s-base | f2e004c9e0g7t8b704b7 | READY |
+----------------------+-------------------------+----------+----------------------+--------+
- Terraform и ansible: В директории kubernetes создайте директории infra/terraform и infra/ansible располагаются все необходимые файлы для успешного разворачивания и настройки под ключ с нужным количеством ВМ master и worker-узлов. Их количество определяется переменной "instance_count" в infra/terraform/modules/kubernetes/variables.tf.
Запустить deployment можно из директории kuternetes/infra:
terraform -chdir=terraform/stage apply -auto-approve
Вывод выполнения установки кластера K8s можно найти в kubernetes/README.md
null_resource.run_ansible (local-exec): PLAY RECAP *********************************************************************
null_resource.run_ansible (local-exec): k8s-master-0 : ok=60 changed=38 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
null_resource.run_ansible (local-exec): k8s-worker-0 : ok=43 changed=27 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
null_resource.run_ansible: Creation complete after 8m10s [id=5347774438499733030]
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
Outputs:
kubernetes_image_id = "fd8ecgtorub9r4609man"
kubernetes_master_instance = [
[
"fhm8etrc5c5b4rjtvb2i",
],
[
"k8s-master-0",
],
]
kubernetes_master_ip_address = "158.160.126.55"
kubernetes_worker_instance = [
[
"fhmfmokv63v2pfcvlj1r",
],
[
"k8s-worker-0",
],
]
kubernetes_worker_ip_address = "158.160.99.62"
Статус кластера:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-163-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
New release '22.04.3 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
Last login: Wed Oct 4 13:50:45 2023 from 79.139.148.255
ubuntu@fhm8etrc5c5b4rjtvb2i:~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
fhm8etrc5c5b4rjtvb2i Ready control-plane 4m24s v1.28.2
fhmfmokv63v2pfcvlj1r Ready <none> 3m53s v1.28.2
ubuntu@fhm8etrc5c5b4rjtvb2i:~$ kubectl get pods -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-flannel kube-flannel-ds-bwngw 1/1 Running 0 4m39s 10.128.0.4 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-flannel kube-flannel-ds-mj9ng 1/1 Running 0 4m24s 10.128.0.9 fhmfmokv63v2pfcvlj1r <none> <none>
kube-system calico-kube-controllers-7ddc4f45bc-f7dwd 1/1 Running 0 4m39s 10.244.107.65 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-system calico-node-fhpqj 1/1 Running 0 4m39s 10.128.0.4 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-system calico-node-wbshm 1/1 Running 0 4m24s 10.128.0.9 fhmfmokv63v2pfcvlj1r <none> <none>
kube-system coredns-5dd5756b68-hhr4q 1/1 Running 0 4m39s 10.244.107.67 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-system coredns-5dd5756b68-ltnc4 1/1 Running 0 4m39s 10.244.107.66 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-system etcd-fhm8etrc5c5b4rjtvb2i 1/1 Running 0 4m52s 10.128.0.4 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-system kube-apiserver-fhm8etrc5c5b4rjtvb2i 1/1 Running 0 4m54s 10.128.0.4 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-system kube-controller-manager-fhm8etrc5c5b4rjtvb2i 1/1 Running 0 4m52s 10.128.0.4 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-system kube-proxy-gjqwx 1/1 Running 0 4m39s 10.128.0.4 fhm8etrc5c5b4rjtvb2i <none> <none>
kube-system kube-proxy-jk6w9 1/1 Running 0 4m23s 10.128.0.9 fhmfmokv63v2pfcvlj1r <none> <none>
kube-system kube-scheduler-fhm8etrc5c5b4rjtvb2i 1/1 Running 0 4m52s 10.128.0.4 fhm8etrc5c5b4rjtvb2i <none> <none>
Terraform на основании шаблона infra/terraform/templates/inventory.json.tpl создает инвентарный файл для ansible с указанием групп: masters и workers. Зарускаемый файл terraform'ом playbook - k8s_deploy.yml
- Если уже есть готовые ВМ, можно запускать раздельно установку k8s_install.yml, настройку *k8s_configure.yml и проверку k8s_check.yml
Из директории infra/ansible:
ansible-playbook playbooks/k8s_deploy.yml
В playbooks/k8s_configure.yml используется передача token'а для добавления worker-узлов
- name: Install K8s master base image
hosts: masters
...
register: join_command_raw
- debug:
msg:
- "{{ join_command_raw.stdout }}"
- name: Set join command
set_fact:
join_command: "{{ join_command_raw.stdout_lines[0] }}"
delegate_to: "{{ item }}"
delegate_facts: true
with_items: "{{ groups['workers'] }}"
- name: Configure K8s workers base image
hosts: workers
Ресурсы и статьи по Ansible помимо официального сайта, которые помогли с написанием playbook'ов:
- ansible-role-kubernetes
- ansible - сборник пьес
- starlingx/ansible-playbooks
- torgeirl/kubernetes-playbooks
- Install Kubernetes Cluster with Ansible on Ubuntu in 5 minutes
- работать с Terraform в yandex облаке
- A Simple Public IP Address API
- kubeadm Configuration (v1beta3)
- [List of all ansible_ssh_host in group](https://groups.google.com/g/ansible-project/c/TkDRbw1ques
- Ansible Debug
- Print list to separate lines
- Основы автоматизации в Ansible: роли и сценарии
- Создан Docker хост в Yandex Cloud и настроено локальное окружение на работу с ним Docker хост в Yandex Cloud и инициализировано окружение Docker, выполнен запуск Prometheus в контейнере
DOCKER_MACHINE="$(yc compute instance create \
--name docker-host \
--zone ru-central1-a \
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 \
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-2204-lts,size=15 \
--ssh-key ~/.ssh/id_rsa-appuser.pub \
| awk '/nat:/ { getline; print $2}')"
docker-machine create \
--driver generic \
--generic-ip-address=$(yc compute instance list --format json | jq '.[] | select (.name == "docker-host") | .network_interfaces[0].primary_v4_address.one_to_one_nat.address' | tr -d '"') \
--generic-ssh-user yc-user \
--generic-ssh-key ~/.ssh/id_rsa-appuser \
docker-host
docker-machine ls
eval $(docker-machine env docker-host)
docker run --rm -p 9090:9090 -d --name prometheus prom/prometheus
> docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
docker-host * generic Running tcp://158.160.123.87:2376 v24.0.2
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c42e3a0e47f9 prom/prometheus "/bin/prometheus --c…" 5 minutes ago Up 5 minutes 0.0.0.0:9090->9090/tcp, :::9090->9090/tcp prometheus
- В корне репозитория созданы файлы monitoring/prometheus/Dockerfile и monitoring/prometheus/prometheus.yml и там же собран сам образ и образы для микросервисного приложения
export USER_NAME=23f03013e37f
docker build -t $USER_NAME/prometheus monitoring/prometheus/
for i in ui post-py comment; do cd src/$i; bash docker_build.sh; cd -; done
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
23f03013e37f/prometheus latest 0ff4f47eb4aa 8 minutes ago 112MB
23f03013e37f/ui 1.0 10ca006c2cd7 26 minutes ago 482MB
23f03013e37f/post 1.0 0c9b91c66d82 28 minutes ago 292MB
23f03013e37f/comment 1.0 3837a3a99d37 28 minutes ago 313MB
<none> <none> e930e723c43c 4 hours ago 313MB
prom/prometheus latest 9c703d373f61 2 weeks ago 245MB
mongo 4.4.24 a701426e0e61 4 weeks ago 432MB
- Добавлен сервис prometheus'а в docker/docker-compose.yml и удалены build инструкции, так как образы были собраны выше
- Выполнен запуск сервисов
docker-compose up -d
- Ниже список endpoint'ов
- Проверка работы
-
В файлы docker/docker-compose.yml и monitoring/prometheus/prometheus.yml внсенена информация по новому сервису - node-exporter. Перезапущены сервисы.
-
В списке Status/Targets'ов новый узел
- Ведется сбор данных
- Проверка мониторинга
- Образы загружены на DockerHub и доступны по ссылкам
https://hub.docker.com/repository/docker/23f03013e37f/ui https://hub.docker.com/repository/docker/23f03013e37f/comment https://hub.docker.com/repository/docker/23f03013e37f/post https://hub.docker.com/repository/docker/23f03013e37f/prometheus
Добавить в Prometheus мониторинг MongoDB с использованием необходимого экспортера
- Для добавления в Prometheus мониторинга ДБ MongoDB был выбран данный актуальный проект
- В docker/docker-compose.yml файл добавлен сервис:
mongodb-exporter:
image: percona/mongodb_exporter:0.39.0
command:
- '--mongodb.uri=mongodb://post_db:27017'
networks:
- back_net
- В файл monitoring/prometheus/prometheus.yml добавлен endpoint:
- job_name: 'mongodb-node-exporter'
static_configs:
- targets:
- 'mongodb-exporter:9216'
Добавить в Prometheus мониторинг сервисов comment, post, ui с помощью blackbox exporter.
- В директории monitoring/blackbox созданы файлы настроек blackbox.yml с модулем http_2xx и Dockerfile
FROM prom/blackbox-exporter:latest
COPY ./blackbox.yml /etc/blackbox_exporter/config.yml
modules:
http_2xx:
prober: http
timeout: 5s
http:
valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
valid_status_codes: []
method: GET
follow_redirects: false
- В файл prometheus.yml добавлен блок работы
- job_name: 'blackbox'
metrics_path: /metrics
params:
module: [http_2xx]
static_configs:
- targets:
- http://prometheus.io
- http://ui:9292/new
- http://comment:9292
- http://post:9292
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:9115
-
В файл docker/docker-compose.yml добавлен сервис blackbox-exporter
-
В файл monitoring/prometheus/prometheus.yml добавлен target для Cloudprober
-
blackbox запустился в Prometheus
Напиcать Makefile
- В корне репозитария создан файл Makefile
- Возможные варианты запуска:
- для получения списка команд
make help
- создания образов
make build-[ui|post|comment|prometheus|blackbox]
- push образов в docker.io
make push-[ui|post|comment|prometheus|blackbox]
- сборка или отправка всех образов
make [build|push]-all
- Подготовлен образ с помощью packer'а. Результатом работы является образ с предустановленным docker и docker-compose и другими пакетами, где на выходе post-processor "manifest" создается 'manifest' файл, который содержит image_id.
{
"builds": [
{
"artifact_id": "fd8k6q7qq7sghiuc7h67"
}
]
}
- terraform использует данный 'manifest' файл terraform/stage/packer.auto.tfvars.json, как variables, используя artifact_id обзаза.
resource "null_resource" "image_id" {
triggers = {
image_value = split(":", element(var.builds, 0).artifact_id)[0]
}
}
module "docker" {
...
docker_image_id = resource.null_resource.image_id.triggers.image_value
}
variable "builds" {
type = list(
object(
{
artifact_id = string
}
)
)
description = "List of images, as generated by Packer's 'Manifest' post-processor."
}
И для раскатки 'gitlab-ci' docker'а генерирует из динамического шаблона inventory.json.tpl для ansible инвентори с внешним IP-адресом, который будет использоваться при установке контейнера 'gitlab-ce:latest', как переменная в шаблоне docker-compose.yml.j2 и для пункта со <*> в модуле docker_container. Так же предусмотрена в ansible проверка gitlab контейнера и вывод пароля иницализации. Playbook'и организованы с ролями для docker и gitlab.
-
Чтобы решить ошибку недоступности подключения ansible к установленной виртуальной машине сразу. использовался дополнительные модуль 'ansible.builtin.wait_for_connection' и проверкой доступности docker контейнера.
-
Установка решения выполняется двумя командами:
packer build packer/
terraform -chdir=terraform/stage/ apply -auto-approve
См. лог gitlab-ci/infra/packer-ansible.md и gitlab-ci/infra/terraform-ansible.md
- Можно отдельно запускать сам ansible-playbook
ansible-playbook -i ../terraform/stage/inventory.json playbooks/gitlab.yml
- Проверка работы докера можно выполнить по ssh:
ssh -l ubuntu -i ~/.ssh/id_rsa-appuser 158.160.60.194 docker inspect -f {{.State.Health.Status}} gitlab
healthy
- Для получения пароля можно воспользоавться прочтением файла или через ansible или output.tf из terraform
ssh -l ubuntu -i ~/.ssh/id_rsa-appuser 158.160.60.194 sudo grep -i 'password:' /srv/gitlab/config/initial_root_password
- Если пароль забыт, можно сбросить
https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password
docker exec -it gitlab bash
gitlab-rake 'gitlab:password:reset[root]'
-
После подключения к Gitlab web сайту, выполнено отключение регистрации.
-
Добавлена группа homework, репозиторий example, выполнены пункты задания 4.
-
Зарегистрирован Runner и получен token.
Проверить, что "Run untagged jobs" установлен. Indicates whether this runner can pick jobs without tags
gitlab-runner register --url http://158.160.60.194 --token glrt-CXJApPiVyyGssWMxWVVD
- Добавлен Runner (и как установка в playbook gitlab.yml)
docker run -d --name gitlab-runner --restart always -v /srv/gitlab-runner/config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ea79446f5691 gitlab/gitlab-runner:latest "/usr/bin/dumb-init …" 6 minutes ago Up 6 minutes gitlab-runner
2710b85663fa gitlab/gitlab-ce:latest "/assets/wrapper" 2 hours ago Up 2 hours (healthy) 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:2222->22/tcp gitlab
- Регистрация Runner'а
docker exec -it gitlab-runner gitlab-runner register \
--url http://158.160.60.194/ \
--non-interactive \
--locked=false \
--name DockerRunner \
--executor docker \
--docker-image alpine:latest \
--registration-token glrt-CXJApPiVyyGssWMxWVVD \
--tag-list "linux,xenial,ubuntu,docker" \
--run-untagged
Runtime platform arch=amd64 os=linux pid=16 revision=4e724e03 version=16.4.0
Running in system-mode.
...
Verifying runner... is valid runner=CXJApPiVy
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
- Добавлен Reddit в проект, изменен файл .gitlab-ci.yml и добавлен файл simpletest.rb
git clone https://github.com/express42/reddit.git && rm -rf ./reddit/.git
git add .
git commit -m "Add reddit app"
git push gitlab gitlab-ci-1
-
Пройдены тесты в pipeline'е пунктов ДЗ 6.2
-
Добавлены окружения dev, staging, production и выполнены их проверки пунктов ДЗ 7.X
- Проверены условия, ограничения ручной запуск пунктов ДЗ 8.X
- Проверено добавление динамических окружений пунктов ДЗ 9.X
- Автоматизацию развёртывания GitLab Runner можно выполнить посредством ansible playbook playbooks/gitlab-runner.yml
https://docs.ansible.com/ansible/latest/collections/community/general/gitlab_runner_module.html
Только token необходимо указывать при запуске playbook'а, так как получение возможно(?) только с web-интерфейса.
- Настройка уведомлений из Gitlab в Slack В Slack была настроена новый Workspace otus-devops-2023-05 и в настройках получены token'ы
Заполнение полей в Gitlab
http://gitlab/admin/application_settings/general#js-slack-settings
https://computingforgeeks.com/gitlab-and-slack-integration-for-notifications/ Далее необходимо настроить webhook и добавить его к Gitlab проекту https://docs.gitlab.com/ee/ci/chatops/index.html
Получение ссылки для webhook'а
Добавление уведомлений в Gitlab для Slack
Получение уведомления о merge ветки gitlab-ci-1-1 в main
- Запуск reddit в контейнере
https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
docker exec -it gitlab-runner gitlab-runner register -n --url http://158.160.60.194 --name MyDockerRunner --registration-token "glrt-CXJApPiVyyGssWMxWVVD" --executor docker --docker-image "docker:19.03.12" --docker-privileged --docker-volumes "/certs/client"
При использовании версии 19.03.12 возникает ошибка 'cgroups: cgroup mountpoint does not exist: unknown'
build_job:
stage: build
image: docker:20.10.17
variables:
DOCKER_TLS_CERTDIR: "/certs"
services:
- docker:20.10.17-dind
before_script:
- docker info
script:
- echo 'Building'
- docker build -t reddit-docker-image .
- docker run reddit-docker-image
only:
- branches
except:
- master
- main
Прохождение этапа формирования образа docker-in-docker (dind)
- Подключение в облаке docker host'у
DOCKER_MACHINE="$(yc compute instance create \
--name docker-host \
--zone ru-central1-a \
--core-fraction 5 \
--cores 2 \
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 \
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1804-lts,size=15 \
--ssh-key ~/.ssh/id_rsa-appuser.pub \
| awk '/nat:/ { getline; print $2}')"
docker-machine create \
--driver generic \
--generic-ip-address=$DOCKER_MACHINE \
--generic-ssh-user yc-user \
--generic-ssh-key ~/.ssh/id_rsa-appuser \
docker-host
docker-machine ls
eval $(docker-machine env docker-host)
- Проверка различных вариантов настройки сетей:
for i in none host; do docker run -ti --rm --network $i joffotron/docker-net-tools -c ifconfig; done
- Сравнить с выводом команды ifconfig docker-host'а невозможно, так как нет утилиты, можно доустановить net-tools
$ docker-machine ssh docker-host ifconfig
bash: ifconfig: command not found
exit status 127
$ docker-machine ssh docker-host which ifconfig
exit status 1
$ docker-machine ssh docker-host which ip
/sbin/ip
$ docker-machine ssh docker-host ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether d0:0d:43:72:59:ac brd ff:ff:ff:ff:ff:ff
inet 10.128.0.18/24 brd 10.128.0.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::d20d:43ff:fe72:59ac/64 scope link
valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:28:3b:9c:07 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
- После запуска несколько раз команды
docker run --network host -d nginx
docker ps выдает 4 новых экземпляра контейнеров с одного образа с разными именами, но если будет условие использование порта, напрмер, -p 8080:80, то последующие запуски будут неудачны, так как порт будет занят 6. Создана ссылка на docker-host'е машине
$ docker-machine ssh docker-host sudo ln -s /var/run/docker/netns /var/run/netns
$ docker-machine ssh docker-host ls -Al /var/run/netns
lrwxrwxrwx 1 root root 21 Sep 15 13:57 /var/run/netns -> /var/run/docker/netns
- Повторили 5-й пункт с указанием сетей: none и host. Список изменений namespace'ов . При использовании сети None есть сети на каждый экземпляр:
$ docker-machine ssh docker-host sudo ip netns
7c592d309a71
8ccb91432f3e
95595d213eee
c98d7766dd7a
default
А при host получаем только один экземпляр, так как нет возможности запустить из-за конфликта использованного порта в одном net-namespace'е:
$ docker run -it --network host nginx
/docker-entrypoint.sh: Configuration complete; ready for start up
2023/09/15 14:11:47 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
...
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a56ecab5ee9c nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes inspiring_burnell
$ docker-machine ssh docker-host sudo ip netns
default
- Создана по-умолчанию bridge-сеть в docker и проверена недоступность сервисов по DNS-именам, которые прописанны в ENV-переменных Dockerfile'ов. Проверено, что с указанием при запуске имен --name=comment, --name=post всё равно не работает, если не прописать два имени к docker'у БД. Поэтому подключение к MongoDB нужно указывать с переменной, например для comment docker'а -e COMMENT_DATABASE_HOST=mongo_db или использовать --network-alias, как в предыдущем ДЗ.
- Созданы дополнительно две сети: front_net и back_net и проверена работа запуска контейнеров в разных сетях.
docker network create back_net --subnet=10.0.2.0/24
docker network create front_net --subnet=10.0.1.0/24
- До момента подключения серверов post и comment к сетям, у серверов появляется дополнительный интерфейс к этим сетям - eth1, таким образом сервер "становится" двухногим.
$ docker exec -it comment ip addr
...
41: eth1@if42: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:0a:00:01:04 brd ff:ff:ff:ff:ff:ff
inet 10.0.1.4/24 brd 10.0.1.255 scope global eth1
valid_lft forever preferred_lft forever
- Рассмотрен сетевой стек bridge network driver'а с доустановкой bridge-utils и net-tools
yc-user@docker-host:~$ for i in $(ifconfig | grep br- | cut -d":" -f1); do brctl show $i; done
bridge name bridge id STP enabled interfaces
br-6dfa6a0eeb0b 8000.02428bf36707 no
bridge name bridge id STP enabled interfaces
br-ca3da3d4c146 8000.0242c218da7c no
bridge name bridge id STP enabled interfaces
br-f3669bbb7452 8000.0242b16ce30c no veth52e1e2e
vetha8e49f0
vethb32d61a
vethfab4bc2
$ ps ax | grep docker-proxy
10741 ? Sl 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 9292 -container-ip 10.0.1.2 -container-port 9292
10747 ? Sl 0:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 9292 -container-ip 10.0.1.2 -container-port 9292
$ sudo iptables -nL -t nat
...
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 10.0.1.0/24 0.0.0.0/0
MASQUERADE all -- 10.0.2.0/24 0.0.0.0/0
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE tcp -- 10.0.1.2 10.0.1.2 tcp dpt:9292
- Установлен docker-compose на локальную машину и собрагы образы приложения reddit
- Приложение reddit с помощью docker-compose запускается
$ docker ps
CONTAINER ID COMMAND CREATED STATUS PORTS NAMES
a942fd193317 "docker-entrypoint.s…" 7 minutes ago Up 6 minutes 27017/tcp src-post_db-1
a30c3ff2ba3b "puma" 7 minutes ago Up 6 minutes src-comment-1
babc751f2789 "python3 post_app.py" 7 minutes ago Up 6 minutes src-post-1
b713005ef641 "puma" 7 minutes ago Up 6 minutes 0.0.0.0:9292->9292/tcp, :::9292->9292/tcp src-ui-1
- Файл .env внесён в .gitingore, выполнена копия в .env.exmaple
- Изменён docker-compose.yml для случая использования несколько bridge сетей, сетевых псевдонимов
- Ответ на вопрос, что все создаваемые docker-compose сущности имеют одинаковый префикс и формирование имен контейнеров зависит от нескольких факторов:
-
имя сервиса: Имя контейнера обычно базируется на имени сервиса, определенного в файле docker-compose.yml. Например, если сервис с именем web, Docker Compose будет использовать это имя в формировании имени контейнера.
-
префикс проекта: Docker Compose автоматически добавляет префикс, основанный на именовании проекта Docker Compose. Если не явно указано имя проекта с помощью флага -p или переменной окружения COMPOSE_PROJECT_NAME, Docker Compose использует имя директории, где находится файл docker-compose.yml, как имя проекта. Префикс проекта добавляется к именам контейнеров, чтобы обеспечить уникальность, если вы используете несколько проектов.
-
суффикс номера инстанса (по умолчанию): Если не задано явное имя контейнера с помощью container_name в файле docker-compose.yml, Docker Compose добавит уникальный номер инстанса в конце имени контейнера. Например, если у вас есть сервис с именем web и два контейнера этого сервиса, их имена могут выглядеть как myproject-web-1 и myproject-web-2, где myproject - это имя проекта, web - имя сервиса, а 1 и 2 - номера инстансов.
- Явное имя контейнера, если указано: Если явно указано имя контейнера с помощью параметра container_name в файле docker-compose.yml, то будет использоваться это имя вместо автоматически сгенерированного.
- Создан docker-compose.override.yml
- Для изменения настроек приложения, возможно выполнить подключечния тома, как и для БД.
docker-machine ssh docker-host sudo ls -Al /var/lib/docker/volumes/src_ui_app/_data
Результат:
total 48
-rw-r--r-- 1 root root 396 Sep 13 09:01 config.ru
...
- Для запуска puma приложений в режиме диагностики с двумя процессами --debug и -w 2 можно добавить command: puma --debug -w 2
docker ps
Результат:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f441fae2fbcd 23f03013e37f/ui:1.0 "puma --debug -w 2" 9 seconds ago Up 4 seconds 0.0.0.0:8080->9292/tcp, :::8080->9292/tcp src-ui-1
...
```bash
docker logs $(docker ps -a -q --filter "name=src-ui-1") | head -5
Результат:
[1] Puma starting in cluster mode...
[1] * Version 3.10.0 (ruby 2.3.1-p112), codename: Russell's Teapot
[1] * Min threads: 0, max threads: 16
[1] * Environment: development
[1] * Process workers: 2
- Установлен hadolint'ер из AUR и проверены Dockerfile'ы ui/post/comment, здесь по рекомендациям DL3020 внесены изменения
- Выполнены подготовка и подключение к ранее созданному Docker host’у
- Организована новая структура приложения из архива https://github.com/express42/reddit/archive/microservices.zip в папке src
- Внесены изменения в Dockerfile'ы:
- для ui добавлена версия к RUN
gem install bundler -v 2.3.26
- для comment:
- изменен источник образа на ruby:2.2.10-alpine
- для исправления ошибки со сборкой Requested MarkupSafe>=2.0 заменён ADD на COPY
FROM python:3.6.0-alpine
WORKDIR /app
COPY VERSION *.py requirements.txt VERSION ./
RUN apk --no-cache --update add build-base \
&& pip install --upgrade pip \
&& pip install -r /app/requirements.txt
...
- для post добавлено обновление pip
dockerfile pip install --upgrade pip
- К запуску контейнера ДБ установлена версия для совместимости со старыми версиями приложений
docker run -d \
--network=reddit --network-alias=post_db --network-alias=comment_db \
--volume reddit_db:/data/db \
mongo:4.4.24
- Все контейнеры собраны, сеть и том reddit_db для ДБ добавлены для сохранения потсов, прилоежния успешно запущены и проверена работоспособность http://<docker-host-ip>:9292/
DOCKER_MACHINE="$(yc compute instance create \
--name docker-host \
--zone ru-central1-a \
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 \
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1804-lts,size=15 \
--ssh-key ~/.ssh/id_rsa-appuser.pub \
| awk '/nat:/ { getline; print $2}')"
docker-machine create \
--driver generic \
--generic-ip-address=$DOCKER_MACHINE \
--generic-ssh-user yc-user --generic-ssh-key ~/.ssh/id_rsa-appuser \
docker-host
docker-machine ls
eval $(docker-machine env docker-host)
- Оптимизация образа образа ui
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ui 3.0 6eeb034379b9 3 minutes ago 225MB
ui 1.0 b7eb7bded98b 44 minutes ago 482MB
- Перезапущены контейнеры контейнеры с другими сетевыми алиасами с префиксом "test_", необходмо указать новые ENV в команде
docker run -d --network=reddit --network-alias=test_post_db --network-alias=test_comment_db -v reddit_db:/data/db mongo:4.4.24
docker run -d --network=reddit --network-alias=test_post -e POST_DATABASE_HOST=test_post_db post:1.0
docker run -d --network=reddit --network-alias=test_comment -e COMMENT_DATABASE_HOST=test_comment_db comment:1.0
docker run -d --network=reddit -p 9292:9292 -e POST_SERVICE_HOST=test_post -e COMMENT_SERVICE_HOST=test_comment ui:1.0
- Создан Dockerfile.01 для ui образ на основе Alpine, как и comment
docker build -t <dockerhub-login>/ui:3.0 ./ui --file ui/Dockerfile.01
- Запуск контейнера mongo с volume reddit_db сохраняет данные ДБ в папке /var/lib/docker/volumes/reddit_db/_data и после перезапуска пост остается доступен на портале
- Создан репозиторий docker-2 и устанавлены docker и docker-tools
- Создан новый проект docker
- Создан там docker-host с помощью docker-machine
- Выполнено подключение к удаленному docker instance в docker-hub
- В docker-1.log внесена информация по выполнению реализации в виде прототипа в директории /docker-monolith/infra/:
-
packer в связке с ansible, файл docker.json - шаблон пакера, который создает с ansible playbook образ в облаке.
-
terraform со структурой terraform/stage и модулей modules/docker,
-
ansible с учетом ролей и playbooks для docker terraform модуля
- В файле docker-2-star.log приводится результат создания инфрастуктуры с помошью terraform'а и ранее созданного packer'ом образа, где используется счётчик (их количество задается переменной):
- Изменен outputs.tf так, что можно создавать различные ansible inventory: INI, YAML, JSON
- Добавлен для автоматизации provisioners для уставноки docker'а reddit в облачные docker'ы в ЯОблаке с учетом сгенерированного в output динамического интентори.
- результат выполнения в
- В файле docker-2-additional-star.log лог автоматизиванного выполения за раз развертывания docker'ов по счётчику, а так же сразу выполнение установки и запуска приложений в контейнерах.
- Добавлены null_resource'ы для запуска ansible playbook'а