簡介

在開發服務時,如果需要使用資料庫,通常有三種方法:

  1. 在本機系統中安裝資料庫,自己設置環境。
  2. 連接雲端的資料庫服務,例如:AWS RDS, Google Cloud SQL, Azure SQL, etc.
  3. 在本機使用 Docker 將資料庫執行在獨立的容器中。

本篇筆記針對第三種方法做記錄:使用 Docker 。

使用 Docker 可以不需要在本機安裝資料庫,只需要依照需求快速的啟動容器來執行指定的資料庫。也可以很方便的使用容器並行不同版本的資料庫,以方便隨時切換及管理,但其實不需要透過 Docker 也可以做不同版本的資料庫並行。

只是 Docker 在執行上可以略過系統層上的麻煩,讓不同的開發者在不同的系統環境下也能夠快速設定所需要的開發環境,而部署階段也可以快速的使用 Docker 將指定的環境部署在伺服器主機上,而不需要在從頭設定系統環境。

使用 Docker 前你需要先知道的幾個名詞:

  1. Image (映像檔): 存放用以在容器當中執行的程式以及設定。
  2. Container (容器): 一個執行程式的獨立環境,與其他的容器或系統互不相干,有獨立的環境變數。
  3. Docker Hub: 類似 GitHub 一樣,用以存放不同映像檔的雲端檔案庫。

那我們開始吧!

事前預備

首先,你要先安裝 Docker

你可以直接從 Docker 的官網下載 Docker 的 app 下來直接執行,或者你要很帥氣的使用 homebrew 以便享受一下身爲工程師少數可以耍帥的時刻 (並沒有)。

# 使用 homebrew 安裝
brew cask install docker

接著,有了 Docker 之後就可以來抓取所需的映像檔(image)。

透過 docker pull 這個指令,我們可以把要用來建立實體的映像檔從 Docker Hub 中下載下來。

MySQL

# for amd64
docker pull mysql

# for arm64
docker pull mysql/mysql-server

PostgreSQL

docker pull postgres

這邊注意一下,映像檔會有不同的版本,可以自行前往對應的映像檔所在的 Docker Hub 頁面,點選 “Tags” 來查看自己需要什麼樣的版本,而使用 postgres 也要注意挑選符合自己系統架構的版本。

參考:Postgres Docker Hub

安裝指定版本的 postgres:

# 下載 13.2-alpine 版本
docker pull postgres:13.2-alpine

# 如果沒有指定版本,預設會下載最新版本(latest)
docker pull postgres # 等同於 docker pull postgres:latest

執行

有了映像檔之後,下一步要做的就是使用映像檔建立一個容器來執行我們的資料庫實體。

使用 docker run 來執行指定的映像檔。可參考官方文件

有關環境變數

在執行映像檔的同時,其實就是在啟動一個獨立的容器來存放只屬於該容器的執行環境,也就是說容器內的環境變數是獨立的。所以當我們可以在執行容器的同時,傳入參數來對我們的容器做環境設置。
執行 docker run 時加上 -e flag,我們便可以在執行容器時設定我們所需的環境變數。

建立 MySQL 跟 PostgreSQL 會需要使用的基礎環境變數有:

  • MySQL
    • MYSQL_ROOT_PASSWORD: 必要。用以設定 root (superuser) 的密碼。
    • MYSQL_DATABASE: 可選。用以指定在啟動容器時要一併建立的資料庫名稱。如果下面的使用者及密碼參數也有一併傳入的話,將會將該使用者同時設定為此資料庫的 superuser。(可參考 GRANT)
    • MYSQL_USER, MYSQL_PASSWORD: 可選。用以在啟動容器時同時建立新的使用者,並將該使用者設定為上面傳入的 database 的 superuser。
  • PostgreSQL
    • POSTGRES_PASSWORD: 必要。用以設定 superuser 的密碼,該 superuser 是依照下面傳入的 POSTGRES_PASSWORD 參數來決定。
    • POSTGRES_USER: 用以設定 superuser 之 username,如果沒有傳入值,則 postgres 將被作爲預設值。
    • POSTGRES_DB: 用作第一次啟動容器時將建立的 database 名稱,如果沒有傳入值,則 POSTGRES_USER 將被作爲預設值。

可參考官方文件的環境變數相關章節:MySQL, Postgres

啟動容器

