Bablu Ahmed
Bablu Ahmed

Reputation: 5020

How to run a simple php hello world application using Kubernetes

I'm trying to deploy and run a simple PHP application that will only show a Hello World message through my Kubernetes cluster which is only a master node cluster, unfortunately, I can't do that.

I'm describing my project structure - I have a root project directory called kubernetes-test and under that directory, I have 3 yaml files and one directory called code under that directory I have a PHP file called index.php

hello-world-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    tier: backend
spec:
  selector:
    app: nginx
    tier: backend
  type: NodePort
  ports:
  - nodePort: 30500
    port: 80
    targetPort: 80

nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    tier: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      tier: backend
  template:
    metadata:
      labels:
        app: nginx
        tier: backend
    spec:
      volumes:
      - name: code
        hostPath:
          path: /code
      - name: config
        configMap:
          name: nginx-config
          items:
          - key: config
            path: site.conf
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
        volumeMounts:
        - name: code
          mountPath: /var/www/html
        - name: config
          mountPath: /etc/nginx/conf.d

php-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php
  labels:
    tier: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: php
      tier: backend
  template:
    metadata:
      labels:
        app: php
        tier: backend
    spec:
      volumes:
      - name: code
        hostPath:
          path: /code
      containers:
      - name: php
        image: php:7-fpm
        volumeMounts:
        - name: code
          mountPath: /var/www/html

code/index.php

<?php
  echo 'Hello World';

Above all those things I've found through the internet.

When I ran this command kubectl get pods then the status is showing ContainerCreating forever for the Nginx deployment like this

NAME                    READY   STATUS              RESTARTS   AGE
nginx-64c9df788f-jxwzx   0/1     ContainerCreating    0          12h
php-55f974bb4-qvv9x      1/1     Running              0          25s

Command: kubectl describe pod nginx-64c9df788f-jxwzx

Output:

Name:           nginx-64c9df788f-jxwzx
Namespace:      default
Priority:       0
Node:           bablu-node/192.168.43.123
Start Time:     Mon, 11 May 2020 03:20:58 +0600
Labels:         app=nginx
                pod-template-hash=64c9df788f
                tier=backend
