2017年12月7日 星期四

laravel + vue ( webpack , laravel-mix)

在 laravel 中,  如果要透過 webpack 來使用 vue ,  php 要傳資料給 vue 的方式和傳統方式上不太一樣.

可以參考這篇文章, 我一開始到處亂查資料都沒找對方向, 一直到看了這篇後, 才解決了問題.

vue 的檔案裡不像 blade 檔案會經過 laravel 處理, 所以不能直接放 php 的變數在 vue 檔案裡.  要將變數放在 blade 裡的 vue component 裡, 然後 vue 以 props 的方式取得變數內容.

假設 php 有個 route 要給 vue 以 ajax 使用

blade file
<sysconfigtab ajaxurl="{{ URL::to('/some_url') }}"></sysconfigtab>

vue file

export default{
  data(){
    return {}
  },
  props: ['ajaxurl'],
  mounted(){
    axios.get( this.ajaxurl ).then(function (response) {
    }
  },
}

解決這個問題後, 後續開始學 vue 才算慢慢有了進展.

2017年10月27日 星期五

A.I. 人工智慧 與 黎安娜之歌

A.I.這部電影刻劃了人類情感裡最深沉的部分,也帶出一些讓人思考的方向。

真的能透過 A.I. 學會什麼是愛嗎?

 機器人小男孩一開始的表現符合大部份人對小孩的期望,乖巧 貼心 等等正面的言語與行為,比較像是模擬人類對 "愛" 的想像來表達 "愛" 這件事。反而直到小男孩表達出吃醋的行為後,才讓人感覺到他是愛他母親的。

愛的本質是什麼?

這是有點弔詭的。如果小男孩自始至終都保持一樣正面的態度,會讓他像機器人。透過負面的行為反而讓他更像活生生的人。或者可以說,"愛" 的本質是不帶價值觀的,是在成長的過程中,學習把 "愛" 以正向的方式表達出來。

不過即使小男生開始比較像人類了,還是無法判斷說,這真的是愛嗎? 還是說這只是 A.I. 程式判斷而做出的行為?

如何判斷這是程式行為還是愛?

電影主題選擇用母子親情,而不是男女愛情,或許是在於親情更帶有一種天然的特性,尤其是在母親與孩子之間。小男孩的追尋之旅,某種程度上來說,也像是人類在成長中追尋與學習 "愛"。即使如此,我們還是無法判斷這是程式行為還是愛。

或者,說不定人類某天會需要承認,"愛" 也只是寫在染色體中的程式而已。

插曲,誰是獨一無二的?

在不認識時,我們靠著外表來認識其他人。隨著關係越來越緊密,開始互相馴服彼此而產生獨特性。對父母而言,自己的小孩永遠是獨一無二的,但在陌生人眼中,就像電影裡大量製造的機器人小男孩一樣。而在人的成長過程中,也是從認為自己是獨一無二的,慢慢發現自己越來越多平凡的地方。

如果機器人真的學會 "愛",那和人類的區別還剩下什麼?

這可能是許多科幻小說或電影喜歡探討的議題,不過我想這不是這部電影想要討論的。
在我看來,這部電影想探討 "愛" 這件事。
人類為什麼會有愛? 什麼是愛? 愛的力量可以到什麼程度?

愛是一種最深沉的渴望與記憶,可以深刻到即使過了上千年後依然期待的記憶。


剛翻到這篇文章,看著文章引用的電影對白,回想電影情節,竟然又有一種炫然欲泣的感受。這篇文章寫得很好,比我高明太多了,不過沒看過電影前先不要看比較好。

"如果我跟小木偶一樣變成小男孩,就能回家嗎?"  
看完電影再回頭看到這對白,這是多麼令人心碎的一句話。



黎安娜之歌

A.I. 給我的深沉感受,讓我聯想到這個短篇科幻小說。

這個短篇出自於王溢嘉先生翻譯的科幻小說選裡面的最後一篇。
這本科幻小說選是我最喜歡的科幻小說,印象中小時候家裡就有,從高中起開始已經看了幾十遍了。每個短篇都很有深度,不管是科幻性或故事性都很夠,譯者的文筆也相當好,每篇的風格也都相當不同,整本讀起來相當豐富有趣,也有很多感性的情節或讓人思考的地方。

然而年輕時,卻不太喜歡最後這一篇 "黎安娜之歌",覺得又長又缺乏劇情起伏,也沒什麼科幻的成份,看起來很悶。

一直到念研究所後,慢慢開始看出這篇有意思的地方。
工作後慢慢又多看了幾次,越來越感受到這篇深沉動人的地方。

人與人之間即使關係再親密, 畢竟還是無法心靈相通,因而還是會有所隔閡。
一種類似水蛭且缺乏生命意識的生物,卻能讓信奉者完全屏除隔閡感受到彼此的愛,因而形成一種看似慢性自殺行為的特殊宗教。

人與人之間的隔閡,把愛分隔開了,也讓人疏離了。

這篇深沉的地方在於,人在於面對這種先天限制的隔閡上,是多麼無能為力。你無法讓你愛的人知道你有多愛對方,也無法確實感受到愛你的人究竟有多愛你。人們只能透過外在的言語行為,來表達自己的愛,這是多麼間接又難以區隔真假的方式啊。


laravel + vue 基本使用方式

這篇是以 laravel 5.4 之後的環境為主.

laravel 5.x 開始內建對 vue 的支援, 5.5 內建對 react 的支援, 並提供 artisan 指令切換不同的 framework.
而 laravel 5.4 之後,  打包使用的工具從 elixir 改成 mix (webpack) , 所以查資料時, 會看到兩種不同流程的文件, 在於 laravel 版本不同的關係.

基本使用方式


這篇文章 對於 laravel 5.4 裡使用 vue 已經很清楚了, 不過我還是簡單筆記一下重點及遇到的問題
  1. 更新 node 到最新版, 並且在 laravel project 下安裝相關 node package
    sudo npm install -g n
    sudo n latest
    npm install
  2. add vue file under resources/assets/js/components
    例如新增 login.vue
  3. edit resources/assets/js/app.js , 把上面的 vue 檔案加入設定, 例如
    Vue.component('login', require('./components/Login.vue'));
  4. 執行指令打包 javascript file 到 public/js/  下
    npm run dev
然後就可以在 html  裡, 以  <login></login> 把 login.vue 的內容引入.

如果要把一些共用的 package 分開來不要打包到一起, 可以修改 webpack.mix.js,  設定要分開打包的 package, 例如
.extract(['lodash','jquery','axios','vue'])
就會額外再產生兩個 js file ,  manifest.js 和 vendor.js

補充上面第 1 點,  在 ubuntu 預設 nodejs 套件版本太舊, 因此在執行 npm install 時, 會產生一些問題.  因為對 node 不熟, 浪費了一些時間處理,  最後發現直接更新到最新版就完全不會有這些問題.  不過用這種方式更新 node 的話, 會安裝在 /usr/local/n/versions/node/  下, 使用 node 時就不是用 ubunut 的 nodejs 套件的檔案了.

較完整應用示例


這篇文章 有更完整的應用示例,  除了 php 的部份外, 還使用了 vue-router 和 vue-axios 兩個 package.

vue-router 可以看 這篇文章 , 中間有的動圖可以很容易看懂 vue-router 的功用, 也是一份寫的很清楚的使用教學.

vue-axios 是  vue 使用 ajax 的 package, 可以看 這篇文章



2017年10月13日 星期五

gitlab CI/CD 簡單測試環境建置

和前一篇文章標題看起來差不多, 多了個  CD , 但花了不少時間測試才跑起來  ^^

主要是參考 這篇文件, 寫的非常完整.
部署用的工具是 envoy , 這是給 php 開發環境的.  如果是別的程式語言, 可以換成 ansible 或其他類似的工具.

這邊將主要說明上面那篇文件的做法與流程, 然後再補充實作時遇到的問題與處理/除錯方式.

文件提供的步驟,  剛開始看好像不是很直覺, 因為有些東西是後來要用到, 在較早的步驟就先準備好. 但是弄清楚整個 CI/CD 流程後, 會發現文件步驟其實是非常流暢的.

以 CI/CD 流程來說大致如下
  1. 將程式碼 push 到 gitlab
  2. gitlab-runner 依 .gitlab-ci.yml 設定的 stage 進行
  3. 第一個 stage 是 unit test
  4. 第二個 stage 是透過 envoy 來 deploy 到遠端的機器

從文件來看, 前面準備的項目用途如下:
  1. Unit Test Code :  供 phpunit 測試用的程式, 其實不寫也沒關係, laravel 有預設一個範例, 會讓 phpunit 回傳成功
  2. Configure the production server  , 先準備好要部署的目標機器
    a. create new user,  可以 ssh 登入的帳號, 並且對網頁 document root 有讀寫權限
    b. add ssh key , 這有兩個地方會用到
        一個是讓 enovy 不用密碼連到這台機器, 要將金鑰內容先以變數的方式放在 gitlab 設定裡, 然後在 .gitlab-ci.yml 裡透過變數取得金鑰內容
        另一個是讓這台機器可以從 gitlab clone project
    c. configuring nginx ,  主要是將 document root 先指向預備要部署的路徑
  3. create container image , 以 php 官方的 image 為主, 再加上 php composer 及 envoy 的自訂 image, 作者連 Dockerfile 都提供了.
    作者是將 image 放到  gitlab container register 裡, 這個非必要. 如果要放到 dockerhub 或其他地方, 記得修改 .gitlab-ci.yml 裡的 image 設定即可.

文件中提到一些 CI/CD 流程裡的小技巧蠻實用的 (對我這個新手來說), 例如說
  1. 將 ssh 金鑰以變數的方式存在 gitlab project 設定裡, 然後在 .gitlab-ci.yml 就可以透過變數取得內容, 避免將金鑰直接放在 git repo 裡.
  2. 測試用的 mysql 是透過 gitlab-runner 帶起來的, 所以可以另外指定測試用的連線資訊放在 .env.example , 就不會把實際的連線資訊放到 git repo 裡.

實作時的環境, 一台 google cloud engine (gcloud) , 一台 aws ec2 . 
gcloud 上以 docker 環境執行 gitlab / gitlab-runner ,  ec2 則是直接跑 nginx + php-fpm

在過程中, 因為不瞭解以及環境的差異, 有遇到的問題如下
  1.  gitlab-runner 會執行指定的 image 所跑起來的 container 內自動取得 project, 放置的路徑是在  /build/[gitlab 的 username]/[project name]
    例如我的 gitlab user 是 duan , project name 是 MyFamily , 所以路徑就是 /build/duan/MyFamily
    一開始不知道, 想說為什麼沒有在 .gitlab-ci.yml 做取得 code 的步驟, 就可以進行相關的動作如 composer install
  2.  一開始流程想錯, 想在自訂的  php-fpm image 就先透過 git 取得 code , 後來發現這樣每次都要重新 build image ,  還是依文件的流程才對.
  3. 因為我程式開發環境也是用 docker , 而且和 gitlab 放同一台. 原本想讓測試環境用現成的 mysql container , 但這樣會讓測試與開發環境使用的 database 混在一起, 應該不正確.
  4. 在 .gitlab-ci.yml 裡設定的 variable 是給測試環境裡執行 mysql container 用的, 而 .env.example 則是在測試時, 在跑 artisan migrate 時用的連線資訊.
    因為我不是用官方的 mysql image, 和作者環境不太一樣, 在這邊遇到一些連線問題, 從 gitlab jobs 的資訊可以看到無法連上 mysql.
    除錯方式就是自己手動跑一下  docker run -it mysql /bin/bash , 發現第一個問題是這個 image 的 mysql user 不能用 root 去執行, 會有錯誤.
    修正後, container 跑起來後,  手動以 mysql 指令來登入, 發現第二個問題.
    無法以 mysql user 變數裡指定的名稱做登入, 而是用 root 登入.
  5. 在以 envoy 部署時, 因為之前沒用過 envoy,  看到在  git clone 的步驟出現連線錯誤資訊, 以為是連到目標機器時產生的, 後來才發現這是從目標機器要 clone project 時的權限錯誤.
    除錯方式就是自己 docker run 測試用的 image , 然後一步步模擬 .gitlab-ci.yml 裡的動作, 才發現 ssh key 沒問題, 可以連到目標機器, 才發現可能是目標機器在 clone project 時的錯誤 (ssh key 沒設好, 直接改用 http 的 url 來 clone 就沒問題了).

其實還有不少小問題, 都是基於不瞭解整個流程而造成的, 而且一開始也因為不瞭解各流程的相關性, 而不知道該如何除錯, 導致每次改一點設定檔 , 就 push 到 gitlab 看結果, 這還蠻花時間的.

最後看到 CI/CD 整個流程完成,  感覺真是太爽了, 雖然這只是個新手入門的程度而已.  QQ

之後近期目標放在幾個方向
  1. CI/CD 的部份:
    a. 用 ansible 取代 envoy  ,  這樣可以處理其他程式語言的需求
    b. 把部署目標的環境改成 docker .  不過這點我還在思考, 這樣做的目的是什麼.
  2. php / Laravel 的部份:
    a. 練習多寫 unit test ,  這方面我很缺乏.
    b. 除了 unit test 外的測試方式.

另外, 隨著程式架構開始複雜,  也會影響 CI/CD 流程上的處理方式,  這就要隨時間慢慢累積經驗了.

2017年10月6日 星期五

gitlab CI 簡單測試環境建置

先簡單說明建置的環境, 及規劃透過 gitlab CI 的流程

gitlab 的部分:
  • 用 docker  跑 gitlab
  • 用 docker  跑 gitlab-runner

要跑 CI 的環境:
  • 用 docker 跑三個 container,  nginx + php-fpm + mysql
  • 網站使用  laravel 放在 host 上, 以 volume 的方式掛在 php-fpm container

gitlab CI 流程:
  1. 在 host 上將 laravel code push 到 gitlab
  2. 依設定會觸發 gitlab-runner 開始跑 CI
  3. gitlab-runner 以指定的 docker image 為環境進行測試

大部分的設置流程在這篇寫得很清楚了, 這篇記錄一些遇到的問題及測試的方法.

  1. 我所有的環境都在同一台機器上面 (google cloud) 跑的, 所以在 gitlab container run 的時候, 設定的 GITLAB_HOST 為 device docker0 的 ip,  預設為 172.17.0.1
    如果 gitlab 和 gitlab-runner 不在同一台機器的話, 就要設定為 gitlab 那台的對外 ip
  2. .gitlab-ci.yml 裡有設定 tags 的話, gitlab 會去找設定 tags 一樣的 runner.
    這是前面文件裡沒提到的部分, 例如在 .gitlab-ci.yml 設定 tags 為 docker , 但是沒有 runner 的 tags 設定為 docker 時, 會從 gitlab 的 pipeline 裡看到 fail , 會有錯誤訊息如下
    
    This job is stuck, because you dont have any active runners online with any of these tags assigned to them
    

  3. gitlab-runner 的設定檔 config.toml 裡, 可以設定要掛載哪些 volume, 可以參考官方文件
    在 [runners.docker] section 裡, 設定格式為
    volumes = ["/home/duan/data/run/php:/run/php", "/home/duan/mysite:/var/www/mysite"]
    這表示會掛載 2 個 volume 分別到 /run/php 和 /var/www/mysite



因為 gitlab 不熟, 也沒用過 gitlab CI , 全部都用 docker 來跑感覺更複雜, 看文件時好像都很順, 自己實作時卻發現很多問號. 然後之前也沒寫過/用過 laravel 的 unit test , 所以還手動跑到  php-fpm 的 container 裡執行 phpunit  , 看會不會和 host 裡執行有不一樣的結果.

花了不少時間 (總共大約兩個工作天) 查資料, 設定和除錯, 最後看到 gitlab 的 jobs 出現 phpunit 測試完成的訊息, 還真是頗高興地. 最後附上主要的設定檔及一些相關的說明


config.toml

concurrent = 1

[[runners]]
  name = "my_first_runner"
  url = "http://gitlab_ip:port/ci"
  token = "the_token_from_gitlab"
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "php-fpm"
    privileged = false
    disable_cache = false
    volumes = ["/home/duan/data/run/php:/run/php", "/home/duan/mysite:/var/www/mysite"]
  [runners.cache]
    Insecure = false
.gitlab-ci.yml , 執行 phpunit 做測試

stages:
  - build

build-test:
  stage: build
  tags:
    - "docker"
  image:
    name: php-fpm
  script:
    - cd /var/www/mysite && ./vendor/bin/phpunit
跑 nginx + php-fpm + mysql 的 docker-compose.yml , 為了方便不想修改 nginx image 內的設定檔, 所以掛載 nginx 設定檔來用.

web:
  image: nginx
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - /home/duan/data/nginx:/etc/nginx
    - /home/duan/mysite:/var/www/mysite
  links:
    - php

php:
  image: duanli/php-fpm
  volumes:
    - /home/duan/mysite:/var/www/mysite
    - /home/duan/data/run/php:/run/php
  links:
    - mysql

mysql:
  image: mysql
  volumes:
    - /home/duan/data/mysql:/var/lib/mysql
  environment:
    - MYSQL_ROOT_PASSWORD=rootpassword
    - MYSQL_DATABASE=database
    - MYSQL_USER=user
    - MYSQL_PASSWORD=password
跑 gitlab 的 docker-compose.yml , 包含 gitlab + redis + postgresql , 這個 image 作者有提供很完整的 yml 設定方式

version: '2'

services:
  redis:
    restart: always
    image: sameersbn/redis:latest
    command:
    - --loglevel warning
    volumes:
    - /home/duan/data/redis:/var/lib/redis:Z

  postgresql:
    restart: always
    image: sameersbn/postgresql:9.6-2
    volumes:
    - /home/duan/data/postgresql:/var/lib/postgresql:Z
    environment:
    - DB_USER=user
    - DB_PASS=password
    - DB_NAME=dbname
    - DB_EXTENSION=pg_trgm

  gitlab:
    restart: always
    image: sameersbn/gitlab:9.5.5
    depends_on:
    - redis
    - postgresql
    ports:
    - "10080:80"
    - "10022:22"
    volumes:
    - /home/duan/data/gitlab:/home/git/data:Z
    environment:
    - DEBUG=false

    - DB_ADAPTER=postgresql
    - DB_HOST=postgresql
    - DB_PORT=5432
    - DB_USER=user
    - DB_PASS=password
    - DB_NAME=dbname

    - REDIS_HOST=redis
    - REDIS_PORT=6379

    - GITLAB_HOST=172.17.0.1
    - GITLAB_PORT=10080
    - GITLAB_SSH_PORT=10022
    - GITLAB_RELATIVE_URL_ROOT=
    - GITLAB_SECRETS_DB_KEY_BASE=password
    - GITLAB_SECRETS_SECRET_KEY_BASE=password
    - GITLAB_SECRETS_OTP_KEY_BASE=password
gitlab-runner 的 docker-compose.yml

version: '2'

services:
  gitlab-runner:
    restart: always
    image: gitlab/gitlab-runner:v1.1.0
    volumes:
    - /home/duan/data/gitlab-runner:/etc/gitlab-runner:Z
    - /run/docker.sock:/var/run/docker.sock:Z

2017年9月18日 星期一

docker image 與 docker-hub



這篇大致在說明  編寫image設定檔, 然後透過 github 及 docker hub 自動產生 image 的流程.


基礎資訊

很多情況, 會需要自己建一個符合需求的 image,  例如說我需要 php 7.0 + mongo + mysql 的環境, 但可能沒有剛好符合需求的 image , 就需要自己 build image 了.

在自建 image 前, 先考慮要將 image 放在那個 repo , 最簡單的就是放在 docker hub 上.
假設要放在 docker hub 上, 先去註冊一個帳號,  這個帳號會用來之後 image 要 push 或 pull 時的識別. 例如我的帳號是 duanli , 建立的 image 名稱是 php-fpm , 那 image 完整的識別名稱就是 duanli/php-fpm .

可以用指令  docker login 來快速在 docker hub 上申請帳號.

編寫 Dockerfile 及 build image

docker 要 build image 的設定檔是 Dockerfile , 然後透過指令來 create image
在設定好的 Dockerfile 同目錄下, 最簡單的指令是
docker build -t "duanli/php-fpm" .

以下是我的 php-fpm 的 Dockerfile

FROM ubuntu:xenial

MAINTAINER duan duan.li@gmail.com

ENV HOME /root
ENV LC_ALL          C.UTF-8
ENV LANG            en_US.UTF-8
ENV LANGUAGE        en_US.UTF-8


RUN mkdir -p /usr/local/openssl/include/openssl/ /var/www/html && \
    ln -s /usr/include/openssl/evp.h /usr/local/openssl/include/openssl/evp.h && \
    mkdir -p /usr/local/openssl/lib/ && \
    ln -s /usr/lib/x86_64-linux-gnu/libssl.a /usr/local/openssl/lib/libssl.a && \
    ln -s /usr/lib/x86_64-linux-gnu/libssl.so /usr/local/openssl/lib/

RUN pecl install mongodb

RUN echo "extension=mongodb.so" > /etc/php/7.0/fpm/conf.d/20-mongodb.ini && \
    echo "extension=mongodb.so" > /etc/php/7.0/cli/conf.d/20-mongodb.ini && \
    echo "extension=mongodb.so" > /etc/php/7.0/mods-available/mongodb.ini

COPY fpm /etc/php/7.0/fpm
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

EXPOSE 9000
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

大致上就是設定
  1. FORM : 從那個 image 當 base
  2. ENV  :  設定環境變數
  3. RUN :  在這個 base image 裡要執行那些指令
  4. COPY : 將 host 的 (執行 docker build) 所在目錄下的檔案 copy 到 image 內
  5. EXPOSE :  需要使用到的 port
  6. CMD :  當 container 啟動後, 預設會執行的指令
這篇文章 對 Dockerfile 的編寫有不錯的說明 (其實不止 Dockerfile, 這整篇介紹 Docker 都蠻不錯的)


上傳到 Docker Hub

上傳到 Docker Hub 相當簡單, 以上面例子來說
docker push duanli/php-fpm

之後就可以在任何機器上用 docker pull duanli/php-fpm 取得自建的 image 了.


自動化流程


有時候可能只是改一點 Dockerfile 的設定, 不想用自己機器來 build image , 可以考慮把 Dockerfile 放上 github / bitbucket , 再透過設定去觸發 docker hub 做自動 build image 的流程, 像 這篇文章 最下面提到的流程.

以 github 為例,  這篇 有蠻完整的設定流程.

之後每次把  Dockerfile push 到 github 後, 在 docker hub 上就會自動 build 最新版的 image.

docker 與 docker-compose in linux

docker 的文件非常多, 這邊是簡單整理一下半年前的筆記. 

基本安裝及設定

1. apt-get install docker.io

2. edit /etc/default/docker
   DOCKER="/usr/bin/docker"
   DOCKER_OPTS="-H tcp://127.0.0.1:2375 --dns 192.168.0.1"

3. export DOCKER_HOST=tcp://127.0.0.1:2375

第二項是設定 docker server 要使用的 DNS Server, 之前若沒有設定, 在 docker intranet 可能在 DNS 查詢上會有問題, 後來似乎沒有這個設定也不會有問題了.

docker 裡幾個常看到的名詞
  1. image : 這個和 OS image 是差不多的意思, 只是 docker 的 image 產生的方式不一樣.
  2. container : 將 image 掛載執行起來後的環境, 類似 Virtual Machine .
  3. host :  執行 docker container 的主機
  4. volume : 可以想像為 container 上所掛載的 storage
image 的內容是不會更動的, 如果要保存 data ,  最簡單的方法就是把 data 存在 volume, 這樣當 container 中止後,  volume 內的 data 還可以使用.

另一種情況是, 當環境還在開發中, 例如軟體設定檔和網頁, 就可以放在 volume,  這樣修改和測試都比較方便.

Docker 常用指令


docker images , list all images in host

docker pull [images]:[version] , e.g.  docker pull ubuntu:15.04

docker run -it [images]:[version] /bin/bash , e.g. docker run -it nginx /bin/bash
docker run -itd [images]:[version] /bin/bash  # run as daemon
docker run --name nginx -v /var/www:/usr/share/nginx/html:ro -v /var/nginx/conf:/etc/nginx:ro -p 80:80 -d nginx
   
docker ps -a  , list all container

docker exec -ti [container]

docker rm [container]
docker rm [image]

docker inspect [container] , get container information

docker rm $(docker ps -f "status=exited" -q)
docker rmi $(docker images -f "dangling=true" -q)
docker volume rm `docker volume ls -f 'dangling=true' -q`

因為後來都使用 docker-compose 來管理, 除了最後 3 個指令, 已經很少用 docker 指令了.

Docker Compose 安裝與使用

docker-compose 是透過 yml 格式的設定檔, 來管理 container , 例如 docker 的 argument , container 執行的相依性 (順序)  等.
對初學者來說, 看起來好像是要多學一個東西, 但其實可以節省不少管理與學習摸索上的時間, 而且對於架構不是非常複雜的環境來說, docker-compose 算蠻實用的了.

ubuntu 上直接用 apt-get 安裝即可
apt-get install docker-compose

docker-compose 預設讀取現行目錄下的 docker-compose.yml  的內容, 可以使用 -f 來指定 yml 檔案路徑名稱.

docker-compose  1.x 和 2.x 的 yml 格式有相當差異, 我之前只使用過 1.x 版本, 因此以下都是以 1.x 版本當例子

yml 格式設定蠻容易理解的, 例如


web:
  image: nginx
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - /home/duan/docker/nginx:/etc/nginx:rw,z
  links:
    - php

php:
  image: duanli/php-fpm
  volumes:
    - /home/duan/docker/bridge:/var/www/bridge:rw,z
    - /run/php:/run/php:rw,z
  links:
    - mysql
    - mongo
    - redis

這是透過  docker-compose 帶起 nginx + php-fpm 的例子, 後面省略了 mysql + mongo + redis 的部份.

要說明的部份大概是 volumes 和 links
  1.  volumes 最後的 rw,z 在 ubuntu 並非必要的, 在 centos 上因為 SELinux 的關係, 需要加上參數才能設定 volume 的讀寫權限
  2. 上面的例子中, nginx 的 volume 是為了使用 host 上的設定檔, 這樣測試時比較方便.
    php 的第一個 volume 是放網頁的路徑, 理由同上.
    第二個 volume 是因為我使用的 image 內沒有那個路徑, 但是 php-fpm 執行時需要這個路徑, 因此預設情況下會造成錯誤. 在不變更 image 內容情況下, 最簡單的方式就是掛載一個 volume 來給 php-fpm 使用.
  3. links 表示這個 continer 執行前, 要先執行那些 container .
    以上面例子而言, 就是要先執行 mysql/mongo/redis  , 再執行 php , 再執行 web

這邊可以看到 docker-compose 的格式會因為作業系統而有所差異, 在 windows 上的差異又更大了.

設定完 yml 後, 可以透過 docker-compose 指令來管理 container 了.

docker-compose up -d  # 將 yml 裡的所有 container 放到背景執行
docker-compose stop # 停止所有 container
docker-compose start # 將目前 stop 的 container 再 start
docker-compose logs # 將目前執行中的 container 所產生的 log 列出來, 常使用的指令
docker-compose rm # 移除目前所有被 stop 的 container
docker-compose ps # 列出目前所有 container

centos  上的安裝與設定

印像中是在 centos 6.0 上, 因為環境的不同及 SELinux 機制的關係, 所以會有些差異.
原則上我會盡量保持 host 原本的樣子, 所以不會去關閉 SELinux, 所以有些地方就必須配合系統的差異性去做調整.

mkdir -p /etc/systemd/system/docker.service.d/
vim /etc/systemd/system/docker.service.d/docker.conf
systemctl daemon-reload
systemctl restart docker

export DOCKER_HOST=tcp://127.0.0.1:2375

最後一行在 centos 7.0 的作法又不太一樣, 編輯  /etc/sysconfig/docker-network 
DOCKER_NETWORK_OPTIONS="-H tcp://127.0.0.1:2375 --dns 168.95.1.1"


docker-compose 的部份
yum install epel-release
yum install -y python-pip
pip install docker-compose
pip install docker-compose --force --upgrade

Container 的使用

一般來說, 初學者最常遇到的問題是,  怎麼尋找需要的 image , 以及如何設定 container 的參數. 有時候即使是設定很常見或很熟悉的軟體, 因為是透過 docker 使用, 也會遇到不少問題.

透過指令會列出符合名稱的 image , 例如
docker search postgresql
就會列出 postgresql 相關的 image, 以及一些相關的資訊, 例如是否官方提供的 (officical) , 目前得到好評數 ( Stars)
我個人的經驗是, official 不一定好用, 看 stars 可能較有參考性.

至於設定 container 的參數, 原則上當然是先找該 image 的官方文件.
其次是透過 docker exec 的方式進到 container 內查看 log 或系統情況來找問題.
最後就是修改該 image 的 dockerfile 重新製作 debug 用的 image 來找問題.
或者換一個 image 也可能是比較快的解決方案.


透過 Samba 設定 AD DC 及將 NAS (Synology) 加入網域

主要是參考 這系列的文章 , 這系列文章都有 簡體中文翻譯 的版本 (事實上我是先查到簡體版本, 沒寫文章出處但感覺是翻譯的, 才自行找到原文).

如果單純只是要設定 Samba 作為 AD Server, 這系列的前兩篇就足夠了.
後面的部份我也沒有設定過, 因為手上沒有可以加入網域的 windows client (隨機版的 windows 10 沒有加入網域的功能).

原本對 Samba 的印像還停留在很久前, 最多就是設定為 PDC BDC 之類的.
看了這系列文章, 加上正好公司進了台 Synology NAS,  因此找了台 linux 設定為 AD DC , 然後將 Synoloy NAS 加入網域並同步帳號, 來讓 NAS 的使用管理上更為方便.

基本上照文章來設定, 大致上不會遇到什麼問題, 很容易就設定好一台 AD DC.
在設定過程中, 會因為測試或管理的需求, 使用到很多平常不會用到的指令.

samba-ad-dc 管理上主要的指令是 samba-tool , 最常見的 user / group 的管理, 指令本身的 help 做的很完善, 大部份參數都很直覺, 一看就知道用途  ,  例如:
  • samba-tool user add duan  #網域裡新增使用者 duan
  • samba-tool user list
  • samba-tool group add mygroup
  • samba-tool group addmembers mygroup duan   #使用者 duan 加入 mygroup 群組
  • samba-tool user setpassword duan
另外內建的 password policy 很嚴格, 如果要變更寬鬆一些, 可以用 samba-tool domain passwordsettings 指令及相關參數來檢視現有規則及進行變更.

這篇文章 有蠻完整的 samba-tool 指令介紹.


將 Synoloy NAS 和 AD DC 同步帳號

在控制台的 "網域/LDAP" 設定裡, 設定網域的部份
  • 勾選 "加入網域"
  • 設定 "網域名稱"  "DNS 伺服器" , 後者即設定為 samba AD DC 的 IP
  • 勾選 "進階網域選項"
  • 設定 "DC IP/FQDN" (DNS 伺服器)   "網域的 Netbios 名稱"  "網域 FQDN" 
設定完後 Synology 會詢問 AD DC 的 administrator 密碼, 然後進行一些連線檢查,  都通過的話, 在 "網域使用者" 及 "網域群組" 就會出現  samba AD DC 上所設定好的資料.  之後就可以透過網域使用者或群組來設定 NAS 使用權限.


管理  AD DC 裡的使用者/群組資料

我要讓 AD DC 裡的使用者/群組資料同步的來源是 mysql ,  因此自己寫 php script 先從 mysql 拉資料出來, 然後用 shell script 讀資料同步到 samba AD DC 裡.

script 寫的不好, 就不在這裡獻醜了.  不過有兩個經驗可以分享:

1. 在用 samba-tool  add user 時, 有參數可以直接設定 password .
但是平日太習慣一遇到問題就 google,  沒去查 samba-tool 本身的文件, 直接 google 網路上其他人的設定經驗, 結果大部份文件因為不是用 script 處理, 所以沒有需求要直接帶參數設定密碼, 結果繞了半天路才發現可以直接設定, 浪費了不少時間.  samba-tool 在新增使用者時, 可以直接設定一些 attribute , 例如:

samba-tool user create $user $passwd --mail-address "$email" --department "$depart" --description "$name"

2. samba-tool 本身不提供 "修改" 使用者資訊的功能, 除了密碼以外.
也就是如果要修改使用者資訊, 要透過其他方式.
如果刪除使用者重新新增,  實際上會是不一樣的使用者 (ID).  這點從 Synology 同步過後的資料可以明顯看出.
如果在同步後刪除了某個使用者, 再新增名稱一樣的使用者, 從 Synology 同步後會發現該使用者的家目錄是一個新的位置, 原本的家目錄就只有 admin 可以看到了.


變更 AD DC 使用者資料的方法

要修改使用者資料 ( attribute ,  例如 email ) , 要透過 samba 本身提供的 ldap 相關指令.
使用上和 openldap 的指令很像但又有些差異, 所以一開始我誤以為是 openldap 的指令, 試了半天才發現錯了.

例如 openldap 有 ldapsearch ,  samba 是 ldbsearch,   openldap 有 ldapmodify,  samba 是 ldbmodify  .  指令看起來很像, 但參數不一樣,  而且 samba 的 ldb series 是不直接吃 stdin.

要用 ldbmodify 來修改 attribute,  預設是要先編寫一個 ldif file , 裡面定義好要 modify/add/delete 的參數和指令, 然後再把 ldif file 餵給 ldbmodify 指令來執行

如果要在 script 下指令,  會類似這樣的 command

echo -e "dn: CN=duan,CN=Users,DC=piip,DC=pro\nchangetype: modify\nreplace: department\ndepartment: 資訊室\n" | ldbmodify -H ldap://www2 -k yes -b CN=duan,CN=Users,DC=piip,DC=pro

在執行 ldbmodify 之前, 要先用 kinit 登入 administrator 以取得權限
所以在用 samba-tool 新增使用者之前, 一定要小心, 否則修改起來前會很麻煩的.


最後貼上當初設定 AD DC 做的簡易筆記, 從一些訊息可以大略看出當初設定遇到的小問題, 像是 password 預設要求的 policy 太嚴格之類的.

http://weiwenku.net/d/152352
https://www.tecmint.com/install-samba4-active-directory-ubuntu/

1. sudo apt-get install samba krb5-user krb5-config winbind libpam-winbind libnss-winbind smbldap-tools ldb-tools
   kerb5 server : www2.piip.pro
2. cd /etc/samba; mv smb.conf /tmp
3. samba-tool domain provision --use-rfc2307 --interactive
   "ERROR(ldb): uncaught exception - 0000052D: Constraint violation - check_password_restrictions: the password does not meet the complexity criteria!"  :  password too weak , re-run command
4. systemctl start samba-ad-dc.service
   "Failed to start samba-ad-dc.service: Unit samba-ad-dc.service is masked."  : systemctl unmask samba-ad-dc , systemctl enable samba-ad-dc

5. verify
   a. samba-tool domain level show
   b. kinit administrator@PIIP.PRO 
      "kinit: Cannot find KDC for realm "PIIP.PRO" while getting initial credentials"  :  edit /etc/kerb5.conf 

6 extra
  a. sudo samba-tool domain passwordsettings set --complexity=off  ,  no complex password needed
  b. sudo samba-tool domain passwordsettings show , show policy of password
  c. sudo samba-tool domain passwordsettings set --min-pwd-age=0
     sudo samba-tool domain passwordsettings set --max-pwd-age=0
     sudo samba-tool domain passwordsettings set --min-pwd-length=6
     https://lists.samba.org/archive/samba/2013-August/174949.html



http://weiwenku.net/d/157028

1. sudo samba-tool user create duan  // add samba user
2. samba-tool user list
3. samba-tool user delete [username]
4. samba-tool user setpassword [username]
5. samba-tool user enable/disable [username]
6. samba-tool gorup add/list/delete/listmembers/addmembers/removemembers

7. sudo systemctl disable winbind.service; sudo systemctl stop winbind.service ; sudo service samba-ad-dc restart
8. wbinfo -g
9. wbinfo -u
10. wbinfo -i [username]


https://linux.cn/article-8097-1.html

1. sudo apt-get install ntp ntpdate
   a. http://www.pool.ntp.org/zone/asia  , select ntp server
   b. edit /etc/ntp.conf
      change the pool server to "tw.pool.ntp.org"
      add config under 'driftfile',  "ntpsigndsocket /var/lib/samba/ntp_signd/"
      add config under 'restrict source', "restrict default kod nomodify notrap nopeer mssntp"
   c. sudo chown root:ntp /var/lib/samba/ntp_signd/
   d. sudo chmod 750 /var/lib/samba/ntp_signd/
   e. sudo systemctl restart ntp
   f. verify
      sudo netstat -tulpn | grep ntp
      ntpq -p
   g. force sync,  ntpdate -qu www2

AWS EC2 - cloud watch , load balance , auto scaling

AWS EC2 的 ELB ( load balance )  設定前,  要先設定好其他一些相關的項目.
因為是大約一年前設定的記憶, 自己之前筆記的也不是很完整, 只能列出設定的大略流程.


假設目前有個 instance , 需求是能在 CPU loading 80% 時, 自動再開一個一模一樣的 instance 做 load balance , 然後在原本 instance  CPU loading 30% 時, 自動把新開的 instance 清掉. 那需要相關的設定如下:

1.  將目標 instance 製作 image  (在 instance 介面的  Action 選 create image )

2. Launch Configurations (在 EC2  menu 的 Auto Scaling 裡)
    create launch configuration 時, 選擇 "My AMIs" 然後選擇前一步所產生的 image
    後續的設定和設定 instance 差不多.

3. 新增一個 Auto Scaling 設定, 選擇前一步設定好的 Launch Configure Name

4. 設定 Cloud Watch:  這設定不在 EC2 選單內, 而是 AWS 的服務之一. 
   設定兩個  policy,  對目標 instance 的 CPU loading, 超過 80% 或低於 30% 時發出 Alarm .

   Alarm 要設定 "AutoScaling Action" , 然後設定為前一步設定好的 Auto Scaling Name.
   然後 Action 分別設定為 "Add 1 instance"  或 "Remove 1 instance"

   到目前為止, 已經可以透過 Auto Scaling 和 Cloud Watch 在目標 instance CPU loading 變化時, 來自動增減 instance .  但是要做到分流, 也就是 load balance , 還要設定下一步. 如果在這階段就測試的話, 因為沒有分流, 所以目標 instance CPU loading 不會因為增加 instance 而降低, 不小心可能就會帶起很多 instance 了.

5. 設定 Load Balance (ELB),  在設定前要先瞭解一下 AWS 的 load balance 的運作方式.
    在做 load balance 時, AWS 會提供一個 ip (AWS 也有提供 FQDN) 做為實際流量的入口, 因此服務的入口要透過 AWS 的 ip/FQDN , 才會有 load balance 的效果.

    每個 load balance policy 大約會設定以下項目
  • instance :  那些 instance 會加入 load balance , 選目標 instance 即可. 之後透過 AutoScaling 增加的 instance 會自動加入 load balance 裡.
  • Security Group :  這邊值得注意一下. 因為這邊會套用這個 firewall policy 的是 AWS 的 ip/FQDN , 所以可以只要開 service 需要的 port 就好, 不需要開 ssh 之類的 (也連不上).
  • Listeners : 要讓 ELB 做 load balance 的 port  (即實際上需要 load balance 的 port )
  • Tag :   這個 ELB policy 取名, 會影響之後產生的 FQDN 


到目前為止, 大致上算設定完成, 只要將流量導入的點設定到 AWS ELB 所提供的 FQDN 即可.
如果自己有 DNS 要設定的話, 只要將原本設定的 FQDN 設定一筆 CNAME 到  ELB 的 FQDN 就好.


接下來是一些測試的方式或工具

ELB 的 Log 


aws 的 elb ( ec2 load balance)  預設是沒有 log 的, 可以在設定中打開, 它會丟到指定的 s3  Buckets

而 s3 那邊要設定 permission 才能收 elb 丟來的 log,  可是那個設定的方式很機車, 可以參考這個網頁  , 其中欄位設定上比較讓人困擾的可能是 s3 Bucket 要如何填.

假設我的 buckets 名稱是 duan, 裡面有個目錄叫 elb , 於是應該要填 arn:aws:s3:::duan/elb/*
比較機車的是, 都填寫好後, 它只是把你填寫的資料變成設定檔, 還要自己把設定檔加到 policy 中 (現在不知道介面改的友善了點沒).

至於 elb 那邊就比較簡單了,  s3 Butkets 的路徑就填寫 duan/elb 就好
elb 設定完成時會檢查 s3 的permission, 檢查過的話應該就沒問題, 會建立好相關的路徑並丟一份 test log , 可以馬上檢查設定正確與否

壓力測試

對 instance 做壓力測試, 可以很容易確認 Auto Scaling 和 Load Balance 有沒有生效
我在 Cloud Watch 設定是監控  memory 使用量,  所以我用 這個網頁 介紹的工具來做測試.

這個網頁有對 這個工具 stress  做更完整的介紹, 可以測試相當多種類的壓力測試.


關於 CloudWatch  monitor 的項目

CloudWatch 內建對 EC2 的系統資源只包含 cpu  network-inbound/outbound 及 disk-read/wrtie 的統計, 如果要自己監控其他資源如 memory 的話, 就要自己送資料給 CloudWatch.

而最快的方法就是用 AWS 提供的 script , 這個 script 可以取得 memory 和 storage 使用量送給 CloudWatch , 不用自己寫 script 就簡單多了.

可以參考 這個網頁 ,  不過有地方沒寫很清楚, 就是如果要使用 awscreds.template 設定 key, 就要指定這個檔案的路徑, 而不是放在同個目錄下就好. 至於如何使用這個 script ( argument 等等) , 如何設定 cron 以及安裝方式等等, 這個網頁算寫的很清楚了.

如果還要取得其它資訊送到 cloudwatch 監測 (例如資料庫的資訊) , 可能就要自己寫 script 或找別人寫好的來用了.

2017年9月17日 星期日

AWS EC2 - 基本設定

AWS 本身的文件算非常完整了, 網路上也有很多文件, 兩個交互參考的話, 常用的功能應該設定上不會有什麼問題.

AWS 上的設定介面和方式很多都有在更新, 我是半年多前使用的, 可能有些已經不太一樣, 不過概念上應該都差不多.

首先還是來簡單說明 aws ec2 web 介面上的名詞
  1. Instance : 即虛擬機器, 可以視為一台電腦 (硬體).
  2. AMI : 給 Instance 開機用的作業系統 image (OS)
  3. Volume : 儲存空間, 可以視為硬碟 (Storage)
  4. Security Group : 簡易的防火牆規則
  5. Key Pair : 加密金鑰, EC2 預設的 image 都是用金鑰而非密碼登入的. 
  6. Elastic IPs: instance 每次開機取得的 public IP 可能不一樣, 因此可以透過 Elastic IPs 申請固定的 public IP , 然後 assign 到指定的 instance 來使用.

使用 ec2 , 首先是設定  instance , 大致上是決定以下事項
  1. 決定 intance 所在的區域, 選擇上的主要考量是網路速度及價格
  2. 決定硬體等級 , 選擇上的主要考量是硬體速度及價格
  3. AMI :  ec2 預設的 linux image 有 AWS 自己的 linux,  centos, SuSE  和 ubuntu.
  4. Security Group , Key Pair 等.  這些之後都可以再變更.

有一點要注意的是, 預設的 root devise 只有 8G,  要記得在設定過程中先變更好, 否則之後要調整會比較麻煩.

在選擇預設的 AMI 時, 不同版本的 linux, 預設登入的帳號不一樣. 例如 AWS 自己的 linux 預設登入帳號是 ec2-user,   Ubuntu 預設登入的帳號是 ubuntu .

接下來就是 ssh 進去, 和一般 linux 幾乎是一樣的操作環境了.

2017年9月15日 星期五

Elasticsearch / Kibana 操作

Kibana 的操作介面,  大致上只要清楚幾個名詞的定義就可以大概知道要怎麼運用

  1. index pattern  :  elasticsearch 的 index , 類似 database 裡的 table name, 也就是 logstash 在 output 裡設定的 index 值. 可以透過以下指令取得 elasticsearch 的 index list
    curl 'http://localhost:9200/_cat/indices?v'
    
    而 index pattern 則是可以透過萬用字元, 來設定要從那些 index 找資料, 例如 filebeat-* 就是從所有名稱開頭為 filebeat- 的 index 裡找資料
  2. filter :  從選定的 index pattern 所符合的資料中, 設定過濾條件.
    在選單上的 Discover 裡, 可以設定多組 filter  (從上方的 add a filter).
    每組 filter 裡可以設定多個過濾條件.
  3. visualize : 從選定的 index pattern 所符合的資料中, 產生統計圖.
    在 visualize 的設定裡, 可以自行設定 filter, 或套用已設定好的 filter.
    可以從設定好的 visualize , 再增加額外的 filter 來觀察統計結果的變化.
  4. dashboard : 可以在 dashboard 中, 指定一個以上的 visualize .
    目的是在於可以將相關的統計資料 (visualize) 放在同一個 dashboard 來觀看.

field list

在設定完 index pattern 後, 會列出符合的 index 資料裡的所有欄位資料.
可以先確認是否符合自己設定的 grok parse 的結果.

filter 的設定

kibana 的介面提供的預設篩選條件有限, 若需要自定義篩選條件時, 可以點選 "Edit Query DSL" 來自行編輯語法.
可以從這個網站查詢合適的語法, 例如我要設定查詢條件是
欄位(field) "source"  的內容包含有 finance 這個字串, 設定的查詢語法是

{
  "query": {
    "wildcard": {
      "source": "*finance*"
    }
  }
}


visualize 的設定

visualize 設定裡, 選擇圖表種累的部份還蠻容易懂的, 視需要選擇即可.
比較需要說明的, 可能是設定 aggregation (X 軸) 的選項, 例如

  1. Date Histogram :依設定的日期/時間 field 來做統計
  2. Terms : 選擇需要的 field 來統計
  3. Filters : 可以設定 filter 來先對選取的資料過濾後再進行統計
另外就是在右上角可以設定資料篩選的時間範圍, 我一開始時沒注意到這邊, 結果時間範圍是錯的, 一直顯示沒有符合的資料, 浪費了不少時間除錯.


其他

Kibana 的 menu 裡有個選項  Dev Tools,  可以用來測試篩選資料的方法, 會比自己用 curl 對 elasticsearch 測試來得方便.

另外就是 menu 裡的 Timelion 選項, 可以看到資料匯入的統計圖, 可以用來確認資料是否有持續正常匯入.


大致上知道這些就可以簡單入手操作 kibana/elasticsearch , 其他的功能可能就要遇到才有機會瞭解了.

2017年9月14日 星期四

ELK 設定與除錯

關於 Elasticsearch , 一開始是沒有打算使用這麼複雜的解決方案的.

前一陣子用 php + monolog 將公司內的系統操作記錄以文字方式存成 log file, 原本是打算自己寫程式來處理. 但一來是考慮效能問題, 再者也想說這種東西應該有現成的工具可以處理, 所以就找了一些資料.

結果先是看到 logstash , 然後發現這東西似乎不是單獨運作的, 最後就變成嘗試  Elasticsearch + Kibana + Logstash + Filebear 這種複雜架構了.

安裝環境是 ubuntu 17.04 , 如果處理的目標是常見的 log format  (e.g. apache),  那照 這篇文章 幾乎不會遇到問題就安裝設定完成了.

我照著做遇到的問題, 大概只有以下地方
  1. 我偷懶, 沒設定 filebear 和 logstash 透過 ssl  , 結果不透過 ssl 的話, 從 filebear 的 log 來看,    logstash 會拒絕 filebeat 傳來的資料.
  2. 還是我偷懶, 文章沒看完. 在 Kibana 的操作介面上, 要修改 index pattern 的名稱.
    我漏了這段, 所以一直看不到資料.
  3.  grok pattern 設定的問題, 基本上可以用文章的 logstash 設定方式, 只是會無法照自己意思去產生需要的欄位, 所以需要自訂 grok pattern .

其實一開始我還搞不清楚這幾個套件的關聯/架構 , 就照著文件硬幹下去.  後來遇到問題反覆查資料才弄清楚.
  1. logstash :  依指定 pattern 來 parse log  , 將結果轉到 elasticsearch
  2. elasticsearch : 存放最後資料的地方 ,  支援 RESTFul 的操作方式
  3. kibana : elasticsearch 的 web 操作介面, 透過 RESTFul 和 Elasticsearch 溝通
  4. filebear :  將 log file 內容透過 network 傳給 logstash . 
    filebeat 並非必要, 尤其如果 logstash 和 log file 在同一台機器上時.
  5. grok :  logstash parse log file 的工具,  透過 grok 來設定的 pattern 來 parse log file



grok 的部份

我的 log file format 大致上像這樣:

[2017-09-11 10:34:25] duan.INFO: user add {"id":"106","ip":"10.0.0.1","name":"test","group":"1","page":"adduser"} {"sql":"insert into user values(NULL, 'test', 'test', '334', '2', NULL, '1', 'test@example.com.tw')"}


各欄位描述大約如下:

["時間"]  
"操作者"."log level"
"user action"
"user infomation" (json format)  
"extra information" (json format, 是一些額外的資訊, 例如該 action 實際的 sql 語法)


我先查了一下網路上別人提供用來 parse monolog 的 pattern , 然後依照自己的 format 修改. 這個測試 grok pattern 的網站很有幫助.
這介面可以設定     log format sample , parse patterns , custom patterns

log format sample 就是上面貼的 log 內容,  後面兩個要用實際例子說明一下.


grok {
  patterns_dir => ["/etc/logstash/patterns"]
  match => { "message" "\[%{TIMESTAMP_ISO8601:timestamp}\] %{DATA:logger}.%{LOGLEVEL:level}: %{MYWORD:action} %{MYJSON:userinfo} %{GREEDYDATA:extra}" }
}


message 後面的那串就是用來 parse log 的 pattern  , 而  TIMESTAMP_ISO8601  DATA  LOGLEVEL 等等, 是 grok 內建定義好的 pattern

而  MYWORD  MYJSON 這些則是我自己定義的 pattern ,  設定的檔案放在  /etc/logstash/patterns/ 下,  內容是


MYWORD [a-zA-Z ]*    #連續的英文字或空白
MYJSON {[^}].*}    # 以  { } 包圍的連續字串

一般來說, 大部份情況可以直接透過 grok 內建定義好的 pattern 來 parse log file
但因為我是先自己寫程式用 monolog 去產生 log file, 才來找工具 parse ,  所以沒有事先規劃好就變成要自己寫 pattern 了.

設定 custom pattern 的方法
grok 內建的 pattern


debug 的方式

filebeat 和  logstash 雖然都有自己的 log file, 不過我這邊 logstash 似乎沒設定好,  log 的內容很簡略

1. 如果要看 logstash 是否有正確 parse 資料, 可以在 output 設定裡加上

stdout { codec => rubydebug }

然後以手動方式執行 logstash  (而不是 service logstash start 這種方式)

/usr/share/logstash/bin/logstash --path.settings /etc/logstash --debug


如果 filebeat 有送資料來且有正確處理的話, 就會看到資料 parse 的結果

2. 有時候 log 成長的速度很慢, 但為了測試 filebeat 和 logstash 之間的運作, 會希望把整個  log file 重新送出
單純重啟 filebeat 是沒用的, 因為它會記得前一次處理到那邊 ( file point offset)  ,  所以要清掉它記錄相關資料的檔案


a. service filebeat stop
b. rm /var/lib/filebeat/registry   (該檔案路徑可以在  filebeat 本身的 log file 看到, 或者從設定檔也可找到)
c. service filebeat start


3. 清掉  elasticsearch 的資料
因為 elasticsearch 是 RESTFul 架構, 所以很多操作可以透過 curl 進行

curl -X DELETE 'http://localhost:9200/_all'



2017年9月13日 星期三

失眠夜

半夜睡不著覺 把心情寫到 Facebook
只好到客廳找另一個夢境

睡夢中被吵醒 我還是不確定
怎會有鍵盤聲音在外面的客廳

我悄悄關上門 帶著疑惑出去
原來是我家裡常出現的那個人

那個人不就是我家裡那迷糊的人
我們有同樣的問題 用筆電
用筆電 查詢失眠的症狀 Ho~Ho~


在客廳望著你的車 / 在客廳和我家的人
讓寶寶點綴成 最忙碌的夜晚
失眠這時刻 這一分一秒全都停止
心開始糾結

在客廳望著你的車 / 在客廳和我家的人
將泛黃的夜    獻給最孤獨的月
失眠這時刻 這一分一秒全都停止
心開始糾結  夢有你而美

讓我失眠是誰 是我
讓你失眠是誰 是妳
怎會有 寶寶哭聲環繞在我倆的身邊
讓我失眠是誰 是我
讓你失眠是誰 是妳
原來是 這客廳有美麗的邂逅

2017年9月11日 星期一

關於文言文

似乎不該淌這個渾水, 畢竟不說話沒人當我是啞巴. QQ

======

這樣說對很多教過我的老師們很抱歉, 恕罪則個. 在念大學加研究所七年裡, 唯一讓我從不翹課且能開心進教室上課的只有大一國文, 可能也是外系老師裡我少數記得名字, 也打從心底喜歡的教授.

夏教授不點名也不考試, 只要交 (指定的) 小說的心得報告. 當時學校還有統一使用的教材, 教授顯然不是很滿意但還是依循指定教材來上課, 但依然言談有趣. 而因為國文課常排在早上前兩節, 加上不點名也不考試, 上學期可能大家還是新生的關係, 上課人數比較齊全一點, 後來感覺人是越來越少, 教授也不以為意. 印像有一次開始上課時才到四五個人 (冬天~) , 之後才陸陸續續進來到有十幾個人.

我的文言文程度普普, 看看古文觀止之類的還過得去, 詩經楚辭四書之類的, 只看原文的話, 只怕誤讀的地方會不少.

對我來說, 如果有需要的話, 看文言文不會排斥, 但很少會主動去接觸. 以我個人的認知來說, 文言文已經超脫語言學習的範籌, 而是 藝術 文學 之類的事情.

學文言文有沒有用? 有用啊, 就好像學 音樂 繪畫 一樣.
用不同的方式運用文字, 表達特有的意境 情緒 或美感, 需要經過一定的學習才能領略.

在我看來, 文言文 詩 小說 散文 等等, 就好像數學裡的 代數 幾何 統計 微積分 一樣. 高中數學也有面臨過類似的討論, 統計 微積分 要不要教, 要不要考.

而這類問題的 源頭/徵結 或許在於, 我們對教育的目標和想法是什麼?

我們不能不正視的問題是, 現在學生要學的東西越來越多, 領域越來越廣泛. 若只看單一學科, [有可能] 覺得好像教的
東西越來越少. 但整體來看, 他們需要學習的量有變少, 壓力有變輕嗎?

我脫離學生很久了, 或許不是很瞭解現在學生的情況. 但就日常接觸到的訊息來看, 有些狀態和以往是一樣的. 不考的不會唸, 要學生唸的方法就是用考試.

就理想上來說, 教育應該是開啟一扇扇的門, 讓學生學習必要的知識及接觸各種不同的領域. 前者像必修, 後者像選修.

但是當各領域都認為自己領域是屬於 "必要的知識" 而非 "可能有興趣的領域" , 在資源有限的情況下, 必然產生排擠的效應.

如果把中文的學習, 只以實務化的角度去看, 就和學習外文一樣, 在於聽說讀寫, 那 詩 文言文 這些就自然成了被排擠的項目, 就好像數學的 微積分 統計 一樣.

我還記得在我小時候的年代 (小學的事都不太記得了, 以國中的情況來說), 許多被大人認為不重要的課 (聯考不考的課), 音樂 體育 美術 等等, 都被挪作自習或其他課程.

如果我們自己都以這樣 "實務性" 的角度來安排課程的話, 怎麼怪下一代也用這樣的角度來爭取他們認為應該學的範籌?

如果什麼都要學, 什麼都要用考試來鑑別學習能力, 又怎麼會有資源和時間可以尋找或學習考試以外的領域?

或許多留給學生們想像與思考的空間, 才能對於學習 文言文 音樂 繪畫 之類的產生興趣. 無法產生興趣的話, 怎麼學都只是填鴨式教育, 無法對人生有任何的助益.

我喜歡的詩句或古句, 絕大多數是在小說裡看到而吸收下來的 (所以往往只是片斷句子, 很少詩是會背整首的), 絕少是透過國高中課程而產生感動而記下的.

沒有一種食物裡的養份是其他食物不能取代的, 沒有一門學科能讓人在生命裡得到的助益是其他學科無法取代的. 或許有難易度之別或繞遠路的可能, 但人生多得是繞遠路才能領悟到的東西.

或許基於文化的傳承, 這是學習 "文言文" 或 "詩" 的一個原因. 畢竟如果和一般學外語一樣, 只要求聽說讀寫的話, 這些是屬於非實務的部份. 就好像國高中英文也不會拿莎士比亞或荷馬的作品來上課 (應該是吧? 我記得大一還是大二英文暑修 /咳 時, 是拿荷馬史詩上課的, 在公車上看到睡著.......)

然而如果希望這種文化傳承是有效的, 除了列在課綱裡, 還需要更有效的方法. 否則究竟有多少比例的人, 在日常生活之餘, 還會去 看古書 看詩 看小說 的?

簡單講, 我覺得在國高中課程裡, 文言 白話 詩 散文 小說 都應該有一定比例, 讓學生有機會接觸文學的各個層面.
問題出在於教育理念, 什麼都教不是大問題, 而是什麼都考.
於是讓學生花太多精力在不擅長或缺乏興趣的領域上, 而無從挖掘出真正的興趣.


======

在看過許多正反兩方的意見裡, 固然反對 [文言文] 的一方對於文言文的批判不是過於實務角度, 就是缺乏邏輯的合理性. 但擁護 [文言文] 的一方, 也有不少立論奇怪之處.
在我看來, 既有 "為反對而反對" 的人, 也有 "為反對反對而反對" 的文.

例如說:
1. 不會文言文, 沒辦法學習古人書裡的智慧, 像孫子兵法.

個人覺得, 自己若對文言文有興趣, 讀原文是一種樂趣. 否則像這類需求, 大可以看別人解讀後的心得, 極可能比自己看懂再領悟來得更有效率且正確.

2. 不會文言文, 就看不懂詩.

我覺得 "看懂文言文" 和 "看懂詩" 並非有絕對的關聯, 詩很多其實也還蠻白話的, 有時看不懂是在於生活體驗上的差異, 或者是典故的引用, 或者是對於文字使用情境上的不熟悉.

"兩岸猿聲啼不住, 輕舟已過萬重山" 有很文言文嗎?
難的或許是在瞭解這首詩創作的背景(典故), 才能體會這首詩的心境.

"葡萄美酒夜光杯, 欲飲琵琶馬上催, 醉臥沙場君莫笑, 古來征戰幾人回"
這應該也蠻白話的, 只要知道 "琵琶" 指的是什麼就好. 這是生活體驗差異的問題.

當然白話與否, 每個人標準也不太一樣. 連金庸也因為被不少讀者反應, 而把整套小說改寫的更 "白話" 一點.

不過我是覺得, 熟悉 文言文 是有助於對詩的文字與意境的理解, 但不見得不熟悉文言文一定就有礙於讀詩的樂趣或情感.


3. 還有文章拿文字遊戲來舉例, 同樣的幾個字用不同方式排一排都可以說得通, 來說明文言文的優美和特色.

我只想說那個例子舉得很爛, 完全是個反例, 其他不想說了.

看了一些文章, 其實有些好奇. 這一些吹捧文言文好處的作者, 有多少會在日常生活裡會去看古文或讀詩的. 看他們的文字或所舉的例子, 看不太出來 "會文言文" 對他們寫文章的幫助 QQ


2017年9月3日 星期日

[PHP] 使用 composer 管理自行開發中的套件

使用 composer 管理 php 套件, 遇到要自行開發的套件, 又不想上傳到 github 公開,  也不想自建一個 repo 那麼麻煩時, 可以透過以下方式進行.

假設環境如下:
  1. 網站根目錄為  /var/www/mysite
  2. 自行開發套件的路徑為  /var/www/mypackage
  3.  套件名稱為 duan/firstpackage
  4.  一個名稱為 mylog 的 class
[更新] :
原本設定方式還是要透過 git 方式來加入自己開發的套件, 而不是直接使用本機 (local file) 上的套件檔案.
正確做法應該是將 compoeser 裡的 repo 設定改為 path , 這樣就可以加上 symlink 參數, 產生 symbolic link. 這樣直接修改套件內容就會直接產生效果, 對於開發中的套件會方便許多. (原本方式要在修改後更新到 git , 然後在引用套件的地方跑一次 composer update 才會從 git 取出來更新 vendor 下的套件檔案).

[修正後的 composer.json 內容]
更新 compoer.json 的內容如下 (只列出 repo 的部份, 其它照舊)

"repositories": [{
    "type": "path",
    "url": "/var/www/piiplib/piiplib",
    "options": {
        "symlink": true
    }   
}]
[修正前的流程]

處理流程大致如下
1. 到 /var/www/mypackage 建立 composer.json (例如用 composer init )
    然後修改內容大致如下 (以 follow PSR 4.0 來說)

{
    "name": "duan/firstpacakge",
    "description": "my first pacakge",
    "license": "MIT",
    "authors": [
     {
           "name": "duan",
           "email": "duan@example.com"
     }
     ],
    "require": {},
    "require-dev": { },
    "autoload": {
        "psr-4": {"Duan\\Firstpackage\\": "src"}
    }
}


2. 在 /var/www/mypackage 建立以下路徑  src tests 後, 執行 composer update

3. 在 /var/www/mypackage/src 下建立檔案 Mylog.php
    namespace 為 Duan\Firstpackage


4. 對  /var/www/mypackage 做套件管理 (git)
a.  git init
b.  edit .gitignore , 加入  /vendor 和 composer.lock
c.  git add .
d.  git commit -a -m "some description"

5. 到 /var/www/mysite 下, 在 composer.json 加入以下內容

 "require": {
     "duan/firstpackage": "dev-master"
  }
 "repositories": [{
     "type": "vcs",
     "url": "/var/www/mypackage",
 }]
6. 在 /var/www/mysite 執行 composer update


沒錯誤的話, 在 /var/www/mysite/vendor/  裡, 應該會複製了一份  /var/www/mypackage commit 在 git 裡的內容到   /var/www/mysite/vendor/duan/firstpackage 路徑下

這時候要使用時, 依 composer autoload 的方式就可以使用了, 例如

require_once "/var/www/mysite/vendor/autoload.php";
use Duan\Firstpackage\Mylog;
$log = new Mylog;


參考資料
http://culttt.com/2014/05/07/create-psr-4-php-package/

2017年8月8日 星期二

痛風

上個月初,痛風又發作了一次。在發作過後的那個禮拜,才發現這次和以往不太一樣了。
痛風病史大約十年了,第一次發作時,和一些人一樣還以為是腳扭到之類的。當時在 MiTAC 工作,同事聽到我痛風都傻眼,當然我自己也很無言。
此後平均約二年左右會來一次痛的,中間則不定時夾雜一兩次輕的。痛的是指幾乎無法走路,一定要去看診打止痛針。輕的則有時候自己忍一下或是吃個庫存的藥,半天一天後就紓解了。關於庫存的藥,是因為醫生說不痛就不要吃,藥對腎和胃負擔大,留著等有發作再吃。以往來說,打完止痛針再吃個一次藥,就沒事了。
然而這次卻不太一樣,打完針之後的兩週,平均兩天沒吃藥就會開始有發作的徵兆,第三天若再不吃藥,就會開始痛到妨礙行走。
三四年前,和幾個大學同學聚餐。席間九位男生裡,倒有四位有痛風病史,我應該是其中最小咖的。聽著熟悉的朋友講著病史及就診的辛酸,那是我第一次對自己有痛風這件事感到壓力與恐懼。
我是很普通的人,我的思維與行為應該和大部分人接近,我想應該有不少人和我一樣,在遇到這情況時抱有僥倖的心理。狀況好像還好啊,大概也就這樣吧,一兩年痛個一兩天打打診吃藥就沒事了。
不過該來的還是要來,三週前的情況,讓同學描述的場景又回到眼前,我應該要面對現實了。開始規劃運動的時間和方式,試著讓自己能養成習慣,來調整身體隨年紀增長而變慢的代謝能力。
上週去看診拿藥,順便抽血驗尿做檢測。昨天去拿報告,醫生解說了落落長的一段話。檢測數據裡,大部分如肝腎功能之類的都正常,而膽固醇與尿酸過高。
醫生說後者和體質相關性高,不易直接從改變生活方式來改善,建議先開始長期吃藥控制看看,先吃三個月觀察。前者也達
服藥標準,但這和生活習慣有關,建議我先自行調整看看,三個月後再抽血看是否有改善。
關於這次發作的情況,我是真的有嚇到。
只能說身體健康就和考試一樣,或許可以僥倖個幾次混過關,但長期來說還是看實力的。或許每個人天生的實力不一樣,但已經知道有問題的地方,是躲不過去的。
老師都已經說這門課快當掉了,還一直翹課,就太有種了一些,只是………人生是不能重修的。

2017年7月10日 星期一

Laravel 筆記 - 安裝問題排除

安裝環境 ubuntu(1704) + apache(2.0+) + php (7.0+) + laravel (5.3+)

依 Laravel 官網建議方式安裝後, 可能還會遇到以下問題

1. apache 的 config file 需要設定在 laravel 的 public 有以下權限

AllowOverride All


否則從 Apache Log 可以看到 .htaccess: Options not allowed here 錯誤


2. laravel 目錄下的  storage  及其子目錄需要 web server owner 有讀寫權限, 否則會產生 http code 500 回應

3. 如果 apache module rewrite 沒有開啟, 在 ubuntu 可以下指令
sudo a2enmod rewrite


在很多情況下,  不是用  Laravel 架整個 web , 例如首頁是用 wordpress , 然後在其間用 laravel 架設其他需要的功能.

假設 wordpress 裝在  /var/www/wordpress ,  http://my.site/
laravel 裝在 /var/www/laravel
然後希望  http://my.site/mysite/  看到 laravel 架設的網站, 作法如下

1.  cd /var/www/wordpress;  ln -sf /var/www/laravel/public  mysite
2.  修改 apache config file ,  要把 directory 設定改為

AllowOverride All



這時瀏覽 http://my.site/mysite 就可以看到 laravel 預設的首頁了