【DevOps】第一步: 編寫Dockerfile與項(xiàng)目實(shí)踐

一、Dockerfile簡(jiǎn)介

Dockerfile 是一個(gè)文本文件,其內(nèi)包含了一條條的 指令(Instruction),每一條指令構(gòu)建一層,因此每一條指令的內(nèi)容,就是描述該層應(yīng)當(dāng)如何構(gòu)建。

Dockerfile分為四部分:

  • FROM:基礎(chǔ)鏡像信息
  • MAINTAINER:維護(hù)者信息
  • RUN、COPY、ADD、EXPOSE等:鏡像操作指令
  • CMD、ENTRYPOINT:容器啟動(dòng)時(shí)執(zhí)行指令。

例如:

FROM python:3.6

MAINTAINER cbbing <cbbing@163.com>

COPY pip.conf requirements.txt /root/.pip/

#設(shè)置時(shí)區(qū)
ENV TZ=Asia/Shanghai
ENV PYTHONPATH=/usr/src/app

RUN mkdir -p /usr/src/app \
    && pip install -r /root/.pip/requirements.txt \
    && apt-get update \
    && apt-get install -y supervisor \
    && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
    && echo $TZ > /etc/timezone

WORKDIR /usr/src/app

CMD ["python"]

Dockerfile指令

指令的一般格式為 INSTRUCTION arguments,指令包括 FROM、MAINTAINER、RUN 等。

1. FROM

格式為 FROM <image>或FROM <image>:<tag>。
第一條指令必須為 FROM 指令。并且,如果在同一個(gè)Dockerfile中創(chuàng)建多個(gè)鏡像時(shí),可以使用多個(gè) FROM 指令(每個(gè)鏡像一次)。

2. MAINTAINER

格式為 MAINTAINER <name>,指定維護(hù)者信息。

3. RUN

格式為 RUN <command> 或 RUN ["executable", "param1", "param2"]。
前者將在 shell 終端中運(yùn)行命令,即 /bin/sh -c;后者則使用 exec 執(zhí)行。指定使用其它終端可以通過第二種方式實(shí)現(xiàn),例如 RUN ["/bin/bash", "-c", "echo hello"]。
每條 RUN 指令將在當(dāng)前鏡像基礎(chǔ)上執(zhí)行指定命令,并提交為新的鏡像。當(dāng)命令較長(zhǎng)時(shí)可以使用 \ 來?yè)Q行。

4. CMD

支持三種格式
CMD ["executable","param1","param2"] 使用 exec 執(zhí)行,推薦方式;
CMD command param1 param2 在 /bin/sh 中執(zhí)行,提供給需要交互的應(yīng)用;
CMD ["param1","param2"] 提供給 ENTRYPOINT 的默認(rèn)參數(shù);
指定啟動(dòng)容器時(shí)執(zhí)行的命令,每個(gè) Dockerfile 只能有一條 CMD 命令。如果指定了多條命令,只有最后一條會(huì)被執(zhí)行。
如果用戶啟動(dòng)容器時(shí)候指定了運(yùn)行的命令,則會(huì)覆蓋掉 CMD 指定的命令。

5. EXPOSE

格式為 EXPOSE <port> [<port>...]。
告訴 Docker 服務(wù)端容器暴露的端口號(hào),供互聯(lián)系統(tǒng)使用。在啟動(dòng)容器時(shí)需要通過 -P,Docker 主機(jī)會(huì)自動(dòng)分配一個(gè)端口轉(zhuǎn)發(fā)到指定的端口。

6. ENV

格式為 ENV <key> <value>。 指定一個(gè)環(huán)境變量,會(huì)被后續(xù) RUN 指令使用,并在容器運(yùn)行時(shí)保持。
例如

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
ENV TZ=Asia/Shanghai
ENV PYTHONPATH=/usr/src/app

7. ADD

格式為 ADD <src> <dest>。
該命令將復(fù)制指定的 <src> 到容器中的 <dest>。 其中 <src> 可以是Dockerfile所在目錄的一個(gè)相對(duì)路徑;也可以是一個(gè) URL;還可以是一個(gè) tar 文件(自動(dòng)解壓為目錄)。

8. COPY

格式為 COPY <src> <dest>。
復(fù)制本地主機(jī)的 <src>(為 Dockerfile 所在目錄的相對(duì)路徑)到容器中的 <dest>。
當(dāng)使用本地目錄為源目錄時(shí),推薦使用 COPY。

9. ENTRYPOINT

兩種格式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2(shell中執(zhí)行)。
配置容器啟動(dòng)后執(zhí)行的命令,并且不可被 docker run 提供的參數(shù)覆蓋。
每個(gè) Dockerfile 中只能有一個(gè) ENTRYPOINT,當(dāng)指定多個(gè)時(shí),只有最后一個(gè)起效。

10. VOLUME

