从零开始学习docker(零)简单介绍Dockerfile
如果不使用docker,面臨的問題有:
- 部署非常慢
 - 成本非常高
 - 資源浪費
 - 難于遷移和擴展
 - 可能會被限定硬件廠商
 
虛擬化技術的優點:
- 虛擬化技術出現以后,一個物理機可以部署多個App,每個App獨立運行在一個VM里。
 - 資源池,一個物理機的資源分配到了不同的虛擬機里
 - 容易擴展,加物理機或者虛擬機。
 - 很容易云化,亞馬遜AWS,阿里云等。
 
虛擬化技術的局限性:
- 每一個虛擬機都是一個完整的操作系統,要給其分配資源,當虛擬機數量增多時,操作系統本身消耗的資源勢必增多。
 
?docker 命令每次都需要使用sudo來執行,不太方便,如何去掉sudo?
? ? 2.?更新用戶組?
newgrp docker? ? 3. 重啟docker服務
iie4bu@hostdocker:~$ sudo service docker restart? ? ?4. 重新登錄終端即可,這樣就不需要加sudo了 ,非常方便
從零構建第一個hello world
目標:構建一個最簡單的hello world
新建目錄hello-world
第一步寫一個最簡單的c語言,新建hello.c
#include<stdio.h> int main() {printf("hello docker \n"); }編譯hello.c,輸出為hello
iie4bu@hostdocker:~/ddy/hello-world$ gcc -static hello.c -o hello在當前目錄下新建Dockerfile
FROM scratch ADD hello / CMD ["/hello"]Dockerfile是一個包含用于組合映像的命令的文本文檔。可以使用在命令行中調用任何命令。 Docker通過讀取Dockerfile中的指令自動生成映像。
Dockerfile文件說明與常用命令
Docker以從上到下的順序運行Dockerfile的指令。為了指定基本映像,第一條指令必須是FROM。一個聲明以#字符開頭則被視為注釋。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。
在這里列出了一些常用的說明
FROM
該指令用于設置后續指令的基本映像。有效的Dockerfile必須使用FROM作為其第一條指令。表示我們想要在哪個base image之上來build我們的image。例如我們FROM centos,表示我們想要在centos的基礎之上去build我們的image。
FROM scratch # 制作base image FROM centos # 制作base image FROM ubuntu:xenialscratch表示空鏡像。
盡量使用官方的image作為base image!(為了安全)
LABEL
可以為映像添加標簽來組織項目的映像。需要使用LABEL指令設置映像的標簽。定義了我們的metadata。
LABEL maintainer="vincent" LABEL version="1.0" LABEL description="This is description"RUN
該指令用于執行當前映像的任何命令。一般我們安裝一些軟件的時候需要使用RUN,
RUN yum update && yum install -y vim \python-dev # 反斜線換行 RUN apt-get update && apt-get install -y perl \pwgen --no-install-recommends && rm -rf \/var/lib/apt/lists/* # 注意清理cache RUN /bin/bash -c 'source $HOME/.bashrc;echo $HOME'RUN需要注意的是我們每運行一次RUN都會生成新的一層container,所以說對于RUN來講,為了避免無用的分層,合并多條命令成一行。為了美觀,復雜的RUN請用反斜線換行!
CMD
這用于執行映像的應用程序。應該以下列形式總是使用CMD -?
CMD ["executable", "param1", "param2"?]這是使用CMD的首選方法。Dockerfile文件中只能有一個CMD。如果使用多個CMD,則只會執行最后一個CMD。
ADD 和 COPY
ADD和COPY的作用很像,都是通過把本地的一些文件,添加到我們的docker image里面。例如我們把本地的hello的一個可執行文件拷貝到根目錄中,該指令用于將來自源的新文件或目錄復制到目的地的容器的文件系統。
ADD hello / #把本地的hello的一個可執行文件拷貝到根目錄中 ADD test.tar.gz / #添加到根目錄并解壓 COPY abc/ /xyzADD與COPY的區別是,ADD可以解壓縮。
ADD和COPY往往和WORKDIR一起使用。
WORKDIR /root ADD hello test #/root/test/hello大部分情況,COPY要比ADD優先去使用。ADD除了COPY以外,還有解壓縮的功能。如果要添加遠程文件/目錄請使用curl或者wget
規則
- source路徑必須在構建的上下文之內。無法使用COPY ../something /something,因為docker構建的第一步是將上下文目錄(和子目錄)發送到docker守護程序。
 - 如果source是目錄,則會復制目錄的全部內容,包括文件系統元數據。
 
