使用docker搭建數據分析環境


:早在學習《雲計算》這門課之前就已經知道docker,學習這門課時老師還鼓勵我們自己嘗試一下;但是直到去年年底才有機會嘗試,用過之后感覺確實很好用。最近需要部署幾個shiny應用,又回顧了一下,並記錄與此。

 

1. 初識docker


最開始聽說docker,就知道可以使用docker來部署應用,相對於之前在主機上直接安裝應用所需的運行環境,docker要方便的多。對於我這樣時不時被開發環境搞的懷疑人生的半個開發人員,自然會對這樣神奇的工具有所關注,只是一直沒有找到機會嘗試。直到去年年底,為了部署一個Django應用,終於有機會嘗試了一下。有以下幾點認識:

  1. 與虛擬機相比,docker是操作系統級別的虛擬化,與host共享了很多系統資源。因此docker比虛擬機更輕量級,運行的時候啟動速度更快,開銷也更小;
  2. 虛擬機可以安裝桌面,但是docker部署的應用一般通過"IP+端口"的方式來訪問;
  3. docker以image(鏡像)為基礎,應用程序運行在基於特定image開啟的container(容器)上;
  4. 如果多個程序使用同一個image來開啟不同的container,這些container共享該image而不需要復制多個;
  5. 一個image可以是一個完整的操作系統(例如Ubuntu的官方鏡像,大小為2G左右),也可以只是滿足某個應用程序運行的基本環境(例如運行nginx的alpine鏡像,只有15.5M);
  6. image是分層的,已有image的層是只讀屬性的,可以在已有image的基礎上添加新的層來覆蓋下層的東西,從而構建出新的image.

圖1:docker與虛擬機的區別,圖片來源:link

由於應用程序運行在container中,而容器又是基於image構建的,因此image就顯得非常重要了。image相當於一個刻錄好的光盤,里面有預裝好的操作系統或應用程序等。docker官方維護了docker hub這個網站,類似於github,可以直接從該網站上pull各種應用程序的官方鏡像。這些鏡像可以直接使用,也可以在此基礎上添加新的層,來構建自己的鏡像。

 

 Docker的安裝

windows下需要win10操作系統的特定版本(Windows 10 64bit: Pro, Enterprise or Education (1607 Anniversary Update, Build 14393 or later))才可以安裝docker engine。下面是CentOS下安裝及啟動Docker Community Edition(CE)的官方文檔:

  • 下載安裝:https://docs.docker.com/install/linux/docker-ce/centos/
  • 基本使用方法:https://docs.docker.com/get-started/

在安裝好docker,並啟動docker之后,就可以pull官方的image,並在這些image的基礎上按照自己的需要創建新的image。

 

2. Dockerfile文件


創建自己的image只需要一個Dockerfile文件就可以,該文件中保存了構建image的每一步命令。image的每一層可以僅包含一個命令也可以是多個命令,且每一層執行完成后可以緩存起來(下次不用重新執行已構建完成的層中的命令),這樣就讓可追溯的逐步搭建運行環境成為可能。

Dockerfile中保存的是與基礎image對應的操作系統命令,例如以Ubuntu為基礎image構建的新的image,該文件中就是Ubuntu系統的shell命令。以下是docker官網對該文件的介紹:

Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.

下面是一個該文件的示例,使用了jupyter的官方鏡像datascience-notebook:

 1 # 指定基礎image
 2 FROM jupyter/datascience-notebook:03b897d05f16
 3 MAINTAINER Xin Xiong <xiongxin20008@126.com>
 4 
 5 # 替換CRAN鏡像為國內的鏡像,可以更快的安裝R packages
 6 ARG CRAN_MIRROR=https://mirrors.tuna.tsinghua.edu.cn/CRAN/
 7 
 8 # 由於要安裝程序,使用root身份
 9 USER root
