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

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

Полная версия

Выберем зону:

[50] europe-north1-a

Did not print [12] options.

Too many options [62]. Enter "list" at prompt to print choices fully.

Please enter numeric choice or text value (must exactly match list

item):

Please enter a value between 1 and 62, or a value present in the list: 50

essh@kubernetes-master:~/node-cluster$ PROJECT_I="node-cluster-243923"

essh@kubernetes-master:~/node-cluster$ echo $PROJECT_I

node-cluster-243923

essh@kubernetes-master:~/node-cluster$ export GOOGLE_APPLICATION_CREDENTIALS=$HOME/node-cluster/kubernetes_key.json

essh@kubernetes-master:~/node-cluster$ sudo docker-machine create –driver google –google-project $PROJECT_ID vm01

sudo export GOOGLE_APPLICATION_CREDENTIALS=$HOME/node-cluster/kubernetes_key.json docker-machine create –driver google –google-project $PROJECT_ID vm01

// https://docs.docker.com/machine/drivers/gce/

// https://github.com/docker/machine/issues/4722

essh@kubernetes-master:~/node-cluster$ gcloud config list

[compute]

region = europe-north1

zone = europe-north1-a

[core]

account = esschtolts@gmail.com

disable_usage_reporting = False

project = node-cluster-243923

Your active configuration is: [default]

Добавим копирование файла и выполнение скрипта:

essh@kubernetes-master:~/node-cluster$ cat main.tf

provider "google" {

credentials = "${file("kubernetes_key.json")}"

project = "node-cluster-243923"

region = "europe-north1"

}

resource "google_compute_address" "static-ip-address" {

name = "static-ip-address"

}

resource "google_compute_instance" "cluster" {

name = "cluster"

zone = "europe-north1-a"

machine_type = "f1-micro"

boot_disk {

initialize_params {

image = "debian-cloud/debian-9"

}

}

metadata = {

ssh-keys = "essh:${file("./node-cluster.pub")}"

}

network_interface {

network = "default"

access_config {

nat_ip = "${google_compute_address.static-ip-address.address}"

}

}

}

