經過 DockerFile 打包鏡像

在介紹 Docker 具體的操做前,先簡要複習下 Docker 的架構,這樣能夠更好地幫助咱們理解 Docker 中的各個命令。html

首先咱們一直對 Docker 這個叫法就有些誤解,Docker 其實指代的是用於開發,部署,運行應用的一個平臺。日常中說的 Docker 準確來講是 Docker Engine.python

Docker Engine 是一個 C/S 架構的應用。其中主要的組件有:git

  • Docker Server:長時間運行在後臺的程序,就是熟悉的 daemon 進程.
  • Docker Client: 命令行接口的客戶端。
  • REST API: 用於和 daemon 進程的交互。

咱們經過給 Docker Client 下發各類指令,而後 Client 經過 Docker daemon 提供的 REST API 接口進行交互,來讓 daemon 處理編譯,運行,部署容器的繁重工做。 大多數狀況下, Docker Client 和 Docker Daemon 運行在同一個系統下,但有時也可使用 Docker Client 來鏈接遠程的 Docker Daemon 進程,也就是遠程的 Server 端。docker

清楚了 Docker 的簡單架構,就能夠了解下具體的命令了。shell

Docker 鏡像構建指令

指令1:構建鏡像

docker build [OPTIONS] PATH | URL | -

Docker 構建鏡像的上下文。數據庫

這裏的上下文指的就是命令的最後一個參數 PATH | URL| -,具體來講就 docker build . 中的 . 不少人覺得這個 . 是 DockerFile 的位置,其實否則,準確來講是構建上下文的位置。前面說到 Docker 是 C/S 架構,在 Client 端下發具體的命令,在 Server 端(Daemon)執行具體的內容。這也就意味着,構建鏡像的過程實際上是在 Server 端完成的。而上下文的出現,就是爲了把須要的內容傳遞給 Server,這也就爲何在每次構建時都能看到這樣一句話。centos

[root@localhost python_shell]# docker build --rm -t temp/python-test .
Sending build context to Docker daemon  4.608kB

這裏的 Sending 其實就是把本地 Client 端的文件內容,拷貝到 Server 端。而許多初學者,在 DockerFile 中寫出了 COPY /opt/xxxx /app 這樣的話,其實就是沒有理解上下文的概念,並不知道在 Server 端是沒有 opt/xxxx 的文件的。緩存

還有的人將 Docker File 放在硬盤根目錄執行,卻不知,這樣會將根目錄全部的文件都拷貝到 Server 端,形成構建極其緩慢。服務器

Options 經常使用參數:網絡

  • -t: 打包出鏡像的名稱及標籤,一般寫法爲 name:tag
  • --rm: 構建成功後,刪除中間產生的容器。
  • --force-rm=true: 不管是否構建成功,都刪除中間產生的容器
  • --no-cache: 構建鏡像時不使用緩存。
  • -f: 指定 DockerFile 的路徑
docker build --no-cache --force-rm -t --no-cache local/centos7:v1 .

DockerFile 指令及編寫規範

指令1:指定基礎鏡像

經過 FROM 來制定基礎鏡像,命令很簡單,但有一點須要注意的是,必定確切指定基礎鏡像的版本,而不是寫成 latesst, 由於隨着時間推移,官方的最新鏡像都會一直更新,這樣就會形成沒法構建的狀況。

FROM centos:7 # That's perfect!
FROM centos:latest # That's so bad!

指令2:容器中執行命令

RUN 命令用於在容器中執行命令行的命令。通常有兩種寫法:

  • shell 形式:

RUN 後面直接跟 shell 命令就能夠了。切記,在 shell 形式下,不要把命令拆成多行 RUN。由於每一次的 RUN 都會構建一層新的鏡像,保存了不少沒有用的運行信息。並且 Union FS 是由最大層數限制的。因此儘可能將命令合成一行。

RUN yum -y install httpd; yum clean all; systemctl enable httpd.service # That's perfect!

RUM yum -y install httpd / # Another perfect solution!
    yum clean all /
    systemctl enable httpd.service 

RUN yum -y install httpd # That's so bad!
RUN yum clean all;
....

還有一點須要注意,把構建時沒用的依賴包想着清空。不然的話,隨着鏡像的重複構建,保存了大量的沒有信息。

  • exec 寫法

exec 寫法更像函數調用中的格式。

RUN ["可執行文件", "參數1", "參數2"]

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

指令3:設置工做目錄

