hadolint

名字裡有 lint 的,通常表示這是個用來檢查語法的工具。所以 hadolint 是個檢查用的工具,主要是用來檢查 Dockerfile 。

安裝方法

hadolint 的原始碼放在 github 上,可以直接透過這個網址去找最新的編譯好的版本來下載:https://github.com/hadolint/hadolint/tags

下載以後,變更權限,然後搬到 /usr/local/bin ,就可以使用了。

chmod +x hadolint-Linux-x86_64
mv hadolint-Linux-x86_64 /usr/local/bin/hadolint

使用

使用很簡單,帶入 Dockerfile 即可。

hadolint Dockerfile

執行以後,會出現 hadolint 覺得有問題的地方,這時候就可以參考 hadolint rules (要往下捲,找到 Rules) 的說明去修改 Dockerfile。修改完可以再重新執行檢查。

其他使用方式

除了可以下載執行檔以外,也可以透過 container 方式來運行,簡單的說就是 hadolint 工具被包在 Container image 裡,那用 docker run 或 podman run 就可以檢查。

docker pull hadolint/hadolint
docker pull hadolint/hadolint:v2.12.0-debian
docker pull hadolint/hadolint:v2.12.0-alpine

docker run --rm -i hadolint/hadolint < Dockerfile
docker run --rm -i hadolint/hadolint:v2.12.0-debian < Dockerfile
docker run --rm -i hadolint/hadolint:v2.12.0-alpine < Dockerfile

最後一種方式,是搭配 Visual Studio Code (vscode) 的 extension,如果你剛好是把 Visual Studio Code 當作主力的話,這很方便。從 Visual Studio Code 的 marketplace 上找 hadolint ,就可以安裝。安裝以後,在編輯 Dockerfile 時,下方就會出現檢查結果,邊修改邊檢查。

結論

Dockerfile 的檢查可以導入 CI 的流程裡,一方面是可以讓 Dockerfile 標準化,一方面透過這些規則,也可以縮小 Container image 的大小,加快 docker pull 的速度。

參考資料

microk8s使用自建的mirror registry

文件在這邊:How to work with a private registry

設定方法很簡單,設定檔案位置在 /var/snap/microk8s/current/args/certs.d/ ,每個 registry server 都有一個對應的目錄,裏面會有 hosts.toml 。例如 docker.io ,目錄就是

/var/snap/microk8s/current/args/certs.d/docker.io ,hosts.toml 的內容是
server = "https://docker.io"

[host."https://registry-1.docker.io"]
  capabilities = ["pull", "resolve"]

要改為 mirror registry,就變為

server = "https://docker.io"

[host."http://your_registry_server/v2"]
  capabilities = ["pull", "resolve"]
  override_path = true

關鍵在於 [host.””] 裡的 server 位置還有 override_path = true

修改完成以後,重新啟動 microk8s 即可。

sudo snap restart microk8s

基本設定方法就是這樣。之前在看的時候因為眼花,一直想說為什麼會有 “‘ | sudo tee -a /var/snap/microk8s/current/args/certs.d/k8s.gcr.io/hosts.toml” 這一段,後來仔細看,才知道文件裡貼的是一個指令。

以後改為這樣,就可以不用去外部拉取,加快拉取 image 速度了。

podman如何使用registry mirror

之前弄好 registry mirror,因為是用 docker,所以需要調整的設定檔是 /etc/docker/daemon.json

那在 podman 裡,該怎麼設定呢?

這部份可以參考 /etc/containers/registries.conf 裡面的說明,然後在 /etc/containers/registries.conf.d 資料夾裏面加上新的設定檔就可以。

例如可以在 /etc/containers/registries.conf.d/my-mirror.conf 加入以下內容

[[registry]]
location = "docker.io"

[[registry.mirror]]
location = "192.168.11.2:6000"
insecure = true

