Розгортання простого сервісу з Nginx на Kubernetes за допомогою Terraform

Використання перших скриптів для деплою DevOps для додатку в K8S

Після створення облікового запису служби для доступу до нашого прикладу простору імен у статті тут, ми можемо почати використовувати його для деплою першого сервісу.

Створимо простий додаток для деплою, який буде базуватися на контейнері Docker.

Створіть папку зі структурою:

  • Dockerfile
  • Makefile
  • html/index.html

Dockerfile створить контейнер для додатку на основі nginx-проксі, який буде віддавати html файл.

FROM nginx:alpine  
WORKDIR /usr/share/nginx/html  
COPY ./html/ /usr/share/nginx/html/  
EXPOSE 80

Файл index.hml виглядає так:


Example is here  

І нарешті, нам потрібен Makefile, де потрібно змінити реєстр та інші параметри.

# Variables  
REGISTRY = registry.example.com  
PROJECT = example  
IMAGE_NAME = example-web-ui  
TAG = latest  
FULL_IMAGE_NAME = $(REGISTRY)/$(PROJECT)/$(IMAGE_NAME):$(TAG)  
CONTAINER_NAME = example-web-ui-app  
PORT = 8080  

# Build the Docker image  
build:  
 docker build -t $(FULL_IMAGE_NAME) .  

# Run the Docker container  
run:  
 docker run --rm --name $(CONTAINER_NAME) -p $(PORT):80 $(FULL_IMAGE_NAME)  

# Push the image to the registry  
push:  
 docker push $(FULL_IMAGE_NAME)

Давайте побудуємо та заштовхаємо наш образ

make  
make push

Після того як ми заштовхали образ, ми готові до деплою додатку, але для самого деплою давайте використаємо Terraform.

Створимо додаткову папку ‘infra’ у нашому репозиторії та помістимо там terraform-скрипти, які визначатимуть деплой, сервіс та http-маршрут до сервісу.

Main.tf

terraform {  
 required_providers {  
 docker = {  
 source = "kreuzwerker/docker"  
 version = "~> 3.0" # Replace with the latest compatible version  
 }  
 }  
}  

provider "kubernetes" {  
 # Use kubeconfig from local environment or specify directly  
 config_path = "~/.kube/config" # This path should be modified if necessary  
}  

provider "docker" {  
 registry_auth {  
 address = "registry.example.com"  
 username = "example"  
 password = var.registry_example_password  
 }  
}  

resource "kubernetes_secret" "docker_registry" {  
 metadata {  
 name = "docker-registry-secret"  
 namespace = var.namespace  
 }  

 data = {  
 ".dockerconfigjson" = jsonencode({  
 auths = {  
 "https://registry.example.com" = {  
 username = "example"  
 password = var.registry_example_password  
 email = "[email protected]"  
 }  
 }  
 })  
 }  

 type = "kubernetes.io/dockerconfigjson"  
}

Основний файл визначає плагіни/постачальників, яких ми будемо використовувати, і секрет реєстру Docker для приватного реєстру з образом.

Давайте створимо підпапку .kube та помістимо туди конфігураційний файл, який ми створили раніше тут. Додатково давайте додамо файл ‘.kube/secrets.tfvars’, щоб зберігати пароль для приватного реєстру контейнерів.

registry_depecher_password = "..."

Також нам потрібно визначити секрети як змінні, щоб ми могли використовувати їх у Terraform.

Створимо файл ‘secrets.tf’

variable "registry_depecher_password" {  
 description = "The password for the registry"  
 sensitive = true  
}

Наступний крок — визначити змінні, які ми будемо використовувати у файлі variables.tf

variable "namespace" {  
 description = "Kubernetes namespace"  
 type = string  
}  

variable "cos" {  
 description = "Class of Service"  
 type = string  
}  

variable "project" {  
 description = "Project name"  
 type = string  
}  

variable "hostnames" {  
 description = "List of hostnames for the HTTP route"  
 type = list(string)  
}

І налаштуємо самі змінні, які можуть бути специфічними для кожного середовища.

