Docker開放遠程安全訪問及PyCharm配置遠程Docker解析器

環(huán)境說明:
操作系統(tǒng):ubuntu~18.04
Docker版本:19.03.3
PyCharm版本:2019.2.2 (Professional Edition)
ps. 本文操作指南在其他環(huán)境未必適用,歡迎參考。
轉載請注明出處(http://www.itdecent.cn/p/4f78bbaaf844) 如果覺得有用麻煩點個贊噢~

從github上下載別人的開源代碼,往往發(fā)現(xiàn)工程里有一個文件叫Dockerfile或者requirements.txt,就算這兩個文件都沒有,但一般也會在某處說明一下工程適用的運行環(huán)境。Docker是一個不錯的工具,用來在同一個電腦上輕松搭建不同的環(huán)境來支撐對不同工程的運行。

現(xiàn)在希望在一臺具備獨立顯卡的電腦上運行docker,并讓其他電腦可以遠程使用Docker里的python作為PyCharm的解析器(Interpreter)。這樣的好處是,我只需要在一臺充當服務器的電腦上運行docker,根據(jù)工程所要求的環(huán)境配置獨立的docker容器,然后在其他電腦上共享這些容器,用于調試或運行工程。下面我將運行docker服務的電腦稱為服務器,而遠程訪問docker容器的電腦稱為客戶端。

這里有一些前提準備,本文不涉及:

  1. 服務器已經安裝好了Docker CE
  2. 客戶端已經安裝好了PyCharm,并且是專業(yè)(付費)版的,據(jù)說社區(qū)免費版沒有配置遠程Interpreter的功能。
  3. 服務器具有公網可訪問的IP
  4. 擁有一個動態(tài)域名,可解析到上一點所提的IP。
  5. 如果跟我一樣,服務器就是家里的一個臺式電腦,由于家庭網絡通常都是動態(tài)IP,那么就需要一個動態(tài)域名(花生殼不錯)。另外如果發(fā)現(xiàn)公網訪問不進來,一般是因為寬帶運營商沒給你開通公網訪問,打客服電話申請以下即可。最后還要配置路由,做好端口映射,才能將外部的訪問轉發(fā)到這個臺式機上來。

一. 配置Docker Server

僅僅開放遠程訪問Docker API,這個還不夠的,因為會有安全問題。關于這點,Docker有相關的安全機制,參考官方文檔Protect the Docker daemon socket,大致就是:生成證書,用來達到驗證客戶端身份的目的。下面是操作步驟:

以下所有命令里的$HOST都替換成你給服務器的域名(DNS name)

* 找個合適的目錄存儲將要生成的文件
$ mkdir -pv /etc/docker/certs
$ cd /etc/docker/certs
* (服務器上執(zhí)行)生成CA公私玥。
$ openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
......................++
........++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:

$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Enter pass phrase for ca-key.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:Queensland
Locality Name (eg, city) []:Brisbane
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []:Sven@home.org.au
* (服務器上執(zhí)行)生成server keyCSR
$ openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
...............++
...............................++
e is 65537 (0x10001)

$ openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
* (服務器上執(zhí)行)用CA簽署一個公鑰,由于TLS連接可以通過DNS域名也可以通過IP地址,這里需要指定這些信息(對于IP地址,建議設置兩個,一個是127.0.0.1供本地訪問,另一個是內網IP供局域網訪問)
$ echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1 >> extfile.cnf
* (服務器上執(zhí)行)設置這些key僅用于服務器鑒權
$ echo extendedKeyUsage = serverAuth >> extfile.cnf
* (服務器上執(zhí)行)生成簽名證書
$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out server-cert.pem -extfile extfile.cnf
Signature ok
subject=/CN=your.host.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:
* (服務器上執(zhí)行)生成client keyCSR,供客戶端發(fā)起遠程訪問時使用
$ openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................++
................++
e is 65537 (0x10001)

$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
* (服務器上執(zhí)行)配置這些key是供客戶端鑒權使用的
$ echo extendedKeyUsage = clientAuth > extfile-client.cnf
* (服務器上執(zhí)行)生成簽名證書
$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out cert.pem -extfile extfile-client.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:
* (服務器上執(zhí)行)確定已經生成好cert.pemserver-cert.pem之后,可以刪除那幾個CSR和cnf文件
$ rm -v client.csr server.csr extfile.cnf extfile-client.cnf
* (服務器上執(zhí)行)為防止私鑰文件被更改以及被其他用戶查看,修改其權限
$ chmod -v 0400 ca-key.pem key.pem server-key.pem
* (服務器上執(zhí)行)為防止公鑰文件被更改,修改其權限
$ chmod -v 0444 ca.pem server-cert.pem cert.pem
* 測試是否已生效
# 將客戶端的證書文件拷貝到客戶端,包括:ca.pem,cert.pem,key.pem
# 假設放到 ~/.docker/certs/目錄里:mkdir -pv ~/.docker/certs/; cd ~/.docker/certs/; scp ??? ./
# 在服務器上啟動docker服務器
$ dockerd --tlsverify --tlscacert=/etc/docker/certs/ca.pem \
    --tlscert=/etc/docker/certs/server-cert.pem \
    --tlskey=/etc/docker/certs/server-key.pem \
    -H=0.0.0.0:2376
# 在客戶端執(zhí)行
$ docker --tlsverify \
    --tlscacert=/Users/howard/.docker/certs/ca.pem \
    --tlscert=/Users/howard/.docker/certs/cert.pem \
    --tlskey=/Users/howard/.docker/certs/key.pem \
    -H=$HOST:2376 version
# 看到輸出docker信息就說明通了
# 通過curl來訪問docker api
$ curl https://$HOST:2376/images/json \
  --cert ~/.docker/certs/cert.pem \
  --key ~/.docker/certs/key.pem \
  --cacert ~/.docker/certs/ca.pem
* (服務器上執(zhí)行)配置Docker Server默認以此方式啟動
# 拷貝安裝包單元文件到/etc,這樣就不會因為docker升級而被覆蓋
$ cp /lib/systemd/system/docker.service /etc/systemd/system/docker.service
# 更新文件/etc/systemd/system/docker.service里的ExecStart設置為:
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376 \
  --tlsverify --tlscacert=/etc/docker/certs/ca.pem \
  --tlskey=/etc/docker/certs/server-key.pem \
  --tlscert=/etc/docker/certs/server-cert.pem
# 重載systemd配置
$ systemctl daemon-reload
# 重啟docker服務
$ systemctl restart docker
* (客戶端上執(zhí)行)配置默認遠程調用服務器docker服務
# 配置~/.zshrc(或者~/.bashrc,根據(jù)你的客戶端環(huán)境而定),在末尾添加以下幾行
export DOCKER_HOST=tcp://$HOST:2376 DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=~/.docker/certs/
# 然后讓加載到當前會話
$ source .zshrc
# 測試以下
$ docker ps
* 務必非常小心保管這些key,它們就跟服務器root密碼一樣重要,如果被壞人獲取,等同于將服務器交出去了

二. 配置PyCharm

以下所有命令里的$HOST都替換成你給服務器的域名(DNS name)

* 打開配置(Preferences)窗口
31862168536dac620dc2b284fd81411d.png
* 選中Build,Execution,Deployment下的Docker,并點擊添加一個Docker服務
95ec10ab2a39f11d82468f895015a4da.png
* 選擇TCP socket,Engine API URL欄輸入:https://$HOST:2376,Certificates folder欄輸入那幾個證書所在的目錄
5f20f3f6f5fe6543cd956fdb250c5291.png
* 點擊其他任意地方或者點擊Apply按鈕,如果成功連接,會看到提示:Connection successful
a5b6d8eba4ee39f9589bebfb87555ad2.png
* 選擇Project下的Project Interpreter,點擊新增Project Interpreter
0ba6372962deb786279d439cb59c3dd1.png
* 這里可以看見有一個叫Docker,一個叫Docker Compose。我們應該選擇Docker Compose。

我們先明確一點,當我們在PyCharm中運行/調試代碼時,遠程調用服務器上的docker容器其實是臨時創(chuàng)建的新容器,本次運行/調試結束后會自動被關閉。
而我們會在PyCharm中修改代碼,我們需要能自動將變動同步到服務器的代碼中,而服務器上的代碼要能被臨時啟動的docker容器訪問到。所以,臨時啟動的docker容器需要掛載相應的工程代碼。
這個就是選擇Docker Compose的原因所在,它可以讀取預先配置好的docker-compose.yml文件,以我們預設的方式啟動容器。我們可以在工程下新增一個文件命名為:docker-compose.yml,內容類似于:

version: '3'
services:
  interpreter:
    image: detectron2:v0
    volumes:
        - /home/howard/Nutstore Files/fish:/fish
    working_dir: /fish/detectron2

我們需要先寫好這個文件后才能繼續(xù)下面的步驟。

* 選擇Docker Compose,然后Server欄選擇前面創(chuàng)建的Docker server,在Configuration file欄選擇上一步創(chuàng)建的yml文件,這個時候如果yml文件編寫正確,可以在Service欄看到可選的服務,最后在Python interpreter path欄輸入鏡像里的python解析器路徑(如果在環(huán)境變量里,那么也可以在直接寫一個命令名python3)
ce0bc1429c0b8c31630275182bc29b28.png
* 回到上一級窗口,還要設置一下Path mappings,將本地工程目錄跟docker容器里的路徑關聯(lián)起來
2b33cc5ae372b637576f8ea93ae4d299.png
* 但本地的代碼修改后,怎么同步到服務器上呢?這里只給思路,具體方法請自行搜索。一種同步方案是,通過類似堅果云之類的云盤服務,自動同步代碼變動到服務器;另一種方案是,通過PyCharm自帶的Deployment功能手動/自動同步到服務器。

三. Docker部署支持GPU的深度學習環(huán)境

這個主要依靠NVIDIA提供的nvidia-docker工具包來實現(xiàn),參見其github:nvidia-docker。其github上已經有非常詳細的安裝和測試說明了。下面拷貝ubuntu環(huán)境的指南過來。

Make sure you have installed the NVIDIA driver and Docker 19.03 for your Linux distribution Note that you do not need to install the CUDA toolkit on the host, but the driver needs to be installed

* Ubuntu 16.04/18.04, Debian Jessie/Stretch/Buster
# Add the package repositories
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
$ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

$ sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
$ sudo systemctl restart docker
* Usage
#### Test nvidia-smi with the latest official CUDA image
$ docker run --gpus all nvidia/cuda:9.0-base nvidia-smi

# Start a GPU enabled container on two GPUs
$ docker run --gpus 2 nvidia/cuda:9.0-base nvidia-smi

# Starting a GPU enabled container on specific GPUs
$ docker run --gpus '"device=1,2"' nvidia/cuda:9.0-base nvidia-smi
$ docker run --gpus '"device=UUID-ABCDEF,1"' nvidia/cuda:9.0-base nvidia-smi

# Specifying a capability (graphics, compute, ...) for my container
# Note this is rarely if ever used this way
$ docker run --gpus all,capabilities=utility nvidia/cuda:9.0-base nvidia-smi
* 如何通過docker-compose部署GPU容器?
  1. 安裝nvidia-docker2
$ sudo apt install nvidia-docker2
  1. 修改/etc/docker/daemon.json,加入一下內容:
{
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}
  1. 測試是否成功
# 編輯docker-compose.yml文件,內容如下
version: '3'
services:
  cuda:
    image: nvidia/cuda:9.0-base

# 然后執(zhí)行下面命令
$ docker-compose run cuda nvidia-smi
# 如果能輸出顯卡信息,那么說明成功
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容