Author: jatuporn.ch

  • เรียนรู้เทคโนโลยี OAuth2

    OAuth2 คืออะไร ทำไมต้องใช้

                 OAuth2 คือมาตรฐานหนึ่งของระบบยืนยันตัวตน และจัดการสิทธิ์การเข้าใช้งานระบบต่าง ๆ เป็นมาตรฐาน rfc6747[1] ที่ใช้สำหรับ Client เชื่อมต่อกับ Server ที่ใช้ในการ Authen & Authorize เพื่อให้ได้รับสิ่งที่เรียกว่า Access Token เพื่อใช้แทน Username และ Password (สามารถใช้อย่างอื่นเพื่อขอ Token ก็ได้) เพื่อนำไปใช้กับบริการอื่น ๆ ทำให้มีความปลอดภัยมากขึ้น รวมถึงบอกว่าทำมีสิทธิ์ทำอะไรได้บ้างกับบริการนั้น ๆ (จริง ๆ แล้วถ้า Access Token หลุดก็เอาไปเข้าระบบอื่น ๆ ได้ อาจจะต่างตรงแค่ไม่เห็น Password) โดยแนะนำต้องใช้คู่กับ https อีกชั้นเพื่อความปลอดภัยสูงสุด โดยแสดงภาพคร่าว ๆ เป็น Protocol Flow ดังรูป[2]

                  โดย Access Token จะมีเวลาจำกัดในการใช้งานเมื่อ Token หมดอายุ ก็ต้องไปขอใหม่ เมื่อเลิกใช้งานก็ขอยกเลิก Token รูปแบบการใช้งานมี 4 รูปแบบหรือเรียกว่า grant_type โดยแต่ละแบบมีรายละเอียดดังนี้[3]

    1. Authorization Codeใช้สำหรับ Web Server ที่ใช้ Code ด้านหลังในการเชื่อมต่อกับ OAuth Server โดยไม่ได้เปิดเผยให้สาธารณะเห็น อธิบายเป็นลักษณะการใช้งานคือ
      – ผู้ใช้งานเข้า Web Site
      – จะมีให้กด Login Facebook, Twitter, Google หรืออื่น ๆ 
      – เมื่อผู้ใช้กดก็จะเด้งให้ไป Login ที่ผู้ให้บริการนั้น ๆ ถ้าเคย Login ไว้แล้วก็จะข้ามขั้นตอนนี้ไป
      – ถ้าผู้ให้บริการนั้น ๆ เช่น Facebook จะให้กดยอมรับข้ออนุญาต ส่วนมากจะถามเรื่องสิทธิ์ในการเข้าถึงข้อมูลส่วนตัว
      – เมื่อผู้ใช้กดอนุญาต ก็จะกลับมายัง Web Site โดยในเบื้องหลัง WebSite จะได้ authorization code มาเรียกร้อยแล้วจากผู้ให้บริการ
      – จากนั้นทาง Web Site ก็สามารถเข้าถึงข้อมูลของผู้ให้บริการนั้น ๆ ได้ตามสิทธิที่อนุญาตไว้


      วิธีใช้ authorization code

      1. มีปุ่ม login ซึ่งมี link มี parameter คล้ายๆแบบนี้
        https://[oauth-server]/authorize?response_type=code&client_id=testclient&client_secret=testpass&redirect_uri=http%3A%2F%2F10.1.0.20%3A32778%2F%3Fauth%3Dsso
      2. เมื่อกดปุ่ม login ระบบจะต้องแจ้งว่า จะขอใช้สิทธิเรื่องใดบ้าง

      3. เมื่อผู้ใช้กดตกลงอนุญาต หน้าจอจะถูกพาไปยัง redirect_uri ที่ระบุไว้ พร้อมทั้งส่ง authorization code มาให้ด้วย
      4. ซึ่งจะมีหน้าตาประมาณนี้
         https://yoursite.com/oauth/callback?code=xxx 
      5. อ่าน code ออกมาเพื่อนำไปขอ access_token กับ API ของผู้ให้บริการ login ตัวนั้นๆ
         POST https://api.blah.com/oauth/token?grant_type=authorization_code&code=xxx&redirect_uri=xxx&client_id=xxx&client_secret=xxx 
        

        ค่า client_id, client_secret โดยมาก เจ้าของ login API (Identity provider) จะเป็นคนกำหนดมาให้

        หลังจากส่ง code ด้วย HTTP method POST และบอกว่าเป็น grant_type แบบ authorization_code ไปแล้ว client จะได้ access_token กลับมา เราจะเอา access_token นั้นในการเรียก API อื่น ๆ ต่อไป

    2. Implicit

      ใช้สำหรับ App ฝั่ง Client ซึ่งไม่จำเป็นต้องมี Web Server เป็นเหมือนการคุยระหว่าง Web Browser Client กับ OAuth Server ตรง ๆ เหมาะกับพวกที่ลงท้ายด้วย JS เช่น ReactJS, AngularJS ที่ต้องการดึงข้อมูลด้วย Browser เลย (เหมาะกับ Mobile เป็นพิเศษ) ลักษณะการทำงานคล้าย ๆ กับข้อ 1 แต่จะต่างกันตรงไม่ต้องส่ง Client_Secret เป็นวิธีที่เปิดเผยให้สาธารณะเห็น

      วิธีใช้ Implicit

      1. สร้างปุ่ม login ที่ส่ง action ไปยัง URL แบบนี้
         https://login.blah.com/oauth?response_type=token&client_id=xxx&redirect_uri=xxx&scope=email 
      2. เมื่อผู้ใช้กดปุ่ม จะแสดงหน้าต่างขอใช้สิทธิ หากตกลงข้อมูลจะ submit ไป server แล้วข้อมูล token จะถูกส่งกลับมาตาม redirect_uri ที่กำหนดเอา
      3. client_id ในข้อ 1 id provider เป็นคนกำหนดมาให้
      4. token ที่ได้มา เอาไปใช้ได้ดึงข้อมูลตามสิทธิ์ที่ได้มาได้เลย
    3. Password Credentials

      ใช้สำหรับ Application ที่มีการจัดการสิทธิเอง แต่ต้องการยืนยันตัวตนเท่านั้น ซึ่งวิธีนี้ไม่ต้อง Redirect ไปที่ผู้ให้บริการอื่น วิธีนี้เหมาะกับการใช้งานที่เป็นบริการของตัวเอง เพราะ username password จะปรากฎในเครื่องที่ส่งขอ token ถ้าไปรันวิธีนี้บน Server อื่นที่ไม่ได้เป็นเจ้าของ แสดงว่า เขาอาจจะดักเอา username password ไปใช้ก็ได้


      การใช้ Password Credentials

      1. มี form รับ username/password เมื่อกด submit แล้ว ส่ง form submit (POST method) ไปยัง server/service ของเรา
         POST https://login.blah.com/oauth/token?grant_type=password&username=xxx&password=xxx&client_id=xxx 
      2. จะได้ access_token มาใช้งานได้เลยหากใส่ข้อมูลถูกต้อง
    4. Client Credentials

      ในกรณีที่เป็นการคุยระหว่าง Application -> Service โดยจะไม่เกี่ยวข้องกับผู้ใช้ ยกตัวอย่างว่าเราอาจจะได้ข้อมูลสักอย่างแต่ต้องการความปลอดภัยว่าต้องเป็นเครื่องที่เราให้สิทธิ์ ก็สามารถส่ง id และ secret ที่ออกให้ Application ส่งมาขอ token เพื่อเข้าถึง Service นั้น ๆ ได้เลย

    OAuth2 ปลอดภัยหรือไม่

          อยู่ที่การใช้งาน ว่าปลอดภัยหรือไม่ ถ้ารันบน http ธรรมดา ยังไงก็ไม่ปลอดภัย ถ้าใช้ php 4/5 หรือ windows 2003/2008/2008/2008 R2 ยังไงก็ไม่ปลอดภัย 

    แล้วทำไมต้องใช้ OAuth2

    – เนื่องจากเป็นมาตรฐานที่พัฒนาจาก OAuth1.0a ที่มีการใช้งานมาก ทำให้ Version 2 ซึ่งลดความซับซ้อนลง การใช้งานจึงเข้าใจง่ายขึ้น

    – เร็วกว่า xml web service ใช้ json ในการสื่อสาร เพราะขนาดข้อมูลที่ส่งจะเล็กกว่ามาก

    – มีผู้ให้บริการภายนอกหลากหลาย

    – เหมาะกับใช้งานที่หลากหลาย เพราะรองรับหลากหลายภาษา (Java, Python, Go, .NET. Ruby, PHP, .NET, ฯลฯ)

    – สามารถประยุกต์นำมาใช้งานเป็น Single Sign On ได้ (ต้องพัฒนาเพิ่มเอง ไม่มีมาให้ในมาตรฐาน)

    =================================================

    References :
    [1] The OAuth 2.0 Authorization Framework : https://tools.ietf.org/html/rfc6749

    [2] An Introduction to OAuth2 : https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2

    [3] OAuth 2.0 clients in Java programming, Part 1, The resource owner password credentials grant : https://www.ibm.com/developerworks/library/se-oauthjavapt1/index.html

  • วิธีการติดตั้ง Kubernetes Add-on DNS และ DashBoard

    Kubernetes มี DNS กับ GUI ให้ใช้งานบ้างไหม

                หลังจากติดตั้ง Kubernetes แล้วการสร้าง containner โดยในที่นี้เรียกว่า pod (ใช้เรียก 1 containner หรือกลุ่มของ containner ก็ได้) ซึ่งสามารถสร้างด้วยคำสั่ง Kubectl ได้เลย (สร้างจากที่ไหนก็ได้) สำหรับผู้ที่ยังไม่ได้ติดตั้ง ต้องติดตั้งก่อนตามวิธีติดตั้ง Kubernetes บน CoreOS ตอนที่ 1[1] และ ตอนที่ 2[2] 

    สำหรับ Add-on ที่แนะนำจะเป็น DNS และ Dashboard ซึ่งเรียกได้ว่าจำเป็นต้องมี ไม่งั้นชีวิตจะยุ่งยากขึ้นเยอะครับ คงไม่มีใครอยากดูรายการ pod เยอะ ๆ ด้วย command line กันนะครับ

     วิธีการติดตั้ง DNS-Addon[3]

    • เข้าไปยังเครื่องที่ติดตั้ง Kubectl ไว้แล้ว ทำการสร้าง yaml file ดังนี้ (ถ้า vim วางแล้วเพี้ยนให้ใช้ nano แทน)
      nano dns-addon.yaml

      เนื้อหาในไฟล์ดังนี้ (อย่าลืมเปลี่ยน > เป็น > เวลา Copy & Paste ด้วยครับ)

      apiVersion: v1
      kind: Service
      metadata:
        name: kube-dns
        namespace: kube-system
        labels:
          k8s-app: kube-dns
          kubernetes.io/cluster-service: "true"
          kubernetes.io/name: "KubeDNS"
      spec:
        selector:
          k8s-app: kube-dns
        clusterIP: <DNS Cluster-IP>
        ports:
        - name: dns
          port: 53
          protocol: UDP
        - name: dns-tcp
          port: 53
          protocol: TCP
      
      
      ---
      
      
      apiVersion: v1
      kind: ReplicationController
      metadata:
        name: kube-dns-v20
        namespace: kube-system
        labels:
          k8s-app: kube-dns
          version: v20
          kubernetes.io/cluster-service: "true"
      spec:
        replicas: 1
        selector:
          k8s-app: kube-dns
          version: v20
        template:
          metadata:
            labels:
              k8s-app: kube-dns
              version: v20
            annotations:
              scheduler.alpha.kubernetes.io/critical-pod: ''
              scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
          spec:
            containers:
            - name: kubedns
              image: gcr.io/google_containers/kubedns-amd64:1.9
              resources:
                limits:
                  memory: 170Mi
                requests:
                  cpu: 100m
                  memory: 70Mi
              livenessProbe:
                httpGet:
                  path: /healthz-kubedns
                  port: 8080
                  scheme: HTTP
                initialDelaySeconds: 60
                timeoutSeconds: 5
                successThreshold: 1
                failureThreshold: 5
              readinessProbe:
                httpGet:
                  path: /readiness
                  port: 8081
                  scheme: HTTP
                initialDelaySeconds: 3
                timeoutSeconds: 5
              args:
              - --domain=cluster.local.
              - --dns-port=10053
              ports:
              - containerPort: 10053
                name: dns-local
                protocol: UDP
              - containerPort: 10053
                name: dns-tcp-local
                protocol: TCP
            - name: dnsmasq
              image: gcr.io/google_containers/kube-dnsmasq-amd64:1.4
              livenessProbe:
                httpGet:
                  path: /healthz-dnsmasq
                  port: 8080
                  scheme: HTTP
                initialDelaySeconds: 60
                timeoutSeconds: 5
                successThreshold: 1
                failureThreshold: 5
              args:
              - --cache-size=1000
              - --no-resolv
              - --server=127.0.0.1#10053
              - --log-facility=-
              ports:
              - containerPort: 53
                name: dns
                protocol: UDP
              - containerPort: 53
                name: dns-tcp
                protocol: TCP
            - name: healthz
              image: gcr.io/google_containers/exechealthz-amd64:1.2
              resources:
                limits:
                  memory: 50Mi
                requests:
                  cpu: 10m
                  memory: 50Mi
              args:
              - --cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 > /dev/null
              - --url=/healthz-dnsmasq
              - --cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1:10053 > /dev/null
              - --url=/healthz-kubedns
              - --port=8080
              - --quiet
              ports:
              - containerPort: 8080
                protocol: TCP
            dnsPolicy: Default
    • จากนั้นทำการสร้าง pod ดังนี้
      kubectl create -f dns-addon.yaml
    • สามารถตรวจสอบสถานะการสร้าง pod และ log ได้ดังนี้
      kubectl get pods --namespace=kube-system
      kubectl logs kube-dns-v20-xxxxx -n=kube-system dnsmasq
    • โดยอันที่จริงมีอีกหลายแบบครับ มีแบบ auto scale ก็มีครับ ลองศึกษาดูใน Internet ครับ

      วิธีการติดตั้ง Dashboard-Addon[3]

    • เข้าไปยังเครื่องที่ติดตั้ง Kubectl ไว้แล้ว ทำการสร้าง yaml file ดังนี้ (ถ้า vim วางแล้วเพี้ยนให้ใช้ nano แทน)
      nano kube-dashboard-rc.yaml

      เนื้อหาในไฟล์ดังนี้

      apiVersion: v1
      kind: ReplicationController
      metadata:
        name: kubernetes-dashboard-v1.6.1
        namespace: kube-system
        labels:
          k8s-app: kubernetes-dashboard
          version: v1.6.1
          kubernetes.io/cluster-service: "true"
      spec:
        replicas: 1
        selector:
          k8s-app: kubernetes-dashboard
        template:
          metadata:
            labels:
              k8s-app: kubernetes-dashboard
              version: v1.6.1
              kubernetes.io/cluster-service: "true"
            annotations:
              scheduler.alpha.kubernetes.io/critical-pod: ''
              scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
          spec:
            containers:
            - name: kubernetes-dashboard
              image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.6.1
              resources:
                limits:
                  cpu: 100m
                  memory: 50Mi
                requests:
                  cpu: 100m
                  memory: 50Mi
              ports:
              - containerPort: 9090
              livenessProbe:
                httpGet:
                  path: /
                  port: 9090
                initialDelaySeconds: 30
                timeoutSeconds: 30
    • จากนั้นสร้างอีกไฟล์ดังนี้
      nano kube-dashboard-svc.yaml

      เนื้อหาในไฟล์ดังนี้

      apiVersion: v1
      kind: Service
      metadata:
        name: kubernetes-dashboard
        namespace: kube-system
        labels:
          k8s-app: kubernetes-dashboard
          kubernetes.io/cluster-service: "true"
      spec:
        selector:
          k8s-app: kubernetes-dashboard
        ports:
        - port: 80
          targetPort: 9090
    • จากนั้นทำการสร้าง pod ดังนี้
      kubectl create -f kube-dashboard-rc.yaml
      kubectl create -f kube-dashboard-svc.yaml
    • สามารถตรวจสอบสถานะการสร้าง pod และทำการ forward port ได้ดังนี้
      kubectl get pods --namespace=kube-system
      kubectl port-forward kubernetes-dashboard-v1.6.1-XXXX 9090 --namespace=kube-system

       

    • หลังจากนั้นสามารถทดสอบเปิด Browser บนเครื่องที่รัน kubectl เพื่อทดสอบได้เลย (สามารถเปิดใช้งานได้ 2 วิธีคือผ่านช่องทางปกติ หรือใช้ kubectl ดึงมาจาก pod โดยตรงแล้วทำการ forward port ก็ได้)

                      จะเห็นว่าถ้าเป็น web จะดูอะไรก็ง่ายขึ้นเยอะครับ แต่เนื่องจากไม่มีระบบ login ก็ต้องระวังดี ๆ ถ้าจะเปิดให้ใครก็ได้ใช้งาน อาจต้องสร้าง proxy ดักให้ใส่รหัสผ่านก่อนชั้นหนึ่ง หรือจะเน้นใช้ command line อย่างเดียวก็ได้ครับ….

    ==================================

    Reference :

    [1] รู้จักกับ Kubernetes และวิธีติดตั้ง Kubernetes ด้วย CoreOS (ตอนที่ 1 Master Node) : https://sysadmin.psu.ac.th/2017/05/17/setup-kubernetes-coreos-section-1/

    [2] วิธีติดตั้ง Kubernetes ด้วย CoreOS (ตอนที่ 2 Worker Node) : https://sysadmin.psu.ac.th/2017/05/27/setup-kubernetes-coreos-section-2/

    [3] CoreOS -> Deploy Add-ons : https://coreos.com/kubernetes/docs/latest/deploy-addons.html

     

  • วิธีติดตั้ง Kubernetes ด้วย CoreOS (ตอนที่ 2 Worker Node)

    ถ้าต้องการระบบจัดการ docker container สักตัวต้องทำอย่างไร (ตอนที่ 2)

                จากตอนก่อนหน้านี้ รู้จักกับ Kubernetes และวิธีติดตั้ง Kubernetes ด้วย CoreOS (ตอนที่ 1 Master Node)[1] ก็มาต่อด้วยการติดตั้งบนเครื่อง Worker ต่อครับ

     วิธีการติดตั้ง Kubernetes Worker Node[2]

    • ทำการติดตั้ง CoreOS บน Worker Node
    • สร้าง directory ดังนี้
      sudo mkdir -p /etc/kubernetes/ssl
    • ทำการ copy ที่สร้างไว้ก่อนหน้านี้มาไว้ใน folder ดังนี้
      File: /etc/kubernetes/ssl/ca.pem
      File: /etc/kubernetes/ssl/${WORKER_FQDN}-worker.pem
      File: /etc/kubernetes/ssl/${WORKER_FQDN}-worker-key.pem
    • เปลี่ยน permission และ owner file เฉพาะไฟล์ -key* ดังนี้
      sudo chmod 600 /etc/kubernetes/ssl/*-key.pem
      sudo chown root:root /etc/kubernetes/ssl/*-key.pem
    • ทำการทำ link เผื่อให้ config แต่ละ worker เหมือน ๆ กัน
      cd /etc/kubernetes/ssl/
      sudo ln -s ${WORKER_FQDN}-worker.pem worker.pem
      sudo ln -s ${WORKER_FQDN}-worker-key.pem worker-key.pem
    • จากนั้นตั้งค่า flannel network เป็น network ที่เอาไว้เชื่อมแต่ละ pod ให้สามารถคุยกันได้ข้ามเครื่อง เพื่อสร้างวง virtual ip ให้กับ pod ดังนี้
      sudo mkdir /etc/flannel

      ทำการสร้างไฟล์ option.env

      sudo vim /etc/flannel/options.env

      เนื้อหาในไฟล์ดังนี้

      FLANNELD_IFACE=<Worker IP>
      FLANNELD_ETCD_ENDPOINTS=http://<Master IP>:2379,http://<Node1 IP>:2379,http://<Node2 IP>:2379
    • จากนั้นทำการสร้าง flannel service ดังนี้
      sudo mkdir -p /etc/systemd/system/flanneld.service.d/
      sudo vim /etc/systemd/system/flanneld.service.d/40-ExecStartPre-symlink.conf

      เนื้อหาในไฟล์ดังนี้

      [Service]
      ExecStartPre=/usr/bin/ln -sf /etc/flannel/options.env /run/flannel/options.env
    • จากนั้นทำการตั้งค่า docker เพื่อกำหนดให้ใช้งานผ่าน flannel โดยสร้างไฟล์ config ดังนี้
      sudo mkdir -p /etc/systemd/system/docker.service.d
      sudo vim /etc/systemd/system/docker.service.d/40-flannel.conf

      เนื้อหาในไฟล์ดังนี้

      [Unit]
      Requires=flanneld.service
      After=flanneld.service
      [Service]
      EnvironmentFile=/etc/kubernetes/cni/docker_opts_cni.env

      โดยที่ทำสร้างไฟล์ docker_opts_cni.env ขึ้นมาประกอบดังนี้

      sudo mkdir -p /etc/kubernetes/cni
      sudo vim /etc/kubernetes/cni/docker_opts_cni.env

      เนื้อหาในไฟล์ดังนี้

      DOCKER_OPT_BIP=""
      DOCKER_OPT_IPMASQ=""
    • จากนั้นทำการ restart docker service สักรอบดังนี้
      sudo systemctl restart docker
    • หนังจากนั้นทำการสร้าง config ไฟล์สุดท้ายสำหรับ flannel ดังนี้
      sudo mkdir -p /etc/kubernetes/cni/net.d
      sudo vim /etc/kubernetes/cni/net.d/10-flannel.conf

      เนื้อหาในไฟล์ดังนี้

      {
       "name": "podnet",
       "type": "flannel",
       "delegate": {
       "isDefaultGateway": true
       }
      }
    • ทำการ start flannel service และตรวจสถานะการทำงานดู
      sudo systemctl start flanneld
      sudo systemctl enable flanneld
      sudo systemctl status flanneld
    • จากนั้นทำการสร้าง kubelet service ดังนี้
      sudo vim /etc/systemd/system/kubelet.service

      เนื้อหาในไฟล์ดังนี้ (KUBELET_IMAGE_TAG version สามารถตรวจสอบล่าสุดได้ที่ https://quay.io/repository/coreos/hyperkube?tab=tags)

      [Service]
      Environment=KUBELET_IMAGE_TAG=v1.6.2_coreos.0
      Environment="RKT_RUN_ARGS=--uuid-file-save=/var/run/kubelet-pod.uuid \
        --volume var-log,kind=host,source=/var/log \
        --mount volume=var-log,target=/var/log \
        --volume dns,kind=host,source=/etc/resolv.conf \
        --mount volume=dns,target=/etc/resolv.conf"
      ExecStartPre=/usr/bin/mkdir -p /etc/kubernetes/manifests
      ExecStartPre=/usr/bin/mkdir -p /var/log/containers
      ExecStartPre=-/usr/bin/rkt rm --uuid-file=/var/run/kubelet-pod.uuid
      ExecStart=/usr/lib/coreos/kubelet-wrapper \
        --api-servers=https://<Master_IP> \
        --cni-conf-dir=/etc/kubernetes/cni/net.d \
        --network-plugin=cni \
        --container-runtime=docker \
        --register-node=true \
        --allow-privileged=true \
        --pod-manifest-path=/etc/kubernetes/manifests \
        --cluster_dns=<DNS_SERVICE_IP> \
        --cluster_domain=<CLUSTER_DNS_NAME> \
        --kubeconfig=/etc/kubernetes/worker-kubeconfig.yaml \
        --tls-cert-file=/etc/kubernetes/ssl/worker.pem \
        --tls-private-key-file=/etc/kubernetes/ssl/worker-key.pem
      ExecStop=-/usr/bin/rkt stop --uuid-file=/var/run/kubelet-pod.uuid
      Restart=always
      RestartSec=10
      
      [Install]
      WantedBy=multi-user.target
    • ทำการ start kubelet service และตรวจสถานะการทำงานดู (ต้องรอสักพักเพราะ service จะต้องโหลด image มาติดตั้งให้ในการ start ครั้งแรก สามารถใช้คำสั่ง journalctl -xe เพื่อตรวจสอบสถานะการทำงาน) ซึ่งจะพบว่ายัง error เพราะยังไม่ได้ลง kube-apiserver (แนะนำลงตัวอื่นให้เสร็จให้หมดแล้วค่อยมา start kubelet service ก็ได้)
      sudo systemctl start kubelet
      sudo systemctl enable kubelet
      sudo systemctl status kubelet
    • หลังจากนั้นทำการติดตั้ง kube-proxy ดังนี้
      sudo vim /etc/kubernetes/manifests/kube-proxy.yaml

      มีเนื้อหาในไฟล์ดังนี้

      apiVersion: v1
      kind: Pod
      metadata:
        name: kube-proxy
        namespace: kube-system
      spec:
        hostNetwork: true
        containers:
        - name: kube-proxy
          image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
          command:
          - /hyperkube
          - proxy
          - --master=https://${MASTER_IP}
          - --kubeconfig=/etc/kubernetes/worker-kubeconfig.yaml
          securityContext:
            privileged: true
          volumeMounts:
          - mountPath: /etc/ssl/certs
            name: "ssl-certs"
          - mountPath: /etc/kubernetes/worker-kubeconfig.yaml
            name: "kubeconfig"
            readOnly: true
          - mountPath: /etc/kubernetes/ssl
            name: "etc-kube-ssl"
            readOnly: true
        volumes:
        - name: "ssl-certs"
          hostPath:
            path: "/usr/share/ca-certificates"
        - name: "kubeconfig"
          hostPath:
            path: "/etc/kubernetes/worker-kubeconfig.yaml"
        - name: "etc-kube-ssl"
          hostPath:
            path: "/etc/kubernetes/ssl"
      
    • ต่อด้วยการตั้งค่า kubeconfig ดังนี้
      sudo vim /etc/kubernetes/worker-kubeconfig.yaml

      มีเนื้อหาในไฟล์ดังนี้

      apiVersion: v1
      kind: Config
      clusters:
      - name: local
        cluster:
          certificate-authority: /etc/kubernetes/ssl/ca.pem
      users:
      - name: kubelet
        user:
          client-certificate: /etc/kubernetes/ssl/worker.pem
          client-key: /etc/kubernetes/ssl/worker-key.pem
      contexts:
      - context:
          cluster: local
          user: kubelet
        name: kubelet-context
      current-context: kubelet-context
    • ในกรณีที่ต้องการแจ้ง systemd ว่าเรามีการเปลี่ยนข้อมูลใน config และต้องการ rescan ทุกอย่างอีกครั้งให้สั่งดังนี้
      sudo systemctl daemon-reload
    • จากนั้นทดสอบ restart kubelet service ใหม่อีกครั้งและตรวจสอบ status หรือ journalctl -xe เพื่อดูว่าการทำงานปกติหรือไม่ ถ้าไม่แก้ให้ถูกต้องแล้ว restart ใหม่อีกครั้งไปเรื่อย ๆ จนกว่าจะปกติ
      sudo systemctl restart kubelet

      จบไปแล้วครับสำหรับการติดตั้ง worker node ครับ ต่อด้วยการติดตั้ง Kubectl สำหรับตรวจสอบสถานะแต่ละ Node ครับ

    วิธีการติดตั้ง Kubectl[3]

    • ทำการ Download tool ในที่นี้จะติดตั้งลงบนเครื่องอื่นที่ไม่ใช่ coreos (อย่าลืม copy เอา ca.pem,admin.pem,admin-key.pem ไปด้วย) เพราะ coreos ไม่อนุญาติให้เขียนบนพื้นที่ ../bin (จริง ๆ ติดตั้งที่ใดก็ได้ OS ใดก็ได้ครับยกตัวอย่าง windows[4]) โดยเลือก Version ให้สอดคล้องกับที่ใช้งานอยู่ครับ
      curl -O https://storage.googleapis.com/kubernetes-release/release/v1.6.2/bin/linux/amd64/kubectl
    • จากนั้นทำการเปลี่ยน permission ให้ execute ได้ และ ทำการ copy ไป global command
      chmod +x kubectl
      sudo mv kubectl /usr/local/sbin/ 
    • จากนั้นทำการตั้งค่าโปรแกรม (ต้องรันทุกครั้งหลังจาก Restart เครื่อง)
      kubectl config set-cluster default-cluster --server=https://<MASTER_IP> --certificate-authority=<cer path>/ca.pem
      kubectl config set-credentials default-admin --certificate-authority=<cer path>/ca.pem, --client-key=<cer path>/admin-key.pem --client-certificate=<cer path>/admin.pem
      kubectl config set-context default-system --cluster=default-cluster --user=default-admin
      kubectl config use-context default-system
    • จากนั้นจะสามารถใช้คำสั่งตรวจสอบสถานะได้ดังนี้
      ./kubectl get nodes

    จบไปแล้วครับสำหรับวิธีติดตั้ง Worker Node และ Kubectl ถ้าทำไม่ได้ลองทำซ้ำ ๆ ดูครับ ลองดู docker logs <containner_id> ตรวจดูปัญหาใน docker และ journalctl -xe เพื่อตรวจสอบ service ดูครับ…..

    ==================================

    Reference :

    [1] รู้จักกับ Kubernetes และวิธีติดตั้ง Kubernetes ด้วย CoreOS (ตอนที่ 1 Master Node) : https://sysadmin.psu.ac.th/2017/05/17/setup-kubernetes-coreos-section-1/

    [2] CoreOS -> Deploy Kubernetes Worker Node(s) : https://coreos.com/kubernetes/docs/latest/deploy-workers.html

    [3] CoreOS -> Setting up kubectl : https://coreos.com/kubernetes/docs/latest/configure-kubectl.html

    [4] Running kubectl on Windows : https://github.com/eirslett/kubectl-windows

  • รู้จักกับ Kubernetes และวิธีติดตั้ง Kubernetes ด้วย CoreOS (ตอนที่ 1 Master Node)

    ถ้าต้องการระบบจัดการ docker container สักตัวต้องทำอย่างไร

                เมื่อกล่าวถึงระบบจัดการ docker container สักตัวหนึ่ง มักจะกล่าวถึง opensource ตัวหนึ่งชื่อ kubernetes ซึ่งเป็นเครื่องมือที่พัฒนาขึ้นมาด้วย Google[1] ซึ่งสามารถรองรับทั้งในส่วนของ Google Container Engine และ CoreOS จริง ๆ ลงได้บนอีกหลาย platform แต่สำหรับ CoreOS ได้ออกแบบมาให้รองรับ Kubernetes ทำให้การติดตั้งง่ายและสมบูรณ์มากขึ้น (อ่านไปเรื่อย ๆ จะสงสัยนี่ง่ายแล้วเหรอ แต่หลังจากเขียนบทความนี้เสร็จก็มีตัวติดตั้งตัวใหม่ชื่อ Tectonic[6] น่าจะเป็นตัวที่ทำให้การติดตั้งง่ายขึ้นครับ)

                Kubernetes เป็นเครื่องมือที่ช่วย build, deploy และ scale ให้เป็นเรื่องง่าย ๆ และสามารถ replicate containner ได้ง่ายมาก การติดตั้ง Kubernetes บน Google Container Engine ง่ายมาก แต่การติดตั้งบน CoreOS จะยากกว่า ซึ่งสามารถติดตั้งแบบ Single Node และ Multi-Node ซึ่งถ้าจะทำ Multi-Node ต้องเชื่อมต่อไปยัง etcd service ที่ติดตั้งบน CoreOS Cluster ด้วยก็จะดีครับ (ไม่งั้นเป็น Cluster ที่ไม่สมบูรณ์) สามารถอ่านเพิ่มเติมได้ที่วิธีการติดตั้ง CoreOS Cluster[2] (ในบทความใช้ก่อนหน้านี้ CoreOS Cluster เขียนในส่วน ETCD Version 2 แต่การใช้งานกับ Kubernetes ต้องใช้ ETCD Version 3 ตอนนี้ยังไม่มีบทความเสริมในส่วนนี้ อาจจะต้องหาข้อมูลเองก่อนครับ จริง ๆ มีวิธีแต่ยังยากในการเขียนบทความ ไว้ค่อยเขียนเพิ่มให้อีกทีครับ) ซึ่งแน่นอนระหว่าง master node และ worker node มีการตรวจสอบเรื่อง certificate ระหว่าง service ด้วย

                ตัวอย่าง Diagram ของระบบทั้งหมด[3]

    เรียนรู้การค่าเบื้องต้นก่อนการติดตั้ง[4]

    • MASTER_HOST : คือ ชื่อ host ที่ node ลูกเอาไว้ติดต่อและให้เข้าถึงจาก external client  เช่นเวลาสั่งสร้างเครื่องก็จะสั่งผ่านตัวนี้ ในกรณที่ต้องการทำ HA MASTER_HOST จะถูกใช้เป็น load balance โดยใช้ร่วมกับ DNS name โดยการคุยกันระหว่าง MASTER_HOST และ worker (node ลูก) จะใช้ port 443 และมีการเข้ารหัสและยืนยันด้วย TLS Certificate
    • ETCD_ENDPOINT : คือ บริการของ etcd service ซึ่งมีใน CoreOS Cluster ที่ติดตั้งไว้ ให้ใส่ไปให้หมดว่ามีเครื่องอะไรบ้าง คั่นด้วย , (ในที่นี้ไม่ได้ใช้ fleet สร้างเครื่อง หลังจากลงเสร็จจะใช้ kubectl สร้างแทน)
    • POD_NETWORK : เช่น 10.2.0.0/16 ใช้สำหรับกำหนดวง IP ของ pod (pod คือ ชื่อเรียก container อาจจะแค่ 1 container เหรือเรียกเป็นกลุ่มของหลาย ๆ container ที่สร้างด้วย kubernetes)
    • SERVICE_IP_RANGE : เช่น 10.3.0.0/24 ใช้สำหรับ service cluster VIPs ซึ่งควรจะอยู่คนละวงกับ pod_network ซึ่งเป็น ip ที่ไว้ให้ข้างนอกวงเข้าถึง มักจะเป็น IP สำหรับให้บริการ Service ต่าง ๆ และไว้สำหรับใช้งานคู่กับ kube-proxy
    • K8S_SERVICE_IP : เช่น 10.3.0.1 เป็น IP หนึ่งในวง SERVICE_IP_RANGE เพื่อให้บริการ Kubernetes API Service
    • DNS_SERVICE_IP : เช่น 10.3.0.10 เป็น IP หนึ่งในวง SERVICE_IP_RANGE ใช้สำหรับเป็น VIP ของ DNS Service เพื่อให้บริการ DNS ให้กับ worker แต่ละตัว

    วิธีการตั้งค่า TLS สำหรับ Kubernetes[4]

    • ทำการสร้าง Cluster Root CA  (ทุกไฟล์ต่อจากนี้ทำเก็บไว้ที่ CoreOS สักเครื่องหนึ่ง ถึงเวลาติดตั้ง Master, Worker ค่อยมาโหลดมาใช้ครับ)
      sudo openssl genrsa -out ca-key.pem 2048
      sudo openssl req -x509 -new -nodes -key ca-key.pem -days 10000 -out ca.pem -subj "/CN=kube-ca"

    • ตั้งค่า openssl config สำหรับ api-server โดยการสร้างไฟล์ดังนี้
      vim openssl.cnf

      โดยมีเนื้อหาในไฟล์ดังนี้ (ให้ระบุ IP Service และ Master Host ลงไปด้วย)
      *หมายเหตุ : แทนที่เลข IP ใน <….>

      [req]
      req_extensions = v3_req
      distinguished_name = req_distinguished_name
      [req_distinguished_name]
      [ v3_req ]
      basicConstraints = CA:FALSE
      keyUsage = nonRepudiation, digitalSignature, keyEncipherment
      subjectAltName = @alt_names
      [alt_names]
      DNS.1 = kubernetes
      DNS.2 = kubernetes.default
      DNS.3 = kubernetes.default.svc
      DNS.4 = kubernetes.default.svc.cluster.local
      IP.1 = <K8S_SERVICE_IP>
      IP.2 = <MASTER_HOST>
    • จากนั้นทำการสร้าง API Server Keypair ดังนี้
      sudo openssl genrsa -out apiserver-key.pem 2048
      sudo openssl req -new -key apiserver-key.pem -out apiserver.csr -subj "/CN=kube-apiserver" -config openssl.cnf
      sudo openssl x509 -req -in apiserver.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out apiserver.pem -days 365 -extensions v3_req -extfile openssl.cnf

    • ต่อไปเป็นส่วนของสร้าง Cer ในเครื่องลูกทุกเครื่อง (สร้างทีละเครื่องนะครับ) ทำการสร้างไฟล์ดังนี้
      vim worker-openssl.cnf

      เนื้อหาในไฟล์ดังนี้ แทนที่ WORKER_IP ด้วยเครื่องที่ต้องการ

      [req]
      req_extensions = v3_req
      distinguished_name = req_distinguished_name
      [req_distinguished_name]
      [ v3_req ]
      basicConstraints = CA:FALSE
      keyUsage = nonRepudiation, digitalSignature, keyEncipherment
      subjectAltName = @alt_names
      [alt_names]
      IP.1 = <WORKER_IP>
    • จากนั้นทำการสร้าง Keypair ในแต่ละ Worker ดังนี้ (WORKER_FQDN คือชื่อที่จด domain ไว้ ไม่มีก็ใส่ /etc/hosts เอาก็ได้ครับ แต่ต้องใส่เองทุกเครื่อง)
      sudo openssl genrsa -out ${WORKER_FQDN}-worker-key.pem 2048
      sudo WORKER_IP=${WORKER_IP} openssl req -new -key ${WORKER_FQDN}-worker-key.pem -out ${WORKER_FQDN}-worker.csr -subj "/CN=${WORKER_FQDN}" -config worker-openssl.cnf
      sudo WORKER_IP=${WORKER_IP} openssl x509 -req -in ${WORKER_FQDN}-worker.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out ${WORKER_FQDN}-worker.pem -days 365 -extensions v3_req -extfile worker-openssl.cnf
    • สุดท้ายคือการสร้าง Keypair ของ Cluster Admin
       sudo openssl genrsa -out admin-key.pem 2048
       sudo openssl req -new -key admin-key.pem -out admin.csr -subj "/CN=kube-admin"
       sudo openssl x509 -req -in admin.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out admin.pem -days 365
    • ทำได้ครบแล้วก็จะได้ประมาณดังรูป (ยกตัวอย่างมี  4 worker ใช้วิธีเขียน gen-worker-keypair.sh เพื่อรันหลาย ๆ รอบ ลองหาวิธีเขียนดูเองนะครับ)

    วิธีการตั้งค่า Kubernetes บน CoreOS Cluster[5]

    เพื่อให้การติดตั้งง่ายขึ้น อย่าลืมกำหนดค่าต่าง ๆ จดไว้ก่อน ไม่งั้นจะงงได้ ประมาณนี้ครับ

    ETCD_ENDPOINT=http://a.b.c.d:2379,http://a.b.c.e:2379,http://a.b.c.f:2379
    SERVER_IP_NETWORK=?
    K8S_SERVICE_IP=?
    MASTER_HOST=?
    DNS_SERVICE_IP=?
    POD_NETWORK=?

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

    • ทำการติดตั้ง CoreOS บน Master Node
    • สร้าง directory ดังนี้
      sudo mkdir -p /etc/kubernetes/ssl
    • ทำการ copy ที่สร้างไว้ก่อนหน้านี้มาไว้ใน folder ดังนี้
      File: /etc/kubernetes/ssl/ca.pem
      File: /etc/kubernetes/ssl/apiserver.pem
      File: /etc/kubernetes/ssl/apiserver-key.pem
    • เปลี่ยน permission และ owner file เฉพาะไฟล์ -key* ดังนี้
      sudo chmod 600 /etc/kubernetes/ssl/*-key.pem
      sudo chown root:root /etc/kubernetes/ssl/*-key.pem
    • จากนั้นตั้งค่า flannel network[3] เป็น network ที่เอาไว้เชื่อมแต่ละ pod ให้สามารถคุยกันได้ข้ามเครื่อง (ต้องทำทุก node รวม worker ด้วย) เพื่อสร้างวง virtual ip ให้กับ pod ดังนี้
      sudo mkdir /etc/flannel

      ทำการสร้างไฟล์ option.env

      sudo vim /etc/flannel/options.env

      เนื้อหาในไฟล์ดังนี้

      FLANNELD_IFACE=<Master IP>
      FLANNELD_ETCD_ENDPOINTS=http://<Master IP>:2379,http://<Node1 IP>:2379,http://<Node2 IP>:2379
    • จากนั้นทำการสร้าง flannel service ดังนี้
      sudo mkdir -p /etc/systemd/system/flanneld.service.d/
      sudo vim /etc/systemd/system/flanneld.service.d/40-ExecStartPre-symlink.conf

      เนื้อหาในไฟล์ดังนี้

      [Service]
      ExecStartPre=/usr/bin/ln -sf /etc/flannel/options.env /run/flannel/options.env
    • จากนั้นทำการตั้งค่า docker เพื่อกำหนดให้ใช้งานผ่าน flannel โดยสร้างไฟล์ config ดังนี้
      sudo mkdir -p /etc/systemd/system/docker.service.d
      sudo vim /etc/systemd/system/docker.service.d/40-flannel.conf

      เนื้อหาในไฟล์ดังนี้

      [Unit]
      Requires=flanneld.service
      After=flanneld.service
      [Service]
      EnvironmentFile=/etc/kubernetes/cni/docker_opts_cni.env

      โดยที่ทำสร้างไฟล์ docker_opts_cni.env ขึ้นมาประกอบดังนี้

      sudo mkdir -p /etc/kubernetes/cni
      sudo vim /etc/kubernetes/cni/docker_opts_cni.env

      เนื้อหาในไฟล์ดังนี้

      DOCKER_OPT_BIP=""
      DOCKER_OPT_IPMASQ=""
    • จากนั้นทำการ restart docker service สักรอบดังนี้
      sudo systemctl restart docker
    • หนังจากนั้นทำการสร้าง config ไฟล์สุดท้ายสำหรับ flannel ดังนี้
      sudo mkdir -p /etc/kubernetes/cni/net.d
      sudo vim /etc/kubernetes/cni/net.d/10-flannel.conf

      เนื้อหาในไฟล์ดังนี้

      {
       "name": "podnet",
       "type": "flannel",
       "delegate": {
       "isDefaultGateway": true
       }
      }
    • ทำการ start flannel service และตรวจสถานะการทำงานดู
      sudo systemctl start flanneld
      sudo systemctl enable flanneld
      sudo systemctl status flanneld
    • จากนั้นทำการสั่งให้ทำ vxlan ผ่าน etcdctl ให้กับทุก node ดังนี้ (เปลี่ยน IP เป็นวงที่ต้องการ)
      sudo etcdctl set /coreos.com/network/config "{\"Network\":\"10.2.0.0/16\",\"Backend\":{\"Type\":\"vxlan\"}}"
    • จากนั้นทำการสร้าง kubelet service ดังนี้
      sudo vim /etc/systemd/system/kubelet.service

      เนื้อหาในไฟล์ดังนี้ (KUBELET_IMAGE_TAG version สามารถตรวจสอบล่าสุดได้ที่ https://quay.io/repository/coreos/hyperkube?tab=tags)

      [Service]
      Environment=KUBELET_IMAGE_TAG=v1.6.2_coreos.0
      Environment="RKT_RUN_ARGS=--uuid-file-save=/var/run/kubelet-pod.uuid \
        --volume var-log,kind=host,source=/var/log \
        --mount volume=var-log,target=/var/log \
        --volume dns,kind=host,source=/etc/resolv.conf \
        --mount volume=dns,target=/etc/resolv.conf"
      ExecStartPre=/usr/bin/mkdir -p /etc/kubernetes/manifests
      ExecStartPre=/usr/bin/mkdir -p /var/log/containers
      ExecStartPre=-/usr/bin/rkt rm --uuid-file=/var/run/kubelet-pod.uuid
      ExecStart=/usr/lib/coreos/kubelet-wrapper \
        --api-servers=http://127.0.0.1:8080 \
        --container-runtime=docker \
        --allow-privileged=true \
        --pod-manifest-path=/etc/kubernetes/manifests \
        --cluster_dns=<DNS_SERVICE_IP> \
        --cluster_domain=<CLUSTER_DNS_NAME>
      ExecStop=-/usr/bin/rkt stop --uuid-file=/var/run/kubelet-pod.uuid
      Restart=always
      RestartSec=10
      
      [Install]
      WantedBy=multi-user.target
    • ทำการ start kubelet service และตรวจสถานะการทำงานดู (ต้องรอสักพักเพราะ service จะต้องโหลด image มาติดตั้งให้ในการ start ครั้งแรก สามารถใช้คำสั่ง journalctl -xe เพื่อตรวจสอบสถานะการทำงาน) ซึ่งจะพบว่ายัง error เพราะยังไม่ได้ลง kube-apiserver (แนะนำลงตัวอื่นให้เสร็จให้หมดแล้วค่อยมา start kubelet service ก็ได้)
      sudo systemctl start kubelet
      sudo systemctl enable kubelet
      sudo systemctl status kubelet
    • ขั้นตอนต่อไปเป็นการสร้าง Rest API สำหรับจัดการ kubernetes (รวมถึงสามารถติดตั้งหน้าจอ GUI เพื่อควบคุมผ่าน Web ได้) โดยสร้างไฟล์ดังนี้
      sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml

      เนื้อหาในไฟล์มีดังนี้ (ระวังเรื่องวรรคด้วยนะครับ ถ้าจะพิมพ์เองไม่แนะนำ copy ไปแก้ดีกว่า)

      apiVersion: v1
      kind: Pod
      metadata:
        name: kube-apiserver
        namespace: kube-system
      spec:
        hostNetwork: true
        containers:
        - name: kube-apiserver
          image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
          command:
          - /hyperkube
          - apiserver
          - --bind-address=0.0.0.0
          - --etcd-servers=${ETCD_ENDPOINTS1},${ETCD_ENDPOINTS2},${ETCD_ENDPOINTS3}
          - --allow-privileged=true
          - --service-cluster-ip-range=${SERVICE_IP_RANGE}
          - --secure-port=443
          - --advertise-address=${ADVERTISE_IP}
          - --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota
          - --tls-cert-file=/etc/kubernetes/ssl/apiserver.pem
          - --tls-private-key-file=/etc/kubernetes/ssl/apiserver-key.pem
          - --client-ca-file=/etc/kubernetes/ssl/ca.pem
          - --service-account-key-file=/etc/kubernetes/ssl/apiserver-key.pem
          - --runtime-config=extensions/v1beta1/networkpolicies=true
          - --anonymous-auth=false
          livenessProbe:
            httpGet:
              host: 127.0.0.1
              port: 8080
              path: /healthz
            initialDelaySeconds: 15
            timeoutSeconds: 15
          ports:
          - containerPort: 443
            hostPort: 443
            name: https
          - containerPort: 8080
            hostPort: 8080
            name: local
          volumeMounts:
          - mountPath: /etc/kubernetes/ssl
            name: ssl-certs-kubernetes
            readOnly: true
          - mountPath: /etc/ssl/certs
            name: ssl-certs-host
            readOnly: true
        volumes:
        - hostPath:
            path: /etc/kubernetes/ssl
          name: ssl-certs-kubernetes
        - hostPath:
            path: /usr/share/ca-certificates
          name: ssl-certs-host
      
    • เพื่อให้สมบูรณ์ไปในคราวเดียว ต่อด้วยติดตั้ง kube-proxy ดังนี้
      sudo vim /etc/kubernetes/manifests/kube-proxy.yaml

      มีเนื้อหาในไฟล์ดังนี้

      apiVersion: v1
      kind: Pod
      metadata:
        name: kube-proxy
        namespace: kube-system
      spec:
        hostNetwork: true
        containers:
        - name: kube-proxy
          image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
          command:
          - /hyperkube
          - proxy
          - --master=http://127.0.0.1:8080
          securityContext:
            privileged: true
          volumeMounts:
          - mountPath: /etc/ssl/certs
            name: ssl-certs-host
            readOnly: true
        volumes:
        - hostPath:
            path: /usr/share/ca-certificates
          name: ssl-certs-host
      
    • ยังไม่หมดต่อด้วย kube-controller-manager ใช้ในการทำ scale, replica โดยเป็นตัวควบคุม API อีกชั้นหนึ่ง ทำการสร้างไฟล์ดังนี้
      sudo vim /etc/kubernetes/manifests/kube-controller-manager.yaml

      มีเนื้อหาในไฟล์ดังนี้

      apiVersion: v1
      kind: Pod
      metadata:
        name: kube-controller-manager
        namespace: kube-system
      spec:
        hostNetwork: true
        containers:
        - name: kube-controller-manager
          image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
          command:
          - /hyperkube
          - controller-manager
          - --master=http://127.0.0.1:8080
          - --leader-elect=true
          - --service-account-private-key-file=/etc/kubernetes/ssl/apiserver-key.pem
          - --root-ca-file=/etc/kubernetes/ssl/ca.pem
          resources:
            requests:
              cpu: 200m
          livenessProbe:
            httpGet:
              host: 127.0.0.1
              path: /healthz
              port: 10252
            initialDelaySeconds: 15
            timeoutSeconds: 15
          volumeMounts:
          - mountPath: /etc/kubernetes/ssl
            name: ssl-certs-kubernetes
            readOnly: true
          - mountPath: /etc/ssl/certs
            name: ssl-certs-host
            readOnly: true
        volumes:
        - hostPath:
            path: /etc/kubernetes/ssl
          name: ssl-certs-kubernetes
        - hostPath:
            path: /usr/share/ca-certificates
          name: ssl-certs-host
    • สำหรับอันสุดท้ายก็จะเป็น kube-scheduler (สำหรับการใช้งานเนื่องจากยังไม่ได้ลองใช้งาน ยังไม่เข้าใจว่าทำงานอย่างไร ถ้าว่างจะมาแก้อีกทีครับ) ทำการสร้างไฟล์ดังนี้
      sudo vim /etc/kubernetes/manifests/kube-scheduler.yaml

      มีเนื้อหาในไฟล์ดังนี้

      apiVersion: v1
      kind: Pod
      metadata:
        name: kube-scheduler
        namespace: kube-system
      spec:
        hostNetwork: true
        containers:
        - name: kube-scheduler
          image: quay.io/coreos/hyperkube:v1.6.2_coreos.0
          command:
          - /hyperkube
          - scheduler
          - --master=http://127.0.0.1:8080
          - --leader-elect=true
          resources:
            requests:
              cpu: 100m
          livenessProbe:
            httpGet:
              host: 127.0.0.1
              path: /healthz
              port: 10251
            initialDelaySeconds: 15
            timeoutSeconds: 15
      
    • ในส่วนของ Calico จะใช้สำหรับทำ Network Policy เนื่องจากเป็น Optional จึงขอยังไม่ตั้งค่าสำหรับบริการนี้
    • ในกรณีที่ต้องการแจ้ง systemd ว่าเรามีการเปลี่ยนข้อมูลใน config และต้องการ rescan ทุกอย่างอีกครั้งให้สั่งดังนี้
      sudo systemctl daemon-reload
    • จากนั้นทดสอบ restart kubelet service ใหม่อีกครั้งและตรวจสอบ status หรือ journalctl -xe เพื่อดูว่าการทำงานปกติหรือไม่ ถ้าไม่แก้ให้ถูกต้องแล้ว restart ใหม่อีกครั้งไปเรื่อย ๆ จนกว่าจะปกติ
      sudo systemctl restart kubelet

      จบตอนแรกไปก่อนนะครับ ตอนต่อไปจะต่อด้วย worker node และ addon อื่น ๆ

    ==================================

    Reference :

    [1] [Kubernetes] Deploy Docker Container บน Google Container Engine : https://www.nomkhonwaan.com/2016/04/12/deploy-docker-container-on-google-container-engine

    [2] วิธีสร้าง CoreOS Cluster : https://sysadmin.psu.ac.th/2017/05/04/setup-coreos-cluster/

    [3] How to Deploy Kubernetes on CoreOS Cluster : https://www.upcloud.com/support/deploy-kubernetes-coreos/

    [4] CoreOS -> Cluster TLS using OpenSSL : https://coreos.com/kubernetes/docs/latest/openssl.html

    [5] CoreOS -> CoreOS + Kubernetes Step By Step : https://coreos.com/kubernetes/docs/latest/getting-started.html

    [6] CoreOS -> Tectonic, Kubernetes cluster orchestrator with enterprise integration  : https://coreos.com/tectonic/docs/latest/

     

  • เรียนรู้เกี่ยวกับ GlusterFS และวิธีติดตั้ง GlusterFS บน Ubuntu 16.04

    อยากเอา Storage จากหลาย ๆ เครื่องมารวมกัน เครื่องพังไฟล์ไม่หาย แถมเข้าจากที่ไหนก็ได้ต้องทำอย่างไร

                GlusterFS[1] คือ filesystem รูปแบบหนึ่งสามารถรวม Storage จากหลาย ๆ เครื่อง และทำการกระจายข้อมูลเก็บโดยมีระบบคล้าย ๆ RAID ที่ช่วยป้องกันเวลาเครื่องใดเครื่องหนึ่งพัง โดย Access ได้หลาย Protocol แล้วแต่ว่าการทำไปใช้งานกับอะไร
    Gluster FS มีโครงสร้างระบบไฟล์ 2 ส่วน[2]คือ

    • Brick คือ Directory ที่อยู่บนแต่ละเครื่อง โดยจะสร้าง Partition เพิ่มจาก Partition ปกติ (จะ Disk ใหม่ Disk เดิมก็ได้) หรือจะใช้เป็น Directory ที่อยู่บน Partition เดิมก็ได้ โดย recommend เวลาสร้าง partition ให้เป็น xfs (รองรับ inode หรือจำนวนไฟล์ได้สูงกว่า ext4 ถ้าใช้ ext4 อาจจะเจอว่าเนื้อที่ยังไม่หมดแต่สร้างไฟล์ไม่ได้แล้วเนื่องจาก inode หมด) 
    • Volume คือชุดของข้อมูลที่ให้บริการ กล่าวง่าย ๆ คือ การเอา Brick จากแต่ละเครื่องมาให้บริการภายใต้ Volume นั้น ๆ (อาจจะเทียบได้ประมาณ เอา HDD หลาย ๆ ก้อนมารวมกันแล้วทำ RAID 5 ตั้งชื่อ Volume แล้วผู้ใช้ก็มีหน้าที่ใช้งานอย่างเดียว)

    ประเภทของ Volume[3]         

    1. Distributed Glusterfs Volume : เป็น default volume ของ glusterfs คือการเมื่อสร้างไฟล์ลงใน volume นั้น ๆ ก็จะกระจายไปเก็บยัง Brick ต่าง ๆ ถ้าเครื่องไหน Brick พังไฟล์ในเครื่องนั้นก็จะพังไปด้วยเพราะไม่มีสำรอง ข้อดีที่คือประหยัดที่ ข้อเสียคือการป้องกันไฟล์เสียหายขึ้นอยู่กับ Hardware RAID เครื่องอย่างเดียว
    2. Replicated Glusterfs Volume : รูปแบบนี้จะมีการทำสำเนาข้อมูลไฟล์นั้น ๆ ไปยังอีกเครื่อง ซึ่งจะตั้งให้เก็บกี่สำเนาก็ได้แล้วแต่จะกำหนด ถ้า Brick ใดเสียก็สามารถ Access ไฟล์นั้นจากอีก Brick แทน


    3. Distributed Replicated Glusterfs Volume : รูปแบบนี้จะซับซ้อนขึ้นแต่ก็ประมาณ ไฟล์หนึ่งจะกำหนดให้เก็บกี่สำเนา ส่วนจะไปอยู่เครื่องใดบ้าง ระบบจัดการให้เอง แต่จำนวนสำเนาต้องสอดคล้องกับจำนวน Brick นั่นเอง (ผมไม่อธิบายมากนะครับ เพราะยังไม่ค่อยเข้าใจมากนั้น เพราะไม่ได้ลอง)


    4. Striped Glusterfs Volume : คือการใช้หลักการในข้อ 1 เพิ่มเติมคือหั่นไฟล์ย่อย ๆ และเก็บกระจาย


    5. Distributed Striped Glusterfs Volume : คือการใช้หลักการในข้อ 2 เพิ่มเติมคือหั่นไฟล์ย่อย ๆ และเก็บกระจายเช่นเดียวกัน


    6. Distributed Replicated and Striped Glusterfs Volume : เป็นรูปแบบทีทั้งหั่นไฟล์ และสำเนาไฟล์ ซึ่งจะดีที่สุด แต่จะเปลืองเนื้อที่ที่สุด (ไม่มีรูปตัวอย่าง)
      ถ้าสรุปง่าย ๆ Strip เพื่อให้ได้เนื้อที่เยอะ ๆ โดยเอามาจากหลาย ๆ node ในส่วนของ Replicate เพื่อป้องกันข้อมูลหาย ตัวอย่างวิธีการคิดขนาดในแต่ละ Volume Type[3]


    ตัวอย่างการออกแบบผังการเชื่อมต่อเครือข่าย[4]         

     

    ซึ่งที่แนะนำปกติจะให้มี Storage Network เพื่อใช้ในการคุยกันระหว่าง Block(ชื่อ Node) และมี Data Network สำหรับใช้ในการดึงข้อมูล และมี Management Network ไว้เผื่อในกรณีฉุกเฉิน Data Network ข้อมูลเต็ม จะได้เข้าไป Config อะไรได้

    วิธีการติดตั้ง GlusterFS บน Ubuntu 16.04[5]

    1. ติดตั้ง ubuntu 16.04 ให้เรียบร้อย
    2. เพิ่ม ppa เข้าไปใน apt ดังนี้
      sudo apt install software-properties-common
      sudo add-apt-repository ppa:gluster/glusterfs-3.8
    3. จากนั้นทำการ Update และ ติดตั้ง Glusterfs Server
      sudo apt update
      sudo apt install glusterfs-server
    4. Start Glusterfs Server
      sudo service glusterfs-server start
    5. สุดท้ายต้องทำการเปิด Firewall ให้เครื่องที่จะให้ใช้งานเข้าถึงดังนี้ (ถ้าจะระบุ Port ให้ระบุ TCP 24007-24008 และ TCP 49152-49156)
      sudo ufw allow from 10.x.x.x
      sudo ufw allow from 192.x.x.x
      sudo ufw allow ssh
      sudo ufw default deny incoming
      sudo ufw default alllow outgoing
      sudo ufw enable

    วิธีการสร้าง xfs partition บน Ubuntu 16.04[6]

    1. ทำการจัดการ Partition ด้วย fdisk (ในกรณีขนาดเกิน 2TB ต้องใช้ parted ซึ่งไม่ขออธิบายครับ) ในตัวอย่างเป็น sdb
      sudo fdisk /dev/sdb
      
    2. กด n (New Partition) ตามด้วย p (Primary Partition) จากนั้นกด Enter ไป 3 ครั้ง จบด้วยกด w (Write Partition)
    3. จากนั้นทำการสร้าง Partition ด้วย mk.xfs
      sudo mkfs.xfs /dev/sdb1
      
    4. จากนั้นทำการสร้าง path ที่ต้องการ mount ยกตัวอย่างดังนี้
      sudo mkdir /cluster
      
    5. ทำการแก้ fstab เพื่อให้ automount ทุกครั้งที่ boot ดังนี้
      sudo vim /etc/fstab
      
    6. เพิ่มข้อความท้ายไฟล์ประมาณนี้
      /dev/sdb1      /cluster      xfs     defaults     0      0
      
    7. สั่ง mount (ในกรณีที่ไม่ได้รีเครื่อง)
      sudo mount -a
      
    8. สามารถใช้งาน path ที่ mount ได้แล้ว

    วิธี config glusterfs cluster node บน ubuntu 16.04

                โดยขั้นต้นในการสื่อสารระหว่าง node ต้องมีชื่อ node ซึ่งเครื่องแต่ละเครื่องจะจด domain ก็ได้ แต่ในที่นี้ใช้วิธีกำหนดไปใน host ไฟล์ (ป้องกัน dns หรือ data network มีปัญหา server ก็จะยังคุยกันได้ แต่ถ้าต้องการให้ client ใช้งานได้ต้องจดชื่อจริงทั้งหมด เพราะเมื่อ client ต่อก็จะมีการไล่ check ไปตาม node อื่น ๆ ด้วย ถ้าไม่จดชื่อจะ mount ไม่สำเร็จ) ทำได้ดังนี้

    • ทำการแก้ไขไฟล์ /etc/hosts
      sudo vim /etc/hosts
    • เพิ่มข้อความในส่วนของ ipv4 ประมาณนี้ครับ
      10.x.x.10 gluster-cluster1.x.y.z gluster-cluster1
      10.x.x.11 gluster-cluster2.x.y.z gluster-cluster2
      10.x.x.12 gluster-cluster3.x.y.z gluster-cluster3
      10.x.x.13 gluster-cluster4.x.y.z gluster-cluster4
      
    • ทำการค้นหา node อื่น ๆ ให้เข้ามาอยู่ใน cluster เดียวกันดังนี้ (เข้าทีหลังเครื่อง และค้นหาเฉพาะที่ไม่ใช่ node ตัวเอง) ยกตัวอย่างคำสั่งที่ใช้ใน gluster-cluster1 (ทำแค่ node เดียว)
      sudo gluster peer probe gluster-cluster2.x.y.z
      sudo gluster peer probe gluster-cluster3.x.y.z
      sudo gluster peer probe gluster-cluster4.x.y.z
    • คำสั่งที่ใช้ในการดูสถานะของ node ใน cluster ซึ่งเมื่อดูจาก node ที่ไม่ได้เพิ่มก็จะขึ้นสถานะเหมือนกัน
      sudo gluster peer status

    • คำสั่งสำหรับดู pool list (อีกมุมมอง) ใช้คำสั่งดังนี้
      sudo gluster pool list

    • ในกรณีที่ต้องการเอาเครื่องออก สั่งดังนี้
      sudo gluster peer detach <node> force

    วิธีการสร้าง glusterfs volume บน ubuntu 16.04[2]

    ยกตัวอย่างจากหัวข้อก่อนหน้า กำหนดให้ใช้ Volume Type แบบ Distributed Replicated and Striped Glusterfs Volume มี 4 node (4 brick) โดย Stripe ข้อมูล 2 node และกำหนดให้ Replicate จำนวน 2 ชุดข้อมูล (พังได้ 1 node ที่เป็นคู่กัน)

    • ทำการสร้าง Volume ตามเงื่อนไขข้างต้น
      sudo gluster volume create cluster1_volume replica 2 strip 2 \
      transport tcp \
      gluster-cluster1.x.y.z:/cluster \
      gluster-cluster2.x.y.z:/cluster \
      gluster-cluster3.x.y.z:/cluster \
      gluster-cluster4.x.y.z:/cluster \
      force

    • จากนั้นทำการ Start volume
      sudo gluster volume start cluster1_volume

    • ทำการตรวจสอบ volume ดังนี้
      sudo gluster volume list
    • อีกคำสั่งในการตรวจสอบทำได้ดังนี้
      sudo gluster volume status

    • ในกรณีที่ต้องการ Stop volume ใช้คำสั่งดังนี้
      sudo gluster volume stop cluster1_volume
    • คำสั่งในการลบ volume ดังนี้
      sudo gluster volume delete cluster1_volume

    วิธีการ mount glusterfs จาก client บน ubuntu 16.04[7]

    • ให้สร้างเครื่อง client ubuntu 16.04 เพื่อเชื่อมต่อ glusterfs
    • ทำการติดตั้ง glusterfs-client
      sudo apt install glusterfs-client attr
    • ทำการทดสอบ mount ไปยัง glusterfs-server ตามที่ตั้งไว้ในหัวข้อข้างต้น (ต้องจดชื่อที่แล้ว ถ้าไม่ได้จดต้องบันทึก list server ทั้งหมดใน /etc/hosts เช่นเดียวกัน)
      sudo mkdir /glusterfs
      sudo mount -t glusterfs gluster-cluster1.x.y.z:/cluster1_volume /glusterfs
    • หลังจากนั้นจะได้ mount volume ซึ่งสามารถใช้งานได้เลยดังรูป
    • ในกรณีที่ต้องการสร้างเป็น fstab สามารถทำได้โดยแก้ไข /etc/fstab ดังนี้
      sudo vim /etc/fstab
    • ทำการเพิ่มท้ายไฟล์ประมาณนี้ครับ
      gluster-cluster1.x.y.z:/cluster1_volume /glusterfs glusterfs defaults,_netdev 0 0

    ทดสอบใช้งาน GlusterFS

    • ทดสอบให้ gluster volume เป็นแบบ 2 stripe และ 2 replica และให้ client ต่อไปยัง cluster1 ด้วยการ mount
    • จากนั้นทำการทดสอบสร้าง image ขนาด 5.2 GB ด้วยคำสั่ง dd ที่ client ดังนี้

      sudo dd if=/dev/zero of=test.img bs=512K count=10000

      ผลลัพธ์ได้ดังรูป

                  จะเห็นได้ว่า ระบบจะแบ่งไฟล์ออกเป็น 2.5G และทำการ replicate กระจายไปยังอีกคู่ node (1:2->3:4)

    • จากนั้นทดสอบปิด glusterfs server node 1 ที่ client กำลังต่ออยู่ดูว่ายังเข้าถึงไฟล์จาก node อื่นได้ไหม

                    จากการทดสอบพบว่ายังสามารถเข้าถึงไฟล์ได้อยู่ ซึ่งเมื่อสั่ง netstat ดูจะพบว่าเครื่องยังต่อไปยัง cluster ที่เหลืออยู่

    • การทดสอบสุดท้าย ทดสอบสร้างไฟล์ขณะที่ glusterfs server node 1 ปิดอยู่ แล้วลองเปิด node 1 กลับเข้ามา active ดูว่าไฟล์จะมาไหม ผลที่ได้คือไฟล์ถูก sync หลังจาก เปิด node 1 ขึ้นมา

                    ซี่งปกติแล้ว Port TCP 24007-24008 ใช้สำหรับตรวจสอบข้อมูลของ GlusterFS Server node ต่าง ๆ (เดาว่าถ้าเราปิด node 1 ถ้ามีการเพิ่ม node อาจจะไม่เห็น node ใหม่ ใครว่างลองทดสอบดูครับ) และ TCP 49152-49156 ใช้สำหรับโอนถ่ายข้อมูล

    • จากการทดสอบสร้างไฟล์บน server พบว่าไม่มีการกระจายไฟล์ (เนื่องจากสร้างโดยไม่ผ่าน glusterfs-client) เพราะฉนั้นควรสร้างผ่านการ mount เป็น glusterfs เท่านั้น

                    หวังว่าไม่ยาวไปนะครับ ไม่อยากแยกหลายบทความเดี๋ยวจะงง เอาเป็นบทความเดียวจบเลยนะครับ ตอนหน้าน่าจะพูดถึง docker management tools อย่าง kubernetes หรือไม่ก็ rancher ครับ……

    ==================================
    Reference :

    [1] Gluster : https://www.gluster.org/

    [2] How to install and use GlusterFS : https://www.rmuti.ac.th/user/prakai/w/images/9/9c/How-to-install-and-use-GlusterFS-ARIT-Net-3.pdf

    [3] GlusterFS Storage Cluster on CentOS 7 : https://wiki.centos.org/HowTos/GlusterFSonCentOS

    [4] Gluster Docs – Architecture : http://gluster.readthedocs.io/en/latest/Quick-Start-Guide/Architecture/

    [5] Gluster Docs – Install Gluster : https://gluster.readthedocs.io/en/latest/Install-Guide/Install/

    [6] Format a Volume as XFS in Debian and Ubuntu : https://linhost.info/2012/08/format-a-volume-as-xfs-in-debian-and-ubuntu/

    [7] Install and Configure GlusterFS on Ubuntu 16.04 / Debian 8 : http://www.itzgeek.com/how-tos/linux/ubuntu-how-tos/install-and-configure-glusterfs-on-ubuntu-16-04-debian-8.html

  • วิธีสร้าง CoreOS Cluster

    จะสร้าง CoreOS ให้กลายเป็น Cluster Docker Container ได้อย่างไร

                 จากบทความที่แล้วที่แนะนำเกี่ยวกับ CoreOS และการติดตั้งบน Vmware[1] ไปแล้วนั้น เราก็สามารถสร้างให้เป็นในรูปแบบ Cluster ได้ โดยมองว่าเครื่องแต่ละเครื่องที่สร้างนั้นเป็น Node หนึ่ง ๆ ใน Cluster โดยใช้ etcd เป็นตัวเก็บข้อมูลของ Node และ Fleet เป็นตัว Deploy docker ให้กระจายไปยัง Node ต่าง ๆ อย่างเหมาะสม โดยที่จะสามารถย้ายตัวเองได้เมื่อมีเครื่องใดเครื่องหนึ่งมีปัญหา (Recommend จำนวนเลขคี่ และอย่างต่ำต้อง 3 node ขึ้นไป ยิ่งเยอะ โอกาสล่มก็ยิ่งต่ำ)


                etcd ในปัจจุบันเป็น Version 3 ซึ่งจะมีประสิทธิภาพเพิ่มขึ้นจาก Version 2 (แต่ใน document web ยังเป็น etcd2 เป็นส่วนมาก) โดยใช้สำหรับเก็บข้อมูลแต่ละ Node ทำให้รู้ว่าในแต่ละ Cluster มีเครื่องใด IP อะไรบ้าง มีทั้งหมด 3 วิธีคือ 

    1. Static เป็นวิธีที่ระบุลงไปเลยในแต่ละเครื่องว่ามีเครื่องไหนบ้างที่อยู่ใน Cluster วิธีการนี้ข้อเสียคือถ้าเพิ่มต้องเพิ่มทุกเครื่อง
    2. etcd Discovery เป็นวิธีที่จะให้ Discovery Service เป็นคนทำหน้าที่เก็บข้อมูล (เหมือน tracker torrent) เมื่อเพิ่มเครื่องใหม่ ก็แค่ชี้ไป Discovery URL ก็เสร็จ
    3. DNS Discovery เป็นวิธีการใช้วิธีการจด DNS ในรูปแบบ SRV record เพื่อบอกว่า บริการนี้มีเครื่องอะไรอยู่บ้าง ซึ่งจะมีการอ้างอิงอยู่กับ Domain Name โดยวิธีนี้จำเป็นต้องจดชื่อ Domain ทุกเครื่อง

                ในบทความนี้จะอธิบายวิธีที่ 1 ซึ่งแม้ยุ่งยาก แต่เหมาะกับระบบที่ Internet Public ไม่ค่อยเสถียร และ ถ้าใครต้องการลองวิธีอื่นสามารถตามอ่านได้ใน Web CoreOS[2] ครับ

      วิธีการตั้งค่า etcd2

    • ทำการสร้าง service etcd2 service ด้วย systemd ดังนี้
      sudo vim /etc/systemd/system/etcd2.service
    • ข้อความในไฟล์มีดังนี้ (ถ้าต้องการความปลอดภัยสามารถใช้ https ได้ครับ แต่ต้องมีการทำ certificate เพิ่มเติม ซึ่งไม่ขออธิบายครับ)
      [Unit]
      Description=etcd2
      Conflicts=etcd.service
      
      [Service]
      User=etcd
      Type=notify
      Environment=ETCD_DATA_DIR=/var/lib/etcd
      ExecStart=/usr/bin/etcd2 --name node01 --initial-advertise-peer-urls http://[IP]:2380 \
       --listen-peer-urls http://[IP]:2380 \
       --listen-client-urls http://[IP]:2379,http://127.0.0.1:2379 \
       --advertise-client-urls http://[IP]:2379 \
       --initial-cluster-token etcd-cluster-1 \
       --initial-cluster node01=http://[IP_node01]:2380,node02=http://[IP_node02]:2380,node03=http://[IP_node03]:2380 \
       --initial-cluster-state new
      
      Restart=always
      RestartSec=10s
      LimitNOFILE=40000
      TimeoutStartSec=0
      
      [Install]
      WantedBy=multi-user.target
      
    • Enable etcd2 service เพื่อให้รันทุกครั้งที่เปิดเครื่อง
      sudo systemctl enable etcd2
    • Start etcd2 service
      sudo systemctl start etcd2
    • ตรวจดูสถานะการทำงานของ etcd2 service
      sudo systemctl status etcd2
    • เราสามารถดูข้อมูลสมาชิกได้ดังนี้
      etcdctl member list

     วิธีการตั้งค่า Fleet

    • ทำการสร้าง service fleet โดยการตั้งค่าใน systemd ดังนี้
      sudo vim /etc/systemd/system/fleet.service
    • ข้อความในไฟล์มีดังนี้ (จะเห็นว่า config ตั้งค่าให้ Start หลัง etcd2)
      [Unit]
      Description=fleet daemon
      
      After=etcd2.service
      
      Wants=fleet.socket
      After=fleet.socket
      
      [Service]
      User=fleet
      Environment=GOMAXPROCS=1
      Environment="FLEET_PUBLIC_IP=[IP]"
      ExecStart=/usr/bin/fleetd
      Restart=always
      RestartSec=10s
      
      [Install]
      WantedBy=multi-user.target
    • Enable fleet service เพื่อให้รันทุกครั้งที่เปิดเครื่อง
      sudo systemctl enable fleet
    • Start fleet service
      sudo systemctl start fleet
    • ตรวจดูสถานะการทำงานของ fleet service
      sudo systemctl status fleet
    • วิธีตรวจดูสถานะแต่ละ Node ทำได้ดังนี้
      fleetctl list-machines
    • จะได้ผลลัพธ์หน้าตาประมาณนี้ครับ

           ให้ทำการติดตั้งไปเรื่อย ๆ ทั้ง CoreOS->Etcd2->Fleet จนครบ 3 เครื่อง หรือ 5,7,9 เครื่องแล้วแต่จะต้องการว่าจะสร้างกี่ Node ครับ ยกตัวอย่างถ้าครบ 3 เครื่องก็จะได้ประมาณนี้ครับ

    ทดสอบการสร้าง WordPress ผ่าน Fleet[3]

    • วิธีการทำแน่นอนครับ หนีไม่พ้นไฟล์รูปแบบ systemd (อีกแล้ว) แต่ไม่ต้องรันด้วย systemctl นะครับ ทำที่เครื่องใดเครื่องหนึ่ง สร้างที่ /home/core ก็ได้ดังนี้
    • ก่อนอื่นต้องติดตั้ง docker mysql โดยสร้างไฟล์ mysql.service ดังนี้
      vim mysql.service
    • ข้อความในไฟล์ประมาณนี้ครับ
      [Unit]
      Description=MySQL DataBase
      After=etcd.service
      After=docker.service
      
      [Service]
      TimeoutStartSec=0
      ExecStartPre=-/usr/bin/docker kill mysql
      ExecStartPre=-/usr/bin/docker rm mysql
      ExecStartPre=/usr/bin/docker pull mysql:5.7
      ExecStart=/usr/bin/docker run --name mysql -e MYSQL_ROOT_PASSWORD="wordpress" -e MYSQL_DATABASE="wordpress" -e MYSQL_USER="wordpress" -e MYSQL_PASSWORD="wordpress" mysql:5.7
      ExecStop=/usr/bin/docker stop mysql
    • สร้างไฟล์ wordpress.service ดังนี้
      vim wordpress.service
    • ข้อความในไฟล์ประมาณนี้ครับ
      [Unit]
      Description=WordPress
      After=mysql.service
      
      [Service]
      TimeoutStartSec=0
      ExecStartPre=-/usr/bin/docker kill wordpress
      ExecStartPre=-/usr/bin/docker rm wordpress
      ExecStartPre=/usr/bin/docker pull wordpress
      ExecStart=/usr/bin/docker run --name wordpress --link mysql -p 8880:80 -e WORDPRESS_DB_PASSWORD=wordpress -e WORDPRESS_DB_NAME=wordpress -e WORDPRESS_DB_USER=wordpress wordpress
      ExecStop=/usr/bin/docker stop wordpress
      
      [X-Fleet]
      X-ConditionMachineOf=mysql.service
    • สั่ง Start mysql service ด้วย fleetctl ดังนี้
      fleetctl start mysql.service

    • สั่ง Start wordpress service ด้วย fleetctl ดังนี้
      fleetctl start wordpress.service

    • สั่งคำสั่งเพื่อตรวจสอบสถานะดังนี้ (ซึ่งจะบอกว่าติดตั้งที่ Node ใด และสถานะการใช้งาน หรือการติดตั้งเป็นอย่างไร)
      fleetctl list-units

    • เสร็จแล้วลองสั่ง fleetctl list-units ที่ Node อื่น ๆ ดูครับก็จะได้ผลลัพธ์เหมือน ๆ กัน
    • ก็จะได้ web wordpress เอาไว้ใช้งานแล้ว
    • จากนั้นทดสอบลองปิด Node ดูครับ สำหรับระบบที่มี 3 Node พังได้แค่ Node เดียวครับ ถ้าอยากได้มากกว่านั้นต้องเพิ่มจำนวน Node ขึ้นไป 
    • จะพบว่าเครื่องจะย้ายไป Start อีก Node ทันที (มันจะสั่ง start ใหม่นะครับ ไม่ได้ย้ายไปแบบ vmware) เท่าที่ทดสอบข้อมูลไม่ได้มาด้วยครับ อีกทั้งยังได้ ip ที่เครื่องใหม่ เพราะฉะนั้นต้องหาวิธีทำ map volume และ proxy web เอาเองครับ

    (Optional) วิธีการ Fix IP แทน DHCP

    • ในกรณีที่ต้องการ Fix IP แทน DHCP ให้เข้าไปสร้าง systemd network config โดยสร้างไฟล์ดังนี้
      sudo vim /etc/systemd/network/static.network
    • ข้อความในไฟล์ประมาณนี้ครับ
      [Match]
      Name=[Interface Name]
      
      [Network]
      Address=[IP/Mask]
      Gateway=[IP Gateway]
      DNS=[DNS IP มีหลาย IP ให้เว้นวรรค เช่น 10.0.0.1 10.0.0.2]
    • จากนั้นให้ทำการ Restart เครื่อง (จริง ๆ restart service ก็น่าจะได้ แต่ลองแล้วไม่ได้ครับ)

                 สำหรับ CoreOS Cluster ก็มีเท่านี้ครับ แต่จะเห็นว่ายังขาด Docker Management ที่เป็น GUI รวมถึง Docker Gateway และระบบ Storage ติดตามในตอนต่อ ๆ ไปแล้วกันครับ

    ==================================

    Reference :

    [1] มารู้จักกับ CoreOS Linux และวิธีติดตั้ง CoreOS Linux บน Vmware : https://sysadmin.psu.ac.th/2017/05/03/coreos-linux-install-vmware/

    [2] CoreOS Clustering Guide : https://coreos.com/etcd/docs/latest/op-guide/clustering.html#etcd-discovery

    [3] Deploy WordPress in CoreOS Cluster using Fleet : https://wenfeng-gao.github.io/2016/06/03/deploy-wordpress-in-coreos-cluster-using-fleet.html

  • มารู้จักกับ CoreOS Linux และวิธีติดตั้ง CoreOS Linux บน Vmware

    CoreOS Linux คืออะไร เอาไปใช้ทำอะไร

                 ในโลกของ Containner ในปัจจุบันมีหลายตัวเลือกให้ใช้งาน แต่การใช้งานที่สมบูรณ์จำเป็นต้องมี 3 อย่างรวมกันคือ Containner (ยกตัวอย่าง Docker,Rocket,Lxd), OS ขนาดเล็ก (ยกตัวอย่าง CoreOS, RancherOS, PhotonOS, Snappy Ubuntu Core, Redhat Project Atomic), และสุดท้ายคือโปรแกรมบริหารจัดการ Containner (ยกตัวอย่าง Kubernete, Admiral, Rancher, Kitematic)

                 สำหรับ CoreOS[1] เองรองรับการงานทั้งใน Cloud(AWS,Digital Ocean,Azure,Google Cloud Platform) หรือใน Virtualization Platform (ยกตัวอย่าง OpenStack, VMWare, Vagrant) และยังสามารถติดตั้งลงบนเครื่อง Physical (Bare Metal) ได้อีกด้วย
                 CoreOS มีจุดเด่นอีกอย่างคือระบบ CoreOS Cluster[2] ที่สามารถออกแบบเป็น Cluster ช่วยให้สามารถบริการ Docker Containner กระจายไปยัง Node ต่าง ๆ โดยใช้ etcd ในการจัดการระบบและใช้ fleet ในการสร้าง Docker ที่อยู่บน CoreOS Cluster ซึ่งจะกล่าวต่อไปในบทความต่อไปครับ


                 การติดตั้ง ติดตั้งได้หลากหลายช่องทาง เขียนแผ่นติดตั้งก็ได้ ผ่าน iso ก็ได้ แต่ถ้าเป็น Image จะไม่มี User (จริง ๆ มีแต่ไม่มี Password) ต้องสร้างด้วยสิ่งที่เรียกว่า cloud-config ซึ่งจำเป็นต้องตั้งค่าก่อนติดตั้ง หรืออีกแบบที่จะขอแนะนำ เป็นวิธีที่ใช้สำหรับ Image ที่เป็น ova ของ Vmware ซึ่งใช้วิธี Bypass เข้า Auto Login Mode[1] แล้วเข้าไป Add User เอาเองภายหลัง (สามารถนำไปประยุกต์กับวิธีติดตั้ง Image แบบอื่นได้เช่นเดียวกัน) 

    การติดตั้ง CoreOS ด้วย OVA Image ผ่าน vSphere Client 6.0[3]

    • โหลดไฟล์ติดตั้งจาก https://stable.release.core-os.net/amd64-usr/current/coreos_production_vmware_ova.ova
      (ุถ้า Link เปลี่ยนไปหาดูเอาเองนะครับ)
    • เปิด vSphere Client เลือก deploy OVF Template จากนั้นทำการ Browse File เลือกเครื่อง เลือก DataStore ตามปกติ

     

    • เมื่อถึงหน้าตั้งค่าใส่ Hostname และการตั้งค่า Network เบื้องต้น (ในที่นี้ผมใส่แค่ Hostname และ ชื่อ Network Interface โดยรับ IP จาก DHCP)


    • จากนั้นเมื่อสร้างเสร็จและเปิดเครื่องขึ้นมารีบกดปุ่มลูกศรลงในหน้า Grub ป้องกัน Auto Boot (เร็วมาก ถ้าไม่ทันก็ให้รีเครื่องใหม่)

    • จากนั้นให้เลือก CoreOS default แล้วกด e และพิมพ์ coreos.autologin (ไม่ต้องพิมพ์ \ ก่อนขึ้นบรรทัดใหม่ ระบบจะใส่ให้เอง)

    • จากนั้นกด F10 ก็จะทำการ Boot และ ได้ Prompt สำหรับ adduser ตั้งรหัสผ่านได้เลย ซึ่งจริง ๆ แล้วจะมี User ตั้งต้นอยู่แล้วชื่อ core จะตั้งเฉพาะรหัสผ่าน core ก็ได้เช่นเดียวกัน)
    • ทดสอบ Login จาก ssh เครื่องอื่น ก็จะสามารถใช้งานได้แล้ว

               ตอนต่อไปจะมาดูกันว่าเราจะสร้าง CoreOS Cluster ได้อย่างไร….

    ==================================

    Reference :

    [1] มาเล่น CoreOS กัน : http://thaiopensource.org/%E0%B8%A1%E0%B8%B2%E0%B9%80%E0%B8%A5%E0%B9%88%E0%B8%99-coreos-%E0%B8%81%E0%B8%B1%E0%B8%99/

    [2] CoreOS Cluster บน DigitalOcean : http://thaiopensource.org/tag/coreos/

    [3] How-To Install and Configure CoreOS OVA Image on ESXi : https://www.vladan.fr/how-to-install-and-configure-coreos-ova-image-on-esxi/

  • การเชื่อมต่อ OAuth2 ด้วย .NET C#

    อยาก  Login ด้วย OAuth2 กับ .NET C# ต้องทำอย่างไร

                 สำหรับตัวอย่างนี้เอามาจาก GitHub Project : https://github.com/titarenko/OAuth2[1]
    ซึ่งเป็น Code ตัวอย่างนำมาเพิ่ม ในส่วนของการเชื่อมต่อ PSU Passport ดังนี้

    • เพิ่ม class file ใน project OAuth2->Client->Impl ชื่อ PassportClient.cs เนื้อหาดังนี้
      using Newtonsoft.Json.Linq;
      using OAuth2.Configuration;
      using OAuth2.Infrastructure;
      using OAuth2.Models;
      
      namespace OAuth2.Client.Impl
      {
       /// <summary>
       /// Passport authentication client.
       /// </summary>
       public class PassportClient : OAuth2Client
       {
          /// <summary>
          /// Initializes a new instance of the <see cref="PassportClient"/> class.
          /// </summary>
          /// <param name="factory">The factory.</param>
          /// <param name="configuration">The configuration.</param>
          public PassportClient(IRequestFactory factory, IClientConfiguration configuration)
              : base(factory, configuration)
          {
          }
      
          /// <summary>
          /// Defines URI of service which issues access code.
          /// </summary>
          protected override Endpoint AccessCodeServiceEndpoint
          {
              get
              {
                  return new Endpoint
                  {
                       BaseUri = "https://oauth.psu.ac.th",
                       Resource = "?oauth=authorize"
                  };
              }
          }
      
          /// <summary>
          /// Defines URI of service which issues access token.
          /// </summary>
          protected override Endpoint AccessTokenServiceEndpoint
          {
              get
              {
                  return new Endpoint
                  {
                       BaseUri = "https://oauth.psu.ac.th",
                       Resource = "?oauth=token"
                  };
              }
          }
      
          /// <summary>
          /// Defines URI of service which allows to obtain information about user which is currently logged in.
          /// </summary>
          protected override Endpoint UserInfoServiceEndpoint
          {
              get
              {
                  return new Endpoint
                  {
                       BaseUri = "https://oauth.psu.ac.th",
                       Resource = "?oauth=me"
                  };
              }
          }
      
          /// <summary>
          /// Friendly name of provider (OAuth2 service).
          /// </summary>
          public override string Name
          {
              get { return "Passport"; }
          }
      
      
          /// <summary>
          /// Should return parsed <see cref="UserInfo"/> from content received from third-party service.
          /// </summary>
          /// <param name="content">The content which is received from third-party service.</param>
          protected override UserInfo ParseUserInfo(string content)
          {
              var response = JObject.Parse(content);
              return new UserInfo
              {
                   Id = response["user_login"].Value<string>(),
                   FirstName = response["user_login"].Value<string>(),
                   LastName = "",
                   Email = response["user_email"].SafeGet(x => x.Value<string>())
              };
          }
        }
      }
      
    • จากนั้นทำการแก้ไข Web.config ใน Project OAuth2.Example เพิ่มข้อความดังนี้
       <add clientType="PassportClient"
       enabled="true"
       clientId="testclient3"
       clientSecret="testpass3"
       redirectUri="~/auth" />
    • โดยต้องแจ้ง Redirect URI ให้ผู้ดูแลระบบ จากนั้นทางผู้ดูแลระบบจะแจ้ง Client ID และ Client Secret รวมถึงชื่อ Server ในการใช้งานให้ทราบ (ชื่อ server ใส่ในไฟล์ก่อนหน้านี้)
    • เมื่อรันโปรแกรม จะปรากฎหัวข้อ Login มากมายให้เลือก ให้ทดสอบในส่วนของ Login passport
    • หลังจากนั้นจะปรากฎหน้า Login ซึ่งอันนี้หน้าตาแล้วแต่ผู้ดูแลจะสร้างครับ (อันนี้ผมทำแค่เป็นตัวอย่างนะครับ)
    •  หลักจากนั้นจะปรากฎหน้าถามเพื่อขออนุญาตให้สิทธิ์การใช้งาน
    • จากนั้นระบบจะเด้งกลับมายังหน้าแรกแต่จะแสดงข้อความ Profile ดังรูป
    •   ตัวอย่างนี้ในการใช้งานจริงต้องนำไปประยุกต์เองครับ ซึ่งแสดงให้เห็นวิธีการใช้งาน OAuth สำหรับ .NET C# ว่าวิธีไม่ได้ต่างกับการใช้งาน CMS แต่อย่างใดครับ

    ==================================

    Reference :

    [1] OAuth2 client implementation for .NET : https://github.com/titarenko/OAuth2

  • Workshop : PSU Passport OAuth2

    มีวิธี Authen แบบอื่นนอกจาก LDAP กับ Web Service ไหม

                ด้วยยุคสมัยเปลี่ยนไป ด้วยเทคโนโลยี OAuth2 ทำให้เราจำเป็นต้องศึกษาหาความรู้เพิ่มเติมกันอีกครั้งครับ

    หมายเหตุ : เนื่องจากต้องการให้เป็น Blog เปิดเผยได้ จึงขอไม่ระบุชื่อ Server จริง ๆ ลงไปนะครับ 


    โดยรวบรวม Blog แบ่งเป็น 7 Blog ดังนี้

    Blog ที่ ชื่อ Blog
         1 เรียนรู้เทคโนโลยี OAuth2
         2 การติดตั้ง Postman
         3 ทดสอบเชื่อมต่อ OAuth2 ด้วย Postman
         4 การเชื่อมต่อ OAuth2 ด้วย Joomla
         5 การเชื่อมต่อ OAuth2 ด้วย Wordpress
         6 การเชื่อมต่อ OAuth2 ด้วย .NET C#
         7 การเชื่อมต่อ OAuth2 ด้วย PHP (กำลังดำเนินการ)

    บทความและ Link เพิ่มเติม

    [1] การยืนยันตัวตนและกำหนดสิทธิ์ Apache UserGrid 2.x

    [2] รวม Code สำหรับเชื่อมต่อ OAuth ทุกภาษา 1 : https://oauth.net/code/

    [3] รวม Code สำหรับเชื่อมต่อ OAuth ทุกภาษา 2 : https://developer.byu.edu/docs/consume-api/use-api/follow-sample-code