docker-gitlab 與 container registry

之前有試了兩三次,但都卡在憑證部份,昨天終於搞定了。

主要參考文件是 https://github.com/sameersbn/docker-gitlab/blob/master/docs/container_registry.md

第一步是產生憑證,不要用上面文章提供的 openssl 指令,用 https://gitlab.com/gitlab-org/gitlab-ce/issues/25967 所提供的:

# 假定是在 docker-compose.yml 所在的目錄下
mkdir -p certs
cd certs
openssl req -nodes -newkey rsa:4096 -keyout registry-auth.key -out registry-auth.csr -subj "/CN=gitlab-issuer"
openssl x509 -in registry-auth.csr -out registry-auth.crt -req -signkey registry-auth.key -days 3650

第二步是修改、調整原有的 docker-compose.yml

# gitlab
gitlab:
  environments:
    # ... other environment variables
    # ...
    # Registry
    - GITLAB_REGISTRY_ENABLED=true
    - GITLAB_REGISTRY_HOST=registry.example.com
    - GITLAB_REGISTRY_PORT=443
    - GITLAB_REGISTRY_API_URL=http://registry:5000
    - GITLAB_REGISTRY_KEY_PATH=/certs/registry-auth.key
    - SSL_REGISTRY_KEY_PATH=/certs/registry-auth.key
    - SSL_REGISTRY_CERT_PATH=/certs/registry-auth.crt
  volumes:
    # ... other volumes
    # ...
    # 將前面建立的 certs 資料夾掛載到 /certs
    - ./certs:/certs
  links:
    # ... other service links
    # ...
    - registry
registry:
  image: registry:2.4.1
  expose:
    - "5000"
  ports:
    - "5000:5000"
  volumes:
    - /var/gitlab/shared/registry:/registry
    - ./certs:/certs
  environment:
    - REGISTRY_LOG_LEVEL=info
    - REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/registry
    - REGISTRY_AUTH_TOKEN_REALM=https://git.example.com/jwt/auth
    # 如果你用的 gitlab 設定為 relative url root,那就是
    # - REGISTRY_AUTH_TOKEN_REALM=https://example.com/git/jwt/auth
    - REGISTRY_AUTH_TOKEN_SERVICE=container_registry
    - REGISTRY_AUTH_TOKEN_ISSUER=gitlab-issuer
    - REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE=/certs/registry-auth.crt
    - REGISTRY_STORAGE_DELETE_ENABLED=true

第三步是改 Apache 或 Nginx 的設定,Nginx 的設定可以參考 https://github.com/sameersbn/docker-gitlab/blob/master/docs/container_registry.md

我用的是 Apache ,所以我是參考 https://gist.github.com/dkarlovi/5f6ab416aa882086c7305b004b590dd4 來做修改,改完記得重新啟動 Apache 或 Nginx。

修改完以後,執行 docker-compose up ,應該就沒問題了。

我遇過以下問題:

  1. 瀏覽 https://git.example.com/group1/project1/container_registry 時,出現 Internal server error:主要是 gitlab 環境變數裡的 GITLAB_REGISTRY_KEY_PATH、SSL_REGISTRY_KEY_PATH、SSL_REGISTRY_CERT_PATH 跟 registry 環境變數裡的 REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE 沒有設定好,當然其他的也有可能,儘可能都檢查一次。
  2. docker login 失敗:其實也是上面提的那幾個環境變數沒設定好。

檢討之前為什麼沒能架設好,原因如下:

  1. 對 SSL 憑證不了解:我的 git.example.com / registry.example.com 都使用 SSL 憑證,但又跟 registry-auth 的憑證搞混,總的來說,總共三個憑證,git.example.com 一個,registry.example.com 一個,registry-auth 一個。gitlab.example.com/registry.example.com 的憑證是給網頁伺服器(Apache/Nginx)使用,Apache/Nginx 設定裡要設定,然後 docker-compose.yml gitlab 服務裡只用到 gitlab.example.com 的憑證,要用 SSL_KEY_PATH, SSL_CERTIFICATE_PATH 跟 SSL_DHPARAM_PATH 這三個環境變數來設定。而 registry-auth 則是 gitlab 服務與 registry 服務作 token 認証用的,主要是 gitlab 環境變數裡的 GITLAB_REGISTRY_KEY_PATH、SSL_REGISTRY_KEY_PATH、SSL_REGISTRY_CERT_PATH 跟 registry 環境變數裡的 REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE。
  2. Apache 的 SSL 設定錯誤:SSLCertificateFile, SSLCertificateKeyFile, SSLCertificateChainFile 跟 SSLCACertificateFile 都要設定,我少設了 SSLCertificateChainFile,導致 registry 服務去問 gitlab 時,有憑證錯而有 HTTP request fail 的問題。

找 git repository commits 裡的特定字串

因為在目前的程式碼裡找不到特定的字串,但是之前應該有,可能被移掉了,所以想找找之前提交的程式碼有沒有特定的字串。

請 Google 大神幫忙了一下,在 StackOverflow 上找到答案:How to grep (search) committed code in the git history? – Stack Overflow ,原來用 git grep 搭配 git rev-list 就可以做到:

# 找全部
git grep <regexp> $(git rev-list --all)

# 只找特定目錄下的
git grep <regexp> $(git rev-list --all -- lib/util) -- lib/util

在 javascript 裡將 dict/object 轉換為 xml

照一般的作法,會是用操作 DOM 的方式來產生出 XML,但這樣很繁瑣,找了好幾個函式庫,最後是找到 object-to-xml (Github網址),使用方法很簡單:

var objectToXML = require('object-to-xml');

var obj = { 
  '?xml version=\"1.0\" encoding=\"iso-8859-1\"?' : null,
  request : {
    '@' : {
      type : 'product',
      id : 12344556
    },
    '#' : {
      query : {
        vendor : 'redhat',
        name : 'linux'
      }
    }
  }
};

console.log(objectToXML(obj));
/*
<?xml version="1.0" encoding="iso-8859-1"?>
<request type="product" id="12344556">
  <query>
    <vendor>redhat</vendor>
    <name>linux</name>
  </query>
</request>
*/

在 ‘@’ 裡的,都是屬性,而在 ‘#’ 裡的則是子 element 。

在 typescript 裡,要用

import * as objectToXML from 'object-to-xml';

來匯入使用。

sqlite3 的 alter table

sqlite3 的 alter table 不能刪除 column 或是修改 column ,只能新增!!

是故,如果要修改或刪除,只能先將原來的 table 更名,然後重新建立,再使用 insert into + select 來插入。

語法示意如下:

BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;

寫這篇,才想到 django migration 可能也因為這限制,所以當資料庫是 sqlite3 時,有刪除或更動 model 屬性時,並沒有真的更動資料表格。

Run ansible in gitlab runner

在 gitlab runner 裡想要執行 ansible playbook 進行佈署,幾個問題以及我的應對:

  1. ansible 的安裝:是可以自己加 ansible 的 package repository ,然後用 apt 安裝,但已經有好心人打包了裝好 ansible 的 docker image ,只要在 .gitlab-ci.yml 加入 image 設定即可:
    image: williamyeh/ansible:ubuntu14.04
  2. playbook 要放在哪裡?因為要直接在專案進行佈署,所以 playbook 也要放在專案裡,我在專案裡建立了一個 ansible 的目錄,把 playbook 放在這裡。
  3. 私密資料如何保存?gitlab 在專案設定裡提供了 variables,可以用來存放這些私密的資料,在 .gitlab-ci.yml 裡,只要用 $VAR_NAME 就可以使用這些變數。.gitlab-ci.yml 裡也可以自訂 variables ,但這些 variables 是進到 git repository 的,要視情況使用。
  4. Ansible 連到目的機器需要 SSH Key:這比較麻煩一點,好在 gitlab 有提供如何使用 SSH key 的文件,步驟很詳細,簡述如下:
    1. 在專案設定的 variables 裡新增有 SSH private key 的變數。
    2. 檢查 ssh-agent ,如果沒有就安裝 openssh-client
    3. 執行 ssh-agent
    4. 用 ssh-add 匯入第一個步驟所設定好的變數
    5. 在 ~/.ssh/config 裡加入不檢查 host key 的設定:
      Host *
          StrictHostKeyChecking no

參考資料:

compodoc

前兩天看到這篇:你寫的文件別人看得懂嗎?:compodoc ,所以想來試試看。

因為想放到 Jenkins 裡去自動產生 Angular App 的文件,所以想用 docker 來避免在 Jenkins 主機上安裝 compodoc 。

首先找到有沒有已經寫好的 docker image,很幸運,找到 sdvplurimedia/lea-pulse-compodoc – Docker Hub ,github repository 在 SDV-Plurimedia/docker-images: DockerFiles public ,從 Dockerfile 可以看出就只是簡單的繼承自 nodejs 的 image ,然後用 yarn 安裝 compodoc。

所以用 docker pull 拉到本地端,就可以用 docker run 來執行了,先看看有什麼參數:

docker run -it sdvplurimedia/lea-pulse-compodoc:latest compodoc --help