10 
11 # install Java
12 RUN \
13   apt-get update -qq && \
14   apt-get install -y openjdk-8-jdk  && \
15   apt-get install -y mlocate && updatedb && \
16   rm -rf /var/lib/apt/lists/*
17 
18 # 為安裝rJava做准備
19 # need using ln to avoid some errors, such as conftest.c:1:10: fatal error: jni.h: No such file or directory
20 RUN \
21   ln -s /usr/lib/jvm/java-8-openjdk-amd64/include/jni.h /opt/conda/include/ && \
22   ln -s /usr/lib/jvm/java-8-openjdk-amd64/include/linux/jni_md.h /opt/conda/include/  && \
23   ln -s /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so /usr/lib/ && \  
24   R CMD javareconf
25 
26 
27 # 添加本地文件夾package到鏡像中的/src
28 ADD ./package/ /src/
29 
30 # link lib
31 RUN \
32   ln -s /opt/conda/lib/libpcre.so /usr/lib/  && \
33   ln -s /opt/conda/lib/liblzma.so /usr/lib/  && \
34   ln -s /opt/conda/lib/libbz2.so /usr/lib/  && \
35   ln -s /opt/conda/lib/libz.so /usr/lib/  && \
36   ln -s /opt/conda/lib/libiconv.so /usr/lib/  && \
37   ln -s /opt/conda/lib/libicuuc.so /usr/lib/  && \
38   ln -s /opt/conda/lib/libicui18n.so /usr/lib/
39 
40 # 安裝前面添加到鏡像/src文件夾中rpacks.txt文件中的R package
41 # 且使用國內的鏡像地址CRAN_MIRROR
42 RUN \
43   cd /src && \
44   R -e 'install.packages(sub("(.+)\\\\n","\\1", scan("rpacks.txt", "character")), repos="'"${CRAN_MIRROR}"'")'
45 
46 # 安裝前面添加到鏡像/src文件夾中requirements.txt文件中的Python package
47 RUN \
48   cd /src && \
49   pip --no-cache-dir install -r requirements.txt && \
50   rm -rf /root/.cache
51 
52 # 直接使用conda安裝Python package
53 RUN conda install -c rdkit rdkit
54 
55 # 切換到默認普通用戶
56 USER jovyan

Jupyter的官方鏡像datascience-notebook,包含了Python, R和Julia以及一些數據分析中常用的包。我在該鏡像的基礎上,安裝了Java以及其他一些自己需要的Python和R包。這些包的的名字保存在文件夾package的rpacks.txt和requirements.txt兩個文件中,每個包名稱一行。在上面的操作中,第28行添加該文件夾中的內容到鏡像中的的/src目錄下,第42-44行安裝了rpacks.txt文件中的R包,第47-50行安裝了requirements.txt文件中的Python包。更多關於該鏡像的說明可以在官方文檔的描述中看到,還有其他的官方鏡像可供選擇。

 

 

2.1 Dockfile中的關鍵字

如上面的例子所示,Dockfile中包含的最重要的內容是可以在系統命令行中執行的命令,只是每一行命令前加了一些Dockfile特有的關鍵詞。下面是一些常見的關鍵詞:

2.1.1 FROM

FROM <鏡像>:<標簽>  指定基礎鏡像為該鏡像的一個標簽版本,上面例子中的第2行

2.1.2 RUN

運行指定的命令。使用RUN可以運行任何被基礎image支持的命令。如果基礎image是ubuntu系統,那么軟件管理部分只能使用ubuntu的命令

2.1.3 ADD

添加本地文件或目錄到container

2.1.4 LABLE

添加一些元數據,格式為LABEL <key>=<value>,例如上面的第3行可以寫成LABEL maintainer="Xin Xiong, xiongxin@20008@126.com". MAINTAINER關鍵詞已棄用。

2.1.5 ARG

定義一個變量,如第6行,可以重復使用

更多關鍵詞,可以參考官方文檔。此外,"&&"用來連接兩條不同的指令,"\" 表示同一條語句換行顯示

 

2.2 Dockerfile的最佳實踐

官方文檔給出了一些最佳實踐指南,比如說不要安裝不需要的package,應用解偶聯,最小化層數,如何最好的使用"apt-get",COPY和ADD關鍵詞的差別等。

 

2.3 構建自己的image

有了上面的文件,就可以在Dockerfile這個文件所在的文件夾,使用下面的命令build自己的鏡像了:

docker build -t onlybelter/ds-notebook .

這句命令會使用當前目錄下的Dockerfile文件,構建一個image,新image的名稱為onlybelter/ds-notebook。

 

3. docker-compose


docker-compose可以用來配置一些在image中沒有設置的參數,例如端口號,log日志的目錄,容器啟動時運行的命令等。此外還可以用來啟動、停止容器,打印log,查看容器狀態和限制資源使用等功能。

docker-compose的其他介紹及安裝可以參考官方文檔

 

3.1 docker-compose的配置文件

docker-compose的配置文件是一個放在與Dockerfile相同目錄下,以.yml結尾的文件,示例如下:

 1 version: '2.2'
 2 
 3 services:
 4   jupyterlab:
 5     image: onlybelter/ds-notebook
 6     command: /bin/bash -c "jupyter lab --no-browser --ip=0.0.0.0 --notebook-dir=/mnt/notebook"
 7     cpus: 16
 8     mem_limit: 8g
 9     volumes:
10       - /mnt/home/belter/github/jupyter-note:/mnt/notebook
11       - /etc/localtime:/etc/localtime:ro
12     environment:
13       - PYTHONUNBUFFERED=1
14     ports:
15       - 8888:8888

第1行,指定了配置文件的版本號,由於v3不支持單機模式下配置資源,因此這里使用了v2.2(如果不適用swarm或其他集群模式,官方推薦使用v2);

第4行是service的名稱;

第5行指定了image的名稱,就是上面build好的鏡像;

第6行設置了容器啟動時的命令;

第7-8行限制了資源的使用:16個CPU核,8G內存;

第10行,相當於掛載了一個本地目錄到容器,這樣容器和外部的host之間就可以交換文件了(內外對應的文件夾里的內容是同步的);

第11行用於同步容器與host的時間;

第13行設置了一個環境變量;

第15行設置了容器內外端口號的對應關系,左邊是host的端口號,右邊是容器內的端口號。

 

3.2 啟動容器

配置好上面的.yml文件(我的文件為docker-compose.yml)后,就可以啟動前面build好的鏡像來創建一個容器了。

$ sudo docker-compose up -d

$ sudo docker-compose logs

第1行命令使用當前目錄下的docker-compose.yml文件創建容器,並在后台運行;第2行命令打印logs,可以從logs中獲得Jupyter Notebook生成的token來登錄。

Attaching to jupyterlab_jupyterlab_1
jupyterlab_1  | [I 20:07:06.567 LabApp] Writing notebook server cookie secret to /home/jovyan/.local/share/jupyter/runtime/notebook_cookie_secret
jupyterlab_1  | [I 20:07:06.731 LabApp] JupyterLab beta preview extension loaded from /opt/conda/lib/python3.6/site-packages/jupyterlab
jupyterlab_1  | [I 20:07:06.731 LabApp] JupyterLab application directory is /opt/conda/share/jupyter/lab
jupyterlab_1  | [W 20:07:06.737 LabApp] JupyterLab server extension not enabled, manually loading...
jupyterlab_1  | [I 20:07:06.737 LabApp] JupyterLab beta preview extension loaded from /opt/conda/lib/python3.6/site-packages/jupyterlab
jupyterlab_1  | [I 20:07:06.737 LabApp] JupyterLab application directory is /opt/conda/share/jupyter/lab
jupyterlab_1  | [I 20:07:06.744 LabApp] Serving notebooks from local directory: /mnt/notebook
jupyterlab_1  | [I 20:07:06.744 LabApp] 0 active kernels
jupyterlab_1  | [I 20:07:06.744 LabApp] The Jupyter Notebook is running at:
jupyterlab_1  | [I 20:07:06.744 LabApp] http://1c7e68e582c4:8888/?token=3bda623azj07414dbcf58bf977e2c2855158bd052f77afa2
jupyterlab_1  | [I 20:07:06.744 LabApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).

使用該host的ip加端口號8888,輸入日志中的token就可以打開Jupyter的界面。我的本地地址為,http://192.168.1.33:8888/lab?

界面如下圖所示:

 

其他docker-compose命令:

$ sudo docker-compose down  # 關閉容器
$ sudo docker-compose ps # 查看容器運行狀態

此外使用docker stats <container name>可以查看該容器資源使用情況:

 

PS: 最近都是忙到每個月最后一天更新博客,要改改啦!

 

Reference


https://stackoverflow.com/questions/16047306/how-is-docker-different-from-a-virtual-machine

https://www.docker.com/resources/what-container#/package_software

https://blog.csdn.net/weixin_37645838/article/details/83343029

https://docs.docker.com/engine/reference/builder/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM