Потрібно піднімати Kubernetes кластер, але я всього-лише програміст коду. Вихід є

Доброго часу доби. Чергова замітка з мого досвіду. В цей раз поверхнево про базову інфраструктуру, яку використовую, якщо треба щось передати, а поруч немає devOps хлопців. Але поточний рівень абстракції, в технологіях, дозволяє вже близько року жити з цією інфраструктурою, порушеної за ніч, використовуючи інтернет і готові речі.

Ключові слова — AWS + Terraform + kops . Якщо це корисно мені — можливо буде корисно кому-небудь ще. Ласкаво просимо в коментарі.

-1. Те, з чим маємо справу

Класична ситуація — проект написаний до такої стадії, коли його необхідно кудись вивантажувати і починати використовувати. І проект складніше, ніж проста html-сторінка. Хотілося б можливості горизонтального масштабування, ідентичності оточення на локальних, тестових, прод стендах і більш-менш нормальний процес деплоя.

Мова піде про додаток на Laravel, щоб показати від початку і до кінця весь процес. Але аналогічним чином можна деплоіти розсип сервісів на go, python-застосунки, невеликі сайти на WP, html-сторінки і багато всього. До якогось рівня цього досить, а потім вже і в команді з’являється окрема людина, яка покращить і доповнить.
Останнім часом я прийшов до того, що на локальних машинах встановлюю GoLand, PhpStorm, Docker, Git і повністю готовий до роботи. Та й керувати з однієї машини ви можете розсипом кластерів, тому весь процес буду описувати без обліку ОЗ, на якій працюєте, упаковуючи речі у докер контейнер.

0. Готуємося до роботи.

Давайте уявимо, що ми вже зареєстрували акаунт на AWS, попросили через техпідтримку збільшити ліміти аккаунта на кількість одночасно запущених серверів, створили IAM користувача і тепер у нас є Access Key + Secret Key. Зона — us-east-1.

Що нам знадобиться на локальному комп’ютері? AWS CLI, Terraform для декларативного управління AWS, kubectl, kops для налаштування кластера і Helm, для розгортання деяких сервісів. Збираємо Dockerfile (який я давно знайшов десь на просторах гітхабу, але не можу знайти де). Пишемо свій docker-compose.yml для маунта директорій і Makefile для аліасів.

Dockerfile

FROM ubuntu:16.04

ARG AWSCLI_VERSION=1.12.1
ARG HELM_VERSION=2.8.2
ARG ISTIO_VERSION=0.6.0
ARG KOPS_VERSION=1.9.0
ARG KUBECTL_VERSION=1.10.1
ARG TERRAFORM_VERSION=0.11.0

# Install generally useful things
RUN apt-get update 
 && apt-get -y --force-yes install --no-install-recommends 
 curl 
 dnsutils 
 git 
 jq 
 net-tools 
 ssh 
 telnet 
 unzip 
 vim 
 wget 
 && apt-get clean 
 && apt-get autoclean 
 && apt-get autoremove 
 && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Install AWS CLI
RUN apt-get update 
 && apt-get -y --force-yes install 
 python-pip 
 && pip install awscli==${AWSCLI_VERSION} 
 && apt-get clean 
 && apt-get autoclean 
 && apt-get autoremove 
 && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Install Terraform
RUN wget -O terraform.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip 
 && unzip terraform.zip 
 && mv terraform /usr/local/bin/terraform 
 && chmod +x /usr/local/bin/terraform 
 && rm terraform.zip

# Install kubectl
ADD https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl /usr/local/bin/kubectl
RUN chmod +x /usr/local/bin/kubectl

# Install Kops
ADD https://github.com/kubernetes/kops/releases/download/${KOPS_VERSION}/kops-linux-amd64 /usr/local/bin/kops
RUN chmod +x /usr/local/bin/kops

# Install Helm
RUN wget -O helm.tar.gz https://storage.googleapis.com/kubernetes-helm/helm-v${HELM_VERSION}-linux-amd64.tar.gz 
 && tar xfz helm.tar.gz 
 && mv linux-amd64/helm /usr/local/bin/helm 
 && chmod +x /usr/local/bin/helm 
 && rm -Rf linux-amd64 
 && rm helm.tar.gz

# Create default user "kops"
RUN useradd -ms /bin/bash kops
WORKDIR /home/kops
USER kops

# Ensure the prompt doesn't break if we don't mount the ~/.kube directory
RUN mkdir /home/kops/.kube 
 && touch /home/kops/.kube/config

docker-compose.yml

версія: '2.1'
services:
cluster-main:
 container_name: cluster.com
 image: cluster.com
 user: root
 stdin_open: true
volumes:
 - ./data:/data
 - ./.ssh:/root/.ssh
 - ./.kube:/root/.kube
 - ./.aws:/root/.aws

cluster-proxy:
 container_name: cluster.com-kubectl-proxy
 image: cluster.com
 user: root
 entrypoint: kubectl proxy --address='0.0.0.0' --port=8001 --accept-hosts='.*'
ports:
 - "8001:8001"
 stdin_open: true
volumes:
 - ./data:/data
 - ./.ssh:/root/.ssh
 - ./.kube:/root/.kube
 - ./.aws:/root/.aws

Makefile

docker.build:
 docker build -t cluster500.com .

docker.run:
 docker-compose up -d

docker.bash:
 docker exec -it cluster500.com bash

Dockerfile — беремо базовий образ ubuntu і встановлюємо весь софт. Makefile — просто для зручності, можна використовувати і звичайний механізм аліасів. Docker-compose.yml — ми додали додатковий контейнер, який нам прокине в браузер K8S Dashboard, якщо потрібно візуально щось подивитися.

Читайте також  Повнолітня журналістика: від Росії до Кремля

Створюємо папки data, .ssh, .kube, .aws в корені і кладемо туди наш конфіг для aws, ssh ключі і можемо збирати і запускати наш контейнер через make docker.build & make docker.run.

Ну і в папці data створюємо папку, в яку покладемо yaml файли k8s, а поряд другу, в якій будемо зберігати стан terraform кластера.

1. Піднімаємо наш кластер.

Далі буде вільний переклад цієї замітки. Я опущу багато теоретичних моментів, постараюся описати коротку витримку. Все таки формат моєї замітки — tldr.
В нашу папку data/aws-cluster-init-kops-terraform клонуєм те, що лежить в цьому репозиторії і заходимо в консоль контейнера через make docker.bash. Починається розсип нудних команд.

AWS CLI

Створюємо користувача kops , додаємо права доступу і переконфігуруємо AWS CLI на нього, щоб не запускати команди від суперюзера.

aws iam create group --group-name kops

# Політики доступу
aws iam attach-group policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess --group-name kops
aws iam attach-group policy --policy-arn arn:aws:iam::aws:policy/AmazonRoute53FullAccess --group-name kops
aws iam attach-group policy --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess --group-name kops
aws iam attach-group policy --policy-arn arn:aws:iam::aws:policy/IAMFullAccess --group-name kops
aws iam attach-group policy --policy-arn arn:aws:iam::aws:policy/AWSCertificateManagerFullAccess --group-name kops
aws iam attach-group policy --policy-arn arn:aws:iam::aws:policy/AmazonVPCFullAccess --group-name kops

aws iam create-user --user-name kops

aws iam add-user-to-group --user-name kops --group-name kops

aws iam create-access-key --user-name kops

 

aws configure 

 

Ініціалізуємо Terraform

Змінюємо у файлі data/aws-cluster-init-kops-terraform/variables.tf ім’я кластера на потрібне. Не забуваємо взяти з файлу update.json наші dns сервера і оновити їх там, де купували свій домен.

# Переходимо в папку
cd /data/aws-cluster-init-kops-terraform

# Експортуємо змінні від AWS CLI
export AWS_ACCESS_KEY_ID=$(aws configure get aws_access_key_id)
export AWS_SECRET_ACCESS_KEY=$(aws configure get aws_secret_access_key)

# Ініціалізуємо terraform
terraform init
terraform get
terraform apply

# Беремо NS сервера
cat update-zone.json 
 | jq ".Changes[].ResourceRecordSet.Name="$(terraform output name)."" 
 | jq ".Changes[].ResourceRecordSet.ResourceRecords=$(terraform output -json name_servers | jq '.value|[{"Value": .[]}]')" 
 > update-zone.json

 

Читайте також  Написання шейдерів у Unity. GrabPass, PerRendererData

Kops

Створюємо кластер через kops, експортуючи в конфіг .tf файл.

export NAME=$(terraform output cluster_name)
export KOPS_STATE_STORE=$(terraform output state_store)
export ZONES=$(terraform output -json availability_zones | jq -r '.value|join(",")')

kops create cluster 
 --master-zones $ZONES 
 --zones $ZONES 
 --topology private 
 --dns zone $(terraform output public_zone_id) 
 --networking calico 
 --vpc $(terraform output vpc_id) 
 --target=terraform 
 --out=. 
${NAME}

Тут потрібна невелика ремарка. Terraform створить VPC, і нам необхідно буде трохи підправити конфіг, який нам віддасть kops. Це робиться досить просто, через допоміжний спосіб ryane/gensubnets:0.1

# Всередині конейнера
terraform output -json > subnets.json

 

# На вашій машині хоста
echo subnets.json | docker run --rm -i ryane/gensubnets:0.1

Ви можете додати політики для route53.

additionalPolicies:
 master: |
[
{
 "Effect": "Allow",
 "Action": ["route53:ListHostedZonesByName"],
 "Resource": ["*"]
},
{
 "Effect": "Allow",
 "Action": ["elasticloadbalancing:DescribeLoadBalancers"],
 "Resource": ["*"]
},
{
 "Effect": "Allow",
 "Action": ["route53:ChangeResourceRecordSets"],
 "Resource": ["*"]
}
]
 node: |
