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


gcloud container clusters create mycluster zone europe-north1-a

Через некоторое время, у меня это заняло две с половиной минуты, будут подняты 3 виртуальные машины, на них установлена операционная система и примонтирован диск. Проверим:

esschtolts@cloudshell:~ (essch)$ gcloud container clusters list filter=name=mycluster

NAME LOCATION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS

mycluster europe-north1-a 35.228.37.100 n1-standard-1 1.10.9-gke.5 3 RUNNING


esschtolts@cloudshell:~ (essch)$ gcloud compute instances list

NAME MACHINE_TYPE EXTERNAL_IP STATUS

gke-mycluster-default-pool-43710ef9-0168 n1-standard-1 35.228.73.217 RUNNING

gke-mycluster-default-pool-43710ef9-39ck n1-standard-1 35.228.75.47 RUNNING

gke-mycluster-default-pool-43710ef9-g76k n1-standard-1 35.228.117.209 RUNNING

Подключимся к виртуальной машине:

esschtolts@cloudshell:~ (essch)$ gcloud projects list

PROJECT_ID NAME PROJECT_NUMBER

agile-aleph-203917 My First Project 546748042692

essch app 283762935665


esschtolts@cloudshell:~ (essch)$ gcloud container clusters get-credentials mycluster \

-zone europe-north1-a \

-project essch

Fetching cluster endpoint and auth data.

kubeconfig entry generated for mycluster.

У нас пока нет кластера:

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

No resources found.

Создадим кластер:

esschtolts@cloudshell:~ (essch)$ kubectl run Nginx image=Nginx replicas=3

deployment.apps "Nginx" created

Проверим его состав:

esschtolts@cloudshell:~ (essch)$ kubectl get deployments selector=run=Nginx

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE

Nginx 3 3 3 3 14s


esschtolts@cloudshell:~ (essch)$ kubectl get pods selector=run=Nginx

NAME READY STATUS RESTARTS AGE

Nginx-65899c769f-9whdx 1/1 Running 0 43s

Nginx-65899c769f-szwtd 1/1 Running 0 43s

Nginx-65899c769f-zs6g5 1/1 Running 0 43s

Удостоверимся, что все три реплики кластера распределились равномерно на все три ноды:

esschtolts@cloudshell:~ (essch)$ kubectl describe pod Nginx-65899c769f-9whdx | grep Node:

Node: gke-mycluster-default-pool-43710ef9-g76k/10.166.0.5

esschtolts@cloudshell:~ (essch)$ kubectl describe pod Nginx-65899c769f-szwtd | grep Node:

Node: gke-mycluster-default-pool-43710ef9-39ck/10.166.0.4

esschtolts@cloudshell:~ (essch)$ kubectl describe pod Nginx-65899c769f-zs6g5 | grep Node:

Node: gke-mycluster-default-pool-43710ef9-g76k/10.166.0.5

Теперь поставим балансировщик нагрузки:

esschtolts@cloudshell:~ (essch)$ kubectl expose Deployment Nginx type="LoadBalancer" port=80

service "Nginx" exposed

Проверим, что он создался:

esschtolts@cloudshell:~ (essch)$ kubectl expose Deployment Nginx type="LoadBalancer" port=80

service "Nginx" exposed


esschtolts@cloudshell:~ (essch)$ kubectl get svc selector=run=Nginx

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

Nginx LoadBalancer 10.27.245.187 pending> 80:31621/TCP 11s


esschtolts@cloudshell:~ (essch)$ sleep 60;


esschtolts@cloudshell:~ (essch)$ kubectl get svc selector=run=Nginx

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

Nginx LoadBalancer 10.27.245.187 35.228.212.163 80:31621/TCP 1m

Проверим его работу:

esschtolts@cloudshell:~ (essch)$ curl 35.228.212.163:80 2>\dev\null | grep h1

< h1>Welcome to Nginx!< /h1>

Чтобы каждый раз не копировать полные названия сохраним их в переменных (подробнее о формате JSONpath в документации Go: https://golang.org/pkg/text/template/#pkg-overview):

esschtolts@cloudshell:~ (essch)$ pod1=$(kubectl get pods -o jsonpath={.items[0].metadata.name});

esschtolts@cloudshell:~ (essch)$ pod2=$(kubectl get pods -o jsonpath={.items[1].metadata.name});

esschtolts@cloudshell:~ (essch)$ pod3=$(kubectl get pods -o jsonpath={.items[2].metadata.name});

esschtolts@cloudshell:~ (essch)$ echo $pod1 $pod2 $pod3

Nginx-65899c769f-9whdx Nginx-65899c769f-szwtd Nginx-65899c769f-zs6g5

Изменим страницы в каждом POD, скопировав уникальные страницы в каждую реплику, и проверим балансировку, проверяя распределение запросов по POD:

esschtolts@cloudshell:~ (essch)$ echo 1 > test.html;

esschtolts@cloudshell:~ (essch)$ kubectl cp test.html ${pod1}:/usr/share/Nginx/html/index.html

esschtolts@cloudshell:~ (essch)$ echo 2 > test.html;

esschtolts@cloudshell:~ (essch)$ kubectl cp test.html ${pod2}:/usr/share/Nginx/html/index.html

esschtolts@cloudshell:~ (essch)$ echo 3 > test.html;

esschtolts@cloudshell:~ (essch)$ kubectl cp test.html ${pod3}:/usr/share/Nginx/html/index.html


esschtolts@cloudshell:~ (essch)$ curl 35.228.212.163:80 && curl 35.228.212.163:80 && curl 35.228.212.163:80

3

2

1


esschtolts@cloudshell:~ (essch)$ curl 35.228.212.163:80 && curl 35.228.212.163:80 && curl 35.228.212.163:80

3

1

1

Проверим отказоустойчивость кластера удалением одного POD:

esschtolts@cloudshell:~ (essch)$ kubectl delete pod ${pod1} && kubectl get pods && sleep 10 && kubectl get pods

pod "Nginx-65899c769f-9whdx" deleted

NAME READY STATUS RESTARTS AGE

Nginx-65899c769f-42rd5 0/1 ContainerCreating 0 1s

Nginx-65899c769f-9whdx 0/1 Terminating 0 54m

Nginx-65899c769f-szwtd 1/1 Running 0 54m

Nginx-65899c769f-zs6g5 1/1 Running 0 54m

NAME READY STATUS RESTARTS AGE

Nginx-65899c769f-42rd5 1/1 Running 0 12s

Nginx-65899c769f-szwtd 1/1 Running 0 55m

Nginx-65899c769f-zs6g5 1/1 Running 0 55m

Как мы видим, сразу после того, как POD стал недоступен (начался процесс его удаления) начала создаваться его замена. Вскоре, кластер полностью восстановит свою структуру. После того как мы закончили наши эксперименты, удалим виртуальные машины с кластером:

esschtolts@cloudshell:~ (essch)$ gcloud container clusters delete mycluster zone europe-north1-a;

The following clusters will be deleted.

 [mycluster] in [europe-north1-a]

Do you want to continue (Y/n)? Y

Deleting cluster myclusterdone.

Deleted [https://container.googleapis.com/v1/projects/essch/zones/europe-north1-a/clusters/mycluster].

esschtolts@cloudshell:~ (essch)$ gcloud container clusters list filter=name=mycluster

Итого. Мы создали кластер и создали балансировщик нагрузки всего двумя командами run и expose, теперь мы может заходить по IP-адресу балансировщика и наблюдать в браузере приветствующую страницу NGINX. При этом кластер само восстанавливается, для этого мы эмулировали отказ пода его удалением он был создан снова.

Воспроизводимость создания кластера

Давайте разберём ситуацию из предыдущей главы, в которой мы создали кластер, удалили реплику, а она восстановилась. Дело в том, что мы не управляем командами на прямую, а с помощью команд создаём описания необходимой конфигурации кластера и помещаем его в распределённое хранилище, после чего состояние нод поддерживаются в соответствии с этим описанием в распределённом хранилище. Мы также можем получить и отредактировать эти описании или же написать самим и потом загрузить их в распределённое хранилище. Это позволит нам сохранять состояние на диске в виде YAML файлов и восстанавливать его обратно, так часто поступают при переносе с рабочего сервера на тестовый. К тому же мы получаем возможность более гибко настраивать состояние, но, так как мы не ограниченны командами.

esschtolts@cloudshell:~ (essch)$ kubectl get deployment/Nginx output=yaml

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

annotations:

deployment.kubernetes.io/revision: "1"

creationTimestamp: 2018-12-16T10:23:26Z

generation: 1

labels:

run: Nginx

name: Nginx

namespace: default

resourceVersion: "1612985"

selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/Nginx

uid: 9fb3ad6a-011c-11e9-bfaa-42010aa60088

spec:

progressDeadlineSeconds: 600

replicas: 1

revisionHistoryLimit: 10

selector:

matchLabels:

run: Nginx

strategy:

rollingUpdate:

maxSurge: 1

maxUnavailable: 1

type: RollingUpdate

template:

metadata:

creationTimestamp: null

labels:

run: Nginx

spec:

containers:

 image: Nginx

imagePullPolicy: Always

name: Nginx

resources: {}

terminationMessagePath: /dev/termination-log

terminationMessagePolicy: File

dnsPolicy: ClusterFirst

restartPolicy: Always

schedulerName: default-scheduler

securityContext: {}

terminationGracePeriodSeconds: 30

status:

availableReplicas: 1

conditions:

 lastTransitionTime: 2018-12-16T10:23:26Z

lastUpdateTime: 2018-12-16T10:23:26Z

message: Deployment has minimum availability.

reason: MinimumReplicasAvailable

status: "True"

type: Available

 lastTransitionTime: 2018-12-16T10:23:26Z

lastUpdateTime: 2018-12-16T10:23:28Z

message: ReplicaSet "Nginx-64f497f8fd" has successfully progressed.

reason: NewReplicaSetAvailable

status: "True"

type: Progressing

observedGeneration: 1

readyReplicas: 1

replicas: 1

updatedReplicas: 1

Для нас это будет излишним, поэтому удалю ненужное, ведь когда создавали, мы указали лишь имя и образ, остальное было заполнено значениями по умолчанию:

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

labels:

run: Nginx

name: Nginx

spec:

selector:

matchLabels:

run: Nginx

template:

metadata:

labels:

run: Nginx

spec:

containers:

 image: Nginx

name: Nginx

Также можно создать шаблон:

gcloud services enable compute.googleapis.com project=${PROJECT}

gcloud beta compute instance-templates create-with-container ${TEMPLATE} \

-machine-type=custom-1-4096 \

-image-family=cos-stable \

-image-project=cos-cloud \

-container-image=gcr.io/kuar-demo/kuard-amd64:1 \

-container-restart-policy=always \

-preemptible \

-region=${REGION} \

-project=${PROJECT}

gcloud compute instance-groups managed create ${TEMPLATE} \

-base-instance-name=${TEMPLATE} \

-template=${TEMPLATE} \

-size=${CLONES} \

-region=${REGION} \

-project=${PROJECT}

Высокая доступность сервиса

Чтобы обеспечить высокую доступность нужно в случае падения приложения перенаправлять трафик на запасной. Также, часто важно, чтобы нагрузка была распределена равномерно, так как приложение в единичном экземпляре не способно обрабатывать весь трафик. Для этого создаётся кластер, для примера возьмём более сложный образ, чтобы разобрать большее количество нюансов:

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

apiVersion: apps/v1

kind: Deployment

metadata:

name: Nginxlamp

spec:

selector:

matchLabels:

app: lamp

replicas: 1

template:

metadata:

labels:

app: lamp

spec:

containers:

 name: lamp

image: mattrayner/lamp:latest-1604-php5

ports:

 containerPort: 80


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

apiVersion: v1

kind: Service

metadata:

name: frontend

spec:

type: LoadBalancer

ports:

 name: front

port: 80

targetPort: 80

selector:

app: lamp


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

NAME READY STATUS RESTARTS AGE

Nginxlamp-7fb6fdd47b-jttl8 2/2 Running 0 3m


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

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

frontend LoadBalancer 10.55.242.137 35.228.73.217 80:32701/TCP,8080:32568/TCP 4m

kubernetes ClusterIP 10.55.240.1 none> 443/TCP 48m

Теперь мы можем создать идентичные копии наших кластеров, например, для Production и Develop, но балансировка не будет работать должным образом. Балансировщик будет находить POD по метке, а этой метке соответствуют и POD в production, и в Developer кластере. Также не станет препятствием размещение кластеров в разных проектах. Хотя, для многих задач, это большой плюс, но не в случае кластера для разработчиков и продакшне. Для разграничения области видимости используются namespace. Мы незаметно их используем, когда мы выводим список POD без указания области видимости нам выводится область видимости по умолчанию default, но не выводятся POD из системной области видимости:

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

Назад Дальше