選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
8 分鐘閱讀時間 (1538 個字)

Ansible # 15 - Playbook [2]

shutterstock_198004562
  • 今天我們要來建立一個簡單的 Hello World 網頁,會用到以下:

    • Node JS Application
    • 安裝 Node JS 與操作 npm
    • 上面都是建立在 CentOS 上
  • 通常要增加 yum 或是 apt 的 repository 都會用以下的 shell script 來達成,以 EPELRemi 為例:

      # Import Remi GPG key.
      wget http://rpms.famillecollet.com/RPM-GPG-KEY-remi -O /etc/pki/rpm-gpg/RPM-GPG-KEY-remi
      rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-remi
    
      # Install Remi repo
      rpm -Uvh --quiet http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
    
      # Install EPEL repo
      yum install epel-release
    
      # Install Node.js (npm plus all its dependencies).
      yum --enablerepo=epel install node
    • 如果只是一台機器的話,手動打這些指令很OK,但:

      • 如果 GPG key 已經下載了呢?
      • 如果網路很爛,載東西的時候壞了?
      • p.s. GPG => GNU Privacy Guard,是作者用來替它們的 package 提供簽章驗證的方式
    • Ansible 可以使得上面的 shell script 更強健:

        ---
        - hosts: all
      
          tasks:
            - name: Import Remi GPG key.
              rpm_key: "key={{ item }} state=present"
              with_items:
                - "http://rpms.famillecollet.com/RPM-GPG-KEY-remi"
      
            - name: Install Remi repo.
              command: "rpm -Uvh --force {{ item.href }} creates= {{ item.creates }}"
              with_items:
                - href: "http://rpms.famillecollet.com/enterprise/remi-release-7.rpm"
                - creates: "/etc/yum.repos.d/remi.repo"
      
            - name: Install EPEL repo.
              yum: name=epel-release state=present
      
            - name: Ensure firewalld is stopped (since this is for testing).
              service: name=firewalld state=stopped
      
            - name: Install Node.js and npm.
              yum: name=npm state=present enablerepo=epel
      
            - name: Install Forever (to run our Node.js app).
              npm: name=forever global=yes state=present
      • rpm_key 是個單純的 module,它負責從 URL檔案匯入 RPM key,並確保它的 state 是 absent 或 present
      • Ansible 並沒有 rpm 這個 module,但我們可以用 command module 來替代,並達到以下功能:
        • creates,這個參數可以告訴 module 何時不需要再執行安裝,也就是何時真的要執行 command
        • with_items,可以提供多維的陣列讓 command 不只安裝一個 rpm repo
      • 跟上面的 Remi 相比,yum module 安裝 EPEL 方便多了,只要設定 namestate
      • 因為只是測試所以我們先透過 service module 關閉了 firewalld 這個服務
      • yum module 安裝了 node.js 及 npm,並設定了要啟用 epel 這個 repo。你也可以選擇設定 disable(有太多 repo 要掃的時候也是耗時)
      • 最後我們透過 npm module 安裝一個叫 forever 的程式,它可以讓我們撰寫的 node.js application 不停的執行
    • 下一步當然是寫一個簡單的 node.js application 囉:

        // filename app.js
        const express = require('express')
        const app = express()
        const port = 3000
      
        app.get('/', (req, res) => {
          res.send('Hello World!')
        })
      
        app.listen(port, () => {
          console.log(`Example app listening at http://localhost:${port}`)
        })
      • 這是一個非常簡單的 web application,首先引用了 express 這個簡單的框架
      • 接著設定如果 request 發到 / 這個 endpoint,就會送出 Hello World! 的 response
      • 最後當然是要監聽 3000 port 啦(一般 http 預設都會用 80,https 用 443,當然這也不是絕對)
    • 既然我們引用了 express,那自然是要調整一下套件管理的內容,也就是要在建立一個叫 package.json 的檔案,並在裡頭加上以下:

        {
            "name": "examplenodeapp",
            "description": "Example Express Node.js app.",
            "author": "Jeff Geerling <geerlingguy@mac.com>;",
            "dependencies": {
                "express": "3.x.x"
            },
            "engine": "node >= 0.10.6"
        }
    • 前面我們在 playbook 裡寫到安裝 nodejs/npm 就停手了,但其實還要將程式部署呢

        - name: Ensure Node.js app folder exists
          file: "path={{ node_apps_location }} state=directory"
      
        - name: 
        example Node.js app to server.
          copy: "src=app dest={{ node_apps_location }}"
      
        - name: Install app dependencies defined in package.json.
          npm: path={{ node_apps_location }}/app
      • 首先我們確保 node.js 的原始碼所在資料夾 app 有存在
        • 特別留意有用到一個變數叫 node_apps_location
        • 它的值可以定義在 playbook 的 vars、或是 inventory,也可以在呼叫 ansible-playbook 的時候當作參數傳入
      • 接著我們把 Ansible 所在機器的 app 這個目錄透過 copy 模組搬進了 node_apps_locationcopy module 厲害的是它會自動判斷是要複製單檔還是遞迴複製整個目錄
      • 承上,但如果有上百上千的檔案目錄要複製的話,建議選擇用 synchronize module
      • 最後我們透過 npm module 安裝了定義在 package.json 裡相依的套件
    • 部署完之後當然是要啟動囉

        - name: Check list of running Node.js apps.
          command: forever list
          register: forever_list
          changed_when: false
      
        - name: Start example Node.js app.
          command: "forever start {{ node_apps_location }}/app/app.js"
          when: "forever_list.stdout.find('{{ node_apps_location }}/app/app.js') == -1"
      • 這裡超重要的啦!一定要仔細看喔~
      • register 做了一件事,就是把執行 forever list 的結果註冊(存放) 到 forever_list 這個變數中
      • 承上,這個變數會存的是 console 的 stdout 及 stderr 的內容
      • changed_when 是告訴 Ansible 這個 Task 什麼時候會是 changed
      • 承上,由於我們只是執行 forever list 這個 command,它永遠不會改變 server 上的什麼狀態,因此放 false
      • 最後我們用 forever 框架啟動了 app.js,而且只會啟動一次,什麼時候啟動呢?
      • 承上,看到 when,就是啟動的時機,而這個時機會動用到前面所註冊的變數 forever_list 的 stdout,如果找得到 app.js 就不再重新啟動了
    • 綜合以上,完整的 playbook.yml 如下:

        ---
      
        - hosts: all
          become: yes
      
          vars:
            node_apps_location: /usr/local/opt/node
      
          tasks:
            - name: Import Remi GPG key.
              rpm_key: key={{ item }} state=present
              with_items:
                - "http://rpms.famillecollet.com/RPM-GPG-KEY-remi"
      
            - name: Install Remi repo.
              command: "rpm -Uvh --force {{ item.href }} creates={{ item.creates }}"
              with_items:
                - href: "http://rpms.famillecollet.com/enterprise/remi-release-7.rpm"
                  creates: "/etc/yum.repos.d/remi.repo"
      
            - name: Install EPEL repo.
              yum: name=epel-release state=present
      
            - name: Ensure firewalld is stopped (since this is for testing).
              service: name=firewalld state=stopped
      
            - name: Install Node.js and npm.
              yum: name=npm state=present enablerepo=epel
      
            - name: Install Forever (to run our Node.js app).
              npm: name=forever global=yes state=present
      
            - name: Ensure Node.js app folder exists
              file: path={{ node_apps_location }} state=directory
      
            - name: Copy example Node.js app to server.
              copy: src=app dest={{ node_apps_location }}
      
            - name: Install app dependencies defined in package.json.
              npm: path={{ node_apps_location }}/app
      
            - name: Check list of running Node.js apps.
              command: forever list
              register: forever_list
              changed_when: false
      
            - name: Start example Node.js app.
              command: "forever start {{ node_apps_location }}/app/app.js"
              when: "forever_list.stdout.find('{{ node_apps_location }}/app/app.js') == -1"
      • 執行看看結果吧!
          vagrant up
      • 看起來是成功了,接著瀏覽網頁看
      • 耶~成功
        • 眼尖的人會發現我下的是 vagrant up
        • 沒錯,前幾篇提過的 vagrant provision ... 就是用在這種時候,我可不想在我的 mac 上建一堆沒用的東西,所以選擇用 vagrant provision
        • 原本應該是要下 ansible-playbook playbook.yml -i hosts -u vagrant
  • 以上就是透過 Ansible 部署一個 nodejs application,之後的篇幅都會愈來愈趨近實用的案例,不會是這種 Hello World 系列的

    • 到目前為止我們認識到了多少有關 playbook 的?
      • hosts
      • vars
      • tasks
      • name
      • with_items
      • register
      • created
    • 各位都還記得嗎?不記得的話再重看一次囉
  • 下一篇將會部署一個完成的 LAMP + Drupal

    • 這東西有點古老可能沒人聽過了,但我們會介紹更多 playbook 相關的觀念
      • 我們會讓 playbook 變得更簡潔、更共用
      • 我們會知道如何在某個 Task 完成時作某種處理
      • 我們會知道如何共用別人定義好的 playbook ...等
Ansible # 16 - Playbook [3]
Ansible #14 - Playbook [1]

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/05/29, 週三

Captcha 圖像