Kubernetes: запуск Gravl в Minikube

Автор: | 12/06/2017

Задача: используя Minikube запустить контейнер с CMS Grav.

Шаги:

  1. собрать Docker образ с NGINX + PHP-FPM и кодом приложения
  2. запушить его в DockerHub
  3. запустить контейнер с приложением в Kubernetes кластере

Установка и запуск Minikube описаны тут>>>.

Проверка Grav

Т.к. эту CMS ещё не использовал – сначала попробуем запустить её вручную, и посмотреть как она стартует.

На Arch для проверки устанавливаем PHP и php-gd:

[simterm]

$ sudo pacman -S php
$ sudo pacman -S php-gd

[/simterm]

Используем NGINX + PHP-FPM из DockerHub:

[simterm]

$ sudo docker pull richarvey/nginx-php-fpm:latest

[/simterm]

Качаем сам Grav:

[simterm]

$ cd /tmp
$ git clone https://github.com/getgrav/grav.git
$ cd grav/

[/simterm]

Запускаем контейнер, монтируем каталог со скачанным Grav:

[simterm]

$ sudo docker run -ti -p 80:80 -v /tmp/grav/:/var/www/html  richarvey/nginx-php-fpm:latest bash
bash-4.3#

[/simterm]

Проверяем файлы и выполняем установку зависимостей:

[simterm]

bash-4.3# cd /var/www/html/                                                                                                                                                                                                                   
bash-4.3# ls -l                                                                                                                                                                                                                               
total 248                                                                                                                                                                                                                                     
-rw-r--r--    1 1000     1000         87315 Jun 12 10:50 CHANGELOG.md
-rw-r--r--    1 1000     1000          6417 Jun 12 10:50 CONTRIBUTING.md
...
drwxr-xr-x    8 1000     1000           160 Jun 12 10:50 user
drwxr-xr-x    2 1000     1000           160 Jun 12 10:50 webserver-configs
bash-4.3# bin/grav install
Preparing to install vendor dependencies...

Do not run Composer as root/super user! See https://getcomposer.org/root for details
Loading composer repositories with package information
Installing dependencies from lock file
Package operations: 27 installs, 0 updates, 0 removals
  - Installing antoligy/dom-string-iterators (v1.0.0): Downloading (100%)         
...
  - Installing twig/twig (v1.33.2): Downloading (100%)
...
SUCCESS cloned https://github.com/getgrav/grav-theme-antimatter -> /var/www/html/user/themes/antimatter

[/simterm]

Запускаем NGINX:

[simterm]

bash-4.3# /start.sh
Do not run Composer as root/super user! See https://getcomposer.org/root for details
Loading composer repositories with package information
Installing dependencies from lock file
Nothing to install or update
Generating autoload files
2017-06-12 10:55:51,000 CRIT Set uid to user 0
2017-06-12 10:55:51,230 INFO RPC interface 'supervisor' initialized
2017-06-12 10:55:51,230 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2017-06-12 10:55:51,231 INFO supervisord started with pid 194
2017-06-12 10:55:52,236 INFO spawned: 'php-fpm' with pid 204
2017-06-12 10:55:52,241 INFO spawned: 'nginx' with pid 205
2017-06-12 10:55:53,724 INFO success: php-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2017-06-12 10:55:53,724 INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

[/simterm]

С другой консоли проверяем:

[simterm]

$ curl localhost
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Home | Grav</title>
    <meta name="generator" content="GravCMS" />
<meta name="description" content="Grav is an easy to use, yet powerful, open source flat-file CMS" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <link rel="icon" type="image/png" href="/user/themes/antimatter/images/favicon.png" />
    <link rel="canonical" href="http://localhost" />

[/simterm]

ОК, всё работает – переходим к сборке образа.

Сборка Grav в Docker образ

В будущем сборка будет выполняться в Jenkins, а пока готовим свой репозиторий и Dockerfile.

Готовим репозиторий:

[simterm]

$ git clone https://github.com/setevoy2/keygrav.git
Cloning into 'keygrav'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
$ git clone https://github.com/getgrav/grav.git
Cloning into 'grav'...
remote: Counting objects: 32074, done.
remote: Compressing objects: 100% (55/55), done.
remote: Total 32074 (delta 33), reused 54 (delta 20), pack-reused 31990
Receiving objects: 100% (32074/32074), 10.91 MiB | 213.00 KiB/s, done.
Resolving deltas: 100% (18767/18767), done.
$ rsync -av --progress grav/ keygrav/ --exclude .git
$ ls -l keygrav/
total 292
drwxr-xr-x 2 setevoy setevoy   4096 Jun 12 14:00 assets
drwxr-xr-x 2 setevoy setevoy   4096 Jun 12 14:00 backup
drwxr-xr-x 2 setevoy setevoy   4096 Jun 12 14:00 bin
...
drwxr-xr-x 8 setevoy setevoy   4096 Jun 12 14:00 user
drwxr-xr-x 2 setevoy setevoy   4096 Jun 12 14:00 webserver-configs
$ cd keygrav/
$ git add -A && git commit -m "grav init"

[/simterm]

CMS небольшая:

[simterm]

$ du -sh .
11M     .

[/simterm]

Поэтому можно весь код собрать прямо в образ, по аналогии со сборкой образов NodeJS.

Устанавливаем зависимости:

[simterm]

$ php bin/grav install

[/simterm]

Создаём Dockerfile:

FROM richarvey/nginx-php-fpm
COPY . /var/www/html
EXPOSE 443 80
CMD ["/start.sh"]

Пробуем собрать его:

[simterm]

$ sudo docker build -t keygrav .                                                                                                                                           
Sending build context to Docker daemon  7.426MB                                                                                                                                                                                               
Step 1/5 : FROM richarvey/nginx-php-fpm                                                                                                                                                                                                       
 ---> d1db840b1d7d                                                                                                                                                                                                                            
Step 2/5 : COPY . /var/www/html                                                                                                                                                                                                               
 ---> 00b95aff170c                                                                                                                                                                                                                            
...
Step 5/5 : CMD /start.sh
 ---> Running in ec169fdc37e5
 ---> c8c8da1b26b5
Removing intermediate container ec169fdc37e5
Successfully built c8c8da1b26b5
Successfully tagged keygrav:latest

[/simterm]

Запускаем:

[simterm]

$ sudo docker run -ti -p 80:80 keygrav
Do not run Composer as root/super user! See https://getcomposer.org/root for details
Loading composer repositories with package information
Installing dependencies from lock file
Nothing to install or update
Generating autoload files
2017-06-12 12:13:49,649 CRIT Set uid to user 0
2017-06-12 12:13:50,046 INFO RPC interface 'supervisor' initialized
2017-06-12 12:13:50,047 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2017-06-12 12:13:50,047 INFO supervisord started with pid 1
2017-06-12 12:13:51,052 INFO spawned: 'php-fpm' with pid 16
2017-06-12 12:13:51,057 INFO spawned: 'nginx' with pid 17
2017-06-12 12:13:52,582 INFO success: php-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2017-06-12 12:13:52,582 INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

[/simterm]

Проверяем:

[simterm]

$ curl localhost
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Home | Grav</title>
    <meta name="generator" content="GravCMS" />
<meta name="description" content="Grav is an easy to use, yet powerful, open source flat-file CMS" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <link rel="icon" type="image/png" href="/user/themes/antimatter/images/favicon.png" />
    <link rel="canonical" href="http://localhost" />
    ...

[/simterm]

Работает.

Собираем ещё раз, и пушим в DockerHub:

[simterm]

$ sudo docker build -t setevoy/keygrav .
$ sudo docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: setevoy
Password:
Login Succeeded
$ sudo docker push setevoy/keygrav

[/simterm]

Последний шаг – создать деплоймент для Kubernetes.

Kubernetes deployment

Создаём deployment файл для K8n:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: keygrav
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx-php-grav
    spec:
      containers:
      - name: keygrav
        image: setevoy/keygrav:latest
        ports:
        - containerPort: 80

Запускаем Minikube:

[simterm]

