Skip to content

aasdhajkshd microservices repository

License

Notifications You must be signed in to change notification settings

Otus-DevOps-2023-05/aasdhajkshd_microservices

Repository files navigation

aasdhajkshd_microservices

aasdhajkshd microservices repository

INFO Информация на картинках, как IP адреса, порты или время, может отличаться от приводимой в тексте.


Содержание


Выполненные работы


Подготовка окружения 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 файла.

Логирование Docker-контейнеров

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

Cбор структурированных логов

Reference Reference

Визуализация

Reference

Сбор неструктурированных логов

Reference

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

Рузультат:

Reference

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] ___________

Reference

Распределенный трейсинг

Reference

Задание со *

Загружено приложение, собрано с tag'ом bugged. Для тестирования подготовлен docker/docker-commpse-apps.yml. Выполнен запуск и проведен анализ запросов.

Если взять значения по умолчанию, то задржка из-за неверного порта по-умолчанию: 4567 Reference

Как выясняется, проблема подключении на порт сервиса post отдается ошибка 500, если в UI указывается ENV на порт 5000 для POST.

Reference Reference


Список литературы и статей:

Выполненные работы


Minikube

  1. Установка выполнена 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...

Reference

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


Задание со *

  1. Разворачивание Managed Service for Kubernetes было выполнено в рамках проектной работы. Настройка terraform манифестов выполнялась по статье Развертывание и управление организацией и правами доступа через IaC terraform и Создайте кластер Managed Service for Kubernetes

  2. 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

Managed Kubernetes Dashboard

Список литературы и статей:


Выполненные работы


Helm

  1. Установка

Выполнена установка 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'ом.

  1. 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
  1. 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
  1. Управление зависимостями

При выполнении команды:

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

Результат:

GitLab + Kubernetes

  1. Установка и внесение изменений в конфигурационные файлы
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).

Установка 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

Reddit Deploy

Инициализация проекта в репозиторий 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)

Gitlab Runner

Настройки Runner

Нужно прописать полученный 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)"

Результат:

23f03013e37f/post:0.0.2

Для заруска задачи (Job) в отдельном Kubernetes POD-е доавлены атрибуты tags в CI/CD Runner и в .gitlab-ci.yml

Пайплайн здорового человека для ui можно посмотреть здесь, рабочий вариант... для reddit-deploy.

Чтобы запустить CI/CD в Environment: staging, production нужно подключить кластер Kubernetes к Gitlab,

Поэтому выполнить настройку аутентификацию Kubernetes в GitLab необходимо.

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

Operate/Kubernetes clusters

Operate/Kubernetes clusters

Установка 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:

Enable Review Apps

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"

Types of environments

Чтобы заработала связка Gitlab и Kubernetes, полезно воспользоваться этой статьей, в Envrironment добавил KUBE_CONFIG из ~/.kube/config и при выполнении docker'ом в CI Pipeline'е можно было увидеть context, который был выставлен:

Kubernetes Context Agent

Далее выяснилось, что используемый 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

Далее задать своего агента можно и через переменную:

Kube Context Variable

Все остальные действия уже чисто дело техники, сам процесс уже понятен... :-)

Альтернатива агенту по схеме pull - Flux

Файлы .gitlab-ci.yml скопированы из Gitlab_ci в папку src/{ui,post-py,comment} и для reddit-deploy - в charts.


Список литературы и статей:

Абстракции над подами

  • Deployment — инструмент контроля за состоянием подов
  • ReplicaSet — запускает несколько подов
  • DaemonSet — запускает строго один под на каждом узле кластера
  • StatefulSet — запускает нумерованные поды для stateful-приложений
  • Job — запускает под один раз, пока он не завершится успешно
  • CronJob — запускает Job по крону (расписанию)

Так как на странице 24 домашнего задания представлена картинка управлеяемого кластера, выполнение пунктов выполнялась с установкой Yandex Managed Service для Kubernetes кластере infra-kube.


Выполненные работы


Создание управляемого кластера для Kubernetes в YC

  1. Настройка групп безопасности
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'
  1. Запишем в переменную <id группы безопасности>:
export SG_ID=$(yc vpc security-group get --name yc-security-group | head -1 | awk '{print $2}')
echo $SG_ID
  1. Создадим сервисный аккаунт для кластера 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
  1. Создаём публичный зональный кластер в зоне 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
  1. Создаём рабочую группу из одного узла:

С помощью флага --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"
  1. Получаем конфигигурацию и выполняем подключение:
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
  1. Проверяем, что доступ есть и нода создалась:
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'.
  1. Конфигурация контекста
kubectl config current-context

Результат:

yc-kube-infra
  1. Запустим 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

После:

Pod ExternalName

Результат:

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

DNS в Kubernetes

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.

  1. "Отключение" службы 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
  1. Проверка недоступности 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
  1. Проверка с доступностью 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 объединяет несколько подов в пул и даёт единый сетевой доступ к этому пулу. Это решает проблему непостоянства адреса одиночных подов

Сетевые абстракции

  1. 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

  1. LoadBalancer
spec:
  type: LoadBalancer

INFO При создании сервиса типа LoadBalancer, контроллер Yandex Cloud создает и настраивает сетевой балансировщик нагрузки в вашем каталоге с публичным IP-адресом.

Network Load Balancer

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

  1. Ingress и TLS Termination

Ingress Сontroller — один из важнейших компонентов кластера Kubernetes. Через него проходят практически все внешние запросы к кластеру. Для стандартного Nginx Ingress Controller входящий трафик обрабатывается Nginx, который развёрнут внутри кластера и распределяет запросы по сервисам типа ClusterIP.

Для выполнения домашнего задания были рассмотрены два Ingress Сontroller:

Для настройки 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 |
+--------+-----------------+------+----------------+-----+

Пример Cloud DNS

Получить сертификат и ключ из облака

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

Пример NLB Inginx Controller'а

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

Приложение на 443 порту

Задание со *

Secret

---
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

Network Policy

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
  1. Применён файл mongo-network-policy.yml
kubectl get networkpolicies.networking.k8s.io -n dev

Результат:

NAME              POD-SELECTOR                 AGE
deny-db-traffic   app=reddit,component=mongo   25m
  1. Для проверки использовался pod ui
kubectl exec -ti -n dev ui-cf9f76bc-8qwr4 -- telnet mongo 27017
Trying 10.96.241.0...
^C
  1. Для обеспечения доступности:
    - 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 '^]'.
^]

Хранилище для базы

  1. тип Volume emptyDir
  volumeMounts:
    - name: mongo-persistent-storage
      mountPath: /data/db
volumes:
  - name: mongo-ps
    emptyDir: {}

Сообщения удаляются при перезагрузке или удалении pod'а с ДБ

  1. Динамическая подготовка тома

В большинстве случаев нет необходимости вручную создавать объекты 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
  1. Статический том

При использовании статического диска, необходимо к 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

  1. Тестирование

Удаляем диск и создаем новый, после создаем новый, прописываем в 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

Результат:

Статус PV

Статус PVC

После удаления и создания mongo Deployment все сообщения сохранены:

Добавление сообщения

kubectl delete -n dev -f mongo-deployment.yml

kubectl apply -n dev -f mongo-deployment.yml

Статус mongo

Реузльтат:

Сообщения сохранились

На картинках ниже будет видно статус использования диска

Утилизация диска

kubectl cheatsheet

Полезные команды kubectl, которые могут пригодиться при работы с кластером:

  • kubectl apply -f — применить манифесты
  • kubectl get <kind> — получить список объектов
  • kubectl get <kind> <name> -o wide — выдает больше информации, в зависимости от kind
  • kubectl get <kind> <name> -o yaml — в виде yaml
  • kubectl describe <kind> <name> — текстовое описание + события
  • kubectl edit — редактирование прямо в терминале любого ресурса
  • kubectl logs <pod_name> — посмотреть логи пода
  • kubectl port-forward — пробросить порт из Kubernetes на локальный хост
  • kubectl exec — выполнить команду внутри запущенного контейнера

Если что-то не получается, может это поможет? Troubleshooting Kubernetes

Список литературы и статей:


Выполненные работы


  1. Выполнены изучение и разбор на практике всех компонентов Kubernetes

  2. Установка 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
  1. Настройка 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
  1. Вывод команды выше: 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
  1. Копируется файл с ключами, чтобы от пользователя можно было выполнять команды
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
  1. Далее настройки выполняются от пользователя yc-user
su - yc-user
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc

kubectl get nodes
  1. Необходимо повторить пункт 2 и для worker узла в другой консоли
YC_HOSTNAME="k8s-worker"
...
  1. По-умолчанию сети нет, поэтому необходимо установить 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
  1. После установки на 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.
  1. Добавление плагина сети 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
  1. Созданные ранее манифесты применяются kubectl get pods
NAME                               READY   STATUS    RESTARTS   AGE
post-deployment-68db465f9c-lmcv7   1/1     Running   0          2m11s
...

Задания со star star

  1. Выполнены различные варианты автоматической установки кластера 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  |
