Репозиторий для работы над домашними заданиями в рамках курса "DevOps практики и инструменты"
- HW.12 - Технология контейнеризации. Введение в Docker
- HW.13 - Docker-образы. Микросервисы
- HW.14 - Docker: сети, docker-compose
- HW.15 - Устройство Gitlab CI. Построение процесса непрерывной поставки
- HW.16 - Введение в мониторинг. Системы мониторинга
- HW.17 - Мониторинг приложения и инфраструктуры
- HW.18 - Логирование и распределенная трассировка
- HW.19 - Kubernetes - The Hard Way
- HW.20 - Kubernetes. Запуск кластера и приложения. Модель безопасности.
- HW.21 - Kubernetes. Networks ,Storages.
- HW.22 - Kubernetes. CI/CD в Kubernetes
- HW.23 - Kubernetes. Мониторинг и логирование
- Установлены:
$ docker version
Client: Docker Engine - Community
Version: 19.03.1
API version: 1.40
Go version: go1.12.5
...
Server: Docker Engine - Community
Engine:
Version: 19.03.1
API version: 1.40 (minimum version 1.12)
Go version: go1.12.5
$ docker-compose -v
docker-compose version 1.24.1, build 4667896b
$ docker-machine -v
docker-machine version 0.16.1, build cce350d7- Запуск конта:
$ docker run hello-world
...
Hello from Docker!- Список контов:
$ docker ps -a- Список имиджей:
$ docker images- Запуск и вход внутрь конта:
$ docker run -it ubuntu:16.04 /bin/bash- docker run запускает новый конт каждый раз
- Если флаг --rm, то после остановки конта, содержимое будет удалено с диска
- Работа с docker ps
$ docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.CreatedAt}}\t{{.Names}}"
CONTAINER ID IMAGE CREATED AT NAMES
c6fcb5db6c5b ubuntu:16.04 2019-11-19 00:27:04 +0300 MSK musing_davinci- Запуск созданного конта
$ docker start c6fcb5db6c5b- Присоединить терминал к созданному конту
$ docker attach c6fcb5db6c5bdocker run = docker create + docker start + docker attach- docker create юзается, если не надо стартовать конт сразу после создания
- Через параметры передаются лимиты(cpu/mem/disk), ip, volumes -i – запускает контейнер в foreground режиме (docker attach) -d – запускает контейнер в background режиме -t - создает TTY docker run -it ubuntu:16.04 bash docker run -dt nginx:latest
- Запускает новый процесс внутри контейнера
docker exec -it <u_container_id> bash- Создает image из контейнера
- Контейнер при этом остается запущенным
$ docker commit c6fcb5db6c5b jull/ubuntu-tmp-file
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jull/ubuntu-tmp-file latest cd948fb78b1e 9 seconds ago 123MB- Информация о контейнере
$ docker inspect c6fcb5db6c5b- Информация об имидже
$ docker inspect 5f2bf26e3524- Описание отличий контейнера и образа в файле docker-monolith/docker-1.log
- kill сразу посылает SIGKILL
- stop посылает SIGTERM, и через 10 секунд посылает SIGKILL
- SIGTERM - сигнал остановки приложения
- SIGKILL - безусловное завершение процесса
$ docker ps -q
c6fcb5db6c5b
$ docker kill $(docker ps -q)
c6fcb5db6c5b- Отображает сколько дискового пространства занято образами, контейнерами и volume’ами
- Отображает сколько из них не используется и возможно удалить
$ docker system df- rm - удаляет контейнер, можно добавить флаг -f, чтобы удалялся работающий container
- rmi удаляет image, если от него не зависят запущенные контейнеры
$ docker rm $(docker ps -a -q)
fb9e81c240cf
c6fcb5db6c5b$ docker rmi $(docker images -q)
Untagged: jull/ubuntu-tmp-file:latest
Deleted: sha256:cd948fb78b1e621df8fed21133b738d404b6c554962f72891e5a06f1824ca59a
...- Создал новый проект
- На своей машине:
gcloud init
gcloud auth application-default login- docker-machine - встроенный в докер инструмент для создания хостов и установки на них docker engine. Имеет поддержку облаков и систем виртуализации (Virtualbox, GCP и др.)
Команда создания:
$ export GOOGLE_PROJECT=_ваш-проект_
$ docker-machine create --driver google --google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts --google-machine-type n1-standard-1 --google-zone europe-west1-b docker-hostПереключение между именами:
eval $(docker-machine env <имя>)Переключение на локальный докер:
eval $(docker-machine env --unset)Удаление
docker-machine rm <имя>- docker-machine создает хост для докер демона с указываемым образом в --google-machine-image. Образы, которые используются для построения докер контейнеров, к этому никак не относятся.
- Все докер команды, которые запускаются в той же консоли после eval $(docker-machine env <имя>) работают с удаленным докер демоном в GCP.
$ export GOOGLE_PROJECT=docker-666666
$ docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-machine-type n1-standard-1 \
--google-zone europe-west1-b \
docker-host
...
Docker is up and running!- Проверяем, что наш Docker-хост успешно создан:
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
docker-host - google Running tcp://35.240.64.47:2376 v19.03.5
$ eval $(docker-machine env docker-host)Отличия вывода команд:
docker run --rm -ti tehbilly/htopВыводит только PID 1, с которым запущен htop (т.е. процессы в неймспейсе конта)
docker run --rm --pid host -ti tehbilly/htopВыводит процессы хостовой машины, на которой запущен докер (т.е. процессы неймспейса хостовой машины)
-
Создал в каталоге docker-monolith файлы:
- mongod.conf - конфиг для mongodb
- start.sh - скрипт запуска приложения
- db_config - переменная окружения со ссылкой на mongodb
- Dockerfile - текстовое описание образа
-
Сборка образа (запускается в каталоге docker-monolith)
docker build -t reddit:latest .-t - тег для собранного образа
- Смотреть все образы:
$ docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
reddit latest 4e8932235b85 23 minutes ago 692MB
<none> <none> b19e58d2fdb5 23 minutes ago 692MB
<none> <none> f92db4a5c445 23 minutes ago 692MB
...- Запуск контейнера и проверка результата:
$ docker run --name reddit -d --network=host reddit:latest
5a8623f5c010f0adaa3b64dac44646ebd10452ec20c4a1d33cd7a77ded49e2d4
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5a8623f5c010 reddit:latest "/start.sh" 10 seconds ago Up 8 seconds reddit
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
docker-host * google Running tcp://35.240.64.47:2376 v19.03.5- Добавил правило для фаерволла:
$ gcloud compute firewall-rules create reddit-app \
--allow tcp:9292 \
--target-tags=docker-machine \
--description="Allow PUMA connections" \
--direction=INGRESS- Проверка http://35.240.64.47:9292/
Docker hub - облачный registry сервис от компании Docker
- Аутентифицировался на докер хаб:
$ docker login
...
Login Succeeded- Протегировал и запушил свой образ в докер хаб:
$ docker tag reddit:latest <my-login>/otus-reddit:1.0
$ docker push <my-login>/otus-reddit:1.0- Скачала и запустил образ на локальной тачке:
$ docker run --name reddit -d -p 9292:9292 <my-login>/otus-reddit:1.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<my-login>/otus-reddit 1.0 4e8932235b85 54 minutes ago 692MB
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
40f026f1ba90 <my-login>/otus-reddit:1.0 "/start.sh" About a minute ago Up About a minute 0.0.0.0:9292->9292/tcp reddit-
Проверка http://localhost:9292/
-
Команды:
# Смотреть логи конта
$ docker logs 40f026f1ba90 -f
# Зайти в конт
$ docker exec -it 40f026f1ba90 bash
ps aux
# Убить контейнер
killall5 1
# Проверить что конт стопнулся
$ docker ps -a
STATUS - Exited
# Стартануть конт и проверить что он запустился
$ docker start 40f026f1ba90 && docker ps -a
# Остановить и удалить конт
$ docker stop 40f026f1ba90; docker rm 40f026f1ba90
# Запуск конта без запуска приложения
$ docker run --name reddit --rm -it <my-login>/otus-reddit:1.0 bash
# Смотреть инфу об образе
$ docker inspect 4e8932235b85
# Смотреть куски информации
$ docker inspect 4e8932235b85 -f '{{.ContainerConfig.Cmd}}'
[/bin/sh -c #(nop) CMD ["/start.sh"]]
# Запуск конта с приложением из образа (по image id)
$ docker run --name reddit -d -p 9292:9292 4e8932235b85
29eed99a817e4b2a77492cf71dd762d373e4f90eea28efaaa458c155970997ee
# Зайти в конт, добавить/удалить файлы/каталоги
$ docker exec -it reddit bash
mkdir /test111 && touch test111/test.txt
rmdir opt/
# Посмотреть внесенные изменения
$ docker diff 29eed99a817e
# Пересоздать конт и проверить что изменения не сохранились
$ docker stop reddit && docker rm reddit && docker run --name reddit --rm -it 4e8932235b85 bash -c ls
При сборке image из одинакового базового образа первая команда build кеширует его, а последующие уже будут собираться не с первого шага.
Для запуска контейнеров с определенной переменной окружения применяется ключ --env. Для указания нескольких переменных, указывается либо файл со значениями, либо несколько таких ключей.
Пример:
$ docker run -d --network=reddit1 --network-alias=post1_db --network-alias=comment1_db mongo:latest
$ docker run -d --network=reddit1 --network-alias=post1 --env POST_DATABASE_HOST=post1_db playjim/post:1.0
$ docker run -d --network=reddit1 --network-alias=comment1 --env COMMENT_DATABASE_HOST=comment1_db playjim/comment:1.0
$ docker run -d --network=reddit1 -p 9292:9292 --env POST_SERVICE_HOST=post1 --env COMMENT_SERVICE_HOST=comment1 playjim/ui:2.0
$ docker volume create reddit_db
reddit_db
$ docker run -d --network=reddit1 --network-alias=post1_db --network-alias=comment1_db -v reddit_db:/data/db mongo:latest
Типы сетей в Docker:
- none - только loopback, сеть изолирована
- host - использует сеть хоста
- bridge - отдельные namespaces сети ("виртуальная" сеть)
- MacVlan - на основе сабинтерфейсов Linux, поддерка 802.1Q
- Overlay - несколько Docker хостов в одну сеть, работает поверх VXLAN
При запуске контейнера можно указать только одну сеть параметром --network=<name>. Для подключения дополнительных сетей к контейнерам применить команду: docker network connect. Также несколько сетей могут быть подключены к контейнеру при запуске, если используется docker-compose.
Docker-compose позволяет запускать сразу несколько контейнеров по заданному сценарию.
Пример:
# docker-compose.yml
version: '3.3'
services:
post_db:
image: mongo:${TAGDB}
volumes:
- post_db:/data/db
networks:
- back_net
ui:
build: ./ui
image: ${USERNAME}/ui:${TAG}
ports:
- ${PORT}/tcp
networks:
- front_net
post:
build: ./post-py
image: ${USERNAME}/post:${TAG}
networks:
- back_net
- front_net
comment:
build: ./comment
image: ${USERNAME}/comment:${TAG}
networks:
- back_net
- front_net
volumes:
post_db:
networks:
back_net:
front_net:
Значения переменных подставляется или из переменных окружения или из файла .env. Имя проекта docker-compose присваивается от имени директории, в котором он располагается. Для переопределения этого имени используется параметр -p или переменная COMPOSE_PROJECT_NAME.
# .env
PORT=9292:9292
TAG=1.0
TAGDB=3.2
USERNAME=playjim
- Необходимые параметры для машины, на которой будем разворачивать Gitlab CI
1 CPU 3.75GB RAM 50-100GB HDD Ubuntu 16.04 IMAGE
- В официальной документации описаны рекомендуемые характеристики сервера
С помощью docker-machine создаем ВМ и не забываем разрешить подключение по http/https:
$ docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-zone europe-west1-b \
--google-machine-type n1-standard-1 \
--google-disk-size 100 \
gitlab-ci
...
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 gitlab-ci
- На ВМ создаем каталоги и создаем файл docker-compose.yml
$ eval $(docker-machine env gitlab-ci) $ docker-machine ssh gitlab-ci sudo su mkdir -p /srv/gitlab/config /srv/gitlab/data /srv/gitlab/logs cd /srv/gitlab/ vim docker-compose.yml
- Запускаем сборку образа и запускаем Gitlab CI (надо запускать в каталоге, где лежит docker-compose.yml)
/srv/gitlab# docker-compose up -d
- Откуда взяли содержимое файла docker-compose.yml - https://docs.gitlab.com/omnibus/docker/README.html#install-gitlab-using-docker-compose
- Проверка http://ip-vm/
- Выкл регу новых юезров
- В Settings/General/Sign-up restrictions сняла галку с Sign-up enabled
Из лекции:
- Каждый проект в Gitlab CI принадлежит к группе проектов
- В проекте может быть определен CI/CD пайплайн
- Задачи (jobs), входящие в пайплайн, должны исполняться GitLab runner
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 --run-untagged --locked=falseна runners
Правим правила фаервола на gcp. Открываем порты для Prometheus и Puma:
$ gcloud compute firewall-rules create prometheus-default --allow tcp:9090
$ gcloud compute firewall-rules create puma-default --allow tcp:9292
Создадим Docker хост в GCE и настроим локальное окружение на работу с ним:
$ export GOOGLE_PROJECT=_ваш-проект_
# create docker host
docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-machine-type n1-standard-1 \
--google-zone europe-west1-b \
docker-host
# configure local env
eval $(docker-machine env docker-host)
Систему мониторинга Prometheus будем запускать внутри Docker контейнера. Для начального знакомства воспользуемся готовым образом с DockerHub.
$ docker run --rm -p 9090:9090 -d --name prometheus prom/prometheus:v2.1.0
$ docker ps
Открываем веб интерфейс. По умолчанию сервер слушает на порту 9090. Чтобы узнать ip созданной ВМ, используем команду:
$ docker-machine ip docker-host
Соберем на основе готового образа с DockerHub свой Docker образ с конфигурацией для мониторинга наших микросервисов. Создайте директорию monitoring/prometheus. Затем в этой директории создайте простой Dockerfile, который будет копировать файл конфигурации с нашей машины внутрь контейнера:
#monitoring/prometheus/Dockerfile
FROM prom/prometheus:v2.1.0
ADD prometheus.yml /etc/prometheus/
Мы определим простой конфигурационный файл для сбора метрик с наших микросервисов. В директории monitoring/prometheus создайте файл prometheus.yml со следующим cодержимым:
---
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'
В директории prometheus собираем Docker образ:
$ export USER_NAME=playjim
$ docker build -t $USER_NAME/prometheus .
Где USER_NAME - ВАШ логин от DockerHub.
В коде микросервисов есть healthcheck-и для проверки работоспособности приложения. Сборку образов теперь необходимо производить при помощи скриптов docker_build.sh, которые есть в директории каждого сервиса. С его помощью мы добавим информацию из Git в наш healthcheck.
Выполните сборку образов при помощи скриптов docker_build.sh в директории каждого сервиса.
/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
Будем поднимать наш Prometheus совместно с микросервисами. Определите в вашем docker/docker-compose.yml файле новый сервис.
services:
...
prometheus:
image: ${USERNAME}/prometheus
networks:
- back_net
- front_net
ports:
- '9090:9090'
volumes:
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention=1d'
volumes:
prometheus_data:
Не забываем добавить секцию networks для сервиса prometheus, чтобы он смог общаться со всеми серверами.
Воспользуемся Node_exporter для сбора информации о работе Docker хоста (виртуалки, где у нас запущены контейнеры)и предоставлению этой информации в Prometheus.
Запушим собранные вами образы на DockerHub:
$ docker login
Login Succeeded
$ docker push $USER_NAME/ui
$ docker push $USER_NAME/comment
$ docker push $USER_NAME/post
$ docker push $USER_NAME/prometheus
Ссылка на мой профиль в DockerHub: https://hub.docker.com/u/playjim
Сначала создаем на GCP ВМ с докером.
$ export GOOGLE_PROJECT=_ваш-проект_
# Создать докер хост
docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-machine-type n1-standard-1 \
--google-zone europe-west1-b \
docker-host
# Настроить докер клиент на удаленный докер демон
eval $(docker-machine env docker-host)
# Переключение на локальный докер
eval $(docker-machine env --unset)
$ docker-machine ip docker-host
$ docker-machine rm docker-host
Оставим описание приложений в docker-compose.yml, а мониторинг выделим в отдельный файл docker-compose-monitoring.yml.
Для запуска приложений будем как и ранее использоватьdocker-compose up -d, а для мониторинга - docker-compose -f docker-compose-monitoring.yml up -d
Мы будем использовать cAdvisor для наблюдения за состоянием наших Docker контейнеров. cAdvisor собирает информацию о ресурсах потребляемых контейнерами и характеристиках их работы.
Добавим информацию о новом сервисе в конфигурацию Prometheus, чтобы он начал собирать метрики:
scrape_configs:
...
- job_name: 'cadvisor'
static_configs:
- targets:
- 'cadvisor:8080'
Пересоберем образ Prometheus с обновленной конфигурацией:
$ export USER_NAME=username # где username - ваш логин на Docker Hub
$ docker build -t $USER_NAME/prometheus .
Не забываем открыть порты:
$ gcloud compute firewall-rules create prometheus-default --allow tcp:9090
$ gcloud compute firewall-rules create puma-default --allow tcp:9292
$ gcloud compute firewall-rules create cadvisor-default --allow tcp:8080
Запустим сервисы:
$ docker-compose up -d
$ docker-compose -f docker-compose-monitoring.yml up -d
cAdvisor имеет UI, в котором отображается собираемая о контейнерах информация. Откроем страницу Web UI по адресу http://:8080
Используем инструмент Grafana для визуализации данных из Prometheus. Добавим новый сервис в docker-compose-monitoring.yml:
grafana:
image: grafana/grafana:5.0.0
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=secret
depends_on:
- prometheus
ports:
- 3000:3000
networks:
- front_net
- back_net
volumes:
grafana_data:
Внесем правки в правила firewall gcp:
$ gcloud compute firewall-rules create grafana-default --allow tcp:3000
Alertmanager - дополнительный компонент для системы мониторинга Prometheus, который отвечает за первичную обработку алертов и дальнейшую отправку оповещений по заданному назначению. Создайте новую директорию monitoring/alertmanager. В этой директории создайте Dockerfile со следующим содержимым:
FROM prom/alertmanager:v0.14.0
ADD config.yml /etc/alertmanager/
Настройки Alertmanager-а как и Prometheus задаются через YAML файл или опции командой строки. В директории monitoring/alertmanager создайте файл config.yml, в котором определите отправку нотификаций в ВАШ тестовый слак канал. Для отправки нотификаций в слак канал потребуется создать СВОЙ Incoming Webhook monitoring/alertmanager/config.yml:
global:
slack_api_url: 'https://hooks.slack.com/services/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'
route:
receiver: 'slack-notifications'
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#dmitry_borisov'
- Соберем образ alertmanager:
monitoring/alertmanager $ docker build -t $USER_NAME/alertmanager .
- Добавим новый сервис в компоуз файл мониторинга. Не забудьте добавить его в одну сеть с сервисом Prometheus:
services:
...
alertmanager:
image: ${USER_NAME}/alertmanager
command:
- '--config.file=/etc/alertmanager/config.yml'
ports:
- 9093:9093
networks:
- front_net
- back_net
Создадим файл alerts.yml в директории prometheus, в котором определим условия при которых должен срабатывать алерт и посылаться Alertmanager-у. Мы создадим простой алерт, который будет срабатывать в ситуации, когда одна из наблюдаемых систем (endpoint) недоступна для сбора метрик (в этом случае метрика up с лейблом instance равным имени данного эндпоинта будет равна нулю).
monitoring/prometheus/alerts.yml:
groups:
- name: alert.rules
rules:
- alert: InstanceDown
expr: up == 0
for: 1m
labels:
severity: page
annotations:
description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minute'
summary: 'Instance {{ $labels.instance }} down'
Добавим операцию копирования данного файла в Dockerfile: monitoring/prometheus/Dockerfile:
FROM prom/prometheus:v2.1.0
ADD prometheus.yml /etc/prometheus/
ADD alerts.yml /etc/prometheus/
prometheus.yml:
rule_files:
- "alerts.yml"
alerting:
alertmanagers:
- scheme: http
static_configs:
- targets:
- "alertmanager:9093"
Пересоберем образ Prometheus.
Ссылка на мой профиль DockerHub: https://hub.docker.com/u/playjim
Создаем Docker хост в GCE:
$ export GOOGLE_PROJECT=docker-xxxxxx
$ docker-machine create --driver google --google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts --google-machine-type n1-standard-1 --google-open-port 5601/tcp --google-open-port 9292/tcp --google-open-port 9411/tcp logging
$ eval $(docker-machine env logging)
$ docker-machine ip logging
Обновив код в директории /src выполняем сборку образов ui, post-py, comment:
export USER_NAME=playjim
/src/ui $ bash docker_build.sh && docker push $USER_NAME/ui
/src/post-py $ bash docker_build.sh && docker push $USER_NAME/post
/src/comment $ bash docker_build.sh && docker push $USER_NAME/comment
Или сразу все из корня репозитория:
for i in ui post-py comment; do cd src/$i; bash docker_build.sh; cd -; done
Как упоминалось на лекции хранить все логи стоит централизованно: на одном (нескольких) серверах. В этом ДЗ мы рассмотрим пример системы централизованного логирования на примере Elastic стека (ранее известного как ELK): который включает в себя 3 осовных компонента:
- ElasticSearch (TSDB и поисковый движок для хранения данных)
- Logstash (для агрегации и трансформации данных)
- Kibana (для визуализации) Однако для агрегации логов вместо Logstash мы будем использовать Fluentd, таким образом получая еще одно популярное сочетание этих инструментов, получившее название EFK
Для нашей системы логирования создаем отдельный compose-файл docker/docker-compose-logging.yml:
version: '3.3'
services:
zipkin:
image: openzipkin/zipkin
ports:
- "9411:9411"
networks:
- front_net
- back_net
fluentd:
image: ${USERNAME}/fluentd
ports:
- "24224:24224"
- "24224:24224/udp"
elasticsearch:
image: elasticsearch:7.5.0
container_name: elasticsearch
environment:
- xpack.security.enabled=false
- discovery.type=single-node
- bootstrap.memory_lock=true
ulimits:
memlock:
soft: -1
hard: -1
expose:
- 9200
ports:
- "9200:9200"
- "9300:9300"
kibana:
image: kibana:7.5.0
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
networks:
front_net:
back_net:
Опишем приложение в контексте Kubernetes с помощью manifest-ов в YAML-формате. Основным примитивом будет Deployment. Основные задачи сущности Deployment:
- Создание Replication Controller-а (следит, чтобы число запущенных Pod-ов соответствовало описанному);
- Ведение истории версий запущенных Pod-ов (для различных стратегий деплоя, для возможностей отката);
- Описание процесса деплоя (стратегия, параметры стратегий).
post-deployment.yml:
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: post-deployment
spec:
replicas: 1
selector:
matchLabels:
app: post
template:
metadata:
name: post
labels:
app: post
spec:
containers:
- image: chromko/post
name: post
Также я опишу comment-deployment.yml, mongo-deployment.yml, ui-deployment.yml;
Для практики и в качестве обучения я пройду "Kubernetes The Hard Way".
[tmux] (https://github.com/tmux/tmux/wiki) можно использовать для одновременного запуска команд на нескольких экземплярах вычислений. Лабораторные работы в этом руководстве могут потребовать выполнения одних и тех же команд для нескольких вычислительных экземпляров, в этих случаях рассмотрите возможность использования tmux и разбиения окна на несколько панелей с включенными синхронизирующими панелями, чтобы ускорить процесс подготовки.
Разделить окно вертикально
ctrl + b "Разделить окно горизонтальноctrl + b %Включите синхронизацию, нажавctrl + b, а затемshift +:. Далее введитеsetw synchronize-panes onв командной строке. Чтобы отключить синхронизацию:setw synchronize-panes off. Для управления мышью нужно создать конфиг ~/.tmux.conf:
set-option -g -q mouse on
bind-key -T root WheelUpPane if-shell -F -t = "#{alternate_on}" "send-keys -M" "select-pane -t =; copy-mode -e; send-keys -M"
bind-key -T root WheelDownPane if-shell -F -t = "#{alternate_on}" "send-keys -M" "select-pane -t =; send-keys -M"
Утилиты командной строки cfssl и cfssljson будут использоваться для обеспечения инфраструктуры PKI и создания сертификатов TLS.
The kubectl command line utility is used to interact with the Kubernetes API Server.
Создаю kubernetes-the-hard-way пользовательскую сеть VPC:
gcloud compute networks create kubernetes-the-hard-way --subnet-mode custom
A subnet must be provisioned with an IP address range large enough to assign a private IP address to each node in the Kubernetes cluster.
gcloud compute networks subnets create kubernetes \
--network kubernetes-the-hard-way \
--range 10.240.0.0/24
Создаю правило брандмауэра, которое разрешает внутреннюю связь по всем протоколам:
gcloud compute firewall-rules create kubernetes-the-hard-way-allow-internal \
--allow tcp,udp,icmp \
--network kubernetes-the-hard-way \
--source-ranges 10.240.0.0/24,10.200.0.0/16
Создаю правило брандмауэра, которое разрешает внешний SSH, ICMP и HTTPS:
gcloud compute firewall-rules create kubernetes-the-hard-way-allow-external \
--allow tcp:22,tcp:6443,icmp \
--network kubernetes-the-hard-way \
--source-ranges 0.0.0.0/0
Выделяю статический IP-адрес, который будет подключен к внешнему балансировщику нагрузки на серверах API Kubernetes:
gcloud compute addresses create kubernetes-the-hard-way \
--region $(gcloud config get-value compute/region)
Создаю три вычислительных экземпляра, в которых будет размещена плоскость управления Kubernetes:
for i in 0 1 2; do
gcloud compute instances create controller-${i} \
--async \
--boot-disk-size 200GB \
--can-ip-forward \
--image-family ubuntu-1804-lts \
--image-project ubuntu-os-cloud \
--machine-type n1-standard-1 \
--private-network-ip 10.240.0.1${i} \
--scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
--subnet kubernetes \
--tags kubernetes-the-hard-way,controller
done
Каждому рабочему экземпляру требуется выделение подсети pod из диапазона CIDR кластера Kubernetes. Выделение подсети под будет использоваться для настройки сети контейнера в дальнейшем упражнении.pod-cidr Метаданные экземпляра будет использоваться , чтобы выставить стручок подсеть распределения в случаи вычислительных во время выполнения.
Создаю три вычислительных экземпляра, в которых будут размещаться workerы Kubernetes:
for i in 0 1 2; do
gcloud compute instances create worker-${i} \
--async \
--boot-disk-size 200GB \
--can-ip-forward \
--image-family ubuntu-1804-lts \
--image-project ubuntu-os-cloud \
--machine-type n1-standard-1 \
--metadata pod-cidr=10.200.${i}.0/24 \
--private-network-ip 10.240.0.2${i} \
--scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
--subnet kubernetes \
--tags kubernetes-the-hard-way,worker
done
Для подключения к экземплярам я буду использовать ssh.
gcloud compute ssh controller-0
Генерим CA конф файл, серт и приватный ключ
$ cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"kubernetes": {
"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "8760h"
}
}
}
}
EOF
$ cat > ca-csr.json <<EOF
{
"CN": "Kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "Kubernetes",
"OU": "CA",
"ST": "Oregon"
}
]
}
EOF
# Генерим серт и ключ
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
# Результат
$ ll
-rw------- ca-key.pem
-rw-r--r-- ca.pemГенерим клиентские и серверные сертификаты для каждого компонента Kubernetes и клиентский сертификат для админа Kubernetes
The Admin Client Certificate
cat > admin-csr.json <<EOF
{
"CN": "admin",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "system:masters",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}
EOF
# Генерим серт и ключ
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
admin-csr.json | cfssljson -bare admin
# Результат
-rw------- admin-key.pem
-rw-r--r-- admin.pemThe Kubelet Client Certificates
for instance in worker-0 worker-1 worker-2; do
cat > ${instance}-csr.json <<EOF
{
"CN": "system:node:${instance}",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "system:nodes",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}
EOF
EXTERNAL_IP=$(gcloud compute instances describe ${instance} \
--format 'value(networkInterfaces[0].accessConfigs[0].natIP)')
INTERNAL_IP=$(gcloud compute instances describe ${instance} \
--format 'value(networkInterfaces[0].networkIP)')
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-hostname=${instance},${EXTERNAL_IP},${INTERNAL_IP} \
-profile=kubernetes \
${instance}-csr.json | cfssljson -bare ${instance}
done
# Результат
-rw------- worker-0-key.pem
-rw-r--r-- worker-0.pem
-rw------- worker-1-key.pem
-rw-r--r-- worker-1.pem
-rw------- worker-2-key.pem
-rw-r--r-- worker-2.pemThe Controller Manager Client Certificate
cat > kube-controller-manager-csr.json <<EOF
{
"CN": "system:kube-controller-manager",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "system:kube-controller-manager",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}
EOF
# Генерим серт и ключ
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager
# Результат
-rw------- kube-controller-manager-key.pem
-rw-r--r-- kube-controller-manager.pemThe Kube Proxy Client Certificate
cat > kube-proxy-csr.json <<EOF
{
"CN": "system:kube-proxy",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "system:node-proxier",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}
EOF
# Генерим серт и ключ
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
kube-proxy-csr.json | cfssljson -bare kube-proxy
# Результат
-rw------- kube-proxy-key.pem
-rw-r--r-- kube-proxy.pemThe Scheduler Client Certificate
cat > kube-scheduler-csr.json <<EOF
{
"CN": "system:kube-scheduler",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "system:kube-scheduler",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}
EOF
# Генерим серт и ключ
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
kube-scheduler-csr.json | cfssljson -bare kube-scheduler
# Результат
-rw------- kube-scheduler-key.pem
-rw-r--r-- kube-scheduler.pemThe Kubernetes API Server Certificate
# Вытащить статик ип из GCE
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
--region $(gcloud config get-value compute/region) \
--format 'value(address)')
KUBERNETES_HOSTNAMES=kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster,kubernetes.svc.cluster.local
cat > kubernetes-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "Kubernetes",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}
EOF
# Генерим серт и ключ
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-hostname=10.32.0.1,10.240.0.10,10.240.0.11,10.240.0.12,${KUBERNETES_PUBLIC_ADDRESS},127.0.0.1,${KUBERNETES_HOSTNAMES} \
-profile=kubernetes \
kubernetes-csr.json | cfssljson -bare kubernetes
# Результат
-rw------- kubernetes-key.pem
-rw-r--r-- kubernetes.pemThe kubernetes-the-hard-way static IP address will be included in the list of subject alternative names for the Kubernetes API Server certificate. This will ensure the certificate can be validated by remote clients.
The Kubernetes API server is automatically assigned the kubernetes internal dns name, which will be linked to the first IP address (10.32.0.1) from the address range (10.32.0.0/24) reserved for internal cluster services during the control plane bootstrapping lab.
The Service Account Key Pair
cat > service-account-csr.json <<EOF
{
"CN": "service-accounts",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "Kubernetes",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}
EOF
# Генерим серт и ключ
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
service-account-csr.json | cfssljson -bare service-account
# Результат
-rw------- service-account-key.pem
-rw-r--r-- service-account.pem- Копируем серты и ключи на воркеры
for instance in worker-0 worker-1 worker-2; do
gcloud compute scp ca.pem ${instance}-key.pem ${instance}.pem ${instance}:~/
done- Копируем серты и ключи на контроллеры
for instance in controller-0 controller-1 controller-2; do
gcloud compute scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
service-account-key.pem service-account.pem ${instance}:~/
doneБудем генерить kubeconfig файлы для controller manager, kubelet, kube-proxy, планировщика и админа.
Kubernetes Public IP AddressКаждому kubeconfig нужно канекаться к Kubernetes API Server. Для доступности заасайним статик ip на внешний LB за которым будет Kubernetes API Servers- Извлечь статик IP
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \ --region $(gcloud config get-value compute/region) \ --format 'value(address)')
- Извлечь статик IP
The kubelet Kubernetes Configuration FileПри создании файлов kubeconfig для Kubelets должен использоваться сертификат клиента, соответствующий имени узла Kubelet. Тогда будет норм работать Kubernetes Node AuthorizerКоманды нужно запускать в той же дире, где генерили серты в разделе Provisioning a CA and Generating TLS Certificates
- Генерация kubeconfig файлов для каждого воркера
for instance in worker-0 worker-1 worker-2; do kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \ --kubeconfig=${instance}.kubeconfig kubectl config set-credentials system:node:${instance} \ --client-certificate=${instance}.pem \ --client-key=${instance}-key.pem \ --embed-certs=true \ --kubeconfig=${instance}.kubeconfig kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:node:${instance} \ --kubeconfig=${instance}.kubeconfig kubectl config use-context default --kubeconfig=${instance}.kubeconfig done
- Создались файлы
-rw------- worker-0.kubeconfig -rw------- worker-1.kubeconfig -rw------- worker-2.kubeconfig
- Генерация kubeconfig файлов для каждого воркера
The kube-proxy Kubernetes Configuration File- Генерация kubeconfig файла для kube-proxy
kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \ --kubeconfig=kube-proxy.kubeconfig kubectl config set-credentials system:kube-proxy \ --client-certificate=kube-proxy.pem \ --client-key=kube-proxy-key.pem \ --embed-certs=true \ --kubeconfig=kube-proxy.kubeconfig kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-proxy \ --kubeconfig=kube-proxy.kubeconfig kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig - Создался файл
-rw------- kube-proxy.kubeconfig
- Генерация kubeconfig файла для kube-proxy
The kube-controller-manager Kubernetes Configuration File- Генерим kubeconfig для kube-controller-manager
kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=kube-controller-manager.kubeconfig kubectl config set-credentials system:kube-controller-manager \ --client-certificate=kube-controller-manager.pem \ --client-key=kube-controller-manager-key.pem \ --embed-certs=true \ --kubeconfig=kube-controller-manager.kubeconfig kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-controller-manager \ --kubeconfig=kube-controller-manager.kubeconfig kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig
- Файл готов
-rw------- kube-controller-manager.kubeconfig
- Генерим kubeconfig для kube-controller-manager
The kube-scheduler Kubernetes Configuration File- Генерим kubeconfig для kube-scheduler
kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=kube-scheduler.kubeconfig kubectl config set-credentials system:kube-scheduler \ --client-certificate=kube-scheduler.pem \ --client-key=kube-scheduler-key.pem \ --embed-certs=true \ --kubeconfig=kube-scheduler.kubeconfig kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-scheduler \ --kubeconfig=kube-scheduler.kubeconfig kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig
- Файл готов
-rw------- kube-scheduler.kubeconfig
- Генерим kubeconfig для kube-scheduler
The admin Kubernetes Configuration File- Генерим kubeconfig для админа
kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=admin.kubeconfig kubectl config set-credentials admin \ --client-certificate=admin.pem \ --client-key=admin-key.pem \ --embed-certs=true \ --kubeconfig=admin.kubeconfig kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=admin \ --kubeconfig=admin.kubeconfig kubectl config use-context default --kubeconfig=admin.kubeconfig
- Файл готов
-rw------- admin.kubeconfig
- Генерим kubeconfig для админа
- Копируем кубеконфиги kubelet и kube-proxy на воркеры
for instance in worker-0 worker-1 worker-2; do
gcloud compute scp ${instance}.kubeconfig kube-proxy.kubeconfig ${instance}:~/
done- Копируем кубеконфиги admin, kube-controller-manager и kube-scheduler на контроллеры
for instance in controller-0 controller-1 controller-2; do
gcloud compute scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig ${instance}:~/
doneKubernetes хранит различные данные, включая состояние кластера, конфиги приложенией и секреты. Kubernetes поддерживает возможность шифрования данных кластера в состоянии покоя. Создадим ключ шифрования и encryption config, подходящие для шифрования Kubernetes Secrets
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)- Создали файл
encryption-config.yaml
cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${ENCRYPTION_KEY}
- identity: {}
EOF- И скопировали его на контроллеры
for instance in controller-0 controller-1 controller-2; do
gcloud compute scp encryption-config.yaml ${instance}:~/
doneKubernetes components are stateless and store cluster state in etcd. We will bootstrap a three node etcd cluster and configure it for high availability and secure remote access.
Все дальнейшие команды нужно запускать на каждом инстансе:
controller-0,controller-1, andcontroller-2
- Логинимся на контроллеры
$ gcloud compute ssh controller-0
$ gcloud compute ssh controller-1
$ gcloud compute ssh controller-2- Качаем официальные etcd release binaries с GitHub
wget -q --show-progress --https-only --timestamping \ "https://github.com/etcd-io/etcd/releases/download/v3.4.0/etcd-v3.4.0-linux-amd64.tar.gz" - Разархивируем и устанавливаем
etcd serverиetcdctltar -xvf etcd-v3.4.0-linux-amd64.tar.gz sudo mv etcd-v3.4.0-linux-amd64/etcd* /usr/local/bin/ - Конфигурим etcd server
sudo mkdir -p /etc/etcd /var/lib/etcd sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/
- The instance internal IP address will be used to serve client requests and communicate with etcd cluster peers. Retrieve the internal IP address for the current compute instance
INTERNAL_IP=$(curl -s -H "Metadata-Flavor: Google" \ http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip)
- Each etcd member must have a unique name within an etcd cluster. Set the etcd name to match the hostname of the current compute instance
ETCD_NAME=$(hostname -s)- Создаем systemd unit
etcd.serviceи стартуем etcd server
etcd.service
cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos
[Service]
Type=notify
ExecStart=/usr/local/bin/etcd \\
--name ${ETCD_NAME} \\
--cert-file=/etc/etcd/kubernetes.pem \\
--key-file=/etc/etcd/kubernetes-key.pem \\
--peer-cert-file=/etc/etcd/kubernetes.pem \\
--peer-key-file=/etc/etcd/kubernetes-key.pem \\
--trusted-ca-file=/etc/etcd/ca.pem \\
--peer-trusted-ca-file=/etc/etcd/ca.pem \\
--peer-client-cert-auth \\
--client-cert-auth \\
--initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\
--listen-peer-urls https://${INTERNAL_IP}:2380 \\
--listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
--advertise-client-urls https://${INTERNAL_IP}:2379 \\
--initial-cluster-token etcd-cluster-0 \\
--initial-cluster controller-0=https://10.240.0.10:2380,controller-1=https://10.240.0.11:2380,controller-2=https://10.240.0.12:2380 \\
--initial-cluster-state new \\
--data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOFsudo systemctl daemon-reload
sudo systemctl enable etcd
sudo systemctl start etcd- List the etcd cluster members
sudo ETCDCTL_API=3 etcdctl member list \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/etcd/ca.pem \
--cert=/etc/etcd/kubernetes.pem \
--key=/etc/etcd/kubernetes-key.pemoutput
3a57933972cb5131, started, controller-2, https://10.240.0.12:2380, https://10.240.0.12:2379, false
f98dc20bce6225a0, started, controller-0, https://10.240.0.10:2380, https://10.240.0.10:2379, false
ffed16798470cab5, started, controller-1, https://10.240.0.11:2380, https://10.240.0.11:2379, falseIn this lab you will bootstrap the Kubernetes control plane across three compute instances and configure it for high availability. You will also create an external load balancer that exposes the Kubernetes API Servers to remote clients. The following components will be installed on each node: Kubernetes API Server, Scheduler, and Controller Manager.
Все дальнейшие команды нужно запускать на каждом инстансе:
controller-0,controller-1, andcontroller-2
- Логинимся на контроллеры
$ gcloud compute ssh controller-0
$ gcloud compute ssh controller-1
$ gcloud compute ssh controller-2- Create the Kubernetes configuration directory
sudo mkdir -p /etc/kubernetes/config - Download the official Kubernetes release binaries
wget -q --show-progress --https-only --timestamping \
"https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-apiserver" \
"https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-controller-manager" \
"https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-scheduler" \
"https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl"- Install the Kubernetes binaries
chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/Configure the Kubernetes API Server
sudo mkdir -p /var/lib/kubernetes/
sudo mv ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
service-account-key.pem service-account.pem \
encryption-config.yaml /var/lib/kubernetes/- The instance internal IP address will be used to advertise the API Server to members of the cluster. Retrieve the internal IP address for the current compute instance
INTERNAL_IP=$(curl -s -H "Metadata-Flavor: Google" \
http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip)- Create the kube-apiserver.service systemd unit file
kube-apiserver.service
cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
--advertise-address=${INTERNAL_IP} \\
--allow-privileged=true \\
--apiserver-count=3 \\
--audit-log-maxage=30 \\
--audit-log-maxbackup=3 \\
--audit-log-maxsize=100 \\
--audit-log-path=/var/log/audit.log \\
--authorization-mode=Node,RBAC \\
--bind-address=0.0.0.0 \\
--client-ca-file=/var/lib/kubernetes/ca.pem \\
--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
--etcd-cafile=/var/lib/kubernetes/ca.pem \\
--etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
--etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
--etcd-servers=https://10.240.0.10:2379,https://10.240.0.11:2379,https://10.240.0.12:2379 \\
--event-ttl=1h \\
--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
--kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
--kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
--kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\
--kubelet-https=true \\
--runtime-config=api/all \\
--service-account-key-file=/var/lib/kubernetes/service-account.pem \\
--service-cluster-ip-range=10.32.0.0/24 \\
--service-node-port-range=30000-32767 \\
--tls-cert-file=/var/lib/kubernetes/kubernetes.pem \\
--tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOFConfigure the Kubernetes Controller Manager
- Move the kube-controller-manager kubeconfig into place and create the
kube-controller-manager.servicesystemd unit file
sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/kube-controller-manager.service
cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
--address=0.0.0.0 \\
--cluster-cidr=10.200.0.0/16 \\
--cluster-name=kubernetes \\
--cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\
--cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\
--kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
--leader-elect=true \\
--root-ca-file=/var/lib/kubernetes/ca.pem \\
--service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\
--service-cluster-ip-range=10.32.0.0/24 \\
--use-service-account-credentials=true \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOFConfigure the Kubernetes Scheduler
- Move the kube-scheduler kubeconfig into place and create the
kube-scheduler.yamlconfiguration file
sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
leaderElect: true
EOF- Create the
kube-scheduler.servicesystemd unit file
cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
--config=/etc/kubernetes/config/kube-scheduler.yaml \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOFStart the Controller Services
sudo systemctl daemon-reload
sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
sudo systemctl start kube-apiserver kube-controller-manager kube-schedulerA Google Network Load Balancer will be used to distribute traffic across the three API servers and allow each API server to terminate TLS connections and validate client certificates. The network load balancer only supports HTTP health checks which means the HTTPS endpoint exposed by the API server cannot be used. As a workaround the nginx webserver can be used to proxy HTTP health checks. In this section nginx will be installed and configured to accept HTTP health checks on port 80 and proxy the connections to the API server on https://127.0.0.1:6443/healthz
The /healthz API server endpoint does not require authentication by default
Установим базовый веб-сервер для хелсчеков
sudo apt-get update
sudo apt-get install -y nginxcat > kubernetes.default.svc.cluster.local <<EOF
server {
listen 80;
server_name kubernetes.default.svc.cluster.local;
location /healthz {
proxy_pass https://127.0.0.1:6443/healthz;
proxy_ssl_trusted_certificate /var/lib/kubernetes/ca.pem;
}
}
EOFsudo mv kubernetes.default.svc.cluster.local \
/etc/nginx/sites-available/kubernetes.default.svc.cluster.local
sudo ln -s /etc/nginx/sites-available/kubernetes.default.svc.cluster.local /etc/nginx/sites-enabled/sudo systemctl restart nginx
sudo systemctl enable nginxVerification
На каждом контроллере проверяем статус
kubectl get cs --kubeconfig admin.kubeconfig
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
etcd-1 Healthy {"health":"true"}
controller-manager Healthy ok
etcd-0 Healthy {"health":"true"}
etcd-2 Healthy {"health":"true"}Проверяем хелсчеки (тоже на контроллерах)
curl -H "Host: kubernetes.default.svc.cluster.local" -i http://127.0.0.1/healthz
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sun, 05 Jan 2020 01:44:07 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive
X-Content-Type-Options: nosniff
okIn this section you will configure RBAC permissions to allow the Kubernetes API Server to access the Kubelet API on each worker node. Access to the Kubelet API is required for retrieving metrics, logs, and executing commands in pods.
This tutorial sets the Kubelet --authorization-mode flag to Webhook. Webhook mode uses the Subject Access Review API to determine authorization
Дальнейшие команды влияют на весь кластер, и их нужно запускать только один раз с одного из узлов контроллера
gcloud compute ssh controller-0Создаем system:kube-apiserver-to-kubelet Cluster Role с разрешениями для доступа к API Kubelet и выполнения наиболее распространенных задач, связанных с управлением подами
cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:kube-apiserver-to-kubelet
rules:
- apiGroups:
- ""
resources:
- nodes/proxy
- nodes/stats
- nodes/log
- nodes/spec
- nodes/metrics
verbs:
- "*"
EOFKubernetes API Server аутентифицируется в Kubelet как пользователь kubernetes, используя сертификат клиента, как определено флагом --kubelet-client-certificate
Bind the system:kube-apiserver-to-kubelet ClusterRole to the kubernetes user
cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: system:kube-apiserver
namespace: ""
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:kube-apiserver-to-kubelet
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: kubernetes
EOFIn this section you will provision an external load balancer to front the Kubernetes API Servers. The kubernetes-the-hard-way static IP address will be attached to the resulting load balancer.
The compute instances created in this tutorial will not have permission to complete this section. Run the following commands from the same machine used to create the compute instances
Provision a Network Load Balancer
- Create the external load balancer network resources
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
--region $(gcloud config get-value compute/region) \
--format 'value(address)')
gcloud compute http-health-checks create kubernetes \
--description "Kubernetes Health Check" \
--host "kubernetes.default.svc.cluster.local" \
--request-path "/healthz"
gcloud compute firewall-rules create kubernetes-the-hard-way-allow-health-check \
--network kubernetes-the-hard-way \
--source-ranges 209.85.152.0/22,209.85.204.0/22,35.191.0.0/16 \
--allow tcp
gcloud compute target-pools create kubernetes-target-pool \
--http-health-check kubernetes
gcloud compute target-pools add-instances kubernetes-target-pool \
--instances controller-0,controller-1,controller-2
gcloud compute forwarding-rules create kubernetes-forwarding-rule \
--address ${KUBERNETES_PUBLIC_ADDRESS} \
--ports 6443 \
--region $(gcloud config get-value compute/region) \
--target-pool kubernetes-target-poolVerification
The compute instances created in this tutorial will not have permission to complete this section. Run the following commands from the same machine used to create the compute instances.
- Получаем статик IP
kubernetes-the-hard-way
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
--region $(gcloud config get-value compute/region) \
--format 'value(address)')- Курлом смотрим инфо о версии кубика в кластере
curl --cacert ca.pem https://${KUBERNETES_PUBLIC_ADDRESS}:6443/version
{
"major": "1",
"minor": "15",
"gitVersion": "v1.15.3",
"gitCommit": "2d3c76f9091b6bec110a5e63777c332469e0cba2",
"gitTreeState": "clean",
"buildDate": "2019-08-19T11:05:50Z",
"goVersion": "go1.12.9",
"compiler": "gc",
"platform": "linux/amd64"
}Будем бутстрапить воркеры. На каждой ноде будут установлены:
Команды будут выполняться на каждом воркере
- Заходим на каждый воркер
gcloud compute ssh worker-0- Install the OS dependencies
sudo apt-get update sudo apt-get -y install socat conntrack ipset
The socat binary enables support for the
kubectl port-forwardcommand - Disable Swap
By default the kubelet will fail to start if swap is enabled. It is recommended that swap be disabled to ensure Kubernetes can provide proper resource allocation and quality of service.
- Verify if swap is enabled
sudo swapon --show
- Если вывод пустой, то свап отклюен
- Disable swap
sudo swapoff -a
To ensure swap remains off after reboot consult your Linux distro documentation
- Verify if swap is enabled
wget -q --show-progress --https-only --timestamping \
https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.15.0/crictl-v1.15.0-linux-amd64.tar.gz \
https://github.com/opencontainers/runc/releases/download/v1.0.0-rc8/runc.amd64 \
https://github.com/containernetworking/plugins/releases/download/v0.8.2/cni-plugins-linux-amd64-v0.8.2.tgz \
https://github.com/containerd/containerd/releases/download/v1.2.9/containerd-1.2.9.linux-amd64.tar.gz \
https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl \
https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-proxy \
https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubelet- Create the installation directories
sudo mkdir -p \
/etc/cni/net.d \
/opt/cni/bin \
/var/lib/kubelet \
/var/lib/kube-proxy \
/var/lib/kubernetes \
/var/run/kubernetes- Install the worker binaries
mkdir containerd
tar -xvf crictl-v1.15.0-linux-amd64.tar.gz
tar -xvf containerd-1.2.9.linux-amd64.tar.gz -C containerd
sudo tar -xvf cni-plugins-linux-amd64-v0.8.2.tgz -C /opt/cni/bin/
sudo mv runc.amd64 runc
chmod +x crictl kubectl kube-proxy kubelet runc
sudo mv crictl kubectl kube-proxy kubelet runc /usr/local/bin/
sudo mv containerd/bin/* /bin/- Retrieve the Pod CIDR range for the current compute instance
POD_CIDR=$(curl -s -H "Metadata-Flavor: Google" \
http://metadata.google.internal/computeMetadata/v1/instance/attributes/pod-cidr)- Create the bridge network configuration file
cat <<EOF | sudo tee /etc/cni/net.d/10-bridge.conf
{
"cniVersion": "0.3.1",
"name": "bridge",
"type": "bridge",
"bridge": "cnio0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"ranges": [
[{"subnet": "${POD_CIDR}"}]
],
"routes": [{"dst": "0.0.0.0/0"}]
}
}
EOF- Create the loopback network configuration file
cat <<EOF | sudo tee /etc/cni/net.d/99-loopback.conf
{
"cniVersion": "0.3.1",
"name": "lo",
"type": "loopback"
}
EOF- Create the containerd configuration file
sudo mkdir -p /etc/containerd/cat << EOF | sudo tee /etc/containerd/config.toml
[plugins]
[plugins.cri.containerd]
snapshotter = "overlayfs"
[plugins.cri.containerd.default_runtime]
runtime_type = "io.containerd.runtime.v1.linux"
runtime_engine = "/usr/local/bin/runc"
runtime_root = ""
EOF- Create the containerd.service systemd unit file
containerd.service
cat <<EOF | sudo tee /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target
[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStart=/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
[Install]
WantedBy=multi-user.target
EOF sudo mv ${HOSTNAME}-key.pem ${HOSTNAME}.pem /var/lib/kubelet/
sudo mv ${HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig
sudo mv ca.pem /var/lib/kubernetes/- Create the kubelet-config.yaml configuration file
kubelet-config.yaml
cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
enabled: true
x509:
clientCAFile: "/var/lib/kubernetes/ca.pem"
authorization:
mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
- "10.32.0.10"
podCIDR: "${POD_CIDR}"
resolvConf: "/run/systemd/resolve/resolv.conf"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/${HOSTNAME}.pem"
tlsPrivateKeyFile: "/var/lib/kubelet/${HOSTNAME}-key.pem"
EOFThe resolvConf configuration is used to avoid loops when using CoreDNS for service discovery on systems running
systemd-resolved
- Create the kubelet.service systemd unit file:
kubelet.service
cat <<EOF | sudo tee /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=containerd.service
Requires=containerd.service
[Service]
ExecStart=/usr/local/bin/kubelet \\
--config=/var/lib/kubelet/kubelet-config.yaml \\
--container-runtime=remote \\
--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \\
--image-pull-progress-deadline=2m \\
--kubeconfig=/var/lib/kubelet/kubeconfig \\
--network-plugin=cni \\
--register-node=true \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOFsudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig- Create the kube-proxy-config.yaml configuration file
cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
kubeconfig: "/var/lib/kube-proxy/kubeconfig"
mode: "iptables"
clusterCIDR: "10.200.0.0/16"
EOF- Create the kube-proxy.service systemd unit file
kube-proxy.service
cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-proxy \\
--config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF sudo systemctl daemon-reload
sudo systemctl enable containerd kubelet kube-proxy
sudo systemctl start containerd kubelet kube-proxyThe compute instances created in this tutorial will not have permission to complete this section. Run the following commands from the same machine used to create the compute instances.
List the registered Kubernetes nodes
gcloud compute ssh controller-0 \
--command "kubectl get nodes --kubeconfig admin.kubeconfig"
NAME STATUS ROLES AGE VERSION
worker-0 Ready <none> 80s v1.15.3
worker-1 Ready <none> 77s v1.15.3
worker-2 Ready <none> 75s v1.15.3In this lab you will generate a kubeconfig file for the kubectl command line utility based on the admin user credentials.
Run the commands in this lab from the same directory used to generate the admin client certificates.
Each kubeconfig requires a Kubernetes API Server to connect to. To support high availability the IP address assigned to the external load balancer fronting the Kubernetes API Servers will be used.
Generate a kubeconfig file suitable for authenticating as the admin user
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
--region $(gcloud config get-value compute/region) \
--format 'value(address)')
kubectl config set-cluster kubernetes-the-hard-way \
--certificate-authority=ca.pem \
--embed-certs=true \
--server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443
kubectl config set-credentials admin \
--client-certificate=admin.pem \
--client-key=admin-key.pem
kubectl config set-context kubernetes-the-hard-way \
--cluster=kubernetes-the-hard-way \
--user=admin
kubectl config use-context kubernetes-the-hard-way- Check the health of the remote Kubernetes cluster
$ kubectl get componentstatuses
NAME STATUS MESSAGE ERROR
controller-manager Healthy ok
scheduler Healthy ok
etcd-0 Healthy {"health":"true"}
etcd-1 Healthy {"health":"true"}
etcd-2 Healthy {"health":"true"}- List the nodes in the remote Kubernetes cluster
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
worker-0 Ready <none> 7m30s v1.15.3
worker-1 Ready <none> 7m27s v1.15.3
worker-2 Ready <none> 7m25s v1.15.3Pods scheduled to a node receive an IP address from the node's Pod CIDR range. At this point pods can not communicate with other pods running on different nodes due to missing network routes.
In this lab you will create a route for each worker node that maps the node's Pod CIDR range to the node's internal IP address.
There are other ways to implement the Kubernetes networking model.
In this section you will gather the information required to create routes in the kubernetes-the-hard-way VPC network.
- Смотреть внутренний IP-адрес и диапазон Pod CIDR для каждого воркера
$ for instance in worker-0 worker-1 worker-2; do
gcloud compute instances describe ${instance} \
--format 'value[separator=" "](networkInterfaces[0].networkIP,metadata.items[0].value)'
done
10.240.0.20 10.200.0.0/24
10.240.0.21 10.200.1.0/24
10.240.0.22 10.200.2.0/24- Create network routes for each worker instance
$ for i in 0 1 2; do
gcloud compute routes create kubernetes-route-10-200-${i}-0-24 \
--network kubernetes-the-hard-way \
--next-hop-address 10.240.0.2${i} \
--destination-range 10.200.${i}.0/24
done- List the routes in the kubernetes-the-hard-way VPC network
$ gcloud compute routes list --filter "network: kubernetes-the-hard-way"
NAME NETWORK DEST_RANGE NEXT_HOP PRIORITY
default-route-68e820c79db00527 kubernetes-the-hard-way 10.240.0.0/24 kubernetes-the-hard-way 1000
default-route-e0148b1790f684a4 kubernetes-the-hard-way 0.0.0.0/0 default-internet-gateway 1000
kubernetes-route-10-200-0-0-24 kubernetes-the-hard-way 10.200.0.0/24 10.240.0.20 1000
kubernetes-route-10-200-1-0-24 kubernetes-the-hard-way 10.200.1.0/24 10.240.0.21 1000
kubernetes-route-10-200-2-0-24 kubernetes-the-hard-way 10.200.2.0/24 10.240.0.22 1000In this lab you will deploy the DNS add-on which provides DNS based service discovery, backed by CoreDNS, to applications running inside the Kubernetes cluster.
- Deploy the
corednscluster add-on
$ kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns.yaml
serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
configmap/coredns created
deployment.apps/coredns created
service/kube-dns created- List the pods created by the
kube-dnsdeployment
$ kubectl get pods -l k8s-app=kube-dns -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-5fb99965-8h4qp 1/1 Running 0 76s
coredns-5fb99965-h9cfc 1/1 Running 0 76s- Create a
busyboxdeployment
$ kubectl run --generator=run-pod/v1 busybox --image=busybox:1.28 --command -- sleep 3600- List the pod created by the
busyboxdeployment
$ kubectl get pods -l run=busybox
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 45s- Retrieve the full name of the busybox pod
POD_NAME=$(kubectl get pods -l run=busybox -o jsonpath="{.items[0].metadata.name}")- Execute a DNS lookup for the kubernetes service inside the busybox pod
$ kubectl exec -ti $POD_NAME -- nslookup kubernetes
Server: 10.32.0.10
Address 1: 10.32.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes
Address 1: 10.32.0.1 kubernetes.default.svc.cluster.localIn this lab you will complete a series of tasks to ensure your Kubernetes cluster is functioning correctly.
In this section you will verify the ability to encrypt secret data at rest
- Create a generic secret
kubectl create secret generic kubernetes-the-hard-way \
--from-literal="mykey=mydata"- Print a hexdump of the
kubernetes-the-hard-waysecret stored in etcd
gcloud compute ssh controller-0 \
--command "sudo ETCDCTL_API=3 etcdctl get \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/etcd/ca.pem \
--cert=/etc/etcd/kubernetes.pem \
--key=/etc/etcd/kubernetes-key.pem\
/registry/secrets/default/kubernetes-the-hard-way | hexdump -C"The etcd key should be prefixed with
k8s:enc:aescbc:v1:key1, which indicates theaescbcprovider was used to encrypt the data with thekey1encryption key.
In this section you will verify the ability to create and manage Deployments
- Create a deployment for the nginx web server
$ kubectl create deployment nginx --image=nginx- List the pod created by the nginx deployment
$ kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-554b9c67f9-djhrq 1/1 Running 0 39sIn this section you will verify the ability to access applications remotely using port forwarding
- Retrieve the full name of the nginx pod
POD_NAME=$(kubectl get pods -l app=nginx -o jsonpath="{.items[0].metadata.name}")- Forward port 8080 on your local machine to port 80 of the nginx pod
$ kubectl port-forward $POD_NAME 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80- In a new terminal make an HTTP request using the forwarding address
$ curl --head http://127.0.0.1:8080
HTTP/1.1 200 OK
Server: nginx/1.17.6
Date: Mon, 06 Jan 2020 01:29:00 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 19 Nov 2019 12:50:08 GMT
Connection: keep-alive
ETag: "5dd3e500-264"
Accept-Ranges: bytesIn this section you will verify the ability to retrieve container logs.
- Print the nginx pod logs
$ kubectl logs $POD_NAME
127.0.0.1 - - [06/Jan/2020:01:29:00 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.64.1" "-"In this section you will verify the ability to execute commands in a container
- Print the nginx version by executing the nginx -v command in the nginx container
$ kubectl exec -ti $POD_NAME -- nginx -v
nginx version: nginx/1.17.6In this section you will verify the ability to expose applications using a Service
- Expose the nginx deployment using a NodePort service
$ kubectl expose deployment nginx --port 80 --type NodePort
service/nginx exposedThe LoadBalancer service type can not be used because your cluster is not configured with cloud provider integration. Setting up cloud provider integration is out of scope for this tutorial.
- Retrieve the node port assigned to the nginx service
NODE_PORT=$(kubectl get svc nginx \
--output=jsonpath='{range .spec.ports[0]}{.nodePort}')- Create a firewall rule that allows remote access to the nginx node port
gcloud compute firewall-rules create kubernetes-the-hard-way-allow-nginx-service \
--allow=tcp:${NODE_PORT} \
--network kubernetes-the-hard-way- Retrieve the external IP address of a worker instance
EXTERNAL_IP=$(gcloud compute instances describe worker-0 \
--format 'value(networkInterfaces[0].accessConfigs[0].natIP)')- Make an HTTP request using the external IP address and the nginx node port
$ curl -I http://${EXTERNAL_IP}:${NODE_PORT}
HTTP/1.1 200 OK
Server: nginx/1.17.6
Date: Mon, 06 Jan 2020 01:37:04 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 19 Nov 2019 12:50:08 GMT
Connection: keep-alive
ETag: "5dd3e500-264"
Accept-Ranges: bytesIn this lab you will delete the compute resources created during this tutorial.
- Delete the controller and worker compute instances
gcloud -q compute instances delete \
controller-0 controller-1 controller-2 \
worker-0 worker-1 worker-2 \
--zone $(gcloud config get-value compute/zone)- Delete the external load balancer network resources
gcloud -q compute forwarding-rules delete kubernetes-forwarding-rule \
--region $(gcloud config get-value compute/region)
gcloud -q compute target-pools delete kubernetes-target-pool
gcloud -q compute http-health-checks delete kubernetes
gcloud -q compute addresses delete kubernetes-the-hard-way- Delete the kubernetes-the-hard-way firewall rules
gcloud -q compute firewall-rules delete \
kubernetes-the-hard-way-allow-nginx-service \
kubernetes-the-hard-way-allow-internal \
kubernetes-the-hard-way-allow-external \
kubernetes-the-hard-way-allow-health-check- Delete the kubernetes-the-hard-way network VPC
gcloud -q compute routes delete \
kubernetes-route-10-200-0-0-24 \
kubernetes-route-10-200-1-0-24 \
kubernetes-route-10-200-2-0-24
gcloud -q compute networks subnets delete kubernetes
gcloud -q compute networks delete kubernetes-the-hard-way- Развернуть локальное окружение для работы с Kubernetes
- Развернуть Kubernetes в GKE
- Запустить reddit в Kubernetes
- kubectl - фактически, главная утилиты для работы c Kubernetes API (все, что делает kubectl, можно сделать с помощью HTTP-запросов к API k8s)
- Директории ~/.kube - содержит служебную инфу для kubectl (конфиги, кеши, схемы API)
- minikube - утилиты для разворачивания локальной инсталляции Kubernetes.
Устанавливаю kubectl
Для работы Minukube требуется локальный гипервизор. Я буду использовать VirtualBox
Инструкция по установке Minikube для разных ОС: https://kubernetes.io/docs/tasks/tools/install-minikube/
После установки запускаю Minikube:
$ minikube start
😄 minikube v1.7.2 on Ubuntu 18.04
✨ Automatically selected the virtualbox driver
💿 Downloading VM boot image ...
> minikube-v1.7.0.iso.sha256: 65 B / 65 B [--------------] 100.00% ? p/s 0s
> minikube-v1.7.0.iso: 166.68 MiB / 166.68 MiB [-] 100.00% 8.69 MiB p/s 20s
🔥 Creating virtualbox VM (CPUs=2, Memory=2000MB, Disk=20000MB) ...
🐳 Preparing Kubernetes v1.17.2 on Docker 19.03.5 ...
💾 Downloading kubectl v1.17.2
💾 Downloading kubelet v1.17.2
💾 Downloading kubeadm v1.17.2
🚀 Launching Kubernetes ...
🌟 Enabling addons: default-storageclass, storage-provisioner
⌛ Waiting for cluster to come online ...
🏄 Done! kubectl is now configured to use "minikube"
P.S. Если нужна конкретная версия kubernetes, указывайте флаг
--kubernetes-version <version> (v1.8.0)
P.P.S.По-умолчанию используется VirtualBox. Если у вас другой гипервизор, то ставьте флаг
--vm-driver=<hypervisor>
Наш Minikube-кластер развернут. При этом автоматически был настроен конфиг kubectl. Проверим, что это так:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 18m v1.17.2
Конфигурация kubectl - это контекст.
Контекст - это комбинация:
- cluster - API-сервер
- user - пользователь для подключения к кластеру
- namespace - область видимости (не обязательно, поумолчанию default)
Информацию о контекстах kubectl сохраняет в файле
~/.kube/config:
$ cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority: /home/playjim/.minikube/ca.crt
server: https://192.168.99.100:8443
name: minikube
contexts:
- context:
cluster: minikube
user: minikube
name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
user:
client-certificate: /home/playjim/.minikube/client.crt
client-key: /home/playjim/.minikube/client.key
Кластер (cluster) - это:
- server - адрес kubernetes API-сервера
- certificate-authority - корневой сертификат (которым подписан SSL-сертификат самого сервера), чтобы убедиться, что нас не обманывают и перед нами тот самый сервер
- name (Имя) для идентификации в конфиге
Пользователь (user) - это:
- Данные для аутентификации (зависит от того, как настроен сервер). Это могут быть:
- username + password (Basic Auth
- client key + client certificate
- token
- auth-provider config (например GCP)
- name (Имя) для идентификации в конфиге
Контекст (контекст) - это:
- cluster - имя кластера из списка clusters
- user - имя пользователя из списка users
- namespace - область видимости по-умолчанию (не обязательно)
- name (Имя) для идентификации в конфиге
Обычно порядок конфигурирования kubectl следующий:
- Создать cluster: $ kubectl config set-cluster … cluster_name
- Создать данные пользователя (credentials) $ kubectl config set-credentials … user_name
- Создать контекст
$ kubectl config set-context context_name
--cluster=cluster_name
--user=user_name - Использовать контекст $ kubectl config use-context context_name
Таким образом kubectl конфигурируется для подключения к разным кластерам, под разными пользователями. Текущий контекст можно увидеть так:
$ kubectl config current-context
minikube
Список всех контекстов можно увидеть так:
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* minikube minikube minikube
Каталог с yaml манифестами приложения находится в ./kubernetes/reddit
Основные объекты - это ресурсы Deployment. Как помним из предыдущего занятия, основные его задачи:
- Создание ReplicationSet (следит, чтобы число запущенных Pod-ов соответствовало описанному)
- Ведение истории версий запущенных Pod-ов (для различных стратегий деплоя, для возможностей отката)
- Описание процесса деплоя (стратегия, параметры стратегий)
Для запуска приложения сначало создаю namespace dev, а потом применяю конфигурацию:
$ kubectl apply -f ./kubernetes/reddit/dev-namespace.yml
namespace/dev created
$ kubectl apply -f ./kubernetes/reddit/ -n dev
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
Minikube может выдавать web-странцы с сервисами которые были помечены типом NodePort Попробуйте:
$ minikube service list
Получить список расширений:
$ minikube addons list
- Ingress Controller
- Ingress
- Secret
- TLS
- LoadBalancer Service
- Network Policies
- PersistentVolumes
- PersistentVolumeClaims
- селекторные сервисы (k8s сам находит POD-ы по label’ам)
- безселекторные сервисы (мы вручную описываем конкретные endpoint’ы)
и способ коммуникации с ними (тип (type) сервиса):
- ClusterIP - дойти до сервиса можно только изнутри кластера
- nodePort - клиент снаружи кластера приходит на опубликованный порт
- LoadBalancer - клиент приходит на облачный (aws elb, Google gclb) ресурс балансировки
- ExternalName - внешний ресурс по отношению к кластеру
ClusterIP - это виртуальный (в реальности нет интерфейса, pod’а или машины с таким адресом) IP-адрес из диапазона адресов для работы внутри, скрывающий за собой IP-адреса реальных POD-ов. Сервису любого типа (кроме ExternalName) назначается этот IP-адрес.
Kubernetes не имеет своего собственного DNSсервера для разрешения имен. Поэтому используется плагин kube-dns (это тоже Pod). Его задачи:
- ходить в API Kubernetes’a и отслеживать Service-объекты
- заносить DNS-записи о Service’ах в собственную базу
- предоставлять DNS-сервис для разрешения имен в IP-адреса (как внутренних, так и внешних)
Service с типом NodePort - похож на сервис типа ClusterIP, только к нему прибавляется прослушивание портов нод (всех нод) для доступа к сервисам снаружи. При этом ClusterIP также назначается этому сервису для доступа к нему изнутри кластера. kube-proxy прослушивается либо заданный порт (nodePort: 32092), либо порт из диапазона 30000-32670. Дальше IPTables решает, на какой Pod попадет трафик.
Тип LoadBalancer позволяет нам использовать внешний облачный балансировщик нагрузки как единую точку входа в наши сервисы, а не полагаться на IPTables и не открывать наружу весь кластер.
Для более удобного управления входящим снаружи трафиком и решения недостатков LoadBalancer можно использовать другой объект Kubernetes - Ingress.
Ingress – это набор правил внутри кластера Kubernetes, предназначенных для того, чтобы входящие подключения могли достичь сервисов (Services)
Сами по себе Ingress’ы это просто правила. Для их применения нужен Ingress Controller
NetworkPolicy - инструмент для декларативного описания потоков трафика.
Вместо того, чтобы хранить данные локально на ноде, имеет смысл подключить удаленное хранилище. В нашем случае можем использовать Volume gcePersistentDisk, который будет складывать данные в хранилище GCE.
- Работа с Helm
- Развертывание Gitlab в Kubernetes
- Запуск CI/CD конвейера в Kubernetes
Helm - пакетный менеджер для Kubernetes. С его помощью мы будем:
- Стандартизировать поставку приложения в Kubernetes
- Декларировать инфраструктуру
- Деплоить новые версии приложения
Helm - клиент-серверное приложение. Установим его клиентскую часть - консольный клиент Helm.
brew install helm@2
cd /usr/local/bin
ln -s /usr/local/opt/helm@2/bin/tiller tiller
ln -s /usr/local/opt/helm@2/bin/helm helm2Helm читает конфигурацию kubectl (~/.kube/config) и сам определяет текущий контекст (кластер, пользователь, неймспейс).
Если потребуется сменить кластер, то либо меняем контекст с помощью:
kubectl config set-contextлибо подгружаем helm’у собственный config-файл флагом --kube-context.
Установим серверную часть Helm’а - Tiller.
Tiller - это аддон Kubernetes, т.е. Pod, который общается с API Kubernetes.
Для этого понадобится ему выдать ServiceAccount и назначить роли RBAC, необходимые для работы.
Chart - это пакет в Helm.
Основным содержимым Chart’ов являются шаблоны манифестов Kubernetes.
- Создадим директорию ui/templates
- Перенесем в неё все манифесты, разработанные ранее для сервиса ui (ui-service, ui-deployment, ui-ingress)
- Переименуем их (уберем префикс “ui-“) и поменяем расширение на .yaml) - стилистические правки
└── ui
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── ingress.yaml
│ └── service.yamlПо-сути, это уже готовый пакет для установки в Kubernetes:
- Убедимся, что у вас не развернуты компоненты приложения в kubernetes. Если развернуты - удалим их
- Установим Chart
helm install --name test-ui-1 ui/- Передаем имя и путь до Chart'a соответсвенно. Посмотрим, что получилось
helm ls- Развертывание Prometheus в k8s
- Настройка Prometheus и Grafana для сбора метрик
- Настройка EFK для сбора логов
Из Helm-чарта установим ingress-контроллер nginx:
$ kubectl apply -f kubernetes/reddit/tiller.yml
$ helm init --service-account tiller
$ helm install stable/nginx-ingress --name nginx
ip-адрес выданный nginx добавляю в /etc/hosts
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 10m
nginx-nginx-ingress-controller LoadBalancer 10.0.6.149 35.198.70.111 80:31528/TCP,443:30314/TCP 2m57s
nginx-nginx-ingress-default-backend ClusterIP 10.0.9.107 <none> 80/TCP 2m57s
$ cd kubernetes/Charts && helm fetch --untar stable/prometheus
Create in to chart directory file custom_values.yml main differences in the custom_values file:
- отключена часть устанавливаемых сервисов (pushgateway,
- alertmanager, kube-state-metrics)
- включено создание Ingress’а для подключения через nginx
- поправлен endpoint для сбора метрик cadvisor
- уменьшен интервал сбора метрик (с 1 минуты до 30 секунд)
Запуск Прометеус:
helm upgrade prom . -f custom_values.yml --install
install grafana:
helm upgrade --install grafana stable/grafana --set "adminPassword=admin" \
--set "service.type=NodePort" \
--set "ingress.enabled=true" \
--set "ingress.hosts={reddit-grafana}"
http://reddit-grafana/ user: admin pass: admin
Добавим prometheus data-source в GUi.
Адрес найдем из имени сервиса prometheus сервера:
kubectl get svcДобавим самый распространенный dashboard для отслеживания состояния ресурсов k8s.
Добавьте собственные дашборды, созданные ранее (в ДЗ по мониторингу). Они должны также успешно отобразить данные.
В текущий момент на графиках, относящихся к приложению, одновременно отображены значения метрик со всех источников сразу. При большом количестве сред и при их динамичном изменении имеет смысл сделать динамичной и удобно настройку наших дашбордов в Grafana.
Сделать это можно в нашем случае с помощью механизма templating’а.
- создадмим новую переменную
- Name: namespace
- Label: Env
- Type: Query
- Quary: label_values(namespace) - получить значения всех label-ов kubernetes_namespace
- Regex: /.+/ - отфильтруем (уберем пустой namespace)
- Multi-value - checked - возможность выбирать несколько значений
- Include All option - checked - возножность выбирать все значения одной кнопкой
У нас появился список со значениями переменной.
Пока что они бесполезны. Чтобы их использование имело эффект нужно шаблонизировать запросы к Prometheus.
Меняем запрос в графиках на: {kubernetes_namespace=~"$namespace"}
Теперь мы можем настраивать общие шаблоны графиков и с помощью переменных менять в них нужные нам поля (в нашем случае это namespace).
Параметризуем все Dashboard’ы, отражающие параметры работы приложения (созданные нами в предыдущих ДЗ) reddit для работы с несколькими окружениями (неймспейсами).
Получившиеся дашборды сохраним в репозиторий ./kubernetes/Grafana/Dashboards/.
Импортируем следующий график: https://grafana.com/dashboards/741
На этом графике одновременно используются метрики и шаблоны из cAdvisor, и из kube-state-metrics для отображения сводной информации по деплойментам.
Добавим label самой мощной ноде в кластере:
$ kubectl label node gke-cluster-1-default-pool-20c17a34-vsd8 elastichost=true
node/gke-cluster-1-default-pool-20c17a34-vsd8 labeled
Логирование в k8s будем выстраивать с помощью уже известного стека EFK:
- ElasticSearch - база данных + поисковый движок
- Fluentd - шипер (отправитель) и агрегатор логов
- Kibana - веб-интерфейс для запросов в хранилище и отображения их результатов
Создадим файлы в новой папке kubernetes/efk/:
- fluentd-ds.yaml
- fluentd-configmap.yaml
- es-service.yaml
- es-statefulSet.yaml
- es-pvc.yaml
Запустим стек в вашем k8s:
kubectl apply -f ./efkKibana поставим из helm чарта:
helm upgrade --install kibana stable/kibana \
--set "ingress.enabled=true" \
--set "ingress.hosts={reddit-kibana}" \
--set "env.ELASTICSEARCH_URL=http://elasticsearch-logging:9200" \
--set "service.type=NodePort" \
--version 0.1.1