Skip to content

Otus-DevOps-2023-09/skyfly535_microservices

Repository files navigation

skyfly535_microservices

skyfly535 microservices repository

HW21 Применение системы логирования в инфраструктуре на основе Docker (должок).

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. В YC cоздан хост и инициализировано окружение Docker на нем docker-machine create;
$ yc compute instance create \
>   --name worker \
>   --zone ru-central1-a \
>   --network-interface subnet-name=netterraform-ru-central1-a,nat-ip-version=ipv4 \
>   --create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1804-lts,size=15 \
>   --memory 8 --cores 4 --core-fraction 100 \
>   --ssh-key ~/.ssh/ubuntu.pub
done (31s)

$ docker-machine create \
>   --driver generic \
>   --generic-ip-address=51.250.88.176 \
>   --generic-ssh-user yc-user \
>   --generic-ssh-key ~/.ssh/ubuntu  \
>   docker-host
Running pre-create checks...
...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env docker-host

eval $(docker-machine env docker-host)
  1. Подготовлено окружение. Скачана новая ветка reddit. Скопированы файлы в каталог приложения ./src.;
git clone https://github.com/express42/reddit.git
  1. Выполнена сборка образов ui, post, comment с тэгами logging;
$ export USER_NAME=skyfly534

/src$ cd ui && bash docker_build.sh && docker push $USER_NAME/ui

...

$ cd ../post-py && bash docker_build.sh && docker push $USER_NAME/post
...

$ cd ../comment && bash docker_build.sh && docker push $USER_NAME/comment
...

$ docker images
REPOSITORY          TAG       IMAGE ID       CREATED          SIZE
skyfly534/comment   logging   5c0952de5afb   9 minutes ago    89.1MB
skyfly534/post      logging   822d0f5d8e8c   11 minutes ago   210MB
skyfly534/ui        logging   dd98a7f398ae   14 minutes ago   188MB
  1. Создан файл docker-compose-logginig.yml для системы логирования стек EFK ElasticSearch + Fluentd + Kibana;

./docker/docker-compose-logging.yml

version: '3'
services:
  fluentd:
    image: ${USERNAME}/fluentd
    ports:
      - "24224:24224"
      - "24224:24224/udp"

  elasticsearch:
    image: elasticsearch:7.4.0
    environment:
      - ELASTIC_CLUSTER=false
      - CLUSTER_NODE_MASTER=true
      - CLUSTER_MASTER_NODE_NAME=es01
      - discovery.type=single-node
    expose:
      - 9200
    ports:
      - "9200:9200"

  kibana:
    image: kibana:7.4.0
    ports:
      - "5601:5601"
  1. Для запуска Fluentd созданы файлы Dockerfile и конфигурации для Fluentd, выполнена сборка образа;

./logging/fluentd/Dockerfile

FROM fluent/fluentd:v1.14.0-1.0
USER root
RUN gem uninstall -I elasticsearch && gem install elasticsearch -v 7.17.1
RUN gem install fluent-plugin-elasticsearch --no-document --version 5.2.3
RUN gem install fluent-plugin-grok-parser --no-document --version 2.6.2
USER fluent
ADD fluent.conf /fluentd/etc

./logging/fluentd/fluent.conf

<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<match *.**>
  @type copy
  <store>
    @type elasticsearch
    host elasticsearch
    port 9200
    logstash_format true
    logstash_prefix fluentd
    logstash_dateformat %Y%m%d
    include_tag_key true
    type_name access_log
    tag_key @log_name
    flush_interval 1s
  </store>
  <store>
    @type stdout
  </store>
</match>

Билдим образ

./logging/fluentd$ docker build -t skyfly534/fluentd .
[+] Building 234.5s (11/11) FINISHED
 => [internal] load build definition from Dockerfile

...

=> => naming to docker.io/skyfly534/fluentd

$ docker images
REPOSITORY          TAG       IMAGE ID       CREATED          SIZE
skyfly534/fluentd   latest    ea25c7dd2003   16 seconds ago   124MB
skyfly534/comment   logging   5c0952de5afb   23 minutes ago   89.1MB
skyfly534/post      logging   822d0f5d8e8c   26 minutes ago   210MB
skyfly534/ui        logging   dd98a7f398ae   28 minutes ago   188MB
  1. Откорректирован файл docker-compose.yml и поднят тестовый сайт;
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
services:
       - back_net

   ui:
-    image: ${USERNAME}/ui:latest
+    image: ${USERNAME}/ui:logging
     ports:
       - ${UI_PORT}:9292/tcp
     networks:
       - front_net

   post:
-    image: ${USERNAME}/post:latest
+    image: ${USERNAME}/post:logging
     networks:
       - back_net
       - front_net

   comment:
-    image: ${USERNAME}/comment:latest
+    image: ${USERNAME}/comment:logging
     networks:
       - back_net
       - front_net
-  prometheus:
-    image: ${USERNAME}/prometheus:latest
-    ports:
-      - '9090:9090'
-    volumes:
-      - prometheus_data:/prometheus
-    command:
-      - '--config.file=/etc/prometheus/prometheus.yml'
-      - '--storage.tsdb.path=/prometheus'
-      - '--storage.tsdb.retention=1d'
-    networks:
-      - front_net
-      - back_net
-
-  node-exporter:
-    image: prom/node-exporter:v0.15.2
-    user: root
-    volumes:
-      - /proc:/host/proc:ro
-      - /sys:/host/sys:ro
-      - /:/rootfs:ro
-    command:
-      - '--path.procfs=/host/proc'
-      - '--path.sysfs=/host/sys'
-      - '--collector.filesystem.ignored-mount-points="^/(sys|proc|dev|host|etc)($$|/)"'
-    networks:
-      - front_net
-      - back_net
-
-  mongo-exporter:
-    image: percona/mongodb_exporter:0.35
-    command:
-      - '--mongodb.uri=mongodb://post_db:27017'
-      - '--collect-all'
-      - '--log.level=debug'
-    ports:
-      - '9216:9216'
-    networks:
-      - back_net
-
-  blackbox-exporter:
-    image: r2d2k/blackbox-exporter:1.0
-    ports:
-      - '9115:9115'
-    networks:
-      - front_net
-
 volumes:
   post_db:
-  prometheus_data:

 networks:
   front_net:

Настроим драйвер логирования для сервиса post

--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
services:
     networks:
       - back_net
       - front_net
+    logging:
+      driver: "fluentd"
+      options:
+        fluentd-address: localhost:24224
+        tag: service.post

   comment:
     image: ${USERNAME}/comment:logging

Запускаем сайт и EFK стек

./docker$ docker compose -f docker-compose-logging.yml up -d

./docker$ docker compose up -d

$ docker compose ps
NAME                        IMAGE                       COMMAND                  SERVICE             CREATED             STATUS              PORTS
skyfly534-comment-1         skyfly534/comment:logging   "puma"                   comment             5 minutes ago       Up 5 minutes
skyfly534-elasticsearch-1   elasticsearch:7.4.0         "/usr/local/bin/dock…"   elasticsearch       6 minutes ago       Up 6 minutes        0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp
skyfly534-fluentd-1         skyfly534/fluentd           "tini -- /bin/entryp…"   fluentd             6 minutes ago       Up 6 minutes        5140/tcp, 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp, :::24224->24224/tcp, :::24224->24224/udp
skyfly534-kibana-1          kibana:7.4.0                "/usr/local/bin/dumb…"   kibana              6 minutes ago       Up 6 minutes        0.0.0.0:5601->5601/tcp, :::5601->5601/tcp
skyfly534-mongo_db-1        mongo:3.2                   "docker-entrypoint.s…"   mongo_db            5 minutes ago       Up 5 minutes        27017/tcp
skyfly534-post-1            skyfly534/post:logging      "python3 post_app.py"    post                5 minutes ago       Up 5 minutes
skyfly534-ui-1              skyfly534/ui:logging        "puma"                   ui                  5 minutes ago       Up 5 minutes        0.0.0.0:80->9292/tcp, :::80->9292/tcp
  1. Изучен сбор структурированных и неструктурированных логов сервисов post и ui;

Видим, что elasticsearch и kibana поднялись и отвечают на запросы. Создадим несколько постов и проверим, что видно в kibana.

Для разбора строки на поля, для этого берем фильры fluentd.

{"addr": "10.0.1.3", "event": "request", "level": "info", "method": "GET", "path": "/healthcheck?", "request_id": null, "response_status": 200, "service": "post", "timestamp": "2024-03-02 12:17:41"}
<filter service.post>
  @type parser
  format json
  key_name log
</filter>

Для разбора неструктурированных логов сервиса ui добавим к контейнеру драйвер для логирования:

--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
services:
       - ${UI_PORT}:9292/tcp
     networks:
       - front_net
    logging:
      driver: "fluentd"
      options:
        fluentd-address: localhost:24224
        tag: service.ui

   post:
     image: ${USERNAME}/post:logging

логи сервиса ui

{"addr": "10.0.1.3", "event": "request", "level": "info", "method": "GET", "path": "/healthcheck?", "request_id": null, "response_status": 200, "service": "post", "timestamp": "2024-03-02 12:30:17"}

Разбираем при помощи регулярного выражения. Редактируем файл fluent.conf.

