這部分介紹比較實用,在開中會常常用到,由於咱們會根據本身的項目來構建本身的鏡像,而後發佈。html
首先跑起來一個ubuntu,在ubuntu上安裝相應的軟件。java
docker run -it --name temp ubuntu /bin/bash
進來後,安裝一個apache2吧mysql
apt-get update && apt-get install apache2
使用dpkg -L來查看下apache2安裝的位置。在/usr/local/apache2就是咱們安裝的。nginx
開始以這個容器爲基礎來製做鏡像吧!git
docker commit -a "robinyang" temp robinyang0909/apache2:latest
好了,很快就會出現長uuid說明生成完成了。執行docker ps看看github
你確定有疑惑,開發要是這樣來爲應用製做鏡像的話,豈不是要瘋了,確實,我也這麼以爲,死難用!別慌,下面來看看第二種製做鏡像的方法。web
首先,在學習目錄下新建一個buildImage下新建一個Dockerfile文件(buildImage是一個docker上下文)。並在新建文件里加入下面指令:sql
FROM ubuntu:14.04 MAINTAINER robinyang RUN apt-get update && apt-get install nginx RUN echo "HelloWorld">/usr/share/nginx/html/index.html EXPOSR 80
保存退出,先來執行下:docker
docker build -t robinyang0909/nginx:latest .
上面指定不知道什麼意思嗎?第一節有操做過,固然,記不住啊!確實有時候忘記了,docker build --help 查看。shell
這裏說下原理,每執行一條指令都穿建立一個鏡像層,而且會commit提交,執行嚇一跳指定,就是基於剛纔的鏡像層來建立。
你會發如今你的鏡像列表裏多出一個你剛纔構建的鏡像。這樣的好,咱們能夠在咱們的項目的頂級目錄下新建一個Dockerfile來給你的應用定製鏡像。既然用Dockerfile來構建鏡像的話,咱們先來系統瞭解一下Dockerfile所須要的基本指令。
Dockerfile的第一條指令,後面指定一個基礎鏡像。你新建的鏡像是想基於那個基礎鏡像來構建的。必定要放在第一句。
FROM ubuntu:14.04
你能夠 docker search 去搜索你須要的鏡像。
指定這個鏡像的做者信息。
MAINTAINER robinyang robinyang_beijing@163.com
這個用的不少,就詳細說說。在構建鏡像的時候,在當前鏡像層裏執行這些shell腳本,而且會commit,構建成新一層鏡像。
RUN <command> //默認是使用/bin/sh 來執行 RUN ["", "", "", ...] //默認調用docker exec來執行,有的鏡像沒有sh
來看幾個例子
RUN touch /tmp/hello.html && echo hello>hello.html RUN /bin/bash -c "touch /tmp/hello.html && echo hello>hello.html" RUN ["/bin/bash", "-c", "touch /tmp/hello.html && echo hello>hello.html"] RUN ["touch", "/tmp/hello.html && echo hello>hello.html"]
這個很簡單,讓你的容器在運行的時候監聽哪一個端口。可是有一點要注意,他監聽的是容器的端口,而不是主機的端口,因此咱們建立容器的時候,要用 -p 來指定端口。
EXPOSE 8080
學了以上的幾個命令的話,如今能夠來個小練習。以tomcat爲基礎,加入一個hello.html頁面,並在頁面中加入一個helloWorld內容。
FROM tomcat MAINTAINER robinyang robinyang_beijing@163.com RUN touch /usr/local/tomcat/webapps/ROOT/hello.html RUN ["/bin/bash", "-c", "echo helloWorld>/usr/local/tomcat/webapps/ROOT/hello.html"] EXPOSE 8080
構建下
docker build -t robinyang09090/tomcat:latest .
鏡像製做完成了,如今能夠建立容器了
docker run -d -p 8080:8080 --name testTomcat robinyang09090/tomcat 【-p 宿主機:容器】
能夠用你的瀏覽器訪問下 host:8080/hello.html試試。
在上面的Dockerfile 構建文件中,咱們會發現不少有重複的,能夠用一個變量來帶代替重複的地方。改寫下上面Dockerfile
FROM tomcat MAINTAINER robinyang robinyang_beijing@163.com ENV rootPath=/usr/local/tomcat/webapps/ROOT/hello.html RUN touch $rootPath RUN ["/bin/bash", "-c", "echo helloWorld>$rootPath"] EXPOSE 8080
是否是簡潔不少。這裏ENV的書寫有兩種方式:
ENV key1=val1 key2=val2 //推薦 或者 ENV key1 val1 ENV key2 val2
這些變量會被持久保存到咱們建立的任何容器裏,因此咱們取的變量名要注意,別和系統自帶變量名衝突就行。
在容器啓動的時候執行的命令,RUN則是在構建鏡像的時候執行的命令,這裏要區分開。還記得咱們在以前容器裏執行循環,docker run
後面 /bin/sh -c "while [ true ]; do echo helloWorld; sleep 1; done"
至關於CMD的做用。
FROM ubuntu CMD ["/bin/sh", "-c", "while [ true ];do echo helloWorld; sleep 1;"]
//指定了本身的執行器, CMD ["executable","param1","param2"] //還有其餘的寫法 CMD ["param1","param2"] (as default parameters to ENTRYPOINT) CMD command param1 param2 (shell form)
注意:咱們在docker build -t 取鏡像名的時候,這裏要注意,字母只能所有小寫。給容器取名的能夠大寫。
能夠構建咱們的鏡像了
docker build -t robinyang0909/loop .
建立並運行容器
docker run -d --name testLoop robinyang0909/loop //接着打印日誌 docker logs testLoop
這個指令跟CMD有點相似,都是容器啓動成功開始運行執行腳本;但又不一樣的地方,CMD會被覆蓋,而ENTRYPOINT不會被覆蓋;ENTRYPOINT是一個基礎前綴指令。
ENTRYPOINT ["/bin/sh", "-c"] CMD ["java Hello"]
上面的會構成一個完整的指令/bin/sh -c java Hello
,CMD或者docker run後面的參數,都會傳入ENTRYPOINT,他來指定執行器,咱們這裏的執行器是/bin/bash -c
,通常咱們會ENTRYPOINT和CMD搭配起來一塊兒用。
上面ENTRYPOINT裏咱們指定了一個java Hello
運行咱們本身的jar包,可是有一個問題,你怎麼知道你的jar在哪裏。就算你知道目錄了,你難道要在Hello前拼接路徑嗎?這樣確實能夠,可是不優雅,這裏有更好的方法,就是WORKDIR。
WORKDIR指定一個工做目錄,這樣CMD和ENTRYPOINT就會在該目錄下執行咱們的程序。
FROM java ENV jarPath=/var/class/ ENTRYPOINT ["/bin/sh", "-c"] WORKDIR $jarPath CMD ["java Hello"]
構建鏡像後,run一個容器,啓動會失敗!別慌,還有一步沒有完成,等下一步完成後,就能夠了。
WORKDIR能夠屢次出現,意思就是切換目錄。
上面的例子跑不起來,還缺乏COPY一個Hello文件,接下來,將會拷貝一個文件到鏡像裏去。在Dockerfile同級目錄下新建一個tmp文件,生成一個Hello.class文件。
COPY tmp/Hello.class /var/class/
補充上面的,完整的是
FROM java ENV jarPath=/var/class/ ENTRYPOINT ["/bin/sh", "-c"] WORKDIR $jarPath COPY tmp/Hello.class $jarPath CMD ["java Hello"]
生成鏡像docker build -t roinyang0909/hello .
,建立啓動容器docker run -d --name hello robinyang0909/hello
,看下容器的日誌docker logs hello
發現咱們的java程序跑起來了。
上面的
CMD ["java Hello"]
千萬別寫成了CMD ["java", "Hello"]
系統會先執行java再執行Hello的,是分步執行的,很顯然只運行java指令就出錯。建議 一個""
就引發一個完整的指令。
上面COPY文件的時候,咱們只能拷貝上下文裏的文件,上下文以外的文件是無法拷貝成功的。什麼是上下文了,就是Dockerfile文件所在目錄及其子目錄,都是上下文。
這個指令,估計有點陌生,確實!首先咱們來談談容器的存儲機制,咱們知道,容器只能在最上面的讀寫層進行文件的讀寫,這樣作理論上沒有問題,可是你有沒有想過一個很嚴重的問題,我在這個容器裏寫了一些東西,下次升級,舊容器裏的文件是否是要拷貝出來,而後放入到新容器裏去,最典型的應用就是數據庫容器。如今咱們能夠將宿主機的一個目錄掛在到容器上,這樣能夠將數據放到宿主機上。
來吧!咱們用容器跑一個mysql
docker pull mysql //拉去mysql鏡像 docker run -d -p 3307:3306 --name mysql1 -e MYSQL_ROOT_PASSWORD=root mysql //建立啓動mysql容器
用SQLyog工具鏈接3307,密碼爲root ,沒有問題。咱們建立一個demo庫。docker stop mysql1
關閉容器,從新建立啓動一個mysql2的容器docker run -d -p 3307:3306 --name mysql1 -e MYSQL_ROOT_PASSWORD=root mysql
,再次鏈接,發現咱們剛纔建立的demo庫沒有了。而後你可能想到的解釋就是,mysql容器將數據放在了容器裏面,不一樣的容器數據固然不同。這樣理解,也不能算錯。其實數據是寫在宿主機上的,由於mysql這個鏡像指定了VOLUMN,https://github.com/docker-lib... 這是docker鏡像的製做github倉庫,在Dockerfile 裏能夠看到VOLUME /var/lib/mysql
已經掛在了/var/lib/mysql。
其實Dockerfile的VOLUMN掛在,只是掛在了/var/lib/docker/volumes/w75wew.../_date/這個下面的一個零食目錄裏的,能夠docker inspect mysql1
看到Mounts信息,掛在在Source指定的宿主機目錄下
name的一長串數字字符組成的id是惟一的,每次啓動一個Dockerfile指定了VOLUMN的容器,都會建立一個像這樣的目錄,因此新建容器就是一個新的,是沒有數據的。
容器刪除的時候,這些零時文件不會被刪除的。假設咱們沒有將這些目錄掛在到宿主機上,容器刪除了,數據還在。只不過尋找過程麻煩點,可是這也會是一個補救的機會。
小結: 在Dockerfile裏指定VOLUMN會映射到宿主機上,可是他會在/var/lib/docker/volumns/下生成一個惟一的目錄來作掛載。
能不能讓容器的掛在目錄映射到我指定的目錄下?固然能
docker run -d -p 3307:3306 --name mysql2 -e MYSQL_ROOT_PASSWORD=root -v /home/mysql/data/:/var/lib/mysql mysql //將容器的/var/lib/mysql映射到宿主機的/home/mysql/data/