構建第一個應用
要構建的第一個應用是Jekyll框架的自定義網站。我們會構建一下兩個鏡像。
- 一個鏡像安裝Jekyll以及其他用於構建Jekyll網站的必要的軟件包。
- 一個鏡像通過Apache來讓Jekyll網站工作起來。
在啟動容器時,通過創建一個新的Jekyll網站來實現自服務。工作流程如下:
- 創建Jekyll基礎鏡像和Apache鏡像(只需要構建一次)。
- 從Jekyll鏡像創建一個容器,這個容器存放通過卷掛載的網址源代碼。
- 從Apache鏡像創建一個容器,這個容器利用包含編譯后的網站的卷,並為其服務。
- 在網站需要更新時,清理並重復上面的步驟。
可以把這個例子看做是創建一個多主機站點最簡單的方法。
構建Jekyll鏡像
創建Dockerfile:
# mkdir /opt/jekyll # cd /opt/jekyll # vim Dockerfile FROM ubuntu:latest MAINTAINER Bourbon Tian "bourbon@1mcloud.com" ENV REFRESHED_AT 2017-06-13 RUN apt-get -qq update RUN apt-get -qq install ruby ruby-dev build-essential nodejs RUN gem install --no-rdoc --no-ri jekyll -v 2.5.3 VOLUME /data VOLUME /var/www/html WORKDIR /data ENTRYPOINT [ "jekyll", "build", "--destination=/var/www/html" ]
鏡像基於Ubuntu:latest,並且安裝ruby和用於支持Jekyll的包。然后通過VOLUME指令創建了以下兩個卷。
- /data 用來存放網站的源代碼
- /var/www/html 用來存放編譯后的Jekyll網站碼
然后將工作目錄設置到/data/,並通過ENTRYPOINT指令指定自動構建的命令,這個命令會將工作目錄/data/中所有的Jekyll網站代碼構建到/var/www/html/目錄中。
構建Jekyll基礎鏡像
通過Dockerfile,可以使用docker build命令構建出可以啟動容器的鏡像。
# docker build -t test/jekyll . Sending build context to Docker daemon 1.691 MB Sending build context to Docker daemon Step 0 : FROM ubuntu:latest ---> db12a182ded0 Step 1 : MAINTAINER Bourbon Tian "bourbon@1mcloud.com" ---> 6c517b49846f ... Successfully built c8ea1e6c398b
這里就構建了一個名為test/jekyll、ID為c8ea1e6c398b的新鏡像。我們可以通過docker images命令來查看:
# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE test/jekyll latest c8ea1e6c398b About an hour ago 455.3 MB
構建Apache鏡像
創建Dockerfile:
# mkdir /opt/apache # cd /opt/apache # vim Dockerfile FROM ubuntu:latest MAINTAINER Bourbon Tian "bourbon@1mcloud.com" ENV REFRESHED_AT 2017-06-13 RUN apt-get -qq update RUN apt-get -qq install apache2 VOLUME [ "/var/www/html" ] WORKDIR /var/www/html ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_LOG_DIR /var/log/apache2 ENV APACHE_PID_FILE /var/run/apache2.pid ENV APACHE_RUN_DIR /var/run/apache2 ENV APACHE_LOCK_DIR /var/lock/apache2 RUN mkdir -p $APACHE_RUN_DIR $APACHE_LOCK_DIR $APACHE_LOG_DIR EXPOSE 80 ENTRYPOINT [ "/usr/sbin/apache2" ] CMD ["-D", "FOREGROUND"]
這個鏡像也是基於Ubuntu:latest,並且安裝了Apache。然后使用VOLUME指令創建了一個卷,即/var/www/html/,用來存放編譯后的Jekyll網站。然后將/var/www/html設為工作目錄。
使用ENV指令設置了一些必要的環境變量,創建了必要的目錄,並且使用EXPOSE公開了80端口。最后指定了ENTRYPOINT和CMD指令組合賴在容器啟動時默認運行Apache。
構建Apache鏡像
# docker build -t test/apache . Sending build context to Docker daemon 2.56 kB Sending build context to Docker daemon Step 0 : FROM ubuntu:latest ---> db12a182ded0 ... Successfully built f97bb19ef81c
這里構建了一個名為test/apache、ID為f97bb19ef81c的新鏡像。可以通過docker images查看:
# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE test/apache latest f97bb19ef81c About an hour ago 255.2 MB
啟動Jekyll網站
現在有了以下兩個鏡像。
- Jekyll:安裝了Ruby及其他必備軟件包的Jekyll鏡像。
- Apache:通過Apache Web服務器來讓Jekyll網站工作起來的鏡像。
我們從使用docker run命令來創建一個新的Jekyll容器開始我們的網站。我們將啟動容器,並構建我們的網站。我們將啟動容器,並構建我們的網站。
# cd /opt/jekyll # git clone https://github.com/turnbullpress/james_blog.git # docker run -v /opt/jekyll/james_blog:/data/ --name james_blog test/jekyll Configuration file: /data/_config.yml Source: /data Destination: /var/www/html Generating... done.
這里啟動了一個叫james_blog的新容器,把本地的james_blog目錄作為/data/卷掛載到容器里。容器已經拿到網站的源代碼,並將其構建到已編譯的網站,存放到/var/www/html/目錄。
卷是在一個或多個容器中特殊指定的目錄,卷會繞過聯合文件系統,為持久化數據提和共享數據提供幾個有用的特性:
- 卷可以在容器間共享和重用。
- 共享卷時不一定要運行相應的容器。
- 對卷的修改會直接在卷上反映出來。
- 更新鏡像時不會包含對卷的修改。
- 卷會一直存在,直到沒有容器使用它們。
利用卷,可以在不用提交鏡像修改的情況下,向鏡像里加入數據(如源代碼、數據或者其他內容),並且可以在容器間共享這些數據。卷在Docker宿主機的/var/lib/docker/volumes目錄中。可以通過docker inspect命令查看某個卷的具體位置。
# docker inspect -f "{{ .Volumes}}" james_blog map[/data:/opt/jekyll/james_blog /var/www/html:/var/lib/docker/volumes/6bf6fbf5ef4017c7aaab6f681b396f32f0f3878a3958f833e02973e0826ccafd/_data]
如果想在另一個容器里使用/var/www/html/卷里編譯好的網站,可以創建一個新的連接到這個卷的容器:
# docker run -d -P --volumes-from james_blog test/apache
- --volumes-from把指定容器里的所有卷都加入新創建的容器里。
這意味着,Apache容器可以訪問之前創建的james_blog容器里/var/www/html卷中存放的編譯后的Jekyll網站。即便james_blog容器沒有運行,Apache容器也可以訪問這個卷。
不過,容器本身必須存在。如果用docker rm命令刪除了james_blog容器,那么這個卷和卷里的內容也就不存在了。
現在在宿主機上瀏覽該網站,首先查看容器公開的80端口映射到了宿主機的哪個端口:
# docker port f39825fd8f61 80 0.0.0.0:32782
更新Jekyll網站
如果需要更新網站的數據,假設要修改Jekyll網站的博客名字,我們只需要通過編輯宿主機上 james_blog/_config.yml文件
# pwd /opt/jekyll/james_blog # vi _config.yml
並將title域改為Bourbon Blog。
然后通過使用docker start命令啟動Docker容器即可:
# docker start james_blog james_blog ## 看上去什么都沒有發生。我們來查看一下日志 # docker logs james_blog Configuration file: /data/_config.yml Source: /data Destination: /var/www/html Generating... done. Auto-regeneration: disabled. Use --watch to enable. Configuration file: /data/_config.yml Source: /data Destination: /var/www/html Generating... done. Auto-regeneration: disabled. Use --watch to enable.
可以看到,Jekyll編譯過程第二次被運行,並且往網站已經被更新。這次更新已經寫入了對應的卷。現在瀏覽Jekyll網站,就能看到變化了。
由於共享的卷會自動更新,這一切都不要更新或者重啟Apache容器。這個流程非常簡單,可以將其擴展到更復雜的部署環境。
備份Jekyll卷
如果擔心一不小心刪除卷。由於卷的優點之一就是可以掛載到任意的容器,因此可以輕松備份它們。現在創建一個新容器,用來備份/var/www/html卷
# docker run --rm --volumes-from james_blog -v $(pwd):/backup ubuntu tar cvf /backup/james_blog_backup.tar /var/www/html tar: Removing leading `/' from member names /var/www/html/ /var/www/html/History.markdown /var/www/html/atom.xml /var/www/html/pages.html ... # ls james_blog_backup.tar james_blog_backup.tar
這里我們運行了一個已有的Ubuntu容器,並把james_blog的卷掛載到該容器。這會在該容器里創建/var/www/html目錄。然后我們使用一個-v標志把當前目錄(通過$(pwd)命令獲得)掛載到容器的/backup 目錄。最后我們的容器運行這一備份命令。
- --rm 標志,這個標志對於只用一次的容器,或者說用完即扔的容器,很有用。這個標志在容器的進程運行完畢后,自動刪除容器。對於只用一次的容器來說,這是一種很方便的清理方法。