ENV
通過設置環境變量來設置一個常量?
ENV MYSQL_VERSION 5.6 #設置常量 RUN apt-get install -y mysql-server="${MYSQL_VERSION}" \&& rm -rf /var/lib/apt/lists/* #引用常量WORKDIR
用于設定當前工作目錄的。與linux下面的cd命令很像。WORKDIR用于為Dockerfile中的RUN,CMD和COPY指令設置工作目錄。如果工作目錄不存在,它默認將會創建。
 我們可以在Dockerfile文件中多次使用WORKDIR。
使用WORKDIR,而不要使用RUN cd? 盡量使用絕對目錄!不要使用相對目錄。
RUN:執行命令并創建新的Image Layer
CMD:設置容器啟動后默認執行的命令和參數
ENTRYPOINT:設置容器啟動時運行的命令
CMD和ENTRYPOINT有什么區別呢?
在介紹CMD和ENTRYPOINT區別之前,先介紹Shell和Exce格式
Shell格式,把我們要運行的命令當成shell來執行,如下所示:
RUN apt-get install -y vim CMD echo "hello docker" ENTRYPOINT echo "hello docker"Exec格式,需要特定的格式去指明我們要運行的命令以及命令所跟的參數,如下所示:
RUN [ "apt-get", "install", "-y", "vim" ] CMD [ "/bin/echo", "hello docker" ] ENTRYPOINT [ "/bin/echo", "hello docker"]目前我們還不知道Shell和Exec有什么區別,下面來解釋,首先新建兩個Dockerfile
Dockerfile1:
FROM ubuntu:xenial ENV name Docker ENTRYPOINT echo "hello $name"Dockerfile2:
FROM ubuntu:xenial ENV name Docker ENTRYPOINT ["/bin/echo", "hello $name"]這兩個Dockerfile看起來功能是一樣的。
我們在命令行執行第一個Dockerfile
iie4bu@hostdocker:~/ddy/docker1$ docker build -t vincent/ubuntu-entrypoint-shell . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM ubuntu:xenial---> 5e8b97a2a082 Step 2/3 : ENV name Docker---> Running in 6be14dd0ab64 Removing intermediate container 6be14dd0ab64---> 0ebb88d446f9 Step 3/3 : ENTRYPOINT echo "hello $name"---> Running in acdad580ef15 Removing intermediate container acdad580ef15---> 37a8a7654a8f Successfully built 37a8a7654a8f Successfully tagged vincent/ubuntu-entrypoint-shell:latest然后我們查看我們的image
iie4bu@hostdocker:~/ddy/docker1$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE vincent/ubuntu-entrypoint-shell latest 37a8a7654a8f 17 seconds ago 114MB有了新的image,然后我們創建一個container。如下:
iie4bu@hostdocker:~/ddy/docker1$ docker run vincent/ubuntu-entrypoint-shell hello Docker iie4bu@hostdocker:~/ddy/docker1$然后我們將執行Exec格式的Dockerfile。然后build一個新的image如下:
iie4bu@hostdocker:~/ddy/docker2$ docker build -t vincent/ubuntu-entrypoint-exec . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM ubuntu:xenial---> 5e8b97a2a082 Step 2/3 : ENV name Docker---> Using cache---> 0ebb88d446f9 Step 3/3 : ENTRYPOINT ["/bin/echo", "hello $name"]---> Running in 01984afd1b19 Removing intermediate container 01984afd1b19---> 9d4f9618de5d Successfully built 9d4f9618de5d Successfully tagged vincent/ubuntu-entrypoint-exec:latest然后我們創建一個container。如下:
iie4bu@hostdocker:~/ddy/docker2$ docker run vincent/ubuntu-entrypoint-exec hello $name iie4bu@hostdocker:~/ddy/docker2$也就是說,它并沒有把我們$name替換成我們ENV中定義的常量。如何得到正確結果呢?需要修改Dockerfile,內容如下:
FROM ubuntu:xenial ENV name Docker ENTRYPOINT ["/bin/bash", "-c", "echo", "hello $name"]-c表示后面的是我們的參數
然后重新生成image,然后再次生成container,發現輸出結果為空:
iie4bu@hostdocker:~/ddy/docker2$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE vincent/ubuntu-entrypoint-exec latest eec8fd1389b2 30 seconds ago 114MB iie4bu@hostdocker:~/ddy/docker2$ docker run vincent/ubuntu-entrypoint-execiie4bu@hostdocker:~/ddy/docker2$原因是通過/bin/bash -c去執行命令的時候,要把后面所有的命令作為一個命令來執行。修改Dockerfile如下:
FROM ubuntu:xenial ENV name Docker ENTRYPOINT ["/bin/bash", "-c", "echo hello $name"]重新build 和 run 這樣結果就正常輸出了:
iie4bu@hostdocker:~/ddy/docker2$ docker build -t vincent/ubuntu-entrypoint-exec . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM ubuntu:xenial---> 5e8b97a2a082 Step 2/3 : ENV name Docker---> Using cache---> 0ebb88d446f9 Step 3/3 : ENTRYPOINT ["/bin/bash", "-c", "echo hello $name"]---> Running in 83332f6ed34f Removing intermediate container 83332f6ed34f---> 02dde803e324 Successfully built 02dde803e324 Successfully tagged vincent/ubuntu-entrypoint-exec:latest iie4bu@hostdocker:~/ddy/docker2$ docker run vincent/ubuntu-entrypoint-exec hello Docker iie4bu@hostdocker:~/ddy/docker2$CMD
- 容器啟動時默認執行的命令
 - 如果docker run指定了其他命令,CMD命令會被忽略
 - 如果定義了多個CMD,只有最后一個會執行
 
