這裡紀錄下使用
GitLab 做 CICD (Continuous Integration/Continuous
Deployment, 持續整合、持續佈置)
時的一些佈驟及事項
GitLab 使用了 Job, Gitlab-Runner 及 executor 的設計。
在 Git 專案根目錄上會需要建立一個描述 Job 的 .gitlab-ci.yml 檔案,
其中記載了
CICD 要做的事項流程即要執行的 script 語句。
Gitlab-Runner 會跟 Gitlab repository 連線、檢查有無分派 Job 、並接受 Job , Gitlab-Runner 可能是被按裝在在 Windows 系統上、或是被按裝在 Linux 上、或甚至是在某台 server 跑起來的 Docker 中都有可能。
在 Gitlab-Runner 中,可以在其設定檔 config.toml 中設定多個 Gitlab-executor,當
Gitlab-Runner 接到需要執行的 Job 時,就會啟動相應 (例如 .gitlab-ci.yml 中擁有
tag1, tag2 的 Job 對應到有 tag1, tag2 的 Gitlab-executor) 的
Gitlab-executor。
因為 Gitlab-executor 本身需要跟 Giblab repository 連線,所以需要指定 Gitlab repository 的 SSL 憑證,例如如果專案位於 Gitlab 的 https://xxx.git/... 上,那就要取得 https://xxx.git 的憑證 (例如可以用 Chrome 瀏覽器去下載憑證)。
Gitlab-executor 是主要執行 .gitlab-ci.yml 中 Job 的 script
語句的環境,有分成幾種不同環境的 executor,
例如官方列出的 shell, docker,
ssh 等等,可參考 Executors | GitLab。
當使用 shell 環境的 Gitlab-executor 時,相當於直接在 Git-Runner
安裝環境上直接執行 script,
例如如果 Git-Runner 裝在 Windows
系統上,相當於直接執行 Windows 的 command line 語句。
或是例如如果
Git-Runner 裝在 Linux 系統 (如果裝在 docker 運行的 Linux 上也是一樣)
上,則相當於直接執行 Linux bash 語句。
當使用 docker 環境的 Gitlab-executor 時,Gitlab-Runner 會在所處環境下以指定的
docker image (可在 .gitab-ci.yml 中指定,或是在 config.toml 中指定預設的
image) 啟動一個指定的 docker container (這時 Gitlab-Runner
所被安裝的環境下必須要事先安裝好 docker),
Job 的 script
會以在這個被啟動的 docker container 下被執行。
例如如果啟動的 docker
container 是含有安裝 php 的 container,那就能執行 Job script 的 php 語句。
這裡我以一個例子做示範,
情境是用 docker 啟動兩個 container,
一個運行
Gitlab-Runner,
另一個運行 Linux ubuntu
系統來模擬正式環境程式要佈署到的那台 server,
在這裡 Git project
假設是一個 Maven 專案,我想要利用 GitLab CICD 來對專案進行建構 war 檔 (mvn
clean install),並以 SSH 的方式傳送到佈署 server,並以 SSH 連線佈署 server
執行解包 (jar -xvf xxx.war)
以下開始示範:
首先先安裝 Gitlab-Runner,
詳請可參考官網
Install GitLab Runner | GitLab ,
在這邊我以用 docker 來安裝 Gitlab-Runner 示範,
docker
可以使用 gitlab/gitlab-runner:latest 這個 docker image 來安裝
Gitlab-Runner,
安裝完好後,接下來要來進行 Gitlab-executor 的設定,
詳請請參閱官網
Registering runners | GitLab,
設定主要以/etc/gitlab-runner/config.toml 設定檔來設定,
如果
Gitlab-Runner 不是用 root 身份啟動的話,檔案就會改放在登入 user 的位置,
例如
~/.gitlab-runner/config.toml,可參考 Advanced configuration | GitLab 。
如果本來就有已設定好的 config.toml 檔的話,放在 Gitlab-Runner
container 中的正確位置就可以了,基本上每次對 config.toml 檔的修改
Gitlab-Runner 都會偵測到並再次載入修改的設定,不太需要重開 Gitlab-Runner,
如果第一次沒有 config.toml 檔存在,想要一個範本的話,可以使用交互模示來進行
register Gitlab-executor,啟動 Gitlab-Runner 後,執行 container 的
gitlab-runner register 指令:
(假設 container 名稱取叫
my-gitlab-runner-container)
docker exec -it my-gitlab-runner-container
gitlab-runner register
如果 Gitlab repository 是 https
的話,如之前所述需要設定憑證位置,指令要改成:
docker exec -it
my-gitlab-runner-container gitlab-runner register
--tls-ca-file=/path/to/tlsCaFile
請把 /path/to/tlsCaFile 改成憑證在
container 中的位置,
設定好以後就會多出 /etc/gitlab-runner/config.toml,
裡面的內容之後可以依自己需求需改
(保持可例如用
docker cp 取出後設定到 docker-compose volume 裡面做持久化之類),
而註冊好的
Gitlab-Executor 也會在 GibLab 網站上各 Project (如果是 Project runner 的話)
的
CICD --> Runner 設定裡面,
需要注意的是,在 GitLab
網站介面中,有時也會把 Gitlab-executor 稱呼成 Runner,
其實可以把
Gitlab-executor 當成是 Gitlab-Runner 在不同環境下的 Job script
執行就可以比較好理解了。
我的模擬目錄如下:
/docker-compose.yml
/gitlab-runner/Dockerfile
/gitlab-runner/config/config.toml
/gitlab-runner/ssh/id_rsa
/gitlab-runner/ssh/id_rsa.pub
/gitlab-runner/ssl/gitLabCA.cer
/online-server/Dockerfile
/online-server/ssh/authorized_keys
/online-server/project/
這邊我就直接貼上 docker-compose 的設定:
docker-compose.yml:
version: '2' services: docker-runner: build: ./gitlab-runner volumes: - /var/run/docker.sock:/var/run/docker.sock - ./gitlab-runner/ssl:/data/ssl/ - ./gitlab-runner/config:/etc/gitlab-runner online-server: build: ./online-server volumes: - ./online-server/ssh:/root/.ssh - ./online-server/project:/data/project ports: - "2222:22"
來讓 Gitlab-Runner 可以像宿主機 (host) 那樣使用 docker 去啟動一個 Gitlab-executor。
FROM gitlab/gitlab-runner:latest RUN mkdir /home/gitlab-runner/.ssh ADD ["ssh", "/home/gitlab-runner/.ssh"] RUN chown -R gitlab-runner:gitlab-runner /home/gitlab-runner/.ssh RUN chmod -R 700 /home/gitlab-runner/.ssh
可以看到在 /gitlab-runner-Dockerfile 中,設定了要啟動的 docker image 為 gitlab/gitlab-runner,
建立了 gitlab-runner 身份的 .ssh 資料夾,
用 ADD 指定放進了 id_rsa,並且用 chown 更改了檔案擁有者、
用了 chmod 改變了檔案權限,700 代表只允設檔案擁有者有 read (讀), write (寫), execute (執行) 的權限,須注意的是不能把 id_rsa 權限設定的太大,例如 777,太大的權限在 SSH 連線時也有可能會被禁止。
在我們看 /gitlab-runner/config/config.toml 之前,
先來看一下 onlin-server 的設定,online-server 是用來模擬一個有開放 SSH 連接並已有安裝好 jdk 的線上佈署環境。
/online-server/Dockerfile :
FROM ubuntu:latest RUN apt-get update RUN apt-get install ssh -y RUN apt-get install openjdk-11-jdk -y # RUN echo "root:12345"|chpasswd # we don't need to set password for root because we want to use ssh-key-only-login RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config # let root user can login #RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin without-password/g' /etc/ssh/sshd_config # let root user can login but only use way without password, like ssh key RUN sed -i 's/#AuthorizedKeysFile/AuthorizedKeysFile/g' /etc/ssh/sshd_config # let "authorized_keys" file can be used for ssh login RUN mkdir ~/.ssh RUN chmod -R 700 ~/.ssh RUN service ssh start EXPOSE 22 CMD ["/usr/sbin/sshd","-D"]
#PermitRootLogin prohibit-password 換成
PermitRootLogin yes 或
PermitRootLogin without-password (可以登入,但不能用密碼登入),
sed -i
的指令來做修改。
#AuthorizedKeysFile 也要把註解拿掉,換成
/gitlab-runner/Dockerfile 中的方式,為非 root 身份建立正確位置的 .ssh 資料夾、設定擁有者和權限。
並把 container 的生命週期綁在 "/usr/sbin/sshd" 上:
concurrent = 1 check_interval = 0 [session_server] session_timeout = 1800 [[runners]] name = "xxx description" url = "https://xxx-git" token = "xxxxxxxxxxxxxxxxxxxx" tls-ca-file = "/data/ssl/gitLabCA.cer" executor = "docker" [runners.custom_build_dir] [runners.cache] [runners.cache.s3] [runners.cache.gcs] [runners.cache.azure] [runners.docker] tls_verify = false image = "ubuntu:latest" privileged = false disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false network_mode = "gitlab-cicd-test_default" volumes = ["/c/Users/xxx/.m2/repository:/.m2/repository"] shm_size = 0 [[runners]] name = "yyy description" url = "https://xxx-git/" token = "yyyyyyyyyyyyyyyyyyyy" tls-ca-file = "/data/ssl/gitLabCA.cer" executor = "shell" [runners.custom_build_dir] [runners.cache] [runners.cache.s3] [runners.cache.gcs] [runners.cache.azure]
效果等同於 docker run 指令的 --network_mode 參數及 docker-compose.yml 裡的 network_mode 參數。
因為我的 docker-compose.yml 建立起來的 gitlab-runner 和 online-server 這兩個 container 所處的網路名稱為 gitlab-cicd-test_default,
variables: MAVEN_OPTS: "-Dmaven.repo.local=/.m2/repository" cache: paths: - .m2/repository/ stages: - build # - test - deploy build-job-docker: stage: build image: maven:3.6.3-jdk-11 script: - mvn clean install tags: - docker artifacts: paths: - target/*.war expire_in: 1 day when: manual build-job-shell: stage: build script: - mvn clean install tags: - shell artifacts: paths: - target/*.war expire_in: 1 day when: manual deploy-job-docker-executor: stage: deploy before_script: ## ## Install ssh-agent if not already installed, it is required by Docker. ## (change apt-get to yum if you use an RPM-based image) ## - 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )' ## ## Run ssh-agent (inside the build environment) ## - eval $(ssh-agent -s) ## ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store ## We're using tr to fix line endings which makes ed25519 keys work ## without extra base64 encoding. ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 ## - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - ## ## Create the SSH directory and give it the right permissions ## - mkdir -p ~/.ssh - chmod 700 ~/.ssh ## ## Optionally, if you will be using any Git commands, set the user name and ## and email. ## # - git config --global user.email "user@example.com" # - git config --global user.name "User name" script: - cd target - apt-get update && apt-get install ssh -y - scp -o StrictHostKeyChecking=no xxx.war root@online-server:/data/project - ssh -o StrictHostKeyChecking=no root@online-server "cd /data/project && jar -xvf /data/project/xxx.war" tags: - docker when: manual deploy-job-ssh-shell-executor: stage: deploy script: - cd target - scp xxx.war root@online-server:/data/project - ssh -o StrictHostKeyChecking=no root@online-server "cd /data/project && jar -xvf /data/project/xxx.war" tags: - shell when: manual
- 在這裡,我已經為注冊好的兩個 Gitlab executor 分別設定了 "docker" 和 "shell" 的 tag。
- MAVEN_OPTS: "-Dmaven.repo.local=/.m2/repository" 是設定給 Maven 的環境變量,指定 Maven local repository 的存放位置,並利用 cache paths 的設定將期設成 cache,:
cache: paths: - .m2/repository/
這樣在一個 Gitlab CICD Pipeline中的 Job 可以共享 Maven 下載的 Library,不用每個 Job 都再下載一次,雖然效果跟 artifacts 很像,不過用途不太一樣,如果要在 Job 之間共享檔案並且希望能在 Gitlab 網頁界面上存取,例如被 CICD 建立 (build) or 佈署 (deploy) 的檔案,通常會使用 artifacts 而非 cache,詳情可參考 How cache is different from artifacts。
需注意的是,以上的 Maven repository 設定只對 Docker Gitlab Executor 有效,代表的是被啟動的 Docker Gitlab Executor Container 的 Maven local repository 位置,
如果是 Shell Gitlab Executor 的話, Gitlab Runner 本身所處的環境應該會已經先設定好 Maven 的相關配置才對。 - 在這裡我只用到了 build, deploy 兩個 stage,所以 test stage 被注解掉了。
artifacts 設定了要和其他 Job 共享的檔案位置,是對 project 根目錄的相對位置,在這裡 target/*.war 即是 Maven 的 clean install 命令產出的 war 檔位置,expire_in 可設置 artifact 檔留存在 Gitlab 上的時間,在留存期間我們都可以到 Gitlab 上下載。artifacts: paths: - target/*.war expire_in: 1 day
- deploy-job-docker-executor 是一個 Docker Gitlab Executor,before_script 裡設定的語句可以在 script 語句執行之前被執行,在這邊執行的語句是參考了官方的範例:SSH keys when using the Docker executor ,把我們在 Gitlab 中設定 SSH_PRIVATE_KEY 參數設定到了 Docker Gitlab Executor Container 中的 SSH Private Key 應存放位置 (SSH_PRIVATE_KEY 的值即為 id_rsa 裡的 private key 內容)。
- deploy-job-docker-executor 的 script 內容為,安裝 SSH 連線用軟體,
進到 target 資料夾 (裡面有之前設定到 artifact 的 war 檔),
使用 scp 指令將 war 檔傳到要被布署的 server 上,
再使用 ssh 指令登入布署 server,用 jar -xvf 指令去解開 war 檔完成佈署。 - deploy-job-ssh-shell-executor 是一個 Shell Gitlab Executor,因為已經事先在其所在環境上 (即 Gitlab Runner 所安裝處的環境,此例為 Linux 環境) 設置好配置,所以只要直接執行 script 指令就好,script 指令基本跟 deploy-job-docker-executor 一樣,只差在不用再安裝 SSH 連線軟體。
需要注意的是,如果 Gitllab Runner 是裝在 Windows 系統上的話,script 裡的語句就會是 Windows cmd 的語法,可能會與 Linux bash 語法稍有不同。 - scp 及 ssh 指令的 -o StrictHostKeyChecking=no 參數是告訴 scp, ssh 指令使用"非交互方式" 執行,因為例如在第一次使用 scp, ssh 連線時,會有提示訊息詢問,例如:
The authenticity of host 'xxx (xxx)' can't be established. RSA key fingerprint is yyyyyyy. Are you sure you want to continue connecting (yes/no)?
但因為我們沒有辦法在 script 模式下用 yes 或 no 的交互模式,所以這時就可以用
-o StrictHostKeyChecking=no
來取消交互方式。 - 因為我們的 Gitlab Executor 都是設定手動執行 (when: manual),所以需要到 Gitlab 上自行啟動 CICD Pipeline ,如果一切都順利的話,應就可在被佈署 server 上 (即 online-server) 看到被解包的專案了。