Потрібно піднімати 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
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
Буду радий, якщо це буде комусь цікаво і подвійно радий, якщо це комусь допоможе. Спасибі за вашу увагу.