+----------------------+-------------------------+----------+----------------------+--------+
  1. 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

  1. Если уже есть готовые ВМ, можно запускать раздельно установку 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'ов:


Выполненные работы

  1. Создан 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
  1. В корне репозитория созданы файлы 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
  1. Добавлен сервис prometheus'а в docker/docker-compose.yml и удалены build инструкции, так как образы были собраны выше
  2. Выполнен запуск сервисов
docker-compose up -d

Мониторинг состояния микросервисов

  1. Ниже список endpoint'ов

  1. Проверка работы

Сбор метрик хоста

  1. В файлы docker/docker-compose.yml и monitoring/prometheus/prometheus.yml внсенена информация по новому сервису - node-exporter. Перезапущены сервисы.

  2. В списке Status/Targets'ов новый узел

  1. Ведется сбор данных

  1. Проверка мониторинга

  1. Образы загружены на 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

Задания со star

Добавить в Prometheus мониторинг MongoDB с использованием необходимого экспортера

  1. Для добавления в Prometheus мониторинга ДБ MongoDB был выбран данный актуальный проект

https://hub.docker.com/r/percona/mongodb_exporter

  1. В docker/docker-compose.yml файл добавлен сервис:
  mongodb-exporter:
    image: percona/mongodb_exporter:0.39.0
    command:
      - '--mongodb.uri=mongodb://post_db:27017'
    networks:
      - back_net
  1. В файл monitoring/prometheus/prometheus.yml добавлен endpoint:
  - job_name: 'mongodb-node-exporter'
    static_configs:
      - targets:
        - 'mongodb-exporter:9216'
  1. Результат доступных mongodb метрик

Задания со star

Добавить в Prometheus мониторинг сервисов comment, post, ui с помощью blackbox exporter.

  1. В директории 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
  1. В файл 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
  1. В файл docker/docker-compose.yml добавлен сервис blackbox-exporter

  2. В файл monitoring/prometheus/prometheus.yml добавлен target для Cloudprober

  3. blackbox запустился в Prometheus

Задания со star

Напиcать Makefile

  1. В корне репозитария создан файл Makefile
  2. Возможные варианты запуска:
  • для получения списка команд
make help
  • создания образов
make build-[ui|post|comment|prometheus|blackbox]
  • push образов в docker.io
make push-[ui|post|comment|prometheus|blackbox]
  • сборка или отправка всех образов
make [build|push]-all

Выполненные работы

  1. Подготовлен образ с помощью packer'а. Результатом работы является образ с предустановленным docker и docker-compose и другими пакетами, где на выходе post-processor "manifest" создается 'manifest' файл, который содержит image_id.
{
  "builds": [
    {
      "artifact_id": "fd8k6q7qq7sghiuc7h67"
    }
  ]
}
  1. 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.

  1. Чтобы решить ошибку недоступности подключения ansible к установленной виртуальной машине сразу. использовался дополнительные модуль 'ansible.builtin.wait_for_connection' и проверкой доступности docker контейнера.

  2. Установка решения выполняется двумя командами:

packer build packer/
terraform -chdir=terraform/stage/ apply -auto-approve

См. лог gitlab-ci/infra/packer-ansible.md и gitlab-ci/infra/terraform-ansible.md

  1. Можно отдельно запускать сам ansible-playbook
ansible-playbook -i ../terraform/stage/inventory.json playbooks/gitlab.yml
  1. Проверка работы докера можно выполнить по ssh:
ssh -l ubuntu -i ~/.ssh/id_rsa-appuser 158.160.60.194 docker inspect -f {{.State.Health.Status}} gitlab
healthy
  1. Для получения пароля можно воспользоавться прочтением файла или через 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
  1. Если пароль забыт, можно сбросить

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]'
  1. После подключения к Gitlab web сайту, выполнено отключение регистрации.

  2. Добавлена группа homework, репозиторий example, выполнены пункты задания 4.

  3. Зарегистрирован 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
  1. Добавлен 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
  1. Регистрация 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"
  1. Добавлен 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
  1. Пройдены тесты в pipeline'е пунктов ДЗ 6.2

  2. Добавлены окружения dev, staging, production и выполнены их проверки пунктов ДЗ 7.X

  1. Проверены условия, ограничения ручной запуск пунктов ДЗ 8.X

  1. Проверено добавление динамических окружений пунктов ДЗ 9.X

Задания со star

  1. Автоматизацию развёртывания GitLab Runner можно выполнить посредством ansible playbook playbooks/gitlab-runner.yml

https://docs.ansible.com/ansible/latest/collections/community/general/gitlab_runner_module.html

Только token необходимо указывать при запуске playbook'а, так как получение возможно(?) только с web-интерфейса.

  1. Настройка уведомлений из 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

  1. Запуск 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'

https://gitlab.com/gitlab-org/gitlab-runner/-/issues/29132

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)


Выполненные работы

  1. Подключение в облаке 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)
  1. Проверка различных вариантов настройки сетей:
for i in none host; do docker run -ti --rm --network $i joffotron/docker-net-tools -c ifconfig; done
  1. Сравнить с выводом команды 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
  1. После запуска несколько раз команды
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
  1. Повторили 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
  1. Создана по-умолчанию bridge-сеть в docker и проверена недоступность сервисов по DNS-именам, которые прописанны в ENV-переменных Dockerfile'ов. Проверено, что с указанием при запуске имен --name=comment, --name=post всё равно не работает, если не прописать два имени к docker'у БД. Поэтому подключение к MongoDB нужно указывать с переменной, например для comment docker'а -e COMMENT_DATABASE_HOST=mongo_db или использовать --network-alias, как в предыдущем ДЗ.
  2. Созданы дополнительно две сети: 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
  1. До момента подключения серверов 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
  1. Рассмотрен сетевой стек 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
  1. Установлен docker-compose на локальную машину и собрагы образы приложения reddit
  2. Приложение 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
  1. Файл .env внесён в .gitingore, выполнена копия в .env.exmaple
  2. Изменён docker-compose.yml для случая использования несколько bridge сетей, сетевых псевдонимов
  3. Ответ на вопрос, что все создаваемые 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 - номера инстансов.

  1. Явное имя контейнера, если указано: Если явно указано имя контейнера с помощью параметра container_name в файле docker-compose.yml, то будет использоваться это имя вместо автоматически сгенерированного.

Задания со star

  1. Создан docker-compose.override.yml
  2. Для изменения настроек приложения, возможно выполнить подключечния тома, как и для БД.
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
...
  1. Для запуска 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

Выполненные работы

  1. Установлен hadolint'ер из AUR и проверены Dockerfileui/post/comment, здесь по рекомендациям DL3020 внесены изменения
  2. Выполнены подготовка и подключение к ранее созданному Docker host’у
  3. Организована новая структура приложения из архива https://github.com/express42/reddit/archive/microservices.zip в папке src
  4. Внесены изменения в 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
  1. К запуску контейнера ДБ установлена версия для совместимости со старыми версиями приложений
docker run -d \
  --network=reddit --network-alias=post_db --network-alias=comment_db \
  --volume reddit_db:/data/db \
  mongo:4.4.24
  1. Все контейнеры собраны, сеть и том 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)
  1. Оптимизация образа образа 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

Задания со star

  1. Перезапущены контейнеры контейнеры с другими сетевыми алиасами с префиксом "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
  1. Создан Dockerfile.01 для ui образ на основе Alpine, как и comment
docker build -t <dockerhub-login>/ui:3.0 ./ui --file ui/Dockerfile.01
  1. Запуск контейнера mongo с volume reddit_db сохраняет данные ДБ в папке /var/lib/docker/volumes/reddit_db/_data и после перезапуска пост остается доступен на портале

Выполненные работы

  1. Создан репозиторий docker-2 и устанавлены docker и docker-tools
  2. Создан новый проект docker
  3. Создан там docker-host с помощью docker-machine
  4. Выполнено подключение к удаленному docker instance в docker-hub

Задания со star

  1. В docker-1.log внесена информация по выполнению реализации в виде прототипа в директории /docker-monolith/infra/:
  • packer в связке с ansible, файл docker.json - шаблон пакера, который создает с ansible playbook образ в облаке.

  • terraform со структурой terraform/stage и модулей modules/docker,

  • ansible с учетом ролей и playbooks для docker terraform модуля

  1. В файле docker-2-star.log приводится результат создания инфрастуктуры с помошью terraform'а и ранее созданного packer'ом образа, где используется счётчик (их количество задается переменной):
  2. Изменен outputs.tf так, что можно создавать различные ansible inventory: INI, YAML, JSON
  3. Добавлен для автоматизации provisioners для уставноки docker'а reddit в облачные docker'ы в ЯОблаке с учетом сгенерированного в output динамического интентори.
  • результат выполнения в
  1. В файле docker-2-additional-star.log лог автоматизиванного выполения за раз развертывания docker'ов по счётчику, а так же сразу выполнение установки и запуска приложений в контейнерах.
  2. Добавлены null_resource'ы для запуска ansible playbook'а