格式為 VOLUME ["/data"]。
創(chuàng)建一個(gè)可以從本地主機(jī)或其他容器掛載的掛載點(diǎn),一般用來存放數(shù)據(jù)庫(kù)和需要保持的數(shù)據(jù)等。

11. USER

格式為 USER daemon。
指定運(yùn)行容器時(shí)的用戶名或 UID,后續(xù)的 RUN 也會(huì)使用指定用戶。
當(dāng)服務(wù)不需要管理員權(quán)限時(shí),可以通過該命令指定運(yùn)行用戶。并且可以在之前創(chuàng)建所需要的用戶,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres。要臨時(shí)獲取管理員權(quán)限可以使用 gosu,而不推薦 sudo。

12. WORKDIR

格式為 WORKDIR /path/to/workdir。
為后續(xù)的 RUN、CMD、ENTRYPOINT 指令配置工作目錄。
可以使用多個(gè) WORKDIR 指令,后續(xù)命令如果參數(shù)是相對(duì)路徑,則會(huì)基于之前命令指定的路徑。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

則最終路徑為 /a/b/c。

13. ONBUILD

格式為 ONBUILD [INSTRUCTION]。
配置當(dāng)所創(chuàng)建的鏡像作為其它新創(chuàng)建鏡像的基礎(chǔ)鏡像時(shí),所執(zhí)行的操作指令。
ONBUILD指令,實(shí)際上就是相當(dāng)于創(chuàng)建一個(gè)模板鏡像,后續(xù)可以根據(jù)該模板鏡像創(chuàng)建特定的子鏡像
例如,Dockerfile 使用如下的內(nèi)容創(chuàng)建了鏡像 image-A。

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

image-A的構(gòu)建過程中不執(zhí)行這兩條命令,但在FROM image-A的子鏡像中會(huì)執(zhí)行這兩條命令。

二、構(gòu)建鏡像

docker build -t mynginx:v2019 .

注意不要少了最后面的“.”

三、Dockerfile編寫建議

  • 通過 Docker 多階段構(gòu)建將多個(gè)層壓縮為一個(gè)
    在構(gòu)建 Docker 容器時(shí),應(yīng)該盡量想辦法獲得體積更小的鏡像,因?yàn)閭鬏敽筒渴痼w積較小的鏡像速度更快。
    從 Docker 1.10 開始,COPY、ADD 和 RUN 語(yǔ)句會(huì)向鏡像中添加新層。層會(huì)占用空間,你擁有的層越多,最終的鏡像就越大。Git 存儲(chǔ)庫(kù)在這方面也是類似的,存儲(chǔ)庫(kù)的大小隨著層數(shù)的增加而增加,因?yàn)?Git 必須保存提交之間的所有變更。
  • 將不經(jīng)常改動(dòng)的命令,比如python的reqirements.txt 依賴包,單獨(dú)拎出來放在項(xiàng)目代碼的前面。因?yàn)橐蕾嚢唤?jīng)常變動(dòng),每次生成鏡像時(shí),只需將后面的項(xiàng)目代碼copy到鏡像即可。
# 方案一:
FROM python:3.6

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

COPY . /usr/src/app
RUN pip install -r /usr/src/app/requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

CMD python /usr/src/app/manage.py runserver 0.0.0.0:8000


# 方案二:
FROM python:3.6

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

COPY requirements.txt /usr/src/app/
RUN pip install -r /usr/src/app/requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

COPY . /usr/src/app
CMD python /usr/src/app/manage.py runserver 0.0.0.0:8000

方案二比方案一好,為什么?因?yàn)榉桨敢恢械膔equirements.txt實(shí)際項(xiàng)目中是很少變動(dòng),但是因?yàn)?usr/src/app的項(xiàng)目代碼是經(jīng)常變動(dòng),導(dǎo)致每次打包都得根據(jù)equirements.txt安裝依賴包。而方案二先把requirements.txt 拷貝進(jìn)來,如果requirements.txt 沒改動(dòng),docker會(huì)用到之前緩存的,加快打包速度。
實(shí)際運(yùn)行幾次打包過程,會(huì)發(fā)現(xiàn)速度的差異是比較明顯的。

四、實(shí)踐

公司現(xiàn)在的所有項(xiàng)目均以容器封裝,幾十個(gè)容器運(yùn)行在十來臺(tái)主機(jī)中,現(xiàn)在正在將服務(wù)器整合到kubernetes集群中,可以說一切皆容器,一切皆Docker。

1. Django項(xiàng)目的Dockerfile

Django項(xiàng)目打包成Docker鏡像是比較簡(jiǎn)單,主要是安裝python依賴包,然后就是manage.py啟動(dòng)服務(wù)。
Dockerfile 位于Django項(xiàng)目的根目錄,目錄結(jié)構(gòu)如下:

├── requirements.txt
├── Dockerfile
├── api
│ ├── __init__.py
│ ├── urls.py
│ ├── wsgi.py
│ └── settings.py

Dockerfile文件:

FROM python:3.6

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

