# Cобеседование по Java. Разбор вопросов и ответов.


<a href="https://mc.yandex.ru/pixel/8711235002931986822?rnd=%aw_random%">
    <img src="https://mc.yandex.ru/pixel/8711235002931986822?rnd=%aw_random%" />        
  </a>&nbsp;&nbsp;
<a href="https://mc.yandex.ru/watch/92801430">
    <img src="https://mc.yandex.ru/watch/92801430" />        
  </a>&nbsp;&nbsp;


Нажмите ★, если вам нравится проект. Ваш вклад сердечно ♡ приветствуется.

Если вам интересно мое резюме: https://github.com/DEBAGanov


https://badtry.net/docker-tutorial-dlia-novichkov-rassmatrivaiem-docker-tak-iesli-by-on-byl-ighrovoi-pristavkoi/
https://eternalhost.net/base/vps-vds/docker-zapusk-konteynera

# Docker / Kubernetes
- [Cобеседование по Java. Разбор вопросов и ответов.](#cобеседование-по-java-разбор-вопросов-и-ответов)
- [Docker / Kubernetes](#docker--kubernetes)
  - [Что такое _Docker_?](#что-такое-docker)
  - [Зачем _Docker_?](#зачем-docker)
  - [Что такое _Docker Image (образ)_?](#что-такое-docker-image-образ)
  - [Что такое _Docker контейнер_?](#что-такое-docker-контейнер)
  - [Как менять контейнер?](#как-менять-контейнер)
  - [Что такое _Dockerfile_?](#что-такое-dockerfile)
  - [Как пробрасывать локальную папку в контейнер Докера (монтирование папки)?](#как-пробрасывать-локальную-папку-в-контейнер-докера-монтирование-папки)
  - [Что такое Docker Volumes?](#что-такое-docker-volumes)
  - [Как работают и пробрасываются Docker порты?](#как-работают-и-пробрасываются-docker-порты)
  - [Слои Docker образа, прослойка данных и кеширование](#слои-docker-образа-прослойка-данных-и-кеширование)
  - [Что такое Docker-Compose?](#что-такое-docker-compose)
  - [Как писать Микро Сервисы с Docker? Что такое микросервисы?](#как-писать-микро-сервисы-с-docker-что-такое-микросервисы)
  - [Что-такое-Kubernetes](#что-такое-kubernetes)
  - [Компоненты и архитектура Kubernetes](#компоненты-и-архитектура-kubernetes)
  - [Kubernet Cluster](#kubernet-cluster)
  - [Kubectl](#kubectl)
  - [Основные Обьекты Kubernetes](#основные-обьекты-kubernetes)
  - [Kubernetes Pods](#kubernetes-pods)
  - [Kubernetes Deployment](#kubernetes-deployment)
  - [Kubernetes Services](#kubernetes-services)
  - [Kubernetes Ingress Controller](#kubernetes-ingress-controller)
  - [Helm Charts](#helm-charts)

## Что такое _Docker_?
На сайте Докера можно найти статью (https://www.docker.com/), в которой подробно рассказывается, что такое Docker. Из их слов - это стандартизированное ПО для разработки и развёртывания проектов.

__Если говорить проще:__ Давайте на секунду забудем про Докер, и вспомним про такую ностальгическую штуку, как GameBoy Color. Если вы помните, игры для этой приставки поставлялись в виде картриджей. И я уверен в том, что производители видео игр пользуются успехом из-за своей простоты:

+ Когда ты хочешь поиграть, ты просто вставляешь картридж в приставку, и игра сразу же работает.
+ Ты можешь поделиться своей игрой с друзьями, передав всего лишь картридж, который они вставят в приставку, и сразу же смогут играть.

Docker следует похожему принципу - позволяет запускать своё ПО настолько просто, что это соизмеримо с вставкой картриджа и нажатием кнопки ON на приставке. Это основная суть, почему Docker настолько полезен - теперь кто угодно, у кого установлен Docker может запустить ваше приложение, выполнив для этого всего несколько команд. Раньше, вы, создавая приложения, к примеру на PHP, устанавливали локально PHP, MySql, возможно, NodeJs, при этом устанавливая зависимости в виде нужных расширений и библиотек. И, в случае передачи вашего скрипта какому-то знакомому, ему требовалось настраивать аналогичное окружение, аналогичных версий, иметь аналогичные расширения и конфигурацию, чтобы успешно запустить ваше приложение. Сейчас же, при использовании Докера, такой проблемы не возникнет впринципе. Теперь вам достаточно иметь установленную программу Docker, которая по одной вашей команде установит окружение, описанное в конфиге для запуска вашего приложения.

Какое программное обеспечение можно запустить с помощью докера? В техническом плане, Docker чем-то похож на виртуальную машину:

Докер - это движок, который запускает виртуальную операционную систему, имеющую чрезвычайно маленький вес (в отличие от Vagrant-а, который создаёт полноценную виртуальную ОС, Докер, имеет особые образы ПО, запускающиеся в виртуальной среде, не создавая полную копию ОС). Docker позволяет запустить ОС Linux в изолированной среде очень быстро, в течение нескольких минут.

[к оглавлению](#Docker)

## Зачем _Docker_?
__Кошмар при установке ПО__, с которым приходится сталкиваться. У вас когда-нибудь было такое, что вы пытаетесь установить ПО на ваш компьютер, а оно отказывается работать? Вы получаете несколько непонятных вам ошибок, из-за которых ничего не запускается. И после нескольких часов гугления, на десятой странице гугла...и на каком-то форуме, до этого неизвестного вам, вы наконец-то находите случайный комментарий, который помогает исправить вашу проблему.

Аналогично, что делает написание PC игр более сложным, чем написание Game Boy игр - это то, что приходится проектировать систему с учётом большого множества существующих PC девайсов и спецификаций. Так как разные компьютеры имеют различные операционные системы, драйвера, процессоры, графические карты, и т.д.
И потому задача разработчика - написать приложение совместимое со всеми популярными системами, является достаточно затруднительной и трудоёмкой.

Docker, как и Game Boy приставка, __берёт стандартизированные части программного обеспечения и запускает их__ так, как Game Boy запускал бы игру.

В этом случае вы не должны беспокоиться об операционной системе, на которой пользователь будет запускать ваше приложение. Теперь, когда пользователи будут запускать приложение через Docker - конфигурация будет собрана автоматически, и код будет выполняться ВСЕГДА.

__Как разработчик__, теперь вы не должны волноваться о том, на какой системе будет запущено ваше приложение.
__Как пользователь__, вам не нужно волноваться о том, что вы скачаете неподходящую версию ПО (нужного для работы программы). В Докере эта программа будет запущена в аналогичных условиях, при которых это приложение было разработано, потому, исключается факт получить какую-то новую, непредвиденную ошибку. Для пользователя все действия сводятся к принципу подключи и играй.

[к оглавлению](#Docker)

## Что такое _Docker Image (образ)_?
Docker образ (он же Docker Image), похож на Game Boy картридж - это просто программное обеспечение. Это стандартизированное программное обеспечение, которое запускается на любой приставке Game Boy. Вы можете дать игру вашему другу, и он сможет просто вставить картридж в приставку, и играть.

Как в случае с картриджами, бывают различные игры, так и Docker имеет различные образы ПО: ubuntu, php (который наследуется от оригинального образа Ubuntu), nodejs, и т.д.

```java
docker pull IMAGE_NAME // где <IMAGE_NAME> - имя скачиваемого образа

docker pull ubuntu:18.10
```
Эта команда сообщает Докеру о том, что нужно скачать образ Ubuntu 18.10 с Dockerhub.com - основной репозиторий Docker-образов, на котором вы и можете посмотреть весь их список и подобрать нужный образ для вашей программы. Представленные в хабе образы можно найти при помощи команд docker и search. К примеру, найти образ Ubuntu можно следующим образом:
````java
docker search ubuntu
````

Теперь, для того, чтобы посмотреть список всех загруженных образов, нужно выполнить:
````java
docker images
````
Проводя аналогии, команда docker images выглядит как коллекция картриджей от приставки, которые у вас есть сейчас

[к оглавлению](#Docker)


## Что такое _Docker контейнер_?
Теперь представьте, что мы обновили нашу приставку с Game Boy на GameCube. Игры хранятся на диске, который предназначен только для чтения самого образа игры. А прочие файлы (сохранения, кеш и т.д.) сохраняются внутри самой приставки, локально.

Так же, как и игра на диске, исходный Docker Image (образ) - __неизменяемый__.

Docker контейнер - это экземпляр запущенного образа. Аналогично тому, что вы вставляете диск в приставку, после чего игра начинается. А сам образ игры никак не модифицируется, все файлы, содержащие изменения хранятся где-то локально на приставке.

Запуск Docker контейнера соответствует тому, что вы играете в свою Gamecube игру. Docker запускает ваш образ в своей среде, аналогично тому, как Gamecube запускает игру с диска, не модифицируя оригинальный образ, а лишь сохраняя изменения и весь прогресс в какой-то песочнице.

Для запуска контейнера существует команда: 
````java
docker run <image> <опциональная команды, которая выполнится внутри контейнера>
````

__Давайте запустим наш первый контейнер Ubuntu__
````java
docker run ubuntu:18.10 echo 'hello from ubuntu'
````
Команда echo 'hello from ubuntu' была выполнена внутри среды Ubuntu. Другими словами, эта команда была выполнена в контейнере ubuntu:18.10.

Теперь выполним команду для проверки списка запущенных контейнеров:
````java
docker ps
````
Здесь пустота... это потому что docker ps показывает только список контейнеров, которые запущены в данный момент (наш же контейнер выполнил одну команду echo 'hello from ubuntu' и завершил свою работу).

А для того, чтобы посмотреть список всех контейнеров без исключения, нужно добавить флаг -a, выполним:
````java
docker ps -a
````

После выполнения нужных операций внутри контейнера, то Docker-контейнер завершает работу. Это похоже на режим сохранения энергии в новых игровых консолях - если вы не совершаете действий какое-то время, то система выключается автоматически.

Каждый раз, когда вы будете выполнять команду docker run, будет создаваться новый контейнер, на каждую из выполненных команд.

__Выполнение неограниченное количество команда внутри контейнера__

Давайте добавим немного интерактивности в наше обучение. Мы можем подключиться к консоли виртуальной ОС (Ubuntu 18.10), и выполнять любое количество команд без завершения работы контейнера, для этого, запустим команду:
````java
docker run -it ubuntu:18.10 /bin/bash
````
Опция -it вместе с /bin/bash даёт доступ к выполнению команд в терминале внутри контейнера Ubuntu.

Теперь, внутри этого контейнера можно выполнять любые команды, применимые к Ubuntu. Вы же можете представлять это как мини виртуальную машину, условно, к консоли которой мы подключились по SSH.

__Узнаём ID контейнера__
Иногда является очень полезным узнать ID контейнера, с которым мы работаем. И как раз-таки, при выполнении команды docker run -it <IMAGE> /bin/bash, мы окажемся в терминале, где все команды будут выполняться от имени пользователя root@<containerid>.

Теперь, все команды буду выполняться внутри операционной системы Ubuntu. Попробуем, например, выполнить команду ls, и посмотрим, список директорий, внутри этого образа Ubuntu.docker-ubuntu-ls

Docker контейнер является полностью независимым от системы хоста, из которой он запускался. Как изолированная виртуальная машина. И в ней вы можете производить любые изменения, которые никак не повлияют на основную операционную систему.

Это аналогично тому, как, если бы вы играли в Mario Kart на приставке Gamecube, и неважно, что вы делаете в игре, вы никак не сможете изменить само ядро игры, или изменить информацию, записанную на диске.

Контейнер является полностью независимым и изолированным от основной операционной системы, аналогично виртуальной операционной системе. Вы можете вносить любые изменения внутри виртуалки, и никакие из этих изменений не повлияют на основную операционную систему.

Теперь откройте новое окно терминала (не закрывая и не отключаясь от текущего), и выполните команду docker ps
docker-ps-new-window

Только на этот раз вы можете увидеть, что контейнер с Ubuntu 18.10 в текущий момент запущен.

Теперь вернёмся назад к первому окну терминала (который находится внутри контейнера), и выполним:
````java
mkdir /truedir   //создаст папку truedir
exit             //выйдет из контейнера, и вернётся в основную ОС
````
Выполнив команду exit, контейнер будет остановлен (чтобы убедиться, можете проверить командой docker ps). Теперь, вы так же знаете, как выйти из Docker контейнера.

Теперь, попробуем ещё раз просмотреть список всех контейнеров, и убедимся, что новый контейнер был создан docker ps -adocker-ps--a2

Так же, для того, чтобы запустить ранее созданный контейнер, можно выполнить команду docker start <CONTAINER_ID>,

где CONTAINER_ID - id контейнера, который можно посмотреть, выполнив команду docker ps -a (и увидеть в столбце CONTAINER_ID)

В моём случае, CONTAINER_ID последнего контейнера = 7579c85c8b7e (у вас же, он будет отличаться)

Запустим контейнер командой:
````java
docker start 7579c85c8b7e    //ваш CONTAINER_ID
docker ps
docker exec -it 7579c85c8b7e /bin/bash  //ваш CONTAINER_ID
````
И теперь, если внутри контейнера выполнить команду ls, то можно увидеть, что ранее созданная папка truedir существует в этом контейнереdocker-exex-truedir

Команда exec позволяет выполнить команду внутри запущенного контейнера. В нашем случае, мы выполнили /bin/bash, что позволило нам подключиться к терминалу внутри контейнера.

Для выхода, как обычно, выполним exit.

Теперь остановим и удалим Docker контейнеры командами:
````java
docker stop <CONTAINER_ID>
docker rm <CONTAINER_ID>
docker ps a                // просмотрим список активных контейнеров
docker stop aa1463167766   // остановим активный контейнер
docker rm aa1463167766     // удалим контейнер
docker rm bb597feb7fbe     // удалим второй контейнер
````
В основном, нам не нужно, чтобы в системе плодилось большое количество контейнеров. Потому, команду docker run очень часто запускают с дополнительным флагом --rm, который удаляет запущенный контейнер после работы:
```java
docker run -it --rm ubuntu:18.10 /bin/bash
```

[к оглавлению](#Docker)

## Как менять контейнер?
Во время запуска контейнера из существующего образа у пользователя есть возможность создавать или удалять файлы, аналогично работе на виртуальной машине. При этом изменения будут распространяться только в определенном контейнере. Доступна и возможность запуска с последующей остановкой контейнера, но после его удаления с помощью docker rm будут утеряны внесенные изменения.

Соответственно, следует ознакомиться со способом сохранения текущего контейнера как нового образа.

По завершении инсталляции Node.js в контейнере Ubuntu, на компьютере работает загруженный из образа контейнер. При этом он будет отличаться от использованного для его создания образа. В свою очередь, пользователю может понадобиться уже контейнер Node.js, чтобы использовать его при создании для новых образов.

Соответственно, следует сохранить результаты в текущем образе предложенной ниже командой:
```java
docker commit -m "What you did to the image" -a "Author Name" container_id repository/new_image_name
```
Добавление опции -m дает возможность указать сообщение подтверждения. Это позволит будущим пользователям образа понять, что именно было изменено. Что касается параметра -a — с его помощью можно указать, кто его создатель. container_id является тем же идентификатором, который был использован ранее, во время запуска интерактивной сессии в Docker.

К примеру, с именем пользователя admin и идентификатором 2c8ec46adae1 команда должна иметь следующий вид:
```java
docker commit -m "added Node.js" -a "admin" 2c8ec46adae1 admin/ubuntu-nodejs
```
Как запустить Docker контейнер на Linux

После того, как образ будет подтвержден (commit) он сохраняется на компьютере локально.

Завершающий этап — сохранение созданных образов в базу Docker Hub или другой репозиторий, откуда их может скачать любой желающий. Чтобы получить такую возможность, предварительно нужно создать аккаунт.

Отправка образов в репозиторий начинается с авторизации на Docker Hub.
```java
docker login -u docker-registry-username
```

Чтобы вход был успешно осуществлен, потребуется ввести пароль Docker Hub. Если он правильный, авторизация пройдет успешно.

Здесь важно знать, что если в реестре Docker имя пользователя отличается от локального, используемого при создании образа, обязательно нужно привязать этот образ к имени учетной записи в хабе. На примере контейнера с NodeJS команда привязки будет выглядеть так:
```java
docker tag admin/ubuntu-nodejs docker-registry-username/ubuntu-nodejs
```

После чего можно приступать к загрузке образа на сервер:
```java
docker push docker-registry-username/docker-image-name
```

__Автозагрузка контейнеров__

Часто встречается ситуация, когда контейнеры останавливаются вследствие определенных факторов. Простейший пример – произошла перезагрузка сервера. Чтобы избавиться от необходимости вручную запускать их, можно настроить автозапуск контейнеров. Для этого следует создать текстовые файлы со специальным форматом для сервиса systemcmd. Рассмотрим пример их создания на примере контейнера my-db, введя в терминал команду:
```java
cat /etc/systemd/system/my-db.service
```
В пустой файл необходимо добавить следующий код и сохранить его:
```java
[Unit]
Description=MY DB (PG) docker container
Requires=docker.service
After=docker.service
[Service]
Restart=always
ExecStart=/usr/bin/docker start -a my-db
ExecStop=/usr/bin/docker stop -t 2 my-db
TimeoutSec=30
[Install]
WantedBy=multi-user.target
```

После этого остается перезапустить демон systemcmd и включить автозагрузку контейнера mydb, набрав в терминале поочередно команды:
```java
systemctl daemon-reload
systemctl start my-db.service
systemctl enable my-db.service
```

[к оглавлению](#Docker)

## Что такое _Dockerfile_?
Docker позволяет вам делиться с другими средой, в которой ваш код запускался и помогает в её простом воссоздании на других машинах.

Dockerfile - это обычный конфигурационный файл, описывающий пошаговое создание среды вашего приложения. В этом файле подробно описывается, какие команды будут выполнены, какие образы задействованы, и какие настройки будут применены. А движок Docker-а при запуске уже распарсит этот файл (именуемый как Dockerfile), и создаст из него соответствующий образ (Image), который был описан.

К примеру, если вы разрабатывали приложение на php7.2, и использовали ElasticSearch 9 версии, и сохранили это в Dockerfile-е, то другие пользователи, которые запустят образ используя ваш Dockerfile, получат ту же среду с php7.2 и ElasticSearch 9.

С Dockerfile вы сможете подробно описать инструкцию, по которой будет воссоздано конкретное состояние. И делается это довольно-таки просто и интуитивно понятно.

Если вам нужно воспроизвести среду (образ) на другом ПК, с докером вы так же имеете два варианта при создании образа:

+ Вы можете запаковать ваш контейнер, создать из него образ (аналогично тому, что вы запишите на диск новую игру с собственными модификациями). Это похоже на способ, когда вы делитесь сохранениями напрямую.
+ Или же, можно описать Dockerfile - подробную инструкцию, которая приведёт среду к нужному состоянию.

Я склоняюсь ко второму варианту, потому что он более подробный, гибкий, и редактируемый (вы можете переписать Dockerfile, но не можете перемотать состояние образа в случае прямых изменений).

Пришло время попрактиковаться на реальном примере. Для начала, создадим файл cli.php в корне проекта с содержимым:
````php
<?php
$n = $i = 5;

while ($i--) {
    echo str_repeat(' ', $i).str_repeat('* ', $n - $i)."\n";
}
````
Пример Dockerfile:
````java
FROM php:7.2-cli
WORKDIR
COPY cli.php /cli.php
RUN chmod +x /cli.php
CMD php /cli.php
EXPOSE 8080:80
````
Имена команд в Dockerfile это синтаксис разметки Dockerfile. Эти команды означают:
+ FROM - это как буд-то вы выбираете движок для вашей игры (Unity, Unreal, CryEngine). Хоть вы и могли бы начать писать движок с нуля, но больше смысла было бы в использовании готового. Можно было бы использовать, к примеру, ubuntu:18.10, в нашем коде используется образ php:7.2-cli, потому весь код будет запускаться внутри образа с предустановленным php 7.2-cli.
+ LABEL — добавляет метаданные для образа. Хорошее место для размещения информации об авторе;
+ ENV— создаёт переменную окружения;
+ WORKDIR - устанавливает рабочую директорию для инструкции CMD и ENTRYPOINT
+ COPY - Копирует файл с основной системы в контейнер (копируем файл cli.php внутрь контейнера, с одноимённым названием)
+ ADD — делает всё то же, что и инструкция COPY. Но ещё может распаковывать локальные .tar файлы;
+ RUN - Выполнение shell-команды из терминала контейнера (в текущем случае, присвоим права на выполнение скрипта /cli.php
+ CMD - Выполняет эту команду каждый раз, при новом запуске контейнера. Параметры могут быть переопределены. Использоваться может только одна инструкция CMD
+ EXPOSE - порт на ПК и внутри контейнера
+ ARG — определяет переменную для передачи Docker’у во время сборки;
+ ENTRYPOINT — предоставляет команды и аргументы для выполняющегося контейнера. Она похожа на CMD, но параметры ENTRYPOINT не переопределяются, если контейнер запущен с параметрами командной строки
+ VOLUME — создаёт точку подключения директории для добавления и хранения постоянных данных

Все команды https://docs.docker.com/engine/reference/builder/#from или https://tproger.ru/translations/docker-instuction/#:~:text=WORKDIR%20%E2%80%94%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%B0%D0%B2%D0%BB%D0%B8%D0%B2%D0%B0%D0%B5%D1%82%20%D1%80%D0%B0%D0%B1%D0%BE%D1%87%D1%83%D1%8E%20%D0%B4%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D1%8E%20%D0%B4%D0%BB%D1%8F,%D0%B8%20%D0%B0%D1%80%D0%B3%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D1%8B%20%D0%B4%D0%BB%D1%8F%20%D0%B2%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F%D1%8E%D1%89%D0%B5%D0%B3%D0%BE%D1%81%D1%8F%20%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%B9%D0%BD%D0%B5%D1%80%D0%B0.

При написании Dockerfile, начинать следует с наиболее актуального существующего образа, дополняя его в соответствии с потребностями вашего приложения.
К примеру, мы могли не использовать образ php:7.2-cli, а могли взять ubuntu:18.10, последовательно выполняя команды в RUN одна за одной, устанавливая нужное ПО. Однако, в этом мало смысла, когда уже есть готовые сборки.

Для создания образа из Dockerfile нужно выполнить:
````java
docker build <DOCKERFILE_PATH> --tag <IMAGE_NAME>
<DOCKERFILE_PATH> - путь к файлу Dockerfile (. - текущая директория),
<IMAGE_NAME> - имя, под которым образ будет создан
````

Выполним:
````java
docker build . --tag pyramid
````
При том, что имя файла Dockerfile при указывании пути упускается, нужно указывать только директорию, в которой этот файл находится (а . означает, что файл находится в той директории, из которой была запущена консоль)

После того, как команда выполнилась, мы можем обращаться к образу по его имени, которое было указано в <IMAGE_NAME>, проверим список образов: docker images

Теперь, запустим контейнер из нашего образа командой docker run pyramid

__Сначала мы скопировали файл cli.php в Docker образ, который создался с помощью Dockerfile. Для того, чтобы удостовериться в том, что файл действительно был проброшен внутрь контейнера, можно выполнить команду docker run pyramid ls, которая в списке файлов покажет и cli.php.__

Однако, сейчас этот контейнер недостаточно гибкий. Нам бы хотелось, чтобы можно было удобно изменять количество строк, из скольки состоит пирамида.

Для этого, отредактируем файл cli.php, и изменим, чтобы количество аргументов принималось из командной строки. Отредактируем вторую строку на:
````php
$n = $i = $argv[1] ?? 5; //а было $n = $i = 5
// это значит, что мы принимаем аргумент из консоли, а если он не получен, то используем по-умолчанию 5
````
После чего, пересоберём образ: docker build . --tag pyramid
И запустим контейнер: docker run pyramid php /cli.php 9, получив вывод ёлки пирамиды в 9 строк

Почему это работает?
Когда контейнер запускается, вы можете переопределить команду записанную в Dockerfile в поле CMD.

Наша оригинальная CMD команда, записанная в Dockerfile php /cli.php - будет переопределена новой php /cli.php 9.
Но, было бы неплохо передавать этот аргумент самому контейнеру, вместо переписывания всей команды. Перепишем так, чтобы вместо команды php /cli.php 7 можно было передавать просто аргумент-число.

Для этого, дополним Dockerfile:
````java
FROM php:7.2-cli
COPY cli.php /cli.php
RUN chmod +x /cli.php
ENTRYPOINT ["php", "/cli.php"]
## аргумент, который передаётся в командную строку
CMD  ["9"]
````
Мы немного поменяли формат записи. В таком случае, CMD будет добавлена к тому, что выполнится в ENTRYPOINT.

["php", "/cli.php"] на самом деле запускается, как php /cli.php. И, учитывая то, что CMD будет добавлена после выполнения текущей, то итоговая команда будет выглядеть как: php /cli.php 9 - и пользователь сможет переопределить этот аргумент, передавая его в командную строку, во время запуска контейнера.

Теперь, заново пересоберём образ
````java
docker build . --tag pyramid
````
И запустим контейнер с желаемым аргументом
````java
docker run pyramid 3
````
[к оглавлению](#Docker)


## Как пробрасывать локальную папку в контейнер Докера (монтирование папки)?
Монтирование директории в Docker контейнер - это предоставление доступа контейнеру на чтение содержимого вашей папки из основной операционной системы. Помимо чтения из этой папки, так же, контейнер может её изменять, и такая связь является двусторонней: при изменении файлов в основной ОС изменения будут видны в контейнере, и наоборот.

__Монтирование директории в контейнер позволяет ему читать и писать данные в эту директорию, изменяя её состояние.__

Для того, чтобы смонтировать папку из основной системы в контейнер, можно воспользоваться командой
docker run -v <DIRECTORY>:<CONTAINER_DIRECTORY> ...,
где DIRECTORY - это путь к папке, которую нужно смонтировать,
CONTAINER_DIRECTORY - путь внутри контейнера.

Только путь к монтируемой папке должен быть прописан полностью: C:\projects\docker-example, или на *nix-системах можно воспользоваться конструкцией $(pwd)

Выполним команду:
````java
docker run -it -v C:\projects\docker-example\cli:/mounted  ubuntu:18.10 /bin/bash
ls
ls mounted
touch mounted/testfile
````
При выполнении этой команды, указанная папка смонтируется в папку /mounted, внутри файловой системы контейнера, а команда touch mounted/testfile создаст новый файл под названием testfile, который вы можете увидеть из основной ОС.

Теперь вы можете увидеть, что после выполнения этой команды в текущей директории появился новый файл testfile. И это говорит о том, что двусторонняя связь работает - при изменении директории на основной ОС всё отразится на смонтированную папку внутри контейнера, а при изменениях изнутри контейнера всё отразится на основную ОС.

Монтирование папки позволяет вам изменять файлы вашей основной системы прямо во время работы внутри Docker контейнера.

Это удобная особенность, которая позволяет нам редактировать код в редакторе на основной ОС, а изменения будут сразу же применяться внутри контейнера.
[к оглавлению](#Docker)


## Что такое Docker Volumes?
С Docker Volum-ами мы имеем контейнер, который хранит постоянные данные где-то на нашем компьютере (это актуально, потому что после завершения работы контейнер удаляет все пользовательские данные, не входящие в образ). Вы можете прикрепить Volume-данные к любому из запущенных контейнеров.

Вместо того, чтобы каждый раз, при запуске контейнера, писать, какие из папок вы хотите смонтировать, вы просто можете создать один контейнер с общими данными, который потом будете прикреплять.

В Dockerfile прописывается параметр volumes: и указывается название переменной и путь к данным, которые хотим сохранять (внутри конкретного контейнера). Вне контейнера можно указать значения по умолчанию
```java
services:
    php:
        volumes: 
            - bddata:/var/lib/postgresql/data/

volumes:
    bddata: null
    
```
Лично я, не использую это очень часто на практике, потому что есть много других методов по управлению данными. Однако, это может быть очень полезно для контейнеров, которые должны сохранять какие-то важные данные, или данные, которыми нужно поделиться между несколькими контейнерами.

[к оглавлению](#Docker)


## Как работают и пробрасываются Docker порты?

Docker позволяет нам получить доступ к какому-то из портов контейнера, пробросив его наружу (в основную операционную систему). По умолчанию, мы не можем достучаться к каким-либо из портов контейнера. Однако, в Dockerfile опция EXPOSE позволяет нам объявить, к какому из портов мы можем обратиться из основной ОС.

Для этого, на по-быстрому, запустим Docker-образ php-apache, который работает на 80 порту.

Для начала, создадим новую папку apache (перейдём в неё cd apache), в которой создадим файл index.php, на основе которого мы и поймём, что всё работает.
````php
<?php
echo 'Hello from apache. We have PHP version = ' . phpversion() . PHP_EOL;
````
А так же, в этой папке создадим файл Dockerfile:
```java
FROM php:7.2-apache
# Указываем рабочую папку
WORKDIR /var/www/html
# Копируем все файлы проекта в контейнер
COPY . /var/www/html
EXPOSE 80
```
Для работы с сетью в Docker, нужно проделать 2 шага:

+ Прокинуть системный порт (Expose).
+ Привязать порт основной ОС к порту контейнера (выполнить соответствие).

+ Это что-то похоже на подключение вашей PS4 приставки к телевизору по HDMI кабелю. При подключении кабеля, вы явно указываете, какой HDMI-канал будет отображать видео. В этой аналогии наша основная ОС будет как телевизор, а контейнер - это игровая консоль. Мы должны явно указать, какой порт основной операционной системы будет соответствовать порту контейнера.

EXPOSE в Докерфайле разрешает подключение к 80 порту контейнера - как разрешение HDMI подключения к PS4.

Выполним первый шаг прокидывания порт. Сбилдим контейнер:
````java
docker build . --tag own_php_apache
````
И после этого, запустим контейнер:
````java
docker run own_php_apache
apache_call
````
После чего, попробуем перейти по адресу localhost:80

Но, это не сработало, потому что мы ещё не выполнили 2 шаг по маппингу портов.

Выйдите из контейнера, нажав CTRL+C. Если у вас проблемы с остановкой контейнера, в новом окне откройте терминал, выполните docker ps, найдите ID контейнера, который сейчас запущен, и выполните docker stop {CONTAINER_ID} (указав ваш ID контейнера)

Теперь, осталось сообщить нашему компьютеру, какой порт контейнера ему нужно слушать, и для этого формат записи будет такой:
````java
docker run -p <HOST_PORT>:<CONTAINER_PORT>
````
И мы можем указать любое соответствие портов, но сейчас просто укажем, что порт системы 80 будет слушать 80 порт контейнера:
````java
docker run -p 80:80 own_php_apache
````
Здесь, вы уже наверное заметили, что добавился новый параметр -p 80:80, который говорит Docker-у: я хочу, чтобы порт 80 из apache был привязан к моему локальному порту 80.

И теперь, если перейти по адресу localhost:80, то должны увидеть успешный ответ:
[к оглавлению](#Docker)


## Слои Docker образа, прослойка данных и кеширование
Каждый раз, когда вы собираете образ, он кешируется в отдельный слой. Ввиду того, что образы являются неизменяемыми, их ядро никогда не модифицируются, потому применяется система кеширования, которая нужна для увеличения скорости билдинга.

Каждая команда в Dockerfile сохраняется как отельный слой образа.

Рассмотрим это на примере нашего прошлого Dockerfile-а:
````java
FROM php:7.2-apache
# Копирует код ядра
COPY . /var/www/html
WORKDIR /var/www/html
EXPOSE 80
````
Когда вы пишите свой Dockerfile, вы добавляете слои поверх существующего основного образа (указанного в FROM), и создаёте свой собственный образ (Image).

+ FROM: говорит Докеру взять за основу этот существующий образ. А все новые команды будут добавлены слоями поверх этого основного образа.
+ COPY: копирует файлы с основной ОС в образ
+ WORKDIR: устанавливает текущую папку образа в /var/www/html

Слой Образа Докера это как точка сохранения в игре Super Mario. Если вы хотите изменить какие-то вещи, произошедшие до этой точки сохранения, то вам придётся перезапустить этот уровень полностью. Если вы хотите продолжить прогресс прохождения, вы можете начать с того места, где остановились.

Docker начинает кешировать с "того места, где остановился" во время билдинга Dockerfile. Если в Докерфайле не было никаких изменений с момента последнего билдинга, то образ будет взят полностью из кеша. Если же вы измените какую-то строку в Dockerfile - кеш будет взят только тех слоёв команд, которые находятся выше изменённой команды.

Для иллюстрации этого, добавим новые строки в Dockerfile:
````java
FROM php:7.2-apache
WORKDIR /var/www/html
# Copy the app code
COPY . /var/www/html
RUN apt-get update && apt-get upgrade -y && apt-get install -y curl php7.2-mbstring php7.2-zip php7.2-intl php7.2-xml php7.2-json php7.2-curl
RUN echo "Hello, Docker Tutorial"
EXPOSE 80
````
После чего, пересоберём образ:
````java
docker build . --tag own_php_apache
````
Выполнив эту команду, из вывода в консоль можете увидеть, что некоторые слои были взяты из кеша. Это как раз те команды, выше которых в Dockerfile не было добавлено/изменено содержимого.

И можно заметить, что в случае изменения Dockerfile, билдинг занимает больше времени, потому что не используется кеш. Где бы вы не написали команду, все закешированные команды, которые находятся ниже в Dockerfile, будут перебилжены заново. А те, что находятся выше, будут по-прежнему браться из кеша.

Когда вы используете команду COPY, она копирует указанную директорию в контейнер. И, в случае изменения содержимого любого из файлов этой директории, кеш команды COPY будет сброшен. Docker сверяет изменения во время билдинга в каждом из файлов. Если они были изменены, кеш будет сброшен, как и для всех последующих слоёв.

Если честно, то это действительно крутая функция. Docker следит за изменениями в файлах и использует кеш всегда, когда это нужно (когда были произведены изменения в каких-то из файлов). Изменение ваших файлов потенциально может затрагивать будущие команды, из-за чего, и все последующие слои билдятся заново, а не берутся из кеша.

Какие выводы из этого можно сделать:

+ Команды, которые вероятнее всего не будут меняться в будущем, нужно помещать как можно выше в Dockerfile.
+ Команды копирования данных нужно помещать ниже, потому что файлы при разработке изменяются довольно часто.
+ Команды, которые требуют много времени на билдинг, нужно помещать выше.

+ В заключение, так же хочу сказать, как можно уменьшить размер слоёв Docker образов.
В Dockerfile вы можете иметь несколько команд (RUN) на выполнение:
```java
RUN apt-get update
RUN apt-get install -y wget
RUN apt-get install -y curl
```

В результате выполнения этой команды, будет создано 3 разных слоя в образе. Вместо этого, все команды стараются объединить в одну строку:
```java
RUN apt-get update && apt-get install -y wget curl
```
Если команда становится длинной, и нечитаемой, то для переноса на следующую строку делаем так:
```java
RUN apt-get update && apt-get install -y wget curl && \
&& apt-get clean -y \
&& docker-php-ext-install soap mcrypt pdo_mysql zip bcmath
```
Если же команда становится слишком большой, и неудобной для чтения, то можно создать новый shell скрипт, в который поместить длинную команду, и запускать этот скрипт одной простой командой RUN.

Технически, только команды ADD, COPY, и RUN создают новый слой в Docker образе, остальные команды кешируются по-другому (https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#minimize-the-number-of-layers)
[к оглавлению](#Docker)


## Что такое Docker-Compose?
Инструмент Docker Compose входит в комплект официального программного обеспечения для Docker. Он позволяет решать различные задачи, связанные с управлением одновременно несколькими контейнерами, составляющих в целом один проект.

Самый очевидный пример – веб-сайт, где для авторизации пользователей необходимо подключение к базе данных. Для такого проекта нужно два сервиса – один отвечает за функционирование сайта, а другой за базу данных.

Docker-compose написан в формате YAML который по своей сути похож на JSON или XML. Но YAML имеет более удобный формат для его чтения, чем вышеперечисленные. В формате YAML имеют значения пробелы и табуляции, именно пробелами отделяются названия параметров от их значений.

Создадим новый файл docker-compose.yml, для рассмотрения синтаксиса Docker Compose:
```java
version: '3'

services:
app:
build:
context: .
ports:
- 8080:80
```

Теперь, построчно разберёмся с заданными параметрами, и что они значат:
+ version: какая версия docker-compose используется (3 версия - самая последняя на даный момент).
+ services: контейнеры которые мы хотим запустить.
+ app: имя сервиса, может быть любым, но желательно, чтобы оно описывало суть этого контейнера.
+ build: шаги, описывающие процесс билдинга.
+ context: где находится Dockerfile, из которого будем билдить образ для контейнера.
+ ports: маппинг портов основной ОС к контейнеру.

```java
docker-compose build - собирает проект из Dockerfile
docker-compose up  - запускает все сервисы контейнера
docker-compose down  - останавливает все сервисы контейнера
Ctrl+C для остановки контейнера
```

[к оглавлению](#Docker)


## Как писать Микро Сервисы с Docker? Что такое микросервисы?

[к оглавлению](#Docker)


## Что-такое-Kubernetes
Это платформа с открытым исходным кодом для развертывания, масштабирования, управления и контроля контейнеризованных приложений либо сервисов.

__Kubernetes делает следующее:__
+ Управляет и запускает контейнеры. 
+ Балансирует сетевой трафик между узлами кластера Kubernetes и количеством реплик контейнеров. Можно раскатить один образ на несколько Воркер нодов, получив разные IP порты и DNS имя, а кубер сделает Load Balancing между этими контейнерами
+ Осуществляет контроль состояния, автоматические развертывания и откаты реплик контейнеров внутри узлов кластера Kubernetes.
+ Осуществляет распределение нагрузки между узлами кластера Kubernetes. Например управление ресурсами при развертывания нового контейнера
+ Предоставляет автоматическое монтирование систем хранения для контейнеров. Например прикрепить локальный или облачный диск к одному или нескольким докер контейнерам
+ Предоставляет декларативный API и CLI для управления
+ И еще множество полезных, и не очень, модулей и сервисов, которые можно развернуть для управления автоматизацией, инфраструктурой и контейнерами

__Kubernetes не делает следующее:__
+ Не собирает контейнеры с исходным кодом вашего приложения или сервиса
+ Не предоставляет процессы и решения непрерывной интеграции (CI)
+ Не включает в себя решения и системы сбора журналов и метрик
+ Не включает в себя решения и системы хранения данных. Только подключение сторонних
+ Не включает в себя решения и системы хранения контейнеров (registry)
+ Не включает в себя решения и системы от всех бед и болячек инфраструктуры

Kubernetes или K8S — это не просто система оркестрации. Техническое определение оркестрации — это выполнение определенного рабочего процесса: сначала сделай A, затем B, затем C. Напротив, Kubernetes содержит набор независимых, компонуемых процессов управления, которые непрерывно переводят текущее состояние к предполагаемому состоянию. Неважно, как добраться от А до С. Не требуется также и централизованный контроль. Это делает систему более простой в использовании, более мощной, надежной, устойчивой и расширяемой.

Основной фактор использования Kubernetes в технологических компаниях, где ведется активная разработка приложений, - это гибкий подход к разработке. Сегодня подход в построении архитектуры приложений изменился - приложения больше не выглядят как монолит кода или один большой сервис, где весь функционал был в одном репозитории. Раньше сборки проектов занимали достаточно много времени, но с приходом контейнеров и таких методологий как DevOps приложения стали модульными, и теперь за каждую функцию или группу функций отвечают определенные сервисы этого приложения. Это можно сравнить с кирпичиками конструктора LEGO: из всех деталей складывается наше приложение, каждый сервис мы можем достать, чтобы что-то изменить и протестировать и вставить обратно в нашу конструкцию. Главная идея состоит в том, чтобы быстро внедрять новый функционал в уже имеющееся приложение.

Но что если у нас таких сервисов тысячи, каждый за что-то отвечает и работает сам по себе? А если еще развернуты несколько реплик для отказоустойчивости? Как управлять всем и уделить внимание каждому из них? Как понять, что сервис правильно работает и взаимодействует с другими? Для этого и есть специальные системы, оркестраторы в своем роде, такие как HashiCorp Nomad, Docker Swarm и Kubernetes.

Бизнес чаще всего ценит такие возможности К8S как собирать и тестировать только часть приложения, с которой мы работаем, что в разы уменьшает объем необходимых ресурсов; добавлять и убирать сервисы «на лету», тестировать новый функционал в разных регионах и смотреть, как он себя показывает. За счет этого Kubernetes, который дает унификацию и гибкость в способе обслуживания и содержания сервисов приложения. 

__Kubernetes предоставляет:__

+ Быструю и автоматическую масштабируемость. При росте нагрузки можно быстро добавить необходимые узлы приложения, а также быстро их вывести, чтобы не тратить драгоценные ресурсы
+ Гибкий подход к эксплуатации. Мы можем быстро и легко построить структуру приложения, так как вся структура описывается в конфигурационных файлах - манифестах
+ Гибкий подход в управлении. Kubernetes не потребует перестройки инфраструктуры и прочего, если вы захотели провести тестирование, внедрить новый сервис или сделать деплой по методологии blue-green
+ Универсальность. С помощью манифестов легко переехать, если вы захотели поменять провайдера или переезжали в свой собственный кластер
+ Низкий порог вхождения в использование. Kubernetes довольно легок в освоении манифестов, потому что большую часть работы он делает за вас

Если вы задумываетесь о преимуществах, описанных выше, и можете точно сказать, что вам нужна гибкость в разработке и быстрое внедрение сервисов, адаптируемый подход и универсальность в управлении большим количеством сервисов и их реплик, то думаю, что пора попробовать Kubernetes и у себя.

Однако, если ваш проект имеет постоянную нагрузку и не требует высокой степени гибкости и быстрого масштабирования, новый функционал появляется редко и у вас есть команда, уверенно работающая с существующим окружением, то на данный момент возможности K8S для бизнеса избыточны, но "посмотреть" на технологию в фоновом режиме все же стоит, так как те или иные условия могут и поменяться.

[к оглавлению](#Docker)

## Компоненты и архитектура Kubernetes

Kubernetes, так как он был реализован как облачное решение, предпочтительнее разворачивать именно в облаке.

Если вы решились ставить Kubernetes внутри, на своих собственных ресурсах, то вам придется позаботиться о развёртывании и обслуживании такой инфраструктуры вокруг кластера как: репозиторий для контейнеров, внешние или внутренние балансировщики, сетевые хранилища, хранилище секретов, решения для сбора логов и метрик. Также важна и внутренняя инфраструктура “кубера”: CertManager, Ingress, Istio и другие.

1) Основной компонент K8S состоит из __Кластера (Cluster)__

Сам кластер состоит из __узлов или нодах (Nodes)__, в которых помимо контейнеров компонентов самого кластера, размещаются контейнеры наших проектов и сервисов.

2) __Worker nodes__ - сервер, на котором запускаются и работают контейнеры

Состоит из компонентов:

+ kubelet - сервис или агент, который контролирует запуск компонентов (контейнеров) кластера
+ kube-proxy - конфигурирует правила сети на узлах

3) Плоскость управления (__Master nodes__) управляет рабочими узлами и подами в кластере. Там располагаются компоненты, которые управляют узлами кластера и предоставляют доступ к API.

__Control plane__ или Kubernete Master, состоит из компонентов:

+ kube-apiserver - предоставляет API кубера
+ kube-scheduler - планирует размещение подов на узлах кластера
+ kube-controller-manager - запускает контроллеры
+ kubelet - сервис или агент, который контролирует запуск основных компонентов (контейнеров) кластер
+ etcd - распределенное key-value хранилище для всех данных кластера. Необязательно располагается внутри мастера, может стоять как отдельный кластер

Когда запускаем команды управления они всегда посылаются именно на Master nodes. С воркер нодами напрямую не работаем.

__Виды контроллеров__
+ Deployments - контроллер, который управляет состоянием развертывания подов, которое описывается в манифесте, следит за удалением и созданием экземпляров подов. Управляет контроллерами ReplicaSet.
+ ReplicaSet - гарантирует, что определенное количество экземпляров подов всегда будет запущено в кластере.
+ StatefulSets - так же как и Deployments, управляет развертыванием и масштабированием набора подов, но сохраняет набор идентификаторов и состояние для каждого пода.
+ DaemonSet - гарантирует, что на каждом узле кластера будет присутствовать экземпляр пода.
+ Jobs - создает определенное количество подов и смотрит, пока они успешно не завершат работу. Если под завершился с ошибкой, повторяет создание, которое мы описали определенное количество раз. Если под успешно отработал, записывает это в свой журнал.
+ CronJob - запускает контроллеры Jobs по определенному расписанию.

[к оглавлению](#Docker)

## Kubernet Cluster

Состоит минимум из одного (для непрерывной и безопасной работы может быть больше), Мастер нода. И минимум один Воркер нод (на практике их обычно больше)

В мастер ноде указываем откуда брать Docker Images (из Dockerhub, AWS Container Registry ECR, Google Container Registry, Azure Container Registry и тд). Контейнеры разворачиваются на воркер нодах, заполняя память и процессоры. Если ресурсов будет не хватать, кубер может предложить масштабироваться, добавив еще воркер нодов.
```java
//minikube исп для управления при локальном запуске. при работе с AWS, Google кластером и тд нужно использовать соответствующий кластер и систему управления
minikube start -p NAME - запустить с нашим именем
minikube start --cpus=2 --memory=4gb/4000mb --disk-size=20gb - с конкретными ресурсами
minikube start --no-vtx-check - если ошибка и просить включить виртуализацию в биосе
minikube ssh - зайти внутрь виртуальной машины (можно через VirtualBox)
        
        
kubectl get componentstatuses  - состояние кластера
kubectl cluster-info - инфа о кластере
kubectl get nodes
kubectl version --client

```

Чтобы загрузить Докер образ в кластер нужно 

[к оглавлению](#Docker)

## Kubectl
При работе с кластером нам потребуется инструмент командной строки kubectl. https://Kubernetes.io/ru/docs/tasks/tools/install-kubectl/

Его можно установить на локальную машину и управлять несколькими кластерами с одной точки.

Основные команды, которые мы часто будем использовать:

+ Kubectl get [имя объекта] -n [Имя_пространства_имен] - команда get, как несложно понять из ее названия, выводит нужную нам информацию в основном виде таблицы. Также с помощью нее можно получить yaml, который хранится внутри кластера. Очень часто применяется ключ -n для указания пространства имен, где лежат объекты.

Примеры:
```java
kubectl get services -n Имя_неймспейса - выводит все сервисы в пространстве имён

kubectl get pods --all-namespaces - выводит все поды во всех пространств имён

kubectl get pods -o wide - выводит все поды в текущем пространстве имён с подробностями в виде расширенной таблицы

kubectl get pods -n Имя_неймспейса - выводит все поды в пространстве имён
```

+ Kubectl apply -f [имя манифеста yaml] - команда apply применяет манифест к кластеру, управляет созданием объектов в кластере с помощью манифестов yaml. Если в манифесте не указано пространство имен, его можно задать с помощью ключа -n [Имя_пространства_имен].

Примеры:
```java
kubectl apply -f ./my-manifest.yaml - создать ресурсы

kubectl apply -f ./my1.yaml -f ./my2.yaml - создать ресурсы из нескольких файлов

kubectl apply -f ./dir - создать ресурсы из всех файлов манифеста в директории

kubectl apply -f https://K8S.io/manifest - создать ресурсы из URL-адреса
```

Отличная шпаргалка по командам есть в документации https://Kubernetes.io/ru/docs/reference/kubectl/cheatsheet/

[к оглавлению](#Docker)

## Основные Обьекты Kubernetes

Контейнер не является обьектом K8S - это обьект Докера)

1) Pod - самый маленький обьект Кубера, который мы можем создать. Состоит из контейнера (обычно) или нескольких контейнеров (если надо). Чаще один, чтобы при масштабировании не плодить лишние копии
2) Deployment - состоит из одного или нескольких одинаковых Подов, даже если в разных Нодах
3) Service - дает доступ к Подам, которые в Деплое, через ClusterIP, NodePort, LoadBalancer, ExternalName
4) Nodes - сервера, где все это работает
5) Cluster - обьединение нодов

Т.е. расположены от меньше к большему и каждый следующий содержит предыдущие

[к оглавлению](#Docker)

## Kubernetes Pods

1) Запуск и управление из консоли

```java
kubectl run name --image=imageName --port=80  - создание пода с именем name из образа imageName (если нет локально скачает с Докерхаба) на порту 80
kubectl get pods  - список Подов
kubectl delete pods name  - удалить Под с именем name
kubectl describe pods name  - описание Пода name. Нода, на которой он развернут, его IP внутри кластера, контейнеры внутри Пода, Volums, последние Events и много другой инфы
kubectl exec name date - запустить команду в Поде, например date
kubectl exec -it name sh - запустить команду в Поде, например sh - консоль, и остаться внутри Пода
kubectl logs name - логи Пода name
kubectl port-forward name 7788:80 - переносим с локального порта 7788 на порт 80 Пода name
Ctrl+C  - выйти из Пода
```
2) Для запуска из YML файла

Сам файл, например простой pod-my-web-ver1.yaml:
```java
apiVersion : v1
kind: Pod
metadata:
    name: my-web
    labels:
        env   : prod
        app   : main
        tier  : fronted
        owner : Shell26
spec:
    containers: 
        - name : container-apache
          image: httpd:latest
          ports:
            - containerPort: 80

        - name : container-api
          image: tomcat:8.5.38
          ports:
            - containerPort: 8080
```
И запускаем файл через консоль:
```java
kubectl apply -f ./pod-my-web-ver1.yaml
```

Если изменили файл, убиваем старый Под собранный из файла и запускаем новый:
```java
kubectl delete -f ./pod-my-web-ver1.yaml
kubectl apply -f ./pod-my-web-ver1.yaml
```
__Без удаления можно менять только поле image, иначе ошибка. Достаточно повторно запустить kubectl apply__

[к оглавлению](#Docker)

## Kubernetes Deployment

Если что-то случится с Кластером и Нода умерла, то Кластер перезапустит Ноды, но Поды автоматически не пересоберутся. Для этого Поды создают через Девелопмент, а он управляет Подами

1) Из консоли:
```java
kubectl create deployments name --image httpd:latest
kubectl get deploy
kubectl describe deployments name
kubectl scale deployment name --replicas 4  - создаст 4 копии и ReplicaSet, раскидав копии по разным Нодам
kubectl get rs  - покажет ReplicaSet
kubectl autoscale deployment name --min=4 --max=6 --cpu-percent=80  - автоматически масштабирует для загрузки cpu на 80 и создаст обьект HorizontalPodAutoscaler
kubectl get hpa  - покажет HorizontalPodAutoscaler
kubectl rollout status deployment/name  
kubectl set image deployment/name oldimagename=newimagename  - обновим в нашем Деплое контейнер oldimagename на новый newimagename
kubectl rollout undo deployment/name  - откатит на прошлую версию
kubectl rollout history deployment/name  - покажет историю обновлений
kubectl rollout undo deployment/name --to-revision=2  - откатит на версию 2
kubectl rollout restart deployment/name   - пересоберет текущую версию
```

2) Из файла-манифеста deployment-simple.yaml:

```java
apiVersion : apps/v1
kind: Deployment
metadata:
    name: my-web-deployment
    labels:
        app : my-k8s-application
spec:
    replicas: 2              //сколько реплик делать на Нодах, если надо
    selector:                //с какими Подами будет работать Deployment
        matchLabels:         //с Подами которые совпадают по Lables
            project: shell   //именно такой
    template:                //тут часть с Подами
        metadata:
            labels:
                project: shell
    spec:
        containers:
            - name : shell-web
              image: httpd:latest
              ports:
                - containerPort: 80
...                     //можно в этом же файле разделим тремя точками, можно в отдельном файле
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
    name: my-autoscaling
spec:
    scaleTargetRef:                           //что он автоскейлит?
        apiVersion: apps/v2
        kind: Deployment                     //Деплоймент
        name: my-web-deployment             //конкретно этот Деплоймент
    minReplicas: 3
    maxReplacas: 5
    metrics:                          //как скейлит, основываясь на чем?
    - type: Resource                  //либо такой вариант
      resource:
        name: cpu
        target:
            type: Utilization
            averageUtilization: 90
    - type: Resource                  //либо такой вариант
      resource:
        name: memory
        target:
            type: Utilization
            averageUtilization: 70
```

Запускаем Манифест-файл:
```java
kubectl apply -f ./deployment-simple.yaml
```
В итоге создаст два обьекта, Deployment и HorizontalPodAutoscaler

[к оглавлению](#Docker)

## Kubernetes Services
__Типы:__

+ ClusterIP - IP только внутри K8S Кластера. Выбирается по-умолчанию. Один такой сервис существует всегда, Kubernetes создает его автоматически
+ NodePort - Определенный порт на всех K8S Воркер Нодах
+ ExternalName - DNS CNAME Record
+ LoadBalancer - Только для облачных кластеров (AWS, Google, Azure)

1) Из консоли:
```java
kubectl expose deployment name-deploy --type=NodePort --port 80  - создаем Сервис для управлением Деплоем name-deploy, типа NodePort на порту 80
        kubectl get services  -
        kubectl delete svc name
```
2) Манифест-файл deployment-service.yaml:
```java
apiVersion : apps/v1
kind: Deployment
metadata:
    name: my-web-deployment
    labels:
        app : my-k8s-application
spec:
    replicas: 2              //
    selector:                //
        matchLabels:         //
            project: shell   //Сервис коннектится сюда, а не выше (Девелопмент)
    template:                //
        metadata:
            labels:
                project: shell
    spec:
        containers:
            - name : shell-web
              image: httpd:latest
              ports:
                - containerPort: 80
...                     
apiVersion: v1
kind: Service
metadata:
    name: my-service
    labels:
        env   : prod
        owner : Me
spec:
    type: LoadBalancer
    selector:                         //чем управляет сервис, не Деплоем, а какими Подами
        project: shell                //выбираем Поды с этим лейблом
    ports:
        - name      : app-listener
          protocol  : TCP
          port      : 80              //Порт для LoadBalancer
          targetPort: 80              //Порт для Pod
```

Запускаем Манифест-файл:
```java
kubectl apply -f ./deployment-service.yaml
```

[к оглавлению](#Docker)

## Kubernetes Ingress Controller

Отдельное приложение, которое позволяет управлять Сервисами внутри Кластера, с помощью одного публичного Сервиса, к которому есть публичный доступ. Вместо создания отдельных публичных Сервисов для каждого приложения внутри Кластера. Ingress Rules позволяют понять, к какому именно Ноду внутри Кластера обращается пользователь 

Было:
![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker1.png)

Стало:
![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker2.png)

Ingress Controller нужно подключать отдельно, например:
https://docs.google.com/spreadsheets/d/191WWNpjJ2za6-nbG4ZoUMXMpUK8KlCIosvQB0f-oq3k/edit#gid=907731238

Деплоим Ingress Controller к Кластеру
```java
kubectl apply -f https://projectcontour.io/quickstart/contour.yaml  - адрес зависит от реализации Ingress Controller
kubectl get services -n projectcontour  - посмотреть Ingress Controller   
```

Например, у нас в кластере 3 Deployments: web1, web2 и tomcat. Web1 и web2 скалированы по 2 копии. Т.е. у нас висит 3 Деплоя, с 5 Подами (2+2+1)
```java
kubectl create deployment web1 --image=dockerhub/web1
kubectl create deployment web2 --image=dockerhub/web1
kubectl create deployment tomcat --image=tomcat:8.5.38
kubectl get deploy
        
kubectl scale deployment web1 --replicas 2
kubectl scale deployment web2 --replicas 2
kubectl get pods
```

Создадим 3 Сервиса, для каждого Деплоя.
```java
kubectl expose deployment web1 --port=80
kubectl expose deployment web2 --port=80
kubectl expose deployment tomcat --port=8080
kubectl get services -o wide  - чуть больше информации
```
Нужно добавить Ingress Rules (ingress-rules.yaml):

```java
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
    name: ingtess-hosts

spec:
    rules:
        - host: www.web1.shell.net
            http:
                paths:
                  - backend:
                    serviceName: web1   //на какой Сервис отправлять клиента, если запрос приходит на этот Хост
                    servicePort: 80     //на какой Порт отправлять клиента, если запрос приходит на этот Хост
        
        - host: www.shell.net      //можно указать путь на страницу, вместо url
            http:
                paths: "/web2"
                - backend:
                    serviceName: web2
                    servicePort: 80

        - host: cat.shell.net
            http:
                paths:
                - backend:
                    serviceName: tomcat
                    servicePort: 8080
```
Запускаем Манифест-файл:
```java
kubectl apply -f ./ingress-rules.yaml
kubectl get ingress
kubectl describe ingress
```

[к оглавлению](#Docker)

## Helm Charts

https://helm.sh/

Если мы хотим что-то поменять в уже задеплоенном приложении, например образ, нужно отредактировать yaml файл, запустить команды на деплой Деплоймента и Сервиса. Если опять что-то поменяли, нужно все повторить.

Helm Charts позволяет сделать поля в yaml файле динамическими, менять их из консоли или использовать несколько yaml файлов с разными параметрами(prod, test). Качаем файл с офф сайта в директорию, и запускаем из строки команду helm

![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker3.png)

```java
helm create Chart-Auto  /автогенерация, много лишнего для начала
```
Например возьму yaml файл с описанием Deployments и сделаю динамическими поля с названием проекта (берется из команды, при осздании Деплоимента) "image" и "replicaCount". Значения по-умолчанию хранятся в values.yaml

deploymets.yaml :
```java
apiVersion : apps/v1
kind: Deployment
metadata:
    name: {{ .Release.Name }}-deployment    //.Release.Name берется из имени Деплоимента, когда создаем
    labels:
        app : {{ .Release.Name }}-application
spec:
    replicas: {{ .Values.replicaCount }}              
    selector:                
        matchLabels:         
            project: {{ .Release.Name }}   
    template:                
        metadata:
            labels:
                project: {{ .Release.Name }}
    spec:
        containers:
            - name : {{ .Release.Name }}-web
              image: {{ .Values.container.image }}
              ports:
                - containerPort: 80
```
values.yaml :
```java
container:
    image: dockerhub/web1

replicaCount: 2
```

Запускаем Helm :
```java
helm install name1 path-to-Chart/  
helm list

helm install name2 path-to-Chart/ --set container.image=dockerhub/web2 --set replicaCount=3  - перезапускаем меняя параметры
helm upgrade name2 path-to-Chart/ --set replicaCount=4  - обновляем
helm install name3 path-to-Chart/ -f prod_values.yaml  - берем не дефолтные значения, а из другого yaml файла

helm package path-to-Chart/  - упаковывает в файл path-to-Chart-0.1.0.tgz
helm install name4 -f path-to-Chart-0.1.0.tgz  - запускаем из файла
```

Можно брать готовые Helm Charts из сети
```java
helm search repo  - ищет в конкретном репозитории
helm repo add bitnami https://charts.bitname.com/bitnami  - добавляем репо bitnami
helm install name123 bitnami/apache
        
helm search hub apache  - ищет в общем хабе
```
[к оглавлению](#Docker)

