Docker學習筆記六 使用Docker構建服務

6.1 使用Jekyll框架和Apache構建應用html

須要構建兩個鏡像:node

  • 一個鏡像安裝了Jekyll以及其餘用於構建Jekyll網站的必要的軟件包;
  • 一個鏡像經過Apache來讓Jekyll網站工做起來。

工做流程以下:python

  1. 建立Jekyll基礎鏡像和Apache鏡像;
  2. 從Jekyll鏡像建立一個容器,這個容器存放經過卷掛載的網站源代碼;
  3. 從Apache鏡像建立一個容器,這個容器利用包含編譯後的網站的卷爲其建立服務;
  4. 在網站須要更新時,清理並重覆上面的步驟。

這個例子能夠看作是建立一個多主機站點最簡單的方法。git

一、Jekyll基礎鏡像github

創建構建環境:web

mkdir jekyll
cd jekyll
touch Dockerfile

編寫Dockerfile:redis

FROM ubuntu:14.04
MAINTAINER James Turnbull <james@example.com>

RUN apt-get -yqq update
RUN apt-get -yqq install ruby ruby-dev make 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" ]

注意書上的代碼 gem jekyll 後面沒有 -v 2.5.3,會報錯。docker

在Dockerfile中使用VOLUME指令建立了兩個卷:apache

  • /data/,用於存放網站源代碼;
  • /var/www/html/,用於存放編譯後的Jekyll網站碼。

最後,將工做目錄指定爲/data,並經過ENTRYPOINT指令指定自動構建的命令,這個命令將工做目錄/data/中全部的Jekyll網站代碼構建到/var/www/html/目錄中。npm

二、構建Jekyll基礎鏡像

docker build -t ivan/jekyll .

三、Apache鏡像

創建構建環境:

mkdir apache
cd apache
touch Dockerfile

編寫Dockerfile:

FROM ubuntu:14.04
MAINTAINER James Turnbull <james@example.com>

RUN apt-get -yqq update
RUN apt-get -yqq 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"]

使用了VOLUME指令建立了一個卷——/var/www/html/,這個目錄是用來存放編譯後的jekyll網站的;

最後指定了ENTRYPOINT和CMD指令組合來在容器啓動時默認運行apache。

四、構建Apache鏡像

docker build -t ivan/apache .

五、啓動Jekyll網站

首先將Jekyll的一些源代碼下載:

cd ~
git clone https://github.com/jamtur01/james_blog.git

而後啓動容器:

docker run -v ~/james_blog:/data/ --name james_blog ivan/jekyll

把剛纔從github上下載的james_blog目錄做爲卷掛載到/data(目錄裏有jekyll網站的源碼)。

這裏再複習一下卷,卷是在一個或多個容器中特殊指定的目錄,卷會繞過聯合文件系統,爲持久化數據和共享數據提供幾個有用的特性:

  • 卷能夠在容器間共享和共用;
  • 共享卷時不必定要運行相應的容器;
  • 對卷的修改會直接在捲上反映出來;
  • 更新鏡像時不會包含對卷的修改;
  • 卷會一直存在,直到沒有容器使用它們。

卷在Docker宿主機的/var/lib/docker/volumes目錄中,經過docker inspect命令能夠查看某個卷的具體位置:

docker inspect -f "{{.Volumes}}"

若是想在另外一個容器裏使用/var/www/html/卷裏編譯好的網站,能夠建立一個新的容器鏈接到這個卷(即Apache容器):

docker run -d -P --volumes-from james_blog ivan/apache

--volumes-from標誌把指定容器裏全部的卷加入新建的容器裏。

若是刪除了最後一個使用卷的容器,卷就被刪除了。因此刪除有卷的容器時要當心。

這樣整個服務就啓動好了。

六、備份Jekyll卷

上文說道刪除容器可能不當心將卷刪除了。因此能夠利用一下命令備份卷:

docker run --rm --volumes-from james_blog -v $(pwd):/backup ubuntu tar cvf /backup/james_blog_backup.tar /var/www/html

--rm命令表示該容器只使用一次,運行完後就刪除。

將當前目錄掛載到/backup,這樣在容器中將數據打包到/backup後,該包其實就在當前目錄下。

tar cvf命令會建立一個名爲james_blog_backup.tar的tar文件(該文件包含了/var/www/html目錄裏的全部內容)。

