如何使用Docker構建開發環境
- 2021 年 12 月 2 日
- 筆記
- docker, docker-compose
我們在開發中都會遇到這樣的問題:在本地開發好功能後,部署到伺服器,或者其他人拉到本地接著開發時,會出現功能無法使用的情況。
這些異常情況,大多數時候是因為系統不同而導致的依賴差異。因此,為了解決這個問題,就產生基於 Docker 構建統一開發環境的需求。
有關 docker 的基本知識,可以參照 docker教程。
1. 使用 Docker 的好處
- 部署方便
我們平常搭建環境常常需要耗費很長時間。對於團隊協作時來說,每有新人進來,都需要浪費這些可以避免的時間。而且搭建環境時,也常常會產生的各種問題,導致項目程式碼運行異常。如果使用了 Docker 的話,只需最開始的人寫好開發容器,其他人只需要 pull 下來,即可完成項目環境的搭建,能有效避免無意義的時間浪費。 - 隔離性
我們時常會在一台電腦部署多個項目環境,若是直接安裝的話,彼此間有可能會造成干擾,比如一個項目需要 Node.js 14,有的又需要 Node.js 12,若是直接在本機部署的話,總是不能共存的,而是用 Docker 的話,則可以避免該問題。Docker 還能確保每個應用程式只使用分配給它的資源(包括 CPU、記憶體和磁碟空間)。一個特殊的軟體將不會使用你全部的可用資源,要不然這將導致性能降低,甚至讓其他應用程式完全停止工作。
2. 安裝 Docker
1) Linux 安裝 Docker
以 Arch Linux 為例,其他發行版也大同小異,只是換成其包管理工具而已。
# 設置中國鏡像站,中國提速用的,可選操作 $ sudo pacman-mirrors -i -c China -m rank # 使用 Pacman 安裝 Docker $ sudo pacman -S docker # 建立 docker 用戶組。默認情況下,docker 命令會使用 Unix socket 與 Docker 引擎通訊。而只有 root 用戶和 docker 組的用戶才可以訪問 Docker 引擎的 Unix socket。出於安全考慮,一般 Linux 系統上不會直接使用 root 用戶。因此,更好地做法是將需要使用 docker 的用戶加入 docker 用戶組。 $ sudo groupadd docker # 將當前用戶加入 docker 組,退出當前終端並重新登錄後生效 $ sudo usermod -aG docker $USER # 測試是否安裝成功 $ docker run --rm hello-world
2) Windows 10
Windows 10 下安裝 docker 比較簡單,有如下幾種方式:
手動下載安裝:
下載 Docker Desktop for Windows。
下載好之後雙擊 Docker Desktop Installer.exe 開始安裝。
使用winget安裝:
$ winget install Docker.DockerDesktop
運行 Docker:
在 Windows 搜索欄輸入 Docker 點擊 Docker Desktop 開始運行。
Docker 啟動之後會在 Windows 任務欄出現鯨魚圖標。
等待片刻,當鯨魚圖標靜止時,說明 Docker 啟動成功,之後你可以打開 PowerShell/CMD/Windows Terminal 使用 Docker。
3) macOS
使用 Homebrew 安裝:
Homebrew 的 Cask 已經支援 Docker Desktop for Mac,因此可以很方便的使用 Homebrew Cask 來進行安裝:
$ brew install --cask docker
手動下載安裝:
如果需要手動下載,請點擊下載 Docker Desktop for Mac。
請注意下載對應晶片類型的軟體,M1 和 Intel 晶片所對應的版本不通用。
如同 macOS 其它軟體一樣,安裝也非常簡單,雙擊下載的 .dmg 文件,然後將那隻叫 Moby 的鯨魚圖標拖拽到 Application 文件夾即可(其間需要輸入用戶密碼)。
運行 Docker:
從應用中找到 Docker 圖標並點擊運行。
運行之後,會在右上角菜單欄看到多了一個鯨魚圖標,這個圖標表明了 Docker 的運行狀態。
安裝完成並啟動後,我們可以在終端通過命令檢查安裝後的 Docker 版本。
$ docker --version
3. Docker 換源
docker 默認的源是國外的,中國訪問的話速度比較慢,因此可以換為中國源,提高鏡像拉去速度。
1) Linux 換源
Linux 下的比較簡單,創建個 deamon.json 文件寫下配置就好:
$ vi /etc/docker/deamon.json # 輸入鏡像源 { # 只換一個源也是可以的,可以直接用字元串,而不是數組。 "registry-mirrors": [ "//registry.docker-cn.com", "//hub-mirror.c.163.com", "//docker.mirrors.ustc.edu.cn" ], } # :wq 保存退出後重啟 docker $ systemctl restart docker
2) Windows 和 Mac 換源
Windows 和 Mac 都是使用的 Docker Desktop,所以直接在 GUI 中配置即可。
打開 Docker 介面,點擊 Docker Engine:
在右邊輸出框中,輸入鏡像源:
{ "registry-mirrors": [ "//registry.docker-cn.com", "//hub-mirror.c.163.com", "//docker.mirrors.ustc.edu.cn" ], }
4. 編寫 Dockerfile
安裝完 Docker 之後,接下來我們便可以來編寫我們自己的項目開發環境了。本文將以前端開發環境為例,構建 Dockerfile。
包含環境:
- node.js 14.17
- npm 6.14
- yarn 1.22
# 前端開發中,時常需要使用 shell 命令,而有一個較為完整的環境比較重要,因此選擇了使用 ubuntu 作為基礎,若在意容器大小的話,可自行選擇適用的基礎鏡像 FROM ubuntu LABEL org.opencontainers.image.authors="[email protected]" # 設置環境變數 ENV DEBIAN_FRONTEND noninteractive # 設置時區 ARG TZ=Asia/Shanghai ENV TZ ${TZ} RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 用 root 用戶操作 USER root # 更換阿里雲源,在中國可以加快速度 RUN sed -i "s/security.ubuntu.com/mirrors.aliyun.com/" /etc/apt/sources.list && \ sed -i "s/archive.ubuntu.com/mirrors.aliyun.com/" /etc/apt/sources.list && \ sed -i "s/security-cdn.ubuntu.com/mirrors.aliyun.com/" /etc/apt/sources.list RUN apt-get clean # 更新源,安裝相應工具 RUN apt-get update && apt-get install -y \ zsh \ vim \ wget \ curl \ python \ git-core # 安裝 zsh,以後進入容器中時,更加方便地使用 shell RUN git clone https://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh && \ cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc && \ git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions && \ git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting && \ sed -i 's/^plugins=(/plugins=(zsh-autosuggestions zsh-syntax-highlighting z /' ~/.zshrc && \ chsh -s /bin/zsh # 創建 me 用戶 RUN useradd --create-home --no-log-init --shell /bin/zsh -G sudo me && \ adduser me sudo && \ echo 'me:password' | chpasswd # 為 me 安裝 omz USER me RUN git clone https://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh && \ cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc && \ git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions && \ git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting && \ sed -i 's/^plugins=(/plugins=(zsh-autosuggestions zsh-syntax-highlighting z /' ~/.zshrc # 安裝 nvm 和 node ENV NVM_DIR=/home/me/.nvm \ NODE_VERSION=v14 RUN mkdir -p $NVM_DIR && \ curl -o- https://gitee.com/mirrors/nvm/raw/master/install.sh | bash && \ . $NVM_DIR/nvm.sh && \ nvm install ${NODE_VERSION} && \ nvm use ${NODE_VERSION} && \ nvm alias ${NODE_VERSION} && \ ln -s `npm bin --global` /home/me/.node-bin && \ npm install --global nrm && \ nrm use taobao && \ echo '' >> ~/.zshrc && \ echo 'export NVM_DIR="$HOME/.nvm"' >> ~/.zshrc && \ echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm' >> ~/.zshrc # 安裝 yarn RUN curl -o- -L https://yarnpkg.com/install.sh | bash; \ echo '' >> ~/.zshrc && \ echo 'export PATH="$HOME/.yarn/bin:$PATH"' >> ~/.zshrc # Add NVM binaries to root's .bashrc USER root RUN echo '' >> ~/.zshrc && \ echo 'export NVM_DIR="/home/me/.nvm"' >> ~/.zshrc && \ echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm' >> ~/.zshrc && \ echo '' >> ~/.zshrc && \ echo 'export YARN_DIR="/home/me/.yarn"' >> ~/.zshrc && \ echo 'export PATH="$YARN_DIR/bin:$PATH"' >> ~/.zshrc # Add PATH for node & YARN ENV PATH $PATH:/home/me/.node-bin:/home/me/.yarn/bin # 刪除 apt/lists,可以減少最終鏡像大小 RUN rm -rf /var/lib/apt/lists/* WORKDIR /var/www 編寫完 Dockerfile 後,構建即可: docker build -t frontend/react:v1 . 構建完之後可以直接運行: # 以 me 身份運行,推薦方式 docker run --user=me -it frontend/react:v1 /bin/zsh # 以 root 角色運行 docker run -it frontend/react:v1 /bin/zsh
5. 編寫 docker-compose.yml
在開發時,我們尋常需要多個容器配合使用,比如需要配合 mysql 或其他容器使用時,使用 docker-compose.yml 可以更好的組織他們。
version: '2' services: react: build: context: . dockerfile: react/Dockerfile tty: true ports: - 30000:3000 volumes: - ./react/www:/var/www networks: - frontend mysql: image: mysql:5.7 ports: - 33060:3306 volumes: - ./mysql/data:/var/lib/mysql - ./mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d environment: - MYSQL_ROOT_PASSWORD=password networks: - frontend # 將容器置於同一 networks 即可直接通過容器名訪問 networks: frontend: driver: bridge
6. 啟動容器
編寫完上述 Dockerfile 和 docker-compose.yml 後,即可愉快的開始開發了!
# 進入 docker-compose.yml 所在目錄 $ cd frontend # 後台啟動 docker-compose.yml 中所有容器,若容器沒有構建則會先構建 $ docker-compose up -d # 進入 react 容器中,以便命令行交互 $ docker-compose exec --user=me react /bin/zsh
為了測試容器間是否能相互訪問,可以使用編寫如下文件,資料庫需自行創建:
// index.js const mysql = require('mysql') const connection = mysql.createConnection({ host: 'mysql', user: 'root', password: 'password', database: 'test', }) connection.connect(); connection.query(`SELECT * FROM users`, function (error, results, fields) { if (error) throw error; console.log(results) }) connection.end();
之後運行,即可看到結果:
$ node index.js [ RowDataPacket { id: 1, name: 'Caster' } ]
7. 總結
使用 Docker 來搭建開發環境十分方便,一次搭建,即可在許多機器上多次使用,即使是要重裝系統,也不必在重複配置。
如不喜歡寫 Dockerfile 的話,也可以直接開啟一個容器,然後進入容器配置完後,使用 docker save/export 導出即可。
參考資料:
1. Docker教程
2. Docker構建開發環境