Category: Virtual Machine

  • เตาะแตะไปกับ Docker ตอนที่ 11 Docker Compose (LDAP services)

    เราจะมาเรียนรู้ การใช้ docker compose รัน service 2 services คือ OpenLDAP และ phpLDAPadmin ซึ่ง docker จะมองว่าการใช้ docker compose คือ เรากำลังรัน project ที่ประกอบด้วย service หลาย ๆ service

    ให้แน่ใจว่าอยู่ที่ home directory ให้ใช้คำสั่งนี้

    $ cd

    สร้างไดเรกทอรีของ project สมมติตั้งชื่อว่า ex1

    $ mkdir ex1
    $ cd ex1

    สร้างไดเรกทอรี openldap ภายในมี dockerfile และไดเรกทอรีที่เกี่ยวข้อง ทำตามบล็อกเรื่อง “เตาะแตะไปกับ Docker ตอนที่ 9 Dockerfile (OpenLDAP)

    สร้างไดเรกทอรี phpldapadmin ภายในมี dockerfile และไดเรกทอรีที่เกี่ยวข้อง ทำตามบล็อกเรื่อง “เตาะแตะไปกับ Docker ตอนที่ 10 Dockerfile (phpLDAPadmin)

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

    $ vi docker-compose.yml
    
    version: '2'
    services:
     openldap:
      build: ./openldap
      container_name: openldap
      volumes:
       - ldapdatavol:/var/lib/ldap
       - ldapconfigvol:/etc/ldap/slapd.d
      ports:
       - "389:389"
       - "636:636"
      restart: always
    
     phpldapadmin:
      build: ./phpldapadmin
      container_name: phpldapadmin
      environment:
       HNAME: "openldap"
      ports:
       - "8080:80"
      depends_on:
       - openldap
      restart: always
    
    volumes:
     ldapdatavol:
      external: false
     ldapconfigvol:
      external: false

    อธิบายได้ดังนี้
    ไฟล์ docker-compose.yml นี้ จะมี service แรกคือ openldap จะ build image จากไดเรกทอรี ./openldap เมื่อรันเป็น container จะตั้งชื่อว่า openldap โดยมีที่เก็บข้อมูลถาวรคือ ldapdatavol จะ mapped ไปยัง /var/lib/ldap ใน container และที่เก็บคอนฟิก ldapconfigvol จะ mapped ไปยัง /etc/ldap/slapd.d ใน container เช่นเดียวกัน
    โดย Host และ container เปิด port ตรงกัน คือ เปิด port TCP 636(LDAPS) และ 389(LDAP) และ container นี้จะทำงานทุกครั้งเมื่อเปิดเครื่อง

    ต่อมา service ที่สองคือ phpldapadmin จะ build image จากไดเรกทอรี ./phpldapadmin เมื่อรันเป็น container จะตั้งชื่อว่า phpldapadmin มีการตั้งค่าตัวแปร HNAME เป็นชื่อ container อันแรก คือ openldap โดยที่ Host จะเปิด port TCP 8080 ไปยัง port TCP 80 ของ container นี้ ถัดมาคือ depends_on คือ จะรัน container นี้ได้ก็ต่อเมื่อมี container ชื่อ openldap และ container นี้จะทำงานทุกครั้งเมื่อเปิดเครื่อง

    ท่อนล่างสุด คือ การกำหนดว่า จะมีการใช้ named volume ชื่อ ldapdatavol และ ldapconfigvol ทั้งสอง volume นี้ จะถูกสร้างโดยคำสั่ง docker-compose ไม่ได้ไปใช้ volume จากที่ได้สร้างไว้ก่อนแล้ว (external: false) หากจะย้อนกลับไปอ่านเรื่อง “เตาะแตะไปกับ Docker ตอนที่ 7 Manage data” จะเข้าใจเรื่อง named volume ครับ

    ผลลัพธ์หลังจากเตรียมครบ ภายในไดเรกทอรี ex1 จะมี 1 ไฟล์ และ 2 ไดเรกทอรี ดังนี้

    $ ls 
    docker-compose.yml openldap phpldapadmin

    ตอนนี้เราก็เตรียมการต่าง ๆ เสร็จแล้ว

    ต่อไปก็ใช้คำสั่ง docker-compose เพื่อสร้าง service คือ openldap และ phpldapadmin ดังนี้

    $ docker-compose up -d

    ตรวจสอบรายการ image

    $ docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    phpldapadmin latest d314c021fd1a 6 hours ago 282MB
    ex1_phpldapadmin latest f2feaa90c59b 9 days ago 282MB
    ex1_openldap latest e98f6bad71a7 9 days ago 259MB
    openldap latest cb8b1c0057cc 11 days ago 259MB
    ubuntu 16.04 ccc7a11d65b1 5 weeks ago 120MB

    จะพบว่ามี image ของ project ex1 คือ ex1_openldap และ ex1_phpldapadmin ส่วน image ชื่อ openldap และ phpldapadmin สองอันนั้นที่เห็นเป็น image ที่สร้างด้วยคำสั่ง docker build ตอนที่เราเรียนรู้เรื่อง “เตาะแตะไปกับ Docker ตอนที่ 9 Dockerfile (OpenLDAP)” และ “เตาะแตะไปกับ Docker ตอนที่ 10 Dockerfile (phpLDAPadmin)” ครับ

    ตรวจสอบรายการ container ที่รันอยู่

    $ docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    b61e8f24802c ex1_phpldapadmin "/bin/bash /run.sh..." 3 minutes ago Up 3 minutes 0.0.0.0:8080->80/tcp phpldapadmin
    489bb2db70d3 ex1_openldap "/bin/sh -c '/usr/..." 3 minutes ago Up 3 minutes 0.0.0.0:389->389/tcp, 0.0.0.0:636->636/tcp openldap

    จะพบว่ามี container ชื่อว่า openldap และ phpldapadmin เกิดขึ้นหลังคำสั่ง docker-compose up -d

    ทดสอบการเข้าใช้งานทาง web ไปที่ http://ldap.example.com:8080/phpldapadmin/ (เนื่องจาก VM ที่ใช้ทดสอบ คือ ubuntu ที่ได้ติดตั้ง XFCE desktop environment ไว้ด้วยทำให้สามารถใช้งาน web browser ได้ด้วย)

    หลังจาก login แล้วจะเห็นข้อมูล LDAP และจัดการ LDAP database ได้

    จากความรู้ที่ได้เรียนมาหลาย ๆ ตอนจนถึงตอนนี้ เราก็น่าจะได้แนวทางในการสร้าง service ความเข้าใจอย่างง่าย ๆ คือ หากเป็น image ที่เราจะทำขึ้นเอง ไม่ได้ไป pull จาก docker hub เราก็ทำด้วย dockerfile และหากมี service ที่ใช้ร่วมกันระหว่าง service ก็เปลี่ยนไปทำต่อด้วย docker-compose จะสะดวกกว่า

    อนึ่งหาก pull หรือมี image อยู่แล้ว ก็เปลี่ยนจาก build: ./openldap เป็น image: openldap และปรับแก้ไขนิดหน่อยตามที่ผู้พัฒนา image นั้นได้แจ้งวิธีการไว้ครับ

    จากที่ได้เขียนบล็อกในชุด “เตาะแตะไปกับ Docker” ตั้งแต่ ตอนที่ 1 ถึง ตอนที่ 11 นี้ ผมคิดว่าก็น่าจะเพียงพอให้เข้าใจว่า docker ใช้งานอย่างไร หวังเป็นอย่างยิ่งว่าผู้อ่านคงได้รับความรู้กันครับ และสามารถลองทำตามได้โดยไม่ติดขัดอะไร

     

  • เตาะแตะไปกับ Docker ตอนที่ 10 Dockerfile (phpLDAPadmin)

    ในตอนที่แล้วเราใช้งาน openldap ด้วยคำสั่ง เช่น ldapsearch หรือ ldapadd ได้แล้ว วันนี้เราจะเรียนรู้การใช้ dockerfile สร้าง image ชื่อ phpldapadmin เพื่อใช้เป็น web interface ในการเข้าไปจัดการ LDAP database ของ container ชื่อ openldap โดยที่ phpLDAPadmin นี้คือการรัน php บน apache2 แล้วเราจะติดต่อระหว่าง container กันได้อย่างไร มาดูกันครับ

    เช่นเดียวกับครั้งที่แล้ว ผมจะแยกเป็น 3 ขั้นตอน คือ 1.ขั้นตอนเตรียมไฟล์ที่เกี่ยวข้อง 2.ขั้นตอนสร้างไฟล์ dockerfile และ 3.ขั้นตอนการรัน container

     

    1.ขั้นตอนเตรียมไฟล์ที่เกี่ยวข้อง

    ให้แน่ใจว่าอยู่ที่ home directory ให้ใช้คำสั่งนี้

    $ cd

    สร้างไดเรกทอรีของ image ที่เราจะสร้าง

    $ mkdir phpldapadmin
    $ cd phpldapadmin
    $ mkdir src

    เราจะเตรียมไฟล์ชื่อ apache2-foreground ให้เปิดไฟล์จากที่นี่ https://github.com/docker-library/php/blob/master/5.6/jessie/apache/apache2-foreground แล้วคัดลอกทุกบรรทัด นำมาสร้างไฟล์ ด้วยเอติเตอร์ที่ถนัด เช่น vi หรือ nano ก็ได้

    $ vi ./src/apache2-foreground

     

    2.ขั้นตอนสร้างไฟล์ dockerfile

    ตอนนี้ก็มาถึงขั้นตอนการเขียน dockerfile ด้วยเอดิเตอร์ที่ถนัดเช่น vi หรือ nano ก็ได้

    $ vi dockerfile
    
    # Composer: Wiboon Warasittichai
    
    FROM ubuntu:16.04
    
    # Change apt source
    RUN sed -i 's/\/us.archive/\/th.archive/g' /etc/apt/sources.list && \
     sed -i 's/\/archive/\/th.archive/g' /etc/apt/sources.list
    
    # Update ubuntu, then install packages
    RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt install -y php-ldap phpldapadmin ldap-utils
    
    # Timezone Asia/Bangkok
    RUN apt-get install -y tzdata && \
     ln -sf /usr/share/zoneinfo/Asia/Bangkok /etc/localtime && \
     dpkg-reconfigure -f noninteractive tzdata
    
    # Cleaning
    RUN apt-get autoremove -y && apt-get clean -y
    
    EXPOSE 80
    
    CMD RUN ulimit -n 1024
    
    ENV HNAME ${HNAME}
    ADD ./src/startup.sh /startup.sh
    
    COPY ./src/apache2-foreground /usr/local/bin/
    RUN chmod +x /usr/local/bin/apache2-foreground
    
    ENTRYPOINT ["/bin/bash","/startup.sh"]

     

    ภายในไฟล์ dockerfile เราได้อ้างถึงการนำไฟล์ชื่อ startup.sh ก็ให้สร้างไฟล์นี้ด้วย

    $ vi ./src/startup.sh 
    
    #!/bin/bash
    
    sed -i "s/127.0.0.1/$HNAME/g" /etc/phpldapadmin/config.php
    /usr/local/bin/apache2-foreground

    จุดน่าสนใจในไฟล์ dockerfile ในตัวอย่างนี้ คือ เราจะเขียนอย่างไรเพื่อให้เราเปลี่ยนค่า 127.0.0.1 ในไฟล์ /etc/phpldapadmin/config.php ใน container ที่รันขึ้นมาได้อัตโนมัติ เราจึงต้องใช้ ENV ในการระบุว่า container openldap ที่รันอยู่นั้นใช้ IP อะไร แล้วเราก็นำ IP ใส่ลงไปในตัวแปร HNAME เพื่อจะติดต่อไปยัง container openldap ได้ถูกต้อง ผมจึงเพิ่มบรรทัดนี้

    ENV HNAME ${HNAME}

     

    3.ขั้นตอนการรัน container

    สร้าง image ด้วยคำสั่งนี้

    $ docker build -t phpldapadmin .

    ก่อนจะรัน container phpldapadmin ต้องไม่ลืมว่า เราจะต้องรัน container openldap ด้วยคำสั่งนี้แล้ว

    $ docker run -d -p 636:636 -p 389:389 --name openldap --volume oldata:/var/lib/ldap --volume olconfig:/etc/ldap/slapd.d openldap

    รัน container และ ตรวจสอบ ด้วยคำสั่งนี้

    $ LDAPSERVER=$(docker exec -it openldap cat /etc/hosts | tail -1 | cut -f1) && docker run -d -p 8080:80 --env HNAME=${LDAPSERVER} --name phpldapadmin phpldapadmin
    
    $ docker ps

    อธิบายได้ดังนี้ container จะรันแบบ detach (-d) เปิด port 8080 ที่ Host เข้าไปยัง port 80 ที่ container ตั้งชื่อ(–name) ว่า phpldapadmin โดยกำหนดค่า ENV สำหรับตัวแปรที่ชื่อ HNAME ให้ใช้ IP จากไฟล์ /etc/hosts ใน container openldap และรันจาก image ชื่อ phpldapadmin

    ทดสอบการเข้าใช้งานทาง web ไปที่ http://ldap.example.com:8080/phpldapadmin/ (เนื่องจาก VM ที่ใช้ทดสอบ คือ ubuntu ที่ได้ติดตั้ง XFCE desktop environment ไว้ด้วยทำให้สามารถใช้งาน web browser ได้ด้วย)

    หลังจาก login แล้วจะเห็นข้อมูล LDAP และจัดการ LDAP database ได้

    โปรดสังเกตว่าคำสั่ง docker run นั้น หากต้องการให้รันทำงานทุกครั้งที่เปิดเครื่อง เราจะสามารถใส่ option นี้เข้าไปได้ด้วย คือ –restart always ใส่หลังจากคำว่า docker run ก็ได้ครับ (ในตัวอย่างข้างต้น ผมไม่ได้ใส่ไว้ เพราะกำลังทดสอบ) หากเรา reboot เครื่อง container ทั้ง 2 ก็หายไปครับ

    แต่เราก็สามารถลบ container ได้ทันที ดังนี้

    $ docker stop phpldapadmin
    $ docker rm phpldapadmin
    $ docker stop openldap
    $ docker rm openldap

    สรุปว่า ในตอนนี้ เรารู้วิธีการใช้ dockerfile สร้าง container 2 อัน อันแรกคือ LDAP database และอีกอันคือ php web program ที่จะติดต่อกับ LDAP database ด้วยการใช้ตัวแปร ENV ร่วมด้วย

    ในตอนต่อไป เราจะมาเรียนรู้ การใช้ docker compose รันทั้ง 2 services นี้แทนการแยกสร้าง dockerfile และ run แยกกัน

     

  • เตาะแตะไปกับ Docker ตอนที่ 9 Dockerfile (OpenLDAP)

    วันนี้เราจะเรียนรู้การใช้ dockerfile สร้าง image ชื่อ openldap เพื่อใช้เป็น LDAP database
    ผมทดสอบด้วย Oracle VM VirtualBox เป็น VM ที่ตั้งค่า Network adapter เป็นแบบ NAT network ที่ติดตั้ง Ubuntu 16.04 และ docker เรียบร้อยแล้ว ในตัวอย่างนี้ผมตั้งชื่อ host ว่า ldap.example.com โดยแก้ไขที่ไฟล์ /etc/hosts และ /etc/hostname ให้เรียบร้อย แล้วรีบูตเครื่องด้วย

    ผมจะขอแยกขั้นตอนออกเป็น 3 ขั้นตอนหลัก คือ 1.ขั้นตอนเตรียมไฟล์ที่เกี่ยวข้องซึ่งรวมไฟล์ ldif ด้วย 2.ขั้นตอนสร้างไฟล์ dockerfile และ 3.ขั้นตอนการรัน container

     

    1.ขั้นตอนเตรียมไฟล์ที่เกี่ยวข้อง
    สร้างไดเรกทอรีของ image ที่เราจะสร้าง

    $ mkdir openldap

    สร้างไดเรกทอรีที่เก็บไฟล์ ldif (โครงสร้าง และ ตัวอย่างข้อมูล)

    $ cd openldap
    $ mkdir src

    สร้างไฟล์ ./src/create-schema.ldif ด้วยเอติเตอร์ที่ถนัด เช่น vi หรือ nano ก็ได้

    $ vi ./src/create-schema.ldif 
    # Starter kit for create user in domain dc=example,dc=com
    # Create 1st tree OU=groups
    dn: ou=groups,dc=example,dc=com
    objectClass: organizationalUnit
    ou: groups
    
    # Create sub tree OU=execs,OU=groups
    dn: ou=execs,ou=groups,dc=example,dc=com
    objectClass: organizationalUnit
    ou: execs
    
    # Create sub tree OU=staffs,OU=groups
    dn: ou=staffs,ou=groups,dc=example,dc=com
    objectClass: organizationalUnit
    ou: staffs
    
    # Create sub tree OU=students,OU=groups
    dn: ou=students,ou=groups,dc=example,dc=com
    objectClass: organizationalUnit
    ou: students
    
    # Create 2nd tree OU=people
    dn: ou=people,dc=example,dc=com
    objectClass: organizationalUnit
    ou: people

    สร้างไฟล์ ssl.ldif

    $ vi src/ssl.ldif
    dn: cn=config
    changetype: modify
    add: olcTLSCipherSuite
    olcTLSCipherSuite: NORMAL
    -
    add: olcTLSCRLCheck
    olcTLSCRLCheck: none
    -
    add: olcTLSVerifyClient
    olcTLSVerifyClient: never
    -
    add: olcTLSCertificateFile
    olcTLSCertificateFile: /etc/ssl/certs/ldap-ca-cert.pem
    -
    add: olcTLSCertificateKeyFile
    olcTLSCertificateKeyFile: /etc/ssl/private/ldap-ca-key.pem

    สร้างไฟล์ ./src/create-users.ldif

    $ vi ./src/create-users.ldif 
    # Create 1 user in tree OU=execs,OU=groups
    dn: cn=nana,ou=execs,ou=groups,dc=example,dc=com
    objectClass: inetOrgPerson
    uid: nana
    sn: Na
    givenName: Na
    cn: nana
    displayName: Na Na
    userPassword: 123456
    mail: nana@example.com
    
    # Create 2 users in tree OU=staffs,OU=groups
    dn: cn=koko,ou=staffs,ou=groups,dc=example,dc=com
    objectClass: inetOrgPerson
    uid: koko
    sn: Ko
    givenName: Ko
    cn: koko
    displayName: Ko Ko
    userPassword: 123456
    mail: koko@example.com
    
    dn: cn=momo,ou=staffs,ou=groups,dc=example,dc=com
    objectClass: inetOrgPerson
    uid: momo
    sn: Mo
    givenName: Mo
    cn: momo
    displayName: Mo Mo
    userPassword: 123456
    mail: momo@example.com
    
    # Create 2 users in tree OU=people
    dn: cn=lala,ou=people,dc=example,dc=com
    objectClass: inetOrgPerson
    uid: lala
    sn: La
    givenName: La
    cn: lala
    displayName: La La
    userPassword: 123456
    mail: lala@example.com
    
    dn: cn=lulu,ou=people,dc=example,dc=com
    objectClass: inetOrgPerson
    uid: lulu
    sn: Lu
    givenName: Lu
    cn: lulu
    displayName: Lu Lu
    userPassword: 123456
    mail: lulu@example.com

    สร้างไฟล์ ./src/create-students.ldif

    $ vi ./src/create-students.ldif 
    # Create 2 students in tree OU=students,OU=groups
    dn: cn=5310110293,ou=students,ou=groups,dc=example,dc=com
    objectClass: inetOrgPerson
    uid: 5310110293
    sn: 5310110293
    givenName: somsak
    cn: 5310110293
    displayName: somsak somsaknaja
    userPassword: 123456
    mail: 5310110293@example.com
    
    dn: cn=5410110308,ou=students,ou=groups,dc=example,dc=com
    objectClass: inetOrgPerson
    uid: 5410110308
    sn: 5410110308
    givenName: somsri
    cn: 5410110308
    displayName: somsri somsrisiya
    userPassword: 123456
    mail: 5410110308@example.com

    ต่อไปเป็นการเตรียม self-signed certificate เพื่อให้ใช้งาน LDAPS ได้

    $ sudo apt install ssl-cert
    $ mkdir cert
    $ cp /etc/ssl/certs/ssl-cert-snakeoil.pem cert/ldap-ca-cert.pem
    $ sudo cp /etc/ssl/private/ssl-cert-snakeoil.key cert/ldap-ca-key.pem
    $ sudo chmod +r cert/ldap-ca-key.pem

     

    2.ขั้นตอนสร้างไฟล์ dockerfile
    ตอนนี้ก็มาถึงขั้นตอนการเขียน dockerfile ด้วยเอดิเตอร์ที่ถนัดเช่น vi หรือ nano ก็ได้

    $ vi dockerfile
    # Composer: Comments
    
    FROM ubuntu:16.04
    
    # Change apt source
    RUN sed -i 's/\/us.archive/\/th.archive/g' /etc/apt/sources.list && \
     sed -i 's/\/archive/\/th.archive/g' /etc/apt/sources.list
    
    # Update ubuntu, then install packages
    RUN apt-get update && \
    echo 'slapd slapd/root_password password 123456' | debconf-set-selections && \
    echo 'slapd slapd/root_password_again password 123456' | debconf-set-selections && \
    echo "slapd slapd/internal/adminpw password 123456" |debconf-set-selections && \
    echo "slapd slapd/internal/generated_adminpw password 123456" |debconf-set-selections && \
    echo "slapd slapd/password2 password 123456" |debconf-set-selections && \
    echo "slapd slapd/password1 password 123456" |debconf-set-selections && \
    echo "slapd slapd/domain string example.com" |debconf-set-selections && \
    echo "slapd shared/organization string example" |debconf-set-selections && \
    echo "slapd slapd/backend string HDB" |debconf-set-selections && \
    echo "slapd slapd/purge_database boolean true" |debconf-set-selections && \
    echo "slapd slapd/move_old_database boolean true" |debconf-set-selections && \
    echo "slapd slapd/allow_ldap_v2 boolean false" |debconf-set-selections && \
    echo "slapd slapd/no_configuration boolean false" |debconf-set-selections && \
    DEBIAN_FRONTEND=noninteractive apt install -y slapd
    
    # LDAP utils, self-signed certificates
    RUN apt-get install -y ldap-utils ssl-cert
    
    # Timezone Asia/Bangkok
    RUN apt-get install -y tzdata && \
     ln -sf /usr/share/zoneinfo/Asia/Bangkok /etc/localtime && \
     dpkg-reconfigure -f noninteractive tzdata
    
    # Cleaning
    RUN apt-get autoremove -y && apt-get clean -y
    
    # LDAP
    COPY ./src/*.ldif /tmp/
    ## Schema only, no user. 
    RUN service slapd start && \
     ldapadd -H ldapi:/// -f /tmp/create-schema.ldif -x -D "cn=admin,dc=example,dc=com" -w 123456
    ## Schema and add users.
    ## RUN service slapd start && \ 
    ## ldapadd -H ldapi:/// -f /tmp/create-schema.ldif -x -D "cn=admin,dc=example,dc=com" -w 123456 && \
    ## ldapadd -H ldapi:/// -f /tmp/create-users.ldif -x -D "cn=admin,dc=example,dc=com" -w 123456 && \
    ## ldapadd -H ldapi:/// -f /tmp/create-students.ldif -x -D "cn=admin,dc=example,dc=com" -w 123456
    
    EXPOSE 389 
    #CMD slapd -h 'ldap:///' -g openldap -u openldap -d 0
    
    #
    # LDAPS
    COPY ./cert/ldap-ca-cert.pem /etc/ssl/certs/
    COPY ./cert/ldap-ca-key.pem /etc/ssl/private/
    
    RUN chgrp ssl-cert /etc/ssl/private/ldap-ca-key.pem && \
     chmod g+r /etc/ssl/private/ldap-ca-key.pem && \
     adduser openldap ssl-cert
    
    RUN echo 'SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///"' >> /etc/default/slapd && \
     echo 'TLS_REQCERT never' >> /etc/ldap/ldap.conf
    
    RUN service slapd start && \
     ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/ssl.ldif -v
    
    EXPOSE 636
    ##CMD slapd -h 'ldaps:///' -g openldap -u openldap -d 0
    
    CMD RUN ulimit -n 1024
    CMD /usr/sbin/slapd -h 'ldap:/// ldaps:/// ldapi:///' -g openldap -u openldap -d 0

     

    3.ขั้นตอนการรัน container
    สร้าง image ด้วยคำสั่งนี้

    $ docker build -t openldap .

    รัน container และ ตรวจสอบ ด้วยคำสั่งนี้

    $ docker run -d -p 636:636 -p 389:389 --name openldap openldap
    $ docker ps

    อธิบายได้ดังนี้ container จะรันแบบ detach (-d) เปิด port 636 และ 389 ตั้งชื่อ(–name) ว่า openldap และรันจาก image ชื่อ openldap

    ทดสอบการนำเข้าข้อมูล ซึ่งเราจะใช้เครื่องที่กำลังทำอยู่นี้เป็น ldap client ไม่ต้องไปหาอีกเครื่องก็ได้ โดยเพิ่มแพ็กเกจ ldap-utils

    $ sudo apt install ldap-utils

    ทดสอบการเพิ่มตัวอย่างข้อมูล โดยเลือกใช้ port 389 (LDAP)

    $ ldapadd -H ldap://ldap.example.com -f ./src/create-users.ldif -x -D "cn=admin,dc=example,dc=com" -w 123456

    ทดสอบการเพิ่มตัวอย่างข้อมูล โดยเลือกใช้ port 636 (LDAPS)

    $ ldapadd -H ldaps://ldap.example.com -f ./src/create-students.ldif -x -D "cn=admin,dc=example,dc=com" -w 123456

    จะพบข้อความว่า

    ldap_sasl_bind(SIMPLE): Can’t contact LDAP server (-1)

    ให้ทำคำสั่ง 2 บรรทัดข้างล่างนี้ เพื่อเพิ่ม certificate จาก LDAP server แล้วลองคำสั่งนั้นอีกครั้งจะทำได้

    $ openssl s_client -connect ldap.example.com:636 -showcerts </dev/null 2>/dev/null | openssl x509 -outform PEM | sudo tee /usr/local/share/ca-certificates/ldap.example.com.crt
    $ sudo update-ca-certificates

    ทดสอบค้นหาข้อมูล

    $ ldapsearch -xLLL -b "dc=example,dc=com" uid=lulu
    $ ldapsearch -xLLL -b "dc=example,dc=com" uid=5310110293

    ลบ container

    $ docker stop openldap
    $ docker rm openldap

     

    เรื่องสุดท้ายของบล็อกในวันนี้คือ ต้องการเก็บตัวอย่างข้อมูลที่เพิ่มไว้จะทำอย่างไร ทำได้โดยการสร้างที่เก็บ persistent data แบบ named volume โดยที่ oldata สำหรับข้อมูล และ olconfig สำหรับไฟล์คอนฟิกกูเรชัน

    $ docker volume create oldata
    oldata
    $ docker volume create olconfig
    olconfig

    เช็คดูรายการ volume

    $ docker volume ls
    DRIVER VOLUME NAME
    local olconfig
    local oldata

    รัน container (เขียนคำสั่งให้อยู่ในบรรทัดเดียว)

    $ docker run -d -p 636:636 -p 389:389 --name openldap --volume oldata:/var/lib/ldap --volume olconfig:/etc/ldap/slapd.d openldap

    อธิบายได้ดังนี้ container จะรันแบบ detach (-d) เปิด port 636 และ 389 ตั้งชื่อ(–name) ว่า openldap เก็บข้อมูลไว้นอก container เอาไว้ที่ volume ชื่อ oldata ที่ mapped ไปยัง /var/lib/ldap ใน container กับ olconfig ที่ mapped ไปยัง /etc/ldap/slapd.d ใน container และรันจาก image ชื่อ openldap

    เพิ่มข้อมูลตัวอย่าง

    $ ldapadd -H ldaps://ldap.example.com -f ./src/create-students.ldif -x -D "cn=admin,dc=example,dc=com" -w 123456

    ค้นหาข้อมูล

    $ ldapsearch -xLLL -b "dc=example,dc=com" uid=5310110293

    ลบ container เมื่อจะไม่ใช้งานแล้ว

    $ docker stop openldap
    $ docker rm openldap

    หลังจากลบ container ข้อมูลของเราจะยังคงอยู่ในที่เก็บข้อมูลแบบ volume อยู่ที่ path /var/lib/docker/volumes

    ในตอนต่อไปจะเป็นการสร้าง dockerfile เพื่อรัน phpldapadmin ใช้ในการเข้า admin ผ่านหน้าเว็บเพจ

  • เตาะแตะไปกับ Docker ตอนที่ 8 Cleanup Disk Space

    การเรียนรู้ docker เราก็จะมีการทดสอบ pull image มา แล้ว run เป็น container รวมทั้งอาจมีการสร้างพื้นที่เก็บข้อมูลที่เรียกว่า volumes (ทั้งแบบ named volume และ anonymous volume) บ่อยครั้งเมื่อเราใช้คำสั่งตรวจสอบ เราจะพบว่ามีอะไรไม่รู้หลงเหลืออยู่กินเนื้อที่ไปเยอะ

    ตรวจสอบรายการ container

    $ docker ps -a

    ลบ containers ที่ไม่ใช้งานแล้ว

    $ docker ps --filter status=dead --filter status=exited --filter status=created -aq | xargs -r docker rm -v

    หมายเหตุ คำสั่งด้านบนนี้จะลบ data containers ด้วย ถ้ามีการสร้าง container ชนิดเก็บ data โปรดตรวจสอบให้ดีนะ ปัจจุบัน data container นั้น deprecated (ไม่แนะนำให้ใช้งาน)

    ตรวจสอบรายการ images

    $ docker images

    ลบ images ที่ไม่ใช้งานแล้ว

    $ docker images --no-trunc | grep '<none>' | awk '{ print $3 }' | xargs -r docker rmi

    ตรวจสอบรายการ volumes

    $ docker volumes ls

    ลบ volume ที่ไม่ถูกใช้งานโดย container ใด ๆ เลย

    $ docker volume ls -q -f dangling=true | xargs -r docker volume rm

    หรือจะใช้อีกแบบ แต่ต้องติดตั้ง jq เพิ่มด้วย

    $ sudo apt install jq
    $ docker ps -aq | xargs docker inspect | jq -r '.[] | .Mounts | .[] | .Name | select(.)'

     

    References:

  • เตาะแตะไปกับ Docker ตอนที่ 7 Manage data

    Docker ให้เราสามารถเลือกใช้วิธีการ mount data เข้าไปให้กับ container อยู่ 3 อย่างคือ
    1. Volumes
    2. Bind mounts
    3. tmpfs mounts

    Volumes จะถูกเก็บอยู่ในส่วนของ Host filesystem ที่จัดการโดย Docker เอง (อยู่ที่ /var/lib/docker/volumes)
    และนี่เป็นวิธีที่ดีที่สุดในการจัดเก็บข้อมูลที่เป็น persistent data (ตามคำบอกในเว็บเพจ docs.docker.com)

    Bind mounts จะถูกเก็บอยู่ในที่ไหนก็ได้ของ Host filesystem เป็นวิธีการที่มีมาตั้งแต่ Docker รุ่นแรก ๆ จึงมีข้อจำกัดเมื่อเทียบกับ Volumes

    tmpfs mounts จะถูกเก็บอยู่ในหน่วยความจำของ Host เท่านั้น

    อ่านรายละเอียดเพิ่มเติมได้จากที่นี่ https://docs.docker.com/engine/admin/volumes/ และ
    https://docs.docker.com/engine/admin/volumes/#more-details-about-mount-types

    ผมขอเล่าถึงตัวอย่างการใช้งาน Volumes ใน docker-compose.yml (version 2) ที่ผมได้ทำเสร็จแล้ว

    $ cat docker-compose.yml 
    version: '2'
    services:
     openldap:
     image: openldap
     container_name: openldap
     volumes:
      - ldapdatavol:/var/lib/ldap
      - ldapconfigvol:/etc/ldap/slapd.d
     ports:
      - "389:389"
      - "636:636"
    
    volumes:
     ldapdatavol:
      external: false
     ldapconfigvol:
      external: false

    อธิบายได้ดังนี้ ในไฟล์ docker-compose.yml นี้ เราจะรัน services ชื่อ openldap จาก image ที่สร้างไว้แล้วชื่อว่า openldap โดยรันเป็น container ที่ผมตั้งชื่อว่า openldap โดยจะเก็บข้อมูลไว้ถาวรที่ volume ชื่อ ldapdatavol ซึ่งจะ mapped กับ /var/lib/ldap ใน container และอีกบรรทัดคือ ldapconfigvol จะ mapped กับ /etc/ldap/slapd.d ใน container

    ถัดมาด้านล่างของไฟล์ เราจะต้องประกาศ volumes ไว้ด้วยว่า ldapdatavol ไม่ได้เป็น volume ที่สร้างไว้อยู่แล้วก่อนการัน docker-compose ด้วยการประกาศค่าว่า external: false เช่นเดียวกับ volume ชื่อ ldapconfigvol

    แต่ถ้าใช้ external: true จะหมายถึง docker-compose จะไม่สร้าง volume ให้ นั่นคือ เราได้สร้างไว้ก่อนแล้วด้วยคำสั่ง

    $ docker volume create --name ldapdatavol
    $ docker volume create --name ldapconfigvol

    เราสามารถดูรายการ volume ด้วยคำสั่งนี้

    $ docker volume ls

    และที่เก็บจริง ๆ จะอยู่ที่นี่ /var/lib/docker/volumes ใช้คำสั่งเปลี่ยนสิทธิเป็น root เข้าไปที่เก็บ volume แล้วเราจะสามารถสำรองข้อมูลนี้ได้โดยใช้คำสั่ง cp หรือ tar ได้เลย ดังนี้

    $ sudo su -
    # cd /var/lib/docker/volumes

    การใช้งาน volume แบบที่แนะนำนี้เรียกว่า named volume คือ เราตั้งเป็นชื่อตามที่เราคิดเอง ส่วนอีกแบบจะเรียกว่า anonymous นั่นคือ docker ตั้งชื่อให้เอง อันนี้ผมไม่ลงรายละเอียดครับ

    จบตอนนี้เราก็จะพอเข้าใจได้แล้วว่า หากจะเก็บข้อมูลของ app เช่นในตัวอย่างนี้คือ openldap ผมจะเลือกใช้ named volume ครับ ข้อมูลจะอยู่ถาวร เรียกว่าการทำ persistent data ในขณะที่ถ้าเราไม่เพิ่มการใช้ volume เข้ามากำหนดที่เก็บข้อมูล หากเราลบ container ก็จะเป็นการลบข้อมูลซึ่งอยู่ใน container ไปด้วย นอกจากว่าเราต้องการให้เป็นอย่างนั้นอาจเพราะว่าข้อมูลเป็นแค่ตัวอย่างไม่สำคัญอะไร ก็ไม่ต้องใช้ volume ครับ

  • 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/