resource "null_resource" "cluster" {

triggers = {

cluster_instance_ids = "${join(",", google_compute_instance.cluster.*.id)}"

}

connection {

host = "${google_compute_address.static-ip-address.address}"

type = "ssh"

user = "essh"

timeout = "2m"

private_key = "${file("~/node-cluster/node-cluster")}"

# agent = "false"

}

provisioner "file" {

source = "client.js"

destination = "~/client.js"

}

provisioner "remote-exec" {

inline = [

"cd ~ && echo 1 > test.txt"

]

}

essh@kubernetes-master:~/node-cluster$ sudo ./terraform apply

google_compute_address.static-ip-address: Creating…

google_compute_address.static-ip-address: Creation complete after 5s [id=node-cluster-243923/europe-north1/static-ip-address]

google_compute_instance.cluster: Creating…

google_compute_instance.cluster: Still creating… [10s elapsed]

google_compute_instance.cluster: Creation complete after 12s [id=cluster]

null_resource.cluster: Creating…

null_resource.cluster: Provisioning with 'file'…

null_resource.cluster: Provisioning with 'remote-exec'…

null_resource.cluster (remote-exec): Connecting to remote host via SSH…

null_resource.cluster (remote-exec): Host: 35.228.82.222

null_resource.cluster (remote-exec): User: essh

null_resource.cluster (remote-exec): Password: false

null_resource.cluster (remote-exec): Private key: true

null_resource.cluster (remote-exec): Certificate: false

null_resource.cluster (remote-exec): SSH Agent: false

null_resource.cluster (remote-exec): Checking Host Key: false

null_resource.cluster (remote-exec): Connected!

null_resource.cluster: Creation complete after 7s [id=816586071607403364]

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

esschtolts@cluster:~$ ls /home/essh/

client.js test.txt

[sudo] password for essh:

google_compute_address.static-ip-address: Refreshing state… [id=node-cluster-243923/europe-north1/static-ip-address]

google_compute_instance.cluster: Refreshing state… [id=cluster]

null_resource.cluster: Refreshing state… [id=816586071607403364]

Enter a value: yes

null_resource.cluster: Destroying… [id=816586071607403364]

null_resource.cluster: Destruction complete after 0s

google_compute_instance.cluster: Destroying… [id=cluster]

google_compute_instance.cluster: Still destroying… [id=cluster, 10s elapsed]

google_compute_instance.cluster: Still destroying… [id=cluster, 20s elapsed]

google_compute_instance.cluster: Destruction complete after 27s

google_compute_address.static-ip-address: Destroying… [id=node-cluster-243923/europe-north1/static-ip-address]

google_compute_address.static-ip-address: Destruction complete after 8s

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

Переходим к Kubernetes

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

essh@kubernetes-master:~/node-cluster/Kubernetes$ cat main.tf

provider "google" {

credentials = "${file("../kubernetes_key.json")}"

project = "node-cluster-243923"

region = "europe-north1"

}

resource "google_container_cluster" "node-ks" {

name = "node-ks"

location = "europe-north1-a"

initial_node_count = 3

}

essh@kubernetes-master:~/node-cluster/Kubernetes$ sudo ../terraform init

essh@kubernetes-master:~/node-cluster/Kubernetes$ sudo ../terraform apply

Кластер создался за 2:15, а после того, как я добавил europe-north1-a две дополнительные зоны europe-north1 -b, europe-north1-c и установили количество создаваемых инстансев в зоне в один, кластер создался за 3:13 секунде, потому что для более высокой доступности ноды были созданы в разных дата-центрах: europe-north1-a, europe-north1-b, europe-north1-c:

provider "google" {

credentials = "${file("../kubernetes_key.json")}"

project = "node-cluster-243923"

region = "europe-north1"

}

resource "google_container_cluster" "node-ks" {

name = "node-ks"

location = "europe-north1-a"

node_locations = ["europe-north1-b", "europe-north1-c"]

initial_node_count = 1

}

Теперь разделим наш кластер на два: управляющий кластер с Kubernetes и кластер для наших POD. Все кластера будет распределены по трём дата – центрам. Кластер для наших POD может авто масштабироваться под нагрузкой до 2 на каждой зоне (с трёх до шести в общем):

essh@kubernetes-master:~/node-cluster/Kubernetes$ cat main.tf

provider "google" {

credentials = "${file("../kubernetes_key.json")}"

project = "node-cluster-243923"

region = "europe-north1"

}

resource "google_container_cluster" "node-ks" {

name = "node-ks"

location = "europe-north1-a"

node_locations = ["europe-north1-b", "europe-north1-c"]

initial_node_count = 1

}

resource "google_container_node_pool" "node-ks-pool" {

name = "node-ks-pool"

cluster = "${google_container_cluster.node-ks.name}"

location = "europe-north1-a"

node_count = "1"

node_config {

machine_type = "n1-standard-1"

}

autoscaling {

min_node_count = 1

max_node_count = 2

}

}

Посмотрим, что получалось и поищем IP- адрес точки входа в кластер:

essh@kubernetes-master:~/node-cluster/Kubernetes$ gcloud container clusters list

NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS

node-ks europe-north1-a 1.12.8-gke.6 35.228.20.35 n1-standard-1 1.12.8-gke.6 6 RECONCILING

essh@kubernetes-master:~/node-cluster/Kubernetes$ gcloud container clusters describe node-ks | grep '^endpoint'

endpoint: 35.228.20.35

essh@kubernetes-master:~/node-cluster/Kubernetes$ ping 35.228.20.35 -c 2

PING 35.228.20.35 (35.228.20.35) 56(84) bytes of data.

64 bytes from 35.228.20.35: icmp_seq=1 ttl=59 time=8.33 ms

64 bytes from 35.228.20.35: icmp_seq=2 ttl=59 time=7.09 ms

-– 35.228.20.35 ping statistics –

2 packets transmitted, 2 received, 0% packet loss, time 1001ms

rtt min/avg/max/mdev = 7.094/7.714/8.334/0.620 ms

Добавив переменные, которые я выделил в отдельный файл только для наглядности, которые параметризуют наш конфиг под разное использование, мы сможем его использовать, например, для создания тестового и рабочего кластеров. Добавлять переменные можно как var.name_value, а вставлять в текст аналогично JS: ${var.name_value}, а также path.root.

essh@kubernetes-master:~/node-cluster/Kubernetes$ cat variables.tf

variable "region" {

default = "europe-north1"

}

variable "project_name" {

type = string

default = ""

}

variable "gce_key" {

default = "./kubernetes_key.json"

}

variable "node_count_zone" {

default = 1

}

Передать их можно через ключ -var, например: sudo ./terraform apply -var="project_name=node-cluster-243923".

essh@kubernetes-master:~/node-cluster/Kubernetes$ cp ../kubernetes_key.json .

essh@kubernetes-master:~/node-cluster/Kubernetes$ sudo ../terraform apply -var="project_name=node-cluster-243923"

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

essh@kubernetes-master:~/node-cluster/Kubernetes$ cd ..

 

essh@kubernetes-master:~/node-cluster$ cat main.tf

module "Kubernetes" {

source = "./Kubernetes"

project_name = "node-cluster-243923"

}

essh@kubernetes-master:~/node-cluster$ sudo ./terraform apply

Или же загрузить в публичный репозиторий:

essh@kubernetes-master:~/node-cluster/Kubernetes$ git init

Initialized empty GIT repository in /home/essh/node-cluster/Kubernetes/.git/

essh@kubernetes-master:~/node-cluster/Kubernetes$ echo "terraform.tfstate" >> .gitignore

essh@kubernetes-master:~/node-cluster/Kubernetes$ echo "terraform.tfstate.backup" >> .gitignore

essh@kubernetes-master:~/node-cluster/Kubernetes$ echo ".terraform/" >> .gitignore

essh@kubernetes-master:~/node-cluster/Kubernetes$ rm -f kubernetes_key.json

essh@kubernetes-master:~/node-cluster/Kubernetes$ git remote add origin https://github.com/ESSch/terraform-google-kubernetes.git

essh@kubernetes-master:~/node-cluster/Kubernetes$ git add .

essh@kubernetes-master:~/node-cluster/Kubernetes$ git commit -m 'create a k8s Terraform module'

[master (root-commit) 4f73c64] create a Kubernetes Terraform module

3 files changed, 48 insertions(+)

create mode 100644 .gitignore

create mode 100644 main.tf

create mode 100644 variables.tf

essh@kubernetes-master:~/node-cluster/Kubernetes$ git push -u origin master

essh@kubernetes-master:~/node-cluster/Kubernetes$ git tag -a v0.0.2 -m 'publish'

essh@kubernetes-master:~/node-cluster/Kubernetes$ git push origin v0.0.2

После публикации в реестре модулей https://registry.terraform.io/, выполнив требования, такие как наличие описания, мы можем его использовать:

essh@kubernetes-master:~/node-cluster$ cat main.tf

module "kubernetes" {

# source = "./Kubernetes"

source = "ESSch/kubernetes/google"

version = "0.0.2"

project_name = "node-cluster-243923"

}

essh@kubernetes-master:~/node-cluster$ sudo ./terraform init

essh@kubernetes-master:~/node-cluster$ sudo ./terraform apply

При очередном создании кластера у меня возникла ошибка ZONE_RESOURCE_POOL_EXHAUSTED "does not have enough resources available to fulfill the request. Try a different zone, or try again later", говорящая о том, что в данном регионе нет нужных серверов. Для меня это не проблема и мне не нужно править код модуля, ведь я параметризовал модуль регионом, и если я просто передам модулю в качестве параметра регионregion = "europe-west2", terraform после команды обновления и инициализации ./terrafrom init и команды применения ./terraform apply перенесёт мой кластер в указный регион. Ещё немного улучшим наш модуль тем, что вынесем провайдер (provider) из дочернего модуля Kubernetes в главный модуль (основной скрипт также является модулем). Вынеся в главный модуль, мы сможем использовать ещё один модуль, в противном случае провайдер в одном модуле будет конфликтовать с провайдером в другом. Наследование из главного модуля в дочерние и их прозрачность относится только к провайдерам. Остальные же данные для передачи из дочернего в родительский придётся использовать внешние переменные, а из родительского в дочерний – параметризовать родительский, но это позже, когда мы будем создавать ещё один модель. Также, вынесение провайдера в родительский модуль, будет полезно при создании следующего модуля, который мы создадим, так как он будет создавать элементы Kubernetes, которые не зависят от провайдера, и мы сможем тем самым отвязать провайдера Google от нашего модуля и его можно будет использовать с другими провайдерами, поддерживающим Kubernetes. Теперь нам не нужна передавать в переменной название проекта – он задан в провайдере. Для удобства разработки я буду пока использовать локальное подключение модуля. Я создал папку и файл нового модуля:

essh@kubernetes-master:~/node-cluster$ ls nodejs/

main.tf

essh@kubernetes-master:~/node-cluster$ cat main.tf

//module "kubernetes" {

// source = "ESSch/kubernetes/google"

// version = "0.0.2"

//

// project_name = "node-cluster-243923"

// region = "europe-west2"

//}

provider "google" {

credentials = "${file("./kubernetes_key.json")}"

project = "node-cluster-243923"

region = "europe-west2"

}

module "Kubernetes" {

source = "./Kubernetes"

project_name = "node-cluster-243923"

region = "europe-west2"

}

module "nodejs" {

source = "./nodejs"

}

essh@kubernetes-master:~/node-cluster$ sudo ./terraform init

essh@kubernetes-master:~/node-cluster$ sudo ./terraform apply

Теперь передадим данные из инфраструктурного модуля Kubernetes в модуль приложений:

essh@kubernetes-master:~/node-cluster$ cat Kubernetes/outputs.tf

output "endpoint" {

value = google_container_cluster.node-ks.endpoint

sensitive = true

}

output "name" {

value = google_container_cluster.node-ks.name

sensitive = true

}

output "cluster_ca_certificate" {

value = base64decode(google_container_cluster.node-ks.master_auth.0.cluster_ca_certificate)

}

essh@kubernetes-master:~/node-cluster$ cat main.tf

//module "kubernetes" {

// source = "ESSch/kubernetes/google"

// version = "0.0.2"

//

// project_name = "node-cluster-243923"

// region = "europe-west2"

//}

provider "google" {

credentials = file("./kubernetes_key.json")

project = "node-cluster-243923"

region = "europe-west2"

}

module "Kubernetes" {

source = "./Kubernetes"

project_name = "node-cluster-243923"

region = "europe-west2"

}

module "nodejs" {

source = "./nodejs"

endpoint = module.Kubernetes.endpoint

cluster_ca_certificate = module.Kubernetes.cluster_ca_certificate

}

essh@kubernetes-master:~/node-cluster$ cat nodejs/variable.tf

variable "endpoint" {}

variable "cluster_ca_certificate" {}

Для проверки балансировки трафика со всех нод запустим NGINX, заменив стандартную страницу на имя хоста. Заменим мы простым вызовом команды и возобновим работу сервера. Для запуска сервера посмотрим в Dockerfile его вызов: CMD ["Nginx", "-g", "daemon off;"], что равносильно вызову Nginx -g 'daemon off;' в командной строке . Как видим, в Dockerfile не используется BASH в качестве среды для запуска, а запускается непосредственно сам сервер, что позволяет в случае в случае падения процесса оболочка будет жить, не давая контейнеру упасть и создаться заново. Но для наших экспериментов вполне подойдёт BASH:

essh@kubernetes-master:~/node-cluster$ sudo docker run -it Nginx:1.17.0 which Nginx

/usr/sbin/Nginx

sudo docker run -it –rm -p 8333:80 Nginx:1.17.0 /bin/bash -c "echo \$HOSTNAME > /usr/share/Nginx/html/index2.html && /usr/sbin/Nginx -g 'daemon off;'"

Теперь создадим наши POD в трёх экземплярах с NGINX, которые Kubernetes будут стараться распределить на разные сервера по умолчанию. Также добавим сервис в качестве балансировщика:

essh@kubernetes-master:~/node-cluster$ cat nodejs/main.tf

terraform {

required_version = ">= 0.12.0"

}

data "google_client_config" "default" {}

provider "kubernetes" {

host = var.endpoint

token = data.google_client_config.default.access_token

cluster_ca_certificate = var.cluster_ca_certificate

load_config_file = false

}

essh@kubernetes-master:~/node-cluster$ cat nodejs/main.tf

resource "kubernetes_deployment" "nodejs" {

metadata {

name = "terraform-nodejs"

labels = {

app = "NodeJS"

}

}

spec {

replicas = 3

selector {

match_labels = {

app = "NodeJS"

}

}

template {

metadata {

labels = {

app = "NodeJS"

}

}

spec {

container {

image = "Nginx:1.17.0"

name = "node-js"

command = ["/bin/bash"]

args = ["-c", "echo $HOSTNAME > /usr/share/Nginx/html/index.html && /usr/sbin/Nginx -g 'daemon off;'"]

}

}

}

}

}

resource "kubernetes_service" "nodejs" {

metadata {

name = "terraform-nodejs"

}

spec {

selector = {

app = kubernetes_deployment.nodejs.metadata.0.labels.app

}

port {

port = 80

target_port = var.target_port

}

type = "LoadBalancer"

}

Проверим работу с помощью kubectl, для этого передадим секреты от gcloud к kubectl.

essh@kubernetes-master:~/node-cluster$ sudo ./terraform apply

essh@kubernetes-master:~/node-cluster$ gcloud container clusters get-credentials node-ks –region=europe-west2-a

Fetching cluster endpoint and auth data.

kubeconfig entry generated for node-ks.

essh@kubernetes-master:~/node-cluster$ kubectl get deployments -o wide

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR

terraform-nodejs 3 3 3 3 25m node-js Nginx:1.17.0 app=NodeJS

essh@kubernetes-master:~/node-cluster$ kubectl get pods -o wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE

terraform-nodejs-6bd565dc6c-8768b 1/1 Running 0 4m45s 10.4.3.15 gke-node-ks-node-ks-pool-07115c5b-bw15 none>

terraform-nodejs-6bd565dc6c-hr5vg 1/1 Running 0 4m42s 10.4.5.13 gke-node-ks-node-ks-pool-27e2e52c-9q5b none>

terraform-nodejs-6bd565dc6c-mm7lh 1/1 Running 0 4m43s 10.4.2.6 gke-node-ks-default-pool-2dc50760-757p none>

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ docker ps | grep node-js_terraform

152e3c0ed940 719cd2e3ed04

"/bin/bash -c 'ech…" 8 minutes ago Up 8 minutes

Kubernetes_node-js_terraform-nodejs-6bd565dc6c-8768b_default_7a87ae4a-9379-11e9-a78e-42010a9a0114_0

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ docker exec -it 152e3c0ed940 cat /usr/share/Nginx/html/index.html

terraform-nodejs-6bd565dc6c-8768b

esschtolts@gke-node-ks-node-ks-pool-27e2e52c-9q5b ~ $ docker exec -it c282135be446 cat /usr/share/Nginx/html/index.html

terraform-nodejs-6bd565dc6c-hr5vg

esschtolts@gke-node-ks-default-pool-2dc50760-757p ~ $ docker exec -it 8d1cf9ef44e6 cat /usr/share/Nginx/html/index.html

terraform-nodejs-6bd565dc6c-mm7lh

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.4.2.6

terraform-nodejs-6bd565dc6c-mm7lh

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.4.5.13

terraform-nodejs-6bd565dc6c-hr5vg

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.4.3.15

terraform-nodejs-6bd565dc6c-8768b

Балансировщик балансируют нагрузку между POD, которые отфильтрованы по соответствию их селекторов в метаинформации и Селектора, указанного в описании балансировщика в секции spec. Все ноды связанны в одну общую сеть, поэтому можно подключиться к любой ноде (я это сделал через SSH WEB- интерфейса GCP в разделе с виртуальными машинами Compute Engine). Обратиться можно как по IP-адресу в контейнере или хосте ноды, так и по хосту сервиса terraform-nodejs в контейнере curl terraform-NodeJS:80, который создаётся внутренним DNS по названию сервиса. Посмотреть внешний IP-адрес EXTERNAL -IP можно как с помощью kubectl у сервиса, так и с помощью веб-интерфейса: GCP –> Kubernetes Engine –> Сервисы:

essh@kubernetes-master:~/node-cluster$ kubectl get service

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

kubernetes ClusterIP 10.7.240.1 none> 443/TCP 6h58m

terraform-nodejs LoadBalancer 10.7.246.234 35.197.220.103 80:32085/TCP 5m27s

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.7.246.234

terraform-nodejs-6bd565dc6c-mm7lh

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.7.246.234

terraform-nodejs-6bd565dc6c-mm7lh

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.7.246.234

terraform-nodejs-6bd565dc6c-hr5vg

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.7.246.234

terraform-nodejs-6bd565dc6c-hr5vg

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.7.246.234

terraform-nodejs-6bd565dc6c-8768b

esschtolts@gke-node-ks-node-ks-pool-07115c5b-bw15 ~ $ curl 10.7.246.234

terraform-nodejs-6bd565dc6c-mm7lh

essh@kubernetes-master:~/node-cluster$ curl 35.197.220.103

terraform-nodejs-6bd565dc6c-mm7lh

essh@kubernetes-master:~/node-cluster$ curl 35.197.220.103

terraform-nodejs-6bd565dc6c-mm7lh

essh@kubernetes-master:~/node-cluster$ curl 35.197.220.103

terraform-nodejs-6bd565dc6c-8768b

 

essh@kubernetes-master:~/node-cluster$ curl 35.197.220.103

terraform-nodejs-6bd565dc6c-hr5vg

essh@kubernetes-master:~/node-cluster$ curl 35.197.220.103

terraform-nodejs-6bd565dc6c-8768b

essh@kubernetes-master:~/node-cluster$ curl 35.197.220.103

terraform-nodejs-6bd565dc6c-mm7lh

Теперь перейдем к внедрению сервера NodeJS:

essh@kubernetes-master:~/node-cluster$ sudo ./terraform destroy

essh@kubernetes-master:~/node-cluster$ sudo ./terraform apply

essh@kubernetes-master:~/node-cluster$ sudo docker run -it –rm node:12 which node

/usr/local/bin/node

sudo docker run -it –rm -p 8222:80 node:12 /bin/bash -c 'cd /usr/src/ && git clone https://github.com/fhinkel/nodejs-hello-world.git &&

/usr/local/bin/node /usr/src/nodejs-hello-world/index.js'

firefox http://localhost:8222

Заменим блок у нашего контейнера на:

container {

image = "node:12"

name = "node-js"

command = ["/bin/bash"]

args = [

"-c",

"cd /usr/src/ && git clone https://github.com/fhinkel/nodejs-hello-world.git && /usr/local/bin/node /usr/src/nodejs-hello-world/index.js"

]

}

Если закомментировать модуль Kubernetes, а в кэше он остаётся, остаётся убрать из кэша лишнее:

essh@kubernetes-master:~/node-cluster$ ./terraform apply

Error: Provider configuration not present

essh@kubernetes-master:~/node-cluster$ ./terraform state list

data.google_client_config.default

module.Kubernetes.google_container_cluster.node-ks

module.Kubernetes.google_container_node_pool.node-ks-pool

module.nodejs.kubernetes_deployment.nodejs

module.nodejs.kubernetes_service.nodejs

essh@kubernetes-master:~/node-cluster$ ./terraform state rm module.nodejs.kubernetes_deployment.nodejs

Removed module.nodejs.kubernetes_deployment.nodejs

Successfully removed 1 resource instance(s).

essh@kubernetes-master:~/node-cluster$ ./terraform state rm module.nodejs.kubernetes_service.nodejs

Removed module.nodejs.kubernetes_service.nodejs

Successfully removed 1 resource instance(s).

essh@kubernetes-master:~/node-cluster$ ./terraform apply

module.Kubernetes.google_container_cluster.node-ks: Refreshing state… [id=node-ks]

module.Kubernetes.google_container_node_pool.node-ks-pool: Refreshing state… [id=europe-west2-a/node-ks/node-ks-pool]

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

Надежность и автоматизация кластера на Terraform

В общих черта автоматизация рассмотрена в https://codelabs.developers.google.com/codelabs/cloud-builder-gke-continuous-deploy/index. html #0. Мы остановимся подробнее. Теперь, если мы запустим удаление ./terraform destroy и попытаемся воссоздать инфраструктуру целиком сначала, то у нас появятся ошибки. Ошибки получаются из-за того, что порядок создания сервисов не задан и terraform, по умолчанию, отравляет запросы к API в 10 параллельных потоков, хотя это можно изменить, указав при применении или удаление ключ -parallelism=1. В результате Terraform пытается создать Kubernetes сервисы (Deployment и service) на серверах (node-pull), которых пока ещё нет, та же ситуация и при создании сервиса, которому требуется , проксирующего ещё не созданный Deployment. Указывая Terraform запрашивать API в один поток ./terraform apply -parallelism=1 мы снижаем возможные ограничения со стороны провайдера на частоту обращений к API, но не решаем проблему отсутствия порядка создания сущностей. Мы не будем комментировать зависимые блоки и постепенно раскомментировать и запускать ./terraform apply, также не будем запускать по частям нашу систему, указывая конкретные блоки ./terraform apply -target=module.nodejs.kubernetes_deployment.nodejs. Мы укажем в коде сами зависимости на инициализации переменной, первая из которой уже определена как внешняя var.endpoint, а вторую мы создадим локально:

locals {

app = kubernetes_deployment.nodejs.metadata.0.labels.app

}

Теперь мы можем добавить зависимости в код depends_on = [var.endpoint] и depends_on = [kubernetes_deployment .nodejs].

Таже может появляться ошибка недоступности сервиса: Error: Get https://35.197.228.3/API/v1…: dial tcp 35.197.228.3:443: connect: connection refused, то скорее превышено время подключения, которое составляет по умолчанию 6 минут (3600 секунд), но тут можно просто попробовать еще раз.

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

essh@kubernetes-master:~/node-cluster$ cat app/server.js

const http = require('http');

const server = http.createServer(function(request, response) {

response.writeHead(200, {"Content-Type": "text/plain"});

response.end(`Nodejs_cluster is working! My host is ${process.env.HOSTNAME}`);

});

server.listen(80);

essh@kubernetes-master:~/node-cluster$ cat Dockerfile

FROM node:12

WORKDIR /usr/src/

ADD ./app /usr/src/

RUN npm install

EXPOSE 3000

ENTRYPOINT ["node", "server.js"]

essh@kubernetes-master:~/node-cluster$ sudo docker image build -t nodejs_cluster .

Sending build context to Docker daemon 257.4MB

Step 1/6 : FROM node:12

––> b074182f4154

Step 2/6 : WORKDIR /usr/src/

––> Using cache

––> 06666b54afba

Step 3/6 : ADD ./app /usr/src/

––> Using cache

––> 13fa01953b4a

Step 4/6 : RUN npm install

––> Using cache

––> dd074632659c

Step 5/6 : EXPOSE 3000

––> Using cache

––> ba3b7745b8e3

Step 6/6 : ENTRYPOINT ["node", "server.js"]

––> Using cache

––> a957fa7a1efa

Successfully built a957fa7a1efa

Successfully tagged nodejs_cluster:latest

essh@kubernetes-master:~/node-cluster$ sudo docker images | grep nodejs_cluster

nodejs_cluster latest a957fa7a1efa 26 minutes ago 906MB

Теперь положим наш образ в реестр GCP, а не Docker Hub, потому что мы получаем сразу приватный репозиторий с которому автоматом есть доступ у наших сервисов:

essh@kubernetes-master:~/node-cluster$ IMAGE_ID="nodejs_cluster"

essh@kubernetes-master:~/node-cluster$ sudo docker tag $IMAGE_ID:latest gcr.io/$PROJECT_ID/$IMAGE_ID:latest

essh@kubernetes-master:~/node-cluster$ sudo docker images | grep nodejs_cluster

nodejs_cluster latest a957fa7a1efa 26 minutes ago 906MB

gcr.io/node-cluster-243923/nodejs_cluster latest a957fa7a1efa 26 minutes ago 906MB

essh@kubernetes-master:~/node-cluster$ gcloud auth configure-docker

gcloud credential helpers already registered correctly.

essh@kubernetes-master:~/node-cluster$ docker push gcr.io/$PROJECT_ID/$IMAGE_ID:latest

The push refers to repository [gcr.io/node-cluster-243923/nodejs_cluster]

194f3d074f36: Pushed

b91e71cc9778: Pushed

640fdb25c9d7: Layer already exists

b0b300677afe: Layer already exists

5667af297e60: Layer already exists

84d0c4b192e8: Layer already exists

a637c551a0da: Layer already exists

2c8d31157b81: Layer already exists

7b76d801397d: Layer already exists

f32868cde90b: Layer already exists

0db06dff9d9a: Layer already exists

latest: digest: sha256:912938003a93c53b7c8f806cded3f9bffae7b5553b9350c75791ff7acd1dad0b size: 2629

essh@kubernetes-master:~/node-cluster$ gcloud container images list

NAME

gcr.io/node-cluster-243923/nodejs_cluster

Only listing images in gcr.io/node-cluster-243923. Use –repository to list images in other repositories.

Теперь мы можем посмотреть его в админке GCP: Container Registry –> Образы. Заменим код нашего контейнера на код с нашим образом. Если для продакшна нужно обязательно версионировать запускаемый образ во избежание автоматического их обновления при системных пересозданиях POD, например, при переносе POD с одной ноды на другую при выводе машины с нашей нодой на техническое обслуживание. Для разработки лучше использовать тег latest, который позволит при обновлении образа обновить сервис. При обновлении сервиса его нужно пересоздать, то есть удалить и заново создать, так как иначе terraform просто обновит параметры, а не пересоздаст контейнер с новым образом. Также, если обновим образ и пометим сервис как изменённый с помощью команды ./terraform taint ${NAME_SERVICE}, наш сервис будет просто обновлён, что можно увидеть с помощью команды ./terraform plan. Поэтому для обновления, пока, нужно пользоваться командами ./terraform destroy -target=${NAME_SERVICE} и ./terraform apply, а название сервисов можно посмотреть в ./terraform state list:

essh@kubernetes-master:~/node-cluster$ ./terraform state list

data.google_client_config.default

module.kubernetes.google_container_cluster.node-ks

module.kubernetes.google_container_node_pool.node-ks-pool

module.Nginx.kubernetes_deployment.nodejs

module.Nginx.kubernetes_service.nodejs

essh@kubernetes-master:~/node-cluster$ ./terraform destroy -target=module.nodejs.kubernetes_deployment.nodejs

essh@kubernetes-master:~/node-cluster$ ./terraform apply

А теперь заменим код нашего контейнера:

container {

image = "gcr.io/node-cluster-243923/nodejs_cluster:latest"

name = "node-js"

}

Проверим результат балансировки на разные ноды(нет переноса строк в конце вывода):

essh@kubernetes-master:~/node-cluster$ curl http://35.246.85.138:80

Nodejs_cluster is working! My host is terraform-nodejs-997fd5c9c-lqg48essh@kubernetes-master:~/node-cluster$ curl http://35.246.85.138:80

Nodejs_cluster is working! My host is terraform-nodejs-997fd5c9c-lhgsnessh@kubernetes-master:~/node-cluster$ curl http://35.246.85.138:80

Nodejs_cluster is working! My host is terraform-nodejs-997fd5c9c-lhgsnessh@kubernetes-master:~/node-cluster$ curl http://35.246.85.138:80

Nodejs_cluster is working! My host is terraform-nodejs-997fd5c9c-lhgsnessh@kubernetes-master:~/node-cluster$ curl http://35.246.85.138:80

Nodejs_cluster is working! My host is terraform-nodejs-997fd5c9c-lhgsnessh@kubernetes-master:~/node-cluster$ curl http://35.246.85.138:80

Nodejs_cluster is working! My host is terraform-nodejs-997fd5c9c-lhgsnessh@kubernetes-master:~/node-cluster$ curl http://35.246.85.138:80

Nodejs_cluster is working! My host is terraform-nodejs-997fd5c9c-lqg48essh@kubernetes-master:~/node-cluster$ curl http://35.246.85.138:80

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