然後在專案目錄下建立 doc 目錄,用 docker run 將專案目錄掛載到 /src,輸出目錄掛到 /src/documentation,再執行:

docker run -v ${PWD}:/src -v ${PWD}/doc:/src/documentation -it sdvplurimedia/lea-pulse-compodoc:latest /bin/sh -c 'cd /src && compodoc -p tsconfig.json'

就可以在執行,並在 documentation 目錄裡找到 HTML 文件了。

Ansible 處理多主機不同密碼

用 Ansible 一陣子了,比較麻煩的地方是,主機不一定密碼都相同,簡單的方法當然是讓主機密碼都一樣,但這樣就降低了安全性。有沒有不需要改動密碼的方式呢?在網路上找了半天,找到這篇:ubuntu – Ansible: how to run a play with hosts with different passwords? – Stack Overflow ,裏面有個回應是建議使用 group_vars + ansible-vault + ansible_become_user/ansible_become_pass 來解決。

以下紀錄大致的步驟:

  1. 在 playbook 所在目錄或是 /etc/ansible 下建立 group_vars 資料夾 (以下簡稱 group_vars 資料夾)。
  2. 在 inventory 檔案裡,將相同密碼的主機編成一個群組,這裡假設群組名稱是 foo。
  3. 進入 group_vars 資料夾,用 ansible-vault create foo.yml ,這時候會詢問你 vault 的密碼,輸入完成以後,會開啟編輯器,請在裏面輸入
    ---
    ansible_become_user: "root"
    ansible_become_pass: "your_password"
  4. 存檔離開編輯器。
  5. 在輸入 ansible-playbook 或 ansible 指令時,帶 –ask-vault-pass 參數,也就是在執行時,會問你 vault 檔案的密碼,然後自動解密並讀入裏面的變數執行。

之後要編輯加密過的檔案,得用 ansible-vault edit foo.yml 才行。

參考資料:

Upgrade Jenkins plugins via SSH

之前都是啟用 CLI over remoting ,用 jenkins CLI 做更新,但前一陣子在更新 Jenkins 以後,開始該該叫,說這樣不安全。查了一下文件,發現可以用 SSH 來做。

  1. 在 http://jenkins/configureSecurity 裡啟用 random SSHD Port
  2. 在會使用 ssh 登入更新的使用者設定裡 (http://jenkins/user/YOUR_USER_NAME/configure),貼上 public key 。
  3. 參考並修改這個腳本,執行它就可以更新了。

wordpress 出現 Invalid post type 錯誤

今天進 wordpress ,選 All posts 後,就出現 Invalid post type. 的錯誤。先試著開啟 wordpress wp-admin/edit.php 看,發現一開始的檢查 $typenow 就導致錯誤 了,$typenow 是空的。

根據程式碼,試著在網址列加上 ?post_type=post ,能進入 All posts 了,但文章列表是空的。

  1. 上網找了一些文章,說是要移除 plugin ,但我試著移除了,沒有用。
  2. 對照目前的程式碼跟網路下載的 wordpress 4.8 程式碼,也幾乎是一樣的。
  3. 試著直接連線到資料庫看,資料表格一切正常,權限也都是正確的。
  4. 直接在 wp-config.php 裡加入
    define('WP_DEBUG', true);
    define('WP_DEBUG_DISPLAY', false);
    define('WP_DEBUG_LOG', true);
    

    ,然後看 wp-content/debug.log ,終於找到一點蛛絲馬跡。

看來是因為 wp-include/vars.php 有問題,裏面用 preg_match 檢查 _SERVER[‘PHP_SELF’] 時,因為 match 不到而出錯,猜想這可能連到影響到後續的 $typenow 有問題。根據這個線索去推敲,PHP 應該沒問題,察看了 php-fpm 的設定,也沒問題。後來找到這篇:php – Wrong SCRIPT_FILENAME in Apache 2.4.26 – Stack Overflow,再循線看到這篇:php – Wrong SCRIPT_FILENAME in Apache 2.4.26 – Stack Overflow才知道可能是 Apache 問題,察看了 pacman 的 log,果然前幾天有更新,但是因為沒有重新啟動,所以都沒發現問題。在設定裡補上

ProxyFCGIBackendType GENERIC

以後,重新啟動 Apache 就沒問題了。

awk 一行下載

為了下載投影片,但懶得一個個複製貼上、改指令,所以預先把投影片網址跟預定的檔名放在文字檔裡,然後用 awk 一次下載。

文字檔的格式是:url filename ,假定這個文字檔的檔名是 slides.txt

那麼,這樣就能搞定:

awk '{system("wget -O \"" $2 "\" " $1)}' slides.txt

參考資料:bash – awk system call – Stack Overflow