Деплой wordpress в kubernetes

  DevOps

Так как давно хотел пощупать GCP и неплохо было бы освежить знания о kubernetes, решил объединить эти задачки и заодно написать статью. Вместо k8s GCP (GKE) можно использовать minikube или куб в Docker Desctop. Ну а мы идём в GCP.

Аккаунтом GCP я уже пользовался, поэтому все необходимые сервисы включены, а права на локальной машине получены. Если Вы будете использовать для тестов локальный кубер, таких проблем вообще не возникнет.

Для начала создам k8s кластер и пул машин, на которых будем гонять тесты. Для этого нужно найти слова Kubernetes Engine в панели GCP и нажать на пару кнопок. Как на скринах ниже.

Ищем сервис Kubernetes Engine и включаем его. Может занять много времени.
Создаём кластер.
Укажем название и регион.
Имеем default-pool с весьма жирными машинами. Не забудьте удалить по окончанию тестов.

На скрине выше видна кнопочка Connect. Нажав её можно узнать команду для подключения к кластеру через gcloud. У меня было так

gcloud container clusters get-credentials wordpress --zone europe-north1-a --project test-gke-and-etc
Fetching cluster endpoint and auth data.
kubeconfig entry generated for wordpress.

Отлично, проверим.

CURRENT   NAME                                             CLUSTER                                          AUTHINFO                                         NAMESPACE
          docker-desktop                                   docker-desktop                                   docker-desktop
*         gke_test-gke-and-etc_europe-north1-a_wordpress   gke_test-gke-and-etc_europe-north1-a_wordpress   gke_test-gke-and-etc_europe-north1-a_wordpress
          minikube                                         minikube                                         minikube

Вроде все как надо. Начинаем самое интересное. План такой:
— сделаем mysql
— поднимем контейнер с apache-php
— настроем loadbalancer
— установим wordpress

Mysql установим как советует нам дока. mysql.yml:

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
    - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - image: mysql:5.6
          name: mysql
          env:
            # Use secret in real usage
            - name: MYSQL_ROOT_PASSWORD
              value: password
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-persistent-storage
          persistentVolumeClaim:
            claimName: mysql-pv-claim

Тут мы создаём деплой mysql и заодно вытарчиваем с него 3306 порт, который будет доступен только внутри кластера.
Но перед этим надо создать Persistent Volume. Сделаю сразу все что понадобиться. pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: site-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Наконец применяем и смотрим что получилось:

kubectl apply -f pvc.yaml
persistentvolumeclaim/mysql-pv-claim created
persistentvolumeclaim/site-data created

kubectl apply -f mysql.yaml
service/mysql created
deployment.apps/mysql created

kubectl get pvc
NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-pv-claim   Bound    pvc-d28125b3-4e36-45df-b260-ace8a5b87331   10Gi       RWO            standard       34s
site-data        Bound    pvc-25cc890f-0330-4091-ab69-5dcc8de0db9b   10Gi       RWO            standard       34s

kubectl get deploy
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
mysql   1/1     1            1           41s

Чтобы зайти в mysql и создать там БД поднимем контейнер. Все из той же доки:

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword
If you don't see a command prompt, try pressing enter.

mysql> create database wordpress;
Query OK, 1 row affected (0.00 sec)

Класс! Mysql уже есть. Как создать юзера. Теперь нам нужен веб сервер и php. Так как я не смог найти ничего, что можно прямо с докерхаба забрать, придётся сделать контейнер самостоятельно. Dockerfile:

FROM ubuntu:20.04

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install apache2 -y && \
apt install php php-mysql -y

CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

Попробуем Container Registry, которое предоставляет нам Гугл. Так как я уже ходил в него, вопросов с доступом не возникнет, но в первый раз docker push не даст доступа, но предоставит ссылочку на инструкцию. В общем делаем как сказано в инструкции:

docker build -t gcr.io/test-gke-and-etc/apache-php .
docker push gcr.io/test-gke-and-etc/apache-php

После push в registry увидим такую картину. В Settings нужно поставить публичный доступ.

Ok! Делаем деплой с apache. Но предварительно сделаем ConfigMap. Просто чтобы потом знать где изменять настройки Virtual Host. configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
  name: siteconfig
