Upgrade to Pro — share decks privately, control downloads, hide ads and more …

LAMP in Containers

LAMP in Containers

An exploration of converting a LAMP app to a multi tier container app.

Terrence Ryan

April 06, 2017
Tweet

More Decks by Terrence Ryan

Other Decks in Technology

Transcript

  1. ‹#›
    @tpryan
    Terry Ryan
    Developer Advocate
    LAMP In Containers

    View full-size slide

  2. ‹#›
    @tpryan
    Who are you?

    View full-size slide

  3. ‹#›
    @tpryan
    01 Introduction
    What Am I Trying to Do?

    View full-size slide

  4. ‹#›
    @tpryan
    I have an app…

    View full-size slide

  5. ‹#›
    @tpryan

    View full-size slide

  6. ‹#›
    @tpryan

    View full-size slide

  7. ‹#›
    @tpryan
    Demo: LAMP Version

    View full-size slide

  8. ‹#›
    @tpryan
    02 LAMP Version

    View full-size slide

  9. ‹#›
    @tpryan
    DB
    API
    UI
    LAMP

    View full-size slide

  10. ‹#›
    @tpryan
    Process
    • Create a server
    • Linux
    • Apache
    • PHP
    • MySQL
    • Get starting schema and content on it
    • Initialize system

    View full-size slide

  11. ‹#›
    @tpryan
    Create a Server
    # Request Machine
    gcloud compute instances create $(MYSQL_HOSTNAME) --zone $(ZONE) \
    --machine-type "f1-micro" --image-family="debian-8" \
    --image-project="debian-cloud" \
    --tags "http-server","https-server"
    # Install Apache + PHP
    apt-get update -y
    apt-get install apache2 php5 curl php5-curl -y
    # Install MySQL and PHP libratries
    DEBIAN_FRONTEND=noninteractive \
    apt-get -y install mysql-server mysql-client php5-mysqlnd php-pear -y
    /etc/init.d/apache2 restart

    View full-size slide

  12. ‹#›
    @tpryan
    Create a Server
    # Security routines
    DELETE FROM mysql.user WHERE User='root' AND Host = '$(MYSQL_HOSTNAME)';
    SET PASSWORD FOR 'root'@'localhost' = '$(MYSQL_ROOTHASH)';
    SET PASSWORD FOR 'root'@'127.0.0.1' = '$(MYSQL_ROOTHASH)';
    SET PASSWORD FOR 'root'@'::1' = '$(MYSQL_ROOTHASH)';
    FLUSH PRIVILEGES;
    mysql_secure_installation
    $(MYSQL_ROOTHASH) = SELECT PASSWORD('SuperSecureIPromise')

    View full-size slide

  13. ‹#›
    @tpryan
    Details
    Time to build ~ 2:30
    Time to update ~ :10
    Lines of config code 63
    Monthly Cost of Solution ~$4.28

    View full-size slide

  14. ‹#›
    @tpryan
    02 3 Tier Version

    View full-size slide

  15. ‹#›
    @tpryan
    DB
    API
    UI

    View full-size slide

  16. ‹#›
    @tpryan
    DB
    UI
    API

    View full-size slide

  17. ‹#›
    @tpryan
    03 Containerized

    View full-size slide

  18. ‹#›
    @tpryan
    03.1 Docker

    View full-size slide

  19. ‹#›
    @tpryan
    Dockerfiles
    FROM mysql/mysql-server:5.6
    ADD sql/load.sql /docker-entrypoint-initdb.d/load.sql
    EXPOSE 3306
    DB
    FROM httpd:2.4
    COPY ui /usr/local/apache2/htdocs/ui
    COPY static /usr/local/apache2/htdocs/static
    UI
    FROM php:7.0-apache
    RUN apt-get update && apt-get install -y php5-mysqlnd
    RUN docker-php-ext-install mysqli
    RUN a2enmod rewrite && a2enmod headers && service apache2 restart
    COPY app/ /var/www/html/
    API

    View full-size slide

  20. ‹#›
    @tpryan
    docker run --name=3tier_frontend -d -p 8080:80 3tier_frontend
    docker run --name=3tier_api --link 3tier_db:mysql -d -p 80:80 3tier_api
    Startup scripts
    docker run --name=3tier_db -d -p 3306:3306 3tier_db
    DB
    UI
    API

    View full-size slide

  21. ‹#›
    @tpryan
    Code Changes
    Change frontend PUT requests to POST to deal with CORS issues.
    DB
    UI
    API

    View full-size slide

  22. ‹#›
    @tpryan
    Original UI
    var url = this.protocol + this.host + this.uri_location_list;
    var type = "POST";
    if (location.id != 0){
    url += location.id;
    type = "PUT";
    }
    $.ajax({
    url: url,
    type: type,
    data: location,
    success: successHandler,
    error: errorHandler
    }); UI

    View full-size slide

  23. ‹#›
    @tpryan
    But secretly the API didn’t really care.
    if ($post_vars['id'] == 0){
    // INSERT SQL..
    } else {
    // UPDATE SQL...
    }
    API

    View full-size slide

  24. ‹#›
    @tpryan
    So now this
    var url = this.protocol + this.host + this.uri_location_list;
    var type = "POST";
    if (location.id != 0){
    url += location.id;
    type = "PUT";
    }
    $.ajax({
    url: url,
    type: type,
    data: location,
    success: successHandler,
    error: errorHandler
    }); UI
    var url = this.protocol + this.host + this.uri_location_list;
    var type = "POST";
    if (location.id != 0){
    url += location.id;
    type = "PUT";
    }
    $.ajax({
    url: url,
    type: type,
    data: location,
    success: successHandler,
    error: errorHandler
    });

    View full-size slide

  25. ‹#›
    @tpryan
    Details
    Time to build ~ :08 (+2:30)
    Time to update ~ :21 + (:10)
    Lines of config code 95
    Monthly Cost of Solution* ~$4.28

    View full-size slide

  26. ‹#›
    @tpryan
    03.2 Kubernetes

    View full-size slide

  27. ‹#›
    @tpryan
    # DB
    FROM mysql/mysql-server:5.6
    ADD sql/load.sql /docker-entrypoint-initdb.d/load.sql
    EXPOSE 3306

    View full-size slide

  28. ‹#›
    @tpryan
    # FRONTEND AND SERVICES
    FROM nginx-php-fpm
    COPY nginx.conf /etc/nginx/nginx.conf
    ADD www /var/www/
    # API
    FROM php:7.0-apache
    RUN apt-get update && apt-get install -y php5-mysqlnd
    RUN docker-php-ext-install mysqli
    RUN a2enmod rewrite && a2enmod headers && service apache2 restart
    COPY app/ /var/www/html/

    View full-size slide

  29. ‹#›
    @tpryan
    # UI
    FROM httpd:2.4
    COPY ui /usr/local/apache2/htdocs/ui
    COPY static /usr/local/apache2/htdocs/static

    View full-size slide

  30. ‹#›
    @tpryan
    Docker

    View full-size slide

  31. ‹#›
    @tpryan
    Docker

    View full-size slide

  32. ‹#›
    @tpryan
    Docker

    View full-size slide

  33. ‹#›
    @tpryan
    Docker Docker

    View full-size slide

  34. ‹#›
    @tpryan
    Docker
    Docker

    View full-size slide

  35. ‹#›
    @tpryan
    Docker Docker

    View full-size slide

  36. ‹#›
    @tpryan
    Docker

    View full-size slide

  37. ‹#›
    @tpryan
    That’s a lot to manage.

    View full-size slide

  38. ‹#›
    @tpryan
    4 3 1

    View full-size slide

  39. ‹#›
    @tpryan
    Kubernetes
    • Container Orchestration System
    • Open Source
    • Started by Google
    • Contributed to by others

    View full-size slide

  40. ‹#›
    @tpryan
    Requirements
    • Kubernetes Cluster
    • Images hosted in repository
    • Multiple running containers for
    • API
    • UI
    • Single instance of
    • DB
    • Disk for
    • DB
    • Public IP and load balancers for
    • API
    • UI

    View full-size slide

  41. ‹#›
    @tpryan
    Create a Cluster
    # Request Cluster
    gcloud container clusters create $(CLUSTER) --num-nodes 2 \
    --disk-size=200 —machine-type="g1-small"

    View full-size slide

  42. ‹#›
    @tpryan
    Build and host images
    # Build API image
    docker build -t gcr.io/projectname/locations-api "containers/api/."
    # Push API to repository
    gcloud docker push gcr.io/projectname/locations-api
    # Build UI image
    docker build -t gcr.io/projectname/locations-ui "containers/ui/."
    # Push UI to repository
    gcloud docker push gcr.io/projectname/locations-ui
    # Build DB image
    docker build -t gcr.io/projectname/locations-db "containers/db/."
    # Push API to repository
    gcloud docker push gcr.io/projectname/locations-db

    View full-size slide

  43. ‹#›
    @tpryan
    Running containers
    Pod Pod Pod Pod
    Replica Set
    Deployment

    View full-size slide

  44. ‹#›
    @tpryan
    Deployment
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    name: frontend-deployment
    spec:
    replicas: 4
    strategy:
    type: RollingUpdate
    template:
    metadata:
    labels:
    layer: ui
    spec:
    containers:
    - name: "frontend"
    image: "gcr.io/lamp-in-containers/locations-frontend"
    ports:
    - name: "http"
    containerPort: 80
    protocol: TCP
    Pod
    Replica Set
    Deployment

    View full-size slide

  45. ‹#›
    @tpryan
    Create a Deployment
    # Create deployment
    kubectl create -f deployment.yaml

    View full-size slide

  46. ‹#›
    @tpryan
    Containers as Pets
    Pod
    Stateful Set
    Persistent
    Volume
    Claim

    View full-size slide

  47. ‹#›
    @tpryan
    Stateful Set
    apiVersion: apps/v1beta1
    kind: StatefulSet
    metadata:
    name: db-set
    spec:
    serviceName: "mysql"
    replicas: 1
    template:
    metadata:
    labels:
    layer: db
    spec:
    terminationGracePeriodSeconds: 0
    containers:
    - name: db
    image: "gcr.io/lamp-in-containers/locations-db"
    ports:
    - containerPort: 3306
    name: mysql
    volumeMounts:
    - name: mysql-pvc
    mountPath: /var/lib/mysql
    volumeClaimTemplates:
    - metadata:
    name: mysql-pvc
    annotations:
    volume.alpha.kubernetes.io/storage-class: anything
    spec:
    accessModes: [ "ReadWriteOnce" ]
    resources:
    requests:
    storage: 1Gi
    Pod
    Persistent
    Volume
    Claim
    Stateful Set

    View full-size slide

  48. ‹#›
    @tpryan
    Exposing containers
    Pod Pod Pod Pod
    Service

    View full-size slide

  49. ‹#›
    @tpryan
    Deployment
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    name: frontend-deployment
    spec:
    replicas: 4
    strategy:
    type: RollingUpdate
    template:
    metadata:
    labels:
    layer: ui
    spec:
    containers:
    - name: "frontend"
    image: "gcr.io/lamp-in-containers/locations-frontend"
    ports:
    - name: "http"
    containerPort: 80
    protocol: TCP
    Pod
    Label

    View full-size slide

  50. ‹#›
    @tpryan
    Service
    apiVersion: v1
    kind: Service
    metadata:
    labels:
    name: frontend
    name: frontend
    spec:
    type: LoadBalancer
    ports:
    - port: 80
    targetPort: 80
    protocol: TCP
    selector:
    layer: ui
    Selector

    View full-size slide

  51. ‹#›
    @tpryan
    ui Pod
    ui deployment
    ui Pod
    ui Pod ui Pod
    ui service (public)
    api Pod
    api deployment
    api service (public)
    api Pod api Pod
    db statefulset
    db service (private)
    db Pod

    View full-size slide

  52. ‹#›
    @tpryan
    Demo: Kubernetes Version

    View full-size slide

  53. ‹#›
    @tpryan
    Details
    Time to build ~ 4:30
    Time to update ~ :24
    Lines of config code 266
    Monthly Cost of Solution* ~$43.63

    View full-size slide

  54. ‹#›
    @tpryan
    Is this the right way?

    View full-size slide

  55. ‹#›
    @tpryan
    04 Cloud Version

    View full-size slide

  56. ‹#›
    @tpryan
    DB
    API
    UI
    Managed SQL
    File Storage
    Leave on Kubernetes
    Switch to App Engine flexible

    View full-size slide

  57. ‹#›
    @tpryan
    Details
    Time to build ~ 8:00
    Time to update ~ 5:00
    Lines of config code 66
    Monthly Cost of Solution ~$47.83

    View full-size slide

  58. ‹#›
    @tpryan
    05 Conclusions
    Bring it home

    View full-size slide

  59. ‹#›
    @tpryan
    All in one boxes
    are a liability

    View full-size slide

  60. ‹#›
    @tpryan
    LAMP

    =
    L+A+M+P

    View full-size slide

  61. ‹#›
    @tpryan
    Technology
    Shift

    View full-size slide

  62. ‹#›
    @tpryan
    Thank You
    terrenceryan.com
    @tpryan
    This preso: http://bit.ly/tpryan-chaos

    View full-size slide