七、擴展Jekyll示例

  • 運行多個Apache容器,這些容器都使用james_blog容器的卷,在這些Apache容器前面加一個負載均衡器,就擁有了Web集羣;
  • 進一步構建一個鏡像,這個鏡像把用戶提供的源數據複製到卷裏,再把這個卷掛載到jekyll容器裏。這就是一個可遷移的通用方案,並且宿主機本地包含任何源代碼;
  • 在上一個擴展基礎上爲咱們的服務構建一個Web前段,這個服務用語從指定的源自動構建和部署網站。

6.2 構建一個Java應用服務(Tomcat)

一、WAR文件獲取器

建立構建環境

mkdir fetcher
cd fetcher
touch Dockerfile

編寫Dockerfile

FROM ubuntu:14.04
MAINTAINER Ivan Jiang <ivanjz93@163.com>
ENV REFRESHED_AT 2016-07-13
 
RUN apt-get -yqq update
RUN apt-get -yqq install wget
 
VOLUME ["/var/lib/tomcat7/webapps/"]
WORKDIR /var/lib/tomcat7/webapps/
 
ENTRYPOINT ["wget"]
CMD ["--help"]

容器執行時,使用wget從指定的URL獲取文件,並把它保存在/var/lib/tomcat7/webapps目錄。若是運行容器時沒有指定URL,ENTRYPOINT和CMD指令組合起來返回wget的幫助。

二、獲取WAR文件

docke run -t -i --name sample ivan/fetcher https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war

三、構建Tomcat7應用服務器

建立構建環境:

mkdir tomcat7
cd tomcat7
touch Dockerfile

編寫Dockerfile:

FROM ubuntu:14.04
MAINTAINER Ivan Jiang <ivanjz93@163.com>
ENV REFRESHED_AT 2016-07-13
 
RUN apt-get -yqq update
RUN apt-get -yqq install tomcat7 default-jdk
 
ENV CATALINA_HOME /usr/share/tomcat7
ENV CATALINA_BASE /var/lib/tomcat7
ENV CATALINA_PID /var/run/tomcat7.pid
ENV CATALINA_SH /usr/share/tomcat7/bin/catalina.sh
ENV CATALINA_TMPDIR /tmp/tomcat7-tomcat7-tmp
 
RUN mkdir -p $CATALINA_TMPDIR
 
VOLUME ["/var/lib/tomcat7/webapps/"]
 
EXPOSE 8080
 
ENTRYPOINT ["/usr/share/tomcat7/bin/catalina.sh", "run"]

構建鏡像:

docker build -t ivan/tomcat7 .

四、運行tomcat7容器

docker run --name sample_app --volumes-from sample -d -P ivan/tomcat7

查看映射到宿主機的端口號:

docker port sample_app 8080

假設端口號是49154,在瀏覽器訪問:IP地址:49154/sample可查看運行的web app。

在例子中做者使用了命令:

docker inspect -f "{{.Volumes}}" sample

查看sample卷的宿主機掛載位置,可是這個命令報錯。

OSChina上的TimWang回答個人問題說Docker 1.8將Volumes信息從docker inspect 的輸出中刪除了,需改用Mounts:

docker inspect -f "{{.Mounts}}" sample

6.3 多容器的應用棧

一、Node.js鏡像

建立構建環境:

mkdir nodejs
cd nodejs
touch Dockerfile

編寫Dockerfile:

FROM ubuntu:14.04
MAINTAINER James Turnbull <james@example.com>
ENV REFRESHED_AT 2014-06-01

RUN apt-get -yqq update
RUN apt-get -yqq install nodejs npm
RUN ln -s /usr/bin/nodejs /usr/bin/node
RUN mkdir -p /var/log/nodeapp

ADD nodeapp /opt/nodeapp/

WORKDIR /opt/nodeapp
RUN npm install

VOLUME [ "/var/log/nodeapp" ]

EXPOSE 3000

ENTRYPOINT [ "nodejs", "server.js" ]

用ln -s創建軟鏈接,把二進制文件modejs鏈接到node,解決Ubuntu上原有的一些沒法向後兼容問題(不懂……)。

構建鏡像:

docker build -t ivan/nodejs .

二、Redis基礎鏡像

mkdir redis_base
cd redis_base
touch Dockerfile
FROM ubuntu:14.04
MAINTAINER James Turnbull <james@example.com>
ENV REFRESHED_AT 2014-06-01