<filter service.ui>
  @type parser
  format /\[(?<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
</filter>

Чтобы уйти от огорода регулярок в этом случае можно использовать grok шаблоны.

<filter service.ui>
  @type parser
  <parse>
    @type grok
    <grok>
      pattern %{RUBY_LOGGER}
    </grok>
  </parse>
  key_name log
</filter>
  1. Разобраны остальные неструктурированные логи;

Используем два последовательных фильтра для разбора на составные части сообщений сервиса ui.

<filter service.ui>
  @type parser
  <parse>
    @type grok
    <grok>
      pattern service=%{WORD:service} \| event=%{WORD:event} \| request_id=%{GREEDYDATA:request_id} \| message='%{GREEDYDATA:message}'
    </grok>
  </parse>
  key_name message
  reserve_data true
</filter>

<filter service.ui>
  @type parser
  <parse>
    @type grok
    <grok>
      pattern service=%{WORD:service} \| event=%{WORD:event} \| path=%{GREEDYDATA:path} \| request_id=%{GREEDYDATA:request_id} \| remote_addr=%{IPV4:remote_addr} \| method= %{WORD:method} \| response_status=%{INT:response_status}
    </grok>
  </parse>
  key_name message
</filter>

Для упращения жизни логично использовать онлайн дебаггеров grok. Один из них.

  1. Настроен распределенный трейсинг;

В compose-файл для сервисов логирования добавлен сервис распределенного трейсинга Zipkin

--- a/docker/docker-compose-logging.yml
+++ b/docker/docker-compose-logging.yml
services:
     image: kibana:7.4.0
     ports:
       - "5601:5601"

  zipkin:
    image: openzipkin/zipkin:2.21.0
    ports:
      - "9411:9411"

Сервисы настроены на использование Zipkin

--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
services:
       options:
         fluentd-address: localhost:24224
         tag: service.ui
    environment:
      - ZIPKIN_ENABLED=${ZIPKIN_ENABLED}

   post:
     image: ${USERNAME}/post:logging
services:
       options:
         fluentd-address: localhost:24224
         tag: service.post
    environment:
      - ZIPKIN_ENABLED=${ZIPKIN_ENABLED}

   comment:
     image: ${USERNAME}/comment:logging
     networks:
       - back_net
       - front_net
    environment:
      - ZIPKIN_ENABLED=${ZIPKIN_ENABLED}

 volumes:
   post_db:

...

image: openzipkin/zipkin:2.21.0
     ports:
       - "9411:9411"
    networks:
      - front_net
      - back_net

networks:
  front_net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.0.1.0/24

  back_net:
    driver: bridge
    ipam:
     config:
        - subnet: 10.0.2.0/24

Изучен функционал трейсов через web-интерфейс Zipkin.

Дополнительное задание.

  1. Произведен траблшутинг UI-экспириенса. Загружен репозиторий со сломанным кодом приложения в каталог src-bugged и запущен.

В zipkin видим, что функция db_find_single_post сервиса post отрабатывает 3 секунды. Находим в исходнике, исправляем.

./bugged-code/post-py/post_app.py

def find_post(id):
         stop_time = time.time()  # + 0.3
         resp_time = stop_time - start_time
         app.post_read_db_seconds.observe(resp_time)
         time.sleep(3) <--------------------------------------- Баг тут!
         log_event('info', 'post_find',
                   'Successfully found the post information',
                   {'post_id': id})

HW20 CI/CD в Kubernetes.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Установлен Helm, развернута необходимая инфраструктура для работы с ним;
$ sudo snap install helm --classic

helm 3.14.1 от Snapcrafters✪ установлен

Helm читает конфигурацию kubectl ~/.kube/config и сам определяет текущий контекст (кластер, пользователь, неймспейс).

Chart - это пакет в Helm. Создан подкаталог Charts в каталоге kubernetes со следующей структурой:

$ mkdir -p kubernetes/Charts/{comment,post,reddit,ui}

$ tree kubernetes/Charts/
kubernetes/Charts/
├── comment
├── post
├── reddit
└── ui

Создан файл-описание чарта для компонента ui, сохранен в ./kubernetes/Charts/ui/Chart.yaml

name: ui
version: 1.0.0
description: OTUS reddit application UI
maintainers:
  - name: me
    email: me@me.me
appVersion: 1.0

Перенесены в каталог ./kubernetes/Charts/ui и переименованы все манифесты для сервиса ui, подготовленные при выполнении предыдущего ДЗ

./kubernetes/Charts$ tree
.
├── comment
├── post
├── reddit
└── ui
    ├── Chart.yaml
    └── templates
        ├── deployment.yaml
        ├── ingress.yaml
        └── service.yaml
  1. Установлен и зарегистрирован локально кластер Kubernetesв Yandex Cloud;
./kubernetes/terraform_YC_k8s$ terraform apply -auto-approve

$ yc managed-kubernetes cluster get-credentials skyfly535 --external

Context 'yc-skyfly535' was added as default to kubeconfig '/home/roman/.kube/config'.
Check connection to cluster using 'kubectl cluster-info --kubeconfig /home/roman/.kube/config'.

Note, that authentication depends on 'yc' and its config profile 'terraform-profile'.
To access clusters using the Kubernetes API, please use Kubernetes Service Account.
  1. Запущен тестовый сервис ui при помощи helm;
$ helm install test-ui-1 ui/
NAME: test-ui-1
LAST DEPLOYED: Mon Feb 19 20:50:00 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

$ helm ls
NAME     	NAMESPACE	REVISION	UPDATED                                	STATUS  	CHART   	APP VERSION
test-ui-1	default  	1       	2024-02-19 20:50:00.115670654 +1000 +10	deployed	ui-1.0.0	1

$ kubectl get deployments
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
ui     3/3     3            3           34s

$ kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
ui-65846d4847-kpf26   1/1     Running   0          47s
ui-65846d4847-rprv5   1/1     Running   0          47s
ui-65846d4847-td2d9   1/1     Running   0          47s
  1. Шаблонизирован Chart ui для запуска несколько релизов одновременно;

--- a/kubernetes/Charts/ui/templates/deployment.yaml
+++ b/kubernetes/Charts/ui/templates/deployment.yaml
 apiVersion: apps/v1
 kind: Deployment       # Deploy metadata
 metadata:
-  name: ui
+  name: {{ .Release.Name }}-{{ .Chart.Name }}
   labels:
     app: reddit
     component: ui
+    release: {{ .Release.Name }}
 spec:                  # Deploy specification
   replicas: 3
   selector:
     matchLabels:
       app: reddit
       component: ui
+      release: {{ .Release.Name }}
   template:            # Pod description
     metadata:
       name: ui-pod
       labels:
         app: reddit
         component: ui
+        release: {{ .Release.Name }}
     spec:
       containers:
       - image: skyfly534/ui


--- a/kubernetes/Charts/ui/templates/ingress.yaml
+++ b/kubernetes/Charts/ui/templates/ingress.yaml
 apiVersion: networking.k8s.io/v1
 kind: Ingress
 metadata:
-  name: ui
+  name: {{ .Release.Name }}-{{ .Chart.Name }}
   annotations:
     nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
 spec:
spec:
         pathType: Prefix
         backend:
           service:
-            name: ui
+            name: {{ .Release.Name }}-{{ .Chart.Name }}
             port:
               number: 9292


--- a/kubernetes/Charts/ui/templates/service.yaml
+++ b/kubernetes/Charts/ui/templates/service.yaml
 apiVersion: v1
 kind: Service
 metadata:
-  name: ui
+  name: {{ .Release.Name }}-{{ .Chart.Name }}
   labels:
     app: reddit
     component: ui
+    release: {{ .Release.Name }}
 spec:
   type: LoadBalancer
   ports:
spec:
   selector:
     app: reddit
     component: ui
+    release: {{ .Release.Name }}

Определены значения переменных в ./kubernetes/Charts.ui/values.yaml

service:
  internalPort: 9292
  externalPort: 9292
image:
  repository: skyfly534/ui
  tag: latest

Добавлено еще два релиза сервиса

$ helm install test-ui-2 ui/
NAME: test-ui-2
LAST DEPLOYED: Mon Feb 19 22:04:20 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

$ helm install test-ui-3 ui/
NAME: test-ui-3
LAST DEPLOYED: Mon Feb 19 22:04:33 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

$ kubectl get ingress
NAME           CLASS   HOSTS   ADDRESS   PORTS     AGE
test-ui-2-ui   nginx   *                 80, 443   30s
test-ui-3-ui   nginx   *                 80, 443   16s
ui             nginx   *                 80, 443   74m

Внешнего IP нет, проверяем балансировщики

$ yc lb nlb list
+----------------------+----------------------------------------------+-------------+----------+----------------+------------------------+--------+
|          ID          |                     NAME                     |  REGION ID  |   TYPE   | LISTENER COUNT | ATTACHED TARGET GROUPS | STATUS |
+----------------------+----------------------------------------------+-------------+----------+----------------+------------------------+--------+
| enpao0gffvjsdbso9d1e | k8s-31668eef5732782c429805d08727ff0e18e271ec | ru-central1 | EXTERNAL |              1 | enpcs57cebvnhj256t1b   | ACTIVE |
| enpgorbphvl5amfutsit | k8s-7283e9558a0d7ea97ad1bc8b02d4b6d1b24ea0a4 | ru-central1 | EXTERNAL |              1 | enpcs57cebvnhj256t1b   | ACTIVE |
+----------------------+----------------------------------------------+-------------+----------+----------------+------------------------+--------+

$ yc lb nlb get enpao0gffvjsdbso9d1e
id: enpao0gffvjsdbso9d1e
folder_id: b1ghhcttrug793gc11tt
created_at: "2024-02-19T12:04:21Z"
name: k8s-31668eef5732782c429805d08727ff0e18e271ec
description: cluster catv7gl39njseu9k53ir, service default/test-ui-2-ui
labels:
  cluster-name: catv7gl39njseu9k53ir
  service-name: test-ui-2-ui
  service-namespace: default
  service-uid: 97390da5-c529-4f87-87fc-05929297db2b
region_id: ru-central1
status: ACTIVE
type: EXTERNAL
listeners:
  - name: default
    address: 158.160.149.197
    port: "9292"
    protocol: TCP
    target_port: "30932"
    ip_version: IPV4
attached_target_groups:
  - target_group_id: enpcs57cebvnhj256t1b
    health_checks:
      - name: default
        interval: 10s
        timeout: 5s
        unhealthy_threshold: "2"
        healthy_threshold: "2"
        http_options:
          port: "10256"
          path: /healthz

$ yc lb nlb get enpgorbphvl5amfutsit
id: enpgorbphvl5amfutsit
folder_id: b1ghhcttrug793gc11tt
created_at: "2024-02-19T12:52:14Z"
name: k8s-7283e9558a0d7ea97ad1bc8b02d4b6d1b24ea0a4
description: cluster catv7gl39njseu9k53ir, service default/test-ui-1-ui
labels:
  cluster-name: catv7gl39njseu9k53ir
  service-name: test-ui-1-ui
  service-namespace: default
  service-uid: 2bb71ff7-d0b3-4af9-b80e-bc19e5a8ee73
region_id: ru-central1
status: ACTIVE
type: EXTERNAL
listeners:
  - name: default
    address: 158.160.147.233
    port: "9292"
    protocol: TCP
    target_port: "31171"
    ip_version: IPV4
attached_target_groups:
  - target_group_id: enpcs57cebvnhj256t1b
    health_checks:
      - name: default
        interval: 10s
        timeout: 5s
        unhealthy_threshold: "2"
        healthy_threshold: "2"
        http_options:
          port: "10256"
          path: /healthz

Балансировщика два, так как достигнут предел квоты ylb.networkLoadBalancers.count.

Но сервис по IP адресам из вывода состояния балансировщиков отвечает.

Проапргрейжены сервисы, теперь уже с объявленными переменными. Сервис отвечает с теми же IP, но по порту 9292.

$ helm upgrade test-ui-1 ui/

$ helm upgrade test-ui-2 ui/

Структура следующая

$ tree
.
├── comment
├── post
├── reddit
└── ui
    ├── Chart.yaml
    ├── templates
    │   ├── deployment.yaml
    │   ├── ingress.yaml
    │   └── service.yaml
    └── values.yaml
  1. Подготовлены по предыдущему образцу чарты для остальных сервисов;
$ tree
.
├── comment
│   ├── Chart.yaml
│   ├── templates
│   │   ├── deployment.yaml
│   │   ├── _helpers.tpl
│   │   └── service.yaml
│   └── values.yaml
├── post
│   ├── Chart.yaml
│   ├── templates
│   │   ├── deployment.yaml
│   │   ├── _helpers.tpl
│   │   └── service.yaml
│   └── values.yaml
├── reddit
│   ├── Chart.yaml
│   ├── requirements.yaml
│   └── values.yaml
└── ui
    ├── Chart.yaml
    ├── templates
    │   ├── deployment.yaml
    │   ├── _helpers.tpl
    │   ├── ingress.yaml
    │   └── service.yaml
    └── values.yaml

В каждую папку с шаблонами добавлен _helpers.tpl

{{- define "ui.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name }}
{{- end -}}

Все ссылки на имена заменены функцией {{ template "comment.fullname" . }}.

Для запуска приложения целиком создан общий чарт в каталоге reddit(файл requirements.yaml)

dependencies:
  - name: ui
    version: "1.0.0"
    repository: "file://../ui"

  - name: post
    version: "1.0.0"
    repository: "file://../post"

  - name: comment
    version: "1.0.0"
    repository: "file://../comment"

Chart.yaml

name: reddit
version: 0.1.0
description: OTUS reddit application
maintainers:
  - name: me
    email: me@me.me
appVersion: 1.0

Проверяем. Загружаем зависимости.

$ helm dep update
Saving 3 charts
Deleting outdated charts

$ tree
.
├── charts
│   ├── comment-1.0.0.tgz
│   ├── post-1.0.0.tgz
│   └── ui-1.0.0.tgz
├── Chart.yaml
├── requirements.lock
├── requirements.yaml
└── values.yaml
  1. Установлен сервис mongodb при помощи чарта;
$ helm repo list
Error: no repositories to show

$ helm repo add google https://kubernetes-charts.storage.googleapis.com
Error: repo "https://kubernetes-charts.storage.googleapis.com" is no longer available; try "https://charts.helm.sh/stable" instead

$ helm repo add google https://charts.helm.sh/stable
"google" has been added to your repositories

$ helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories

$ helm search repo mongo
NAME                              	CHART VERSION	APP VERSION	DESCRIPTION
bitnami/mongodb                   	14.10.1      	7.0.5      	MongoDB(R) is a relational open source NoSQL da...
bitnami/mongodb-sharded           	7.6.0        	7.0.5      	MongoDB(R) is an open source NoSQL database tha...
google/mongodb                    	7.8.10       	4.2.4      	DEPRECATED NoSQL document-oriented database tha...
google/mongodb-replicaset         	3.17.2       	3.6        	DEPRECATED - NoSQL document-oriented database t...
google/prometheus-mongodb-exporter	2.8.1        	v0.10.0    	DEPRECATED A Prometheus exporter for MongoDB me...
google/unifi                      	0.10.2       	5.12.35    	DEPRECATED - Ubiquiti Network's Unifi Controller

Установка из готового чата не удалась, поэтому был подсмотрен и адаптирован чат для БД ./kubernetes/Charts/mongodb.

Дописана секция в файл ./kubernetes/Charts/reddit/requirements.yaml общего чата

- name: mongodb
    version: "1.0.0"
    repository: "file://../mongodb"

Проверяем

$ helm dep update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "google" chart repository
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 4 charts
Deleting outdated charts

$ helm upgrade reddit-test reddit/
Release "reddit-test" has been upgraded. Happy Helming!
NAME: reddit-test
LAST DEPLOYED: Wed Feb 21 18:28:12 2024
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
reddit-test-comment-5557c576fd-bp5jk   1/1     Running   0          21s
reddit-test-mongodb-565577c7cb-ttw9t   1/1     Running   0          48m
reddit-test-post-7fdfcdf4f7-rf794      1/1     Running   0          48m
reddit-test-ui-5cbbbcd5c9-bhsqw        1/1     Running   0          48m
reddit-test-ui-5cbbbcd5c9-hmbrm        1/1     Running   0          48m
reddit-test-ui-5cbbbcd5c9-hx9hs        1/1     Running   0          48m
  1. Указаны переменные окружения для поиска хостов (связи сервисов в кластере);
--- a/kubernetes/Charts/ui/templates/deployment.yaml
+++ b/kubernetes/Charts/ui/templates/deployment.yaml
spec:                        # Deploy specification
       - image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
         name: ui
         ports:
-          - containerPort: {{ .Values.service.internalPort }}
+        - containerPort: {{ .Values.service.internalPort }}
+          name: ui
         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
+              fieldPath: metadata.namespace

Проверяем. Обновляем чаты и релизы.

$ helm dep update ./reddit
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository
...Successfully got an update from the "google" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 4 charts
Deleting outdated charts

$ helm upgrade reddit-test reddit
Release "reddit-test" has been upgraded. Happy Helming!
NAME: reddit-test
LAST DEPLOYED: Wed Feb 21 18:46:25 2024
NAMESPACE: default
STATUS: deployed
REVISION: 3
TEST SUITE: None

Все роботает.

Для нормальной работы приложения не хватает ingress-nginx

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created

$ kubectl get ingress reddit-test-ui
NAME             CLASS   HOSTS   ADDRESS           PORTS     AGE
reddit-test-ui   nginx   *       158.160.148.224   80, 443   76m
  1. В kubernetes кластер при помощи Helm Chart’а разработчика установлен Gitlab;
$ helm repo remove bitnami
"bitnami" has been removed from your repositories

$ helm repo add gitlab https://charts.gitlab.io/
"gitlab" has been added to your repositories

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "gitlab" chart repository
...Successfully got an update from the "bitnami" chart repository
...Successfully got an update from the "google" chart repository
Update Complete. ⎈Happy Helming!⎈

$ helm pull gitlab/gitlab --untar

Редактируем файл ./kubernetes/Charts/gitlab/values.yaml


--- a/kubernetes/Charts/gitlab/values.yaml
+++ b/kubernetes/Charts/gitlab/values.yaml
global:
     labels: {}

   ## https://docs.gitlab.com/charts/installation/deployment#deploy-the-community-edition
-  edition: ee
+  edition: ce

   ## https://docs.gitlab.com/charts/charts/globals#gitlab-version
   # gitlabVersion:

global:
       certName: "tls.crt"

   ## Timezone for containers.
-  time_zone: UTC
+  time_zone: Asia/Vladivostok

   ## Global Service Annotations and Labels
   service:

upgradeCheck:
   priorityClassName: ""

 ## Settings to for the Let's Encrypt ACME Issuer
-# certmanager-issuer:
+certmanager-issuer:
 #   # The email address to register certificates requested from Let's Encrypt.
 #   # Required if using Let's Encrypt.
-#   email: email@example.com
+  email: otus@mail.com

 ## Installation & configuration of jetstack/cert-manager
 ## See requirements.yaml for current version

nginx-ingress:
 ## Installation & configuration of stable/prometheus
 ## See requirements.yaml for current version
 prometheus:
-  install: true
+  install: false
   rbac:
     create: true
   alertmanager:

Производим установку мануал

$ helm install gitlab ./gitlab
NAME: gitlab
LAST DEPLOYED: Mon Feb 26 18:24:04 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
=== NOTICE
The minimum required version of PostgreSQL is now 12. See https://gitlab.com/gitlab-org/charts/gitlab/-/blob/master/doc/installation/upgrade.md for more details.

=== NOTICE
You've installed GitLab Runner without the ability to use 'docker in docker'.
The GitLab Runner chart (gitlab/gitlab-runner) is deployed without the `privileged` flag by default for security purposes. This can be changed by setting `gitlab-runner.runners.privileged` to `true`. Before doing so, please read the GitLab Runner chart's documentation on why we
chose not to enable this by default. See https://docs.gitlab.com/runner/install/kubernetes.html#running-docker-in-docker-containers-with-gitlab-runners
Help us improve the installation experience, let us know how we did with a 1 minute survey:https://gitlab.fra1.qualtrics.com/jfe/form/SV_6kVqZANThUQ1bZb?installation=helm&release=15-6

=== NOTICE
The in-chart NGINX Ingress Controller has the following requirements:
    - Kubernetes version must be 1.19 or newer.
    - Ingress objects must be in group/version `networking.k8s.io/v1`.

$ kubectl get ingress
NAME                        CLASS          HOSTS                  ADDRESS           PORTS     AGE
gitlab-kas                  gitlab-nginx   kas.example.com        158.160.131.134   80, 443   3m42s
gitlab-minio                gitlab-nginx   minio.example.com      158.160.131.134   80, 443   3m42s
gitlab-registry             gitlab-nginx   registry.example.com   158.160.131.134   80, 443   3m42s
gitlab-webservice-default   gitlab-nginx   gitlab.example.com     158.160.131.134   80, 443   3m42s

Еще раз редактируем файл ./kubernetes/Charts/gitlab/values.yaml

--- a/kubernetes/Charts/gitlab/values.yaml
+++ b/kubernetes/Charts/gitlab/values.yaml
global:
     allowClusterRoles: true
   ## https://docs.gitlab.com/charts/charts/globals#configure-host-settings
   hosts:
-    domain: example.com
+    domain: 158.160.131.134.sslip.io
     hostSuffix:
     https: true
-    externalIP:
+    externalIP: 158.160.131.134
     ssh: ~
     gitlab: {}
     minio: {}

Апгрейдим

$ helm upgrade gitlab ./gitlab
Release "gitlab" has been upgraded. Happy Helming!
NAME: gitlab
LAST DEPLOYED: Mon Feb 26 18:33:08 2024
NAMESPACE: default
STATUS: deployed
REVISION: 2
NOTES:
=== NOTICE
The minimum required version of PostgreSQL is now 12. See https://gitlab.com/gitlab-org/charts/gitlab/-/blob/master/doc/installation/upgrade.md for more details.

=== NOTICE
You've installed GitLab Runner without the ability to use 'docker in docker'.
The GitLab Runner chart (gitlab/gitlab-runner) is deployed without the `privileged` flag by default for security purposes. This can be changed by setting `gitlab-runner.runners.privileged` to `true`. Before doing so, please read the GitLab Runner chart's documentation on why we
chose not to enable this by default. See https://docs.gitlab.com/runner/install/kubernetes.html#running-docker-in-docker-containers-with-gitlab-runners

=== NOTICE
The in-chart NGINX Ingress Controller has the following requirements:
    - Kubernetes version must be 1.19 or newer.
    - Ingress objects must be in group/version `networking.k8s.io/v1`.

$ kubectl get ingress
NAME                        CLASS          HOSTS                               ADDRESS           PORTS     AGE
gitlab-kas                  gitlab-nginx   kas.158.160.131.134.sslip.io        158.160.131.134   80, 443   10m
gitlab-minio                gitlab-nginx   minio.158.160.131.134.sslip.io      158.160.131.134   80, 443   10m
gitlab-registry             gitlab-nginx   registry.158.160.131.134.sslip.io   158.160.131.134   80, 443   10m
gitlab-webservice-default   gitlab-nginx   gitlab.158.160.131.134.sslip.io     158.160.131.134   80, 443   10m

Установлено параметру gitlab-runner.runners.privileged значение true для работы в режиме docker in docker

$ helm upgrade gitlab ./gitlab --set gitlab-runner.runners.privileged=true
Release "gitlab" has been upgraded. Happy Helming!
NAME: gitlab
LAST DEPLOYED: Mon Feb 26 18:39:01 2024
NAMESPACE: default
STATUS: deployed
REVISION: 3
NOTES:
=== NOTICE
The minimum required version of PostgreSQL is now 12. See https://gitlab.com/gitlab-org/charts/gitlab/-/blob/master/doc/installation/upgrade.md for more details.

=== NOTICE
The in-chart NGINX Ingress Controller has the following requirements:
    - Kubernetes version must be 1.19 or newer.
    - Ingress objects must be in group/version `networking.k8s.io/v1`.

или

helm upgrade gitlab ./gitlab \
  --set global.hosts.domain=158.160.131.134.sslip.io \
  --set global.hosts.externalIP=158.160.131.134 \
  --set gitlab-runner.runners.privileged=true

Получаем токен для входа

$ kubectl get secret gitlab-gitlab-initial-root-password -ojsonpath='{.data.password}' | base64 --decode ; echo
dqz5UfiobFxwwjR8KEmnfg58IFJSZZVgrIQGRGxPDBupfP2QJXKE6Gn2wM8WFte8

Заходим по адресу https://gitlab.158.160.131.134.sslip.io, логин root, пароль выше.

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

В свойствах группы находим CI/CD, добавляем две переменные - CI_REGISTRY_USER - логин в DockerHub и CI_REGISTRY_PASSWORD - пароль от DockerHub.

В группе создаём четыре публичных проекта: reddit-deploy, comment, post, ui.

Локально создаём каталоги под каждый проект, в comment, post, ui переносим исходный код сервисов, пушим всё в Gitlab.

В reddit-deploy добавляем все чарты, которые мы создавали ранее и аналогично пушим всё в Gitlab.

В репозиторий ui добавим конфигурацию CI/CD, файл .gitlab-ci.yml

image: alpine:latest

stages:
  - build
  - test
  - review
  - release
  - cleanup

build:
  stage: build
  image: docker:git
  services:
    - docker:18.09.7-dind
  script:
    - setup_docker
    - build
  variables:
    DOCKER_DRIVER: overlay2
  only:
    - branches

test:
  stage: test
  script:
    - exit 0
  only:
    - branches

release:
  stage: release
  image: docker
  services:
    - docker:18.09.7-dind
  script:
    - setup_docker
    - release
  only:
    - main

review:
  stage: review
  script:
    - install_dependencies
    - kubectl config get-contexts
    - kubectl config use-context skyfly534/reddit-deploy:reddit-agent
    - kubectl get pods
    - ensure_namespace
    - deploy
  variables:
    KUBE_NAMESPACE: review
    host: $CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG
  environment:
    name: review/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME
    url: http://$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG
    on_stop: stop_review
  only:
    refs:
      - branches
  except:
    - main

stop_review:
  stage: cleanup
  variables:
    GIT_STRATEGY: none
    KUBE_NAMESPACE: review
  script:
    - install_dependencies
    - kubectl config get-contexts
    - kubectl config use-context skyfly534/reddit-deploy:reddit-agent
    - kubectl get pods
    - delete
  environment:
    name: review/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME
    action: stop
  when: manual
  allow_failure: true
  only:
    refs:
      - branches
  except:
    - main

.auto_devops: &auto_devops |
  [[ "$TRACE" ]] && set -x
  export CI_REGISTRY="index.docker.io"
  export CI_APPLICATION_REPOSITORY=$CI_REGISTRY/$CI_PROJECT_PATH
  export CI_APPLICATION_TAG=$CI_COMMIT_REF_SLUG
  export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID}
  export TILLER_NAMESPACE="kube-system"

  function delete() {
    track="${1-stable}"
    name="$CI_ENVIRONMENT_SLUG"
    helm delete "$name" --namespace="$KUBE_NAMESPACE" || true
  }

  function deploy() {
    track="${1-stable}"
    name="$CI_ENVIRONMENT_SLUG"

    if [[ "$track" != "stable" ]]; then
      name="$name-$track"
    fi

    echo "Clone deploy repository..."
    git clone $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/reddit-deploy.git

    echo "Download helm dependencies..."
    helm dep update reddit-deploy/reddit

    echo "Deploy helm release $name to $KUBE_NAMESPACE"
    helm upgrade --install \
      --wait \
      --set ui.ingress.host="$host" \
      --set $CI_PROJECT_NAME.image.tag=$CI_APPLICATION_TAG \
      --namespace="$KUBE_NAMESPACE" \
      --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
      "$name" \
      reddit-deploy/reddit/
  }

  function install_dependencies() {

    apk add -U openssl curl tar gzip bash ca-certificates git
    wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
    wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk
    apk add --force-overwrite glibc-2.35-r0.apk
    apk fix --force-overwrite alpine-baselayout-data
    rm glibc-2.35-r0.apk

    curl https://storage.googleapis.com/pub/gsutil.tar.gz | tar -xz -C $HOME
    export PATH=${PATH}:$HOME/gsutil

    curl https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz | tar zx

    mv linux-amd64/helm /usr/bin/
    helm version --client

    curl  -o /usr/bin/sync-repo.sh https://raw.githubusercontent.com/kubernetes/helm/master/scripts/sync-repo.sh
    chmod a+x /usr/bin/sync-repo.sh

    curl -L -o /usr/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
    chmod +x /usr/bin/kubectl
    kubectl version --client
  }

  function setup_docker() {
    if ! docker info &>/dev/null; then
      if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
        export DOCKER_HOST='tcp://localhost:2375'
      fi
    fi
  }

  function ensure_namespace() {
    kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
  }

  function release() {

    echo "Updating docker images ..."

    if [[ -n "$CI_REGISTRY_USER" ]]; then
      echo "Logging to GitLab Container Registry with CI credentials..."
      docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
      echo ""
    fi

    docker pull "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
    docker tag "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" "$CI_APPLICATION_REPOSITORY:$(cat VERSION)"
    docker push "$CI_APPLICATION_REPOSITORY:$(cat VERSION)"
    echo ""
  }

  function build() {

    echo "Building Dockerfile-based application..."
    echo `git show --format="%h" HEAD | head -1` > build_info.txt
    echo `git rev-parse --abbrev-ref HEAD` >> build_info.txt
    docker build -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .

    if [[ -n "$CI_REGISTRY_USER" ]]; then
      echo "Logging to GitLab Container Registry with CI credentials..."
      docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
      echo ""
    fi

    echo "Pushing to GitLab Container Registry..."
    docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
    echo ""
  }

before_script:
  - *auto_devops

Пайплайн ломается на функции ensure_namespace шага review, судя по всему раннеру не хватает прав.

Чтобы работать с Kubernetes, нужно настроить Gitlab. Процедура описана тут. Установка самого агента описана в этом разделе.

Конфигурация агента должна лежать в основной ветке репозитория, полный путь: .gitlab/agents/<agent-name>/config.yaml. Авторизуем агента для проектов нашей группы.

ci_access:
  groups:
    - id: skyfly534

Пушим изменения в Gitlab.

Идём в проект, в котором создан файл конфигурации, Infastucture/Kubernetes cluster, Connect a cluster, выбираем имя агента и жмём Register. Gitlab генерирует команды для установки и подключения агента

$ helm repo update

$ helm upgrade --install reddit-agent gitlab/gitlab-agent \
>     --namespace gitlab-agent \
>     --create-namespace \
>     --set image.tag=v15.6.0 \
>     --set config.token=9-8PD5mYoaFPJ9Y3vSJzMrBLjJy3LcuUbctKaLZusQQ_6P5CyA \
>     --set config.kasAddress=wss://kas.158.160.131.134.sslip.io
Release "reddit-agent" does not exist. Installing it now.
NAME: reddit-agent
LAST DEPLOYED: Mon Feb 26 19:59:04 2024
NAMESPACE: gitlab-agent
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing gitlab-agent.

Your release is named reddit-agent.

## Changelog

### 1.17.0

Данный конфиг скопирован в post и comment. Для проверки создаём ветку, что-то меняем в ней, пушим. Должны отработать пайплайны с review.

> helm ls --namespace review
NAME    NAMESPACE       REVISION        UPDATED STATUS  CHART   APP VERSION

> helm ls --namespace review
NAME                            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
review-skyfly534-post-vtboig        review          1       2024-02-26 21:07:05.806289118 +0000 UTC     deployed        reddit-0.1.0    1
  1. Для репозитория reddit-deploy созданы staging и production среды;
image: alpine:latest

stages:
  - test
  - staging
  - production

test:
  stage: test
  script:
    - exit 0
  only:
    - triggers
    - branches

staging:
  stage: staging
  script:
  - install_dependencies
  - kubectl config get-contexts
  - kubectl config use-context skyfly534/reddit-deploy:reddit-agent
  - kubectl get pods --namespace "$KUBE_NAMESPACE"
  - ensure_namespace
  - deploy
  variables:
    KUBE_NAMESPACE: staging
  environment:
    name: staging
    url: http://staging
  only:
    refs:
      - main

production:
  stage: production
  script:
    - install_dependencies
    - kubectl config get-contexts
    - kubectl config use-context skyfly534/reddit-deploy:reddit-agent
    - kubectl get pods --namespace "$KUBE_NAMESPACE"
    - ensure_namespace
    - deploy
  variables:
    KUBE_NAMESPACE: production
  environment:
    name: production
    url: http://production
  when: manual
  only:
    refs:
      - main

.auto_devops: &auto_devops |
  # Auto DevOps variables and functions
  [[ "$TRACE" ]] && set -x
  export CI_REGISTRY="index.docker.io"
  export CI_APPLICATION_REPOSITORY=$CI_REGISTRY/$CI_PROJECT_PATH
  export CI_APPLICATION_TAG=$CI_COMMIT_REF_SLUG
  export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID}
  export TILLER_NAMESPACE="kube-system"

  function deploy() {
    echo $KUBE_NAMESPACE
    track="${1-stable}"
    name="$CI_ENVIRONMENT_SLUG"
    helm dep build reddit

    helm upgrade --install \
      --debug \
      --wait \
      --set ui.ingress.host="$host" \
      --set ui.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/ui/-/raw/main/VERSION)" \
      --set post.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/post/-/raw/main/VERSION)" \
      --set comment.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/comment/-/raw/main/VERSION)" \
      --namespace="$KUBE_NAMESPACE" \
      --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
      "$name" \
      reddit
  }

  function install_dependencies() {

    apk add -U openssl curl tar gzip bash ca-certificates git
    wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
    wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk
    apk add --force-overwrite glibc-2.35-r0.apk
    apk fix --force-overwrite alpine-baselayout-data
    rm glibc-2.35-r0.apk

    curl https://storage.googleapis.com/pub/gsutil.tar.gz | tar -xz -C $HOME
    export PATH=${PATH}:$HOME/gsutil

    curl https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz | tar zx

    mv linux-amd64/helm /usr/bin/
    helm version --client

    curl  -o /usr/bin/sync-repo.sh https://raw.githubusercontent.com/kubernetes/helm/master/scripts/sync-repo.sh
    chmod a+x /usr/bin/sync-repo.sh

    curl -L -o /usr/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
    chmod +x /usr/bin/kubectl
    kubectl version --client
  }

  function ensure_namespace() {
    kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
  }

  function delete() {
    track="${1-stable}"
    name="$CI_ENVIRONMENT_SLUG"
    helm delete "$name" --namespace="$KUBE_NAMESPACE" || true
  }

