adastraaero microservices repository
Устанавливаем Docker https://docs.docker.com/engine/install/ubuntu/
Client: Docker Engine - Community Cloud integration: v1.0.24 Version: 20.10.14 API version: 1.41 Go version: go1.16.15 Git commit: a224086 Built: Thu Mar 24 01:47:58 2022 OS/Arch: linux/amd64 Context: default Experimental: true
Server: Docker Engine - Community Engine: Version: 20.10.14 API version: 1.41 (minimum version 1.12) Go version: go1.16.15 Git commit: 87a90dc Built: Thu Mar 24 01:45:50 2022 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.5.11 GitCommit: 3df54a852345ae127d1fa3092b95168e4a88e2f8 runc: Version: 1.0.3 GitCommit: v1.0.3-0-gf46b6ba docker-init: Version: 0.19.0 GitCommit: de40ad0
Устанавливаем docker machine https://github.com/docker/machine/releases
Создаем Docker machine
yc compute instance create \
--name docker-host \
--zone ru-central1-a \
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 \
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1804-lts,size=15 \
--ssh-key ~/.ssh/id_rsa.pub
docker-machine create \
--driver generic \
--generic-ip-address=51.250.14.22 \
--generic-ssh-user yc-user \
--generic-ssh-key /home/mity/.ssh/id_rsa \
docker-host
Переключим docker на удаленный хост:
eval $(docker-machine env docker-host)
Создаем нужные файлы, собираем образ и запускаем контейнер.
docker build -t reddit:latest .
docker run --name reddit -d --network=host reddit:latest
Проверяем
docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS docker-host * generic Running tcp://51.250.14.22:2376 v20.10.17
заходим через браузер и проверяем http://51.250.14.22:9292 .
Отправляем образ в docker репозиторий:
docker login
docker tag reddit:latest adastraaero/otus-reddit:1.0
docker push adastraaero/otus-reddit:1.0
Проверим:
docker run --name reddit -d -p 9292:9292 adastraaero/otus-reddit:1.0
Пересоздаем и запускаем dpcker host:
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=51.250.74.249 \
--generic-ssh-user yc-user \
--generic-ssh-key /home/mity/.ssh/id_rsa \
docker-host
Переключим docker на удаленный хост:
eval $(docker-machine env docker-host)
Скачиваем архив, перемещаем файлы, добавляем в них данные. Используем для сборки Ubuntu. Ставим и применяем hadolint на наши Dockerfiles.
Собираем приложение:
docker pull mongo:latest
docker build -t adastraaero/post:1.0 ./post-py
docker build -t adastraaero/comment:1.0 ./comment
docker build -t adastraaero/ui:1.0 ./ui
Проверяем создание образов:
docker images
Создаем и проверяем сеть для приложения
docker network create reddit
docker network ls
Запускаем контейнеры и проверяем работу приложения.
docker run -d --network=reddit --network-alias=post_db --network-alias=comment_db mongo:latest
docker run -d --network=reddit --network-alias=post adastraaero/post:1.0
docker run -d --network=reddit --network-alias=comment adastraaero/comment:1.0
docker run -d --network=reddit -p 9292:9292 adastraaero/ui:1.0
Создаем ветку в репозитории:
git checkout -b gitlab-ci-1
Создаём VM на Yandex Cloud через GUI.
Устанавливаем Docker + Docler-compose
https://docs.docker.com/engine/install/ubuntu/
Подключаемся к хосту и создаём коталоги:
ssh [email protected]
sudo mkdir -p /srv/gitlab/config /srv/gitlab/data /srv/gitlab/logs
cd /srv/gitlab
sudo touch docker-compose.yml
sudo vim docker-compose.yml
web:
image: 'gitlab/gitlab-ce:latest'
restart: always
hostname: 'localhost'
environment:
GITLAB_OMNIBUS_CONFIG:
external_url 'http://158.160.1.182/'
ports:
- '80:80'
- '443:443'
- '2222:22'
volumes:
- '/srv/gitlab/config:/etc/gitlab'
- '/srv/gitlab/logs:/var/log/gitlab'
- '/srv/gitlab/data:/var/opt/gitlab'
Запускаем gitlab:
sudo docker-compose up -d
Ждём пока службы в контейнере загрузяться и проверяем доступность:
http://158.160.1.182/
Меняем пароль от root и сохраняем значение:
sudo touch /srv/gitlab/config/initial_root_password
sudo cat /srv/gitlab/config/initial_root_password
- Отключаем регистрацию новых пользователей:
Settings -> General -> Sign-up restrictions
[ ] Sign-up enabled
- Создаем группу:
- Name - homework
- Description - Projects for my homework
- Visibility - private
- Создаем проект:
example
Для выполнения push с локального хоста в gitlab выполним (Добавление remote):
git remote add gitlab http://178.154.223.64/homework/example.git
git push gitlab gitlab-ci-1
- Пайплайн для GitLab определяется в файле
.gitlab-ci.yml
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- echo 'Building'
test_unit_job:
stage: test
script:
- echo 'Testing 1'
test_integration_job:
stage: test
script:
- echo 'Testing 2'
deploy_job:
stage: deploy
script:
- echo 'Deploy'
Запушим изменения:
git add .gitlab-ci.yml
git commit -m 'add pipeline definition'
git push gitlab gitlab-ci-1
- Добавление раннера
На сервере Gitlab CI, выполним:
ssh [email protected]/
sudo docker run -d --name gitlab-runner --restart always -v /srv/gitlabrunner/config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest
Регистрация раннера (указываем url сервера gitlab и токен из Settings -> CI/CD -> Pipelines -> Runners
):
sudo docker exec -it gitlab-runner gitlab-runner register \
--url http://158.160.1.182/ \
--non-interactive \
--locked=false \
--name DockerRunner \
--executor docker \
--docker-image alpine:latest \
--registration-token GR1348941psgUg2py7aJ5qjCfVfb6 \
--tag-list "linux,xenial,ubuntu,docker" \
--run-untagged
Если все успешно, то должен появится новый ранер в Settings -> CI/CD -> Pipelines -> Runners
секция Available specific runners
и после появления ранера должен выполнится пайплайн.
- Добавление Reddit в проект
git clone https://github.com/express42/reddit.git && rm -rf ./reddit/.git
git add reddit/
git commit -m "Add reddit app"
git push gitlab gitlab-ci-1
Измените описание пайплайна в .gitlab-ci.yml
, создаем файл тестов 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
Добавим библиотеку rack-test
в reddit/Gemfile
:
gem 'rack-test'
Запушим код в GitLab и убедимся, что test_unit_job гоняет тесты.
- Окружения
Добавим в .gitlab-ci.yml
новые окружения и условия запусков для ранеров
image: ruby:2.4.2
stages:
- build
- test
- review
- stage
- production
variables:
DATABASE_URL: 'mongodb://mongo/user_posts'
before_script:
- cd reddit
- bundle install
build_job:
stage: build
script:
- echo 'Building'
test_unit_job:
stage: test
services:
- mongo:latest
script:
- ruby simpletest.rb
test_integration_job:
stage: test
script:
- echo 'Testing 2'
deploy_dev_job:
stage: review
script:
- echo 'Deploy'
environment:
name: dev
url: http://dev.example.com
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:
- master
staging:
stage: stage
when: manual
only:
- /^\d+\.\d+\.\d+/
script:
- echo 'Deploy'
environment:
name: stage
url: http://beta.example.com
production:
stage: production
when: manual
only:
- /^\d+\.\d+\.\d+/
script:
- echo 'Deploy'
environment:
name: production
url: http://example.com
Решение
yc compute instance create \
--name docker-host \
--memory=4 \
--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=51.250.10.146 \
--generic-ssh-user yc-user \
--generic-ssh-key ~/.ssh/id_rsa \
docker-host
docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
docker-host - generic Running tcp://51.250.10.146:2376 v20.10.18
eval $(docker-machine env docker-host)
docker run --rm -p 9090:9090 -d --name prometheus prom/prometheus
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bd1a4dc9973d prom/prometheus "/bin/prometheus --c…" 7 seconds ago Up 4 seconds 0.0.0.0:9090->9090/tcp, :::9090->9090/tcp prometheus
Проверяем: http://51.250.10.146:9090
Подготовим файловую структуру согласно задания и развернем docker-compose на docker-host
docker-machine ssh docker-host
sudo apt install docker-compose
$ cat Dockerfile
FROM prom/prometheus:v2.1.0
ADD prometheus.yml /etc/prometheus/
prometheus.yml:
---
global:
scrape_interval: '5s'
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets:
- 'localhost:9090'
- job_name: 'ui'
static_configs:
- targets:
- 'ui:9292'
- job_name: 'comment'
static_configs:
- targets:
- 'comment:9292'
Собираем Docker образ в директории prometheus:
export USER_NAME=username
docker build -t $USER_NAME/prometheus .
Проверяем, что появились еще endpoint:
Добавим нагрузки на Docker Host:
docker-machine ssh docker-host
yes > /dev/null
Ссылка на Dockerhub https://hub.docker.com/u/adastraaero
Решение
yc compute instance create \
--name logging \
--memory=4 \
--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=178.154.201.3 \
--generic-ssh-user yc-user \
--generic-ssh-key ~/.ssh/id_rsa \
logging
docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
logging - generic Running tcp://178.154.201.3:2376 v20.10.18
Ставим docker-compose на docker хост:
docker-machine ssh logging
sudo apt install docker-compose
eval $(docker-machine env logging)
Собираем образы:
port USER_NAME='adastraaero'
cd ./src/ui && bash docker_build.sh && docker push $USER_NAME/ui:logging
cd ../post-py && bash docker_build.sh && docker push $USER_NAME/post:logging
cd ../comment && bash docker_build.sh && docker push $USER_NAME/comment:logging
Система централизованного логирования на примере Elastic-стека (ранее известного как ELK), который включает в себя 3 основных компонента:
- ElasticSearch (TSDB и поисковый движок для хранения данных)
- Logstash (для аггрегации и трансформации данных)
- Kibana (для визуализации)
Для аггрегации логов вместо Logstash будем использовать Fluentd, получим еще одно популярное сочетание этих инструментов - EFK.
Fluentd - инструмент, который может использоваться для отправки, аггрегации и преобразования лог-сообщений. Fluentd будем использовать для аггрегации (сбора в одном месте) и парсинга логов сервисов нашего приложения.
В директории logging/fluentd
создаем Dockerfile:
FROM fluent/fluentd:v0.12
RUN gem install faraday-net_http -v 2.1.0
RUN gem install faraday -v 1.10.2
RUN gem install fluent-plugin-elasticsearch --no-rdoc --no-ri --version 1.9.5
RUN gem install fluent-plugin-grok-parser --no-rdoc --no-ri --version 1.0.0
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>
Собираем docker image для fluentd:
cd logging/fluentd
docker build -t $USER_NAME/fluentd .
Логи должны иметь заданную (единую) структуру и содержать необходимую для нормальной эксплуатации данного сервиса информацию о его работе.
Лог-сообщения также должны иметь понятный для выбранной системы логирования формат, чтобы избежать ненужной траты ресурсов на преобразование данных в нужный вид.
Для запуска подготовленных контейнеров настроим docker/docker-compose.yml
на теги :logging
и запустим сервисы:
cd docker && docker-compose -f docker-compose.yml up -d
Можем посмотреть логи, например для post
сервиса:
docker-compose logs -f post
Attaching to docker_post_1
post_1 | {"addr": "172.18.0.3", "event": "request", "level": "info", "method": "GET", "path": "/healthcheck?", "request_id": null, "response_status": 200, "service": "post", "timestamp": "2022-09-15 10:45:40"}
post_1 | {"addr": "172.18.0.3", "event": "request", "level": "info", "method": "GET", "path": "/healthcheck?", "request_id": null, "response_status": 200, "service": "post", "timestamp": "2022-09-15 10:45:45"}
post_1 | {"addr": "172.18.0.3", "event": "request", "level": "info", "method": "GET", "path": "/healthcheck?", "request_id": null, "response_status": 200, "service": "post", "timestamp": "2022-09-15 10:45:49"}
post_1 | {"addr": "172.18.0.3", "event": "request", "level": "info", "method": "GET", "path": "/healthcheck?", "request_id": null, "response_status": 200, "service": "post", "timestamp": "2022-09-15 10:45:55"}
post_1 | {"addr": "172.18.0.3", "event": "request", "level": "info", "method": "GET", "path": "/healthcheck?", "request_id": null, "response_status": 200, "service": "post", "timestamp": "2022-09-15 10:46:00"}
post_1 | {"addr": "172.18.0.3", "event": "request", "level": "info", "method": "GET", "path": "/healthcheck?", "request_id": null, "response_status": 200, "service": "post", "timestamp": "2022-09-15 10:46:04"}
Каждое событие, связанное с работой нашего приложения логируется в JSON-формате и имеет нужную нам структуру: тип события (event), сообщение (message), переданные функции параметры (params), имя сервиса (service) и др.
По умолчанию Docker-контейнерами используется json-file драйвер для логирования информации, которая пишется сервисом внутри контейнера в stdout (и stderr).
Для отправки логов во Fluentd используем docker-драйвер fluentd.
Для сервиса post определим драйвер для логирования - docker/docker-compose.yml
:
post:
image: ${USER_NAME}/post:logging
environment:
- POST_DATABASE_HOST=post_db
- POST_DATABASE=posts
depends_on:
- post_db
ports:
- "5000:5000"
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: service.post
vim kubernetes/reddit/post-deployment.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: post-deployment
spec:
replicas: 1
selector:
matchLabels:
app: post
template:
metadata:
name: post
labels:
app: post
spec:
containers:
- image: adastraaero/post
name: post
Создаём в kubernetes/reddit/ файлы:
- ui-deployment.yml
- comment-deployment.yml
- mongo-deployment.yml
Создаём 2 ноды Ubuntu 18/22.04 c конфигурацией:
- RAM 4
- CPU 4
- SSD 40 GB
yc compute instance create \
--name worker \
--memory=4 \
--cores=4 \
--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=40,type=network-ssd \
--ssh-key ~/.ssh/id_rsa.pub
yc compute instance create \
--name master \
--memory=4 \
--cores=4 \
--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=40,type=network-ssd \
--ssh-key ~/.ssh/id_rsa.pub
ставим на данные ноды k8s - 1.19 и docker 19.03
Ставим docker - https://docs.docker.com/engine/install/ubuntu/
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli
Ставим k8s https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
sudo echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install containerd.io kubelet kubeadm kubectl
sudo apt-get install docker-ce=5:19.03.15~3-0~ubuntu-bionic docker-ce-cli=5:19.03.15~3-0~ubuntu-bionic containerd.io kubelet=1.19.14-00 kubeadm=1.19.14-00 kubectl=1.19.14-00
выключаем swap $ sudo swapoff -a
запускаем кластер k8s:
kubeadm init --apiserver-cert-extra-sans=178.154.204.157--apiserver-advertise-address=0.0.0.0 --control-plane-endpoint=178.154.204.157 --pod-network-cidr=10.244.0.0/16
Подсоединяем ноду в кластер
sudo kubeadm join 178.154.204.157:6443 --token iikyxo.0e2rljhro1s9wetk
--discovery-token-ca-cert-hash sha256:f5ad5fe11063e1d52752c59fbe274b2b359bf6fad934003b56e3f436c28e8a74
Создадим конфиг файл для пользователя на мастер ноде - https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
mkdir $HOME/.kube/
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $USER $HOME/.kube/config
Смотрид состояние нод:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
hodemain NotReady master 18m v1.19.14
node2 NotReady <none> 5m34s v1.19.14
kubectl describe node node2
Ready False Mon, 19 Sep 2022 17:00:21 +0000 Mon, 19 Sep 2022 16:54:51 +0000 KubeletNotReady runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
Ноды находятся в статусе not ready, установим сетевой плагин flanel:
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml
Проверим состояние нод:
yc-user@fhmm6l9ubd6ave5dmfjs:~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
fhme8v4ds600b8bhucku Ready <none> 70s v1.19.14
fhmm6l9ubd6ave5dmfjs Ready master 39m v1.19.14
Запустим один из манифестов нашего приложения и убедимся, что он применяется:
yc-user@fhmm6l9ubd6ave5dmfjs:~$ kubectl apply -f post-deployment.yml
deployment.apps/post-deployment created
yc-user@fhmm6l9ubd6ave5dmfjs:~$ kubectl get pods
NAME READY STATUS RESTARTS AGE
post-deployment-5877559886-bjzld 1/1 Running 0 48s
Удалим ресурсы:
yc compute instance delete worker
yc compute instance delete master
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl
kubectl cluster-info
Установка Minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
sudo dpkg -i minikube_latest_amd64.deb
Запустим Minikube-кластер (весрия 1.19.7):
minikube start --kubernetes-version 1.19.7
Minikube-кластер развернут. При этом автоматически был настроен конфиг kubectl.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 38s v1.19.7
Описываем конфигурации приложения и сервисвов в YAML манифестах в ./kubernetes/reddit
.
Запускаем minikube:
kubectl apply -f kubernetes/reddit/
$ minikube service ui
|-----------|------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------|-------------|---------------------------|
| default | ui | 9292 | http://192.168.49.2:32092 |
|-----------|------|-------------|---------------------------|
🎉 Opening service default/ui in default browser...
Dashboard
Включаем аддон dashboard на minikube:
minikube dashboard
В Dashboard можно:
- Отслеживать состояние кластера и рабочих нагрузок в нем;
- Создавать новые объекты (загружать YAML-файлы);
- Удалять и изменять объекты (кол-во реплик, YAML-файлы);
- Отслеживать логи в POD-ах;
- При включении Heapster-аддона смотреть нагрузку на POD-ах;
- и т. д.c
Создаем Namespace и запускаем приложение в dev неймспейсе:
kubectl apply -f dev-namespace.yml
kubectl apply -n dev -f kubernetes/reddit/
Проверим результат:
minikube service ui -n dev
Удалим:
kubectl delete -n dev -f kubernetes/reddit/
Создаем кластер(GUI) и группу хостов. Подключаемся к кластеру:
yc managed-kubernetes cluster get-credentials cat7q5uo5sno0a6coue0 --external
Проверяем подключение к кластеру
kubectl cluster-info --kubeconfig /home/mity/.kube/config
$ kubectl cluster-info --kubeconfig /home/mity/.kube/config
Kubernetes control plane is running at https://158.160.12.44
CoreDNS is running at https://158.160.12.44/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Проверяем текущий контекст:
$ kubectl config current-context
yc-test-cluster
Смотрим внешние адрес нод и порт:
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
cl150dgfdj7ea2o44din-ihoq Ready <none> 7m17s v1.20.11 10.129.0.6 158.160.14.185 Ubuntu 20.04.4 LTS 5.4.0-124-generic docker://20.10.17
cl150dgfdj7ea2o44din-isil Ready <none> 7m26s v1.20.11 10.129.0.24 130.193.54.96 Ubuntu 20.04.4 LTS 5.4.0-124-generic docker://20.10.17
$ kubectl describe service ui -n dev | grep NodePort
Type: NodePort
NodePort: <unset> 32092/TCP
Подключаемся и делаем пост:
Поднимаем кластер кластер согласно инструкции из предыдущего дз.
Разворачиваем приложение:
kubectl apply -f ./kubernetes/reddit/dev-namespace.yml
kubectl apply -f ./kubernetes/reddit/ -n dev
---
apiVersion: v1
kind: Service
metadata:
name: ui
labels:
app: reddit
component: ui
spec:
type: LoadBalancer
ports:
- port: 80
nodePort: 32092
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: ui
kubectl apply -f ui-service.yml -n dev
kubectl get service -n dev --selector component=ui
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ui LoadBalancer 10.96.182.150 84.201.130.231 80:32092/TCP 14m
Доступ к приложению http://84.201.130.231
Минусы балансировки через LoadBalancer
- Нельзя управлять с помощью http URI (L7-балансировщика)
- Используются только облачные балансировщики (AWS, GCP)
- Нет гибких правил работы с трафико
Для более удобного управления входящим снаружи трафиком и решения недостатков LoadBalancer можно использовать другой объект Kubernetes - Ingress.
Ingress - это набор правил внутри кластера Kuberntes, предназначенных для того, чтобы входящие подключения могли достичь сервисов (Services) Сами по себе Ingress'ы это просто правила. Для их применения нужен Ingress Controller.
Ingress Controller - это скорее плагин (а значит и отдельный POD), который состоит из 2-х функциональных частей:
- Приложение, которое отслеживает через k8s API новые объекты Ingress и обновляет конфигурацию балансировщика
- Балансировщик (Nginx, haproxym traefik, ...), который и занимается управлением сетевым трафиком.
Основные задачи, решаемые с помощью Ingress'ов:
- Организация единой точки входа в приложения снаружи
- Обеспечение балансировки трафика
- Терминация SSL
- Виртуальный хостинг на основе имен и т. д.
Установим Ingress Controller:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/cloud/deploy.yaml
ingress установился в namespace ingress-nginx.
kubectl get service ingress-nginx-controller -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.96.243.18 51.250.83.187 80:32370/TCP,443:32171/TCP 11h
Создадим Ingress для сервиса UI:
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ui
spec:
backend:
serviceName: ui
servicePort: 80
kubectl apply -f ui-ingress.yml -n dev
Посмотрим внешний адрес ingress через GUI yandex cloud - 51.250.83.187
Обновляем UI сервис:
---
apiVersion: v1
kind: Service
metadata:
name: ui
labels:
app: reddit
component: ui
spec:
type: NodePort
ports:
- port: 9292
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: ui
kubectl apply -f ui-service.yml -n dev
Заставим работать Ingress Controller как классический веб:
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ui
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: ui
servicePort: 9292
Защитим наш сервис с помощью TLS:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=51.250.83.187"
Проверяем:
kubectl describe secret ui-ingress -n dev
Type: kubernetes.io/tls
Data
====
tls.key: 1704 bytes
tls.crt: 1123 bytes
Настроим Ingress на прием только HTTPS траффика:
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ui
annotations:
kubernetes.io/ingress.allow-http: "false"
spec:
tls:
- secretName: ui-ingress
backend:
serviceName: ui
servicePort: 9292
Применяем:
kubectl apply -f ui-ingress.yml -n dev
Иногда протокол HTTP может не удалиться у существующего Ingress правила, тогда нужно его вручную удалить и пересоздать:
kubectl delete ingress ui -n dev
kubectl apply -f ui-ingress.yml -n dev
Для начала создадим диск PersitentVolume в ya.cloud:
yc compute disk create \
--name k8s \
--zone ru-central1-a \
--size 4 \
--description "disk for k8s"
Проверим и запомним id созданного диска:
yc compute disk list
Создадим PV в ya.cloud:
mongo-volume.yml:
---
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: b1gl9g5f46b3fv1g4ac1
Создадим PVC (PersitentVolumeClaim) - mongo-claim.yml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongo-pvc
spec:
storageClassName: ""
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
volumeName: mongo-pv
и подключим созданный PVC - mongo-deployment.yml
....
spec:
containers:
- image: mongo:3.2
name: mongo
volumeMounts:
- name: mongo-persistent-storage
mountPath: /data/db
volumes:
- name: mongo-persistent-storage
persistentVolumeClaim:
claimName: mongo-pvc
Соберем:
kubectl apply -f mongo-volume.yml -n dev
kubectl apply -f mongo-claim.yml -n dev
kubectl apply -f mongo-deployment.yml -n dev
Дождаемся пересоздания POD'а (занимает до 10 минут). Для проверки можно создать пост, после удалить deployment и снова создадим деплой mongo:
kubectl delete deploy mongo -n dev
kubectl apply -f mongo-deployment.yml -n dev