Облачная экосистема - Евгений Сергеевич Штольц 15 стр.


esschtolts@cloudshell:~/bitrix (essch)$ kubectl get namespace

NAME STATUS AGE

default Active 5h

kube-public Active 5h

kube-system Active


esschtolts@cloudshell:~/bitrix (essch)$ kubectl get pods namespace=kube-system

NAME READY STATUS RESTARTS AGE

event-exporter-v0.2.3-85644fcdf-tdt7h 2/2 Running 0 5h

fluentd-gcp-scaler-697b966945-bkqrm 1/1 Running 0 5h

fluentd-gcp-v3.1.0-xgtw9 2/2 Running 0 5h

heapster-v1.6.0-beta.1-5649d6ddc6-p549d 3/3 Running 0 5h

kube-dns-548976df6c-8lvp6 4/4 Running 0 5h

kube-dns-548976df6c-mcctq 4/4 Running 0 5h

kube-dns-autoscaler-67c97c87fb-zzl9w 1/1 Running 0 5h

kube-proxy-gke-bitrix-default-pool-38fa77e9-0wdx 1/1 Running 0 5h

kube-proxy-gke-bitrix-default-pool-38fa77e9-wvrf 1/1 Running 0 5h

l7-default-backend-5bc54cfb57-6qk4l 1/1 Running 0 5h

metrics-server-v0.2.1-fd596d746-g452c 2/2 Running 0 5h


esschtolts@cloudshell:~/bitrix (essch)$ kubectl get pods namespace=default

NAMEREADY STATUS RESTARTS AGE

Nginxlamp-b5dcb7546-g8j5r 1/1 Running 0 4h

Создадим область видимости:

esschtolts@cloudshell:~/bitrix (essch)$ cat namespace.yaml

apiVersion: v1

kind: Namespace

metadata:

name: development

labels:

name: development


esschtolts@cloudshell:~ (essch)$ kubectl create -f namespace.yaml

namespace "development" created


esschtolts@cloudshell:~/bitrix (essch)$ kubectl get namespace show-labels

NAME STATUS AGE LABELS

default Active 5h none>

development Active 16m name=development

kube-public Active 5h none>

kube-system Active 5h none>

Суть работы с областью видимости в том, что для конкретных кластеров мы задаём область видимость и можем выполнять команды указывая её, при этом они будут распространяться только на них. При этом, кроме ключей в командах, таких как kubectl get pods области видимости не фигурирую, поэтому конфигурационные файлы контроллеров (Deployment, DaemonSet и других) и сервисов (LoadBalancer, NodePort и других) не фигурируют, позволяя беспроблемно переносить их между областью видимости, что особенно актуально для pipeline разработки: сервер разработчика, тестовый сервер и продакшн сервер. Области видимости прописываются в файле контекстов кластеров $HOME/ .kube/config с помощью команды kubectl config view. Так, у меня в записи контекста нашего кластера не фигурирует запись об области видимости (по умолчанию default ):

 context:

cluster: gke_essch_europe-north1-a_bitrix

user: gke_essch_europe-north1-a_bitrix

name: gke_essch_europe-north1-a_bitrix

Посмотреть можно примерно подобным образом:

esschtolts@cloudshell:~/bitrix (essch)$ kubectl config view -o jsonpath='{.contexts[4]}'

{gke_essch_europe-north1-a_bitrix {gke_essch_europe-north1-a_bitrix gke_essch_europe-north1-a_bitrix []}}

Создадим новый контекст для данного пользователя и кластера:

esschtolts@cloudshell:~ (essch)$ kubectl config set-context dev \

> namespace=development \

> cluster=gke_essch_europe-north1-a_bitrix \

> user=gke_essch_europe-north1-a_bitrix

Context "dev" modified.


esschtolts@cloudshell:~/bitrix (essch)$ kubectl config set-context dev \

> namespace=development \

> cluster=gke_essch_europe-north1-a_bitrix \

> user=gke_essch_europe-north1-a_bitrix

Context "dev" modified.

В результате был добавлен:

 context:

cluster: gke_essch_europe-north1-a_bitrix