[
{
 "Effect": "Allow",
 "Action": ["route53:ListHostedZonesByName"],
 "Resource": ["*"]
},
{
 "Effect": "Allow",
 "Action": ["elasticloadbalancing:DescribeLoadBalancers"],
 "Resource": ["*"]
},
{
 "Effect": "Allow",
 "Action": ["route53:ChangeResourceRecordSets"],
 "Resource": ["*"]
}
]

Редагуємо через kops edit cluster ${NAME}.

Тепер ми можемо піднімати сам кластер.

kops update cluster 
 --out=. 
 --target=terraform 
${NAME}

terraform apply

Все пройде добре, контекст kubectl зміниться. В папці data/aws-cluster-init-kops-terraform у нас буде зберігається стан кластера. Можна просто покласти все в git і відправити в приватний репозиторій бітбакету.

$ kubectl get nodes
NAME STATUS AGE
ip-10-20-101-252.ec2.internal Ready,master 7m
ip-10-20-103-232.ec2.internal Ready,master 7m
ip-10-20-103-75.ec2.internal Ready 5m
ip-10-20-104-127.ec2.internal Ready,master 6m
ip-10-20-104-6.ec2.internal Ready 5m

 

2. Піднімаємо наш додаток

Тепер, коли у нас є щось, ми можемо розгортати в кластері наші сервіси. Зразкові конфіги я покладу в цей же репозиторій. Їх можна пачкою покласти в data/k8s.

Сервісні жарти

Почнемо з сервісних речей. Нам необхідний helm, route53, storage-classes і доступ до нашого приватного registry на hub.docker.com. Ну або до будь-якого іншого, якщо є таке бажання.

# Init helm
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
helm init

 

kubectl apply -f default-namespace.yaml
kubectl apply -f storage-classes.yaml
kubectl apply -f route53.yaml
kubectl apply -f docker-hub-secret.yml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml

 

Читайте також  Статичний веб: повернення до витоків?

PostgreSQL + Redis

Я дуже багато разів обпікався, використовуючи докер не для stateless контейнерів, але остання конфігурація показала себе найбільш підходящою. Використовую Stolon, щоб забезпечити масштабованість. Близько року політ нормальний.

Розвертаємо helm-charts і пару швидких конфіги Redis.

# Розгортаємо etcd для stolon
cd etcd-chart
helm install --name global-etcd .

# Розгортаємо сам stolon
cd stolon-chart
helm dep build
helm install --name global-postgres .

# Розгортаємо redis
kubectl apply -f redis

 

Nginx + PHP

Звичайна зв’язка. Nginx і php-fpm. Конфіги я особливо не викидав, але кожен зможе налаштувати під себе. Перед застосуванням необхідно вказати спосіб, з якого ми будемо брати код + додати рядок сертифіката з AWS Certificate Manager. Сам php — можна брати з докерхабу, але я зібрав свій приватний, додавши трохи бібліотек.

kubectl apply -f nginx
kubectl apply -f php

В нашому образі з кодом ми зберігаємо його у папці /crm-code. Підміняємо на свій образ і воно цілком коректно запрацює. Файл — nginx/deployment.yml.

Виводимо назовні домен. Route53 сервіс його підхопить, змінить/додасть DNS записи, сертифікат довантажити на ELB з AWS Certificate Manager. Файл — nginx/service.yml.

Прокидуємо env змінні в php, щоб мати їх всередині і підключатися до PostgreSQL/Redis. Файл — php/deployment.yml.

Як підсумок, ми маємо K8S кластер, який на базовому рівні ми можемо масштабувати, додавати нові сервіси, нові сервера (ноди), змінювати кількість PostgreSQL, PHP, Nginx інстансів і прожити до того, як у команді з’явиться окрема людина, яка буде цим займатися.

В рамках цієї невеличкої замітки я не буду торкатися питань бекапів/моніторингу цього всього добра. На початковому етапі буде достатньо localhost:8001/ui від K8S Dashboard сервісу. Пізніше можна буде прикрутити Prometheus, Grafana, Barman, або будь-які інші схожі рішення.

Використовуючи термінал, або Teamcity, Jenkins оновлення коду буде робитися приблизно так.

# Збираємо образ з кодом десь на локальній машині або в Teamcity
docker build -t GROUP/crm-code:latest .
docker push GROUP/crm-code:latest

# Оновлюємо код (тут трохи лайфхаков)
kubectl set image deployment/php-fpm php-fpm=GROUP/php-fpm
kubectl rollout status deployment/php-fpm
kubectl set image deployment/php-fpm php-fpm=GROUP/php-fpm:latest

kubectl set image deployment/nginx nginx=danday74/nginx-lua
kubectl rollout status deployment/nginx
kubectl set image deployment/nginx nginx=danday74/nginx-lua:latest

kubectl rollout status deployment/php-fpm
kubectl rollout status deployment/nginx

Буду радий, якщо це буде комусь цікаво і подвійно радий, якщо це комусь допоможе. Спасибі за вашу увагу.

Степан Лютий

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

You may also like...

1 Response

  1. Дмитро сказав:

    nice 🙂

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *