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