執行下列的命令來從映像檔中建立新容器:

MySQL

依照指定的使用者名稱以及密碼建立資料庫容器:

docker run --name <容器名稱> -p 3306:3306 -e MYSQL_ROOT_PASSWORD=<root密碼> -e MYSQL_USER=<使用者名稱> -e MYSQL_PASSWORD=<使用者密碼> -e MYSQL_DATABASE=<資料庫名稱> -d mysql
# 如果是使用 arm64 請將映像檔 `mysql` 改爲使用 `mysql/mysql-server`

或者可以透過下面指令建立一個使用預設的 root 作爲 superuser 之容器:

docker run --name <容器名稱> -p 3306:3306 -e MYSQL_ROOT_PASSWORD=<root密碼> -d mysql

PostgreSQL

docker run --name <容器名稱> -p 5432:5432 -e POSTGRES_USER=<使用者名稱> -e POSTGRES_PASSWORD=<使用者密碼> -e POSTGRES_DB=<資料庫名稱> -d postgres
# 如果是使用 arm64 請將映像檔 `mysql` 改爲使用 `mysql/mysql-server`
  • --name: 設定容器的名稱
  • -p: 設定容器內部與外部的對映 port,這樣我們就可以透過外部的 port 連接到容器內部的資料庫的 port (MySQL 預設為 3306, 而 PostgreSQL 預設為 5432)
  • -d: 以背景方式執行容器

查看容器

查看剛剛啟動的容器:

docker ps

執行此指令會出現目前正在執行中的容器列表,應該可以看見剛剛我們所建立的新容器。

也可以透過下面的指令查看該容器的狀態記錄。

docker logs <容器名稱>

使用終端機操作

進入容器內的終端機:

docker exec -it <容器名稱> bash

# 或者
docker exec -it <容器名稱> /bin/bash

# 或者
docker exec -it <容器名稱> /bin/sh

接著你會看見終端機的提示字元,一旦進入終端機,便可以透過指令直接在容器內部進行操作。

如欲在終端機當中連接 Postgres:

psql <database> <username>

其中的 <database> 以及 <username> 請自行替換成自己所設定的。

如欲在終端機中連接 MySQL 請看下一節。

到這邊,恭喜你已經有一個容器化的資料庫服務可以使用了。
這邊也推薦你一個好用的資料庫 GUI 程式: TablePlus(有提供簡易功能的免費版本)

MySQL 連線問題

如果從外部無法連線進入 MySQL,可能是因為 MySQL 的使用者連線設定問題, 一般來說 MySQL 預設的 root user 是不開放從外部網路進行連線操作的。

這邊要特別說明一下:
Docker 的容器內部與外部是屬於兩個不相通的獨立網路,因此上面我們再透過指令建立容器時, 才需要給予 -p 的參數來告訴 docker 我們希望透過外部的 port 來連接到容器內部的 port。 所以當我們從外部連線至容器內的 MySQL 時,MySQL 將判定我們所做的是一個外部連線,而非本機連線。

因此如果要從外部操作內部的 root user ,我們就需要進入容器當中以本機的方式操作。

我們可以在容器的終端機裡面透過下列指令以 root 身份進入 MySQL 當中:

mysql -u root -p

如此一來我們就可以以 root 的身份來對 MySQL 進行操作了。

那麼,如果我們需要從外部以 superuser 的身份來操作 MySQL 該如何呢?
我們可以建立一個 root 以外的 superuser 來進行。

在容器內的 MySQL 中執行下列指令:

-- MySQL 5.*
CREATE USER '<username>'@'%' IDENTIFIED BY '<password>';

-- MySQL 8.*
CREATE USER '<username>'@'%' IDENTIFIED WITH mysql_native_password BY '<password>';

其中的 % 表示我們建立的使用者允許從外部的任意網路來連線,也可以設定為指定的 IP 位址。

接著透過下面的指令來賦予該使用者 superuser 的權限:

GRANT ALL ON "資料庫名稱".* TO <username>@'%';

或者也可以開放現有的使用者之外部連線:

UPDATE mysql.user SET HOST='%' WHERE USER = "使用者名稱" LIMIT 1;

最後,如果我們需要開放外部的使用者連線來建立 MySQL 裡面的 funciton,我們可以執行這個指令來開啓信任:

SET GLOBAL log_bin_trust_function_creators=1;

大功告成。