RUN apt-get -yqq update
RUN apt-get install -yqq software-properties-common python-software-properties
RUN add-apt-repository ppa:chris-lea/redis-server
RUN apt-get -yqq update
RUN apt-get -yqq install redis-server redis-tools

VOLUME [ "/var/lib/redis", "/var/log/redis" ]

EXPOSE 6379

CMD []
docker build -t ivan/redis .

三、Redis主鏡像

mkdir redis_primary
cd redis_primary
touch Dockerfile
FROM ivan/redis
MAINTAINER James Turnbull <james@example.com>
ENV REFRESHED_AT 2014-06-01

ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-server.log" ]
docker build -t ivan/redis_primary

四、Redis從鏡像

mkdir redis_replica
cd redis_replica
touch Dockerfile
FROM ivan/redis
MAINTAINER James Turnbull <james@example.com>
ENV REFRESHED_AT 2014-06-01

ENTRYPOINT [ "redis-server", "--logfile /var/log/redis/redis-replica.log", "--slaveof redis_primary 6379" ]
docker build -t ivan/redis_replica .

五、建立Redis集羣

啓動Redis主鏡像

docker run -d -h redis-primary --name redis_primary ivan/redis_primary

這裏使用-h參數指定容器的主機名,書中給出的是redis_primary,可是執行會報錯,應該是新版本的Docker不支持帶下劃線的主機名,這裏改成redis-primary。

啓動Redis從服務

docker run -d -h redis-replical --name redis_replical --link redis_primary:redis_primary ivan/redis_replica

這裏對-h參數作相同的處理。

六、建立Node容器

docker run -d --name nodeapp -p 3000:3000 --link redis_primary:redis_primary ivan/nodejs

在瀏覽器中用 宿主機IP:3000測試。

七、捕獲應用日誌

使用Logstash捕獲日誌並將日誌保存到日誌服務器。

mkdir logstash
cd logstash
touch Dockerfile
FROM ubuntu:14.04
MAINTAINER James Turnbull <james@example.com>
ENV REFRESHED_AT 2014-06-01

RUN apt-get -yqq update
RUN apt-get -yqq install wget
RUN wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch |  apt-key add -
RUN echo 'deb http://packages.elasticsearch.org/logstash/1.4/debian stable main' > /etc/apt/sources.list.d/logstash.list
RUN apt-get -yqq update
RUN apt-get -yqq install logstash

ADD logstash.conf /etc/

WORKDIR /opt/logstash

ENTRYPOINT [ "bin/logstash" ]
CMD [ "--config=/etc/logstash.conf" ]

Logstash監控兩個文件/var/log/nodeapp/nodeapp.log和/var/log/redis/redis-server.log,將其中的新內容捕獲,捕獲到的內容輸出到標準輸出上。現實中通常會將Logstash的內容輸出到Elasticsearch集羣。

構建以前將做者的logstash.conf下載到構建根目錄。

構建鏡像

docker build -t ivan/logstash .

啓動容器

docker run -d --name logstash --volumes-from redis_primary --volumes-from nodeapp ivan/logstash

查看logstash容器的輸出日誌

docker logs -f logstash

6.4 不使用SSH管理Docker容器

傳統上,使用SSH登入運行環境或者虛擬機管理服務。在Docker裏,大部分容器只運行一個進程,因此不能使用這種方法。但是使用卷或者連接完成大部分管理操做。如須要給容器發送信號,可使用docker kill命令:

docker kill -s <signal> <container>

這個操做會發送指定的信號給容器,而不是殺掉容器。

可使用nsenter小工做登入容器,它通常適用於1.2或更早的版本,1.3版本引入的docker exec替換了它大部分的功能。

工具nsenter能夠進入Docker用來構成容器的內核命名空間。它能夠進入一個已經存在的命名空間,或者在新的一組命名空間裏執行一個進程。

能夠經過Docker容器安裝nsenter:

docker run -v /usr/local/bin:/target jpetazzo/nsenter

這會把nsenter安裝到/usr/local/bin目錄下。

爲了使用nsenter,首先要知道要進入的容器的進程ID,可使用docker inspect命令得到進程PID:

PID=$(docker inspect --format {{.State.Pid}} <container>)

而後運行下面的命令進入容器:

nsenter --target $PID --mount --uts --ipc --net --pid

也能夠將容器內執行的命令添加在nsenter命令行的後面:

nsenter --target $PID --mount --uts --ipc --net --pid ls
相關文章
相關標籤/搜索