依照行號說明如下:

  1. [[registry]] 必須要加
  2. location = "docker.io" 是表示,遇到 docker.io 時,就要參考這裡的設定。
  3. [[registry.mirror]] 這是表示下面要寫 registry mirror 設定了。
  4. location = "192.168.11.2:6000" 是表示 registry mirror 的位置。
  5. insecure = true 是表示不要檢查這座 registry mirror 的 SSL 憑證。

設定完以後,在 podman pull docker.io/<image> 的時候,實際就會到 registry mirror 拉取 容器映像 ,由 registry mirror 再去決定是否到外面的 docker.io 拉取容器映像。

參考資料

Registry Mirror

最近在建置 container image 時,常會踩到 exceeded 的錯誤,這是因為 docker 在 2020 時,公告了新的收費機制,並為拉取 container image 的 API 加上了限制,沒有登入的使用者現在每6個小時只能有 100 個 pull requests可用;有登入且採用免費方案的使用者,每6個小時有 200 個 pull requests 可用。這在初期測試 pipeline 時,根本很容易就用超過。

上網搜尋看有沒有方法可以避過這限制,後來在 gitlab 網站上找到文章:Caching Docker images to reduce the number of calls to DockerHub from your CI/CD infrastructure | GitLab

文章裡有提到兩個方法,第一個簡單的方法就是登入,這樣可以提升到 200 個 pull requests。

因為大部分的 executor 都是使用 docker 或 docker + machine,所以設定方法可以單純的在 pipeline 裏面作 docker login,但這樣還要多塞指令跟 credential 。gitlab 很貼心的提供了一個內建的變數,要用這個變數,需要在 gitlab 專案的 settings > CI/CD > Variables 裡,去增加 DOCKER_AUTH_CONFIG ,那在執行 pipeline 時,gitlab-runner 看到有這個變數,就會自動做 login 的動作。

DOCKER_AUTH_CONFIG 的內容可以透過以下方式取得:

  1. 在終端機用指令先登入 index.docker.io: docker login
  2. 用 docker 指令登入後,在家目錄的 .docker 目錄下就會出現 config.json : cat ~/.docker/config.json
  3. 用 jq 指令讓內容變成一行: jq -c "." ~/.docker/config.json

輸出的結果,就可以拿來填了。

第二個方法,則是建置 Registry mirror。

找一台已經安裝 docker 的主機,然後使用 docker 啟動 registry container。在啟動的時候帶入環境參數 REGISTRY_PROXY_REMOTEURL

docker run -d -p 6000:5000 \
    -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \
    --restart always \
    --name registry registry:2

然後登入到 gitlab-runner 所在的主機,修改 /etc/docker/daemon.json ,內容如下

{
  "registry-mirrors": ["http://<your-registry-mirror-server:6000"]
}

修改完以後,讓 dockerd 重新載入設定:

sudo systemctl reload docker

再用 docker info | grep -A 1 -B 1 Mirror 確認。

這樣就設定完成了。

那要怎麼知道 gitlab-runner 在建置時有使用這個 registry mirror 呢?這時可以用 curl 指令來檢查

watch -n 60 curl -s http://<your-registry-server>:6000/v2/_catalog

輸出結果大致會是這樣

{"repositories":["library/docker","library/python"]}

至此,就可以算是解決了 exceeded 的問題啦。

ansible-builder

看到這篇:紅帽推出IT自動化工具Ansible Builder以加速執行環境創建 | iThome ,所以就來試試看。

就我試用過以後,我的理解是,這工具可以幫忙創建出一個具有 Ansible 環境的 docker image。

環境是定義在 execution-environment.yml 裡,讓我們直接看範例

---
version: 1

# 即基礎 image,目前來說,都是用 quay.io/ansible/ansible-runner:stable-2.9-devel
base_image: 'quay.io/ansible/ansible-runner:stable-2.9-devel'

# 需要自訂 ansible.cfg 的話,這邊就是填 ansible.cfg 的路徑檔名
ansible_config: 'ansible.cfg'

# 相依
# galaxy 是填 requirements.yml,檔案裡描述 playbook 會用到的 role/collection,沒用到可以不填
# python 則是填 requirements.txt,檔案裡描述會使用到的 Python modules,沒用到可以不填
dependencies:
  galaxy: requirements.yml
  python:

# 額外要加入的 docker image 建置步驟
# prepend 會在預定的建置步驟之前
# append 則是在預定的建置步驟之後
additional_build_steps:
  prepend: |
    RUN whoami
    RUN cat /etc/os-release
  append:
    - RUN echo This is a post-install command!
    - RUN ls -la /etc

有了 execution-environment.yml 之後,就可以用 ansible-builder 來建置。

首先,得先安裝:

# python3
pip3 install ansible-builder

接著就可以建置了

ansible-builder build --tag=example

建置完成,會有 docker image,在目錄下則會出現 context 資料夾,這個資料夾裏面就是 ansible-builder 建置過程中所產出的 Dockerfile 以及建置 Docker image 所需的檔案。

打開 Dockerfile ,就會看到 ansible-builder 幫我們產出了什麼。

FROM quay.io/ansible/ansible-runner:stable-2.9-devel as galaxy

ADD ansible.cfg ~/.ansible.cfg

ADD requirements.yml /build/

RUN ansible-galaxy role install -r /build/requirements.yml --roles-path /usr/share/ansible/roles
RUN ansible-galaxy collection install -r /build/requirements.yml --collections-path /usr/share/ansible/collections

RUN mkdir -p /usr/share/ansible/roles /usr/share/ansible/collections

FROM quay.io/ansible/python-builder:latest as builder

ADD requirements_combined.txt /tmp/src/requirements.txt
RUN assemble

FROM quay.io/ansible/ansible-runner:stable-2.9-devel

RUN whoami
RUN cat /etc/os-release

COPY --from=galaxy /usr/share/ansible/roles /usr/share/ansible/roles
COPY --from=galaxy /usr/share/ansible/collections /usr/share/ansible/collections

COPY --from=builder /output/ /output/
RUN /output/install-from-bindep && rm -rf /output/wheels
RUN echo This is a post-install command!
RUN ls -la /etc

大抵來說,

  1. 使用了 multi stage build 以減少 docker image 的大小
  2. 使用 ansible-galaxy 安裝相依的 role/collection
  3. 會看到 additional_build_steps 裡描述的步驟

好,那建置出 docker image 以後,怎麼使用呢?

假設 playbook 是放在 project 目錄下,那麼就這樣執行

docker run --rm -v /runner/project:$(pwd)/project -it example:latest ansible-playbook -i localhost, -c local /runner/project/test.yml

這邊稍微取了點巧,只簡單用 local connection (-i localhost, -c local) 在本機執行,你也可以在這邊使用自己的 inventory。

建置出 Ansible 執行環境的 docker image 以後,除了可以固定住執行 Ansible playbook 的環境,也有利於打造出標準化的 CD 環境,進而減少開發與佈署的時間。

讓 docker daemon可以給別台電腦存取

docker 官方並不建議使用這方法,但還是有文件:Configure where the docker daemon listens for connections

不建議的原因很簡單,安全性。docker daemon 是用 root 權限在執行,而 docker 又沒有這方面的安全機制去處理帳號驗證,自然不建議這樣使用。 目前的主流是使用 kubernetes,另外,docker 最新的版本是有提出 docker context,利用 context 可以管理多種連接方式,其中一種連線方式是走 ssh ,會比較安全。

目前我只是想方便的操作虛擬機,所以可以不需要太考慮安全性問題。

編輯 systemd unit:sudo systemctl edit docker.service,把下面幾行貼進去

[Service] 
ExecStart= 
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 

讓 systemd 重新載入設定: sudo systemctl daemon-reload

重啟動: sudo systemctl restart docker

檢查: sudo netstat -lntp | grep dockerd

在別台電腦上,先設置環境變數:export DOCKER_HOST=tcp://your_docker_host:2375 , 然後用 docker 指令就可以存取該台 docker 主機了。

用 go 的 docker image 來建置 go 專案

指令蠻簡單的,而且可以指定用 go 的哪個版本來進行建置。

# 先切到你的專案目錄下
cd "$WORKSPACE"

# 執行 docker run
docker run --rm -e GOBIN=/usr/src/myapp -v "$PWD":/usr/src/myapp -w /usr/src/myapp golang:1.6 bash -c make
  • –rm 表示執行完就把 container 刪掉
  • -e GOBIN=/usr/src/myapp 是設定環境變數,GOBIN 這環境變數是指編譯好的執行檔所放的位置
  • -v “$PWD”:/usr/src/myapp 表示把當前的目錄掛載到 container 裡的 /usr/src/app
  • -w /usr/src/myapp 表示把工作目錄指定到 /usr/src/myapp
  • golang:1.6 這是表示要使用 golang 1.6 這版,其他版本可以參考 dockerhub
  • bash -c make 是指用 bash 來執行 make,要用 bash 的原因是大部份 Makefile 都是假設用 bash,用其他 shell 會很容易踩到雷。

dockerhub 上有更多說明。

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 文件了。

parse-server 架設小記

花了一些時間簡單的架設 parse-server,把過程紀錄一下。

  1. 安裝 docker 與 docker-compose
  2. git clone https://github.com/parse-community/parse-server.git
  3. 建立 docker image:
    cd parse-server && docker build --tag parse-server .
  4. git clone https://github.com/parse-community/parse-dashboard.git
  5. 建立 docker image:
    docker build --tag parse-dashboard .
  6. 建立 config.json,your-ip 要填你瀏覽器可以存取到的 IP ,而不是用 docker container 的 ip ,我本來是填 http://parse:1337/parse (請看 docker-compose.yml 裡的 links),但這樣 parse-dashboard 會說無法連到伺服器。
    {
      "apps": [
        {
          "serverURL": "http://<your-ip>:1337/parse",
          "appId": "APPLICATION_ID",
          "masterKey": "MASTER_KEY",
          "appName": "MyApp",
          "production": false
        }
      ],
      "users": [
        {
          "user": "user",
          "pass": "pass"
        }
      ]
    }
    
  7. 建立 docker-compose.yml
    ---
    version: '3'
    services:
      mongodb:
        image: mongo:3.4
    
      parse:
        image: parse-server:latest
        environment:
          FOO: bar
        command: --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongodb/test
        links:
          - mongodb
        ports:
          - "1337:1337"
      parse-board:
        image: parse-dashboard:latest
        environment:
          PARSE_DASHBOARD_ALLOW_INSECURE_HTTP: "yes"
        links:
          - parse
        ports:
          - "4040:4040"
        volumes:
          - ./config.json:/src/Parse-Dashboard/parse-dashboard-config.json
    
  8. docker-compose up

要啟用 push notification ,得調整 docker-compose.yml 裡 parse service 的 command,加入 push notification 的相關選項。從選項可以看出,parse 仍是利用 GCM/APNs 來發送通知。

LiveQuery 在佈署到 AWS 時,要用 ALB,因為 ELB 不支援 WebSocket。

在正式環境裡,要記得把 PARSE_DASHBOARD_ALLOW_INSECURE_HTTP 設為 “no” ,並且加上 SSL 。也可以參考 parse-server github 網頁上對於佈署的說明,有蠻多佈署到平台上的現成範例可參考。

參考資料:

docker daemon socket

dockerd 預設只使用 /var/run/docker.sock,如果需要讓其他的 host 去管理,就需要額外設定。

在 Ubuntu trusty 裡,要編輯 /etc/default/docker,加入

DOCKER_OPTS="-H 0.0.0.0:2375"

然後重新啟動就可以了。

不過這樣設定以後,就等於任何人都可以透過這個 port 去控制你這台機器裡的所有 container ,官方建議加上 TLS 來保障連線的安全,晚點再來研究怎麼加上 TLS 。

參考資料: