Tag: docker

  • ddready – แพ็ครวม django + bootstrap4 + crispy form + docker พร้อมใช้งาน

    สำหรับใครที่อยากจะลองพัฒนา Web Application ด้วย django web framework ผมได้รวบรวมเป็นชุดเริ่มต้น ซึ่งจะสามารถสร้าง Responsive Web และ มีแบบฟอร์มที่สวยงามด้วย crispy form มาเรียบร้อย ใช้งานได้ทั้ง แบบ Python บนเครื่อง และ แบบ Docker ลองทำตามดูได้ครับ

    ต้นแบบ มี Bootstrap 4 พร้อมใช้งาน
    มี Login Form มาให้เลย
    เข้ามาในส่วนของ Profile และ Logout ได้

    Repository

    สามารถเปิด URL ต่อไปนี้ เพื่อไป Download หรือ จะใช้ git clone ก็ได้

    https://github.com/nagarindkx/ddready.git

    https://gitlab.psu.ac.th/kanakorn.h/ddready.git

    จากนั้น ให้เปิด cmd ไปยังตำแหน่งที่ clone ลงมา

    สำหรับผู้ที่ติดตั้ง python อยู่แล้ว

    แล้วใช้คำสั่งต่อไปนี้

    pip install -r requirements.txt
    cd code
    cd main
    waitress-serve --listen *:8080 main.wsgi:application

    สำหรับผู้ที่จะใช้ Docker

    บน Windows ใช้คำสั่งต่อไปนี้

    set PROJECTNAME="projectname"
    set GCP-PROJECT-ID="gcp-project-id"
    set SERVICE="service"
    docker build --rm -f "Dockerfile" -t %PROJECTNAME%:dev .
    docker run -d -v %CD%\code:/code -p 8080:8080 --name %PROJECTNAME% %PROJECTNAME%:dev
    docker exec -it %PROJECTNAME% /bin/sh -c "[ -e /bin/bash ] && /bin/bash || /bin/sh"

    ทดสอบใช้งาน

    http://localhost:8080

    ในตอนต่อไป จะแนะนำวิธีการสร้าง แบบสอบถาม ทดแทนการใช้ Google Forms ครับ

  • [Share and Tell] การพัฒนา Web Application ด้วย django ด้วย Docker และการนำไปใช้งานบน Google Cloud Run

    1.ชื่องานที่ทำ

    การพัฒนา Web Application ด้วย django ด้วย Docker และการนำไปใช้งานบน Google Cloud Run

    2.เป้าหมายของงานที่ทำ

    • เพื่อประยุกต์ใช้งาน Docker ในการพัฒนางาน ทำให้สามารถลดความไม่เข้ากันของรุ่นของซอฟต์แวร์ หรือ ไลบรารี่ในเครื่องพัฒนาได้
    • เพื่อผลักดันการทำงานแบบ Containerize ในองค์กร เป็นต้นแบบการลดความซ้ำซ้อนของการสร้างเซิร์ฟเวอร์แบบ Virtual Machine ทำให้เกิดการใช้งานทรัพยากรอยู่จำกัดให้มีประสิทธิภาพมากขึ้น
    • เพื่อเป็นต้นแบบการทำงานแบบ Serverless ด้วย Google Cloud Run
    • เพื่อเป็นต้นแบบการใช้งาน LINE Front-End Framework ในการทำ Chatbot

    3.สภาพแวดล้อมที่เกี่ยวกับงานที่ทำ

    • Operating System: Windows 10
    • Editor: Visual Studio Code
    • Environment: Docker Oriented
    • Programming Language: python
    • Web Framework: django + crispy form + bootstrap + jQuery
    • Mobile Development: LINE with LIFF (และถ้าเตรียมทัน จะแนะนำการพัฒนา Mobile Application ด้วย Google Flutter)
    • Database: postgresql, Google BigQuery
    • Machine Learning: Google Vision API

    4.บรรยายสรุปสั้น ๆ ว่า ต้องใช้อะไรบ้างและทำอย่างไร (ไม่ต้องลงขั้นตอนละเอียด)

    • ติดตั้ง Microsoft Visual Studio Code (VS Code) และ Docker Extension
    • ใช้ docker เพื่อ pull image ของ Environment ที่ต้องการมา สร้างเป็น Container แล้ว พัฒนาลงไปในนั้น โดยใช้วิธี Bind mount volume เข้าไป แล้วใช้ VS Code เขียน และทำงานผ่าน Attach Shell
    • ติดตั้ง Python Package ที่เกี่ยวข้อง ได้แก่ django + crispy form + bootstrap + jQuery
    • พัฒนา Web Application ด้วย django ตั้งแต่การสร้าง Data Model, สร้างแบบฟอร์ม รวมถึงการสร้าง RESTFul API และ การส่งข้อมูลไปให้ Google Vision API ทำงาน แล้วส่งผลกลับมา
    • พัฒนา LINE Front-End Framework เพื่อใช้เป็น User Interface แบบ Chatbot
    • (ถ้าเตรียมทัน) จะสาธิตการพัฒนา Mobile Application ด้วย Google Flutter

    5.อื่น ๆ อยากบอกอะไรเพิ่มเติม เขียนเพิ่มได้

    Man – a being in search of meaning.

    Plato, 429 BC
  • แนวทางการพัฒนา Web Application ด้วย django จาก local docker สู่ Google Cloud Run

    ในการพัฒนาแอพพลิเคชั่น เราก็จะเจอปัญหานึงเสมอ ๆ คือ เวอร์ชั่น (Version) ของเครื่องมือที่ใช้ในการพัฒนานั้น แต่ละโปรเจคมีความแตกต่างกัน เช่น ในกรณีของ การพัฒนา Web Application ด้วย django web framework เราอาจจะอยากใช้ python รุ่นล่าสุด คือ 3.8 แต่ในขณะเดียวกัน เมื่อหลังบ้านต้องการไปติดต่อ Tensorflow 2.0 ซึ่งยังต้องใช้งานกับ Python 3.6 เป็นต้น วิธีแก้ไขปัญหาทั่วไปคือ ติดตั้ง package ‘virtualenv’ เพื่อให้การพัฒนาแต่ละโปรเจค มี Environment แตกต่างกันได้

    แต่จากการใช้งานจริง พบว่า สุดท้าย ตอนเอาไป Production บนเครื่อง Server ก็ต้องตามไปติดตั้งเครื่องมือ และรุ่นที่ถูกต้อง แม้ใน Python จะมีคำสั่ง pip install -r requirements.txt ก็ตาม แต่ก็ยังไปติดปัญหาว่า OS ของเครื่องที่จะ Production นั้น รองรับรุ่นของเครื่องมืออีกหรือไม่ด้วย

    แนวทางการใช้ Container ด้วย Docker จึงเป็นที่นิยม เพราะ เมื่อเราพัฒนาเสร็จแล้ว สามารถ Pack เข้าไปใน Container แล้วเอาไป Deploy ได้ โดย (แทบจะ) ไม่ต้องกังวลกับ Environment ปลายทาง อีกทั้ง ยังสามารถทดสอบ Environment ใหม่ ๆ ก่อนจะ Deploy ได้ด้วย เช่นการเปลี่ยนรุ่นของ Python เป็นต้น

    ในบทความนี้ จะนำเสนอ แนวทางการสร้าง docker container เพื่อใช้เป็นฐานในการพัฒนา django และ สามารถต่อยอด ติดตั้ง package อื่น ๆ ตามต้องการ ตั้งแต่ Development ไปจนถึง Deployment สู่ Serverless Environment อย่าง Google Cloud Run

    สร้าง Development Container

    เริ่มจาก สร้างโฟลเดอร์ใหม่ขึ้นมา ในนั้นมี 2 ไฟล์ คือ Dockerfile และ requirements.txt กับ โฟลเดอร์ ชื่อ code

    Dockerfile

    • ใช้ image ของ python เป็นรุ่น 3.7-slim ซึ่งตัดส่วนที่ไม่จำเป็นออกแล้ว (ไม่ใช้ alpine เนื่องจาก พบรายงานว่า แม้จะมีขนาดเล็กกว่า แต่ถูกจำกัดทรัพยากรบางอย่าง ทำให้ทำงานได้ช้ากว่า)
    • สร้าง /code แล้วเข้าไปใช้พื้นที่นี้ (เหมือนคำสั่ง mkdir /code ; cd /code อะไรประมาณนั้น)
    • สั่งให้ copy ไฟล์ requirements.txt ไปใช้ที่ root ( / )
    • จากนั้น Upgrade คำสั่ง pip เป็นรุ่นล่าสุด แล้ว ติดตั้ง package ตามที่กำหนดใน requirements.txt
    • เปิด Port 8080 ไว้ เพื่อใช้ในการทดสอบ และเป็นไปในแนวทางเดียวกับการใช้บน Google Cloud Run ต่อไป
    FROM python:3.7-slim
    WORKDIR /code/
    COPY requirements.txt /
    RUN pip install -U pip \
        && pip install -r /requirements.txt
    EXPOSE 8080

    requirements.txt

    ในการพัฒนา django เมื่อทำการติดตั้ง package ใดเพิ่มเติมด้วยคำสั่ง pip install ก็จะบันทึกรายการ พร้อมรุ่นของ package มาในไฟล์นี้ ในที่นี้ ใช้ django, guincorn และ whitenoise เป็นหลัก (จะมีรายการ dependency ติดเข้ามาด้วย)

    Django==2.2.6
    gunicorn==19.9.0
    pytz==2019.3
    sqlparse==0.3.0
    whitenoise==4.1.4

    จากนั้นใช้คำสั่งต่อไปนี้ สร้าง (build) docker image จากไฟล์ Dockerfile ข้างต้น โดยมี option ที่เกี่ยวข้องดังนี้

    docker build --rm -f "Dockerfile" -t mydjango:dev .
    • –rm เมื่อ build แล้ว ก็ลบ container ชั่วคราวทิ้ง
    • -f กำหนดว่าจะเรียกจากไฟล์ใด วิธีนี้ มีประโยชน์ เวลาที่จะต้องมี Dockerfile ทั้ง Development และ Production ในโฟลเดอร์เดียวกัน ซึ่งจำเป็นต้องใช้ชื่อไฟล์ที่แตกต่างกัน
    • -t เป็นตั้งชื่อ docker image และ ชื่อ tag
    • . คือ ให้ build จากตำแหน่งปัจจุบัน

    ขั้นตอนนี้จะได้ docker image ชื่อ mydjango และมี tag เป็น dev แล้ว้

    (ในที่นี้ พัฒนาบนเครื่องคอมพิวเตอร์ที่เป็น Windows 10 ซึ่ง %CD% จะให้ค่า Current Directory แบบ Absolute path ออกมา เช่นเดียวกับบน Linux ที่ใช้ $(pwd) )

    ต่อไป สั่ง run ด้วยคำสั่ง และ options ดังต่อไปนี้

    docker run --rm -it -p 8080:8080  -v %CD%\code:/code  mydjango:dev bash
    • –rm เมื่อจบการทำงาน ก็ลบ container ชั่วคราวทิ้ง
    • -it คือ interactive และเปิด TTY
    • -p เพื่อเชื่อม port 8080 จากภายนอก เข้าไปยัง port 8080 ภายใน container
    • -v เพื่อเชื่อม Volume หรือโฟลเดอร์ของเครื่อง host กับ /code ภายใน container ขั้นตอนนี้สำคัญ
    • bash ข้างท้าย เพื่อส่งคำสั่ง เรียก bash shell ขึ้นมา ซึ่งจะสัมพันธ์กับ -it ข้างต้น ทำให้สามารถใช้งาน shell ภายใน container ได้เลย

    ผลที่ได้คือ bash shell และ อยู่ที่ /code ภายใน container

    root@757bcbb07c7f:/code#

    ตอนนี้ เราก็สามารถสร้าง django ได้ตามปรกติแล้ว (คลิกดูตัวอย่างเบื้องต้น) แต่ในตัวอย่างนี้ จะเริ่มจากการสร้าง project ชื่อ main เพื่อใช้ในขั้นตอนต่อไป ด้วยคำสั่งต่อไปนี้

    django-admin startproject main
    cd main

    ในระหว่างการพัฒนา สามารถใช้คำสั่ง runserver โดยเปิด Port 8080 ใน container เพื่อทดสอบได้ดังนี้ (สอดคล้องตามที่อธิบายข้างต้น)

    python manage.py runserver 0.0.0.0:8080

    สร้าง Production Docker Image

    คำสั่งข้างต้น สามารถใช้ได้เฉพาะขั้นตอนการพัฒนา แต่เมื่อพัฒนาเสร็จแล้ว ควรใช้ Application Server แทน ในที่นี้จำใช้ gunicorn แต่ก่อนอื่น จะต้องปรับ Configuration ของ django ให้พร้อมในการ Deployment ก่อน

    main/settings.py

    # แก้ไข
    DEBUG = False
    ALLOWED_HOSTS = ['localhost','SERVICE-ID.run.app']
    
    # เพิ่ม
    MIDDLEWARE = [
        ...
        # Whitenoise
        'whitenoise.middleware.WhiteNoiseMiddleware',
        ...
    ]
    # เพิ่ม
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')

    มีเพิ่มเติมคือ whitenoise ซึ่งเป็น package สำหรับจัดการเกี่ยวกับการให้บริการ static file ในตัว มิเช่นนั้นจะต้องไปตั้งค่าใน Web Server ให้จัดการแทน

    เมื่อเราตั้งค่า DEBUG = False จะต้องกำหนด ALLOWED_HOSTS เสมอ ในที่นี้กำหนดให้เป็น localhost และ Production URL (SERVICE-ID.run.app) ที่จะเกิดขึ้นเมื่อสร้าง Service บน Google Cloud Run ครั้งแรกไปแล้ว (ค่อยกลับมาแก้ไขแล้ว Revision อีกครั้ง)

    ใช้คำสั่งต่อไปนี้เพื่อรวบรวม static files ต่าง ๆ มาไว้ในที่เดียวกัน ในที่นี้ คือที่ โฟลเดอร์ static

    python manage.py collectstatic

    ทดสอบ Production ด้วย gunicorn ด้วยคำสั่งต่อไปนี้

    gunicorn --bind 0.0.0.0:8080 main.wsgi

    ทดสอบเรียก Local Admin Console ( http://localhost:8080/admin ) ดู ถ้าเรียก static file เช่นกลุ่ม css ได้ ก็พร้อมสำหรับจะนำขึ้น Google Cloud Run ต่อไป

    สุดท้าย สร้าง Production Dockerfile และ สรุป requirements.txt ดังนี้

    Dockerfile.production

    FROM python:3.7-slim
    WORKDIR /code
    COPY ./code/* ./
    RUN pip install -U pip \
        && pip install -r requirements.txt
    EXPOSE 8080
    CMD [ "gunicorn","--bind","0.0.0.0:8080", "main.wsgi"]

    requirements.txt

    pip freeze > requirements.txt

    จากนั้น ใช้คำสั่งต่อไปนี้ เพื่อสร้าง Production Docker Image (ออกมาจาก Development Container shell ก่อน) โดยจะใช้ tag เป็น latest

    docker build -t mydjango:latest -f "Dockerfile.production" .

    ส่ง Production Docker Image ขึ้น Google Container Registry

    Google Cloud Run จะเรียกใช้ docker image ที่อยู่บน Google Container Registry เท่านั้น

    • เปิด Google Console
    • สร้าง Project ใหม่ และ ตั้งค่า Billing
    • เปิดใช้งาน Google Cloud Run และ Google Container Registry
    • ติดตั้อง Google Cloud SDK เพื่อให้สามารถ push ขึ้นได้ และ ยืนยันตัวตนด้วย Google Account

    แล้วใช้คำสั่งต่อไปนี้ เพื่อ tag

    docker tag mydjango:latest gcr.io/YOUR-GCP-PROJECT-ID/SERVICE-NAME
    • YOUR-GCP-PROJECT-ID เป็น Project ID ที่สร้างขึ้น
    • SERVICE-NAME ชื่อ service ที่จะสร้าง

    แล้วก็ push ขึ้น Google Container Registry

    docker push gcr.io/YOUR-GCP-PROJECT-ID/SERVICE-NAME

    สร้าง Google Cloud Run Service

    สร้าง Service โดยเลือก Image ที่ต้องการ

    เมื่อเสร็จแล้วจะได้ URL อย่าลืมเอาไปแก้ไขใน main/settings.py ในส่วนของ ALLOWED_HOSTS แทน SERVICE-ID.run.app

    จากนั้น build, tag และ push ขึ้น Google Cloud Registry อีกครั้ง แล้ว Deploy New Revision เป็นอันเรียบร้อย

    อันนี้เป็นการ Proof of Concept ในบทความต่อไป จะนำเสนอตัวอย่างการใช้งานจริงครับ

  • ELK #6 วิธีการติดตั้ง ELK และ Geoserver แบบ Docker ให้ทำงานร่วมกัน

    จาก ELK #5 การประยุกต์ใช้ ELK ในงานด้าน GIS และ การสร้าง Web Map Service (WMS) บน Geoserver ก็จะเห็นถึงการนำไปใช้เบื้องต้น

    >> ขอบคุณ คุณนพัส กังวานตระกูล สถานวิจัยสารสนเทศภูมิศาสตร์ทรัพยากรธรรมชาติและสิ่งแวดล้อม ศูนย์ภูมิภาคเทคโนโลยีอวกาศและภูมิสารสนเทศ (ภาคใต้) สำหรับความรู้มากมายครับ <<

     

    ต่อไปนี้ จะเป็นขั้นตอนการติดตั้ง ELK และ Geoserver แบบ Docker โดยผมได้สร้าง Github Repository เอาไว้ ซึ่งได้แก้ไขให้ระบบสามารถเก็บข้อมูลไว้ภายนอก

    Prerequisite

    1. ถ้าเป็น Windows ก็ต้องติดตั้ง Docker Toolbox หรือ Docker for Windows ให้เรียบร้อย
    2. ถ้าเป็น Linux ก็ติดตั้ง docker-ce ให้เรียบร้อย (เรียนรู้เกี่ยวกับ Docker ได้จาก ติดตั้ง docker 17.06.0 CE บน Ubuntu)

    ขั้นตอนการติดตั้ง

    1. สร้าง Folder ชื่อ Docker เอาไว้ในเครื่อง เช่นใน Documents หรือ จะเป็น D:\ หรืออะไรก็แล้วแต่
    2. เปิด Terminal หรือ Docker Quickstart Terminal จากนั้นให้ cd เข้าไปมา Folder “Docker” ที่สร้างไว้
    3. ดึง ELK ลงมา ด้วยคำสั่ง
      git clone https://github.com/deviantony/docker-elk.git
    4. ดึง Geoserver ลงมา ด้วยคำสั่ง (อันนี้ผมทำต่อยอดเค้าอีกทีหนึ่ง ต้นฉบับคือ https://hub.docker.com/r/fiware/gisdataprovider/)
      git clone https://github.com/nagarindkx/geoserver.git
    5. เนื่องจาก ไม่อยากจะไปแก้ไข Git ของต้นฉบับ เราจึงต้องปรับแต่งนิดหน่อยเอง
      ให้แก้ไขไฟล์ docker-elk/docker-compose.yml
      โดยจะเพิ่ม Volume  “data” เพื่อไป mount ส่วนของ data directory ของ Elasticsearch ออกมาจาก Containerแก้ไขจาก

      elasticsearch:
       build: elasticsearch/
       volumes:
       - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml

      เป็น

      elasticsearch:
       build: elasticsearch/
       volumes:
       - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
       - ./elasticsearch/data:/usr/share/elasticsearch/data
    6. สร้าง docker-elk/elasticsearch/data
      mkdir docker-elk/elasticsearch/data
    7. แก้ไขไฟล์ docker-elk/logstash/pipeline/logstash.conf ตามต้องการ เช่น ใส่ filter
      filter {
       csv {
         separator => ","
         columns => [
      	"cid","name","lname","pid","house","road","diagcode","latitude","longitude","village","tambon","ampur","changwat"
         ]
       }
       if [cid] == "CID" {
         drop { }
       } else {
         # continue processing data
         mutate {
           remove_field => [ "message" ]
         }
         mutate {
           convert => { "longitude" => "float" }
           convert => { "latitude" => "float" }
         }
         mutate {
           rename => {
             "longitude" => "[geoip][location][lon]"
             "latitude" => "[geoip][location][lat]"
           }
         }
       }
      }
    8. จาก Terminal ให้เข้าไปใน docker-elk แล้ว start ด้วยคำสั่ง
      cd docker-elk
      docker-compose up -d
    9. จาก Terminal ให้เข้าไปใน geoserver แล้ว start ด้วยคำสั่ง
      cd ../geoserver
      docker-compose up -d

    ถึงขั้นตอนนี้ ก็จะได้ ELK และ Geoserver ทำงานขึ้นแล้ว

    ELK: http://localhost:5601

    Geoserver: http://localhost:9090/geoserver/web

     

    ขั้นตอนต่อไป จะเป็นการ นำข้อมูลเข้า และ เชื่อ Kibana กับ Geoserver

    วิธีการนำข้อมูลเข้า Elasticsearch

    เนื่องจาก pipeline ของ Logstash กำหนดว่า จะรับข้อมูลทาง TCP Port 5000 จึงใช้วิธี netcat ไฟล์เข้าไป ด้วยคำสั่ง (ตัวอย่างนี้ ใช้ข้อมูลจากไฟล์ sample.csv)

    cat sample.csv | nc localhost 5000

    วิธีการดึง Map จาก Geoserver มาใช้งานใน Kibana

    ทำตามขั้นตอนที่กล่าวไว้ใน การสร้าง Web Map Service (WMS) บน Geoserver ซึ่งจะได้ URL ของ Layer Preview มา ประมาณนี้
    http://localhost:9090/geoserver/test/wms?service=WMS&version=1.1.0&request=GetMap&layers=test:hadyai_vil&styles=&bbox=631866.963048935,748605.6609660918,677997.0295239205,791055.6681053439&width=768&height=706&srs=EPSG:32647&format=application/openlayers

    ทำตามขั้นตอนที่กล่าวไว้ใน ELK #5 การประยุกต์ใช้ ELK ในงานด้าน GIS ในส่วนของ วิธีใส่ Map Server อื่น แล้วเอา URL นี้ไปใส่ และรายละเอียดเกี่ยวกับ Layer, version, format ตามที่กำหนดไว้ ก็จะสามารถเอา Map ที่เราต้องการ พร้อม Shape File มาใช้งานได้

    หวังว่าจะเป็นประโยชน์ครับ

  • เตาะแตะไปกับ Docker ตอนที่ 4 Stacks

    ในตอนที่แล้ว เราได้เรียนรู้แล้วว่าเราสามารถสร้าง swarm ที่เป็น cluster of machines ที่รัน Docker แล้ว deploy app เกิดเป็น container จำนวนหนึ่งที่กระจายอยู่ในเครื่อง 2 เครื่อง

    ในตอนนี้ เราจะทำความเข้าใจในส่วนบนสุดใน hierachy ของ distributed applications คือ

    Stack (ในตอนนี้เราอยู่ที่นี่)
    Service
    Container

    Stacks คือ การนำ service หลาย ๆ อันที่ทำงานร่วมกัน เช่น จาก web page จะมีการนำข้อมูลไปใส่ใน database หรือ สร้าง web page ที่จะนำข้อมูลไป plot เป็น กราฟ เป็นต้น

    ในตอนที่ 2 นั้นเราได้ใช้คำสั่งเกี่ยวกับ stack ไปบ้างแล้วเป็น single stack ที่รันอยู่บน single host ซึ่งไม่ค่อยเกิดขึ้นจริงใน production แต่เราอาจจะต้องทำ multiple services รันไปบน multiple machines ก็ได้ มาดูกันต่อไป

    ตัวอย่างเช่น เราจะเพิ่ม service เกี่ยวกับการสร้างภาพ diagram ของ swarm ของเรา ทำดังนี้
    1. ไปยังเครื่องที่มีไฟล์ docker-compose.yml ที่สร้างไว้แล้ว

    cd ~/myservice

    2. เปิดไฟล์ docker-compose.yml แล้วเพิ่ม service ชื่อ visualizer ดังนี้

    version: "3"
    services:
      web:
        # replace username/repo:tag with your name and image details
        image: woonpsu/docsdocker:part1
        deploy:
          replicas: 5
          restart_policy:
            condition: on-failure
          resources:
            limits:
              cpus: "0.1"
              memory: 50M
        ports:
          - "80:80"
        networks:
          - webnet
      visualizer:
        image: dockersamples/visualizer:stable
        ports:
          - "8080:8080"
        volumes:
          - "/var/run/docker.sock:/var/run/docker.sock"
        deploy:
          placement:
            constraints: [node.role == manager]
        networks:
          - webnet
    networks:
      webnet:

    ที่เพิ่มเข้ามา 1 service ก็คือ service ชื่อ visualizer เราจะได้เห็นว่ามีการใช้ key ต่าง ๆ เช่น volumes, placement และ constraints เพื่อเขียนเป็นข้อกำหนดตามที่ image ชื่อ dockersamples/visualizer:stable ต้องการให้ตั้งค่าเป็นอย่างนั้น

    3. รันคำสั่งนี้ซ้ำ

    docker stack deploy -c docker-compose.yml testlab

    4. เปิดเว็บเพจ http://node_ip:8080 โดยแทนที่ node_ip ด้วย IP ของเครื่องที่อยู่ใน swarm cluster

    ตัวอย่าง visualizer นี้เป็น standalone service ที่รันแบบไม่ได้ต้องใช้ service อื่นประกอบ ไม่ได้ขึ้นกับอะไรเลย ต่อไปเรามาสร้าง service ที่ต้องมีอันอื่นทำงานร่วมกัน (dependency) เราจะติดตั้ง database ชื่อ Redis เป็น service เพื่อเก็บ visitor counter ในทุกครั้งที่มีการเปิด web

    ทำดังนี้

    1. ตอนนี้เรายังอยู่ที่ directory ~/myservice
    2. แก้ไขไฟล์ docker-compose.yml อีกครั้ง แล้วเพิ่ม service ชื่อ redis ดังตัวอย่าง

    version: "3"
    services:
      web:
        # replace username/repo:tag with your name and image details
        image: woonpsu/docsdocker:part1
        deploy:
          replicas: 5
          restart_policy:
            condition: on-failure
          resources:
            limits:
              cpus: "0.1"
              memory: 50M
        ports:
          - "80:80"
        networks:
          - webnet
      visualizer:
        image: dockersamples/visualizer:stable
        ports:
          - "8080:8080"
        volumes:
          - "/var/run/docker.sock:/var/run/docker.sock"
        deploy:
          placement:
            constraints: [node.role == manager]
        networks:
          - webnet
      redis:
        image: redis
        ports:
          - "6379:6379"
        volumes:
          - ./data:/data
        deploy:
          placement:
            constraints: [node.role == manager]
        networks:
          - webnet
    networks:
      webnet:

    ในการเขียนข้อกำหนดของ service redis คือ เราจะเก็บ data ไว้ที่ไหนนั่นเอง ด้วย key ชื่อ volumes เนื่องจาก redis จะรันที่ swarm manager เสมอ ดังนั้นจึงเก็บ data ไว้ใน filesystem ของเครื่อง host จะไม่เก็บไว้ใน container เพราะหากเราลบ container จะทำให้ data เราหายไป และวิธีการที่จะทำให้ container เข้าถึง data ใน filesystem ก็คือ เราสร้าง /data ไว้ใน container ในขณะเดียวกันเราก็สร้าง ./data ไว้ใน directory ที่เก็บไฟล์ docker-compose.yml นี้ ซึ่งจะทำให้เมื่อ container ทำการเก็บ data ข้อมูลจะถูกนำมาเก็บไว้ใน filesystem ของ host (swarm manager) เสมอ ที่ ~/myservice/data

    2. สร้าง directory ./data

    mkdir ./data

    3. รันคำสั่งนี้ซ้ำ

    docker stack deploy -c docker-compose.yml testlab

    4. เปิดเว็บเพจ http://node_ip แทนที่ node_ip ด้วย IP ของเครื่องที่อยู่ใน swarm cluster คราวนี้เราจะเห็นเลขจำนวนของ visitor counter เปลี่ยนเพิ่มขึ้น

    5. และเมื่อเช็คดู visualizer ด้วย โดยเปิดเว็บเพจ http://node_ip:8080 อีกครั้งจะเห็นว่าในภาพ diagram จะมี service ชื่อ Redis เพิ่มขึ้นมาด้วย

     

    และทั้งหมดนี้ก็คือความหมายของคำว่า Stacks

     

    จากที่ผมเขียนเล่าให้ฟังมาทั้ง 4 ตอน เราก็มี app ชื่อ testlab ที่รันด้วย docker ให้บริการแล้ว แต่เป็นการใช้งาน local machine ของเราเอง ซึ่งเราเลือกใช้ Docker CE รันบน Ubuntu server ก็ใช้งานได้ในระดับเริ่มต้นแล้วครับ ก็น่าพอใจอยู่นะครับ

    สำหรับเรื่องราวลำดับถัดไปก็จะเป็นเรื่องเกี่ยวกับ Docker Cloud ซึ่งเป็นขั้นตอนต่อไปของการจัดการ app (Deploy your app) ที่อยู่บน local machine ของเราที่เตรียมไว้ไปยัง Cloud service providers ในลักษณะการทำงานแบบ Cloud นั่นคือ เครื่องบริการจะอยู่ในที่ต่าง ๆ ทั่วโลกได้นั่นเอง

    ผมมีข้อมูลเบื้องต้นว่า Docker Software สำหรับทำ Docker Cloud จะมีให้เลือกใช้งาน 3 ชนิด คือ Docker CE (Cloud provider), Docker Enterprise (Cloud provider) และ Docker Enterprise (On-premise) ก็เพื่อเป็นทางเลือกที่เราจะต้องจ่ายเงินในการได้รับบริการมากน้อยนั่นเอง

    การใช้ Docker Cloud เมื่อเลือก Software แต่ละชนิด ก็จะมีวิธีการเข้าใช้งานที่แตกต่างกัน เช่น Docker Enterprise จะมี software ชื่อ Docker Datacenter ซึ่งเป็นการจัดการที่ครบถ้วนและสะดวกรวดเร็วในการสร้าง “Dockerized App”

    การใช้งาน Docker Cloud จะมีคู่มือการ setup ของ Software แต่ละชนิด และแยกเป็นของแต่ละ provider เราจะต้องศึกษาเพิ่มเติมอีกมาก โดยส่วนตัวในขณะที่เขียนบทความนี้ก็ยังไม่ได้สมัครขอใช้ Cloud service providers ใด ๆ รวมทั้งยังไม่ได้ลองใช้

    ก่อนจบบทความในชุดนี้ อยากบอกว่ายังมีเรื่องราวอีกมากเลยเกี่ยวกับ Docker แต่ที่ได้แนะนำไปหน่อยนึงนี้ก็พอจะได้ไอเดียของ containers, images, services, swarms, stacks, scaling, load-balancing, volumes, and placement constraints จากที่ผมลองทดสอบและดัดแปลงเนื้อหาจาก Get Started ของ docs.docker.com ให้ทดสอบได้กับ Ubuntu server จำนวน 2 เครื่อง

    ขอบคุณที่ติดตามอ่านจนจบ

     

    References:
    Get Started https://docs.docker.com/get-started/

     

  • เตาะแตะไปกับ Docker ตอนที่ 3 Swarms

    ในตอนที่แล้ว เราทำ app ขึ้นมา และกำหนดค่าว่าจะรันเป็น service และเพิ่มจำนวน replicas เป็น 5x เพื่อให้บริการแบบ load-balancing แต่ทำบนเครื่อง host เครื่องเดียว
    เรียกได้อีกอย่างว่าเป็นการใช้ Docker แบบ a single-host mode แต่ในบทความนี้ เราจะเปลี่ยนให้ Docker ไปทำงานแบบ swarm mode โดยที่เมื่อเราใช้คำสั่ง docker deploy จะเป็นการ deploy app ของเราไปรันบน cluster ของเครื่องจำนวนหลาย ๆ เครื่อง

    ทำความเข้าใจกันสักเล็กน้อยเกี่ยวกับ Swarm Cluster
    swarm คือ เครื่องจำนวนหนึ่งที่รัน docker และได้ join เข้ามายัง cluster หลังจากนี้ในการรันคำสั่งใด ๆ ของ docker จะเรียกว่าเป็นการรันโดย swarm manager และเครื่องเหล่านี้ซึ่งอาจเป็น physical หรือ virtual machine ก็ได้ จะถูกเรียกว่า nodes

    swarm manager คือ เครื่องที่สามารถสั่งหรืออนุญาตให้เครื่องอื่น join เข้ามา และเครื่องนั้นจะถูกเรียกว่า workers เราจะเพิ่มเครื่อง workers เพื่อเป็นการนำมาช่วยในเรื่องของเพิ่มจำนวนหน่วยทำงานเท่านั้น และการสั่งคำสั่งเพื่อสร้าง app ยังคงต้องทำที่เครื่อง manager เหมือนเดิม

    การสร้าง swarm
    รันคำสั่ง docker swarm init เพื่อเปิดใช้ swarm mode ตอนนี้เราจะได้ swarm manager
    จากนั้นไปรันคำสั่ง docker swarm join ที่เครื่องอื่น ๆ เพื่อ join เข้า swarm เป็น workers
    ข้อควรระวัง
    จะต้องตั้งชื่อ hostname ให้กับเครื่องทุกเครื่องเพื่อให้มีชื่อที่แตกต่างกัน อย่าใช้ชื่อว่า ubuntu ทั้งหมด จะงง ผมเจอมาแล้ว จงแก้ไขไฟล์ /etc/hosts และ ไฟล์ /etc/hostname แล้ว reboot ก่อนเริ่มสร้าง swarm

    ผมทดสอบด้วย VM ใน Oracle VM VirtualBox อยู่ใน Windows 10 ตั้งค่า network adapter เป็น Bridge จึงใช้ IP ของที่ทำงานได้ ดังรูป


    เช่น
    เครื่องที่ 1 ให้ตั้งชื่อว่า docker-vm1 ซึ่งก็คือเครื่องที่ทำไว้ในบทความตอนที่ 1 และ 2 ต้องกลับไปเปลี่ยนชื่อ
    เครื่องที่ 2 ให้ตั้งชื่อว่า docker-vm2 ซึ่งก็คือเครื่อง ubuntu server ว่าง ๆ ตัวใหม่ในบทความตอนที่ 3 นี้ที่จะเป็น worker

    มาสร้าง two-machine cluster ให้เป็น swarm กัน
    1. ติดตั้ง docker ลงใน ubuntu server ตัวใหม่ (ชื่อเครื่อง docker-vm2)
    2. อยู่ที่เครื่องที่เป็น swarm manager (ชื่อเครื่อง docker-vm1) รันคำสั่ง

    docker swarm init

    จะได้ข้อความแจ้งว่า จะต้องใช้คำสั่งอย่างไรที่เครื่องที่จะ join เข้ามาเป็น worker (แบบ manual คือ จดบรรทัดที่ได้รับคำแนะนำเอาไว้)

    3. ไปที่เครื่อง ubuntu server ตัวใหม่นี้ พิมพ์คำสั่ง เพื่อ join เข้า swarm เป็น worker

    docker swarm join --token SWMTKN-1-5h25g0ywnp87ohk1pav8mon72zdmotqbbunj4bu88fq7sxm2go-57cqncfd2mzapxg2525q14nug 192.168.6.22:2377
    This node joined a swarm as a worker.

    หรือ

    ทำแบบใช้คำสั่งที่ซับซ้อนขึ้น ต้องใช้ข้อมูล ชื่อ username กับ เลข IP ของ swarm manager (docker-vm1) ในตัวอย่างนี้ username คือ mama และ IP คือ 192.168.6.22 โดยพิมพ์คำสั่งข้างล่างนี้ที่เครื่องที่จะเป็น worker (docker-vm2)

    $(ssh username@IP "docker swarm join-token worker" | grep token)

    4. กลับไปที่เครื่องที่เป็น swarm manager เพื่อรัน app

    cd ~/myservice
    docker stack deploy -c docker-compose.yml testlab

    5. ตรวจสอบว่า มีการสร้าง container ไว้ใน node ทั้งสอง รันคำสั่งนี้

    docker stack ps testlab

    ผลลัพธ์

    ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
    jcd1rfvnglnv testlab_web.1 woonpsu/docsdocker:part1 docker-vm1 Running Running 6 seconds ago
    lt98v35jw5y1 testlab_web.2 woonpsu/docsdocker:part1 docker-vm2 Running Running 6 seconds ago
    nz4gq3ew2f3t testlab_web.3 woonpsu/docsdocker:part1 docker-vm1 Running Running 5 seconds ago
    plnnmmmbm9hx testlab_web.4 woonpsu/docsdocker:part1 docker-vm2 Running Running 6 seconds ago
    jr5a0nhs38zf testlab_web.5 woonpsu/docsdocker:part1 docker-vm1 Running Running 6 seconds ago
    qvexlvmaw8ge testlab_web.6 woonpsu/docsdocker:part1 docker-vm2 Running Running 5 seconds ago

    เสร็จแล้วครับ ลองใช้งานจาก http://node_ip โดยแทนที่ node_ip ด้วย IP ของเครื่องที่อยู่ใน swarm cluster

    จะเห็นว่า หลังคำว่า Hostname: จะเป็นเลข container ID ที่อยู่ในเครื่อง docker-vm1 และ docker-vm2 ซึ่งเป็นการยืนยันว่า web page นี้เรียกมาจาก container ทั้ง 2 เครื่อง

    โดยใช้คำสั่งเพื่อดู container ID ที่เกิดขึ้นของแต่ละ node

    docker ps

    ดูข้อมูลของแต่ละ node ด้วยคำสั่ง (คำสั่งนี้ต้องใช้ที่ swarm manager)

    docker node ls
    docker node inspect docker-vm1 --pretty
    docker node inspect docker-vm2 --pretty

     

    เพิ่มเติม
    ขั้นตอนการเปลี่ยนชื่อเครื่องจากชื่อเดิม ubuntu เป็น docker-vm1
    mama@ubuntu:~$ sudo vi /etc/hosts
    [sudo] password for mama:
    127.0.0.1 localhost
    127.0.1.1 docker-vm1

    mama@ubuntu:~$ sudo vi /etc/hosts
    docker-vm1

    mama@ubuntu:~$ sudo reboot

     

    บันทึก output ที่ docker-vm1

    mama@docker-vm1:~$ docker swarm init
    Swarm initialized: current node (ph8gaptp7quk3cxv9lwt91c7n) is now a manager.

    To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5h25g0ywnp87ohk1pav8mon72zdmotqbbunj4bu88fq7sxm2go-57cqncfd2mzapxg2525q14nug 192.168.6.22:2377

    To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

    mama@docker-vm1:~$ cd ~/myservice
    mama@docker-vm1:~/myservice$ docker stack deploy -c docker-compose.yml testlab
    Creating network testlab_webnet
    Creating service testlab_web

    mama@docker-vm1:~/myservice$ docker stack rm testlab
    Removing service testlab_web
    Removing network testlab_webnet

    mama@docker-vm1:~/myservice$ docker stack deploy -c docker-compose.yml testlab
    Creating network testlab_webnet
    Creating service testlab_web

    mama@docker-vm1:~/myservice$ docker stack ps testlab
    ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
    jcd1rfvnglnv testlab_web.1 woonpsu/docsdocker:part1 docker-vm1 Running Running 6 seconds ago
    lt98v35jw5y1 testlab_web.2 woonpsu/docsdocker:part1 docker-vm2 Running Running 6 seconds ago
    nz4gq3ew2f3t testlab_web.3 woonpsu/docsdocker:part1 docker-vm1 Running Running 5 seconds ago
    plnnmmmbm9hx testlab_web.4 woonpsu/docsdocker:part1 docker-vm2 Running Running 6 seconds ago
    jr5a0nhs38zf testlab_web.5 woonpsu/docsdocker:part1 docker-vm1 Running Running 6 seconds ago
    qvexlvmaw8ge testlab_web.6 woonpsu/docsdocker:part1 docker-vm2 Running Running 5 seconds ago

    บันทึก output ที่ docker-vm2

    mama@docker-vm2:~$ docker swarm join --token SWMTKN-1-5h25g0ywnp87ohk1pav8mon72zdmotqbbunj4bu88fq7sxm2go-57cqncfd2mzapxg2525q14nug 192.168.6.22:2377
    This node joined a swarm as a worker.

     

    References:
    Get Started https://docs.docker.com/get-started/

  • เตาะแตะไปกับ Docker ตอนที่ 2 Services (Scale and load-balancing)

    ในตอนนี้ เราก็จะขยับขึ้นไปอีก 1 level ใน hierachy ของ distributed application
    Stack
    Service (ในตอนนี้เราอยู่ที่นี่)
    Container

    Service 1 Service รันจาก 1 image โดยระบุ port ที่จะใช้ กำหนดจำนวน contrainer ที่จะรัน โดยที่เราสามารถเพิ่มจำนวน (Scale) service ให้รองรับโหลดมาก ๆ ได้ เราจะทำได้โดยการเขียน docker-compose.yml

    เริ่มต้นโดยการสร้างไฟล์นี้ไว้ใน directory ว่าง

    mkdir myservice
    cd myservice

    สร้างไฟล์ชื่อ docker-compose.yml ด้วยเอดิเตอร์ vi ดังนี้

    vi docker-compose.yml

    คัดลอกเนื้อหาจากตัวอย่าง https://docs.docker.com/get-started/part3/#your-first-docker-composeyml-file

    โดยแก้ไขในบรรทัด
    image: username/repository:tag
    ให้เป็น
    image: woonpsu/docsdocker:part1
    ดังนี้

    version: "3"
    services:
      web:
        # replace username/repo:tag with your name and image details
        image: woonpsu/docsdocker:part1
        deploy:
          replicas: 5
          resources:
            limits:
              cpus: "0.1"
              memory: 50M
          restart_policy:
            condition: on-failure
        ports:
          - "80:80"
        networks:
          - webnet
    networks:
      webnet:

    สำหรับสิ่งที่อยู่ในไฟล์ docker-compose.yml นั้น อธิบายอย่างคร่าว ๆ ก็คือจะบอกว่า ดึง image ที่เรา upload ไว้จากบทความในตอนที่แล้ว ซึ่งก็คือ woonpsu/docsdocker:part1 จะสร้าง container กี่อันจาก image นี้ และจะใช้ %CPU เท่าไร จะใช้ Port หมายเลขอะไร มี network อะไรสำหรับทำ load-balancing (a load-balanced overlay network)

    สังเกตในไฟล์ Compose จะเห็นการกำหนดค่า version: เป็น “3” คือความหมายที่เกี่ยวกับเรื่อง compatibility สรุปอย่างสั้น ๆ คือ เราจะทำต่อไปยังตัวอย่างการใช้งาน docker swarm จึงใช้ค่า version เป็น 3

    ตอนนี้ก็มารัน load-balancing กัน โดยเริ่มต้นสร้างหรือเข้าร่วม swarm

    docker swarm init

    หากไม่ run คำสั่งนี้ ก็จะพบ error “this node is not a swarm manager.”

    ตั้งชื่อ app เช่น testlab เป็นต้น แล้วพิมพ์คำสั่งดังนี้

    docker stack deploy -c docker-compose.yml testlab

    ตรวจดูรายการ

    docker stack ps testlab

    ถึงตรงนี้ เราลองไปที่ http://server_ip แล้วกด F5 ซ้ำ ๆ จะเห็นว่า หน้าเว็บที่แสดงนั้นถูกดึงมาจาก container แต่ละตัว สังเกตที่หลังคำว่า Hostname จะเป็นเลข container ID ที่ให้บริการ

    หากเราต้องการเพิ่มจำนวน replicas ก็เข้าไปแก้ไขไฟล์ docker-compose.yml แล้วรันคำสั่ง deploy อีกรอบ จะพบจำนวน container ที่เพิ่มขึ้น

    หากต้องการเลิกใช้ app ชื่อ testlab ดังนี้
    docker stack rm testlab
    หากต้องการออกจาก swarm ด้วยคำสั่ง
    docker swarm leave --force

     

    ในตอนต่อไป เราจะได้เรียนรู้การใช้งานจริง ๆ กันหละ คือรัน app เป็น swarm บน cluster ของเครื่องมากกว่า 1 เครื่อง

     

    ข้างล่างนี้คือบันทึก output ของคำสั่งที่ใช้

    mama@ubuntu:~/myservice$ docker swarm init
    Swarm initialized: current node (bph2py545b2uglv68vmy4qnno) is now a manager.

    To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5h25g0ywnp87ohk1pav8mon72zdmotqbbunj4bu88fq7sxm2go-57cqncfd2mzapxg2525q14nug 192.168.6.22:2377

    To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

     

    mama@ubuntu:~/myservice$ docker stack deploy -c docker-compose.yml testlab
    Creating network testlab_webnet
    Creating service testlab_web

     

    mama@ubuntu:~/myservice$ docker stack ps testlab
    ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
    dqzbgtw1cv8b testlab_web.1 woonpsu/docsdocker:part1 ubuntu Running Running about a minute ago
    il7xlem822wf testlab_web.2 woonpsu/docsdocker:part1 ubuntu Running Running about a minute ago
    j1scjgeprrr2 testlab_web.3 woonpsu/docsdocker:part1 ubuntu Running Running about a minute ago
    k1qclfcosako testlab_web.4 woonpsu/docsdocker:part1 ubuntu Running Running about a minute ago
    o9plgp6ghv9f testlab_web.5 woonpsu/docsdocker:part1 ubuntu Running Running about a minute ago

     

    mama@ubuntu:~/myservice$ docker node ls
    ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
    ftg9wrcso3tnpimg5isj1hz6j * ubuntu Ready Active Leader

     

    mama@ubuntu:~/myservice$ docker stack rm testlab
    Removing service testlab_web
    Removing network testlab_webnet

     

    mama@ubuntu:~/myservice$ docker swarm leave --force
    Node left the swarm.

     

    mama@ubuntu:~/myservice$ docker node ls
    Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.

     

    References:
    Get Started https://docs.docker.com/get-started/

  • เตาะแตะไปกับ Docker ตอนที่ 1 Containers (Build, Ship and Run)

    ผมอ่าน Get Started จาก docs.docker.com แล้วคิดว่าพอเข้าใจว่า docker ใช้งานอย่างไรมากขึ้นในแง่ความหมายของ Docker – Build, Ship, and Run Any App, Anywhere ที่เป็นจุดเด่น หลังจากอ่านจบที่ผมเขียนในตอนที่ 1 นี้ ก็น่าจะเข้าใจคำว่า Container และในตอนถัดไปก็จะนั้นจะเล่าถึงความหมายของคำว่า Service และ Stack ตามลำดับ

    ในการทดสอบเพื่อเขียนบทความ ผมได้ติดตั้ง docker บน ubuntu server 64 bit Xenial 16.04 (LTS) และรุ่นของ Docker ที่ใช้คือ Docker version 17.06.0-ce, build 02c1d87 ซึ่ง Docker Software มี 2 ชนิด คือ Community Edition (CE) และ Enterprise Edition (EE) ให้เลือกใช้

    [Installation]
    วิธีติดตั้ง Docker บน ubuntu นั้นจะมีคำแนะนำสำหรับรุ่น docker-ce คือเริ่มต้นจากการ SET UP THE REPOSITORY เสร็จแล้วจึง INSTALL DOCKER CE อย่างคร่าว ๆ ก็ใช้คำสั่งดังนี้

    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
    sudo apt-get update
    sudo apt-get install -y docker-ce

    วิธีทำให้ไม่ต้องใส่คำว่า sudo หน้าคำสั่ง Docker ทุกครั้ง ดังนี้

    sudo usermod -aG docker ${USER}
    su - ${USER}
    id
    exit

    logout แล้ว login กลับเข้ามาใหม่ พิมพ์คำสั่ง id จะเห็นว่าอยู่ใน group docker ด้วยแล้ว

    ต่อไป Verify ว่า Docker CE ถูกติดตั้งสำเร็จโดยการรัน hello-world image

    docker run hello-world

    ตรวจสอบเวอร์ชั่น

    docker --version

    [Containers]
    ตอนนี้ก็ได้เวลาเตาะแตะแบบ docker เราจะเริ่มกันที่ส่วนล่างสุดของ hierarchy ของการสร้าง app 3 ส่วน นั่นคือ container
    Stack
    Services
    Container (เรากำลังอยู่ที่นี่)

    การสร้าง app เราจะทำ container ขึ้นมาจากสิ่งที่เรียกว่า Dockerfile คือไฟล์ที่เขียนข้อกำหนดว่า container จะมีสภาพแวดล้อมเป็นอะไรบ้าง โดยต้องเริ่มต้นจากสร้าง directory ว่าง ๆ บน host (จะเรียก ubuntu server ที่ติดตั้ง docker ไว้ว่า host) แล้วสร้างไฟล์ชื่อ Dockerfile ด้วยเอดิเตอร์ที่ถนัด เช่น vi หรือ nano เป็นต้น และถ้าภายในไฟล์นี้อ้างถึงไฟล์อื่น ๆ ก็สร้างไว้ให้ครบด้วยนะ ผมจะใช้ตัวอย่างจาก docs.docker.com ครับ

    mkdir myhello
    cd myhello

    สร้างไฟล์ชื่อ Dockerfile ด้วยเอดิเตอร์ vi ดังนี้

    vi Dockerfile

    คัดลอกเนื้อหาจากตัวอย่าง https://docs.docker.com/get-started/part2/#dockerfile

    # Use an official Python runtime as a parent image
    FROM python:2.7-slim
    
    # Set the working directory to /app
    WORKDIR /app
    
    # Copy the current directory contents into the container at /app
    ADD . /app
    
    # Install any needed packages specified in requirements.txt
    RUN pip install -r requirements.txt
    
    # Make port 80 available to the world outside this container
    EXPOSE 80
    
    # Define environment variable
    ENV NAME World
    
    # Run app.py when the container launches
    CMD ["python", "app.py"]

    ภายในไฟล์ Dockerfile มีอ้างถึงไฟล์ชื่อ requirements.txt และ app.py
    สร้างไฟล์ชื่อ requirements.txt ด้วยเอดิเตอร์ vi ดังนี้

    vi requirements.txt

    คัดลอกเนื้อหาจากตัวอย่าง https://docs.docker.com/get-started/part2/#requirementstxt

    Flask
    Redis

    แล้วสร้างไฟล์ชื่อ app.py ด้วยเอดิเตอร์ vi ดังนี้

    vi app.py

    คัดลอกเนื้อหาจากตัวอย่าง https://docs.docker.com/get-started/part2/#apppy

    from flask import Flask
    from redis import Redis, RedisError
    import os
    import socket
    
    # Connect to Redis
    redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
    
    app = Flask(__name__)
    
    @app.route("/")
    def hello():
     try:
      visits = redis.incr("counter")
     except RedisError:
      visits = "<i>cannot connect to Redis, counter disabled</i>"
    
     html = "<h3>Hello {name}!</h3>" \
     "<b>Hostname:</b> {hostname}<br/>" \
     "<b>Visits:</b> {visits}"
     return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
    
    if __name__ == "__main__":
     app.run(host='0.0.0.0', port=80)

    ดังนั้นในตอนนี้ ใน directory ของเราจะมีไฟล์ 3 ไฟล์ คือ

    Dockerfile requirements.txt app.py

     

    ต่อไปเป็นการสร้าง image ชื่อ myhello จากไฟล์ 3 ไฟล์นั้น จะตั้งชื่อเป็นอะไรก็ได้นะ

    docker build -t myhello .

    จุด (.) หลังคำว่า myhello คือ อ้างถึงไฟล์ทุกไฟล์ใน current directory ในการสร้าง image

    ตอนนี้เราก็จะได้ docker image ชื่อ myhello อยู่ในเครื่องของเรา เรียกว่า local Docker image registry ดูรายชื่อด้วยคำสั่งนี้

    docker images

    ตัวอย่างผลลัพธ์

    REPOSITORY TAG IMAGE ID CREATED SIZE
    myhello latest 9748e0bc3640 7 seconds ago 194MB
    python 2.7-slim 4f57b96607d2 6 hours ago 182MB
    hello-world latest 1815c82652c0 4 weeks ago 1.84kB

     

    ต่อไปก็เป็นการสั่งทำงาน app ที่เราสร้างเป็น image แล้วนั้น คือ docker run
    เช่น ระบุว่าจะใช้ port หมายเลข 4000 ของ host ไปยัง port หมายเลข 80 ของ container

    docker run -p 4000:80 myhello
     * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

    ผลของการทำคำสั่ง docker run คือ เราจะได้ container ที่รันให้บริการ

    ตอนนี้ เครื่องที่ติดตั้ง docker นี้มี IP บน net เดียวกับเครื่องอื่น ๆ ก็จะเรียกดูข้อมูลทางหน้าเว็บจากเครื่องใดก็ได้ใน net นั้น เช่น http://server_ip:4000
    จะได้ผลลัพธ์

    หากในเครื่อง host ยังไม่ได้ติดตั้งพวก apache2 หรือ nginx web server อยู่ก่อน (ซึ่งพวกนี้จะมีค่า default ที่ใช้ port หมายเลข 80 สำหรับ http) แล้วหละก็ เราจะสามารถระบุว่าจะใช้ port หมายเลข 80 ของ host ไปยัง port หมายเลข 80 ของ container ได้ โดยเขียนคำสั่งเป็นแบบนี้
    docker run -p 80:80 myhello
    และเมื่อเรียกดูข้อมูลทางหน้าเว็บ ก็เขียนสั้น ๆ แบบนี้ http://server_ip ซึ่งไม่ต้องระบุ port

    แต่ตอนนี้สังเกตดูว่า เราต้องกด Ctrl + C เพื่อออกจาก app ที่รันอยู่เพื่อกลับไป command prompt เพราะว่า app ของเรารันอยู่ใน foreground

     

    ต่อไปเป็นการสั่งรัน app ใน background เหมือนกับการสั่ง service ทั่วไปรันรอให้บริการ
    (run the app in the background, in detached mode) ทำดังนี้

    docker run -d -p 4000:80 myhello

    จะได้ container ID เลขยาว ๆ หลาย ๆ ตัว และกลับออกมาที่ command prompt เลย ตัวอย่างเช่น
    773ddb78be11ec557405817ad871650e18f2ef82fbb62b512d47179de9c4486a

    ตรวจสอบว่าเรามี container ใดรันอยู่บ้าง ด้วยคำสั่ง

    docker ps

    ผลลัพธ์

    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    773ddb78be11 myhello "python app.py" About a minute ago Up About a minute 0.0.0.0:4000->80/tcp romantic_goldberg

    ตอนนี้ให้เราหยุดรัน app โดยการอ้างถึง CONTAINER ID แบบนี้:

    docker stop 773ddb78be11

     

    เมื่อมาถึงตรงจุดนี้ เราได้ทำขั้นตอน “Build” เรียบร้อยแล้ว ยังเหลือ “Ship” และ “Run” มาดูกันต่อ

    Ship ก็คือการที่เราจะนำ image ที่เราสร้างขึ้นนี้อัปโหลดไปไว้บน Docker’s public registry เพื่อ share image ให้ใคร ๆ ก็นำไปใช้ได้จากทุกที่และทุกเวลา จากนั้นเมื่อเราต้องการจะรัน app นี้ เราก็ไปหาเครื่อง Host มาสักตัว ติดตั้ง docker แล้ว run image ที่เราอัปโหลดไปไว้นั้น

    เรามาดูขั้นตอนการ share image ซึ่งเราจะต้องมี User ID ที่ cloud.docker.com จากนั้นใช้คำสั่ง

    docker login

    ก่อนที่เราจะอัปโหลด image ของเราขึ้นไป ให้เราทำการ Tag the image เพื่อเป็นข้อมูลสั้น ๆ ในรูปแบบ
    username/repository:tag เพื่ออธิบายหรือใส่เวอร์ชั่นของ image ก็ได้ และมีรูปแบบคำสั่งคือ
    docker tag image username/repository:tag
    ตัวอย่างเช่น

    docker tag myhello woonpsu/docsdocker:part1

    ตรวจสอบด้วยคำสั่ง

    docker image

    อัปโหลด image (เรียกวิธีการนี้ว่า Publish the image) ในรูปแบบคำสั่ง
    docker push username/repository:tag
    ตัวอย่างเช่น

    docker push woonpsu/docsdocker:part1

     

    เมื่อมาถึงตรงจุดนี้ เราได้ทำขั้นตอน “Build“, “Ship” เรียบร้อยแล้ว เหลือขั้นตอน “Run” มาดูกันต่อ

    จากนี้ไป เราก็สามารถใช้คำสั่ง docker run เพื่อดึง และ รัน app จาก image ที่ฝากไว้บน Public Registry เราจะรันที่เครื่องใด ๆ ก็ได้ (Pull and run the image from the remote repository) ด้วยคำสั่งนี้

    docker run -p 4000:80 woonpsu/docsdocker:part1

    เมื่อมาถึงตรงจุดนี้ เราได้ทำขั้นตอน “Build“, “Ship” และ “Run” เรียบร้อยแล้ว

     

    ในตอนถัดไป เราจะเรียนรู้ว่าวิธีการเพิ่มจำนวน application ของเราโดยการรัน container ในสิ่งที่เรียกว่า service
    โปรดติดตามตอนต่อไป

     

    References:
    Get Started https://docs.docker.com/get-started/
    Get Docker CE for Ubuntu https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/
    How To Install and Use Docker on Ubuntu 16.04 https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04

  • วิธีสร้าง Docker Swarm

    หลายคนคงจะได้ใช้งาน Docker มาแล้ว แต่อาจจะลองใช้งานบน 1 Physical Server กล่าวคือ สร้างหลายๆ container อยู่บนเครื่องคอมพิวเตอร์เครื่องเดียว

    >> ติดตั้ง docker 17.06.0 CE บน Ubuntu

    Docker Swarm เป็นเครื่องมือที่ติดมากับ Docker รุ่นตั้งแต่ 1.12 เป็นต้นมา (ปัจจุบัน ชื่อรุ่นคือ 17.06.0 CE) ก่อนอื่น มาตรวจสอบว่า เรากำลังใช้ Docker รุ่นไหนด้วยคำสั่ง

    docker version

    Docker Swarm ประกอบไปด้วย Master Node และ Worker Node โดยใน 1 Swarm สามารถมีได้ หลาย Master และ หลาย Worker

    ในตัวอย่างนี้ จะแสดงการเชื่อมต่อ Ubuntu 16.04 ทั้งหมด 4 เครื่อง เข้าไปใน 1 Swarm (ทุกเครื่องสามารถเชื่อมต่อถึงกันได้ และต่อ Internet ได้ และติดตั้ง Docker ไว้เรียบร้อยแล้ว)

    [Master Node]

    1. ใช้คำสั่งต่อไปนี้ เพื่อสร้าง Swarm Master Node บนเครื่องนี้
      docker swarm init

      จะได้ผลดังภาพ

    2. ให้ Copy คำสั่ง ตั้งแต่ “docker swarm join –token …..” เป็นต้นไป เพื่อเอาไปสั่งให้ Work Node เข้ามา Join ใน Swarm

    [Worker Node]

    นำคำสั่งจาก Master Node ข้างต้น มาใช้

    จากนั้น ทำเช่นเดียวกันนี้ กับ Worker Node ที่เหลือ (และหากในอนาคตต้องการเพิ่ม Worker Node อีก ก็เอาคำสั่งนี้ไปใช้)

    ตัวอย่างการนำไปใช้
    (หากสนใจ ลอง git clone https://github.com/nagarindkx/elk ไปดูได้)

    1. สร้าง “Stack File” ซึ่งจะคล้ายๆกับการสร้าง Compose File แต่มีรายละเอียดต่างกันเล็กน้อย
    2. ใช้คำสั่ง ต่อไปนี้ เพื่อสร้าง Stack ของ Software ให้กระจายไปใน Worker Nodes
      docker stack deploy -c $(pwd)/elk.yml k1

      ผลที่ได้

    3. วิธีดูว่า ตอนนี้มี Stack อะไรอยู่บ้าง ใช้คำสั่ง
      docker stack ls
      ผลที่ได้
    4. วิธีดูว่า ตอนนี้มีการไปสร้าง Container ไว้ที่ใดใน Docker Swarm บ้าง ด้วยคำสั่ง (สั่งการได้บน Master Node เท่านั้น)
      docker service ps k1
      ผลที่ได้
    5. ต่อไป อยากจะเพิ่ม Scale ให้บาง Service ใน Stack ใช้คำสั่ง
      docker service scale k1_elasticsearch=4
      ผลที่ได้

     

    ในตัวอย่างนี้ จะสามารถใช้งาน Kibana ซึ่งจากภาพจะเห็นได้ว่า อยู่ที่ Node “docker04” แต่เราสามารถเรียกใช้งานได้ที่ Master Node “docker01” ได้เลย เช่น Master Node มี IP เป็น 192.168.xxx.111 ที่ port 5601 ได้

    คร่าวๆแค่นี้ก่อนครับ