Створимо файл ‘terraform.tfvars’.
Ім’я має бути таким, щоб Terraform міг автоматично його завантажити.

cos = "prod"  
project = "example-dot-com"  
namespace = "prod-example-dot-com"  
hostnames = [  
 "www.example.com"  
]

Наступний крок — створити сервіс у файлі ‘service.tf’.

resource "null_resource" "image_change_trigger" {  
 triggers = {  
 image_version = timestamp() # Це може бути на основі динамічного значення або мітки часу  
 }  

 provisioner "local-exec" {  
 command = "kubectl rollout restart deployment example-web-ui -n ${var.namespace}"  
 }  
}  

resource "kubernetes_deployment" "web_ui_deployment" {  
 metadata {  
 name = "example-web-ui"  
 namespace = var.namespace  
 labels = {  
 app = "example-web-ui"  
 cos = var.cos  
 project = var.project  
 }  
 }  

 spec {  
 replicas = 1  

 selector {  
 match_labels = {  
 app = "example-web-ui"  
 }  
 }  

 template {  
 metadata {  
 labels = {  
 app = "example-web-ui"  
 }  
 }  

 spec {  
 container {  
 name = "example-web-ui"  
 image = "registry.example.com/example/example-web-ui:latest"  
 image_pull_policy = "Always"  
 port {  
 container_port = 80  
 }  
 }  

 image_pull_secrets {  
 name = kubernetes_secret.docker_registry.metadata[0].name  
 }  
 }  
 }  
 }  

 depends_on = [null_resource.image_change_trigger]  
}  

resource "kubernetes_service" "web_ui_service" {  
 metadata {  
 name = "example-web-ui"  
 namespace = var.namespace  
 }  

 spec {  
 selector = {  
 app = "example-web-ui"  
 }  

 port {  
 port = 80  
 target_port = 80  
 }  

 type = "ClusterIP"  
 }  
}

Тут ми маємо додатковий null-сервіс, який буде запускати перезавантаження додатку кожного разу, коли ми застосовуємо конфігурацію Terraform. Це потрібно для того, щоб оновити сервіс і знову завантажити образ з параметром ‘imagepullpolicy = “Always”’ щоразу, оскільки ми використовуємо тег “latest”. Іншим чином Kubernetes не знатиме, що тег було змінено.

І остання конфігурація, яку нам потрібно зробити, — це ‘route.tf’.

resource "kubernetes_manifest" "http_route" {  
 manifest = {  
 apiVersion = "gateway.networking.k8s.io/v1"  
 kind = "HTTPRoute"  
 metadata = {  
 name = "example-http-route"  
 namespace = var.namespace  
 }  
 spec = {  
 parentRefs = [  
 {  
 group = "gateway.networking.k8s.io"  
 kind = "Gateway"  
 name = "external-https"  
 namespace = "gateway-infra"  

 }  
 ]  
 hostnames = var.hostnames  
 rules = [  
 {  
 matches = [  
 {  
 path = {  
 type = "PathPrefix"  
 value = "/"  
 }  
 }  
 ]  
 backendRefs = [  
 {  
 name = "example-web-ui"  
 port = 80  
 }  
 ]  
 }  
 ]  
 }  
 }  
}

Це може бути GrpcRoute або будь-який інший тип. Мета цього маршруту — підключитися до існуючого спільного сервісу-шлюзу, який займається сертифікатами TLS та іншою інфраструктурою. На цьому рівні додатку нам потрібно тільки знати доменне ім’я та маршрути, які ми будемо обслуговувати.

Додатково ми можемо додати кілька скриптів для автоматизації.

plan.sh

terraform plan -var-file=".kube/secrets.tfvars"

та apply.sh

terraform apply -var-file=".kube/secrets.tfvars"

Тепер ми готові виконати команди Terraform.

terraform init  
./plan.sh  
./apply.sh

Після цього ми отримаємо щось таке:

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

Отже, насолоджуйтесь!

Перекладено з: Deploy a simple service with Nginx on Kubernetes using Terraform

Leave a Reply

Your email address will not be published. Required fields are marked *