Day: September 15, 2017

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

  • วิธียกเลิก “Keep a local copy as well” บน PSU Webmail

    จากที่เริ่มมีการใช้งาน PSU GSuite (Google Apps for Education – GAFE เดิม) ซึ่งมีเอกสารแนะนำวิธีการใช้งานคือ

    http://gafe.psu.ac.th/support/1/1

    ในช่วงแรก เกรงผู้ใช้จะไม่คุ้นชินกับ Gmail (หึมมมม) ก็เลยแนะนำให้ทำ “Keep a local copy as well” ไว้ด้วย เผื่อว่า ยังสับสน ก็จะได้ดูบน PSU Webmail เดิมได้

    แต่ต่อมา ก็อาจจะลืมไปว่า Redirect ไปแล้ว ก็ยังมีเก็บไว้ในพื้นที่ PSU Webmail อยู่ ไม่ได้เข้ามาลบอีกเลย นานเข้าก็ทำให้พื้นที่เต็ม

    ต่อไปนี้ เป็น วิธียกเลิก “Keep a local copy as well” บน PSU Webmail

    1. Login เข้า PSU Webmail ที่ https://webmail.psu.ac.th
    2. ลบ Email จนได้พื้นที่เป็นสีเขียว
      จาก

      เป็น
    3. คลิก Filters
    4. คลิก Edit ในบรรทัดที่เป็นของ @g.psu.ac.th
    5. คลิก Keep a local copy as well ออก
      จาก

      เป็น
    6. เลื่อนไปล่างสุดของหน้าจอ คลิกปุ่ม Apply Change
    7. เสร็จ จบ
  • เตาะแตะไปกับ 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 ครับ