data:
  000-default.conf: |
    <VirtualHost *:80>
            ServerAdmin webmaster@localhost
            DocumentRoot /var/www/site
            ErrorLog ${APACHE_LOG_DIR}/error.log
            CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>

Применяем:

kubectl apply -f configmap.yaml
configmap/siteconfig created

Теперь деплой apache и php. apache-php.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
  labels:
    app: web-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web-server
  template:
    metadata:
      labels:
        app: web-server
    spec:
      containers:
        - name: apache-php
          image: gcr.io/test-gke-and-etc/apache-php:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: site-data-pvc
              mountPath: /var/www/site
            - name: siteconfig
              mountPath: /etc/apache2/sites-available/000-default.conf
              subPath: 000-default.conf
      volumes:
        - name: site-data-pvc
          persistentVolumeClaim:
            claimName: site-data
        - name: siteconfig
          configMap:
            name: siteconfig
kubectl apply -f apache-php.yaml
deployment.apps/web-server created

Посмотрим что есть на текущий момент:

kubectl get all
NAME                              READY   STATUS    RESTARTS   AGE
pod/mysql-68579b78bb-2btj9        1/1     Running   0          17m
pod/web-server-6bdcd547d7-jxz2s   1/1     Running   0          27s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/kubernetes   ClusterIP   10.4.0.1     <none>        443/TCP    37m
service/mysql        ClusterIP   None         <none>        3306/TCP   17m

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mysql        1/1     1            1           17m
deployment.apps/web-server   1/1     1            1           27s

NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/mysql-68579b78bb        1         1         1       17m
replicaset.apps/web-server-6bdcd547d7   1         1         1       27s

kubectl get configmaps
NAME               DATA   AGE
kube-root-ca.crt   1      37m
siteconfig         1      4m20s

kubectl get pvc
NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-pv-claim   Bound    pvc-d28125b3-4e36-45df-b260-ace8a5b87331   10Gi       RWO            standard       18m
site-data        Bound    pvc-25cc890f-0330-4091-ab69-5dcc8de0db9b   10Gi       RWO            standard       18m

Если у Вас что-то выглядет не так, возможно следует начать сначала 🙂

Сделаем лоадбалансер. Это можно провернуть только в клауде. loadbalanser.yaml:

apiVersion: v1
kind: Service
metadata:
  name: lb
spec:
  selector:
    app: web-server
  ports:
    - port: 80
      targetPort: 80  
  type: LoadBalancer
kubectl apply -f loadbalanser.yaml
service/lb created
kubectl get svc
NAME         TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.4.0.1     <none>        443/TCP        40m
lb           LoadBalancer   10.4.1.189   <pending>     80:30533/TCP   24s
mysql        ClusterIP      None         <none>        3306/TCP       20m
kubectl get svc
NAME         TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)        AGE
kubernetes   ClusterIP      10.4.0.1     <none>          443/TCP        42m
lb           LoadBalancer   10.4.1.189   34.88.201.224   80:30533/TCP   98s
mysql        ClusterIP      None         <none>          3306/TCP       22m

Подождав немного можно увидеть IP-адрес lb. На него уже можно сходить в браузере и увидеть что в папке сайта ничего нет.
Скачаем wordpress с офф сайта, распакуем и копируем в контейнер:

kubectl cp wordpress web-server-6bdcd547d7-jxz2s:/var/www/site

Далее я зашел в контейнер, поправил права и перемести файлы wordpress в корень persistent volume.

kubectl exec -it web-server-6bdcd547d7-jxz2s -- bash

cd /var/www/site/
chown 33:33 -R /var/www/site/
mv wordpress/* ./
rm -rf wordpress/

Ну а теперь наконец идем на LB и устанавливаем wordpress.

Если видим панель установки wordpress, значит apache и php работают.
mysql:3306 здесь неспроста. Это label указанный в деплое и порт сервиса. Порт можно не писать, так как 3306 дефолтный для mysql. Пароль из деплоя.
Кажется готово.

Если Вы дочитали статью до этого момента и все поняли, значит Вы и так все это знали.