GitLab CI + Windows Docker 的一些紀錄


Docker(官網)這種比虛擬機器更輕量化的「容器」虛擬化技術,基本上算是這幾年來一個滿重要的技術。透過 Docker,他可以將需要的環境打包、建構出類似虛擬機器,但是更小、更彈性的可移植環境,在需要自動佈署的環境上,應該算是很實用的一種技術。(參考介紹參考介紹

不過由於工作的領域不同,所以 Heresy 其實之前都沒有去碰這一個技術;真的開始接觸到,是之前試著使用 Docker 來架設 GitLab Server 的時候開始的。

後來,Heresy 也很認真地開始搞整個 GitLab CI/CD 的架構,試著透過 GitLab 的系統來做一些程式開發的自動化建置、測試。最初,考慮到環境的熟系程度,Heresy 都是直接使用作業系統的 shell 來作為 gitlab-runner 的 executor。

而在整個弄到一定程度後,也發現如果能把 runner 的 executor 改成 Docker 的話,的確是有一定程度的幫助的!

怎麼說呢?如果 Runner 是用 shell 在跑的話,基本上就要先幫 runner 的系統安裝好必要的工具、函式庫等等東西。如果有好幾台 runner、就需要每一台都各自安裝成一樣的環境;而日後如果有要更新版本、或是新增套件的話,就需要每一台各自去做調整。

假設又碰到多個專案要共用同一組 runner,但是需要的套件卻有衝突的話,那就會非常麻煩了…

雖然透過虛擬機器的確可以解決這個問題,但是虛擬機器的映像檔其實也不小,要更新時也較為麻煩,所以管理上還是沒這麼方便。

這時候,Docker 就可以解決這個問題了!

透過 Docker,可以針對不同的專案建立需要的環境、而不會影響到系統、也不容易碰到版本衝突的問題;同時,Docker 的映像檔可以直接透過 Dockerfile 來產生,相對好控制、也可以進行版本控管。而透過 Docker registry 的機制,也可以讓 gitlab-runner 自己把需要的 docker image 下載下來、建立出需要的容器,更解決了更新和佈署的問題。


研究了一下、有了一點概念之後,Heresy 就投下去玩了。針對 Linux 的 Docker 環境的部分,基本上都還算玩得很順利,目前也已經把這邊的主要專案改成用 Docker 來建置了。

但是到了 Windows 環境的時候…恩,開始碰到一堆問題了。而最後,到了寫這篇文章的時候,這些問題也沒辦法完全解決,變成只能暫時放棄的狀態了。這篇文章,基本上就是一個簡單的問題紀錄了。

Windows Docker 映像檔的肥大

剛開始玩 Linux Docker 的時候,所使用的 base-image 是 ubuntu,檔案大小不到 70MB,安裝完必要的開發環境、函式庫後,容量到達了 3.8GB 左右、Heresy 就覺得已經大的有點誇張了。

而 Windows 呢?Windows 最單純的 base-image 基本上就已經是 5GB+ 的大小(官網、例如 servercore:1903 的大小就要 12.9GB…),感覺根本是把整個基礎 OS 都包起來了?而且在這肥大的映像檔的狀況下,Windows 容器還是有版本相容性的問題(參考),真的讓 Heresy 懷疑他包這麼多檔案的目的到底是什麼? = =
(這可能是因為 Windows 有使用所謂的「Hyper-V 容器」這樣的執行模式階段的關係)

這還是沒有安裝開發環境的狀況。如果要安裝 Build Tools for Visual Studio 來建置 Visual C++ 的專案的話,還需要使用包含 .Net Framework SDK 的基底影像,其容量更是往上跳了不少。

對於比較複雜、需要工具比較多的專案,整個映像檔要弄到能用,感覺可能接近 20GB 是跑不掉的了…(到時候還得改設定才行)
某方面來說,感覺也已經快要可以和 VM 的虛擬硬碟相比了。

難以處理的程式安裝

Windows 雖然有提供 batch(cmd)和 PowerShell 等 CLI(Command-Line Interface)環境,但是基本上還是一套以圖形介面為基礎的作業系統。

但是 Docker 本身並沒有辦法支援圖形介面,所有的操作都需要以 command line 的形式來進行。

這也導致了本來在 Windows 環境下、很方便就可以完成的安裝流程,在要安裝在 Docker 上的時候,有可能會碰到各式各樣的詭異狀況。

先不說別的,光是要能成功地建立出一個包含 Build Tools for Visual Studio 的 docker image 出來,就花了 Heresy 快要一個禮拜的時間…

雖然微軟官方有提供對應的 Dockerfile 的範例(參考參考),但是在 Heresy 這邊卻是怎樣都沒辦法建置出一個可以用的 Docker image。

直接使用官方 vs-2017 的 Dockerfile 來建置的話,不管怎麼測試,Docker 內就是看不到 C:\BuildTools 這個應該要有開發工具的資料夾…更慘的是,這邊的安裝錯誤,連個錯誤訊息都不給…

後來是找到另一個範例(連結),才終於建置出一個可以用的版本(17.8GB)。
而這時,也才大概確定,Build Tools for Visual Studio 2017 大概只能搭配 mcr.microsoft.com/dotnet/framework/sdk:3.5-windowsservercore-1709 這個 base image 使用、而不能用更新版的 base-image。

雖然官方有說 Visual Studio 2017 version 15.8 以前的版本無法安裝於 1809 以後的版本,但是…現在 Visual Stduio 2017 不是已經更新到 15.9 了嗎?而且 Heresy 有試過用 1803 一樣不行啊…

老實說,這部分其實應該不能算是 Windows Docker 的問題,而是很多 Windows 程式的 installer 針對 CLI-only 的環境其實設計得並不好了…

至於其他程式(Qt、CUDA)的安裝?Heresy 還沒測試就先放棄了。

Windows Docker 的隔離模式

Windows Docker 有兩種隔離模式/運作模式(官網),一種是早期的 Hyper-V 模式,實際上就是透過小型的 Hyper-V 虛擬機器來跑。透過這樣的方式,Windows Docker 也可以在一定的程度上支援 Linux 容器,但是相對地會有一些效能、使用資源上的限制。

而從 Windows 1809 後,Windows Docker 就有支援和 Linux 一樣的 process 隔離了;在個人來看,這種模式應該才算是 Docker 最初設計的目的。

但是如果使用這種模式的話…舊版的 Windows base-image(像是現在被 VS2017 綁死的 1709…)基本上不能用,而同時也不能用 Linux 容器了。

GtLab Runner 支援度不佳

其實前面的問題大多都有解,或是可能有辦法解。但是最後讓 Heresy 放棄繼續研究下去的,是 Gitlab-Runner 本身的問題了…

當 Heresy 成功弄出第一個映像檔後,真的要掛到 GitLab-Runner 上跑的時候,就馬上碰到

ERROR: Preparation failed: could not determine windows version

的錯誤。

而查了一下,這個問題是因為 GitLab Runner 在使用 Docker 來執行的時候,針對 Windows 的支援,是針對不同版本寫死的(參考);而目前又只支援 1803 和 1809 兩個版本,所以在 Heresy 這邊主要的機器都已經升級到 1903 的情況下,就變得完全不能用了…

雖然官方看來是已經知道有這個問題了,不過看來要支援似乎還沒那麼快。而如果官方沒有想要設計一個可以跨版本的方法,那之後應該每次微軟推出大升級,都繪出一次問題了…

檔案存取的問題?

另外,之前在使用建置出來的容器想要用 bind mount(官方文件)的形式把專案拿到容器裡面建置,結果看起來似乎是以 symbolic link(SYMLINKD)的形式存在的,而在平行編譯的時候,會出現檔案存取的錯誤:

fatal error C1041: cannot open program database 'xxx.pdb'; if multiple CL.EXE write to the same .PDB file, please use /FS

這個問題除了去改專案設定還有沒有其他解法呢?恩,還不知道。


上面就是目前碰到的問題了。

而目前想到可能還可以玩的方法:

  • 不要使用 Build Tools for Visual Studio 2017、而改用 2019 版,然後安裝 2017 的 toolset,看看能不能用新版的 base-image。

  • GitLab 的部分,繼續使用 shell 當作 executor、手動去執行 docker 的指令。
    元件參考

之後…總之就再看看吧。

對「GitLab CI + Windows Docker 的一些紀錄」的想法

  1. […] Heresy 從去年年初開始玩 Gitlab CI/CD 一段時間後,就開始試著把建置環境移動到 Docker(官網)上。在 Linux 上的問題不算大,但是在 Windows 上卻常常碰到一些問題(參考一、參考二);前一陣子,甚至因為微軟自己的安全性更新,搞到整個不能用(參考)… […]

  2. Hi Heresy:

    我用 windows:1809 + vs_buildtools: vs2015/vs2017 + gitlab-runner-helper 是可以跑起來的 給你參考


    # escape=`
    # —- Base Node —-
    ###
    # reference:
    # – https://devblogs.microsoft.com/cppblog/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
    # – https://docs.microsoft.com/en-us/visualstudio/install/build-tools-container?view=vs-2017
    # – https://docs.microsoft.com/en-us/visualstudio/install/advanced-build-tools-container?view=vs-2017
    # – https://docs.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2017
    # – https://docs.microsoft.com/en-us/visualstudio/install/create-a-network-installation-of-visual-studio?view=vs-2017
    # – https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/version-compatibility
    # – https://software.intel.com/en-us/articles/build-dldt-app-inside-windows-docker-container
    # – https://forums.docker.com/t/connect-to-the-internet-from-a-windows-container-behind-a-http-proxy/41307/8
    # – https://stackoverflow.com/questions/43933425/best-way-to-extract-list-of-visual-studio-2017-installed-components
    #
    # Multistages: docker build -m 4GB -t vs15 –target=base .
    #
    # there is problem that docker images cannot connect external network via WiFi. use wired network.
    # but it should work in the corp network with proper proxy settings.
    #
    ### download vs2017 offline packages
    # wget https://aka.ms/vs/15/release/vs_buildtools.exe
    # vs_BuildTools.exe –layout vs2017 –add Microsoft.VisualStudio.Workload.VCTools –add Microsoft.VisualStudio.Component.VC.140 –add Microsoft.VisualStudio.Component.Windows10SDK.17763 –add Microsoft.VisualStudio.Component.Windows10SDK.17134 –includeRecommended
    #
    ##
    FROM mcr.microsoft.com/windows:1809-amd64 AS base
    #FROM mcr.microsoft.com/dotnet/framework/sdk:4.7.2-windowsservercore-1803 AS base
    RUN powershell Set-ExecutionPolicy Bypass
    SHELL ["cmd", "/S", "/C"]
    # Install Chocolatey
    RUN powershell -Command `
    iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
    RUN choco feature disable –name showDownloadProgress
    # gnuwin32 (ls, rm, sort, and friends)
    RUN choco install -y gnuwin32-coreutils.install
    # Add gnuwin32 to PATH
    RUN powershell $newpath = $env:path + "';C:\Program Files (x86)\GnuWin32\bin;'" ;`
    Set-ItemProperty -Path `
    'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session `
    Manager\Environment' -Name PATH -Value $newpath
    # other tools by choco
    RUN choco install -y diffutils 7zip.install zip unzip which sed wget curl git.install tortoisesvn python2 openjdk nasm jq jfrog-cli vim
    RUN choco install -y python –version 3.6.6
    # pipeline-trigger for gitlab
    RUN git clone https://gitlab.com/finestructure/pipeline-trigger.git
    RUN pip3 install -r pipeline-trigger\requirements.txt
    RUN cp pipeline-trigger\trigger.py C:\Python36\Scripts\
    # other tools by pip
    RUN pip3 install anybadge pyyaml enum34
    RUN refreshenv
    # vs2017
    FROM base AS vs2017
    WORKDIR C:\temp
    COPY vs2017 C:\temp\
    RUN vs_buildtools.exe –quiet –wait –norestart `
    –add Microsoft.VisualStudio.Workload.VCTools `
    –add Microsoft.VisualStudio.Component.VC.140 `
    –add Microsoft.VisualStudio.Component.Windows10SDK.17763 `
    –add Microsoft.VisualStudio.Component.Windows10SDK.17134 `
    –includeRecommended `
    || IF "%ERRORLEVEL%"=="3010" EXIT 0
    RUN powershell [System.Environment]::SetEnvironmentVariable('VS120COMNTOOLS', 'C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\', 'Machine')
    RUN powershell [System.Environment]::SetEnvironmentVariable('VS140COMNTOOLS', 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\', 'Machine')
    RUN refreshenv
    # Clean up
    RUN rmdir /q /s C:\temp


    # https://notes.alelec.net/blog/post/andrew@alelec.net/Windows-Docker-Gitlab-CI-Runner-2
    #
    # Get registration token from https://gitlab.com/<project settings>/runners
    #
    # multiple roles are in single Windows host.
    # 1. docker images update
    # 2. docker run
    #
    # 0. prepare
    # official now has windows docker support
    $url = "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe&quot;
    Start-BitsTransfer -Source $url -Destination c:\windows\system32\gitlab-runner.exe
    mkdir c:\gitlab
    # 1. docker images update
    $env:REGISTRATION_TOKEN=""
    $env:CI_SERVER_URL="https://gitlab.com&quot;
    $env:RUNNER_NAME="docker-build"
    $env:CONFIG_FILE="C:\gitlab\config.toml"
    $env:REGISTER_LOCKED="false"
    $env:RUNNER_EXECUTOR="shell"
    $env:RUNNER_TAG_LIST="windows"
    $env:RUNNER_SHELL="powershell"
    $env:RUNNER_BUILDS_DIR="c:\gitlab\builds"
    #$env:RUNNER_CACHE_DIR="c:\gitlab\cache"
    $env:REGISTER_RUN_UNTAGGED="true"
    gitlab-runner register –non-interactive
    # 2. docker run
    $env:REGISTRATION_TOKEN=""
    $env:RUNNER_NAME="docker-host1"
    $env:RUNNER_EXECUTOR="docker-windows"
    $env:RUNNER_TAG_LIST="docker,windows,1809"
    $env:DOCKER_MEMORY="4g"
    $env:DOCKER_TLS_VERIFY="false"
    #$env:DOCKER_PRIVILEGED="true"
    $env:DOCKER_IMAGE="vs2017"
    #$env:DOCKER_VOLUMES="\\.\pipe\docker_engine"
    #$env:DOCKER_CACHE_DIR="c:\gitlab\cache"
    $env:DOCKER_PULL_POLICY="if-not-present"
    #$env:DOCKER_HELPER_IMAGE="registry.gitlab.com/andrewleech/gitlab-runner/gitlab-runner-helper:win-x86_64-1809-latest"
    # TODO Switch back to npipe once it works in gitlab (see note above)
    #$ip=(Get-NetAdapter -Name "*Internal vSwitch*" | Get-NetIPAddress -AddressFamily IPv4).IPAddress
    #$env:DOCKER_HOST="tcp://${ip}:2375"
    #$env:RUNNER_ENV="DOCKER_HOST=tcp://${ip}:2375"
    gitlab-runner register –non-interactive
    # You may want to increase the number of concurrent jobs, if so:
    #notepad c:\gitlab\config.toml
    # Then add/edit the following line to the top of the file (withouth the #)
    # concurrent = 4
    # Save and close
    # Install the service
    #gitlab-runner install -user .\LocalSystem –working-directory="C:\gitlab" –config=$env:CONFIG_FILE
    #cmd /c "sc.exe config gitlab-runner start= delayed-auto"
    # edit config.toml, add session_server listen address, then open it in firewall:
    #New-NetFirewallRule -DisplayName "Allow inbound TCP port 8093 (gitlab runner session server)" -Direction inbound -LocalPort 8093 -Protocol TCP -Action Allow

    • 感謝。不過目前應該還是不能在 1903 的系統跑,所以已經放棄了。
      (更何況系統都更新到 1909 了)

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料