Annotations:    <none>
Status:         Pending
IP:             
IPs:            <none>
Controlled By:  ReplicaSet/nginx-64c9df788f
Containers:
  nginx:
    Container ID:   
    Image:          nginx
    Image ID:       
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Waiting
      Reason:       ContainerCreating
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /etc/nginx/conf.d from config (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-l2zp2 (ro)
      /var/www/html from code (rw)
Conditions:
  Type              Status
  Initialized       True 
  Ready             False 
  ContainersReady   False 
  PodScheduled      True 
Volumes:
  code:
    Type:          HostPath (bare host directory volume)
    Path:          /code
    HostPathType:  
  config:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      nginx-config
    Optional:  false
  default-token-l2zp2:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-l2zp2
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason       Age                    From                 Message
  ----     ------       ----                   ----                 -------
  Warning  FailedMount  31m (x14 over 147m)    kubelet, bablu-node  Unable to attach or mount volumes: unmounted volumes=[config], unattached volumes=[default-token-l2zp2 code config]: timed out waiting for the condition
  Warning  FailedMount  16m (x82 over 167m)    kubelet, bablu-node  MountVolume.SetUp failed for volume "config" : configmap "nginx-config" not found
  Warning  FailedMount  6m53s (x44 over 165m)  kubelet, bablu-node  Unable to attach or mount volumes: unmounted volumes=[config], unattached volumes=[code config default-token-l2zp2]: timed out waiting for the condition
  Warning  FailedMount  2m23s (x10 over 163m)  kubelet, bablu-node  Unable to attach or mount volumes: unmounted volumes=[config], unattached volumes=[config default-token-l2zp2 code]: timed out waiting for the condition

Command: kubectl get events -n default

Output:

LAST SEEN   TYPE      REASON              OBJECT                       MESSAGE
18m         Warning   FailedMount         pod/nginx-64c9df788f-jxwzx   MountVolume.SetUp failed for volume "config" : configmap "nginx-config" not found
8m45s       Warning   FailedMount         pod/nginx-64c9df788f-jxwzx   Unable to attach or mount volumes: unmounted volumes=[config], unattached volumes=[code config default-token-l2zp2]: timed out waiting for the condition
4m15s       Warning   FailedMount         pod/nginx-64c9df788f-jxwzx   Unable to attach or mount volumes: unmounted volumes=[config], unattached volumes=[config default-token-l2zp2 code]: timed out waiting for the condition
33m         Warning   FailedMount         pod/nginx-64c9df788f-jxwzx   Unable to attach or mount volumes: unmounted volumes=[config], unattached volumes=[default-token-l2zp2 code config]: timed out waiting for the condition
18m         Normal    Scheduled           pod/php-55f974bb4-qvv9x      Successfully assigned default/php-55f974bb4-qvv9x to bablu-node
18m         Normal    Pulled              pod/php-55f974bb4-qvv9x      Container image "php:7-fpm" already present on machine
18m         Normal    Created             pod/php-55f974bb4-qvv9x      Created container php
18m         Normal    Started             pod/php-55f974bb4-qvv9x      Started container php
18m         Normal    SuccessfulCreate    replicaset/php-55f974bb4     Created pod: php-55f974bb4-qvv9x
18m         Normal    ScalingReplicaSet   deployment/php               Scaled up replica set php-55f974bb4 to 1

Can anyone please help me? Thanks in advance!!

Upvotes: 1

Views: 3540

Answers (2)

Will R.O.F.
Will R.O.F.

Reputation: 4138

I ran your environment and here are the main issues I found:

  • First of all, you don't have the nginx-config deployed, but this would be only your first issue and easily addressable (more on the example below).
  • The second (and on my opinion the main) issue, is the usage of HostPath:
    • As I explained here HostPath requires that the container process run as root.
    • php-fpm runs as www-data therefore he cannot use the mount files at /code if this folder is mounted through hostPath.

From here our options now are:

  • Bake the php file inside the image (or as a configmap) and run both nginx and php in the same pod (sharing an emptydir folder), more about this process in this guide: PHP-FPM, Nginx, Kubernetes, and Docker - while in one hand it involves creating a new Docker image, on the other hand it spare you of configuring a storage provisioner if you don't already have one.
  • Use an external storage to mount the file in a Persistent Volume downloading the php file from an external repository. This approach requires nginx and php to run on the same node - because storage is RWO, meaning that can be mount as read-write on only one node. Since your setup is on a single node, I'll use this approach on this example.

I tried to reproduce as close as your example, but I had to do some changes. Here are the files:

  • cm-nginx.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  labels:
    tier: backend
data:
  config : |
    server {
      index index.php index.html;
      error_log  /var/log/nginx/error.log;
      access_log /var/log/nginx/access.log;

      root /code;

      location / {
          try_files $uri $uri/ /index.php?$query_string;
      }

      location ~ \.php$ {
          try_files $uri =404;
          fastcgi_split_path_info ^(.+\.php)(/.+)$;
          fastcgi_pass php:9000;
          fastcgi_index index.php;
          include fastcgi_params;
          fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
          fastcgi_param PATH_INFO $fastcgi_path_info;
        }
    }
  • root /code is pointing the directory where to look for index.php
  • fastcgi_pass php:9000 pointing to the service called php service listening on port 9000.

  • Storage:

This is mutable depending on the storage type you are using. Minikube comes with storage provider and storageclass configured out of the box. And although minikube storage provider is called minikube-hostpath it's a CSI that will not require root access on container level to run.

  • That being said, here is the pvc.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: code
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: standard

Note that standard is the name of the dynamic storage provider built in minikube. What we are doing here is to create a PVC called code for our app to run.

  • php.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php
  labels:
    tier: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: php
      tier: backend
  template:
    metadata:
      labels:
        app: php
        tier: backend
    spec:
      volumes:
      - name: code
        persistentVolumeClaim:
          claimName: code
      containers:
      - name: php
        image: php:7-fpm
        volumeMounts:
        - name: code
          mountPath: /code
      initContainers:
      - name: install
        image: busybox
        volumeMounts:
        - name: code
          mountPath: /code
        command:
        - wget
        - "-O"
        - "/code/index.php"
        - https://raw.githubusercontent.com/videofalls/demo/master/index.php
  • here we are using a busybox initContainer to wget this php file (which is identical to the one you are using) and save it inside the mounted volume /code.

  • PHP service svc-php.yaml:

apiVersion: v1
kind: Service
metadata:
  name: php
  labels:
    tier: backend
spec:
  selector:
    app: php
    tier: backend
  ports:
  - protocol: TCP
    port: 9000
  • The Nginx deployment nginx.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    tier: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      tier: backend
  template:
    metadata:
      labels:
        app: nginx
        tier: backend
    spec:
      volumes:
      - name: code
        persistentVolumeClaim:
          claimName: code
      - name: config
        configMap:
          name: nginx-config
          items:
          - key: config
            path: site.conf
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
        volumeMounts:
        - name: code
          mountPath: /code
        - name: config
          mountPath: /etc/nginx/conf.d

The key points here, is the mount of the PVC called code on mountPath /code and the configmap we created being monted as a file called site.conf inside the folder /etc/nginx/conf.d

  • The Nginx service svc-nginx.yaml:
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    tier: backend
spec:
  type: NodePort
  selector:
    app: nginx
    tier: backend
  ports:
  - protocol: TCP
    port: 80

I'm using NodePort to ease the output test.


Reproduction:

  • Let's create the files: first the configmap and pvc, since they are required for the pods to start correctly, then the services and deployments:
$ ls
cm-nginx.yaml  nginx.yaml  php.yaml  pvc.yaml  svc-nginx.yaml  svc-php.yaml

$ kubectl apply -f cm-nginx.yaml 
configmap/nginx-config created

$ kubectl apply -f pvc.yaml 
persistentvolumeclaim/code created

$ kubectl get cm
NAME           DATA   AGE
nginx-config   1      52s

$ kubectl get pvc
NAME   STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
code   Bound    pvc-b63559a0-a306-46f2-942b-0a063bc4ab6b   1Gi        RWO            standard       17s

$ kubectl apply -f svc-php.yaml 
service/php created

$ kubectl apply -f svc-nginx.yaml 
service/nginx created

$ kubectl apply -f php.yaml 
deployment.apps/php created

$ kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
php-69d5c956ff-8tjfn   1/1     Running   0          5s

$ kubectl apply -f nginx.yaml 
deployment.apps/nginx created

$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6854dcb7db-75zxt   1/1     Running   0          4s
php-69d5c956ff-8tjfn     1/1     Running   0          22s

$ kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx        NodePort    10.107.16.212   <none>        80:31017/TCP   41s
php          ClusterIP   10.97.237.214   <none>        9000/TCP       44s

$ minikube service nginx --url
http://172.17.0.2:31017

$ curl -i http://172.17.0.2:31017
HTTP/1.1 200 OK
Server: nginx/1.7.9
Date: Thu, 28 May 2020 19:04:48 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/7.4.6

Demo Test

Here we can see the curl returned 200 OK from nginx server, powered by PHP 7 and the content of the index.php file.

I hope it helps you have a clearer understanding of this scenario.

If you have any question, let me know in the comments.

Upvotes: 3

redzack
redzack

Reputation: 1721

Can you please run a describe pod command and provide the output:

kubectl describe pod nginx-64c9df788f-jxwzx

Also kubectl get events -n default

It may be some volume mount issue, or some config problem, or resource creation. It would go to a CrashLoopBack status soon.

I am not sure how are you creating volumes and mounting them, if you have followed the following blog, you will have to create PV and PVCs, also I am failing to see any PHP service running on 9000?

That's the reason it is in ContainerCreating state. The volumes are not configured. Here is a better blog with the same PHP application and relevant steps.

You can follow a better example from native k8s docs.

Upvotes: 2

Related Questions