Dockerfile構建鏡像

本節內容:node

  • Dockerfile介紹
  • Dockerfile指令簡單介紹
  • 示例1:構建Nginx鏡像
  • 示例2:構建一個Ruby運行環境
  • 構建緩存

 

1、Dockerfile介紹

dockerfile是構建鏡像的說明書。dockerfile提供了一種基於DSL語法的指令來構建鏡像,經過代碼化,鏡像構建過程一目瞭然,咱們能看出鏡像構建的每一步都在幹什麼。nginx

若要共享鏡像,咱們只須要共享dockerfile就能夠了。共享dockerfile文件,具備如下優勢:sql

  • 可重現
  • dockerfile能夠加入版本控制,這樣能夠追蹤文件的變化和回滾錯誤
  • dockerfile很輕量,咱們不須要copy幾百M甚至上G的docker鏡像

使用Dockerfile構建的流程:docker

  • 編寫Dockerfile
  • 執行docker build命令

 

2、Dockerfile指令簡單介紹

  • FROM:基礎鏡像。
  • MAINTAINER:維護者信息。
  • RUN:在須要執行的命令前加RUN。
  • ADD:ADD的文件和Dockerfile必須在同一個路徑下。
  • ENV:設置環境變量。
  • WORKDIR:至關於cd。
  • VOLUME:目錄掛載。
  • EXPOSE:映射端口。
  • ENTRYPOINT
  • CMD

 

1. RUN指令shell

RUN指令會在前一條命令建立的鏡像的基礎上啓動一個容器,並在容器中運行指令的命令。在該命令結束後,會提交容器爲新的鏡像。新鏡像會被dockerfile中的下一條指令所使用。vim

默認狀況下,RUN指令會以shell的形式去執行命令。固然咱們也可使用exec格式的RUN指令。在exec方式中,咱們使用一個數組來指定要運行的命令和傳遞給該命令的參數。centos

exec中的參數會當成什麼數組被docker解析,所以必須使用雙引號,而不能使用單引號。數組

shell形式:緩存

RUN yum install -y nginx

exec形式:安全

RUN ["yum", "install", "-y", "nginx"]

 

2. EXPOSE指令

告訴docker該容器內的應用程序將會指定端口,可是這並不意味着咱們能夠自動訪問該容器內服務的端口。出於安全的緣由,docker並不會自動打開該端口。而是須要咱們在使用docker run的時候經過-p參數指定須要打開哪些端口。咱們可使用多個EXPOSE指令向外部暴露多個端口。注意不能夠在dockerfile中指定端口映射關係。好比EXPOSE 80,這條指令是對的,而EXPOSE 8080:80這條指令就是錯的。

【注意】:這會影響docker的可移植性。咱們應該在docker run命令中使用-p參數實現端口映射。

 

3. CMD指令

CMD 指令容許用戶指定容器啓動的默認執行的命令。此命令會在容器啓動且 docker run 沒有指定其餘命令時運行。

  • 若是 docker run 指定了其餘命令,CMD 指定的默認命令將被忽略。
  • 若是 Dockerfile 中有多個 CMD 指令,只有最後一個 CMD 有效。

CMD 有三種格式:

Exec 格式:CMD ["executable","param1","param2"] 這是 CMD 的推薦格式。
CMD ["param1","param2"] 爲 ENTRYPOINT 提供額外的參數,此時 ENTRYPOINT 必須使用 Exec 格式。
Shell 格式:CMD command param1 param2

第一種格式:運行一個可執行的文件並提供參數。

第二種格式 CMD ["param1","param2"] 要與 Exec 格式 的 ENTRYPOINT 指令配合使用,其用途是爲 ENTRYPOINT 設置默認的參數。

第三種格式:是以」/bin/sh -c」的方法執行的命令。

 

下面看看 CMD 是如何工做的。Dockerfile 片斷以下:

CMD echo "Hello world"

運行容器 docker run -it [image] 將輸出:

Hello world

但當後面加上一個命令,好比 docker run -it [image] /bin/bash,CMD 會被忽略掉,命令 bash 將被執行:

[root@76a5fd777648 /]# 

【注意】:使用CMD指令時,最好使用數組語法。

 

4. ENTRYPOIN指令

ENTRYPOINT 看上去與 CMD 很像,它們均可以指定要執行的命令及其參數。不一樣的地方在於 ENTRYPOINT 不會被忽略,必定會被執行,即便運行 docker run 時指定了其餘命令。

實際上docker run命令行中指定的任何參數都會被看成參數再次傳遞給ENTRYPOINT指令中指定的命令。

ENTRYPOINT 有兩種格式:

Exec 格式:ENTRYPOINT ["executable", "param1", "param2"] 這是 ENTRYPOINT 的推薦格式。
Shell 格式:ENTRYPOINT command param1 param2

在爲 ENTRYPOINT 選擇格式時必須當心,由於這兩種格式的效果差異很大。

ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]

 

(1)Exec 格式

ENTRYPOINT 的 Exec 格式用於設置要執行的命令及其參數,同時可經過 CMD 提供額外的參數。

ENTRYPOINT 中的參數始終會被使用,而 CMD 的額外參數能夠在容器啓動時動態替換掉。

好比下面的 Dockerfile 片斷:

ENTRYPOINT ["/bin/echo", "Hello"]  
CMD ["world"]

當容器經過 docker run -it [image] 啓動時,輸出爲:

Hello world

而若是經過 docker run -it [image] CloudMan 啓動,則輸出爲:

Hello CloudMan

 

(2)Shell 格式

