如何Docker化任意一个应用
網上有很多關于如何將應用Docker化的教程,為什么我還要再寫一個呢?
\\我見過的大部分教程都是限定在某種特定技術(例如Java或者Python),可能無法滿足讀者的需求。同時,這些教程也沒有說清楚關于Dev和Ops團隊之間建立明確約定所涉及到的所有相關方面(這正是容器化的精髓所在)。
\\我根據最近的經驗總結了以下一些步驟。它是一份細節清單,包含了其他指南中忽略的內容。
\\聲明:這不是一份新手指南。我建議讀者先掌握一些如何設置和使用docker的基礎知識,并且創建和運行一些容器之后,再來閱讀。
\\讓我們開始吧。
\\一、選擇基礎鏡像
\\每種對應技術幾乎都有自己的基礎鏡像,例如:
\\- https://hub.docker.com/_/java/\\t
- https://hub.docker.com/_/python/\\t
- https://hub.docker.com/_/nginx/\
如果不能直接使用這些鏡像,我們就需要從基礎操作系統鏡像開始安裝所有的依賴。
\\外面有很多教程使用的都是Ubuntu(例如 ubuntu:16.04)作為基礎鏡像,這不能算有問題,但是我建議優先考慮Alpine鏡像:
\\https://hub.docker.com/_/alpine/
\\它是一個非常小的基礎鏡像(大約只有5MB)。
\\注意:在基于Alpine的鏡像中無法使用“apt-get”命令,Alpine系統有自己的軟件包倉庫和包管理工具。詳細請參考:
\\https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management
\\https://pkgs.alpinelinux.org/packages
\\二、安裝必要軟件包
\\這個步驟通常比較瑣碎,有一些容易忽略的細節:
\\- apt-get update和apt-get install命令應該寫在一行(如果使用Alpine則對應的是apk命令)。這不是常見的做法,但是在Dockerfile中應該要這么做,否則“apt-get update”命令產出的臨時層可能會被緩存,導致構建時沒有更新包信息(參見 https://forums.docker.com/t/dockerfile-run-apt-get-install-all-packages-at-once-or-one-by-one/17191 這個討論)。\\t
- 確認是否只安裝了實際需要的軟件(尤其是這個容器會在生產環境中運行)。我看見過有人在他們的鏡像中安裝了vim和其他開發工具。\
如果有必要,針對構建、調試和開發環境創建不同的Dockerfile。這不僅僅關系到鏡像大小,還涉及到安全性、可維護性等等。
\\三、添加自定義文件
\\一些優化Dockerfile的小提示:
\\- 理解COPY和ADD指令的區別,詳見:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy。\\t
- (盡可能)遵照文件系統慣例來存放文件:http://www.pathname.com/fhs/。例如針對解釋型應用程序(如Python),使用/usr/src目錄。\\t
- 檢查添加文件的屬性。如果需要可執行權限,沒有必要在鏡像上新建一個層(通過“RUN chmod +x ...”指令),只需要在代碼倉庫的源文件上修正這些屬性即可。即使開發平臺是Windows,也可以參照下文給文件增加可執行權限:https://stackoverflow.com/questions/21691202/how-to-create-file-execute-mode-permissions-in-git-on-windows。\
四、定義容器運行時的用戶權限
\\現在可以休息一下,閱讀下這篇不錯的的文章:Understanding how uid and gid work in Docker containers。
\\讀完這篇文章,我們會了解:
\\- 僅當應用程序需要訪問用戶或組數據(/etc/passwd或/etc/group)時,才需要在容器啟動時指定固定的用戶ID。\\t
- 盡可能避免容器以root權限運行。\
不幸的是,不少熱門應用程序鏡像需要用特定的用戶id來運行(例如Elastic Search需要uid:gid = 1000:1000)。盡量不要在寫出這樣的鏡像……
\\五、定義暴露的端口
\\這也是一個微不足道的小操作,但是不要為了暴露特權端口(例如80)而將容器以root權限運行。如果有這樣的需求,可以讓容器暴露一個非特權端口(例如8080),然后在啟動時進行端口映射。
\\關于特權端口和非特權端口的不同:https://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html。
\\六、定義入口點(entrypoint)
\\普通方式:直接運行可執行文件。
\\更好的方式:創建一個“docker-entrypoint.sh”腳本,可以用來通過環境變量來配置容器的入口點(具體請參照下一節)。
\\這是一個非常普遍的做法,這里有一些例子:
\\https://github.com/elastic/elasticsearch-docker/tree/master/build/elasticsearch/bin
\\https://github.com/docker-library/postgres/tree/de8ba87d50de466a1e05e111927d2bc30c2db36d/10
\\七、定義一種配置方式
\\基本上每個應用程序都需要參數化。基本上有兩條路可以遵循:
\\如果讀者認為這種方式不夠現代,記住這也是12-factors推薦的方式:
\\https://12factor.net/zh_cn/config
\\這并不意味著我們可以拋開所有的配置文件,并對應用程序進行重構,去除配置文件機制。只需要通過envsubst命令來替換配置文件模板(這個流程需要在docker-entrypoint.sh文件中完成,因為這需要在運行時完成)。
\\例如:
\\https://docs.docker.com/samples/library/nginx/#using-environment-variables-in-nginx-configuration
\\這種方式可以將應用程序的配置文件封裝在容器內部,無須讓使用者了解這些細節。
\\八、外部化數據
\\關于數據存儲有一條黃金法則:絕對不要將任何持久化數據保存到容器內。
\\容器的文件系統被設計成臨時和短暫的。因此任何由應用程序生成的內容、數據文件和處理結果都應該保存到掛載的卷或者操作系統綁定掛載點上(既將宿主機操作系統的目錄掛載到容器中)。
\\對于掛載卷我不太有經驗,因此我個人更傾向于將數據保存到綁定掛載點(bind mounts)。這些掛載點一般通過類似Salt Stack這樣的配置管理工具仔細的在宿主機上創建。
\\這里說的“仔細創建”,主要包括下面幾個步驟:
\\九、確保處理好日志
\\前面關于“持久性數據”沒有一個明確的定義,日志在這里就是灰色地帶。我們該如何處理它們呢?
\\如果這是一個新的應用程序,并且希望它能夠堅持docker約定,就不應該將日志寫入文件。應用程序應該使用標準輸出和標準錯誤輸出日志。和之前推薦使用環境變量一樣,這也是12-factors之一:
\\https://12factor.net/zh_cn/logs
\\Docker會自動捕捉應用程序的標準輸出,并可以通過“docker logs”命令查看:
\\https://docs.docker.com/engine/reference/commandline/logs/
\\當然還有一些實際場景下會遇到問題。例如運行一個簡單的nginx容器,至少會有兩種不同的日志文件:
\\- HTTP訪問日志(Access Logs)\\t
- 錯誤日志(Error Logs)\
對于這種日志按照特定結構輸出的應用,可能不太適合將它們的日志輸出到標準輸出。這個例子中,只需要按照前面一節中說的處理好持久化問題,并確保正確配置文件的輪轉。
\\十、輪轉日志和其他僅追加文件
\\如果應用程序將日志寫到文件,或者會無限追加內容到文件,就需要關注這些文件的輪轉(rotation),這對于防止服務器空間耗盡非常有用(尤其是GDPR和其他數據安全條例出來之后)。
\\如果使用綁定掛載,我們可以依靠宿主機的一些工具來實現文件輪轉功能,例如logrotate(文檔參見https://linux.die.net/man/8/logrotate)。
\\最近我找到的一個簡單且完整的例子:
\\https://www.aerospike.com/docs/operations/configure/log/logrotate.html
\\另外一個例子:
\\https://www.digitalocean.com/community/tutorials/how-to-manage-logfiles-with-logrotate-on-ubuntu-16-04
\\查看英文原文:How to dockerize any application
\\感謝張嬋對本文的審校。
總結
以上是生活随笔為你收集整理的如何Docker化任意一个应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SSD行业要变天了!因为这种闪存芯片要来
- 下一篇: SqlServer注意事项总结,高级程序