一、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
步驟如下:
- 下載package.json中定義的依賴
- npm build生成編譯后的文件到dist
- 基礎(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