ENTRYPOINT 的 Shell 格式會忽略任何 CMD 或 docker run 提供的參數。

【注意】:使用CMD和ENTRYPOINT時,請務必使用數組語法。

 

【總結】:

  • 使用 RUN 指令安裝應用和軟件包,構建鏡像。
  • 若是想爲容器設置默認的啓動命令,可以使用 CMD 指令。用戶可在 docker run 命令行中替換此默認命令。
  • 若是 Docker 鏡像的用途是運行應用程序或服務,好比運行一個 MySQL,應該優先使用 Exec 格式的 ENTRYPOINT 指令。CMD 可爲 ENTRYPOINT 提供額外的默認參數,同時可利用 docker run 命令行替換默認參數。

 

3、示例1:構建Nginx鏡像

建立一個目錄單獨存放各類Dockerfile:

[root@node1 ~]# mkdir /opt/docker-file

建立Nginx目錄存放Nginx Dockerfile和相關文件:

[root@node1 ~]# mkdir nginx
[root@node1 ~]# cd nginx

上傳openresty-1.9.7.3.tar.gz到Nginx目錄下,編寫Dockerfile文件:

[root@node1 nginx]# vim Dockerfile

該Dockerfile內容以下:

# This dockerfile is for openresty
# Version 1.0

# Base images. 
FROM centos:centos7

# Author.
MAINTAINER jkzhao <01115004@wisedu.com>

# Add openresty software.
ADD openresty-1.9.7.3.tar.gz /usr/local

# Define working directory.
WORKDIR /usr/local/openresty-1.9.7.3

# Install epel repo
RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm

# Install openresty.
RUN yum install -y readline-devel pcre-devel openssl-devel gcc perl make 
RUN ./configure && gmake && gmake install
RUN sed -i '1 i\daemon off;' /usr/local/openresty/nginx/conf/nginx.conf
RUN sed -i 's@#error_log  logs\/error.log;@error_log logs\/error.log debug;@' /usr/local/openresty/nginx/conf/nginx.conf

# Define environment variables.
ENV PATH /usr/local/openresty/nginx/sbin:$PATH

# Define default command. 
CMD ["nginx"]

# Expose ports.
EXPOSE 80
Nginx dockerfile

利用該Dockerfile構建鏡像:

[root@node1 nginx]# docker build -t jkzhao/mynginx:v2 /opt/docker-file/nginx/

查看構建的鏡像:

[root@node1 nginx]# docker images
REPOSITORY                              TAG                 IMAGE ID            CREATED             SIZE
jkzhao/mynginx                          v2                  f61afc8ce858        17 minutes ago      581.6 MB

基於鏡像啓動容器:

[root@node1 nginx]# docker run -d -p 84:80 jkzhao/mynginx:v2  

訪問Nginx服務:

若是dockerfile中某條指令執行失敗了,沒有正常結束,那麼咱們也將獲得一個可使用的鏡像。這對調試很是有幫助,咱們能夠基於該鏡像,運行一個具備交互功能的容器,使用最後建立的鏡像對爲何咱們的指令會失敗進行調試。

 

4、示例2:構建一個Ruby運行環境

建立Ruby目錄存放Ruby Dockerfile和相關文件:

[root@node1 ~]# mkdir ruby
[root@node1 ~]# cd ruby

該Dockerfile內容以下:

FROM centos:7  

MAINTAINER jkzhao <01115004@wisedu.com>

# 爲鏡像添加備註信息
LABEL version="2.2.2" lang="ruby"

ENV RUBY_MAJOR 2.2
ENV RUBY_VERSION 2.2.2

RUN yum install -y wget tar gcc g++ make automake autoconf curl-devel openssl-devel zlib-devel httpd-devel apr-devel apr-util-devel sqlite-devel

RUN cd /tmp \
    && wget http://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.2.tar.gz \
    && tar zxvf ruby-2.2.2.tar.gz \
    && cd ruby-2.2.2 \
    && autoconf \
    && ./configure --disable-install-doc \
    && make \
    && make install \
    && rm -rf /tmp/ruby-2.2.2*

# skip installing gem documentation
RUN echo -e 'install: --no-document\nupdate: --no-document' >> "$HOME/.gemrc"

ENV GEM_HOME /usr/local/bundle
ENV PATH $GEM_HOME/bin:$PATH

ENV BUNDLER_VERSION 1.10.6

RUN gem install bundler --version "$BUNDLER_VERSION" \
    && bundle config --global path "$GEM_HOME" \
    && bundle config --global bin "$GEM_HOME/bin"

# don't create ".bundle" in all our apps
ENV BUNDLE_APP_CONFIG $GEM_HOME

CMD [ "irb" ]
Ruby dockerfile

 

5、構建緩存

dockerfile構建鏡像過程很是聰明,它會將每一步的構建結果提交爲一個鏡像,咱們能夠將以前指令建立的鏡像層看作緩存,好比第5條指令因爲命令書寫錯誤而致使構建失敗,當咱們修改它以後,前面的4條指令不會再執行,而是直接使用以前構建過程當中保存的緩存。這樣速度會提升不少,大大減小了構建的時間。

然而有時候咱們要確保構建過程當中不會使用緩存,好比對yum update等操做,要想跳過緩存,

docker build no-cache

緩存還有個好處,就是調試方便。好比第5條指令失敗了,那麼咱們能夠基於第4條指令建立的鏡像啓動一個新的容器。在容器裏手工執行第5條指令所要的操做,調查指令失敗的緣由。

相關文章
相關標籤/搜索