您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么創建用于安裝應用的Dockerfile”,在日常操作中,相信很多人在怎么創建用于安裝應用的Dockerfile問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么創建用于安裝應用的Dockerfile”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
概念
什么是“傳統”應用?
并沒有一個特定的定義能夠描述所有的傳統應用,但它們有一些共同的特性:
使用本地文件系統來持久化存儲,數據文件和應用的文件混合在一起。
在同一個服務器上運行很多服務,比如 MySQL 數據庫,Redis 服務器,nginx web 服務器,一個 Ruby on Rails 應用,以及一大堆定時任務
使用大雜燴式的腳本和手工流程進行安裝和升級(文檔也很簡陋)。
配置是存儲在文件里的,通常散落在多個位置,并與應用的文件混在一起。
進程間的通信是借助本地文件系統進行的(比如在磁盤上放一個文件,另一個進程來讀取),而不是TCP/IP。
按照單個服務器上只運行一個應用的示例的方式來設計的。
傳統應用的缺點
自動化部署很困難。
如果需要運行應用的多個不同的實例,很難讓多個實例在同一個服務器上“共存”。
如果服務器停機,由于需要手工流程所以需要較長的時間來恢復。
部署新版本的過程基本是手動的,或者大部分是手動的,難以回滾。
很有可能測試環境與生產環境有較大差異,導致一些生產環境問題不能在測試期間發現。
很難通過增加新的實例來進行橫向擴展。
什么是容器化?
將應用“容器化”的過程,就是讓應用能夠運行在 Docker 容器或類似技術中,它們能將操作系統環境和應用封裝在一起(完整的系統鏡像)。由于容器能給應用提供近似于完整系統的環境,這就為在不修改,或者少量修改應用的情況下,對應用的部署進行現代化改造提供了一種思路。這也是應用的架構持續能保持“云友好”的基礎。
容器化的好處
部署容易多了:使用新的容器鏡像直接替換整個老版本。
自動化部署也相對容易,甚至可以完全由 CI(Continuous Integration, 持續集成)來驅動。
部署失敗時的回滾只要切換到之前的鏡像。
應用升級非常容易,因為現在沒有可能出錯的“中間步驟”了(不管它是否影響整個部署過程的成功)。
相同的容器鏡像可以在不同的環境中充分測試,再直接部署到生產環境。這可以確保測試態與生產態的產品是完全一致的。
系統更容易從宕機中恢復,因為可以迅速在新硬件資源上啟動裝有這個應用的新容器,并附加到同一數據源上。
開發人員能在本地以容器的形式,在更逼真的環境里測試新功能。
硬件資源的利用更高效,在單一主機上現在可以運行多個容器應用,而以前不能。
容器化是支持零停機升級、金絲雀部署、高可用和橫向擴展的堅實基礎。
容器化之外的選擇
用 Puppet 和 Chef 之類的配置管理工具,能解決一部分的“傳統”問題,比如環境一致性等。但它們不能支持“原子”部署,以及對應用+環境的完整回滾。而一種無法方便回滾的部署方案,仍然會在部署中途充滿風險。
虛擬機鏡像是能實現部分上述能力的另一種方法,而且在有些情形中,相對于容器,使用完整的虛機進行“原子地”部署會更合適。但使用虛機的主要問題是,它對硬件的利用率更低效。因為虛機需要一些獨占的資源(CPU、內存和磁盤等),而容器之間可以共享主機的資源。
如何容器化
一、準備工作
列出存儲數據的文件系統位置
由于部署新版本應用是通過替換 Docker 鏡像實現的,所以任何持久化的數據都應該存儲在容器之外。如果運氣不錯的話,可能遇到應用已經將所有數據都寫入了特定位置,不過多數傳統應用常將它們的數據往磁盤上到處亂寫,還有可能與應用本身的文件混在一起。Docker 的可加載存儲卷(volume)讓主機的文件系統能暴露給容器用作特定路徑,這樣數據可以在容器之間留存。所以,我們無論是哪種情況,我們都需要列出用于存儲數據的位置。
現在你可以考慮考慮讓應用里所有輸出的數據寫入到文件系統的同一目錄去了,這樣能明顯簡化容器化版本的部署工作。不過,如果修改應用難以達成,這也并不是必須的。
找出會隨部署環境變化的配置數據
為了確保一致性,同一個鏡像要在多套環境中使用(比如,測試和生產),因此必須要列出所有在不同環境中會變化的配置值,在啟動容器的時刻再設置值。容器中的程序到時候可以從環境變量,或者從配置文件中獲取這些配置的值。
你可以現在就考慮修改應用并支持從環境變量中讀取配置,以便簡化容器化的過程。同樣的,如果不好修改應用,這也是不一定是必要的。
找出容易移出去的服務
在同一機器上,我們的應用可能要依賴一些其他服務,它們如果獨立性比較高、使用 TCP/IP 通信,就很容易能移出去。舉例來說,如果在同一機器上運行 MySQL 或 PostgreSQL 數據庫,或者類似 Redis 的緩存,那就容易移出去了。可能同時還需要調整配置,才能支持指定機器名(hostname)和端口(port)而不是直接認為應用運行在 localhost。
二、創建容器鏡像
創建用于安裝應用的 Dockerfile
如果已經有基于腳本或者 Chef、Puppet 之類的配置管理工具的自動化安裝能力,那這個過程就很簡單了。挑選一個喜歡的系統鏡像、安裝所有依賴,然后運行自動化腳本就行了。
如果目前的安裝過程是手動的,就需要寫一些腳本了。不過,由于鏡像的狀態是已知的,在這兒編寫腳本要比基于可能存在不一致性的原生系統來的容易。
如果提前找出了要移出去的服務,那么在腳本里就不應該安裝它們了。
下面是一個簡單的示例 Dockerfile:
# 基于官方 Ubuntu 16.04 Docker 鏡像 FROM ubuntu:16.04 # 安裝所依賴的 Ubuntu 軟件包 RUN apt-get install -y <REQUIRED UBUNTU PACKAGES> \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # 將應用的文件復制到鏡像里 ADD . /app # 運行安裝腳本 RUN /app/setup.sh # 切換到應用的目錄 WORKDIR /app # 指定應用的啟動腳本 COMMAND /app/start.sh
制作用于配置的啟動腳本
如果應用已經在使用環境變量中讀取配置值了,那這一步可以跳過了。如果要從文件里讀取特定環境相關的配置值,那啟動腳本就要能從環境變量里讀取配置值,并將這些值更新到配置文件中去。
這里有一個啟動腳本的例子:
#!/usr/bin/env bash set -e # 把環境變量 $MYAPPCONFIG 的值添加到配置文件中 cat >>/app/config.txt <<END my_app_config = "${MYAPPCONFIG}" END # 用環境變量 $MYAPPARG 作為應用的啟動參數 /app/bin/my-app --my-arg="${MYAPPARG}"
推送鏡像
鏡像生成之后(使用 docker build),需要推送到 Docker 倉儲(Registry)中才能從部署機器上拉取到(如果要在生成鏡像的同一臺機器上運行,就不需要)。
可以使用 Docker Hub 來存儲鏡像(用付費賬號可以創建私有倉庫),大多數云服務商也提供容器倉儲(比如 Amazon ECR)。
給鏡像設置標簽(比如 docker tag myimage mycompany/myimage:mytag)之后,就可以推送到倉庫了(比如 docker push mycompany/myimage:mytag)。每次在應用新版本生成鏡像時打上新的標簽,這樣既能明確當前所運行的版本,還能保留舊版本的鏡像以便回滾。
三、如何部署
部署容器是個很大的話題,接下來只關注直接使用 docker 命令運行容器的部分。在現實世界中,應該考慮使用 docker-compose(對于所有容器都運行在同一機器上的簡單情形)和 Kubernetes (在集群中編排容器)之類的工具。
被移出來的服務
提前移出來的服務可以運行在單獨的 Docker 容器中,然后鏈接(link)到我們的應用所在容器。另外,還可以用云上托管的服務。舉個例子,在 AWS 上,可以使用 RDS 作為數據庫、用 Elasticache 作為緩存,這樣可以極大地簡化你的工作,因為他們能為你解決后期維護,高可用和備份等需求。
運行 Postgres 數據庫容器的例子:
docker run -d \ --name db \ -v /usr/local/postgresql/data:/var/lib/postgresql/data \ postgres
容器化之后的應用
要在 Docker 容器中運行一個應用,只要用一個命令行:
docker run -d \ -p 8080:80 \ --name myapp \ -v /usr/local/myappdata:/var/lib/myappdata \ -e MYAPPCONFIG=myvalue \ -e MYAPPARG=myarg \ --link db:db \ myappimage:mytag
其中的 -p 參數將容器里的 80 端口公開并映射到主機上的 8080 端口,-v 參數設置要在容器里加載的、用于持久化數據的存儲卷(格式是 主機上的路徑:容器中的路徑)-e 參數設置一個用于配置的環境變量值(這些參數可以指定多次,從而設置多個卷和環境變量),而 --link參數將數據庫所在容器以鏈接的方式傳入,這樣應用就可以與數據庫通信了。容器會根據 Dockerfile 中的 COMMAND 指令指定的腳本來啟動。
對應用進行升級
如果要升級到應用的新版本,只要停掉舊版的容器(比如 docker rm -f myapp),并用新的鏡像標簽啟動新的容器就可以了(可能有短暫的停機時間)。回滾操作也類似,只要換用舊版的鏡像標簽。
更多相關考量
“init” 進程(PID 1)
傳統應用通常有多個進程,如果沒有 “init” 守護進程(PID 1)的清理,就容易出現孤兒進程(orphan processes)發生累積的情況了。Docker 默認并不提供這樣的守護進程,所以推薦自己用 ENTRYPOINT 在 Dockerfile 里添加一個。dumb-init 是眾多初始守護進程中的比較輕量級的一個。phusion/baseimage 是一個包含 init 初始守護進程和其他一些服務的全功能基準鏡像。 請查看我們博客上關于這個主題的文章:Docker 守護進程:PID-1, 孤兒進程, 僵尸進程和信號。
守護進程和定時任務
在使用 Docker 容器時,一般只會在每個容器中運行一個進程。理想情況下,所有守護進程和定時任務都應該移到其他容器中去,不過對于傳統應用來,這也不一定都行得通,主要是經常要求對應用進行重新設計。要運行多個進程也不是一定不行,但確實會需要一些額外的一些配置,因為標準的基準鏡像里并不包含進程管理和調度能力。小型進程管理程序,比如 runit,比 systemd 之類的完整功能的子系統更適合在容器中用。phusion/baseimage 是一個包含 runit 和定時能力和其他一些服務的全功能基準鏡像。
存儲卷的權限
在容器里,所有進程通常都以 root 身份運行(不過也不是必須的)。傳統的應用對用戶的需求通常復雜一些,可能要用其他用戶來運行(或者用不同的用戶運行多個進程)。這可能給存儲卷的使用帶來一些麻煩,因為 Docker 默認讓加載的卷的所有權指向 root,也就是說非 root 進程就不能寫入到這些卷了。有兩個方法可以解決這個問題:
第一種方式是在在創建容器之前,先在主機上創建好目錄,由有正確的 UID/GID 的用戶持有所有權。注意,由于容器里和主機上的用戶不能匹配,所以需要用容器里用戶的 UID/GID,而不僅僅是用戶名要一致。
另一種方式是在容器里,在啟動過程中調整加載點的所有權。這就需要在切換到用來啟動應用的非 root 用戶之前,還在以 root 身份運行期間處理。
數據庫遷移
數據庫結構遷移在部署工作中經常是一大挑戰,因為數據庫結構通常與應用是嚴格耦合的,這對遷移的時機提出了要求,而且這也讓回滾到舊版本變得更難,因為數據庫遷移并不一定容易回滾。
完成這種遷移的方法是引入一個過渡步驟。如果需要對數據庫結構做出與舊版本不兼容的變更,那就將這個變更分為兩次部署。比如,如果想將數據移到另一處,兩個步驟是:
將數據同時寫入舊的位置和的位置,并只從新的位置讀取。這意味著,如果把應用回滾到前一個版本,在回滾之前新產生的新數據是不會丟的。
不再向舊的位置寫入數據。 要注意的是,如果希望部署期間沒有停機時間,就意味著在同一時間會有應用的多個版本在運行,相應的也會帶來更多挑戰。
數據備份
對容器化的應用進行備份通常比較簡單。數據文件可以從主機上備份,而不需要擔心數據會與應用程序的文件混在一起,因為它們已經嚴格地分開了。如果將數據庫遷移到了像 RDS 這樣的托管服務,他們就會處理好備份(至少自己的工作會簡化一些)。
遷移已有數據
在生產環境中,要把現有應用遷向容器化的版本,就需要對舊的已有數據進行遷移。這個工作往往因地制宜,不過最簡單的就是停掉舊版本,把數據備份直接恢復給新版本用。這個過程應該提前做好,也不可避免地會需要一定的停機時間。
到此,關于“怎么創建用于安裝應用的Dockerfile”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。