Category: docker

  • 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 ตอนที่ 6 Harbor (an enterprise-class registry server)

    จากตอนที่แล้ว เราสามารถทำ Docker registry แบบง่าย ๆ โดยใช้ basic authentication (htpasswd) สำหรับในตอนนี้ เราจะมาทำ Harbor ซึ่งเป็น docker registry ที่มีผู้พัฒนาให้เราสร้างได้ง่ายมากขึ้น และเพิ่มขีดความสามารถให้เราเชื่อมต่อ LDAP/AD เพื่อทำ user authentication และอื่น ๆ อีกมาก

    ผมทดสอบกับ ubuntu server เวอร์ชั่น 16.04 สมมติว่า เราจดชื่อ FQDN ไว้คือ docker-registry.in.psu.ac.th

    ให้แก้ไข hostname ให้ถูกต้อง ดังนี้

    $ sudo vi /etc/hostname
    docker-registry.in.psu.ac.th

    สั่งรีบูตเครื่อง

    $ sudo reboot

     

    ติดตั้ง ssl certificate เนื่องจากเรายังไม่ได้ใช้ certificate ที่ซื้อมา เราจะใช้ ssl-cert-snakeoil ไปก่อนก็ได้

    $ sudo apt install ssl-cert

    ก่อนจะทำในขั้นตอนต่อไป ขณะนี้ได้ติดตั้ง docker ไว้แล้ว วิธีติดตั้งดูจากเรื่อง เตาะแตะไปกับ Docker ตอนที่ 1 Containers (Build, Ship and Run)

    หลังจากติดตั้ง docker แล้ว ในการติดตั้ง harbor จะต้องติดตั้ง docker-compose เพิ่ม ทำดังนี้

    $ sudo apt install docker-compose

     

    ติดตั้ง harbor

    $ wget https://github.com/vmware/harbor/releases/download/v1.1.2/harbor-offline-installer-v1.1.2.tgz
    $ tar xvf harbor-offline-installer-v1.1.2.tgz
    $ cd harbor/

    แก้ไขค่า hostname, ui_url_protocol และ ssl_cert ดังนี้

    $ vi harbor.cfg
    #set hostname
     hostname = docker-registry.in.psu.ac.th
     #set ui_url_protocol
     ui_url_protocol = https
     ......
     #The path of cert and key files for nginx, they are applied only the protocol is set to https 
     ssl_cert = /etc/ssl/certs/ssl-cert-snakeoil.pem
     ssl_cert_key = /etc/ssl/private/ssl-cert-snakeoil.key

    ทำการติดตั้ง

    sudo ./install.sh

     

    เข้า web browser ไปยัง https://docker-registry.in.psu.ac.th


    login เข้าเป็น username admin ในครั้งแรกจะได้รหัสผ่านคือ Harbor12345
    แล้วตั้งค่า configuration ตัวอย่างเช่น Authentication ด้วย OpenLDAP แบบ Anonymous Bind ที่เครื่อง ldap server IP คือ 10.168.16.18 ดังนี้

    เข้าเมนูตามลำดับดังนี้ Administration > Configuration > Authentication
    ใส่ข้อมูลเฉพาะช่องที่แนะนำ

     

    Auth Mode: LDAP

    LDAP URL*: ldaps://10.168.16.18

    LDAP Base DN*: dc=example,dc=com

    LDAP UID*: uid

     

    หากเราใช้ AD และเป็น LDAPS แบบ simple bind จะต้องใส่ข้อมูลใน 2 บรรทัดนี้ด้วย เช่น ldaps ของที่ทำงานของผม (psu.ac.th)

    LDAP Search DN:

    LDAP Search Password:

    LDAP UID*: sAMAccountName

     

    กด SAVE แล้วกด TEST LDAP SERVER เพื่อดูว่า connect ldap server ได้หรือไม่

    ให้ logout ออกจาก username admin

     

    ให้ login ด้วย username ของ ldap server เช่น lulu

    แล้วสร้าง project เช่น job1 (ผมพบว่า หากเรามี user มาใช้ร่วมด้วยหลาย ๆ คน ให้ตั้งชื่อ project ให้ตรงกับชื่อ username เพราะว่า user แต่ละคนไม่สามารถใช้ชื่อ project ซ้ำกันกับของอีกคนได้)

    ผลลัพธ์

     

    ที่ Harbor registry อาจจำเป็นต้องตั้งค่า TLS_REQCERT เป็น never เพื่อติดต่อกับ ldaps server เฉพาะบางเครื่อง ให้เพิ่มคำสั่งดังนี้

    $ sudo sh -c 'echo "TLS_REQCERT never" >> /etc/ldap/ldap.conf'

     

    หากมีการแก้ไข configuration files สำหรับ Harbor ให้ทำขั้นตอนตามลำดับดังนี้

    $ docker-compose down 
    $ vi harbor.cfg
    $ sudo ./prepare
    $ docker-compose up -d

     

    ที่เครื่อง client หากเจอแบบนี้ ก็เพราะว่า ไม่ได้ใช้ certificates จริง

    $ docker login docker-registry.in.psu.ac.th
    Username: lulu
    Password:
    Error response from daemon: Get https://docker-registry.in.psu.ac.th/v2/: x509: certificate signed by unknown authority

    เนื่องจากไม่ได้ใช้ certificates จริง เราต้องทำที่ client ทุกเครื่อง ด้วยคำสั่งเหล่านี้ ก่อนที่จะใช้งานกับ Harbor Registry ได้ ทำดังนี้

    $ export DOMAIN_NAME=docker-registry.in.psu.ac.th
    $ openssl s_client -connect $DOMAIN_NAME:443 -showcerts </dev/null 2>/dev/null | openssl x509 -outform PEM | sudo tee /usr/local/share/ca-certificates/$DOMAIN_NAME.crt
    $ sudo update-ca-certificates

    จากนั้นต้อง restart docker ด้วยคำสั่งดังนี้

    $ sudo service docker restart

    ทดสอบ login อีกครั้ง

    $ docker login docker-registry.in.psu.ac.th
    Username: lulu
    Password:
    Login Succeeded

    ทดสอบ push image
    ไป pull image จาก hub.docker.com สัก 1 อัน เช่น

    $ docker pull woonpsu/docsdocker:part1

    ใส่ tag ก่อน push image ไปยัง Harbor registry ที่ project ชื่อ job1 ตั้งชื่อ image ว่า hellolulu

    $ docker tag woonpsu/docsdocker:part1 docker-registry.in.psu.ac.th/job1/hellolulu

    ทำการ push image

    $ docker push docker-registry.in.psu.ac.th/job1/hellolulu

    แล้ว logout

    $ docker logout docker-registry.in.psu.ac.th

     

    ดูในหน้าเว็บเพจของ Username ชื่อ lulu และ project ชื่อ job1 จะมี image ที่ upload ขึ้นมาไว้ ดังรูป

     

    เราได้รู้จักกับคุณสมบัติ 1 เรื่องของ Harbor Registry ซึ่งยังมีอีกมากครับ แต่ตอนนี้ ผมคิดว่าเอาแค่นี้ก่อนนะ

    เพิ่มเติม (9 ต.ค. 60)

    หากพบว่าจำเป็นต้องสั่งคำสั่ง docker-compose up -d ทุกครั้งหลังจากเปิดเครื่อง ผมพบว่าวิธีแก้ไข คือ นำบรรทัดข้างล่างน่ี้ไปใส่ไว้ในไฟล์ /etc/rc.local (ก่อนบรรทัด exit 0) อย่าลืมเปลี่ยนคำว่า username เป็นชื่อ user ที่คุณใช้งานด้วยนะ

    cd /home/username/harbor ; docker-compose up -d ; cd

    exit 0

    หาก Harbor ใช้งานได้ปรกติทันทีหลังจากเปิดเครื่อง ก็ไม่ต้องใช้วิธีแก้ไขอันนี้

     

    References:
    https://github.com/vmware/harbor
    https://github.com/vmware/harbor/blob/master/docs/installation_guide.md
    https://success.docker.com/KBase/I_get_%22×509%3A_certificate_signed_by_unknown_authority%22_error_when_I_try_to_login_to_my_DTR_with_default_certificates

     

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

    อยากลองใช้ docker แต่ยังไม่ต้องการนำ image อัปโหลดไปไว้บน docker hub จะทำอย่างไรได้มั้ย ที่นี่มีคำตอบครับ

    เราสามารถนำ docker image ที่ได้สร้างขึ้นนั้นไปเก็บไว้ในสิ่งที่เรียกว่า Docker registry ซึ่งแบ่งได้เป็น 2 อย่างคือ Public registry อยู่ที่ hub.docker.com เราจะต้องลงทะเบียนเพื่อขอมีบัญชีผู้ใช้จึงจะสามารถอัปโหลด image ไปเก็บไว้ได้ ส่วนอีกอย่างก็คือ Private (Local) registry จะเป็นการสร้างที่เก็บส่วนตัว ที่เก็บของหน่วยงาน ในบทความนี้จะสาธิตทำที่เก็บ image สำหรับทดสอบงาน

    การใช้งาน Docker public registry ต้องมี user account ที่ cloud.docker.com
    จะต้อง login ก่อนจึงจะ push image ขึ้นไปได้ เหมือนที่แสดงตัวอย่างไว้ในบล็อก “เตาะแตะไปกับ Docker ตอนที่ 1”

    $ docker login

    ตัวอย่างคำสั่ง push ที่ผมนำ image ไปเก็บไว้

    $ docker push woonpsu/docsdocker:part1

    ซึ่งเรียกใช้จากที่เครื่องใดก็ได้ ด้วยคำสั่ง pull

    $ docker pull woonpsu/docsdocker:part1

     

    แต่หากเรายังไม่พร้อมที่จะนำ image ไปไว้บนนั้น เราก็ทำ Local registry ไว้ใช้เองไปพราง ๆ ก่อน ทำได้ 2 แบบ คือ
    1. Local registry ที่ใช้งานภายในเครื่องของเราเครื่องเดียว
    2. Local registry ที่เราเปิดให้เครื่อง client อื่น ๆ เข้ามาใช้ร่วมได้

    Local registry แบบที่ใช้งานภายในเครื่องของเราเครื่องเดียว (localhost:5000)
    ใช้คำสั่งนี้เพื่อเริ่มต้นสร้าง registry container

    $ docker run -d -p 5000:5000 --restart=always --name myregistry registry:2

    จากนั้นเรา pull image สักอัน จาก hub.docker.com เช่น

    $ docker pull ubuntu:16.04

    หลังจากดัดแปลง(ไม่ทำก็ได้)ตามต้องการแล้วก็ใส่ tag localhost:5000/my-ubuntu ดังนี้

    $ docker tag ubuntu:16.04 localhost:5000/my-ubuntu
    $ docker images

    แล้วก็ push image ไปยัง local registry ซึ่งก็คือ เครื่องที่เรากำลังใช้งาน (localhost) ใช้ port หมายเลข 5000 ดังนี้

    $ docker push localhost:5000/my-ubuntu

    ซึ่งวิธีการเรียกใช้งาน image ที่เก็บไว้นั้น เป็นดังนี้

    $ docker pull localhost:5000/my-ubuntu

    ก่อนไปทำตัวอย่างถัดไปซึ่งเป็นแบบที่ 2 ให้ลบ container ชื่อ myregistry ที่ทดสอบเสร็จแล้ว ที่ต้องลบเพราะว่าใช้ port หมายเลขเดียวกัน หากใช้หมายเลข port ต่างกัน ก็ไม่ต้องลบ

    $ docker stop myregistry
    $ docker rm myregistry

     

    Local registry แบบที่เราเปิดให้เครื่อง client อื่น ๆ เข้ามาใช้ร่วมได้ (docker-registry.localdomain:5000)

    เราจะต้องเริ่มต้นที่การกำหนดชื่อเครื่อง ผมสมมติตั้งชื่อว่า docker-registry.localdomain ให้กับ host ที่ติดตั้ง docker registry ใครจะตั้งอย่างไรก็ได้ครับ

    ในบทความนี้ เนื่องจากว่า ผมจะทดสอบแบบไม่ได้ไปจด Domain Name จริงให้กับเครื่อง ผมจะต้องแก้ไขไฟล์ /etc/hosts และ /etc/hostname ดังนี้

    ที่ไฟล์ /etc/hosts ให้เพิ่มบรรทัด

    10.168.16.19 docker-registry.localdomain

    ที่ไฟล์ /etc/hostname ให้เปลี่ยนชื่อเครื่องจากเดิม มาเป็นดังนี้

    docker-registry.localdomain

    เสร็จแล้ว reboot เครื่อง

    ขั้นตอนการตั้งค่า มาดูกันครับ
    เริ่มต้นที่เครื่อง docker-registry.localdomain
    ให้ทำคำสั่งตั้งค่า TLS certificate เพราะว่าจำเป็นต้องใช้ด้วย (หากมี certificate ของจริงก็นำมาใช้แทน domain.crt และ domain.key ได้เลย)

    $ cd
    $ mkdir certs
    $ openssl req \
    -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key \
    -x509 -days 365 -out certs/domain.crt

    จะมีการถามให้เติมข้อมูล เอาคล้าย ๆ ตัวอย่างก็ได้

    Generating a 4096 bit RSA private key
    writing new private key to 'certs/domain.key'
    Country Name (2 letter code) [AU]:TH
    State or Province Name (full name) [Some-State]:SK
    Locality Name (eg, city) [ ]:HDY
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:PSU
    Organizational Unit Name (eg, section) [ ]:CC
    Common Name (e.g. server FQDN or YOUR name) [ ]:docker-registry.localdomain
    Email Address [ ]:

    จะพบว่าในไดเรกทอรี certs จะเกิดไฟล์ 2 ไฟล์

    $ ls certs/
    domain.crt domain.key

    ใช้คำสั่ง docker run สร้าง container และสมมติตั้งชื่อว่า netregistry

    $ docker run -d --restart=always --name netregistry \
    -v `pwd`/certs:/certs \
    -e REGISTRY_HTTP_ADDR=0.0.0.0:5000 \
    -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
    -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
    -p 5000:5000 \
    registry:2

    ต่อไปจะเป็นการทดสอบการใช้งาน local registry
    ใส่ tag ก่อนที่จะ push ดังนี้

    $ docker tag ubuntu:16.04 docker-registry.localdomain:5000/my-ubuntu
    $ docker images

    แล้ว push image เก็บไว้ใน local registry

    $ docker push docker-registry.localdomain:5000/my-ubuntu

     

    ไปที่เครื่อง client สมมติตั้งชื่อ hostname ว่า docker-vm3
    ที่ไฟล์ /etc/hosts ให้เพิ่มบรรทัด

    127.0.1.1 docker-vm3
    10.168.16.19 docker-registry.localdomain

    ที่ไฟล์ /etc/hostname เป็นดังนี้

    docker-vm3

    เสร็จแล้ว reboot เครื่อง
    ต่อไปก็ทดสอบ pull image จาก local registry ที่ทำขึ้นนั้น

    $ docker pull docker-registry.localdomain:5000/my-ubuntu
    $ docker images

    ต่อไปก็ทดสอบ push image โดยเราก็ไปเอา image จาก hub.docker.com มาสัก 1 อัน ใส่ tag แล้ว push ดังนี้

    $ docker pull woonpsu/docsdocker:part1
    $ docker tag woonpsu/docsdocker:part1 docker-registry.localdomain:5000/helloworld
    $ docker push docker-registry.localdomain:5000/helloworld

    จะพบว่าเราสามารถ push image ไปยังเครื่อง local registry ได้ด้วย

    วิธีที่แนะนำไปแล้วข้างต้น เป็นการสร้าง Local registry ที่ไม่ได้มีความปลอดภัย เพราะว่าแค่รู้ชื่อเครื่อง รู้หมายเลข port (เลือกทดสอบกับ port 5000) ก็สามารถ push image ได้แล้ว

     

    ดังนั้นหากจะสร้าง Local registry ให้บริการ (production) เราก็ต้องทำขั้นตอนเพิ่มอีกคือต้องใช้ TLS certificate ร่วมกับ Access control

    ดังนี้

    ที่เครื่อง docker-registry.localdomain
    สร้าง username ที่มีสิทธิจะเข้ามา (access) ใช้ local registry สมมติชื่อ testuser และรหัสผ่านคือ testpassword ดังนี้

    $ mkdir auth
    $ docker run \
     --entrypoint htpasswd \
     registry:2 -Bbn testuser testpassword > auth/htpasswd
    $ cat auth/htpasswd
    testuser:$2y$05$UMOEA8bdBqSbHp/2LfiQ/uxLkf5IkXNqNE8V3Mp2IR/fRCsLXE5Q2

    รัน container ตั้งชื่อว่า privateregistry และเราจะเปลี่ยนมาใช้ port 443 ดังนี้

    $ docker run -d \
     --restart=always \
     --name privateregistry \
     -v `pwd`/auth:/auth \
     -e "REGISTRY_AUTH=htpasswd" \
     -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
     -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
     -v `pwd`/certs:/certs \
     -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
     -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
     -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
     -p 443:443 \
     registry:2

    ไปที่เครื่อง client ชื่อ docker-vm3

    $ docker login docker-registry.localdomain
    Username: testuser
    Password:

    ทดสอบ push image

    $ docker tag woonpsu/docsdocker:part1 docker-registry.localdomain/sawadee
    $ docker push docker-registry.localdomain/sawadee

    ลองทดสอบดูว่าหากไม่ได้ login ก็จะ push ไม่ได้

    $ docker logout docker-registry.localdomain
    $ docker push docker-registry.localdomain/sawadee
    The push refers to a repository [docker-registry.localdomain/sawadee]
    0d960f1d4fba: Preparing
    no basic auth credentials

     

    ข้อควรระวังในการทำ local registry คือ image ที่เรานำไปเก็บไว้ จะอยู่ใน container ซึ่งหากเราลบ container ด้วยคำสั่ง

    $ docker stop privateregistry
    $ docker rm privateregistry

    ก็จะเป็นการลบ image ทั้งหมด
    ดังนั้น เราจะต้องศึกษาวิธี mount volume เข้าไปใน container เพื่อให้ image ที่ push ไว้นั้น ถูกเก็บใน file system ของเครื่อง host แทน โดยเพิ่มบรรทัด -v นี้เข้าไปด้วยวางไว้ก่อนบรรทัดสุดท้าย (registry:2)

    -v /mnt/registry:/var/lib/registry \

    โดยที่ /mnt/registry คือ file system บน host และ /var/lib/registry คือไดเรกทอรีใน container

     

    ขอให้สนุกกับการทดลองเล่น docker กันนะครับ

     

    Reference:
    https://docs.docker.com/registry/deploying/
    https://docs.docker.com/registry/deploying/#restricting-access

     

     

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

  • เปลี่ยน ubuntu sources.list ก่อนสร้าง image ด้วย dockerfile

    การใช้งาน docker นั้นเราสามารถใช้ image จาก docker hub หรือเราจะสร้าง image ของเราเอง ซึ่งมีหลายวิธีในการสร้าง image แบบของเราเอง (custom) วิธีหนึ่งคือการใช้ dockerfile อย่างคร่าว ๆ คือ

    mkdir ~/mydocker
    cd ~/mydocker
    touch dockerfile
    docker built -t test_app:20170713 .
    docker images

    ในไฟล์ชื่อ dockerfile นี้จะมีไวยกรณ์ประมาณนี้

    # Image tag: test_app:20170713 <– บรรทัดนี้คือ comment
    FROM ubuntu:16.04 <– บรรทัดนี้คือ ไปเอา image ชื่อ ubuntu:16.04 จาก docker hub
    RUN apt-get update <– บรรทัดนี้คือ คำสั่งบอกว่าจะติดตั้ง หลังคำว่า RUN นั่นเอง
    RUN apt-get dist-upgrade -y
    RUN apt-get install -y apache2 libapache2-mod-php7.0 php7.0
    COPY …
    ADD …
    EXPOSE …
    CMD …
    และยังมี command อื่น ๆ อีก

    ทีนี้จากการที่ต้องลองผิดลองถูกบ่อย ๆ จึงพบว่า หากเราเพิ่มคำสั่ง 2 บรรทัดนี้เข้าไปก่อนบรรทัด RUN apt-get update ก็จะทำให้เราได้ใช้ ubuntu repository ที่ต้องการแทนค่า default ที่ archive.ubuntu.com เช่นต้องการให้มาใช้ th.archive.ubuntu.com ก็เขียนคำสั่งดังนี้

    RUN sed -i 's/\/us.archive/\/th.archive/g' /etc/apt/sources.list
    RUN sed -i 's/\/archive/\/th.archive/g' /etc/apt/sources.list

    ผลลัพธ์คือ หลังจากทำคำสั่ง docker built -t test_app:20170713 . จะเห็นว่าในขั้นตอนการ build นั้นจะดาวน์โหลดไฟล์ได้รวดเร็วกว่าเดิมมาก

    จึงนำความรู้มาบอกกันครับ อ้อลืมบอกว่าบทความที่เขียนนี้ ผมทดสอบกับ docker version 17.06.0-ce ครับ

    อยากแนะนำความรู้เกี่ยวกับ docker ที่อ่านมา พบว่าน่าสนใจ ลองอ่านดูครับ อ่านง่าย

     

    บทความในต่างประเทศ

    1. 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)
    2. How to Build an Image with the Dockerfile (https://www.sitepoint.com/how-to-build-an-image-with-the-dockerfile/)
    3. Dockerfile reference (https://docs.docker.com/engine/reference/builder/)
    4. Best practices for writing Dockerfiles (https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/)
    5. How to update Docker image to maintain your containers secure  (https://bobcares.com/blog/update-docker-image/2/)
    6. How to upgrade docker container after its image changed
      (https://stackoverflow.com/questions/26734402/how-to-upgrade-docker-container-after-its-image-changed)
    7. Manage data in containers (https://docs.docker.com/engine/tutorials/dockervolumes/)

     

  • ELK #04

    คราวนี้มาติดตั้งบน Docker บ้าง

    1. ถ้าเครื่อง Server เป็น Ubuntu 16.04 ทำตามขั้นตอนนี้เพื่อให้สามารถใช้งาน Docker ได้
      วิธีการติดตั้ง Docker บน Ubuntu 16.04
    2. เนื่องจาก Elasticsearch 5.x ใช้ Virtual Memory มากขึ้น ลองใช้คำสั่งนี้ดูค่าปัจจุบัน
       sysctl vm.max_map_count

      ค่า Default น่าจะประมาณนี้
      vm.max_map_count = 65530 ให้ทำการเพิ่มด้วยคำสั่งนี้

      sudo -i
      sudo echo "vm.max_map_count=262144" >> /etc/sysctl.conf
      exit

      จากนั้นให้ทำการ Reboot

    3. ติดตั้ง docker image ของ sebp/elk ด้วยคำสั่ง
       sudo docker pull sebp/elk

      โดย Default จะได้ Lastest Version

    4. ใช้คำสี่งต่อไปนี้ เพื่อ Start ELK ขึ้นมา โดยเปิด port ให้ Kibana: 5601, Elasticsearch: 9200, Logstash: 5044 และทำงานเป็นแบบ Detach หรือ Background นั่นเอง
      sudo docker run -d -p 5601:5601 -p 9200:9200 -p 5044:5044 -it --name elk sebp/elk

      หรือถ้าจะใช้ Docker Compose ก็สามารถใช้งานด้วยวิธีการนี้
      เริ่มจาก ติดตั้ง Docker Compose ด้วยคำสั่ง

      sudo -i
      curl -L https://github.com/docker/compose/releases/download/1.14.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
      chmod +x /usr/local/bin/docker-compose
      exit

      จากนั้น สร้างไฟล์ /path/to/your/config/elk.yml เนื้อหาดังนี้

      elk:
       image: sebp/elk
       ports:
       - "5601:5601"
       - "9200:9200"
       - "5044:5044"

      จากนั้นก็ Start

      sudo /usr/local/bin/docker-compose -f /path/to/your/config/elk.yml  up -d elk

      หากต้องการให้ container ทำการ start ทุกครั้งที่ Reboot ใช้คำสั่ง

      sudo crontab -e

      แล้วใส่บรรทัดนี้ต่อท้ายไฟล์

      @reboot /usr/local/bin/docker-compose -f /home/mama/elk.yml up -d elk
    5. ทดสอบว่า Container ที่กำลังทำงานอยู่มีอะไรบ้าง
      sudo docker ps

      วิธีดูว่า มี Container อะไรบ้าง (ทั้งที่ทำงานและไม่ทำงาน)

      sudo docker ps -a

      วิธีดูว่าเกิดอะไรขึ้นกับ Container (ในที่นี้ ชื่อ elk)

      sudo docker logs elk
    6. ถึงจุดนี้ ก็สามารถใช้งาน Kibana ทาง web url: http://your.host:5601 ได้แล้ว

     

    Reference:

    https://elk-docker.readthedocs.io/