before_script:
  - *auto_devops

Этот конфиг отличается от предыдущего тем, что:

  • Не собирает docker-образы

  • Деплоит на статичные окружения (staging и production)

  • Не удаляет окружения

После успешного завершения staging прописываем в host соответствие имени среды и IP, который можно увидеть у облачного балансировщика нагрузки. Проверяем - приложение работает. Т.к. количество внешних балансировщиков ограничено, то для запуска production нужно удалить балансировщик staging. Удаляем, запускаем этап production руками из интерфейса Gitlab, смотрим адрес балансировщика, прописываем в hosts - работает.

  1. Перенписан .gitlab-ci.yml с целью уйти от AutoDevOps;

Переносим все процедуры в соотвествующие шаги и получаем для сервиса ui такой вариант

image: alpine:latest

stages:
  - build
  - test
  - review
  - release
  - cleanup

build:
  stage: build
  only:
    - branches
  image: docker:git
  services:
    - docker:18.09.7-dind
  variables:
    DOCKER_DRIVER: overlay2
    CI_REGISTRY: 'index.docker.io'
    CI_APPLICATION_REPOSITORY: $CI_REGISTRY/$CI_PROJECT_PATH
    CI_APPLICATION_TAG: $CI_COMMIT_REF_SLUG
    CI_CONTAINER_NAME: ci_job_build_${CI_JOB_ID}
  before_script:
    - >
      if ! docker info &>/dev/null; then
        if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
          export DOCKER_HOST='tcp://localhost:2375'
        fi
      fi
  script:
    # Building
    - echo "Building Dockerfile-based application..."
    - echo `git show --format="%h" HEAD | head -1` > build_info.txt
    - echo `git rev-parse --abbrev-ref HEAD` >> build_info.txt
    - docker build -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
    - >
      if [[ -n "$CI_REGISTRY_USER" ]]; then
        echo "Logging to GitLab Container Registry with CI credentials...for build"
        docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
      fi
    - echo "Pushing to GitLab Container Registry..."
    - docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"

