基於 Docker 構建統一的開發環境

Clusteramaryllis發表於2021-07-31

title: 基於 Docker 構建統一的開發環境
slug: build-development-environment-with-docker
date: 2021-06-27 18:56:23
thumbnail: ./images/cover/build-development-environment-with-docker.png
categories:

  • Coding
    tags:
  • Docker
  • 開發環境
  • Docker-Compose

前言

大多數人可能都遇到過這樣一個問題,在本地開發好功能後,部署到伺服器,或者其他人拉到本地接著開發時,會出現功能用不了的情況。

大多數時候是由於系統不同,依賴出現差異而導致的。因此,為了解決這個問題,基於 Docker 構建統一開發環境的需求便產生了。

使用 Docker 的好處

  • 部署方便:平常要搭建環境常常需要耗費幾個小時,而且,對於團隊協作時來說,每有新人進來,都需要浪費這可以避免的時間,而且搭建環境時,也常常會產生的各種問題,導致專案程式碼執行異常。如果使用了 Docker 的話,只需最開始的人寫好開發容器,其他人只需要 pull 下來,即可完成專案環境的搭建,能有效避免無意義的時間浪費。
  • 隔離性:我們時常會在一臺電腦部署多個專案環境,若是直接安裝的話,彼此間有可能會造成干擾,比如一個專案需要 Node.js 14,有的又需要 Node.js 12,若是直接在本機部署的話,總是不能共存的,而是用 Docker 的話,則可以避免該問題。Docker 還能確保每個應用程式只使用分配給它的資源(包括 CPU、記憶體和磁碟空間)。一個特殊的軟體將不會使用你全部的可用資源,要不然這將導致效能降低,甚至讓其他應用程式完全停止工作。

實現

安裝 Docker

Linux

我是用的是 Arch Linux,所以以下安裝方法是以 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

Windows 10

Windows 10 下安裝 docker 比較簡單,有如下幾種方式:

手動下載安裝

點選以下 連結 下載 Docker Desktop for Windows。

下載好之後雙擊 Docker Desktop Installer.exe 開始安裝。

使用 winget 安裝
$ winget install Docker.DockerDesktop
執行

在 Windows 搜尋欄輸入 Docker 點選 Docker Desktop 開始執行。

基於 Docker 構建統一的開發環境

Docker 啟動之後會在 Windows 工作列出現鯨魚圖示。

基於 Docker 構建統一的開發環境

等待片刻,當鯨魚圖示靜止時,說明 Docker 啟動成功,之後你可以開啟 PowerShell/CMD/Windows Terminal 使用 Docker。

macOS

使用 Homebrew 安裝

HomebrewCask 已經支援 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 構建統一的開發環境

每次點選鯨魚圖示會彈出操作選單。

基於 Docker 構建統一的開發環境

之後,你可以在終端透過命令檢查安裝後的 Docker 版本。

$ docker --version

編寫 Dockerfile

安裝完 Docker 之後,接下來我們便可以來編寫我們自己的專案開發環境了。本文將以前端開發環境為例,構建 Dockerfile。

包含環境:

  • node.js 14.17

  • npm 6.14

  • yarn 1.22

# 前端開發中,時常需要使用 shell 命令,而有一個較為完整的環境比較重要,因此選擇了使用 ubuntu 作為基礎,若在意容器大小的話,可自行選擇適用的基礎映象
FROM ubuntu
MAINTAINER Caster "moecasts.caster@gmail.com"

# 設定環境變數 
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 
RUN adduser me sudo
RUN 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
ENV 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

USER me
RUN 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

USER root

RUN echo '' >> ~/.zshrc \
    && echo 'export YARN_DIR="/home/me/.yarn"' >> ~/.zshrc \
    && echo 'export PATH="$YARN_DIR/bin:$PATH"' >> ~/.zshrc

# Add PATH for node
ENV PATH $PATH:/home/me/.node-bin

# Add PATH for YARN
ENV PATH $PATH:/home/me/.yarn/bin

# 刪除 apt/lists,可以減少最終映象大小,詳情見:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#general-guidelines-and-recommendations
USER root
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

編寫 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

啟動容器

編寫玩上述 Dockerfiledocker-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' } ]

總結

使用 Docker 來搭建開發環境十分方便,一次搭建,即可在許多機器上多次使用,即使是要重灌系統,也不必在重複配置。

如不喜歡寫 Dockerfile 的話,也可以直接開啟一個容器,然後進入容器配置完後,使用 docker save/export 匯出即可。

原始碼:github.com/MoeCasts/dockerfile-fro...

本文首發於 個人部落格

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章