
Dockerfile-詳解
作者: weipeng.su 目前就職于攜程, 從事大資料開發工作
前面我分享了關于容器、虛拟機、實體機的差別(docker分享與入門), 還介紹了容器裡關鍵的鏡像、容器、倉庫等Docker的核心概念.容器的發展史(容器的發展曆史).虛拟機的搭建(Vagrant搭建虛拟機).本次我們具體聊聊Dockerfile.
分享目的:
•為什麼使用Dockerfile
•Dockerfile的常用文法
• 官網的MySQL的Dockerfile解析
一.為什麼使用Dockerfile
在docker分享與入門 介紹過關于鏡像建構的兩種方式,一種是推薦的Dockerfile,一種是進入容器中一步步安裝和配置我們想要的環境, 最後在通過docker 提供的docker commit指令,将該容器打包成一個鏡像.很多人還是不明白為什麼要使用Dockerfile.總結來說有以下幾點優勢.
•移植性
我們通過Dockerfile描述了, 我們的鏡像是如何一步步建構的.可以清晰的看到安裝了哪些軟體,其基于哪個開源鏡像建構.如果要将鏡像分享給别人, 你可以直接分享Dockerfile就行.Dockerfile就幾KB可遠遠比鏡像來得小.更加具備友善的移植和分享.
•安全性
如果有人不想通過Dockerfile來建構,想直接使用你提供的鏡像, 他可以檢視你的Dockerfile來确認是否該鏡像安全.
•維護性
如果公司中有人離職, 交接程式.一個Dockerfile就把所有的配置都說得清清楚楚.而不是隻給你個鏡像,出來問題,你也不知道是不是環境變量的原因,還是程式沖突,版本過久.
如果還認為Dockerfile不好使,可以自己閱讀下官方和流行的開源項目建構的鏡像, 都是提供了Dockerfile(比如上圖就是MySQL的官方提供的Dockerfile).基于這點和Dockerfile的優勢, 我們沒有理由不适用Dockerfile.
二.Dockfile的文法
2.1 FROM
•FROM用于Dockerfile最開頭,用來描述我們要建構的Image的BaseImage
如上圖的MySQL官網建構的是
FROM debian:buster-slim
說明其基于debian這個BaseImage建構的MySQL
2.2 Label
定義了image的中繼資料資訊.比如維護者、版本、這個image的作用.
LABEL maintainer='weipeng.su'LABEL version="1.0"LABEL description="This is ods image"
2.3 RUN(重點)
該指令用于執行,你要在容器裡執行各種指令,都可以通過該關鍵字來描述.比如安裝軟體、依賴庫.每RUN一次都會 産生一個新的層(Layer).是以RUN的最佳實踐就是将 多條指令合并成一條,使用&&符号.使用 反斜線換行.
RUN yum update && yum install -y vim \python-devRUN /bin/bash -c 'source $HOME/.bashrc;echo $HOME'
•RUN背後的原理
RUN背後其實會自動建立一個臨時容器,然後執行相應的指令,執行完後就會自動執行docker commit指令來生成新的鏡像.是以才會導緻m每RUN一次,都會産生新的一層(Layer).我們可以通過Docker建構鏡像時候的log檢視到這一點.
2.4 WORKDIR(重點)
設定目前的工作目錄.如果該目錄不存在會自動建立.
WORKDIR /testWORKDIR demoRUN pwd # 輸出/test/demo
改變工作目錄,不要使用RUN cd, 要知道每RUN一次就會生成新的一個層Layer,無形中鏡像會膨脹
2.5 ADD和COPY
這兩個關鍵字作用類似,都是用來 将本地的資源檔案、代碼添加到指定的容器目錄裡.唯一差別是, ADD有附加作用,對于壓縮檔案,其會自動解壓.
•大部分情況下我們使用最多的是COPY.•如果是遠端檔案,可以使用RUN curl或者RUN wget擷取.
2.6 Env(重點)
對于很多程式的部署,我們都需要設定環境變量,比如HADOOP的部署,我們需要設定HADOOP_HOME.Env關鍵字提供了該功能.
ENV MYSQL_VERSION 5.6RUN apt-get install -y mysql-server= "${MYSQL_VERSION}" \ && rm -rf /var/lib/apt/lists/*
2.7 VOLUME和EXPOSE(重點)
2.7.1 EXPOSE
expose主要 用來聲明容器中要對主控端開放的端口, 是 幫助鏡像使用者了解這個鏡像服務的守護端口, 以友善配置映射(使用
-p
這是小寫的p)
•expose的是一個聲明,表示容器将會監聽這個端口.是以就算expose了, 端口還是沒有啟動, 如果要實作主控端和容器的端口映射得用-p參數•Expose可以配合
-P
(這是大寫的P)參數使用, 實作随機從主控端挑選一個端口和Expose聲明的端口進行映射.
1.編寫如下Dockerfile
FROM busyboxEXPOSE 8080
2.編譯鏡像
docker build -t demo .
3.啟動容器
docker run -it -d --rm --name demo1 demo /bin/sh -c "sleep 600;""
•這句話的意思是啟動容器,并休眠10分鐘,這樣是為了讓容器跑10分鐘後會自動銷毀,移除容器.•--name demo1 表示容器啟動後就叫demo1. 後一個demo表示的是建立容器依據的demo這個鏡像.
4.檢視該容器的端口映射.
這時候這個容器啟動了,并且監聽着其自己的8080端口,但是這是無法被主控端通路的.
5.檢視該容器的IP
docker exec -it demo1 ifconfig
6.看下8080端口有沒有映射成功
telnet不通說明8080端口實際上并沒有和主控端的8080映射上.
6.使用-p參數進行端口映射
docker run -d -p 8080:8080 --rm --name demo2 demo /bin/sh -c "sleep 800"
然後telnet 127.0.0.1 8080可以看到此時端口已經通了.
總結
•expose的作用僅僅是為了聲明該容器提供服務的端口有哪些,友善使用者通過
docker run -p
進行端口映射.•如果想随機映射到主控端的端口,可以使用
docker run -P
2.7.2 VOLUME
•docker 為我們提供了三種不同的方式将資料挂載到容器中:data volume、bind mount、
tmpfs
.這樣容器銷毀或者退出,資料都不會丢失.
•Data Volume是Docker中資料持久化的最佳方式.
先檢視目前主控端下有哪些VOLUME docker volume ls
1.編寫如下Dockerfile
FROM busyboxVOLUME /home
2.建構鏡像
docker build -t demo:vol .
3.啟動容器
docker run -d --rm --name demo3 demo:vol /bin/sh -c "sleep 800"
4.進入容器的/home目錄建立檔案
建立完後,可以通過
docker volume ls
.看到主控端已經挂載上了該資料.
5.銷毀容器并再次啟動一個新容器
docker rm -f demo3docker run -d --rm --name demo4 -v 6374ab509553b368be41cc5b5cb5534b8f64dae34e48fe969b386bb3c8969c37:/home demo:vol /bin/sh -c "sleep 800"
檢視該容器的/home目錄
docker exec -it demo4 ls /home
可以發現text.txt檔案依然存在.
但是這樣挂載存在問題.就是這個卷名特别長.我們可以在容器啟動的時候就指定-v my_home:/home.這樣就會在主控端中建立一個叫my_home的卷, 以後就友善管理了.
如下圖示範的過程.我們還可以通過
docker volume inspect my_home
.檢視這個卷具所在的主控端位置.
2.8 CMD 和ENTRYPOINT(重點)
這兩個關鍵字主要用于在容器啟動後要執行哪些指令,一般用于程式的啟動.
2.8.1 Dockerfile裡的shell和exec格式
Dockerfile裡RUN、CMD、ENTRYPOINT都支援這兩種文法格式.
shell格式
RUN apt-get install -y vimCMD echo "hello docker"ENTRYPOINT echo "hello docker"
exec格式
RUN ["apt-get", "install", "-y", "vim"]CMD ["/bin/echo", "hello docker"]ENTRYPOINT ["/bin/echo", "hello/docker"]
差別
編寫如下兩個Dockerfile,然後建構出這兩個鏡像.
Dockerfile1
FROM centosENV name DockerENTRYPOINT echo "hello $name"
Dockerfile2
FROM centosENV name DockerENTRYPOINT ["/bin/echo","hello $name"]
建構鏡像
docker build -t entrypoint-shell .
docker build -t entrypoint-exec .
運作容器
第一個DK能列印出hello Docker.第二個DK列印出hello $name.
原因: 我們用shell格式,相當于是在shell中執行指令,是以其能識别出變量. 如果我們用exec格式, 我們隻是執行echo這個程式,而不是在shell中執行echo,是以無法識别環境變量.雖然exec格式預設是無法識别出環境變量, 但是如果我們指定雲運作時候是通過shell指令運作.也是可以繞過這個限制.
2.8.2 CMD
CMD所指定運作的指令會在容器啟動後執行.但可能會被忽略, 如果在
docker run -it centos:7 /bin/bash
後這樣指定了啟動後運作的/bin/bash就會被覆寫.
2.8.3 ENTRYPOINT
ENTRYPOINT可以保證指定的指令一定會被執行,而不會被外部指令覆寫.是以
•一般用在指定容器啟動後一定要運作的背景服務.
三.官網的MySQL Dockerfile解讀
FROM debian:buster-slim# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get addedRUN groupadd -r mysql && useradd -r -g mysql mysqlRUN apt-get update && apt-get install -y --no-install-recommends gnupg dirmngr && rm -rf /var/lib/apt/lists/*# add gosu for easy step-down from root# https://github.com/tianon/gosu/releasesENV GOSU_VERSION 1.12...RUN mkdir /docker-entrypoint-initdb.d...ENV MYSQL_MAJOR 8.0ENV MYSQL_VERSION 8.0.20-1debian10...RUN echo "deb http://repo.mysql.com/apt/debian/ buster mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list...VOLUME /var/lib/mysql# Config filesCOPY config/ /etc/mysql/COPY docker-entrypoint.sh /usr/local/bin/RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compatENTRYPOINT ["docker-entrypoint.sh"]EXPOSE 3306 33060CMD ["mysqld"]
如上是官網的MySQL Dockerfile.
•第一行就是描述基于哪個鏡像.•
RUN groupadd -r mysql && useradd -r -g mysql mysql
是在建立mysql這個使用者組,并且建立mysql使用者,将其添加到這個使用者組中.•
ENV MYSQL_VERSION 8.0.20-1debian10
. 設定了MYSQL的版本•
VOLUME /var/lib/mysql
該目錄是mysql存儲資料的目錄, 表示将mysql的存儲挂載到了主控端•
COPY config/ /etc/mysql/
由于該Dockerfile是官網提供的,放在了github上, 在這個開源項目裡官網已經建立了config這個檔案夾用來描述mysql的一些配置.該句意思就是 将這些配置拷貝入主控端器中的/etc/mysql/目錄下•
EXPOSE 3306 33060
表示将容器将會通過3306和33060提供服務, 如果想進行端口映射應該選映射到容器的這兩個端口•
CMD ["mysqld"]
表示容器啟動後,就會執行這個mysqld,這是mysql的服務端背景程序.