test:
  stage: test
  script:
    - exit 0
  only:
    - branches

release:
  stage: release
  image: docker
  services:
    - docker:18.09.7-dind
  variables:
    CI_REGISTRY: 'index.docker.io'
    CI_APPLICATION_REPOSITORY: $CI_REGISTRY/$CI_PROJECT_PATH
    CI_APPLICATION_TAG: $CI_COMMIT_REF_SLUG
    CI_CONTAINER_NAME: ci_job_build_${CI_JOB_ID}
  before_script:
    - >
      if ! docker info &>/dev/null; then
        if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
          export DOCKER_HOST='tcp://localhost:2375'
        fi
      fi
  script:
    # Releasing
    - echo "Updating docker images ..."
    - >
      if [[ -n "$CI_REGISTRY_USER" ]]; then
        echo "Logging to GitLab Container Registry with CI credentials for release..."
        docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
      fi
    - docker pull "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
    - docker tag "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" "$CI_APPLICATION_REPOSITORY:$(cat VERSION)"
    - docker push "$CI_APPLICATION_REPOSITORY:$(cat VERSION)"
    # latest is neede for feature flags
    - docker tag "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" "$CI_APPLICATION_REPOSITORY:latest"
    - docker push "$CI_APPLICATION_REPOSITORY:latest"
  only:
    - main

review:
  stage: review
  variables:
    KUBE_NAMESPACE: review
    host: $CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG
    CI_APPLICATION_TAG: $CI_COMMIT_REF_SLUG
    name: $CI_ENVIRONMENT_SLUG
  environment:
    name: review/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME
    url: http://$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG
    on_stop: stop_review
  only:
    refs:
      - branches
    kubernetes: active
  except:
    - main
  before_script:
    # installing dependencies
    - apk add -U openssl curl tar gzip bash ca-certificates git
    - wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
    - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk
    - apk add --force-overwrite glibc-2.35-r0.apk
    - apk fix --force-overwrite alpine-baselayout-data
    - rm glibc-2.35-r0.apk
    - curl https://storage.googleapis.com/pub/gsutil.tar.gz | tar -xz -C $HOME
    - export PATH=${PATH}:$HOME/gsutil
    - curl https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz | tar zx
    - mv linux-amd64/helm /usr/bin/
    - helm version --client
    - curl  -o /usr/bin/sync-repo.sh https://raw.githubusercontent.com/kubernetes/helm/master/scripts/sync-repo.sh
    - chmod a+x /usr/bin/sync-repo.sh
    - curl -L -o /usr/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
    - chmod +x /usr/bin/kubectl
    - kubectl version --client
    # Set context
    - kubectl config get-contexts
    - kubectl config use-context skyfly534/reddit-deploy:reddit-agent
    - kubectl get pods
    # ensuring namespace
    - kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
  script:
    - export track="${1-stable}"
    - >
      if [[ "$track" != "stable" ]]; then
        name="$name-$track"
      fi
    - echo "Clone deploy repository..."
    - git clone $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/reddit-deploy.git
    - echo "Download helm dependencies..."
    - helm dep update reddit-deploy/reddit
    - echo "Deploy helm release $name to $KUBE_NAMESPACE"
    - echo "Upgrading existing release..."
    - >
      helm upgrade \
        --install \
        --wait \
        --set ui.ingress.host="$host" \
        --set $CI_PROJECT_NAME.image.tag="$CI_APPLICATION_TAG" \
        --namespace="$KUBE_NAMESPACE" \
        --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
        "$name" \
        reddit-deploy/reddit/

stop_review:
  stage: cleanup
  variables:
    KUBE_NAMESPACE: review
    GIT_STRATEGY: none
    name: $CI_ENVIRONMENT_SLUG
  environment:
    name: review/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME
    action: stop
  when: manual
  allow_failure: true
  only:
    refs:
      - branches
    kubernetes: active
  except:
    - main
  before_script:
    # installing dependencies
    - apk add -U openssl curl tar gzip bash ca-certificates git
    - wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
    - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk
    - apk add --force-overwrite glibc-2.35-r0.apk
    - apk fix --force-overwrite alpine-baselayout-data
    - rm glibc-2.35-r0.apk
    - curl https://storage.googleapis.com/pub/gsutil.tar.gz | tar -xz -C $HOME
    - export PATH=${PATH}:$HOME/gsutil
    - curl https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz | tar zx
    - mv linux-amd64/helm /usr/bin/
    - helm version --client
    - curl  -o /usr/bin/sync-repo.sh https://raw.githubusercontent.com/kubernetes/helm/master/scripts/sync-repo.sh
    - chmod a+x /usr/bin/sync-repo.sh
    - curl -L -o /usr/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
    - chmod +x /usr/bin/kubectl
    - kubectl version --client
    # Set context
    - kubectl config get-contexts
    - kubectl config use-context skyfly534/reddit-deploy:reddit-agent
    - kubectl get pods
  script:
    - helm delete "$name" --namespace="$KUBE_NAMESPACE" || true

Так же поступаем для post и comment.

Для reddit-deploy

image: alpine:latest

stages:
  - test
  - staging
  - production

test:
  stage: test
  script:
    - exit 0
  only:
    - triggers
    - branches

staging:
  stage: staging
  variables:
    KUBE_NAMESPACE: staging
    CI_REGISTRY: "index.docker.io"
    CI_APPLICATION_REPOSITORY: $CI_REGISTRY/$CI_PROJECT_PATH
    CI_APPLICATION_TAG: $CI_COMMIT_REF_SLUG
    CI_CONTAINER_NAME: ci_job_build_${CI_JOB_ID}
  environment:
    name: staging
    url: http://staging
  only:
    refs:
      - main
  before_script:
    # installing dependencies
    - apk add -U openssl curl tar gzip bash ca-certificates git
    - wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
    - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk
    - apk add --force-overwrite glibc-2.35-r0.apk
    - apk fix --force-overwrite alpine-baselayout-data
    - rm glibc-2.35-r0.apk
    - curl https://storage.googleapis.com/pub/gsutil.tar.gz | tar -xz -C $HOME
    - export PATH=${PATH}:$HOME/gsutil
    - curl https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz | tar zx
    - mv linux-amd64/helm /usr/bin/
    - helm version --client
    - curl  -o /usr/bin/sync-repo.sh https://raw.githubusercontent.com/kubernetes/helm/master/scripts/sync-repo.sh
    - chmod a+x /usr/bin/sync-repo.sh
    - curl -L -o /usr/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
    - chmod +x /usr/bin/kubectl
    - kubectl version --client
    # Set context
    - kubectl config get-contexts
    - kubectl config use-context skyfly534/reddit-deploy:reddit-agent
    - kubectl get pods
    # ensuring namespace
    - kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
  script:
    - export track="${1-stable}"
    - export name="$CI_ENVIRONMENT_SLUG"
    - echo "Release name - $name"
    - helm dep build reddit
    - >
       helm upgrade --install \
        --debug \
        --wait \
        --set ui.ingress.host="$host" \
        --set ui.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/ui/-/raw/main/VERSION)" \
        --set post.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/post/-/raw/main/VERSION)" \
        --set comment.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/comment/-/raw/main/VERSION)" \
        --namespace="$KUBE_NAMESPACE" \
        --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
        "$name" \
        reddit

production:
  stage: production
  variables:
    KUBE_NAMESPACE: production
    CI_REGISTRY: "index.docker.io"
    CI_APPLICATION_REPOSITORY: $CI_REGISTRY/$CI_PROJECT_PATH
    CI_APPLICATION_TAG: $CI_COMMIT_REF_SLUG
    CI_CONTAINER_NAME: ci_job_build_${CI_JOB_ID}
  environment:
    name: production
    url: http://production
  when: manual
  only:
    refs:
      - main
  before_script:
    # installing dependencies
    - apk add -U openssl curl tar gzip bash ca-certificates git
    - wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
    - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk
    - apk add --force-overwrite glibc-2.35-r0.apk
    - apk fix --force-overwrite alpine-baselayout-data
    - rm glibc-2.35-r0.apk
    - curl https://storage.googleapis.com/pub/gsutil.tar.gz | tar -xz -C $HOME
    - export PATH=${PATH}:$HOME/gsutil
    - curl https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz | tar zx
    - mv linux-amd64/helm /usr/bin/
    - helm version --client
    - curl  -o /usr/bin/sync-repo.sh https://raw.githubusercontent.com/kubernetes/helm/master/scripts/sync-repo.sh
    - chmod a+x /usr/bin/sync-repo.sh
    - curl -L -o /usr/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
    - chmod +x /usr/bin/kubectl
    - kubectl version --client
    # Set context
    - kubectl config get-contexts
    - kubectl config use-context skyfly534/reddit-deploy:reddit-agent
    - kubectl get pods
    # ensuring namespace
    - kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
  script:
    - export track="${1-stable}"
    - export name="$CI_ENVIRONMENT_SLUG"
    - echo "Release name - $name"
    - helm dep build reddit
    - >
       helm upgrade --install \
        --debug \
        --wait \
        --set ui.ingress.host="$host" \
        --set ui.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/ui/-/raw/main/VERSION)" \
        --set post.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/post/-/raw/main/VERSION)" \
        --set comment.image.tag="$(curl $CI_SERVER_URL/$CI_PROJECT_NAMESPACE/comment/-/raw/main/VERSION)" \
        --namespace="$KUBE_NAMESPACE" \
        --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
        "$name" \
        reddit
  1. Добавлен блок в конфигурацию CI сервисов для автоматического запуска деплоя после сборки образов.
deploy-app:
  stage: deploy-app
  trigger:
    project: skyfly534/reddit-deploy
    branch: main
  only:
    - main

HW19 Kubernetes. Networks, Storages.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Поднят кластер при помощи terraform в Yandex Cloud (подготовлен в прошлом ДЗ);

Регистрируем кластер в локальном окружении:

$ yc managed-kubernetes cluster get-credentials skyfly535 --external

Context 'yc-skyfly535' was added as default to kubeconfig '/home/roman/.kube/config'.
Check connection to cluster using 'kubectl cluster-info --kubeconfig /home/roman/.kube/config'.

Note, that authentication depends on 'yc' and its config profile 'terraform-profile'.
To access clusters using the Kubernetes API, please use Kubernetes Service Account.

$ kubectl cluster-info
Kubernetes control plane is running at https://158.160.98.118
CoreDNS is running at https://158.160.98.118/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
  1. В сервисе ui настроен тип LoadBalancer для работы с внешним облачным балансировщиком;

Тип LoadBalancer позволяет нам использовать внешний облачный балансировщик нагрузки как единую точку входа в наши сервисы, а не полагаться на IPTables и не открывать наружу весь кластер. Настроим соответствующим образом сервис ui, правим ui-service.yml:

apiVersion: v1
kind: Service
metadata:
  name: ui
  labels:
    app: reddit
    component: ui
spec:
  type: LoadBalancer
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9292
  selector:
    app: reddit
    component: ui
  1. Развернуто тестовое приложение при помощи манифестов из прошлого ДЗ с LoadBalancer;
$ kubectl apply -f kubernetes/reddit/dev-namespace.yml
namespace/dev created

