


# Практическое задание 1

В данном задании вы будете создавать простейшие объекты в
кластере minikube, результатом успешного выполнения будет открывшийся интерфейс
веб-страницы из запущенного контейнера в POD-е в вашем браузере.

<details>
<summary><strong>Minikube: namespace, service, deplyment</strong></summary>
<p>

### Полезные ссылки для выполнения задания
- https://kubernetes.io/docs/tasks/administer-cluster/namespaces-walkthrough/

0. 
brew install kubectl
brew install --cask -f docker
open /Applications/Docker.app

1. Установить minikube по инструкции
minikube start --driver=docker

2. Создать новый неймспейс в кластере minikube с названием `homework`
kubectl create namespace homework
kubectl get namespaces

3. Переключить контекст на созданный неймспейс
kubectl config set-context --current --namespace=homework
# Проверить текущий неймспейс
kubectl config view --minify

4. Создать императивным способом deployment
```
kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
```

5. Создать императивным способом service 
```
kubectl expose deployment web --name=my-app-service --type=NodePort --port=8080
# Посмотреть список сервисов
kubectl get services -n homework
```

6. При помощи minikube посмотреть адрес сервиса для доступа к приложению.
```
minikube service -n homework my-app-service --url

```

В случае корректного выполнения мы увидим страницу в текстом:
```
Hello, world!
Version: 1.0.0
Hostname: web-6bf786c76b-f66r7
```

7. Создать ingress для доступа к приложению извне.

Необходимо локально создать файл.
ingress.yaml
```
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: homework
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - host: hello-world.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 8080
```
8. Применить данный файл при помощи утилиты kubectl 
```
kubectl apply -f ingress.yaml
# Проверить ваш ingress
kubectl get ingress -n homework
```

9. Посмотреть внешний адрес кластера
```
minikube ip
```

10. Включить ingress в minikube

minikube addons enable ingress
# Проверить
minikube addons list | grep ingress


10. Добавить в /etc/hosts(WSL or Linux) или в C:\Windows\System32\drivers\etc\hosts (Windows) новую запись
```
IP-адрес-кластера hello-world.info
```

Пример:
```
192.168.49.2 hello-world.info
```

11. Запустить туннель minikube

sudo minikube tunnel

12. Зайти браузером по адресу http://hello-world.info  и увидеть то же сообщение, что мы видели, когда заходили через адрес service.

13. В драйвере docker есть баг: https://github.com/kubernetes/minikube/issues/12899. Поэтому мы пробуем другой способ проверить наш ingress:

curl --resolve "hello-world.info:80:127.0.0.1" -i http://hello-world.info

HTTP/1.1 200 OK
Date: Fri, 23 Jan 2026 01:16:53 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 60
Connection: keep-alive

Hello, world!
Version: 1.0.0
Hostname: web-769bbccc48-fqn8s

# Практическое задание с повышеной сложностью 

14. После того, как создали все объекты кластера и убедились, что всё работает, удаляем, что создали.
```
kubectl delete deployment web
kubectl delete svc my-app-service
kubectl delete ingress example-ingress
```

15. Создать 3 новых файла в формате YAML для deployment, service и ingress и декларативным способом создать эти объекты в кластере
```
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
```

16. Зайти браузером по адресу http://hello-world.info  и увидеть то же сообщение, что мы видели, когда заходили через адрес service.

</p>
</details>





# Практическое задание 2

В данном задании вы соберёте простейший docker image, при
разворачивании которого вы получите доступ к нему и интерфейс веб-страницы
будет каждый раз разным. Это нужно будет для того, чтобы убедится в том, что
клиентский траффик проходит для каждой из реплики POD-а. Также вы научитесь
работать с ресурсами контейнеров и secret для хранения логина и пароля с
доступом до реджистри dockerhub.


<details>
<summary><strong>Resourse Quote, docker, trafic</strong></summary>
<p>

### Полезные ссылки для выполнения задания

* https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/

* https://kubernetes.io/docs/concepts/services-networking/_print/ 

* https://kubernetes.io/docs/concepts/services-networking/service/#proxy-mode-userspace 


1. Собрать docker image на базе Dockerfile и файла app.py 

2. После сборки разместить образ на своем акаунте Dockerhub

3. Создать secret объект с credentials от dockerhub и именем pull-secret

4. Создать YAML файл deployment.yaml 

- в качестве образа использовать путь до докерхаба с вашим образом
- использовать 3 реплики приложения
- использовать cpu request = 100m / cpu limits = 500m для контейнера 
- использовать memory request = 100M / Memory limits = 500M для контейнера
- использовать secret с именем  pull-secret для скачивания имаджа с сайта dockerhub
- название микросервиса my-app, label=my-app

5. Задеплоить deployment в кластер в неймспейс homework
```
kubectl apply -f deployment.yaml
```

6. Создать service для доступа к этому POD, декларативно или императивно

7. Создать Ingress для доступа к этому POD, декларативно или императивно, с адресом my-color-app.info и именем ingress-my-app

8. Посмотреть внешний адрес кластера
```
minikube ip
```

9. Добавить в /etc/hosts(WSL or Linux) или в C:\Windows\System32\drivers\etc\hosts (Windows) новую запись
```
IP-адрес-кластера my-color-app.info
```

Пример:
```
192.168.49.2 my-color-app.info
```
10. При помощи браузера зайти по адресу приложения 

# Практическое задание с повышеной сложностью

Разобраться почему каждый раз при обновлении страницы с адресом приложения меняется цвет.


</p>
</details>

<details>
<summary><strong>Пример docker файла и python</strong></summary>
<p>

Код файла app.py:

