溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么創(chuàng)建用于安裝應(yīng)用的Dockerfile

發(fā)布時(shí)間:2022-01-04 17:26:47 來源:億速云 閱讀:143 作者:iii 欄目:服務(wù)器

這篇文章主要介紹“怎么創(chuàng)建用于安裝應(yīng)用的Dockerfile”,在日常操作中,相信很多人在怎么創(chuàng)建用于安裝應(yīng)用的Dockerfile問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么創(chuàng)建用于安裝應(yīng)用的Dockerfile”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

概念

什么是“傳統(tǒng)”應(yīng)用?

并沒有一個(gè)特定的定義能夠描述所有的傳統(tǒng)應(yīng)用,但它們有一些共同的特性:

  • 使用本地文件系統(tǒng)來持久化存儲(chǔ),數(shù)據(jù)文件和應(yīng)用的文件混合在一起。

  • 在同一個(gè)服務(wù)器上運(yùn)行很多服務(wù),比如 MySQL 數(shù)據(jù)庫,Redis 服務(wù)器,nginx web 服務(wù)器,一個(gè) Ruby on Rails 應(yīng)用,以及一大堆定時(shí)任務(wù)

  • 使用大雜燴式的腳本和手工流程進(jìn)行安裝和升級(jí)(文檔也很簡(jiǎn)陋)。

  • 配置是存儲(chǔ)在文件里的,通常散落在多個(gè)位置,并與應(yīng)用的文件混在一起。

  • 進(jìn)程間的通信是借助本地文件系統(tǒng)進(jìn)行的(比如在磁盤上放一個(gè)文件,另一個(gè)進(jìn)程來讀取),而不是TCP/IP。

  • 按照單個(gè)服務(wù)器上只運(yùn)行一個(gè)應(yīng)用的示例的方式來設(shè)計(jì)的。

傳統(tǒng)應(yīng)用的缺點(diǎn)

  • 自動(dòng)化部署很困難。

  • 如果需要運(yùn)行應(yīng)用的多個(gè)不同的實(shí)例,很難讓多個(gè)實(shí)例在同一個(gè)服務(wù)器上“共存”。

  • 如果服務(wù)器停機(jī),由于需要手工流程所以需要較長的時(shí)間來恢復(fù)。

  • 部署新版本的過程基本是手動(dòng)的,或者大部分是手動(dòng)的,難以回滾。

  • 很有可能測(cè)試環(huán)境與生產(chǎn)環(huán)境有較大差異,導(dǎo)致一些生產(chǎn)環(huán)境問題不能在測(cè)試期間發(fā)現(xiàn)。

  • 很難通過增加新的實(shí)例來進(jìn)行橫向擴(kuò)展。

什么是容器化?

將應(yīng)用“容器化”的過程,就是讓應(yīng)用能夠運(yùn)行在 Docker 容器或類似技術(shù)中,它們能將操作系統(tǒng)環(huán)境和應(yīng)用封裝在一起(完整的系統(tǒng)鏡像)。由于容器能給應(yīng)用提供近似于完整系統(tǒng)的環(huán)境,這就為在不修改,或者少量修改應(yīng)用的情況下,對(duì)應(yīng)用的部署進(jìn)行現(xiàn)代化改造提供了一種思路。這也是應(yīng)用的架構(gòu)持續(xù)能保持“云友好”的基礎(chǔ)。

容器化的好處

  • 部署容易多了:使用新的容器鏡像直接替換整個(gè)老版本。

  • 自動(dòng)化部署也相對(duì)容易,甚至可以完全由 CI(Continuous Integration, 持續(xù)集成)來驅(qū)動(dòng)。

  • 部署失敗時(shí)的回滾只要切換到之前的鏡像。

  • 應(yīng)用升級(jí)非常容易,因?yàn)楝F(xiàn)在沒有可能出錯(cuò)的“中間步驟”了(不管它是否影響整個(gè)部署過程的成功)。

  • 相同的容器鏡像可以在不同的環(huán)境中充分測(cè)試,再直接部署到生產(chǎn)環(huán)境。這可以確保測(cè)試態(tài)與生產(chǎn)態(tài)的產(chǎn)品是完全一致的。

  • 系統(tǒng)更容易從宕機(jī)中恢復(fù),因?yàn)榭梢匝杆僭谛掠布Y源上啟動(dòng)裝有這個(gè)應(yīng)用的新容器,并附加到同一數(shù)據(jù)源上。

  • 開發(fā)人員能在本地以容器的形式,在更逼真的環(huán)境里測(cè)試新功能。

  • 硬件資源的利用更高效,在單一主機(jī)上現(xiàn)在可以運(yùn)行多個(gè)容器應(yīng)用,而以前不能。

  • 容器化是支持零停機(jī)升級(jí)、金絲雀部署、高可用和橫向擴(kuò)展的堅(jiān)實(shí)基礎(chǔ)。

容器化之外的選擇

  • 用 Puppet 和 Chef 之類的配置管理工具,能解決一部分的“傳統(tǒng)”問題,比如環(huán)境一致性等。但它們不能支持“原子”部署,以及對(duì)應(yīng)用+環(huán)境的完整回滾。而一種無法方便回滾的部署方案,仍然會(huì)在部署中途充滿風(fēng)險(xiǎn)。

  • 虛擬機(jī)鏡像是能實(shí)現(xiàn)部分上述能力的另一種方法,而且在有些情形中,相對(duì)于容器,使用完整的虛機(jī)進(jìn)行“原子地”部署會(huì)更合適。但使用虛機(jī)的主要問題是,它對(duì)硬件的利用率更低效。因?yàn)樘摍C(jī)需要一些獨(dú)占的資源(CPU、內(nèi)存和磁盤等),而容器之間可以共享主機(jī)的資源。

如何容器化


一、準(zhǔn)備工作

列出存儲(chǔ)數(shù)據(jù)的文件系統(tǒng)位置

由于部署新版本應(yīng)用是通過替換 Docker 鏡像實(shí)現(xiàn)的,所以任何持久化的數(shù)據(jù)都應(yīng)該存儲(chǔ)在容器之外。如果運(yùn)氣不錯(cuò)的話,可能遇到應(yīng)用已經(jīng)將所有數(shù)據(jù)都寫入了特定位置,不過多數(shù)傳統(tǒng)應(yīng)用常將它們的數(shù)據(jù)往磁盤上到處亂寫,還有可能與應(yīng)用本身的文件混在一起。Docker 的可加載存儲(chǔ)卷(volume)讓主機(jī)的文件系統(tǒng)能暴露給容器用作特定路徑,這樣數(shù)據(jù)可以在容器之間留存。所以,我們無論是哪種情況,我們都需要列出用于存儲(chǔ)數(shù)據(jù)的位置。

現(xiàn)在你可以考慮考慮讓應(yīng)用里所有輸出的數(shù)據(jù)寫入到文件系統(tǒng)的同一目錄去了,這樣能明顯簡(jiǎn)化容器化版本的部署工作。不過,如果修改應(yīng)用難以達(dá)成,這也并不是必須的。

找出會(huì)隨部署環(huán)境變化的配置數(shù)據(jù)

為了確保一致性,同一個(gè)鏡像要在多套環(huán)境中使用(比如,測(cè)試和生產(chǎn)),因此必須要列出所有在不同環(huán)境中會(huì)變化的配置值,在啟動(dòng)容器的時(shí)刻再設(shè)置值。容器中的程序到時(shí)候可以從環(huán)境變量,或者從配置文件中獲取這些配置的值。

你可以現(xiàn)在就考慮修改應(yīng)用并支持從環(huán)境變量中讀取配置,以便簡(jiǎn)化容器化的過程。同樣的,如果不好修改應(yīng)用,這也是不一定是必要的。

找出容易移出去的服務(wù)