$ kubectl get pods -n dev
NAME                       READY   STATUS    RESTARTS   AGE
comment-56cbfb5bdc-5prjp   1/1     Running   0          83s
comment-56cbfb5bdc-cd8sb   1/1     Running   0          83s
comment-56cbfb5bdc-dbxjd   1/1     Running   0          83s
mongo-7f764c4b5b-rjqz4     1/1     Running   0          81s
post-6848446659-8dbvt      1/1     Running   0          80s
post-6848446659-ctm8h      1/1     Running   0          80s
post-6848446659-kzvz2      1/1     Running   0          80s
ui-59446c685-44q8m         1/1     Running   0          79s
ui-59446c685-94fl7         1/1     Running   0          79s
ui-59446c685-bmsr5         1/1     Running   0          79s

$ kubectl get services -n dev
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
comment      ClusterIP      10.96.144.60    <none>           9292/TCP       89s
comment-db   ClusterIP      10.96.240.222   <none>           27017/TCP      90s
mongodb      ClusterIP      10.96.159.155   <none>           27017/TCP      88s
post         ClusterIP      10.96.130.221   <none>           5000/TCP       86s
post-db      ClusterIP      10.96.170.147   <none>           27017/TCP      87s
ui           LoadBalancer   10.96.251.81    158.160.139.68   80:31833/TCP   85s

$ kubectl get service -n dev --selector component=ui
NAME   TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
ui     LoadBalancer   10.96.239.248   158.160.147.33   80:30286/TCP   7m16s

Проверим в браузере: http://158.160.147.33

Видим, что все поды поднялись, сервисы работают, а у сервиса LoadBalancer появился внешний адрес. По этому адресу и доступно наше приложение.

Балансировка с помощью Service типа LoadBalancer имеет ряд недостатков:

  • Нельзя управлять с помощью http URI (L7-балансировщика)
  • Используются только облачные балансировщики
  • Нет гибких правил работы с трафиком

Для более удобного управления входящим снаружи трафиком и решения недостатков LoadBalancer можно использовать другой объект Kubernetes - Ingress.

  1. Запущен Ingress Controller на базе балансировщика Nginx;

Ingress - это набор правил внутри кластера Kuberntes, предназначенных для того, чтобы входящие подключения могли достичь сервисов. Сами по себе Ingress'ы это просто правила. Для их применения нужен Ingress Controller.

Ingress Controller - это скорее плагин (а значит и отдельный POD), который состоит из 2-х функциональных частей:

  • Приложение, которое отслеживает через k8s API новые объекты Ingress и обновляет конфигурацию балансировщика
  • Балансировщик (Nginx, haproxy, traefik, ...), который и занимается управлением сетевым трафиком

Установка ingress controller:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml

Основные задачи, решаемые с помощью Ingress'ов:

  • Организация единой точки входа в приложения снаружи
  • Обеспечение балансировки трафика
  • Терминация SSL
  • Виртуальный хостинг на основе имен
  1. Создан и применен манифест ingress для сервиса ui;
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ui
spec:
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: ui
            port:
              number: 9292

Изменяем, применяем:

$ kubectl apply -n dev -f ui-service.yml
service/ui configured

$ kubectl apply -n dev -f ui-ingress.yml
ingress.networking.k8s.io/ui created

Смотрим статус:

$ kubectl get ingress ui -n dev
NAME   CLASS    HOSTS   ADDRESS          PORTS   AGE
ui     nginx   *       158.160.148.14   80      16m
  1. Создан TLS сертификат, на его основе создан Secret;

Сгенерирован сертификат:

$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=158.160.148.14"
Generating a RSA private key
..+++++
...............................................................+++++
writing new private key to 'tls.key'
-----
Создан секрет с данным сертификатом:

$ kubectl create secret tls ui-ingress --key tls.key --cert tls.crt -n dev
secret/ui-ingress created

$ kubectl describe secret ui-ingress -n dev
Name:         ui-ingress
Namespace:    dev
Labels:       <none>
Annotations:  <none>

Type:  kubernetes.io/tls

Data
====
tls.crt:  1127 bytes
tls.key:  1704 bytes
  1. Настроен доступ к тестовому приложению по https (с самоподписным сертификатом);

Обновлен манифест ui-ingress.yml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ui
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - secretName: ui-ingress
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: ui
            port:
              number: 9292

Запускаем:

$ kubectl apply -n dev -f ui-ingress.yml
ingress.networking.k8s.io/ui configured

Проверяем:

$ kubectl get ingress ui -n dev
NAME   CLASS   HOSTS   ADDRESS          PORTS     AGE
ui     nginx   *       158.160.148.14   80, 443   2m39s

$ kubectl describe ingress ui -n dev
Name:             ui
Labels:           <none>
Namespace:        dev
Address:          158.160.148.14
Ingress Class:    nginx
Default backend:  <default>
TLS:
  ui-ingress terminates
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /   ui:9292 (10.112.128.15:9292,10.112.129.8:9292,10.112.129.9:9292)
Annotations:  nginx.ingress.kubernetes.io/force-ssl-redirect: true
Events:
  Type    Reason  Age                    From                      Message
  ----    ------  ----                   ----                      -------
  Normal  Sync    2m16s (x2 over 2m54s)  nginx-ingress-controller  Scheduled for sync

Alt text

Дополнительные задания

  1. Создаваемый объект Secret в виде Kubernetes-манифеста;

Выведено содержание созданного секрета:

cat kubernetes/reddit/ui-sec-ingress.yml

Вывод команды сохранен в файл cat ./kubernetes/reddit/ui-sec-ingress.yml.

  1. Ограничен любой трафик, поступающий на mongodb, кроме сервисов post и comment;

В прошлых проектах мы договорились о том, что хотелось бы разнести сервисы базы данных и сервис фронтенда по разным сетям, сделав их недоступными друг для друга. В Kubernetes у нас так сделать не получится с помощью отдельных сетей, так как все POD-ы могут достучаться друг до друга по-умолчанию. Мы будем использовать NetworkPolicy - инструмент для декларативного описания потоков трафика.

Описываем правило в манифесте mongo-network-policy.yml. Применяем:

$ kubectl apply -n dev -f mongo-network-policy.yml
networkpolicy.networking.k8s.io/deny-db-traffic created

Проверяем;

$ kubectl get networkpolicy -n dev
NAME              POD-SELECTOR                 AGE
deny-db-traffic   app=reddit,component=mongo   47m

$ kubectl describe networkpolicy -n dev
Name:         deny-db-traffic
Namespace:    dev
Created on:   2024-02-18 16:41:40 +1000 +10
Labels:       app=reddit
Annotations:  <none>
Spec:
  PodSelector:     app=reddit,component=mongo
  Allowing ingress traffic:
    To Port: <any> (traffic allowed to all ports)
    From:
      PodSelector: app=reddit,component=comment
    From:
      PodSelector: app=reddit,component=post
  Not affecting egress traffic
  Policy Types: Ingress
  1. Создано постоянное хранилище данных для mongodb при помощи PersistentVolume.

Основной Stateful сервис в нашем приложении - это базы данных MongoDB. В текущий момент она запускается в виде Deployment и хранит данные в стандартных Docker Volume-ах. Это имеет несколько проблем:

При удалении POD-а удаляется и Volume Потерям Nod'ы с mongo грозит потерей данных Запуск базы на другой ноде запускает новый экземпляр данных Пробуем удалить deployment для mongo и создать его заново. После запуска пода база оказывается пустой.

Для постоянного хранения данных используется PersistentVolume.

Создадим диск в облаке:

$ yc compute disk create --zone ru-central1-a --name k8s --size 4 --description "disk for k8s"
done (5s)
id: fhmahkjc83c8ucpbk6em
folder_id: b1ghhcttrug793gc11tt
created_at: "2024-02-18T07:42:05Z"
name: k8s
description: disk for k8s
type_id: network-hdd
zone_id: ru-central1-a
size: "4294967296"
block_size: "4096"
status: READY
disk_placement_policy: {}

Описываем в манифесте mongo-volume.yml PersitentVolume

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mongo-pv
spec:
  capacity:
    storage: 4Gi
  accessModes:
    - ReadWriteOnce
  csi:
    driver: disk-csi-driver.mks.ycloud.io
    fsType: ext4
    volumeHandle: fhmahkjc83c8ucpbk6em <--- берем из вывода предыдущей команды

Мы создали ресурс дискового хранилища, распространенный на весь кластер, в виде PersistentVolume. Чтобы выделить приложению часть такого ресурса - нужно создать запрос на выдачу - PersistentVolumeClain. Claim - это именно запрос, а не само хранилище. С помощью запроса можно выделить место как из конкретного PersistentVolume (тогда параметры accessModes и StorageClass должны соответствовать, а места должно хватать), так и просто создать отдельный PersistentVolume под конкретный запрос.

Описываем mongo-claim.yml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongo-pvc
spec:
  storageClassName: ""
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  volumeName: mongo-pv

Обновляем mongo-deployment.yml:

volumes:
       - name: mongo-persistent-storage
         emptyDir: {}    <----- меняем на следующие строки
+        persistentVolumeClaim:
+          claimName: mongo-pvc

Применяем:

$ kubectl apply -f mongo-volume.yml
persistentvolume/mongo-pv created

$ kubectl apply -f mongo-claim.yml -n dev
persistentvolumeclaim/mongo-pvc created

$ kubectl apply -f mongo-deployment.yml -n dev
deployment.apps/mongo configured

Проверяем:

$ kubectl get pv
NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mongo-pv   4Gi        RWO            Retain           Available                                   59s

$ kubectl describe pv mongo-pv
Name:            mongo-pv
Labels:          <none>
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:
Status:          Available
Claim:
Reclaim Policy:  Retain
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        4Gi
Node Affinity:   <none>
Message:
Source:
    Type:              CSI (a Container Storage Interface (CSI) volume source)
    Driver:            disk-csi-driver.mks.ycloud.io
    FSType:            ext4
    VolumeHandle:      fhmahkjc83c8ucpbk6em
    ReadOnly:          false
    VolumeAttributes:  <none>
Events:                <none>

$ kubectl get pvc -n dev
NAME        STATUS    VOLUME                CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mongo-pvc   Pending   mongo-ya-pd-storage   0                                        74s

Ждем около 10 минут

$ kubectl get pods -n dev
NAME                       READY   STATUS    RESTARTS   AGE
comment-56cbfb5bdc-l5c84   1/1     Running   0          102m
comment-56cbfb5bdc-ng22h   1/1     Running   0          102m
comment-56cbfb5bdc-x6trv   1/1     Running   0          102m
mongo-794976987-j4wwk      0/1     Pending   0          5m26s <---- Ждем!
mongo-7f764c4b5b-hfkwm     1/1     Running   0          102m
post-6848446659-ndlmm      1/1     Running   0          102m
post-6848446659-v7gql      1/1     Running   0          102m
post-6848446659-vvsv5      1/1     Running   0          102m
ui-65846d4847-s4ssd        1/1     Running   0          102m
ui-65846d4847-s62qk        1/1     Running   0          102m
ui-65846d4847-wlx5c        1/1     Running   0          102m

Проверяем создание поста с последующим удалением и созданием деплоя mongo. Пост остался на месте.

For more information see: полезное чтиво

HW18 Kubernetes. Запуск кластера и приложения. Модель безопасности.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Подготовлено локальное окружение для работы с Kubernetes:
  • kubectl - главная утилита для работы с Kubernets API (все, что делает kubectl, можно сделать с помощью HTTP-запросов к API k8s)

  • minikube - утилита для разворачивания локальной инсталляции Kubernetes

  • ~/.kube - каталог, который содержит служебную информацию для kubectl (конфиги, кеши, схемы API);

  1. Поднят кластер в minikube;

Стандартный драйвер для развертывания кластера в minikube docker. В данной конфигурации кластера у меня возникли проблемы с доступом к образам моего аккаунта в Docker Hub, поэтому кластер был поднят с драйвером Virtualbox (с ним проблем не было).

 minikube start --driver=virtualbox

В процессе поднятия кластера автоматически настраивается kubectl.

$ kubectl get nodes
NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   32s   v1.28.3
  1. Приведены в соответствие подготовленые на прошлом ДЗ манифесты ui-deployment.yml, component-deployment.yml, post-deployment.yml, mongo-deployment.yml (каталог ./kubernetes/reddit) для развертывания тестового приложения;

  2. Развернута инфраструктура из подготовленных манифестов;

Можно разворачивать по отдельности

kubectl apply -f ui-deployment.yml

kubectl apply -f component-deployment.yml

kubectl apply -f post-deployment.yml

kubectl apply -f mongo-deployment.yml

Можно все сразу

kubectl apply -f kubernetes/reddit

Проверяем:

$  kubectl get pods
NAME                       READY   STATUS    RESTARTS   AGE
comment-698585b76f-48nbc   1/1     Running   0          116s
comment-698585b76f-8pgqf   1/1     Running   0          116s
comment-698585b76f-qr8v2   1/1     Running   0          116s
mongo-64c9bf74db-hbj9n     1/1     Running   0          116s
post-6b48846c57-8hkjs      1/1     Running   0          116s
post-6b48846c57-gtxfs      1/1     Running   0          116s
post-6b48846c57-pfzh7      1/1     Running   0          116s
ui-676bf545dc-6496k        1/1     Running   0          116s
ui-676bf545dc-7np2q        1/1     Running   0          116s
ui-676bf545dc-tqfps        1/1     Running   0          116s

$ kubectl get deployment
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
comment   3/3     3            3           2m35s
mongo     1/1     1            1           2m35s
post      3/3     3            3           2m35s
ui        3/3     3            3           2m35s

Можно пробросить порт пода на локальную машину:

kubectl port-forward ui-676bf545dc-6496k 8080:9292
Forwarding from 127.0.0.1:8080 -> 9292
Forwarding from [::1]:8080 -> 9292

Проверяем в браузере по адресу http://127.0.0.1:8080/

  1. Подготовлены манифесты Service для связи компонентов между собой и с внешним миром;

Service - абстракция, которая определяет набор POD-ов (Endpoints) и способ доступа к ним.

Созданы следующие манифесты comment-service.yml, post-service.yml, mongodb-service.yml, comment-mongodb-service.yml, post-mongodb-service.yml (каталог ./kubernetes/reddit).

$ kubectl get services
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
comment      ClusterIP   10.105.43.64     <none>        9292/TCP         13m
comment-db   ClusterIP   10.108.160.146   <none>        27017/TCP        13m
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP          26m
mongodb      ClusterIP   10.102.121.56    <none>        27017/TCP        13m
post         ClusterIP   10.107.58.225    <none>        5000/TCP         13m
post-db      ClusterIP   10.111.128.9     <none>        27017/TCP        13m
ui           NodePort    10.100.170.152   <none>        9292:31702/TCP   13m
  1. Переназначены переменные окружения, указывающие на сервис comment_db в deployment сервисов ui, comment;