COPY requirements.txt /usr/src/app/
RUN pip install -r /usr/src/app/requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

COPY . /usr/src/app

CMD python /usr/src/app/manage.py runserver 0.0.0.0:8000

2. SpringMVC項(xiàng)目的Dockerfile

項(xiàng)目運(yùn)行在tomcat中。
先生成war文件,轉(zhuǎn)成tar.gz文件, 通過ADD命令解壓到鏡像中。接下來就是配置文件的替換,最后運(yùn)行tomact服務(wù)。

# Version 0.1

# 基礎(chǔ)鏡像
FROM cbbing/tomcat

RUN rm -rf $CATALINA_HOME/webapps/ROOT
ADD ROOT.tar.gz $CATALINA_HOME/webapps/

# 配置文件替換
COPY conf/server.xml $CATALINA_HOME/conf/
COPY conf/tomcat-users.xml $CATALINA_HOME/conf/
COPY conf/jdbc.properties $CATALINA_HOME/webapps/ROOT/WEB-INF/classes/properties/

CMD ["catalina.sh", "run"]

mvn.sh: 生成ROOT.tar.gz的腳本如下:

cd ../myweb
mvn clean
mvn install -DskipTests

cd ..
mkdir ./ROOT
cp ./myweb/target/myweb.war ./ROOT

cd ROOT
jar xvf myweb.war
rm myweb.war
cd ..
tar czf ROOT.tar.gz ./ROOT
rm -rf ROOT/

3. Vue.js項(xiàng)目的Dockerfile

步驟如下:

  1. 下載package.json中定義的依賴
  2. npm build生成編譯后的文件到dist
  3. 基礎(chǔ)鏡像為nginx,從nginx代理靜態(tài)文件
FROM nginx:1.15

MAINTAINER cbbing <cbbing@163.com>

ENV LANG C.UTF-8
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone

COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY dist /usr/share/nginx/html

make.sh:制作腳本如下

#!/usr/bin/env bash

echo "npm install"
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install

echo "npm build"
npm run build

image_tag=`date +%Y%m%d` #_%H%M
echo "當(dāng)前時(shí)間:$image_tag"
docker build -t cbbing/web_p:v${image_tag} .

4. Scrapy項(xiàng)目的Dockerfile

scrapy項(xiàng)目和Django項(xiàng)目的差別在于啟動(dòng)的方式不同,這里涉及到一些配置文件。

FROM python:3.6

#設(shè)置時(shí)區(qū)
ENV TZ=Asia/Shanghai
ENV PYTHONPATH=/usr/src/app

RUN mkdir -p /usr/src/app \
    && pip install -r /root/.pip/requirements.txt  -i https://mirrors.aliyun.com/pypi/simple/ \
    && apt-get update \
    && apt-get install -y supervisor \
    && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
    && echo $TZ > /etc/timezone

WORKDIR /usr/src/app

COPY . /usr/src/app

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN mkdir -p /var/log/supervisor

COPY scrapyd.conf /usr/local/lib/python3.6/site-packages/scrapyd/default_scrapyd.conf

CMD ["/usr/bin/supervisord"]

其中用到的配置文件:

  • supervisord.conf
[inet_http_server]
port=9020
username=admin
password=1234

[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log;
pidfile=/var/log/supervisord.pid;
childlogdir=/var/log/supervisor

[program:scrapyd]
command=scrapyd
loglevel=info
redirect_stderr=true
stdout_events_enabled=true

[program:scrapyd-deploy]
command=bash supervisor-scrapyd-deploy.sh
startsecs = 35
loglevel=info

[program:simple_http]
command=/bin/bash supervisor-http-egg.sh
autorestart=true
startsecs=10
loglevel=info

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
  • scrapyd.conf
[scrapyd]
eggs_dir = eggs
logs_dir = logs
items_dir =
jobs_to_keep = 20
dbs_dir = dbs
max_proc = 0
max_proc_per_cpu = 4
finished_to_keep = 100
poll_interval = 5.0
bind_address = 0.0.0.0
http_port = 6800
debug = off
runner = scrapyd.runner
application = scrapyd.app.application
launcher = scrapyd.launcher.Launcher
webroot = scrapyd.website.Root

[services]
schedule.json = scrapyd.webservice.Schedule
cancel.json = scrapyd.webservice.Cancel
addversion.json = scrapyd.webservice.AddVersion
listprojects.json = scrapyd.webservice.ListProjects
listversions.json = scrapyd.webservice.ListVersions
listspiders.json = scrapyd.webservice.ListSpiders
delproject.json = scrapyd.webservice.DeleteProject
delversion.json = scrapyd.webservice.DeleteVersion
listjobs.json = scrapyd.webservice.ListJobs
daemonstatus.json = scrapyd.webservice.DaemonStatus

參考

  1. Dockerfile介紹
  2. 三個(gè)技巧,將 Docker 鏡像體積減小 90%
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容