namespace: development

user: gke_essch_europe-north1-a_bitrix

name: dev

Теперь осталось переключиться на него:

esschtolts@cloudshell:~ (essch)$ kubectl config use-context dev

Switched to context "dev".


esschtolts@cloudshell:~ (essch)$ kubectl config current-context

dev


esschtolts@cloudshell:~ (essch)$ kubectl get pods

No resources found.


esschtolts@cloudshell:~ (essch)$ kubectl get pods namespace=default

NAMEREADY STATUS RESTARTS AGE

Nginxlamp-b5dcb7546-krkm2 1/1 Running 0 10h

Можно было добавить в существующий контекст пространство имён:

esschtolts@cloudshell:~/bitrix (essch)$ kubectl config set-context $(kubectl config current-context) namespace=development

Context "gke_essch_europe-north1-a_bitrix" modified.

Теперь создадим кластер в новой области видимости dev(она теперь по умолчанию и её можно не указывать namespace=dev) и удалим из области видимости по умолчанию default (она теперь не по умолчанию для нашего кластера и её нужно указывать namespace =default):

esschtolts@cloudshell:~ (essch)$ cd bitrix/


esschtolts@cloudshell:~/bitrix (essch)$ kubectl create -f deploymnet.yaml -f loadbalancer.yaml

deployment.apps "Nginxlamp" created

service "frontend" created


esschtolts@cloudshell:~/bitrix (essch)$ kubectl delete -f deploymnet.yaml -f loadbalancer.yaml namespace=default

deployment.apps "Nginxlamp" deleted

service "frontend" deleted


esschtolts@cloudshell:~/bitrix (essch)$ kubectl get pods

NAMEREADY STATUS RESTARTS AGE

Nginxlamp-b5dcb7546-8sl2f 1/1 Running 0 1m

Теперь посмотрим внешний IP-адресс и откроем страницу:

esschtolts@cloudshell:~/bitrix (essch)$ curl $(kubectl get -f loadbalancer.yaml -o json

| jq -r .status.loadBalancer.ingress[0].ip) 2>/dev/null | grep '< h2 >'

< h2>Welcome to github.com/mattrayner/docker-lamp" target="_blank">Docker-Lamp a.k.a mattrayner/lamp< /h2>

Кастомизация

Теперь нам нужно изменить стандартное решение под наши нужды, а именно добавить конфиги и наше приложение. Для простоты мы пометим (изменим стандартный) в корень нашего приложения файл .htaccess, сведя к простому помещения нашего приложения в папку /app. Первое, что напрашивается сделать, это создать POD и потом скопировать с хоста в контейнер наше приложение (я взял Bitrix):

Хотя это решение и работает, оно имеет ряд существенных недостатков. Первое, что то, что нам нужно дожидаться из вне, постоянным опросом POD, когда он поднимет контейнер и мы в него скопируем приложение и не должен этого делать, если контейнер не поднялся, а также обрабатывать ситуацию когда он сломает наш POD, внешние сервисы, могут опираться на статус POD, хоты сам POD будет ещё не готов, пока не будет выполнен скрипт. Вторым недостатком является то, что у нас появляется какой то внешний скрипт, который нужно логически не отделим от POD, но при этом его нужно вручную запускать из вне, где хранить и где-то должна быть инструкция по его использованию. И напоследок, этих POD у нас может быть множество. На первый взгляд, логичным решением поместить код в Dockerfile:

Хотя это решение и работает, оно имеет ряд существенных недостатков. Первое, что то, что нам нужно дожидаться из вне, постоянным опросом POD, когда он поднимет контейнер и мы в него скопируем приложение и не должен этого делать, если контейнер не поднялся, а также обрабатывать ситуацию когда он сломает наш POD, внешние сервисы, могут опираться на статус POD, хоты сам POD будет ещё не готов, пока не будет выполнен скрипт. Вторым недостатком является то, что у нас появляется какой то внешний скрипт, который нужно логически не отделим от POD, но при этом его нужно вручную запускать из вне, где хранить и где-то должна быть инструкция по его использованию. И напоследок, этих POD у нас может быть множество. На первый взгляд, логичным решением поместить код в Dockerfile:

esschtolts@cloudshell:~/bitrix (essch)$ cat Dockerfile

FROM mattrayner/lamp:latest-1604-php5

MAINTAINER ESSch ESSchtolts@yandex.ru>

RUN cd /app/ && ( \

wget https://www.1c-bitrix.ru/download/small_business_encode.tar.gz \

&& tar -xf small_business_encode.tar.gz \

&& sed -i '5i php_value short_open_tag 1' .htaccess \

&& chmod -R 0777 . \

&& sed -i 's/#php_value display_errors 1/php_value display_errors 1/' .htaccess \

&& sed -i '5i php_value opcache.revalidate_freq 0' .htaccess \

&& sed -i 's/#php_flag default_charset UTF-8/php_flag default_charset UTF-8/' .htaccess \

) && cd ..;

EXPOSE 80 3306

CMD ["/run.sh"]


esschtolts@cloudshell:~/bitrix (essch)$ docker build -t essch/app:0.12 . | grep Successfully

Successfully built f76e656dac53

Successfully tagged essch/app:0.12


esschtolts@cloudshell:~/bitrix (essch)$ docker image push essch/app | grep digest

0.12: digest: sha256:75c92396afacefdd5a3fb2024634a4c06e584e2a1674a866fa72f8430b19ff69 size: 11309


esschtolts@cloudshell:~/bitrix (essch)$ cat deploymnet.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: Nginxlamp

namespace: development

spec:

selector:

matchLabels:

app: lamp

replicas: 1

template:

metadata:

labels:

app: lamp

spec:

containers:

 name: lamp

image: essch/app:0.12

ports:

 containerPort: 80


esschtolts@cloudshell:~/bitrix (essch)$ IMAGE=essch/app:0.12 kubectl create -f deploymnet.yaml

deployment.apps "Nginxlamp" created


esschtolts@cloudshell:~/bitrix (essch)$ kubectl get pods -l app=lamp

NAME READY STATUS RESTARTS AGE

Nginxlamp-55f8cd8dbc-mk9nk 1/1 Running 0 5m


esschtolts@cloudshell:~/bitrix (essch)$ kubectl exec Nginxlamp-55f8cd8dbc-mk9nk ls /app/

index.php

Это происходит потому, что разработчик образов, что правильно и написано в его документации, ожидал, что образ будет примонтирован к хосту и в скрипте запускаемого в сам конце удаляется папка app. Также в таком подходе мы столкнёмся с проблемой постоянных обновлений образов, конфигом (мы не сможем задать номер образа переменной, так как он будет исполняться на нодах кластера) и обновлений контейнеров, также мы не сможем обновлять папку, так как при пересоздании контейнера изменения будут возвращены в изначальное состояние.

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

esschtolts@cloudshell:~/bitrix (essch)$ cat deploymnet.yaml

kind: Deployment

metadata:

name: Nginxlamp

namespace: development

spec:

selector:

matchLabels:

app: lamp

replicas: 1

template:

metadata:

labels:

app: lamp

spec:

initContainers:

 name: init

image: ubuntu

command:

 /bin/bash

 -c

 |

cd /app

apt-get update && apt-get install -y wget

wget https://www.1c-bitrix.ru/download/small_business_encode.tar.gz

tar -xf small_business_encode.tar.gz

sed -i '5i php_value short_open_tag 1' .htaccess

chmod -R 0777 .

sed -i 's/#php_value display_errors 1/php_value display_errors 1/' .htaccess

sed -i '5i php_value opcache.revalidate_freq 0' .htaccess

sed -i 's/#php_flag default_charset UTF-8/php_flag default_charset UTF-8/' .htaccess

volumeMounts:

 name: app

mountPath: /app

containers:

 name: lamp

image: essch/app:0.12

ports:

 containerPort: 80

volumeMounts:

 name: app

mountPath: /app

volumes:

 name: app

emptyDir: {}

Посмотреть события во время создания POD можно командой watch kubectl get events, а логи kubectl logs {ID_CONTAINER} -c init или более универсально:

Назад Дальше