WORKDIR 用於改變各層的工做目錄(也就是進入容器內的默認目錄),若是指定的目錄不存在就會建立它。工做目錄在構建過程當中,能夠被各層都訪問到。

WORKDIR /src

指令4:設置匿名卷

在容器運行時,儘可能對容器的存儲層不進行寫操做,對於像數據庫中這樣動態的數據文件應該用 VOLUME 來保存。而在 DockerFile VOLUME 能夠將目錄指定爲匿名卷。這樣在運行時,若是沒有掛載指定的目錄,並不會像容器的存儲層寫入數據,保證存儲層的無狀態化。

VOLUME /data

開啓 Systemd Centos7 鏡像

官方 Centos7 的鏡像已經包含了 systemd 的功能,只是沒有開啓。這裏只須要以其爲基礎鏡像,打開 systemd 的功能便可。須要注意的是,打開 systemd 須要在運行時開啓特權以掛載 Cgroup 等內容。

編寫 DockerFile

[root@localhost docker_images]# cat Dockerfile
FROM centos:7

ENV container docker

RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

VOLUME [ "/sys/fs/cgroup" ]

CMD ["/usr/sbin/init"]
  • VOLUME 指定了匿名目錄,不會在存儲層保存該目錄的內容,適用於動態變化等持久性文件。
  • CMD 保證容器啓動時開啓 systemd

打包鏡像

docker build --rm -t local/c7-systemd .
  • --rm: 表示刪除打包時臨時的容器

運行鏡像

docker run --privileged=true -ti -v /sys/fs/cgroup:/sys/fs/cgroup:ro local/c7-systemd
  • --privileged: 給予容器特殊的權限,來掛載 Cgroup 等。

Httpd 鏡像

下面基於上面開啓 systemd 的鏡像爲基礎,打包 Httpd 鏡像。

編寫 DockerFile

FROM local/c7-systemd

RUN yum -y install httpd; yum clean all; systemctl enable httpd.service

EXPOSE 80
  • 下載依賴包後,刪除沒有用的內容,是編寫 DockerFile 好習慣。

編譯 DockerFile

docker build --rm -t local/c7-systemd-httpd .

運行 Container

docker run --privileged=true -ti -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-p 80:80 local/c7-systemd-httpd

在下載依賴時出現網絡問題,請查看 Docker 代理 這篇文章。

Python 鏡像

有時咱們須要在容器中運行 python 腳本,下面來打包相似的鏡像。在下載須要的依賴時,一般的服務器並沒訪問公網的能力,這時須要爲容器配置配置代理,在下載依賴後,有時因爲代理的緣由,致使內部的服務器沒法訪問,這時能夠再將設置的代理清空。

建立文件

注意,這裏的文件要和 DockerFile 在同級目錄下。

# 建立 requirements 文件保存依賴
[root@localhost home]# cat python_shell/requirements.txt
requests==2.21.0

# 編寫 Python 腳本
[root@localhost home]# cat python_shell/success.py
print("python Running", "!");

編寫 DockerFile

FROM python:3.6.8

# set proxy
ENV MY_PROXY_URL="http://173.39.112.117:80"
ENV HTTP_PROXY=$MY_PROXY_URL \
    HTTPS_PROXY=$MY_PROXY_URL \
    FTP_PROXY=$MY_PROXY_URL \
    http_proxy=$MY_PROXY_URL \
    https_proxy=$MY_PROXY_URL \
    ftp_proxy=$MY_PROXY_URL

WORKDIR /src

COPY . .

RUN ["pip", "install", "--no-cache-dir",  "-r",  "./requirements.txt"]

# clear the proxy
ENV MY_PROXY_URL=
ENV HTTP_PROXY=$MY_PROXY_URL \
    HTTPS_PROXY=$MY_PROXY_URL \
    FTP_PROXY=$MY_PROXY_URL \
    http_proxy=$MY_PROXY_URL \
    https_proxy=$MY_PROXY_URL \
    ftp_proxy=$MY_PROXY_URL


CMD ["python", "./success.py"]

打包鏡像

docker build --rm -t temp/python-test .

運行鏡像

[root@localhost home]# docker run temp/python-test
python Running !

參考

docker-overview

docker-centos-image

docker-python-image

https://blog.fundebug.com/2017/05/15/write-excellent-dockerfile/

dockerfile_best-practices

docker-chinese-reference

相關文章
相關標籤/搜索