在同一機(jī)器上,我們的應(yīng)用可能要依賴一些其他服務(wù),它們?nèi)绻?dú)立性比較高、使用 TCP/IP 通信,就很容易能移出去。舉例來說,如果在同一機(jī)器上運(yùn)行 MySQL 或 PostgreSQL 數(shù)據(jù)庫,或者類似 Redis 的緩存,那就容易移出去了。可能同時(shí)還需要調(diào)整配置,才能支持指定機(jī)器名(hostname)和端口(port)而不是直接認(rèn)為應(yīng)用運(yùn)行在 localhost。

二、創(chuàng)建容器鏡像

創(chuàng)建用于安裝應(yīng)用的 Dockerfile

如果已經(jīng)有基于腳本或者 Chef、Puppet 之類的配置管理工具的自動(dòng)化安裝能力,那這個(gè)過程就很簡(jiǎn)單了。挑選一個(gè)喜歡的系統(tǒng)鏡像、安裝所有依賴,然后運(yùn)行自動(dòng)化腳本就行了。

如果目前的安裝過程是手動(dòng)的,就需要寫一些腳本了。不過,由于鏡像的狀態(tài)是已知的,在這兒編寫腳本要比基于可能存在不一致性的原生系統(tǒng)來的容易。

如果提前找出了要移出去的服務(wù),那么在腳本里就不應(yīng)該安裝它們了。

下面是一個(gè)簡(jiǎn)單的示例 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/*
# 將應(yīng)用的文件復(fù)制到鏡像里
ADD . /app
# 運(yùn)行安裝腳本
RUN /app/setup.sh
# 切換到應(yīng)用的目錄
WORKDIR /app
# 指定應(yīng)用的啟動(dòng)腳本
COMMAND /app/start.sh

制作用于配置的啟動(dòng)腳本

如果應(yīng)用已經(jīng)在使用環(huán)境變量中讀取配置值了,那這一步可以跳過了。如果要從文件里讀取特定環(huán)境相關(guān)的配置值,那啟動(dòng)腳本就要能從環(huán)境變量里讀取配置值,并將這些值更新到配置文件中去。

這里有一個(gè)啟動(dòng)腳本的例子:

#!/usr/bin/env bash
set -e
# 把環(huán)境變量 $MYAPPCONFIG 的值添加到配置文件中
cat >>/app/config.txt <<END
my_app_config = "${MYAPPCONFIG}"
END
# 用環(huán)境變量 $MYAPPARG 作為應(yīng)用的啟動(dòng)參數(shù)
/app/bin/my-app --my-arg="${MYAPPARG}"

推送鏡像

鏡像生成之后(使用 docker build),需要推送到 Docker 倉儲(chǔ)(Registry)中才能從部署機(jī)器上拉取到(如果要在生成鏡像的同一臺(tái)機(jī)器上運(yùn)行,就不需要)。

可以使用 Docker Hub 來存儲(chǔ)鏡像(用付費(fèi)賬號(hào)可以創(chuàng)建私有倉庫),大多數(shù)云服務(wù)商也提供容器倉儲(chǔ)(比如 Amazon ECR)。

給鏡像設(shè)置標(biāo)簽(比如 docker tag myimage mycompany/myimage:mytag)之后,就可以推送到倉庫了(比如 docker push mycompany/myimage:mytag)。每次在應(yīng)用新版本生成鏡像時(shí)打上新的標(biāo)簽,這樣既能明確當(dāng)前所運(yùn)行的版本,還能保留舊版本的鏡像以便回滾。

三、如何部署

部署容器是個(gè)很大的話題,接下來只關(guān)注直接使用 docker 命令運(yùn)行容器的部分。在現(xiàn)實(shí)世界中,應(yīng)該考慮使用 docker-compose(對(duì)于所有容器都運(yùn)行在同一機(jī)器上的簡(jiǎn)單情形)和 Kubernetes (在集群中編排容器)之類的工具。

被移出來的服務(wù)

提前移出來的服務(wù)可以運(yùn)行在單獨(dú)的 Docker 容器中,然后鏈接(link)到我們的應(yīng)用所在容器。另外,還可以用云上托管的服務(wù)。舉個(gè)例子,在 AWS 上,可以使用 RDS 作為數(shù)據(jù)庫、用 Elasticache 作為緩存,這樣可以極大地簡(jiǎn)化你的工作,因?yàn)樗麄兡転槟憬鉀Q后期維護(hù),高可用和備份等需求。

運(yùn)行 Postgres 數(shù)據(jù)庫容器的例子:

docker run -d \
    --name db \
    -v /usr/local/postgresql/data:/var/lib/postgresql/data \
    postgres

容器化之后的應(yīng)用

要在 Docker 容器中運(yùn)行一個(gè)應(yīng)用,只要用一個(gè)命令行:

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 參數(shù)將容器里的 80 端口公開并映射到主機(jī)上的 8080 端口,-v 參數(shù)設(shè)置要在容器里加載的、用于持久化數(shù)據(jù)的存儲(chǔ)卷(格式是 主機(jī)上的路徑:容器中的路徑)-e 參數(shù)設(shè)置一個(gè)用于配置的環(huán)境變量值(這些參數(shù)可以指定多次,從而設(shè)置多個(gè)卷和環(huán)境變量),而 --link參數(shù)將數(shù)據(jù)庫所在容器以鏈接的方式傳入,這樣應(yīng)用就可以與數(shù)據(jù)庫通信了。容器會(huì)根據(jù) Dockerfile 中的 COMMAND 指令指定的腳本來啟動(dòng)。

對(duì)應(yīng)用進(jìn)行升級(jí)

如果要升級(jí)到應(yīng)用的新版本,只要停掉舊版的容器(比如 docker rm -f myapp),并用新的鏡像標(biāo)簽啟動(dòng)新的容器就可以了(可能有短暫的停機(jī)時(shí)間)。回滾操作也類似,只要換用舊版的鏡像標(biāo)簽。

更多相關(guān)考量


“init” 進(jìn)程(PID 1)

傳統(tǒng)應(yīng)用通常有多個(gè)進(jìn)程,如果沒有 “init” 守護(hù)進(jìn)程(PID 1)的清理,就容易出現(xiàn)孤兒進(jìn)程(orphan processes)發(fā)生累積的情況了。Docker 默認(rèn)并不提供這樣的守護(hù)進(jìn)程,所以推薦自己用 ENTRYPOINT 在 Dockerfile 里添加一個(gè)。dumb-init 是眾多初始守護(hù)進(jìn)程中的比較輕量級(jí)的一個(gè)。phusion/baseimage 是一個(gè)包含 init 初始守護(hù)進(jìn)程和其他一些服務(wù)的全功能基準(zhǔn)鏡像。 請(qǐng)查看我們博客上關(guān)于這個(gè)主題的文章:Docker 守護(hù)進(jìn)程:PID-1, 孤兒進(jìn)程, 僵尸進(jìn)程和信號(hào)。

守護(hù)進(jìn)程和定時(shí)任務(wù)

在使用 Docker 容器時(shí),一般只會(huì)在每個(gè)容器中運(yùn)行一個(gè)進(jìn)程。理想情況下,所有守護(hù)進(jìn)程和定時(shí)任務(wù)都應(yīng)該移到其他容器中去,不過對(duì)于傳統(tǒng)應(yīng)用來,這也不一定都行得通,主要是經(jīng)常要求對(duì)應(yīng)用進(jìn)行重新設(shè)計(jì)。要運(yùn)行多個(gè)進(jìn)程也不是一定不行,但確實(shí)會(huì)需要一些額外的一些配置,因?yàn)闃?biāo)準(zhǔn)的基準(zhǔn)鏡像里并不包含進(jìn)程管理和調(diào)度能力。小型進(jìn)程管理程序,比如 runit,比 systemd 之類的完整功能的子系統(tǒng)更適合在容器中用。phusion/baseimage 是一個(gè)包含 runit 和定時(shí)能力和其他一些服務(wù)的全功能基準(zhǔn)鏡像。

存儲(chǔ)卷的權(quán)限

在容器里,所有進(jìn)程通常都以 root 身份運(yùn)行(不過也不是必須的)。傳統(tǒng)的應(yīng)用對(duì)用戶的需求通常復(fù)雜一些,可能要用其他用戶來運(yùn)行(或者用不同的用戶運(yùn)行多個(gè)進(jìn)程)。這可能給存儲(chǔ)卷的使用帶來一些麻煩,因?yàn)?Docker 默認(rèn)讓加載的卷的所有權(quán)指向 root,也就是說非 root 進(jìn)程就不能寫入到這些卷了。有兩個(gè)方法可以解決這個(gè)問題:

第一種方式是在在創(chuàng)建容器之前,先在主機(jī)上創(chuàng)建好目錄,由有正確的 UID/GID 的用戶持有所有權(quán)。注意,由于容器里和主機(jī)上的用戶不能匹配,所以需要用容器里用戶的 UID/GID,而不僅僅是用戶名要一致。

另一種方式是在容器里,在啟動(dòng)過程中調(diào)整加載點(diǎn)的所有權(quán)。這就需要在切換到用來啟動(dòng)應(yīng)用的非 root 用戶之前,還在以 root 身份運(yùn)行期間處理。

數(shù)據(jù)庫遷移

數(shù)據(jù)庫結(jié)構(gòu)遷移在部署工作中經(jīng)常是一大挑戰(zhàn),因?yàn)閿?shù)據(jù)庫結(jié)構(gòu)通常與應(yīng)用是嚴(yán)格耦合的,這對(duì)遷移的時(shí)機(jī)提出了要求,而且這也讓回滾到舊版本變得更難,因?yàn)閿?shù)據(jù)庫遷移并不一定容易回滾。

完成這種遷移的方法是引入一個(gè)過渡步驟。如果需要對(duì)數(shù)據(jù)庫結(jié)構(gòu)做出與舊版本不兼容的變更,那就將這個(gè)變更分為兩次部署。比如,如果想將數(shù)據(jù)移到另一處,兩個(gè)步驟是:

將數(shù)據(jù)同時(shí)寫入舊的位置和的位置,并只從新的位置讀取。這意味著,如果把應(yīng)用回滾到前一個(gè)版本,在回滾之前新產(chǎn)生的新數(shù)據(jù)是不會(huì)丟的。

不再向舊的位置寫入數(shù)據(jù)。 要注意的是,如果希望部署期間沒有停機(jī)時(shí)間,就意味著在同一時(shí)間會(huì)有應(yīng)用的多個(gè)版本在運(yùn)行,相應(yīng)的也會(huì)帶來更多挑戰(zhàn)。

數(shù)據(jù)備份

對(duì)容器化的應(yīng)用進(jìn)行備份通常比較簡(jiǎn)單。數(shù)據(jù)文件可以從主機(jī)上備份,而不需要擔(dān)心數(shù)據(jù)會(huì)與應(yīng)用程序的文件混在一起,因?yàn)樗鼈円呀?jīng)嚴(yán)格地分開了。如果將數(shù)據(jù)庫遷移到了像 RDS 這樣的托管服務(wù),他們就會(huì)處理好備份(至少自己的工作會(huì)簡(jiǎn)化一些)。

遷移已有數(shù)據(jù)

在生產(chǎn)環(huán)境中,要把現(xiàn)有應(yīng)用遷向容器化的版本,就需要對(duì)舊的已有數(shù)據(jù)進(jìn)行遷移。這個(gè)工作往往因地制宜,不過最簡(jiǎn)單的就是停掉舊版本,把數(shù)據(jù)備份直接恢復(fù)給新版本用。這個(gè)過程應(yīng)該提前做好,也不可避免地會(huì)需要一定的停機(jī)時(shí)間。

到此,關(guān)于“怎么創(chuàng)建用于安裝應(yīng)用的Dockerfile”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI