MySQL是如何做容器測試的?
傳統的基礎設施管理是一項手動任務,由系統管理員管理靜態伺服器。現代雲平臺的自動化能力改變了這種工作方式:基礎設施通常被描述為“程式碼”,基礎設施管理系統會對基礎設施自動做出變更。因此,基礎設施的變得更加動態,週轉時間也要短得多。
基礎設施測試框架通常被用於驗證機器映象的狀態(Amazon Machine Images、Google Compute Images或Oracle OCI Images)。隨著容器基礎設施的出現,容器基礎設施的測試變得與機器映象的測試一樣重要。
在SQL/">MySQL,我們有很多基礎設施,我們越來越多地使用容器來代替真實(虛擬)機器。此外,越來越多的核心基礎設施執行在Oracle的雲基礎設施(OCI)上。這要求我們實現多個級別的自動化,並且可以利用基礎設施測試來驗證我們的伺服器(或虛擬機器、容器)的狀態。基礎設施測試還用於驗證我們釋出的一些工件的狀態。
在這篇博文中,我們將重點介紹如何使用自動化基礎設施測試來驗證MySQL Server Docker映象。我們將比較三個可用於進行容器測試的框架,並給出示例程式碼。
自動化基礎設施測試
基礎設施測試用於測試基礎設施的狀態:Apache伺服器是否在監聽80埠?是否正確配置了DNS伺服器,這些設定是否正確反映在resolv.conf檔案中?要安裝的二進位制檔案是否都已經存在於機器映象中?
這類測試可以作為bash指令碼的一部分,因此通常被用於配置任務,或者在(手動)建立例項後進行手動驗證。自動化基礎設施測試更進一步,它假設需要對很多基礎設施和指令碼的正確性做出驗證,以及動態的現代雲環境有很多東西需要通過手動的方式處理。
基礎設施測試工具通常與Ansible、Puppet或Chef等配置工具結合使用。配置工具在機器上安裝軟體,測試框架則確保它們能夠正常執行。然後,任何東西都可以通過程式碼來表示,並使用工具進行自動化。
我們的重點是測試Docker映象,對我們來說,這些工作有點偏向底層。由於我們所有的Docker映象主要是由經過測試併發布的yum軟體包層組成,這些層位於非常可靠的OS層之上,我們主要想要驗證軟體包的版本是否正確,以及這些二進位制檔案的功能是否正常。在映象構建期間,可能會發生網路故障,出現軟體包安裝不完整的情況,我們想要通過自動化測試來捕捉它們。
在評估測試工具時,需要考慮到以下兩個方面的問題:
- 配置語言,即想要測試的內容(可用包、必要的檔案等)
- 測試執行,即如何執行測試(local/ssh/container)
對於以下的工具,我們將關注這兩個方面的問題。這個領域最常見的工具包括:
- InSpec/Serverspec
- Goss
- Container Structure Test
接下來,我們將逐個簡要介紹它們。
InSpec
InSpec基於RSpec(Ruby)測試框架,並借鑑了Serverspec(也是基於RSpec構建,並被廣泛採用)的經驗。它是Chef生態系統的一部分,用於配置和測試基礎設施。它的配置儲存在一個ruby檔案中。
可以通過resources指定多種配置語言
通過targets(local/ssh/docker)來測試執行
Goss
Goss是Serverspec的一個快速而簡單的替代品,是使用Go語言開發的一個伺服器測試和驗證框架。它的配置儲存在一個yaml檔案中,這個檔案可以很方便地從當前系統狀態生成。
支援多種配置語言
支援在本地和Docker容器中執行測試(通過dgoss指令碼)
Container Structure Test
Container Structure Test是一個用於驗證容器映象結構的框架。與Goss一樣,它也是用Go語言編寫,並使用了yaml配置檔案。該專案於今年早些時候釋出,它的應用範圍相對較窄(只支援容器),但它提供了足夠的功能來測試映象。
- 支援的配置語言較少
- 測試執行僅限於本地容器
示例:MySQL Server Images
接下來,我們將演示如何安裝所需工具,解釋各個配置檔案,並在本地執行測試。我們針對最新的MySQL Server容器(latest或8.0標籤)執行測試。為了方便起見,我們跳過構建步驟,從公共登錄檔下載容器並在本地執行測試。在我們的構建管道中,我們首先構建容器,執行測試,在執行成功之後才會推送到公共登錄檔。可以通過輸入以下命令來獲取最新版本的mysql-server映象:
docker pull mysql/mysql-server
總的來說,我們想測試兩個東西:
- 容器是否存在主機上,幷包含正確的元資料
- 容器是否包含所有的包和二進位制檔案
先決條件
除了可用的Docker環境之外,執行該示例還需要在本地安裝InSpec、Goss和Container Structure Test。
InSpec的說明可以在這裡找到:ofollow,noindex" target="_blank">https://downloads.chef.io/inspec 。在Linux平臺上,可以通過執行以下命令安裝Goss和Container Structure Test二進位制檔案:
curl -L https://github.com/aelsabbahy/goss/releases/download/v0.3.6/goss-linux-amd64 -o goss && chmod +x goss curl -L https://raw.githubusercontent.com/aelsabbahy/goss/master/extras/dgoss/dgoss -o dgoss && chmod +x dgoss curl -L https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 -o container-structure-test && chmod +x container-structure-test
安裝好所有二進位制檔案並將它們新增到系統路徑中,然後就可以通過shell指令碼執行測試。
測試配置
為了比較配置和測試執行過程的不同之處,我們提供了用於測試這三個框架的MySQL Server Docker映象的示例檔案:https://github.com/neumayer/mysql-server-image-tests 。
可以通過以下命令來克隆它:
git clone https://github.com/neumayer/mysql-server-image-tests.git
儲存庫中包含的配置檔案:
- mysql-server-inspec.rb
- goss.yaml
- mysql-server-container-structure-test.yml
讓我們來依次檢視這些檔案,先從InSpec配置檔案開始:
control 'container' do impact 0.5 describe docker_container('mysql-server') do it { should exist } it { should be_running } its('repo') { should eq 'mysql/mysql-server' } its('ports') { should eq '3306/tcp, 33060/tcp' } its('command') { should match '/entrypoint.sh mysqld' } end end control 'server-package' do impact 0.5 describe package('mysql-community-server-minimal') do it { should be_installed } its ('version') { should match '8.0.12.*' } end end control 'shell-package' do impact 0.5 describe package('mysql-shell') do it { should be_installed } its ('version') { should match '8.0.12.*' } end end
InSpec通過profile和control來組織測試用例,其中control是較小的單元,是與給定主題相關的一組測試。第一個control叫“container”,針對宿主機器執行,與執行在localhost上的Docker守護程序通訊,驗證容器是否正在執行。另外兩個control檢查容器內的包。這種區別很重要,因為後兩個control可以針對localhost、ssh主機或Docker容器執行。在我們的例子中,我們讓它們針對容器執行,這樣可以帶來非常好的可重用性和靈活性。雖然我們在示例中只使用了Docker和包資源,但實際上control可以使用任何現有的InSpec資源。
下面是執行流程:
- 啟動容器
- 針對localhost執行 “container” control
- 針對容器執行剩餘的control
指令碼看起來是這樣的:
docker run -d --name mysql-server mysql/mysql-server inspec exec mysql-server-inspec.rb --controls container inspec exec mysql-server-inspec.rb -t docker://mysql-server --controls server-package
如果執行成功,InSpec將輸出以下內容:
Profile: tests from mysql-server-inspec.rb (tests from mysql-server-inspec.rb) Version: (not specified) Target:local:// ✔container: Docker Container mysql-server ✔Docker Container mysql-server should exist ✔Docker Container mysql-server should be running ✔Docker Container mysql-server repo should eq "mysql/mysql-server" ✔Docker Container mysql-server ports should eq "3306/tcp, 33060/tcp" ✔Docker Container mysql-server command should match "/entrypoint.sh mysqld" Profile Summary: 1 successful control, 0 control failures, 0 controls skipped Test Summary: 5 successful, 0 failures, 0 skipped Profile: tests from mysql-server-inspec.rb (tests from mysql-server-inspec.rb) Version: (not specified) Target:docker://d06da2588b80a4ee9b839b55c2f719ab9e860904eeb831b71488704f50f8b994 ✔server-package: System Package mysql-community-server-minimal ✔System Package mysql-community-server-minimal should be installed ✔System Package mysql-community-server-minimal version should match "8.0.12.*" Profile Summary: 1 successful control, 0 control failures, 0 controls skipped Test Summary: 2 successful, 0 failures, 0 skipped
Goss的配置檔案如下所示:
file: /usr/sbin/mysqld: exists: true contains: [] package: mysql-community-server-minimal: installed: true mysql-shell: installed: true port: tcp6:3306: listening: true ip: [] tcp6:33060: listening: true ip: [] user: mysql: exists: true process: mysqld: running: true
除了mysqld檔案,我們還要檢查所需的軟體包是否已安裝、公開埠是否正確以及所需的程序是否在執行。Goss將為我們啟動容器:
GOSS_SLEEP=10 dgoss run -p 3306:3306 mysql/mysql-server
因為設定了GOSS_SLEEP,所以我們的伺服器有足夠時間完成初始化,其餘引數被傳給docker run。輸出如下:
INFO: Starting docker container INFO: Container ID: 75bc8869 INFO: Sleeping for 10 INFO: Running Tests File: /usr/sbin/mysqld: exists: matches expectation: [true] User: mysql: exists: matches expectation: [true] Process: mysqld: running: matches expectation: [true] Port: tcp6:33060: listening: matches expectation: [true] Port: tcp6:33060: ip: matches expectation: [[]] Port: tcp6:3306: listening: matches expectation: [true] Port: tcp6:3306: ip: matches expectation: [[]] Package: mysql-shell: installed: matches expectation: [true] Package: mysql-community-server-minimal: installed: matches expectation: [true] Total Duration: 0.038s Count: 9, Failed: 0, Skipped: 0 INFO: Deleting container
Container Structure Test的yaml配置片段如下:
schemaVersion: "2.0.0" metadataTest: exposedPorts: [ "3306", "33060" ] entrypoint: [ "/entrypoint.sh" ] cmd: [ "mysqld" ] volumes: [ "/var/lib/mysql" ] commandTests: - name: "mysqlsh" command: "mysqld" args: - "--version" expectedOutput: - "8.0.12" - name: "mysqlsh" command: "mysqlsh" args: - "--version" expectedOutput: - "8.0.12" fileExistenceTests: - name: "mysqld" path: "/usr/sbin/mysqld"
此外,我們還要檢查公開的埠是否正確,然後直接執行二進位制檔案(而不是像其他工具那樣使用內部包裝器)以驗證它們是否已就位。
container-structure-test --image mysql/mysql-server test --config mysql-server-container-structure-test.yml
與Goss類似,呼叫起來很簡單,只需要提供映象名稱和配置檔案。
================================================================== ====== Test file: mysql-server-container-structure-test.yml ====== ================================================================== INFO: stdout: /usr/sbin/mysqldVer 8.0.12 for Linux on x86_64 (MySQL Community Server - GPL) === RUN: Command Test: mysqlsh --- PASS stdout: /usr/sbin/mysqldVer 8.0.12 for Linux on x86_64 (MySQL Community Server - GPL) INFO: stdout: mysqlshVer 8.0.12 for Linux on x86_64 - for MySQL 8.0.12 (MySQL Community Server (GPL)) === RUN: Command Test: mysqlsh --- PASS stdout: mysqlshVer 8.0.12 for Linux on x86_64 - for MySQL 8.0.12 (MySQL Community Server (GPL)) INFO: File Existence Test: mysqld === RUN: File Existence Test: mysqld --- PASS === RUN: Metadata Test --- PASS =================================================================== ============================= RESULTS ============================= =================================================================== Passes:4 Failures:0 Total tests: 4 PASS
Container Structure Test執行速度快,易於呼叫,只不過只能用於容器。在大多數情況下,它可以確保容器的行為是正確的。
可以在儲存庫的根目錄執行以下指令碼來執行上述的測試:
- ./inspec.sh
- ./goss.sh
- ./container-structure-test.sh
MySQL的容器測試
我們已經開始使用InSpec測試所有的Docker映象。從下一個MySQL版本(8.0.13)開始,基本的InSpec測試將成為MySQL Server、MySQL Cluster和MySQL Router Docker映象自動釋出流程的一部分。我們主要基於以下幾點做出這樣的決定:
- 它擁有最大的作用域(ssh/local/docker),可進一步在內部使用;
- 廣泛的資源;
- 依賴機制;
- 由於它與Chef生態系統的關係以及與Serverspec的相似性而被廣泛採用。
存在的不足:
- 因為是ruby呼叫,執行速度感覺比Go語言的替代品慢(但我們認為這不是關鍵問題);
- InSpec更通用,但需要做出權衡,用更多的指令碼來執行測試。
我們將InSpec作為自動釋出管道的一部分,如果測試失敗,將不會生成任何工件。我們的QA流程包含很多其他步驟,例如之前對MySQL Docker映象中的rpm包進行的單獨測試。