$ minikube start
Starting local Kubernetes cluster...
Starting VM...
SSH-ing files into VM...
Setting up certs...
Starting cluster components...
Connecting to cluster...
Setting up kubeconfig...
Kubectl is now configured to use the cluster.

[/simterm]

Создаём деплоймент:

[simterm]

$ kubectl create -f keygrav.yml
deployment "keygrav" created

[/simterm]

Проверяем:

[simterm]

$ kubectl get deployments
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
keygrav   1         1         1            1           2m

[/simterm]

Запускаем сервис:

[simterm]

$ kubectl expose deployment keygrav --type=NodePort
service "keygrav" exposed

[/simterm]

Проверяем:

[simterm]

$ kubectl get services
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
keygrav      10.0.0.216   <none>        80/TCP    14s

[/simterm]

Находим URL (т.к. Kubernetes работает в Virtualbox-е):

[simterm]

$ minikube service keygrav --url                                                                                                                                           
Waiting, endpoint for service is not ready yet...                                                                                                                                                                                             
http://192.168.99.100:30872

[/simterm]

Проверяем:

[simterm]

$ curl http://192.168.99.100:30872                                                                                                                                         
<!DOCTYPE html>                                                                                                                                                                                                                               
<html lang="en">                                                                                                                                                                                                                              
<head>                                                                                                                                                                                                                                        
    <meta charset="utf-8" />                                                                                                                                                                                                                  
    <title>Home | Grav</title>                                                                                                                                                                                                                
    <meta name="generator" content="GravCMS" />                                                                                                                                                                                               
<meta name="description" content="Grav is an easy to use, yet powerful, open source flat-file CMS" />                                                                                                                                         
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">                                                                                                            
    <link rel="icon" type="image/png" href="/user/themes/antimatter/images/favicon.png" />                                                                                                                                                    
    <link rel="canonical" href="http://192.168.99.100" />

[/simterm]

Работает.

Jenkins pipeline

И ещё один последний шаг – набросать билд-деплой в Jenkins.

Деплоя не будет, т.к. это Proof of Concept и полноценного кластера Kubernetes нет.

Запускаем EC2, запускаем там Jenkins:

[simterm]

# docker run -u root -tid -p 80:8080 -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v /usr/lib/x86_64-linux-gnu/libltdl.so.7.3.0:/usr/lib/x86_64-linux-gnu/libltdl.so.7 –name jenkins jenkins

[/simterm]

Опять таки – это не продакшен решение, workspaces Jenkins-а и все созданные пайплайны после остановки контейнера будут удалены.

Запуск Jenkins с workspaces на отдельном разделе описан в посте Azure: Azure Resource Manager provisioning и Jenkins в Docker.

Создаём джобу:

Создаём билд-файл:

[simterm]

$ mkdir ci
$ cd ci/

[/simterm]

Содержимое файла jenkins.groovy, с функциями:

#!/usr/bin/env groovy

def dockerBuild () {

    stage ('Docker build') {

        sh 'apt-get update && apt-get -y install php5 php5-gd php5-curl' 
        sh '/usr/bin/php5 bin/grav install'

        withDockerRegistry(registry: [credentialsId: 'setevoy-docker-hub']){
            docker.build("setevoy/keygrav:${env.BUILD_NUMBER}").push()
        }
    }
}

def kuberDeploy () {

        stage ('Deploy') {

           /* As this is PoC and we have no Dev/QA/etc environments - deploy wasn't done.
           Possible soultions
           1:
           - update keygrav.yml in repo with new :tag
           - copy to the Master host
           - kubectl apply -f keygrav.yml
           2:
           - SSH to the Master host and exec `kubectl set image deployment/keygrav keygrav=setevoy/keygrav:${env.BUILD_NUMBER}`
           */
    }
}

return this

И скрипт самого билда keybuild.groovy:

#!/usr/bin/env groovy

node {

    git branch: "${BRANCH}", url: "${BUILDREPOURL}"

    def build = load 'ci/jenkins.groovy'

    build.dockerBuild()
//      build.kuberDeploy()
}

Билдим:

Готово.