```python
from flask import Flask
from flask import render_template
import socket
import random
import os
import argparse

app = Flask(__name__)

color_codes = {
    "red": "#e74c3c",
    "green": "#16a085",
    "blue": "#2980b9",
    "blue2": "#30336b",
    "pink": "#be2edd",
    "darkblue": "#130f40"
}

SUPPORTED_COLORS = ",".join(color_codes.keys())

# Get color from Environment variable
COLOR_FROM_ENV = os.environ.get('APP_COLOR')
# Generate a random color
COLOR = random.choice(["red", "green", "blue", "blue2", "darkblue", "pink"])


@app.route("/")
def main():
    # return 'Hello'
    return render_template('hello.html', name=socket.gethostname(), color=color_codes[COLOR])


if __name__ == "__main__":

    print(" This is a sample web application that displays a colored background. \n"
          " A color can be specified in two ways. \n"
          "\n"
          " 1. As a command line argument with --color as the argument. Accepts one of " + SUPPORTED_COLORS + " \n"
          " 2. As an Environment variable APP_COLOR. Accepts one of " + SUPPORTED_COLORS + " \n"
          " 3. If none of the above then a random color is picked from the above list. \n"
          " Note: Command line argument precedes over environment variable.\n"
          "\n"
          "")

    # Check for Command Line Parameters for color
    parser = argparse.ArgumentParser()
    parser.add_argument('--color', required=False)
    args = parser.parse_args()

    if args.color:
        print("Color from command line argument =" + args.color)
        COLOR = args.color
        if COLOR_FROM_ENV:
            print("A color was set through environment variable -" + COLOR_FROM_ENV + ". However, color from command line argument takes precendence.")
    elif COLOR_FROM_ENV:
        print("No Command line argument. Color from environment variable =" + COLOR_FROM_ENV)
        COLOR = COLOR_FROM_ENV
    else:
        print("No command line argument or environment variable. Picking a Random Color =" + COLOR)

    # Check if input color is a supported one
    if COLOR not in color_codes:
        print("Color not supported. Received '" + COLOR + "' expected one of " + SUPPORTED_COLORS)
        exit(1)

    # Run Flask Application
    app.run(host="0.0.0.0", port=8080)
```

Докерфайл:

```docker
FROM python:3.6-alpine
RUN pip install flask
COPY . /opt/
EXPOSE 8080
WORKDIR /opt
ENTRYPOINT ["python", "app.py"]
```

</p>
</details>


# Практическое задание 3

На данном уроке вы сможете научится работать с таким
объектом K8s и OS как HPA (Horizontal Pod AutoScaling) или иначе "контроллер
горизонтального скалирования". Помимо этого, вы научитесь экспортировать
манифест из ETCD K8s себе локально в случае, если, например, вы
потеряли оригинальный манифест ваших объектов кластера. Дополнительно вы
сможете поработать с limux capabilities, который вас научит на уровне
изоляции ограничивать доступ к возможностям операционной системы.

<details>
<summary><strong>ETCD K8s и Horizontal Pod AutoScaling</strong></summary>
<p>

### Полезные ссылки для выполнения задания

* https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/ 
https://man7.org/linux/man-pages/man7/capabilities.7.html 


Для выполнения данного задания необходимо выполнить всё из задания номер 2.

1. Подключить HPA к созданному deployment
- min replicas = 1
- max replicas = 6
- cpu utilization = 40%
- name = hpa-my-app

2. При помощи утилиты curl и циклов сделать нагрузку на сайт, чтобы появились новые реплики приложения и отработал HPA

Пример(WSL или Linux):
```
for x in {1..10000}; do time curl http://1.2.3.4:8080/ ; done ;
```

 > Алтернатива можно просто много раз обновлять страницу вручную при помощи клавиши F5.


Сами реплики мы можем увидеть, несколько раз подряд введя команду 
```
kubectl get pods
```


3. Имитируем проблему и затем решаем

- удаляем все существующие файлы YAML на локальном ПК
- у нас есть только объекты в кластере
- нам их надо как-то сохранить локально, например для сохраности в git репозитории
- при помощи команды импортируем содержимое объектов из кластера себе локально

```
kubectl get secret pull-secret -o yaml -> secret.yaml
kubectl get deployment my-app -o yaml -> deployment.yaml
kubectl get hpa hpa-my-app -o yaml -> hpa-my-app.yaml
kubectl get ingress ingress-my-app -o yaml -> ingress.yaml
```
4. При помощи текстового редактора открыть каждый из файлов и удалить лишние строчки.

5. Удалить все объекты из неймспейса в кластере (чтобы проверить, что мы всё сделали корректно)
```
kubectl delete all --all
```
6. При помощи четырёх файлов восстановить deployment, service, ingress, hpa объекты в кластере
```
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml
kubectl apply -f hpa-my-app.yaml
kubectl apply -f ingress.yaml
```
7. В локальном файле deployment.yaml добавить security context 
- запретить CAP_NET_BIND_SERVICE

8. Применить в кластере новый deployment, посмотреть в логи приложения, убедится, что security context сработал и Nginx не запускается.

9. В локальном файле deployment.yaml добавить security context 
- разрешить CAP_NET_BIND_SERVICE, всё остальное запретить

10. Применить в кластере новый deployment, посмотреть в логи приложения, убедится, что security context сработал и Nginx снова запускается.


</p>
</details>

------

[4.3.6.4 ServiceMash Theme](./4.3.6.4%20ServiceMash.md) | [Back To iTWiki Contents](https://github.com/eldaroid/iTWiki) | [4.4 CI/CD Theme Folder](//4%20Linkage/4.4%20CI-CD/)