ENTRYPOINT
- 讓容器以應用程序或者服務的形式運行,例如啟動一個數據庫服務
 - 不會被忽略,一定會執行
 - 最佳實踐:寫一個shell腳本作為entrypoint
 
小技巧
? ? 如何刪除掉已經停止的container的image?
? ? 如果一個container已經停止,是無法直接刪除這個image的,需要先把這個container刪除掉。
命令:docker container rm $(docker ps -qa)
新建Dockerfile,內容如下:
FROM ubuntu:xenial ENV name Docker CMD echo "hello $name"構建這個image?
iie4bu@hostdocker:~/ddy/docker3$ docker build -t vincent/ubuntu-cmd-shell . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM ubuntu:xenial---> 5e8b97a2a082 Step 2/3 : ENV name Docker---> Using cache---> 0ebb88d446f9 Step 3/3 : CMD echo "hello $name"---> Running in a06dce28b737 Removing intermediate container a06dce28b737---> 04b36269a3c8 Successfully built 04b36269a3c8 Successfully tagged vincent/ubuntu-cmd-shell:latest查看我們現在有哪些image?
iie4bu@hostdocker:~/ddy/docker3$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE vincent/ubuntu-cmd-shell latest 04b36269a3c8 About a minute ago 114MB vincent/ubuntu-entrypoint-exec latest 02dde803e324 20 minutes ago 114MB vincent/ubuntu-entrypoint-shell latest 37a8a7654a8f 36 minutes ago 114MB我們執行ubuntu-cmd-shell
iie4bu@hostdocker:~/ddy/docker3$ docker run vincent/ubuntu-cmd-shell hello Docker iie4bu@hostdocker:~/ddy/docker3$可以正常執行。如果我們用-it,那么就不會打印輸出hello Docker了
iie4bu@hostdocker:~/ddy/docker3$ docker run -it vincent/ubuntu-cmd-shell /bin/bash root@bf0f8f7f6de1:/#然而,我們使用ubuntu-entrypoint-shell就不會出現這種情況,依然會打印輸出hello Docker
iie4bu@hostdocker:~/ddy/docker3$ docker run -it vincent/ubuntu-entrypoint-shell /bin/bash hello Docker iie4bu@hostdocker:~/ddy/docker3$EXPOSE
會把我們運行的container中的端口暴露出來。
構建Dockerfile
iie4bu@hostdocker:~/ddy/hello-world$ docker build -t ddy/hello-world . Sending build context to Docker daemon 916.5kB Step 1/3 : FROM scratch---> Step 2/3 : ADD hello /---> d1a03c513631 Step 3/3 : CMD ["/hello"]---> Running in 7d10f24a5f54 Removing intermediate container 7d10f24a5f54---> 9fe0d93646ea Successfully built 9fe0d93646ea Successfully tagged ddy/hello-world:latest iie4bu@hostdocker:~/ddy/hello-world$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ddy/hello-world latest 9fe0d93646ea 12 seconds ago 913kB使用docker history imageID可以查看鏡像的層級
iie4bu@hostdocker:~/ddy/hello-world$ docker history 9fe0d93646ea IMAGE CREATED CREATED BY SIZE COMMENT 9fe0d93646ea About a minute ago /bin/sh -c #(nop) CMD ["/hello"] 0B d1a03c513631 About a minute ago /bin/sh -c #(nop) ADD file:ac94667f27379ec68… 913kB運行鏡像
iie4bu@hostdocker:~/ddy/hello-world$ docker run ddy/hello-world hello docker說明鏡像構建好了。
container
container是通過Image構建,是在image layer(只讀)之上建立的container layer(可讀可寫)。好比是java中類與對象之間的關系。image負責App的存儲和分發,container負責運行App。
使用docker run IMAGENAME 可以創建一個container。默認不寫tag會指定latest版本
iie4bu@hostdocker:~/ddy/hello-world$ docker run ddy/hello-world列舉正在運行的container:
iie4bu@hostdocker:~$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES可以發現什么都沒有,因為hello-world container運行完hello docker后就退出了,不是一個常駐內存的進程。可以使用docker container ls -a來列舉出所有容器包括正在運行的和退出的。
iie4bu@hostdocker:~$ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6d9bcf87eac4 ddy/hello-world "/hello" About an hour ago Exited (0) About an hour agoDockerfile中的CMD就是這里的COMMAND命令。
交互式運行容器
命令:docker run -it hello-word
例如我們有一個ubuntu的景象:
iie4bu@hostdocker:~/ddy/hello-world$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu xenial 5e8b97a2a082 12 months ago 114MB嘗試交互式運行ubuntu:
iie4bu@hostdocker:~$ docker run -it ubuntu:xenial root@51f52b351e2c:/# ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var查看當前container運行:
iie4bu@hostdocker:~/ddy/hello-world$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 51f52b351e2c ubuntu:xenial "/bin/bash" 44 seconds ago Up 43 seconds zealous_spence當我們退出ubuntu鏡像時,使用exit命令,這時docker container ls就不會有這個鏡像了,使用docker container ls -a可以看到剛剛的鏡像已經退出了。
iie4bu@hostdocker:~$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES iie4bu@hostdocker:~$ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 51f52b351e2c ubuntu:xenial "/bin/bash" 6 minutes ago Exited (127) 13 seconds ago zealous_spence我們每創建一個容器,都會在我們之前的image之上多了一層。
docker container ls -a 與 docker ps -a 命令是一樣的。
刪除一個container命令:
iie4bu@hostdocker:~$ docker container rm 51f52b351e2c 51f52b351e2c實際上使用命令docker rm?51f52b351e2c 默認就是刪除一個container
docker image rm imageID 等同于 docker rmi imageID
docker image ls 等同于 docker images
docker container ls 等同于 docker ps
?docker container commit
這條命令的意思是基于某個image創建一個container,然后我們在這個container里面做一些變化,例如安裝了一些軟件,然后我們可以把已經改變的container給commit一個新的image。這個命令一般可以簡寫成docker commit
我們先運行一個ubuntu的鏡像:
iie4bu@hostdocker:~$ docker run -it ubuntu:xenial root@803945a2c2d1:/# vim bash: vim: command not found root@803945a2c2d1:/#這個鏡像里面沒有vim命令,然后我們安裝vim
root@803945a2c2d1:/# apt-get update root@803945a2c2d1:/# apt-get install vim安裝vim成功后,我們將這個container變為image
退出當前容器。查看當前容器:
root@803945a2c2d1:/# exit exit iie4bu@hostdocker:~$ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 803945a2c2d1 ubuntu:xenial "/bin/bash" 7 minutes ago Exited (0) 8 seconds ago sharp_turing將當前container commit為一個新的image:
iie4bu@hostdocker:~$ docker commit sharp_turing vincent/ubuntu-vim sha256:697cdaba05dc77867ea5067b9d729b78f91aa05cbed872897d5e28cc40decd21 iie4bu@hostdocker:~$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE vincent/ubuntu-vim latest 697cdaba05dc 22 seconds ago 213MB我們看到我們自己的image已經生成了。我們查看這個image與之前ubuntuimage的層級結構有什么不同。
iie4bu@hostdocker:~$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE vincent/ubuntu-vim latest 697cdaba05dc 22 seconds ago 213MB ddy/hello-world latest 9fe0d93646ea 5 hours ago 913kB <none> <none> 37705341d15d 2 months ago 643MB prom/prometheus latest 0694fc214c9f 2 months ago 109MB dgraph/dgraph <none> a3b60a6ee9a7 3 months ago 144MB hello-world latest fce289e99eb9 5 months ago 1.84kB tomcat latest 78b258e36eed 7 months ago 463MB <none> <none> 0e2ca16d4311 11 months ago 45.4GB ubuntu xenial 5e8b97a2a082 12 months ago 114MB docker.elastic.co/elasticsearch/elasticsearch 6.2.3 d8d340eb391b 15 months ago 542MB java 8 d23bdf5b1b1b 2 years ago 643MB iie4bu@hostdocker:~$ docker history 5e8b97a2a082 IMAGE CREATED CREATED BY SIZE COMMENT 5e8b97a2a082 12 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 12 months ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B <missing> 12 months ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 2.76kB <missing> 12 months ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B <missing> 12 months ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B <missing> 12 months ago /bin/sh -c #(nop) ADD file:d37ff24540ea7700d… 114MB iie4bu@hostdocker:~$ docker history 697cdaba05dc IMAGE CREATED CREATED BY SIZE COMMENT 697cdaba05dc 2 minutes ago /bin/bash 99.3MB 5e8b97a2a082 12 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 12 months ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B <missing> 12 months ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 2.76kB <missing> 12 months ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B <missing> 12 months ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B <missing> 12 months ago /bin/sh -c #(nop) ADD file:d37ff24540ea7700d… 114MB我們看到新的image與5e8b97a2a082的image是一樣的,我們只是在5e8b97a2a082的基礎上加了一些東西。
這種方式創建image一般并不提倡,因為我們如果發布出去這個image,別人拿到這個image并不知道這個image是怎么產生的,很可能會把不安全的東西放到image中發布出去,這樣就有安全問題。
我們一般提倡使用Dockerfile方式。
docker image build
可以簡寫為docker build。
我們先將上面的ubuntu-vim鏡像刪除掉:
iie4bu@hostdocker:~$ docker image rm 697 Untagged: vincent/ubuntu-vim:latest Deleted: sha256:697cdaba05dc77867ea5067b9d729b78f91aa05cbed872897d5e28cc40decd21 Deleted: sha256:91ddbb337bcf4e6a6a13b3be81a86b195fbac5e66823c6be625a8cd46ffdab32我們新建目錄docker-ubuntu-vim,進入這個目錄中新建Dockerfile,內容如下:
FROM ubuntu:xenial RUN apt-get update RUN apt-get install -y vimFROM: 必不可少的命令,從某個鏡像作為基。如 FROM <image_name> ,或者 FROM <image_name>:<tag>. 如果不加tag,默認為latest。先從本地鏡像倉庫去搜索基鏡像,如過本地沒有,在去網上docker registry去尋找。
apt-get install -y vim 如果不加-y,那么會被中斷,導致安裝不成功。
iie4bu@hostdocker:~/ddy/docker-ubuntu-vim$ docker build -t mydocker_id/ubuntu-vim-new . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM ubuntu:xenial---> 5e8b97a2a082 Step 2/3 : RUN apt-get update---> Running in 37b5a57504d0我們看到直接使用了ubuntu本地的image layer,5e8b97a2a082
在build的過程中,生成了一個臨時的container,這個container id為37b5a57504d0,隨后會把這個container刪除掉,這一過程會在build的整個階段可能會執行很多次。
查看image:
iie4bu@hostdocker:~/ddy/docker-ubuntu-vim$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE mydocker_id/ubuntu-vim-new latest ddad98db69ba 8 minutes ago 213MB?
總結
以上是生活随笔為你收集整理的从零开始学习docker(零)简单介绍Dockerfile的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: docker安装配置分布式elastic
 - 下一篇: 从零开始搭建spring-cloud(2