Если пробросить порт сервиса ui наружу, попытаться подключиться к нему, то мы увидим ошибку. Сервис ui ищет совсем другой адрес: comment_db, а не mongodb, как и сервис comment ищет post_db. Эти адреса заданы в их Dockerfile в виде переменных окружения: POST_DATABASE_HOST=post_db и COMMENT_DATABASE_HOST=comment_db.

comment-deployment.yml

containers:
- image: skyfly534/comment
  name: comment
  env:
  - name: COMMENT_DATABASE_HOST
    value: mongodb
post-deployment.yml

containers:
- image: skyfly534/post
  name: post
  env:
  - name: POST_DATABASE_HOST
    value: mongodb

Пересобираем

kubectl apply -f kubernetes/reddit

Пробрасываем порт

kubectl port-forward ui-676bf545dc-6496k 8080:9292

Идем в браузер http://127.0.0.1:8080. Все работает. Пишем посты, сохраняем их.

  1. Написан Service для ui (ui-service.yml) для обеспечения доступа к ui снаружи;
$ minikube service ui
|-----------|------|-------------|-----------------------------|
| NAMESPACE | NAME | TARGET PORT |             URL             |
|-----------|------|-------------|-----------------------------|
| default   | ui   |        9292 | http://192.168.59.101:31702 |
|-----------|------|-------------|-----------------------------|
🎉  Opening service default/ui in default browser...
roman@root-ubuntu:~/DevOps/skyfly535_microservices$ Found ffmpeg: /opt/yandex/browser-beta/libffmpeg.so
	avcodec: 3876708
	avformat: 3874148
	avutil: 3743332
Ffmpeg version is OK! Let's use it.
[43557:43557:0214/193723.242300:ERROR:variations_seed_processor.cc(253)] Trial from abt study=BREXP-6200 already created
[43557:43557:0214/193723.242607:ERROR:variations_seed_processor.cc(253)] Trial from abt study=Spaces already created
[43557:43557:0214/193723.583001:ERROR:isolated_origin_util.cc(74)] Ignoring port number in isolated origin: chrome://custo
Окно или вкладка откроются в текущем сеансе браузера.
$ minikube service list
|-------------|------------|--------------|-----------------------------|
|  NAMESPACE  |    NAME    | TARGET PORT  |             URL             |
|-------------|------------|--------------|-----------------------------|
| default     | comment    | No node port |                             |
| default     | comment-db | No node port |                             |
| default     | kubernetes | No node port |                             |
| default     | mongodb    | No node port |                             |
| default     | post       | No node port |                             |
| default     | post-db    | No node port |                             |
| default     | ui         |         9292 | http://192.168.59.101:31702 |
| kube-system | kube-dns   | No node port |                             |
|-------------|------------|--------------|-----------------------------|

В комплекте с minikube идёт достаточно большое количество дополнений:

$ 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 | disabled     | Kubernetes                     |
| default-storageclass        | minikube | enabled ✅   | Kubernetes                     |
| efk                         | minikube | disabled     | 3rd party (Elastic)            |
| freshpod                    | minikube | disabled     | Google                         |
| gcp-auth                    | minikube | disabled     | Google                         |
| gvisor                      | minikube | disabled     | minikube                       |
| headlamp                    | minikube | disabled     | 3rd party (kinvolk.io)         |
| helm-tiller                 | minikube | disabled     | 3rd party (Helm)               |
| inaccel                     | minikube | disabled     | 3rd party (InAccel             |
|                             |          |              | [info@inaccel.com])            |
| ingress                     | minikube | disabled     | Kubernetes                     |
| ingress-dns                 | minikube | disabled     | minikube                       |
| inspektor-gadget            | minikube | disabled     | 3rd party                      |
|                             |          |              | (inspektor-gadget.io)          |
| istio                       | minikube | disabled     | 3rd party (Istio)              |
| istio-provisioner           | minikube | disabled     | 3rd party (Istio)              |
| kong                        | minikube | disabled     | 3rd party (Kong HQ)            |
| kubeflow                    | minikube | disabled     | 3rd party                      |
| kubevirt                    | minikube | disabled     | 3rd party (KubeVirt)           |
| logviewer                   | minikube | disabled     | 3rd party (unknown)            |
| metallb                     | minikube | disabled     | 3rd party (MetalLB)            |
| metrics-server              | minikube | disabled     | Kubernetes                     |
| nvidia-device-plugin        | minikube | disabled     | 3rd party (NVIDIA)             |
| nvidia-driver-installer     | minikube | disabled     | 3rd party (Nvidia)             |
| nvidia-gpu-device-plugin    | minikube | disabled     | 3rd party (Nvidia)             |
| olm                         | minikube | disabled     | 3rd party (Operator Framework) |
| pod-security-policy         | minikube | disabled     | 3rd party (unknown)            |
| portainer                   | minikube | disabled     | 3rd party (Portainer.io)       |
| registry                    | minikube | disabled     | minikube                       |
| registry-aliases            | minikube | disabled     | 3rd party (unknown)            |
| registry-creds              | minikube | disabled     | 3rd party (UPMC Enterprises)   |
| storage-provisioner         | minikube | enabled ✅   | minikube                       |
| storage-provisioner-gluster | minikube | disabled     | 3rd party (Gluster)            |
| storage-provisioner-rancher | minikube | disabled     | 3rd party (Rancher)            |
| volumesnapshots             | minikube | disabled     | Kubernetes                     |
|-----------------------------|----------|--------------|--------------------------------|
  1. Запущен dashboard для отслеживания состояния и управления кластером;
$ minikube dashboard
🔌  Enabling dashboard ...
    ▪ Используется образ docker.io/kubernetesui/dashboard:v2.7.0
    ▪ Используется образ docker.io/kubernetesui/metrics-scraper:v1.0.8
💡  Some dashboard features require the metrics-server addon. To enable all features please run:

	minikube addons enable metrics-server


🤔  Verifying dashboard health ...
🚀  Launching proxy ...
🤔  Verifying proxy health ...
🎉  Opening http://127.0.0.1:36359/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
Found ffmpeg: /opt/yandex/browser-beta/libffmpeg.so
	avcodec: 3876708
	avformat: 3874148
	avutil: 3743332
Ffmpeg version is OK! Let's use it.
[45337:45337:0214/195604.197044:ERROR:variations_seed_processor.cc(253)] Trial from abt study=BREXP-6200 already created
[45337:45337:0214/195604.197256:ERROR:variations_seed_processor.cc(253)] Trial from abt study=Spaces already created
[45337:45337:0214/195604.461036:ERROR:isolated_origin_util.cc(74)] Ignoring port number in isolated origin: chrome://custo
Окно или вкладка откроются в текущем сеансе браузера.

После активации dashboard она откроется в браузере. Можно посмотреть состояние кластера со всех сторон:

  • Отслеживать состояние кластера и рабочих нагрузок в нём;

  • Создавать новые объекты (загружать YAML-файлы);

  • Удалять и изменять объекты (кол-во реплик, YAML-файлы);

  • Отслеживать логи в POD-ах;

  • При включении Heapster-аддона смотреть нагрузку на POD-ах.

  1. Подготовлен манифест dev-namespace.yml для отделения среды разработки тестового приложения от всего остального;

Для того, чтобы выбрать конкретное пространство имен, нужно указать флаг -n или –namespace при запуске kubectl.

Проверяем имеющиеся у нас в кластере

$ kubectl get all -n kube-system
NAME                                   READY   STATUS    RESTARTS   AGE
pod/coredns-5dd5756b68-lb2wk           1/1     Running   0          52m
pod/etcd-minikube                      1/1     Running   0          53m
pod/kube-apiserver-minikube            1/1     Running   0          53m
pod/kube-controller-manager-minikube   1/1     Running   0          53m
pod/kube-proxy-bbfqc                   1/1     Running   0          52m
pod/kube-scheduler-minikube            1/1     Running   0          53m
pod/storage-provisioner                1/1     Running   0          53m

NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
service/kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   53m

NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
daemonset.apps/kube-proxy   1         1         1       1            1           kubernetes.io/os=linux   53m

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/coredns   1/1     1            1           53m

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/coredns-5dd5756b68   1         1         1       53m

При старте Kubernetes кластер имеет 3 namespace:

  • default - для объектов для которых не определен другой Namespace (в нём мы работали все это время)
  • kube-system - для объектов созданных Kubernetes и для управления им
  • kube-public - для объектов к которым нужен доступ из любой точки кластера

Для того, чтобы выбрать конкретное пространство имен, нужно указать флаг -n или –namespace при запуске kubectl.

apiVersion: v1
kind: Namespace
metadata:
  name: dev

Применим изменения

$ kubectl apply -f dev-namespace.yml
namespace/dev created

$ kubectl apply -n dev -f kubernetes/reddit/
  1. Создан Kubernetes кластер под названием skyfly535 через веб-интерфейс консоли Yandex Cloud (Managed Service for kubernetes);
  • Идём в Yandex Cloud, перейдите в "Managed Service for kubernetes"

  • Жмём "Создать Cluster"

  • Имя кластера может быть произвольным

  • Если нет сервис аккаунта его можно создать

  • Релизный канал *** Rapid ***

  • Версия k8s 1.25

  • Зона доступности - на ваше усмотрение (сети - аналогично)

  • Жмём "Создать"" и ждём, пока поднимется кластер

После создания кластера, вам нужно создать группу узлов, входящих в кластер

  • Версия k8s 1.25

  • Количество узлов - 2

  • vCPU - 4

  • RAM - 8

  • Disk - SSD 96ГБ (минимальное значение)

  • В поле "Доступ" добавьте свой логин и публичный ssh-ключ

После поднятия кластера настраиваем к нему доступ:

$ yc managed-kubernetes cluster get-credentials skyfly535 --external

Context 'yc-skyfly535' was added as default to kubeconfig '/home/roman/.kube/config'.
Check connection to cluster using 'kubectl cluster-info --kubeconfig /home/roman/.kube/config'.

Note, that authentication depends on 'yc' and its config profile 'terraform-profile'.
To access clusters using the Kubernetes API, please use Kubernetes Service Account.

$ kubectl config get-contexts
CURRENT   NAME           CLUSTER                               AUTHINFO                              NAMESPACE
          yc-skyfly      yc-managed-k8s-cat2ru389rf94j781as5   yc-managed-k8s-cat2ru389rf94j781as5
          yc-skyfly534   yc-managed-k8s-cat895hbh2845cqv67tb   yc-managed-k8s-cat895hbh2845cqv67tb
*         yc-skyfly535   yc-managed-k8s-catcovvk572g06lr0tqj   yc-managed-k8s-catcovvk572g06lr0tqj

вводим команду из выхлопа команды yc managed-kubernetes cluster get-credentials skyfly535 --external

$ kubectl cluster-info --kubeconfig /home/roman/.kube/config
Kubernetes control plane is running at https://178.154.205.197
CoreDNS is running at https://178.154.205.197/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
  1. Запущено тестовое приложение в кластере YC;
$ kubectl apply -f kubernetes/reddit/dev-namespace.yml
namespace/dev created

$ kubectl apply -n dev -f kubernetes/reddit/
deployment.apps/comment created
service/comment-db created
service/comment created
namespace/dev unchanged
deployment.apps/mongo created
service/mongodb created
deployment.apps/post created
service/post-db created
service/post created
deployment.apps/ui created
service/ui created

$ kubectl get services -n dev
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
comment      ClusterIP   10.96.245.190   <none>        9292/TCP         39s
comment-db   ClusterIP   10.96.207.56    <none>        27017/TCP        40s
mongodb      ClusterIP   10.96.208.88    <none>        27017/TCP        37s
post         ClusterIP   10.96.211.182   <none>        5000/TCP         36s
post-db      ClusterIP   10.96.245.177   <none>        27017/TCP        36s
ui           NodePort    10.96.152.96    <none>        9292:31758/TCP   35s

$ kubectl get pods -n dev
NAME                       READY   STATUS    RESTARTS   AGE
comment-56cbfb5bdc-gftjr   1/1     Running   0          54s
comment-56cbfb5bdc-j2dnw   1/1     Running   0          54s
comment-56cbfb5bdc-n8sqb   1/1     Running   0          54s
mongo-7f764c4b5b-6h78x     1/1     Running   0          52s
post-6848446659-8786l      1/1     Running   0          51s
post-6848446659-8pnkl      1/1     Running   0          51s
post-6848446659-phrg7      1/1     Running   0          51s
ui-59446c685-64ttz         1/1     Running   0          49s
ui-59446c685-dmkdt         1/1     Running   0          49s
ui-59446c685-fbng5         1/1     Running   0          49s

$ kubectl get nodes -o wide
NAME                        STATUS   ROLES    AGE   VERSION   INTERNAL-IP   EXTERNAL-IP       OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
cl1rb9rs892oqjsut8v4-iwuj   Ready    <none>   92m   v1.25.4   10.128.0.15   178.154.205.124   Ubuntu 20.04.6 LTS   5.4.0-167-generic   containerd://1.6.22
cl1v5h010ohkknr8cbbs-ehot   Ready    <none>   92m   v1.25.4   10.128.0.23   158.160.107.18    Ubuntu 20.04.6 LTS   5.4.0-167-generic   containerd://1.6.22

Идем в браузер http://178.154.205.124:31758/, проверяем. Все работает.

  1. Развернут Kubernetes-кластер в YC с помощью Terraform;

Нагуглена конфигурация Terraform для развертывания Kubernetes кластера с использованием ресурсов yandex_kubernetes_cluster и yandex_kubernetes_node_group (каталог ./kubernetes/terraform_YC_k8s).

$ kubectl cluster-info
Kubernetes control plane is running at https://178.154.205.197
CoreDNS is running at https://178.154.205.197/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
  1. Создан YAML-манифесты для описания созданных сущностей для включения dashboard;

Для установки dashboard воспользуемся стандартным манифестом со страницы разработчика (https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/). Сохраним манифест в каталог kubernetes/dashboard.

$ wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

Для того, чтобы полноценно управлять кластером, нужно создать пользователя с ролью cluster-admin. Подготовим манифесты admin-account.yaml admin-roleBinding.yaml и сохраним их рядом с манифестом dashboard.

Применяем

kubectl apply -f dashboard/
serviceaccount/admin-user created
clusterrolebinding.rbac.authorization.k8s.io/admin-user unchanged
namespace/kubernetes-dashboard unchanged
serviceaccount/kubernetes-dashboard unchanged
service/kubernetes-dashboard unchanged
secret/kubernetes-dashboard-certs unchanged
secret/kubernetes-dashboard-csrf configured
Warning: resource secrets/kubernetes-dashboard-key-holder is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
secret/kubernetes-dashboard-key-holder configured
configmap/kubernetes-dashboard-settings unchanged
role.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
deployment.apps/kubernetes-dashboard unchanged
service/dashboard-metrics-scraper unchanged
deployment.apps/dashboard-metrics-scraper unchanged

$ kubectl get pods --all-namespaces
NAMESPACE              NAME                                                  READY   STATUS    RESTARTS       AGE
dev                    comment-56cbfb5bdc-gftjr                              1/1     Running   0              22m
dev                    comment-56cbfb5bdc-j2dnw                              1/1     Running   0              22m
dev                    comment-56cbfb5bdc-n8sqb                              1/1     Running   0              22m
dev                    mongo-7f764c4b5b-6h78x                                1/1     Running   0              22m
dev                    post-6848446659-8786l                                 1/1     Running   0              22m
dev                    post-6848446659-8pnkl                                 1/1     Running   0              22m
dev                    post-6848446659-phrg7                                 1/1     Running   0              22m
dev                    ui-59446c685-64ttz                                    1/1     Running   0              21m
dev                    ui-59446c685-dmkdt                                    1/1     Running   0              21m
dev                    ui-59446c685-fbng5                                    1/1     Running   0              21m
kube-system            calico-node-l4lzt                                     1/1     Running   0              112m
kube-system            calico-node-ndh9j                                     1/1     Running   0              113m
kube-system            calico-typha-7dc6645875-659s7                         1/1     Running   0              111m
kube-system            calico-typha-horizontal-autoscaler-785c94fb55-tr4zn   1/1     Running   0              115m
kube-system            calico-typha-vertical-autoscaler-7679879786-qb2ck     1/1     Running   3 (112m ago)   115m
kube-system            coredns-5c5df59fd4-jlqbh                              1/1     Running   0              115m
kube-system            coredns-5c5df59fd4-jwf9q                              1/1     Running   0              112m
kube-system            ip-masq-agent-c6wzw                                   1/1     Running   0              113m
kube-system            ip-masq-agent-z87rp                                   1/1     Running   0              112m
kube-system            kube-dns-autoscaler-55c4f55869-rdvvv                  1/1     Running   0              115m
kube-system            kube-proxy-r9xzs                                      1/1     Running   0              113m
kube-system            kube-proxy-tfrrs                                      1/1     Running   0              112m
kube-system            metrics-server-9b4bf686c-55lr2                        2/2     Running   0              112m
kube-system            npd-v0.8.0-9724z                                      1/1     Running   0              112m
kube-system            npd-v0.8.0-pdqtv                                      1/1     Running   0              113m
kube-system            yc-disk-csi-node-v2-jgk5d                             6/6     Running   0              113m
kube-system            yc-disk-csi-node-v2-qbtpp                             6/6     Running   0              112m
kubernetes-dashboard   dashboard-metrics-scraper-64bcc67c9c-lgtzd            1/1     Running   0              25s
kubernetes-dashboard   kubernetes-dashboard-5c8bd6b59-lnplz                  1/1     Running   0              27s

Получаем Bearer Token для ServiceAccount

kubectl -n kubernetes-dashboard create token admin-user

выполняем kubectl proxy, Dashboard UI открывается через URL http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

Вводим токен для авторизации и попадаем в dashboard.

Alt text

HW17 Введение в kubernetes.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Описано тестовое приложение в контексте Kubernetes с помощью манифестов в формате yaml. Для каждого сервиса создан Deployment манифест; Каталог с подготовленными манифестами ./kubernetes/reddit. Использованы подготовленные в предыдущих ДЗ контейнеры: skyfly534/comment, skyfly534/post, skyfly534/ui.

  2. Подняты при помощи terraform две ноды для установки k8s с требуемвми характеристиками:

  • RAM 4
  • CPU 4
  • SSD 40 GB Процедура развертывания нод была взята из предыдущих ДЗ с небольшими доработками (./kubernetes/terraform);
  1. Подготовлена конфигурация Ansible для развертывания кластера k8s: одна нода выполняет роль master , вторая - worker; Динамический инвентори берем также из предыдущих ДЗ. Убеждаемся в работоспособности:
ansible all -m ping
178.154.204.151 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
158.160.123.51 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Прописываем хосты, запускаем playbook на исполнение:

ansible-playbook -vv k8s_kubeadm.yml

После завершения работы плейбука проверяем состояние кластера на master ноде:

kubectl get nodes
NAME                   STATUS   ROLES           AGE     VERSION
fhm1d49tbkqfgj81so5v   Ready    control-plane   2m16s   v1.28.2
fhm3bvdi0qvsn4eahq2u   Ready    <none>          76s     v1.28.2
  1. Применены подготовленные манифесты :
kubectl apply -f <manifest>.yml

Смотрим результат:

kubectl get pods
NAME                                  READY   STATUS    RESTARTS   AGE
comment-deployment-6d94947df4-2ljsm   1/1     Running   0          36s
mongo-deployment-f95c7c4fd-pj4ll      1/1     Running   0          27s
post-deployment-754f5447d7-ctjxt      1/1     Running   0          20s
ui-deployment-67b674f4b-txzxl         1/1     Running   0          12s

HW16 Введение в мониторинг. Системы мониторинга.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Создан Docker хост в Yandex Cloud;
yc compute instance create \
  --name docker-host \
  --zone ru-central1-c \
  --network-interface subnet-name=default-ru-central1-c,nat-ip-version=ipv4 \
  --create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1804-lts,size=15 \
  --ssh-key ~/.ssh/id_rsa.pub

и инициализировано окружение Docker;

docker-machine create \
  --driver generic \
  --generic-ip-address=51.250.32.67 \
  --generic-ssh-user yc-user \
  --generic-ssh-key ~/.ssh/id_rsa  \
  docker-host

eval $(docker-machine env docker-host)
  1. Запущен контейнер с системой мониторинга Prometheus из готовым образом с DockerHub;
$ docker run --rm -p 9090:9090 -d --name prometheus prom/prometheus
  1. Изучены web интерфейс системы мониторинга, метрики по умолчанию;

  2. Изучен раздел Targets (цели) и формат собираемых метрик, доступных по адресу host:port/metrics;

  3. Создан Dockerfile ( ./monitoring/prometheus/Dockerfile) при помощи которого копируем файл конфигурации prometheus.yml с "нашей" машины внутрь контейнера;

  4. Созданы образы микросервисов ui, post-py и comment при помощи скриптов docker_build.sh, которые есть в директории каждого сервиса соответственно для добавления информации из Git в наш healthcheck;

/src/ui $ bash docker_build.sh
/src/post-py $ bash docker_build.sh
/src/comment $ bash docker_build.sh

или сразу все из корня репозитория

for i in ui post-py comment; do cd src/$i; bash docker_build.sh; cd -; done
  1. Создан файл docker/docker-compose.yml для совместного развертывания микросервисов ui, post-py, comment и системы мониторинга Prometheus;

  2. Добавлен сервис prom/node-exporter:v0.15.2 в docker/docker-compose.yml для сбора информации о работе Docker хоста (нашей ВМ) и представления этой информации в Prometheus;

Alt text

Ссылка на докер хаб с собранными образами

https://hub.docker.com/repositories/skyfly534

Дополнительные задания

  1. Добавлен сервис percona/mongodb_exporter:0.40 в docker/docker-compose.yml для сбора информации о работе СУБД MongoDB и представления этой информации в Prometheus;

docker-compose.yml:

...

mongo-exporter:
    image: percona/mongodb_exporter:0.40
    command:
      - '--mongodb.uri=mongodb://post_db:27017'
      - '--collect-all'
      - '--log.level=debug'
    ports:
      - '9216:9216'
    networks:
      - back_net

...

prometheus.yml:

...

- job_name: 'mongodb'
    static_configs:
      - targets:
        - 'mongo-exporter:9216'

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

monitoring/blackbox/Dockerfile:

FROM prom/blackbox-exporter:latest
ADD config.yml /etc/blackbox_exporter/

monitoring/blackbox/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

docker-compose.yml:

...

blackbox-exporter:
    image: ${USERNAMEDEVOPS}/blackbox-exporter
    networks:
      - front_net
    depends_on:
      - ui
      - post
      - comment

...

monitoring/prometheus/prometheus.yml:

- job_name: 'blackbox'
    metrics_path: /metrics
    params:
      module: [http_2xx]
    static_configs:
      - targets:
        - http://ui:9292
        - 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

Итоговый список endpoint-ов Prometheus

Alt text

  1. Написаны Makefile в каждом из каталогов ./src/ui/Makefilecd, ./src/post-py/Makefilecd, ./src/comment/Makefilecd и ./src/Makefilecd , которые "билдят" либо "пушат" каждые образ по отдельности, либо все сразу.

Для сборки отдельного образа выполняем make в соответствующем каталоге. Для "пуша" в DockerHub выполняем make push. Эти же команды в родительском каталоге будут действовать на все три сервиса.

HW15 Устройство Gitlab CI. Построение процесса непрерывной поставки.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Подготовлен образ ВМ при помощи packer с минимальными для Gitlab CI требованиями;
  • 2 core СPU
  • 4 GB RAM
  • 50 GB HDD

Изначально созданный файл конфига образа docker.json, был преобразован средствами packer в формат HCL

packer hcl2_upgrade -with-annotations ./packer/docker.json

В результате был сформирован файл конфига ./gitlab-ci/packer/docker.json.pkr.hcl и сформирован файл с переменными ./gitlab-ci/packer/yandex.pkrvars.hcl.

Проверка шаблона и запуск сборки образа

packer validate -var-file=yandex.pkrvars.hcl docker.json.pkr.hcl

packer build -var-file=yandex.pkrvars.hcl docker.json.pkr.hcl
  1. Запущена ВМ из образа packer при помощи terraform;

В кталоге ./gitlab-ci/terraform/ выполняем следующие команды:

terraform validate

terraform apply
  1. При помощи ansible playbook ./gitlab-ci/ansible/gitlab_ci_in_docker.yml развернут Gitlab в контейнере;

Используем плагин inventory для YC из предыдущих ДЗ.

Для работы с контейнерами используем модуль docker_container_module.

$ ansible-playbook gitlab_ci_in_docker.yml

PLAY [Gitlab CI deployment in Docker] ****************************************************************************

TASK [Gathering Facts] *******************************************************************************************
ok: [51.250.85.59]

TASK [create dirs for data volumes] ******************************************************************************
changed: [51.250.85.59] => (item=/srv/gitlab/config)
changed: [51.250.85.59] => (item=/srv/gitlab/logs)
changed: [51.250.85.59] => (item=/srv/gitlab/data)

TASK [install PIP] ***********************************************************************************************
changed: [51.250.85.59]

TASK [install Docker] ********************************************************************************************
changed: [51.250.85.59]

TASK [Gitlab CI deployment in Docker] ****************************************************************************
changed: [51.250.85.59]

PLAY RECAP *******************************************************************************************************
51.250.85.59               : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

После запуска открываем браузер и идем на http://51.250.85.59 (внешний IP ВМ).

Login для первого входа root. Что узнать пароль заходим на ВМ и выполняем следующую команду:

sudo docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password

где gitlab имя контейнера.

  1. Через веб-интерфейс создана группа homework, в ней проект example. Склонирован репозиторий на локальную машину;
git clone http://51.250.85.59/homework/example.git

так же понадобятся аутентификационные данные описанные выше.

  1. В корне репозитория создана начальная версия тестового пайплайна ./.gitlab-ci.yml и запушина в репозиторий;
git add .gitlab-ci.yml

git commit -m "Add pipeline definition"

git push

В CI/CD -> Pipelines видим, что запушеный пайплайн застрял в состоянии pending. Необходимо установить раннеры, которые могут выполнять работу.

  1. Установлен, запущен и зарегистрирован раннер (в контейнере);
sudo mkdir -p /srv/gitlab-runnner/config

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

docker exec -it gitlab-runner gitlab-runner register \
--url http://51.250.85.59/ \
--non-interactive \
--locked=false \
--name DockerRunner \
--executor docker \
--docker-image alpine:latest \
--registration-token GR13489417h3XsW7nxUeqx1U3MLnR \
--tag-list "linux,xenial,ubuntu,docker" \
--run-untagged

Пайплайн вышел из застрявшего состояния, отработал без ошибок.

  1. Добавлено приложение reddit в проект;
git clone https://github.com/express42/reddit.git

Не забываем удалить каталог .git у склонированного иначе не запуши
rm -rf ./reddit/.git

git add reddit/

git commit -m "Add reddit app"

git push
  1. Добавлен запуск тестов приложения reddit в пайплайн;
image: ruby:2.4.2
stages:
...

variables:
  DATABASE_URL: 'mongodb://mongo/user_posts'
before_script:
  - cd reddit
  - bundle install
...

test_unit_job:
  stage: test
  services:
    - mongo:latest
  script:
    - ruby simpletest.rb
...

Создан файл с тестом reddit/simpletest.rb

require_relative './app'
require 'test/unit'
require 'rack/test'

set :environment, :test

class MyAppTest < Test::Unit::TestCase
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end

  def test_get_request
    get '/'
    assert last_response.ok?
  end
end

В reddit/Gemfile добавлена библиотека rack-test для тестирования

...
gem 'sinatra', '~> 2.0.1'
gem 'haml'
gem 'bson_ext'
gem 'bcrypt'
gem 'puma'
gem 'mongo'
gem 'json'
gem 'rack-test' <---
...
  1. Добавлены окружения dev, beta (stage) и production;
stages:
- build
- test
- review <---
...

build_job:
...
test_unit_job:
...

test_integration_job:
...

deploy_dev_job:
  stage: review
  script:
    - echo 'Deploy'
  environment:
    name: dev
    url: http://dev.example.com
- build
   - test
   - review
   - stage      <---
   - production <---
...

staging: # stage окружение
  stage: stage
  when: manual
  script:
    - echo 'Deploy'
  environment:
    name: beta
    url: http://beta.example.com

production: # production окружение
  stage: production
  when: manual
  script:
    - echo 'Deploy'
  environment:
    name: production
    url: http://example.com
  1. Дбавлено условие, при котором на stage и production пойдут только те ветки, которые отмечены тегом;
staging: # stage окружение
  stage: stage
  when: manual
  only: # условие, при котором на stage пойдут только те ветки, которые отмечены тегом
    - tags
  script:
    - echo 'Deploy'
  environment:
    name: beta
    url: http://beta.example.com

production: # production окружение
  stage: production
  when: manual
  only: # условие, при котором на production пойдут только те ветки, которые отмечены тегом
    - tags
  script:
    - echo 'Deploy'
  environment:
    name: production
    url: http://example.com
  1. Cоздано динамические окружение для всех веток, исключая главную ветку main;
branch_review:
  stage: review
  script: echo "Deploy to $CI_ENVIRONMENT_SLUG"
  environment:
    name: branch/$CI_COMMIT_REF_NAME
    url: http://$CI_ENVIRONMENT_SLUG.example.com
  only:
    - branches
  except:
    - main

Дополнительное задание

  1. В этап пайплайна build добавлен запуск контейнера с приложением reddit. Контейнер с reddit деплоился на окружение, динамически создаваемое для каждой ветки в Gitlab;
reddit_run:
  stage: build
  environment:  # Выкачивает с dockerhub образ приложения skyfly534/otus-reddit:1.0 и запускает контейнер с reddit
    name: branch/$CI_COMMIT_REF_NAME
    url: http://$CI_ENVIRONMENT_SLUG.example.com
  image: skyfly534/otus-reddit:1.0
  before_script:
    - echo 'Docker for run reddit'
  script:
    - echo 'Run reddit'

используем для выполнения задания подготовленный в предыдущем ДЗ образ приложения skyfly534/otus-reddit:1.0 и загруженный на dockerhub

  1. Автоматизированно развёртывание GitLab Runner при помощи Ansible плейбук ./gitlab-ci/ansible/gitlab_runner_in_docker.yml.

Регистрация пройдет только при запуске плейбука с тегом registration. Перед запуском плейбука необходимо внести URL CI сервера и регистрационный токен для runner. Переменные не вынесены в файл .env для наглядности.

HW14 Docker: сети, docker-compose.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Изучена работа контейнера с различными сетевыми драйверами none, host;
docker run -ti --rm --network none joffotron/docker-net-tools -c ifconfig

docker run -ti --rm --network host joffotron/docker-net-tools -c ifconfig

docker run --network host -d nginx (запускаем несколько раз)

Вывод docker logs <CONTAINER ID nginx> говорит о том, что все контейнеры nginx кроме первого остановленны, т.к. сеть хоста одна, а порт занят первым запущенным контейнером.

nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
  1. Изучена работа с сетевыми алиасами при запуске тестового проекта Microservices Reddit с использованием bridge-сети;
docker network create reddit --driver bridge

docker run -d --network=reddit --network-alias=post_db --network-alias=comment_db mongo:4
docker run -d --network=reddit --network-alias=post skyfly534/post:1.0
docker run -d --network=reddit --network-alias=comment skyfly534/comment:2.0
docker run -d --network=reddit -p 9292:9292 skyfly534/ui:3.0
  1. Запущен проект в 2-х bridge сетях back_net и front_net, чтобы сервис ui не имел доступа к базе данных;
docker network create back_net --subnet=10.0.2.0/24
docker network create front_net --subnet=10.0.1.0/24

docker run -d --network=front_net -p 9292:9292 --name ui  skyfly534/ui:3.0
docker run -d --network=back_net --name comment  skyfly534/comment:2.0
docker run -d --network=back_net --name post  skyfly534/post:1.0
docker run -d --network=back_net --name mongo_db --network-alias=post_db --network-alias=comment_db mongo:4

docker network connect front_net post
docker network connect front_net comment
  1. Изучен сетевой и правила iptables стек после запуска проекта;
docker-machine ssh docker-host
sudo apt-get update && sudo apt-get install bridge-utils

docker network ls

ifconfig | grep br

brctl show <interface>
sudo iptables -nL -t nat

Docker-compose.

  1. Файл docker-compose.yml (указанный в методичке) переработан для работы с 2-мя сетями и сетевыми алиасами. Произведена параметризация с помощью переменных окружения (файле .env);
# Переменные для Docker-compose.yml
COMPOSE_PROJECT_NAME=reddit
USERNAMEDEVOPS=skyfly534
VER_DB=3.2
DB_PATH=/data/db
VER_UI=3.0
UI_PORT=80
VER_POST=1.0
VER_COMMENT=2.0

Базовое имя образа формируется из названия папки и названия контейнера. Его можно изменить при помощи переменной окружения COMPOSE_PROJECT_NAME, либо указать в параметре ключа -p при запуске docker compose.

Дополнительное задание

  1. При помощи файла docker-compose.override.yml переопределена базовая конфигурация с целью переопределения инструкции command контейнеров comment и ui и для создания volumes (каталогов) и осуществления импорта кодом приложения внутрь контейнеров.
version: '3.3'
services:

  ui:
    command: puma --debug -w 2
    volumes:
      - ./ui:/app

  post:
    volumes:
      - ./post-py:/app

  comment:
    command: puma --debug -w 2
    volumes:
      - ./comment:/app

HW13 Docker-образы. Микросервисы.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Для выполнения домашнего задания и дальнейшей работы с Docker- образами установлен и протестирован linter;
$ /bin/hadolint Dockerfile
Dockerfile:6 DL3013 warning: Pin versions in pip. Instead of `pip install <package>` use `pip install <package>==<version>` or `pip install --requirement <requirements file>`
Dockerfile:6 DL3018 warning: Pin versions in apk add. Instead of `apk add <package>` use `apk add <package>=<version>`
Dockerfile:6 DL3042 warning: Avoid use of cache directory with pip. Use `pip install --no-cache-dir <package>`
  1. Cоздана необходимая структура для развертывания приложения;

Пприложение состоит из трех компонентов:

post-py - сервис отвечающий за написание постов

comment - сервис отвечающий за написание комментариев

ui - веб-интерфейс, работающий с другими сервисами

также требуется база данных MongoDB

  1. Для каждого сервиса создан Dockerfile для дальнейшего создания образа контейнеров;

  2. Собраны образы с сервисами post:1.0, comment:1.0, ui:1.0;

docker pull mongo:4
docker build -t skyfly534/post:1.0 ./post-py
docker build -t skyfly534/comment:1.0 ./comment
docker build -t skyfly534/ui:1.0 ./ui
docker network create reddit

В процессе сборки для замены неотвечающего репозитория задействован архивный репозиторий deb http://archive.debian.org/debian stretch main:

FROM ruby:2.2
RUN set -x \
 && echo "deb http://archive.debian.org/debian stretch main" > /etc/apt/sources.list \
 && apt-get update -qq \
 && apt-get install -y build-essential \
 && apt-get clean
  1. Создана bridge-сеть для контейнеров с именем reddit;

  2. Запущены контейнеры c сетевыми алиасами из подготовленных образов;

docker run -d --network=reddit --network-alias=post_db --network-alias=comment_db mongo:4
docker run -d --network=reddit --network-alias=post skyfly534/post:1.0
docker run -d --network=reddit --network-alias=comment skyfly534/comment:1.0
docker run -d --network=reddit -p 9292:9292 skyfly534/ui:1.0

При использлвании самой свежей mongo приложение возвратило ошибку (Can't show blog posts, some problems with the post service), вызвана она тем, что используется слишком старый драйвер БД. Запускаем БД версии ниже 6.

Проверели прриложение, зашли на http://<docker-host-ip>:9292/

Дополнительное задание

  1. Запущены контейнеры с другими сетевыми алиасами;

Переменные окружения при этом заданы через параметр -e

docker run -d --network=reddit --network-alias=skyfly_post_db --network-alias=skyfly_comment_db mongo:4
 docker run -d --network=reddit --network-alias=skyfly_post -e POST_DATABASE_HOST=skyfly_post_db skyfly534/post:1.0
 docker run -d --network=reddit --network-alias=skyfly_comment -e COMMENT_DATABASE_HOST=skyfly_comment_db skyfly534/comment:1.0
 docker run -d --network=reddit -p 9292:9292 -e POST_SERVICE_HOST=skyfly_post -e COMMENT_SERVICE_HOST=skyfly_comment skyfly534/ui:1.0

  1. Создан новый Dockerfile для сервиса ui, новый образ skyfly534/ui:2.0 собран на базе ubuntu:16.04;

Произведена сверка размеров образов:

docker images
REPOSITORY          TAG       IMAGE ID       CREATED              SIZE
skyfly534/ui        1.0       b94659d48b1b   About a minute ago   999MB
skyfly534/ui        2.0       64cc255f75da   7 minutes ago        485MB
skyfly534/comment   1.0       1beacb74836b   11 minutes ago       996MB
skyfly534/post      1.0       8120ff85bbb3   13 minutes ago       67.2MB
mongo               4         a04ee971f462   4 days ago           434MB

Дополнительное задание

  1. Созданы новые Dockerfile для сервисов ui и comment, новые образы skyfly534/ui:3.0 и skyfly534/comment:2.0 собранs на базе alpine:3.14;

Произведена сверка размеров образов:

docker images
REPOSITORY          TAG       IMAGE ID       CREATED         SIZE
skyfly534/ui        3.0       8c509ac24622   9 minutes ago   93.6MB
skyfly534/ui        1.0       b94659d48b1b   4 hours ago     999MB
skyfly534/ui        2.0       64cc255f75da   4 hours ago     485MB
skyfly534/comment   1.0       1beacb74836b   4 hours ago     996MB
skyfly534/post      1.0       8120ff85bbb3   4 hours ago     67.2MB
mongo               4         a04ee971f462   4 days ago      434MB
  1. Создан Docker volume reddit_db и подключен в контейнер с MongoDB по пути /data/db.
docker run -d --network=reddit --network-alias=post_db --network-alias=comment_db -v reddit_db:/data/db mongo:4

Произведена проверка путем проверки наличия написанного поста в приложении после пересоздания контейнеров.

На память

docker pull mongo:4

docker build -t skyfly534/post:1.0 ./post-py
docker build -t skyfly534/comment:2.0 ./comment
docker build -t skyfly534/ui:3.0 ./ui

docker network create reddit

docker run -d --network=reddit --network-alias=post_db --network-alias=comment_db mongo:4
docker run -d --network=reddit --network-alias=post skyfly534/post:1.0
docker run -d --network=reddit --network-alias=comment skyfly534/comment:2.0
docker run -d --network=reddit -p 9292:9292 skyfly534/ui:3.0

HW12 Технология контейнеризации. Введение в Docker.

В процессе выполнения ДЗ выполнены следующие мероприятия:

  1. Установлен Docker по официальной документации. Проверена версия установленного ПО;

  2. Текущий пользователь добавлен к группе безопасности docker (для работы без sudo);

sudo groupadd docker

sudo usermod -aG docker $USER

newgrp docker
  1. Скачан и запущен теcтовый контейнер;
docker run hello-world
  1. Изучены и выполнены основные команды docker;

не забываем все чистить

docker rm $(docker ps -a -q)

docker rmi $(docker images -q)

Дополнительное задание

  1. На основе вывода команд описаны отличия контейнера от образа в файле /docker-monolith/docker-1.log;

Docker-контейнеры

  1. Проверенны настойки Yandex Cloud CLI (были сделаны ранее);

  2. Создан хост и инициализировано окружение Docker на нем docker-machine create;

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

 docker-machine create \
--driver generic \
--generic-ip-address=<ПУБЛИЧНЫЙ_IP_СОЗДАНОГО_ВЫШЕ_ИНСТАНСА> \
--generic-ssh-user yc-user \
--generic-ssh-key ~/.ssh/id_rsa \
docker-host

docker-machine ls

eval $(docker-machine env docker-host)
  1. Организована необходимая структура репозитория;

  2. Произведена сборка образа reddit:latest;

docker build -t reddit:latest .
  1. После сборки образа на хосте YC c инициализированysv окружение Docker запущен контейнер;
docker run --name reddit -d --network=host reddit:latest

Проверенна работа созданного контейнера http://<ПУБЛИЧНЫЙ_IP_СОЗДАНОГО_ИНСТАНСА>:9292

  1. Пройдена регистрация на https://hub.docker.com с последующей аутентификацией;
docker login
Login with your Docker ID to push and pull images from Docker Hub.
If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: your-login
Password:
Login Succeeded
  1. Загружен созданный образ на docker hub;
docker tag reddit:latest skyfly534/otus-reddit:1.0

docker push skyfly534/otus-reddit:1.0

Проверена работа в локальном Docker

docker run --name reddit -d -p 9292:9292 skyfly534/otus-reddit:1.0

Проверенна работа созданного контейнера http://localhost:9292

Дополнительное задание

  1. При помощи packer подготовлен образ виртуальной машины с установленным docker, используется provisioners ansible, для установки docker и модуля python, плейбук ansible/packer_docker.yml;
packer build -var-file=variables.json docker.json
  1. Инстансы поднимаются с помощью Terraform, их количество задается переменной servers_count;
terraform init

terraform apply
  1. Написан плейбук Ansible deploy_docker_app.yml с использованием динамического инвентори (расмотренно ранее в ДЗ № 10) для установки докера и запуска (для запуска контейнера возьмём community.docker.docker_container) приложения.
ansible-inventory --list
{
    "_meta": {
        "hostvars": {
            "158.160.127.201": {
                "ansible_host": "158.160.127.201"
            },
            "62.84.125.16": {
                "ansible_host": "62.84.125.16"
            }
        }
    },
    "all": {
        "children": [
            "ungrouped"
        ]
    },
    "ungrouped": {
        "hosts": [
            "158.160.127.201",
            "62.84.125.16"
        ]
    }
}
ansible-playbook deploy_docker_app.yml

PLAY [Run reddit-docker] *****************************************************************************************

TASK [Install PIP] ***********************************************************************************************
ok: [158.160.127.201]
ok: [62.84.125.16]

TASK [Install Docker SDK for Python] *****************************************************************************
ok: [158.160.127.201]
ok: [62.84.125.16]

TASK [Run app in container] **************************************************************************************
changed: [62.84.125.16]
changed: [158.160.127.201]

PLAY RECAP *******************************************************************************************************
158.160.127.201            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
62.84.125.16               : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Я запускал два истанса, оба мне ответили по следующим адресам: http://62.84.125.16, http://158.160.127.201.

About

skyfly535 microservices repository

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •