全文共4955字,預計學習時長25分鐘
圖源:unsplash
無論你的專案是用於開發Web應用,處理資料科學問題還是AI,使用配置良好的CI / CD,可在開發中除錯且針對生產環境進行了優化的Docker映象,或一些其它的程式碼品質工具,都能讓你受益。
本文將告訴你該如何把它們加入Python專案中!
這是我的倉庫,其中包含完整的原始碼和文件:https://github.com/MartinHeinz/python-project-blueprint
用於開發的可除錯Docker容器有些人不喜歡Docker,因為容器可能很難除錯,或者因為它們的映象需要很長時間才能構建。因此,讓我們從構建用於開發的理想映象開始,它能夠快速構建且易於除錯。
為了使映象易於除錯,需要基礎映象,其中包括除錯時可能需要的所有工具,例如bash,vim,netcat,wget,cat,find,grep等。
python:3.8.1-buster似乎是這一任務的理想選擇。它在預設情況下包含許多工具,我們可以很容易地安裝所有缺少的東西。這個基本映象非常厚重,但這並不重要,因為此時它將僅用於開發。
你可能已經注意到,我選擇了非常具體的映象:鎖定了Python版本和Debian版本。這是故意的,因為我們希望最大程度地減少由更新的,可能不相容的Python或Debian版本引起“損壞”的可能性。
圖源:techcrunch
可以使用基於Alpine的映象作為替代。但是,這可能會引起一些問題,因為它使用musllibc而不是Python依賴的glibc。因此,如果決定選擇此配置,請記住這一點。
至於構建的速度,我們將利用多階段構建以便快取儘可能多的層。這樣,就可以避免下載例如gcc的依賴項和工具以及(requirements.txt中的)應用程式所需的所有庫。
因為無法將下載和安裝這些工具所需的步驟快取到最終的執行程式映象中,我們將使用前面提到的python:3.8.1-buster建立自定義基本映象,該映象將包含需要的所有工具,從而進一步提升處理速度。
說了這麼多,來看看Dockerfile:
# dev.Dockerfile FROMpython:3.8.1-buster AS builder RUN apt-get update&& apt-get install -y --no-install-recommends --yes python3-venv gcclibpython3-dev && \\ python3 -m venv /venv && \\ /venv/bin/pip install --upgradepip FROM builder ASbuilder-venv COPYrequirements.txt /requirements.txt RUN /venv/bin/pipinstall -r /requirements.txt FROM builder-venv AStester COPY . /app WORKDIR /app RUN/venv/bin/pytest FROMmartinheinz/python-3.8.1-buster-tools:latest AS runner COPY --from=tester/venv /venv COPY --from=tester/app /app WORKDIR /app ENTRYPOINT ["/venv/bin/python3", "-m", "blueprint"] USER 1001 LABEL name={NAME} LABELversion={VERSION}
從上面的文件可以看到我們將建立3箇中間映象,然後建立最終的執行映象。第一個映象被稱為builder,它會下載構建最終應用程式所需的所有必需庫,其中包括gcc和Python虛擬環境。安裝完成後,它還會建立實際的虛擬環境以供下一個映象使用。
接著是builder-venv映象,該映象將依賴項列表(requirements.txt)複製到映象中,然後進行安裝。快取需要此中間映象,因為僅在requirements.txt更改時才會安裝庫,否則僅使用快取。
在建立最終映象之前,首先要針對應用程式執行測試。這就是tester映象做的工作。我們將原始碼複製到映象中並執行測試。如果通過了,程式就會執行至runner。
對於runner映象,我們使用的是自定義映象,其中包括普通Debian映象中不存在的一些額外功能,例如vim或netcat。你可以在Docker Hub上的這裡找到此映象,還可以通過這裡在base.Dockerfile中檢驗這個非常簡單的Dockerfile。
因此,在最終映象中的工作有這些:首先複製虛擬環境,該環境保留了tester映象中所有已安裝的依賴項,接下來複制經過測試的應用程式。
現在,映象已經擁有了所有源,移至應用程式所在的目錄設定ENTRYPOINT,以便在映象啟動時執行應用程式。出於安全原因將USER設定為1001,因為最佳實踐告訴我們,永遠不要在root使用者下執行容器。
最後2行設定映象的標籤。當使用make 命令指向構建並執行時,這些將被替換或填充,這一點稍後我們會看到。
圖源:unsplash
為產品優化的Docker容器談及產品級映象時,我們想確保它們小巧,安全且快速。我個人最喜歡的是Distroless專案中的Python映象。那麼什麼是Distroless?
可以這樣形容它:在理想的世界中,每個人都將使用FROM scratch作為其基本映象(即空映象)來構建其映象。
但這不是大多數人想要做的,因為它要求靜態連線二進位制檔案等。這就是Distroless發揮作用的地方了,它是為每個人設計的FROM scratch。
Distroless是由Google製作的一組映象,包含應用所需的最低要求,這意味著沒有殼(shell),程式包管理器或任何其他工具會使映象膨脹並給安全掃描器(例如CVE)造成訊號噪聲,從而使其變得更難建立規則。
知道了要解決的問題,讓我們看一下生產型Dockerfile ...... 實際上,在這裡不需要做太多更改,只有兩行:
# prod.Dockerfile # 1. Line - Change builder image FROMdebian:buster-slim AS builder # ... # 17. Line - Switch to Distroless image FROMgcr.io/distroless/python3-debian10 AS runner # ... Rest of the Dockefile需要更改的只是用於構建和執行應用程式的基本映象!
但是差別是巨大的:我們的開發映象為1.03GB,而這個映象僅為103MB,這是完全不一樣的!
我知道你會說“但是Alpine可以變得更小”是的,沒錯,但是大小的差距並不那麼重要。你只會在下載/上傳映象時注意映象的大小,這種情況並不常見。當映象執行時,大小完全不重要。比大小更重要的是安全性,就這一點而言,Distroless肯定具有優勢,因為Alpine(這是一個很好的替代)具有許多額外的程式包,可以增加攻擊面。
圖源:unsplash
關於Distroless值得一提的最後一件事是除錯映象。考慮到Distroless不包含任何殼(甚至不包括sh),這使得需要除錯和檢查時非常棘手。為此,所有Distroless映象都有除錯版本。
因此,當遇到麻煩時,可以使用debug標籤構建生產映象,並將其部署到常規映象旁邊,在其中執行並且進行如執行緒轉儲的操作。可以像這樣使用python3映象的除錯版本:
docker run --entrypoint=sh -tigcr.io/distroless/python3-debian10:debug適用一切情況的單一命令在準備好所有Dockerfile後,不妨使用Makefile將其自動化吧!要做的第一件事是使用Docker構建應用程式。因此,為了構建開發映象,我們可以執行make build-dev命令來執行以下目標檔案:
# The binary to build (just the basename). MODULE := blueprint # Where to push the docker image. REGISTRY ?=docker.pkg.github.com/martinheinz/python-project-blueprint IMAGE := $(REGISTRY)/$(MODULE) # This version-strategy uses git tagsto set the version string TAG := $(shell git describe --tags--always --dirty) build-dev: @echo "\\n${BLUE}BuildingDevelopment image with labels:\\n" @echo "name: $(MODULE)" @echo "version: $(TAG)${NC}\\n" @sed \\ -e's|{NAME}|$(MODULE)|g' \\ -e 's|{VERSION}|$(TAG)|g' \\ dev.Dockerfile | docker build -t $(IMAGE):$(TAG) -f- .該目標檔案首先通過在dev.Dockerfile的底部用標籤替換映象的名稱和標籤來構建映象,該標籤是通過執行git describe然後執行docker build來建立的。
下一步——使用make build-prod VERSION = 1.0.0構建生產版本:
build-prod: @echo "\\n${BLUE}Building Productionimage with labels:\\n" @echo "name: $(MODULE)" @echo "version: $(VERSION)${NC}\\n" @sed \\ -e's|{NAME}|$(MODULE)|g' \\ -e 's|{VERSION}|$(VERSION)|g' \\ prod.Dockerfile | docker build -t $(IMAGE):$(VERSION) -f- ..這個與先前的目標檔案非常相似,但是在1.0.0版本上的示例中,我們將把版本作為引數傳遞,而不是使用git標籤作為版本。
當在Docker中執行所有內容時,有時需要在Docker中對其進行除錯,為此,有以下目標檔案:
# Example: make shell CMD="-c 'date> datefile'" shell: build-dev @echo "\\n${BLUE}Launching a shellin the containerized build environment...${NC}\\n" @docker run \\ -ti \\ --rm \\ --entrypoint /bin/bash \\ -u $$(id -u):$$(id -g) \\ $(IMAGE):$(TAG) \\ $(CMD)從上面可以看出,bash覆蓋了入口點,而引數則覆蓋了容器命令。這樣,我們可以像上面的示例那樣直接進入容器並進行除錯或執行一個關閉命令。
當完成編碼並想將映象推送到Docker登錄檔時,可以使用makepush VERSION = 0.0.2。來看看目標檔案的功能:
REGISTRY ?=docker.pkg.github.com/martinheinz/python-project-blueprint push:build-prod @echo"\\n${BLUE}Pushing image to GitHub Docker Registry...${NC}\\n" @dockerpush $(IMAGE):$(VERSION)它首先執行之前看過的build-prod檔案,然後執行docker push。這裡假設已登入Docker登錄檔,因此在執行此登錄檔之前,需要執行docker login。
docker-clean: @docker system prune -f --filter "label=name=$(MODULE)"使用GitHub Actions的CI / CD現在開始使用所有這些方便的make目標命令來設定CI / CD。我們將使用GitHub Actions和GitHub Package Registry來構建管道(工作)並存儲映象。那麼這兩個東西到底是什麼呢?
· Github Actions是可以幫助自動化開發工作流程的作業/管道。可以使用它們來建立單個任務,然後將它們組合到自定義的工作流程中,然後在諸如每次推送到倉庫或建立發行版的時候執行這些工作流程。
· GitHub Package Registry是與GitHub完全整合的軟體包託管服務。它可以儲存各種型別的軟體包,例如:Ruby gems或npm軟體包。我們將使用它來儲存Docker映象。
· 如果你不熟悉GitHub Package Registry,並且想要了解更多相關資訊,可以檢視我的部落格文章:https://martinheinz.dev/blog/6
圖源:unsplash
現在,為了使用GitHub Action,需要建立工作流,這些工作流將根據選擇的觸發器(例如,推送到倉庫)執行。這些工作流是YAML檔案,位於倉庫中的.github / workflows目錄中:
.github └── workflows ├── build-test.yml └── push.yml第一項工作名為build,它通過執行make build-dev命令來驗證是否可以構建應用程式。但在執行它之前,它首先通過執行在GitHub上釋出的名為checkout的操作來檢索倉庫。
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions/setup-python@v1 with: python-version: '3.8' - name: Install Dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - name: Run Makefile test run: make test - name: Install Linters run: | pip install pylint pip install flake8 pip install bandit - name: Run Linters run: make lint第二項工作稍微複雜一些。它針對應用程式以及3個linter(程式碼品質檢查器)執行測試。
與上一項工作相同,我們使用checkout@v1操作獲取原始碼。之後,執行另一個名為setup-python@v1的已釋出操作,該操作幫助設定了python環境(你可以在此處找到有關它的詳細資訊)。
現在有了python環境,還需要使用pip安裝的requirements.txt中的應用程式依賴項。此時可以繼續執行make test命令,這將觸發Pytest套件。如果測試套件通過,將繼續安裝前面提到的linters,即pylint,flake8和bandit。最後執行make lint命令,這將觸發每個linter。
這就是構建/測試工作的全部流程,但是應該如何推送呢?來看一下:
前4行定義了何時觸發這項工作。我們指定僅當將標籤推送到倉庫時才開始該工作(*在這種情況下指定標籤名稱可以是任何模式),因此不會在每次推送到倉庫時都將Docker映象推送到GitHub Package Registry,而僅在推送指定應用程式新版本的標籤時才推送到GitHubPackage Registry。
現在到了工作的主體,它是從檢索原始碼並將RELEASE_VERSION的環境變數設定為推送的git標籤開始的。這是通過GitHub Actions的內建:: setenv功能完成的。
接下來,它使用儲存在repository secrets中的REGISTRY_TOKEN登入到Docker登錄檔,並登入啟動工作流的使用者(github.actor)。最後,在最後一行中,它執行push命令,該命令構建生產映象並將其推送到登錄檔,並以先前推送的git標籤作為映象標籤。
此處可以檢索出完整的程式碼清單:https://github.com/MartinHeinz/python-project-blueprint/tree/master/.github/workflows
使用CodeClimate進行程式碼品質檢查最後但並非最不重要的一點是,還要使用CodeClimate和SonarCloud來新增程式碼品質檢查。這些將與上面展示的test工作一起觸發。因此,我們在其中新增幾行:
# test, lint... - name: Send report toCodeClimate run: | export GIT_BRANCH="${GITHUB_REF/refs\\/heads\\//}" curl -Lhttps://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64> ./cc-test-reporter chmod +x ./cc-test-reporter ./cc-test-reporter format-coverage -t coverage.py coverage.xml ./cc-test-reporter upload-coverage -r "${{secrets.CC_TEST_REPORTER_ID }}" - name: SonarCloudscanner uses: sonarsource/sonarcloud-github-action@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}從CodeClimate開始,首先匯出GIT_BRANCH變數,然後使用GITHUB_REF環境變數進行檢索。其次,下載CodeClimate測試報告程式並使其可執行。然後,使用它來格式化由測試套件生成的覆蓋率報告,並在最後一行將其傳送到帶有測試報告器ID的CodeClimate中,該ID儲存在repository secrets中。
至於SonarCloud,我們需要在倉庫中建立如下所示的sonar-project.properties檔案(檔案中的值可以在SonarCloud儀表板的右下角找到):
.organization=martinheinz-github sonar.projectKey=MartinHeinz_python-project-blueprint sonar.sources=blueprint除此之外,只需使用已有的sonarcloud-github-action就可以幫我們完成所有工作。要做的就是提供2個令牌:GitHub令牌(預設情況下位於倉庫中)和SonarCloud令牌(可從SonarCloud網站獲得)。
注意:有關如何獲取和設定所有上述令牌和金鑰的步驟,請參見README檔案:https://github.com/MartinHeinz/python-project-blueprint/blob/master/README.md
圖源:unsplash
有了上述的工具、配置和程式碼,你就可以構建和自動化下一個Python專案的方各個方面了。快去試試吧!
我們一起分享AI學習與發展的乾貨