bannerbannerbanner
Облачная экосистема

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

REPOSITORY TAGIMAGE ID CREATED SIZE

ubuntu latest 93fd78260bd1 4 weeks ago 86.2MB

alpine latest 196d12cf6ab1 3 months ago 4.41MB

Немного изменив код существенно сэкономили на размере образа:

image: alpine:3.5

command:

– /bin/bash

– -c

– |

cd /app

apk –update add wget && rm -rf /var/cache/apk/*

tar -xf small_business_encode.tar.gz

rm -f small_business_encode.tar.gz

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

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

chmod -R 0777 .

volumeMounts:

Существуют также минималистичные образа с предустановленными пакетами, такие как APIne с git: axeclbr/git и golang:1-alpine.

Способы обеспечения устойчивости к сбоям

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

vagrant@ubuntu:~$ sudo docker pull ubuntu > /dev/null

vagrant@ubuntu:~$ sudo docker run -d ubuntu sleep 60

0bd80651c6f97167b27f4e8df675780a14bd9e0a5c3f8e5e8340a98fc351bc64

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES

0bd80651c6f9 ubuntu "sleep 60" 15 seconds ago Up 12 seconds distracted_kalam

vagrant@ubuntu:~$ sleep 60

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES

vagrant@ubuntu:~$ sudo docker ps -a | grep ubuntu

0bd80651c6f9 ubuntu "sleep 60" 4 minutes ago Exited (0) 3 minutes ago distracted_kalam

В случае с бэкапам – это норма, а в случае с приложениями, которые не должны завершаться – нет. Типичный прием – веб-сервер. Самое простое в таком случае заново рестартовать его:

vagrant@ubuntu:~$ sudo docker run -d –restart=always ubuntu sleep 10

c3bc2d2e37a68636080898417f5b7468adc73a022588ae073bdb3a5bba270165

vagrant@ubuntu:~$ sleep 30

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS

c3bc2d2e37a6 ubuntu sleep 10" 46 seconds ago Up 1 second

Мы видим, что, когда контейнер падает – он рестартует. Как результат – у нас всегда приложение в двух состояниях – поднимается или поднято. Если веб-сервер падает от какой-то редкой ошибки – это норма, но, скорее всего, ошибка в обработке запросов, и он будет падать на каждом таком запросе, а в мониторинге мы увидим поднятый контейнер. Такой веб-сервер лучше мёртвый, чем наполовину живой. Но, при этом нормальный веб-сервер может не стартануть из-за редких ошибок, например, из-за отсутствия подключения к базе данных из-за нестабильности сети. В таком случает, приложение должно уметь обрабатывать ошибки и завершаться. А в случае падения из-за ошибок кода – не перезапускать, чтобы увидеть неработоспособность и отправить на починку разработчикам. В случает же плавающей ошибки можно попробовать несколько раз:

vagrant@ubuntu:~$ sudo docker run -d –restart=on-failure:3 ubuntu sleep 10

056c4fc6986a13936e5270585e0dc1782a9246f01d6243dd247cb03b7789de1c

vagrant@ubuntu:~$ sleep 10

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

c3bc2d2e37a6 ubuntu "sleep 10" 9 minutes ago Up 2 seconds keen_sinoussi

vagrant@ubuntu:~$ sleep 10

vagrant@ubuntu:~$ sleep 10

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

c3bc2d2e37a6 ubuntu "sleep 10" 10 minutes ago Up 9 seconds keen_sinoussi

vagrant@ubuntu:~$ sleep 10

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

c3bc2d2e37a6 ubuntu "sleep 10" 10 minutes ago Up 2 seconds keen_sinoussi

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

docker run –rm -d \

–-name=elasticsearch \

–-health-cmd="curl –silent –fail localhost:9200/_cluster/health || exit 1" \

–-health-interval=5s \

–-health-retries=12 \

–-health-timeout=20s \

{image}

Для демонстрации мы возьмём команду создания файла. Если приложение не достигло в отведённый лимит времени (поставим пок 0) рабочего состояния (к примеру, создания файла), то помечается как на рабочее, но до этого делается заданное кол-во проверок:

vagrant@ubuntu:~$ sudo docker run \

–d –name healt \

–-health-timeout=0s \

–-health-interval=5s \

–-health-retries=3 \

–-health-cmd="ls /halth" \

ubuntu bash -c 'sleep 1000'

c0041a8d973e74fe8c96a81b6f48f96756002485c74e51a1bd4b3bc9be0d9ec5

vagrant@ubuntu:~$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

c0041a8d973e ubuntu "bash -c 'sleep 1000'" 4 seconds ago Up 3 seconds (health: starting) healt

vagrant@ubuntu:~$ sleep 20

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

c0041a8d973e ubuntu "bash -c 'sleep 1000'" 38 seconds ago Up 37 seconds (unhealthy) healt

vagrant@ubuntu:~$ sudo docker rm -f healt

healt

Если же хотя бы одна из проверок сработала – то контейнер помечается как работоспособный (healthy) сразу:

vagrant@ubuntu:~$ sudo docker run \

–d –name healt \

–-health-timeout=0s \

–-health-interval=5s \

–-health-retries=3 \

–-health-cmd="ls /halth" \

ubuntu bash -c 'touch /halth && sleep 1000'

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

160820d11933 ubuntu "bash -c 'touch /hal…" 4 seconds ago Up 2 seconds (health: starting) healt

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

160820d11933 ubuntu "bash -c 'touch /hal…" 6 seconds ago Up 5 seconds (healthy) healt

vagrant@ubuntu:~$ sudo docker rm -f healt

healt

При этом проверки повторяются всё время с заданным интервалом:

vagrant@ubuntu:~$ sudo docker run \

–d –name healt \

–-health-timeout=0s \

–-health-interval=5s \

–-health-retries=3 \

–-health-cmd="ls /halth" \

ubuntu bash -c 'touch /halth && sleep 60 && rm -f /halth && sleep 60'

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

8ec3a4abf74b ubuntu "bash -c 'touch /hal…" 7 seconds ago Up 5 seconds (health: starting) healt

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

8ec3a4abf74b ubuntu "bash -c 'touch /hal…" 24 seconds ago Up 22 seconds (healthy) healt

vagrant@ubuntu:~$ sleep 60

vagrant@ubuntu:~$ sudo docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

8ec3a4abf74b ubuntu "bash -c 'touch /hal…" About a minute ago Up About a minute (unhealthy) healt

Kubernetes предоставляет (kubernetes.io/docs/tasks/configure-POD-container/configure-liveness-readiness-probes/) три инструмента, которые проверяют состояние контейнера из вне. Они имеют больше значение, так они служат не только для информирования, но и для управления жизненным циклом приложения, накатом и откатом обновления. Их неправильная настройка может, и часто такое бывает, приводят к неработоспособности приложения. Так, к если liveness проба буте срабатывать до начала работы приложения – Kubernetes будет убивать контейнер, не дав ему подняться. Рассмотрим её более подробно. Проба liveness служит для определения работоспособности приложения и в случае, если приложение сломалось и не отвечает на пробу liveness – Kubernetes перезагружает контейнер. В качестве примера возьмём shell-пробу, из-за простоты демонстрации работы, но на практике её следует использовать только в крайних случаях, например, если контейнер запускается не как долгоживущий сервер, а как JOB, выполняющий свою работу и завершающий своё существование, достигнув результата. Для проверок серверов лучше использовать HTTP-пробы, которые уже имеют встроенный выделенный проксировщик и не требуют наличия в контейнере curl и не зависящие от внешних настроек kube-proxy. При использовании баз данных нужно использовать TCP-пробу, так как HTTP—протокол они обычно не поддерживают. Создадим долгоживущий контейнер в www.katacoda.com/courses/kubernetes/playground:

controlplane $ cat << EOF > liveness.yaml

apiVersion: v1

kind: Pod

metadata:

name: liveness

spec:

containers:

– name: healtcheck

image: alpine:3.5

args:

– /bin/sh

– -c

– touch /tmp/healthy; sleep 10; rm -rf /tmp/healthy; sleep 60

livenessProbe:

exec:

command:

– cat

– /tmp/healthy

initialDelaySeconds: 15

periodSeconds: 5

EOF

controlplane $ kubectl create -f liveness.yaml

pod/liveness created

controlplane $ kubectl get pods

NAME READY STATUS RESTARTS AGE

 

liveness 1/1 Running 2 2m11s

controlplane $ kubectl describe pod/liveness | tail -n 10

Type Reason Age From Message

–– – – – –

Normal Scheduled 2m37s default-scheduler Successfully assigned default/liveness to node01

Normal Pulling 2m33s kubelet, node01 Pulling image "alpine:3.5"

Normal Pulled 2m30s kubelet, node01 Successfully pulled image "alpine:3.5"

Normal Created 33s (x3 over 2m30s) kubelet, node01 Created container healtcheck

Normal Started 33s (x3 over 2m30s) kubelet, node01 Started container healtcheck

Normal Pulled 33s (x2 over 93s) kubelet, node01 Container image "alpine:3.5" already present on machine

Warning Unhealthy 3s (x9 over 2m13s) kubelet, node01 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

Normal Killing 3s (x3 over 2m3s) kubelet, node01 Container healtcheck failed liveness probe, will be restarted

Мы видим, что контейнер постоянно перезапускается.

controlplane $ cat << EOF > liveness.yaml

apiVersion: v1

kind: Pod

metadata:

name: liveness

spec:

containers:

– name: healtcheck

image: alpine:3.5

args:

– /bin/sh

– -c

– touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 60

livenessProbe:

exec:

command:

– cat

– /tmp/healthy

initialDelaySeconds: 15

periodSeconds: 5

EOF

controlplane $ kubectl create -f liveness.yaml

pod/liveness created

controlplane $ kubectl get pods

NAME READY STATUS RESTARTS AGE

liveness 1/1 Running 2 2m53s

controlplane $ kubectl describe pod/liveness | tail -n 15

SecretName: default-token-9v5mb

Optional: false

QoS Class: BestEffort

Node-Selectors: < none>

Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s

node.kubernetes.io/unreachable:NoExecute for 300s

Events:

Type Reason Age From Message

–– – – – –

Normal Scheduled 3m44s default-scheduler Successfully assigned default/liveness to node01

Normal Pulled 68s (x3 over 3m35s) kubelet, node01 Container image "alpine:3.5" already present on machine

Normal Created 68s (x3 over 3m35s) kubelet, node01 Created container healtcheck

Normal Started 68s (x3 over 3m34s) kubelet, node01 Started container healtcheck

Warning Unhealthy 23s (x9 over 3m3s) kubelet, node01 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

Normal Killing 23s (x3 over 2m53s) kubelet, node01 Container healtcheck failed liveness probe, will be restarted

Также мы видим и на событиях кластера, что когда cat /tmp/health терпит неудачу – контейнере пересоздаётся:

controlplane $ kubectl get events

controlplane $ kubectl get events | grep pod/liveness

13m Normal Scheduled pod/liveness Successfully assigned default/liveness to node01

13m Normal Pulling pod/liveness Pulling image "alpine:3.5"

13m Normal Pulled pod/liveness Successfully pulled image "alpine:3.5"

10m Normal Created pod/liveness Created container healtcheck

10m Normal Started pod/liveness Started container healtcheck

10m Warning Unhealthy pod/liveness Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

10m Normal Killing pod/liveness Container healtcheck failed liveness probe, will be restarted

10m Normal Pulled pod/liveness Container image "alpine:3.5" already present on machine

8m32s Normal Scheduled pod/liveness Successfully assigned default/liveness to node01

4m41s Normal Pulled pod/liveness Container image "alpine:3.5" already present on machine

4m41s Normal Created pod/liveness Created container healtcheck

4m41s Normal Started pod/liveness Started container healtcheck

2m51s Warning Unhealthy pod/liveness Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

5m11s Normal Killing pod/liveness Container healtcheck failed liveness probe, will be restarted

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

controlplane $ cat << EOF > readiness.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: readiness

spec:

replicas: 2

selector:

matchLabels:

app: readiness

template:

metadata:

labels:

app: readiness

spec:

containers:

– name: readiness

image: python

args:

– /bin/sh

– -c

– sleep 15 && (hostname > health) && python -m http.server 9000

readinessProbe:

exec:

command:

– cat

– /tmp/healthy

initialDelaySeconds: 1

periodSeconds: 5

EOF

controlplane $ kubectl create -f readiness.yaml

deployment.apps/readiness created

controlplane $ kubectl get pods

NAME READY STATUS RESTARTS AGE

readiness-fd8d996dd-cfsdb 0/1 ContainerCreating 0 7s

readiness-fd8d996dd-sj8pl 0/1 ContainerCreating 0 7s

controlplane $ kubectl get pods

NAME READY STATUS RESTARTS AGE

readiness-fd8d996dd-cfsdb 0/1 Running 0 6m29s

readiness-fd8d996dd-sj8pl 0/1 Running 0 6m29s

controlplane $ kubectl exec -it readiness-fd8d996dd-cfsdb – curl localhost:9000/health

readiness-fd8d996dd-cfsdb

Наши контейнера отлично работают. Добавим в них трафик:

controlplane $ kubectl expose deploy readiness \

–-type=LoadBalancer \

–-name=readiness \

–-port=9000 \

–-target-port=9000

service/readiness exposed

controlplane $ kubectl get svc readiness

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

readiness LoadBalancer 10.98.36.51 < pending> 9000:32355/TCP 98s

controlplane $ curl localhost:9000

controlplane $ for i in {1..5}; do curl $IP:9000/health; done

1

2

3

4

5

Каждый контейнер имеет задержку. Проверим, что будет, если один из контейнеров перезапустить – будет ли на него перенаправляться трафик:

controlplane $ kubectl get pods

NAME READY STATUS RESTARTS AGE

readiness-5dd64c6c79-9vq62 0/1 CrashLoopBackOff 6 15m

readiness-5dd64c6c79-sblvl 0/1 CrashLoopBackOff 6 15m

kubectl exec -it .... -c .... bash -c "rm -f healt"

controlplane $ for i in {1..5}; do echo $i; done

1

2

3

4

5

controlplane $ kubectl delete deploy readiness

deployment.apps "readiness" deleted

Рассмотрим ситуацию, когда контейнер становится временно недоступен для работы:

(hostname > health) && (python -m http.server 9000 &) && sleep 60 && rm health && sleep 60 && (hostname > health) sleep 6000

/bin/sh -c sleep 60 && (python -m http.server 9000 &) && PID=$! && sleep 60 && kill -9 $PID

По умолчанию, в состояние Running контейнер переходит по завершения выполнения скриптов в Dockerfile и запуску скрипта, заданного в инструкции CMD, если он переопределён в конфигурации в разделе Command. Но, на практике, если у нас база данных, ей нужно ещё подняться (прочитать данные и перенести их оперативную память и другие действия), а это может занять значительно время, при этом она не будет отвечать на соединения, и другие приложения, хотя и прочитают в состоянии готовность принимать соединения не смогут этого сделать. Также, контейнер переходи в состояние Feils, когда падает главный процесс в контейнере. В случае с базой данных, она может бесконечно пытаться выполнить неправильный запрос и не сможет отвечать на приходящие запросы, при этом контейнер не буде перезапущен, так как формально демон (сервер) базы данных не упал. Для этих случаев и придуманы два идентификатора: readinessProbe и livenessProbe, проверяющих по кастомному скрипту или HTTP запросу переход контейнера в рабочее состояние или его неисправность.

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

apiVersion: v1

kind: Pod

metadata:

labels:

test: healtcheck

name: healtcheck

spec:

containers:

– name: healtcheck

image: alpine:3.5

args:

– /bin/sh

– -c

– sleep 12; touch /tmp/healthy; sleep 10; rm -rf /tmp/healthy; sleep 60

readinessProbe:

exec:

command:

– cat

– /tmp/healthy

initialDelaySeconds: 5

periodSeconds: 5

livenessProbe:

exec:

command:

– cat

– /tmp/healthy

initialDelaySeconds: 15

periodSeconds: 5

Контейнер стартует через 3 секунды и через 5 секунд начинается проверка на готовность каждые 5 секунд. На второй проверке (на 15 секунде жизни) проверка на готовность cat /tmp/healthy увенчается успехом. В это время начинает осуществляться проверка на работоспособность livenessProbe и на второй проверке (на 25 секунде) заканчивается ошибкой, после чего контейнер признаётся не рабочим и пересоздается.

esschtolts@cloudshell:~/bitrix (essch)$ kubectl create -f health_check.yaml && sleep 4 && kubectl get

pods && sleep 10 && kubectl get pods && sleep 10 && kubectl get pods

pod "liveness-exec" created

NAME READY STATUS RESTARTS AGE

liveness-exec 0/1 Running 0 5s

NAME READY STATUS RESTARTS AGE

liveness-exec 0/1 Running 0 15s

NAME READY STATUS RESTARTS AGE

liveness-exec 1/1 Running 0 26s

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

NAME READY STATUS RESTARTS AGE

liveness-exec 0/1 Running 0 53s

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

NAME READY STATUS RESTARTS AGE

liveness-exec 0/1 Running 0 1m

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

NAME READY STATUS RESTARTS AGE

liveness-exec 1/1 Running 1 1m

Kubernetes предоставляет ещё и startup, переделяющий момент, когда можно включить readiness и liveness пробы в работу. Это полезно в том случае, если, к примеру, мы скачиваем приложение. Рассмотрим более подробно. Для эксперимента возьмём www.katacoda.com/courses/Kubernetes/playground и Python. Существует TCP, EXEC и HTTP, но лучше использовать HTTP, так как EXEC порождает процессы и может оставлять их в виде "зомби процессов". К тому же, если сервер обеспечивает взаимодействие по HTTP, то именно по нему и нужно проверять (https://www.katacoda.com/courses/kubernetes/playground):

controlplane $ kubectl version –short

Client Version: v1.18.0

Server Version: v1.18.0

cat << EOF > job.yaml

apiVersion: v1

kind: Pod

metadata:

name: healt

spec:

containers:

– name: python

image: python

command: ['sh', '-c', 'sleep 60 && (echo "work" > health) && sleep 60 && python -m http.server 9000']

readinessProbe:

httpGet:

path: /health

port: 9000

initialDelaySeconds: 3

periodSeconds: 3

livenessProbe:

httpGet:

path: /health

port: 9000

initialDelaySeconds: 3

periodSeconds: 3

startupProbe:

exec:

command:

– cat

– /health

initialDelaySeconds: 3

periodSeconds: 3

restartPolicy: OnFailure

EOF

controlplane $ kubectl create -f job.yaml

pod/healt

controlplane $ kubectl get pods # ещё не загружен

NAME READY STATUS RESTARTS AGE

healt 0/1 Running 0 11s

controlplane $ sleep 30 && kubectl get pods # ещё не загружен, но образ уже стянут

NAME READY STATUS RESTARTS AGE

healt 0/1 Running 0 51s

controlplane $ sleep 60 && kubectl get pods

NAME READY STATUS RESTARTS AGE

healt 0/1 Running 1 116s

controlplane $ kubectl delete -f job.yaml

pod "healt" deleted

Самодиагностика микро сервисного приложения

Рассмотрим работу probe на примере микро сервисного приложения bookinfo, входящего в состав Istio как пример: https://github.com/istio/istio/tree/master/samples/bookinfo. Демонстрация будет в www.katacoda.com/courses/istio/deploy-istio-on-kubernetes. После разворачивания будет доступны

Управление инфраструктурой

Хотя, и у Kubernetes есть свой графический интерфейс – UI-дашборд, но кроме мониторинга и простейших действий не предоставляет. Больше возможностей даёт OpenShift, предоставляя совмещения графического и текстового создания. Полноценный продукт с сформированной экосистемой Google в Kubernetes не предоставляет, но предоставляет облачное решение – Google Cloud Platform. Однако, существуют и сторонние решения, такие как Open Shift и Rancher, позволяющие пользоваться полноценно через графический интерфейс на своих мощностях. При желании, конечно, можно синхронизироваться с облаком.

 

Каждый продукт, зачастую, не совместим с друг другом по API, единственным известным исключением является Mail. Cloud, в котором заявляется поддержка Open Shift. Но, существует стороннее решение, реализующее подход "инфраструктура как код" и поддерживающее API большинства известных экосистем – Terraform. Он, так же как Kubernetes, применяет концепция инфраструктура как код, но только не к контейнеризации, а к виртуальным машинам (серверам, сетям, дискам). Принцип Инфраструктура как код подразумевает наличии декларативной конфигурации – то есть описания результата без явного указания самих действий. При активации конфигурация (в Kubernetes это kubectl apply -f name_config .yml, а в Hashicorp Terraform – terraform apply) системы приводится в соответствие с конфигурационными файлами, при изменении конфигурации или инфраструктуры, инфраструктура, в конфликтующих частях с её декларацией приводится в соответствие, при этом сама система решает, как этого достичь, причём поведение может быть различным, например, при изменении метаинформации в POD – она будет изменена, а при изменении образа – POD будет удалён и создан уже новым. Если, до этого мы создавали серверную инфраструктуру для контейнеров в императивном виде с помощью команды gcloud публичного облака Google Cloud Platform (GCP), то теперь рассмотрим, как создать аналогичное с помощью конфигурация в декларативном описании паттерна инфраструктура как код с помощью универсального инструмента Terraform, поддерживающего облако GCP.

Terraform не возник на пустом месте, а стал продолжением длинной истории появления программных продуктов конфигурирования и управления серверной инфраструктуры, перечислю в прядке появления и перехода:

** CFN;

** Pupet;

** Chef;

** Ansible;

** Облачные AWS API, Kubernetes API;

* IasC: Terraform не зависит от типа инфраструктуры (поддерживает более 120 провайдеров, среди которых не только облака), в отличии от ведровых аналогов, поддерживающих только себя: CloudFormation для Amazon WEB Service, Azure Resource Manager для Microsoft Azure, Google Cloud Deployment Manager от Google Cloud Engine.

CloudFormation создан Amazon и предназначен для негоже, также полностью интегрирован в CI/CD его инфраструктуры размещением на AWS S3, что усложняет версионирование через GIT. Мы рассмотрим платформой независимый Terraform: синтаксис базовой функциональности един, а специфичная подключается через сущности Провайдеры (https://www.terraform.io/docs/providers/index. html). Terraform – один бинарный файл, поддерживает огромное количество провайдеров, и, конечно же, таких как AWS и GCE. Terraform, как и большинство продуктов от Hashicorp написаны на Go и представляют из себя один бинарный исполняемый файл, не требует установки, достаточно просто скачать его в папку Linux:

(agile-aleph-203917)$ wget https://releases.hashicorp.com/terraform/0.11.13/terraform_0.11.13_linux_amd64.zip

(agile-aleph-203917)$ unzip terraform_0.11.13_linux_amd64.zip -d .

(agile-aleph-203917)$ rm -f terraform_0.11.13_linux_amd64.zip

(agile-aleph-203917)$ ./terraform version

Terraform v0.11.13

Он поддерживает разбиение на модули, которые можно написать самому или использовать готовые (https://registry.terraform.io/browse?offset=27&provider=google). Для оркестрации и поддержки изменений в зависимостях можно воспользоваться Terragrunt (https://davidbegin.github.io/terragrunt/), например:

terragrant = {

terraform {

source = "terraform-aws-modules/…"

}

dependencies {

path = ["..network"]

}

}

name = "…"

ami = "…"

instance_type = "t3.large"

Единая семантика для разных провайдеров (AWS, GCE, Яндекс. Облако и многих других) конфигураций, что позволяет создать трансцендентную инфраструктуру, например, постоянные нагруженные сервисы расположить для экономии на собственных мощностях, а переменно нагружены (например, в период акций) в публичных облаках. Благодаря тому, что управление декларативно и может быть описана файлами (IaC, инфраструктура как код), создание инфраструктуры можно добавить в pipeline CI/CD (разработка, тестирование, доставка, всё автоматически и с контролем версий). Без CI/CD поддерживается блокировка файла конфигурации для предотвращения конкурентного его редактирования при совместной работе. инфраструктура создаётся не скриптом, а приводится в соответствие с конфигурацией, которая декларативна и не может содержать логики, хотя, можно и внедрить в неё BASH- скрипты и использовать Conditions (термальный оператор) для разных окружений.

Terraform будет читать все файлы в текущем каталоге с расширением .tf в Hachiсort Configuraiton Language (HCL) формате или .tf. json в JSON формате. Часто, вместо одного файл его разделяют на несколько, как минимум на два: первый содержащий конфигурацию, второй – приватные данные, вынесенные в переменными.

Для демонстрации возможностей Terraform мы создадим репозиторий GitHub из-за его простоты авторизации и API. Сперва получим токен, сгенерирована в WEB-интерфейсе: SettingsDeveloper sittings -> Personal access token –> Generate new token и установив разрешения. Ничего не будем создавать, просто проверим подключение:

(agile-aleph-203917)$ ls *.tf

main.tf variables.tf

$ cat variables.tf

variable "github_token" {

default = "630bc9696d0b2f4ce164b1cabb118eaaa1909838"

}

$ cat main.tf

provider "github" {

token = "${var.github_token}"

}

(agile-aleph-203917)$ ./terraform init

(agile-aleph-203917)$ ./terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Теперь, создадим управляющий аккаунт Settings –> Organizations –> New organization –> Create organization.. Используя: API Terraform по создания репозитория www.terraform.io/docs/providers/github/r/repository. html добавим в конфиг описание репозитория:

(agile-aleph-203917)$ cat main.tf

provider "github" {

token = "${var.github_token}"

}

resource "github_repository" "terraform_repo" {

name = "terraform-repo"

description = "my terraform repo"

auto_init = true

}

Теперь осталось применить, посмотреть с планом создания репозитория, согласиться с ним:

(agile-aleph-203917)$ ./terraform apply

provider.github.organization

The GitHub organization name to manage.

Enter a value: essch2

An execution plan has been generated and is shown below.

Resource actions are indicated with the following symbols:

+ create

Terraform will perform the following actions:

+ github_repository.terraform_repo

id:<computed>

allow_merge_commit: "true"

allow_rebase_merge: "true"

allow_squash_merge: "true"

archived: "false"

auto_init: "true"

default_branch: <computed>

description: "my terraform repo"

etag: <computed>

full_name: <computed>

git_clone_url: <computed>

html _url: <computed>

http_clone_url: <computed>

name: "terraform-repo"

ssh_clone_url: <computed>

svn_url: <computed>

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?

Terraform will perform the actions described above.

Only 'yes' will be accepted to approve.

Enter a value: yes

github_repository.terraform_repo: Creating…

allow_merge_commit: "" => "true"

allow_rebase_merge: "" => "true"

allow_squash_merge: "" => "true"

archived: "" => "false"

auto_init: "" => "true"

default_branch: "" => "<computed>"

description: "" => "my terraform repo"

etag: "" => "<computed>"

full_name: "" => "<computed>"

git_clone_url: "" => "<computed>"

html_url: "" => "<computed>"

http_clone_url: "" => "<computed>"

name: "" => "terraform-repo"

ssh_clone_url: "" => "<computed>"

svn_url: "" => "<computed>"

github_repository.terraform_repo: Creation complete after 4s (ID: terraform-repo)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed

Теперь можно наблюдать пустой репозиторий terraform-repo в WEB-интерфейсе. При повторном применении репозиторий не будет создан, так как Terraform применяет только изменения, который не было:

(agile-aleph-203917)$ ./terraform apply

provider.github.organization

The GITHub organization name to manage.

Enter a value: essch2

github_repository.terraform_repo: Refreshing state… (ID: terraform-repo)

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

А вот если я изменю название, то Terraform постарается применить изменения название через удаление и создание нового с актуальным названием. Важно заметить, что любые данные, которые мы бы запушили бы в этот репозиторий после смены названия были бы удалены. Для проверки, как будет производиться обновления можно предварительно спросить перечень производимых действий командой ./Terraform plane. И так, приступим:

(agile-aleph-203917)$ cat main.tf

provider "github" {

token = "${var.github_token}"

}

resource "github_repository" "terraform_repo" {

name = "terraform-repo2"

description = "my terraform repo"

auto_init = true

}

(agile-aleph-203917)$ ./terraform plan

provider.github.organization

The GITHub organization name to manage.

Enter a value: essch

Refreshing Terraform state in-memory prior to plan…

The refreshed state will be used to calculate this plan, but will not be

persisted to local or remote state storage.

github_repository.terraform_repo: Refreshing state… (ID: terraform-repo)

-–

An execution plan has been generated and is shown below.

Resource actions are indicated with the following symbols:

+ create

Terraform will perform the following actions:

+ github_repository.terraform_repo

id:<computed>

allow_merge_commit: "true"

allow_rebase_merge: "true"

allow_squash_merge: "true"

archived: "false"

auto_init: "true"

default_branch: <computed>

description: "my terraform repo"

etag: <computed>

full_name: <computed>

git_clone_url: <computed>

html_url: <computed>

http_clone_url: <computed>

name: "terraform-repo2"

ssh_clone_url: <computed>

svn_url: <computed>

"terraform apply" is subsequently run.

esschtolts@cloudshell:~/terraform (agile-aleph-203917)$ ./terraform apply

provider.github.organization

The GITHub organization name to manage.

Enter a value: essch2

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35 
Рейтинг@Mail.ru