Tag: testlab

  • เตาะแตะไปกับ 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