Хабрахабр

Нужно поднимать Kubernetes кластер, но я всего-лишь программист кода. Выход есть

Очередная заметка из моего опыта. Доброго времени суток. Но текущий уровень абстракции, в технологиях, позволяет уже около года жить с этой инфраструктурой, поднятой за ночь, используя интернет и готовые вещи. В этот раз поверхностно о базовой инфраструктуре, которую использую, если надо что-то выгрузить, а рядом нет devOps ребят.

Если это полезно мне — возможно будет полезно кому-нибудь еще. Ключевые слова — AWS + Terraform + kops . Добро пожаловать в комментарии.

-1. То, с чем имеем дело

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

Но аналогичным образом можно деплоить россыпь сервисов на go, python-приложения, небольшие сайты на WP, html-страницы и много всего. Речь пойдет о приложение на Laravel, чтобы показать от начала и до конца весь процесс. До какого-то уровня этого достаточно, а затем уже и в команде появляется отдельный человек, который улучшит и дополнит.

Последнее время я пришел к тому, что на локальных машинах устанавливаю GoLand, PhpStorm, Docker, Git и полностью готов к работе. Да и управлять с одной машины вы можете россыпью кластеров, поэтому весь процесс буду описывать без учета ОС, на которой работаете, упаковывая все вещи в докер контейнер.

0. Готовимся к работе.

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

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

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==$ \ && 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

version: '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

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. Здесь нужна небольшая ремарка. 1
Это делается достаточно просто, через вспомогательный образ ryane/gensubnets:0.

# Внутри конейнера
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.

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

Как итог, мы имеем 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

Спасибо за ваше внимание. Буду рад, если это будет кому-нибудь интересно и вдвойне рад, если это кому-нибудь поможет. Еще раз прикладываю ссылку на репозиторий один и два.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть