上一篇討論既有系統搬上 AWS 時,如何決定搬遷範圍,以及資料同步與正式切換需要注意哪些問題。
今天延續訂單系統的情境,在 EC2 上自行安裝 PostgreSQL 用來模擬地端資料庫,再透過 AWS Database Migration Service(AWS DMS,資料庫遷移服務) 將資料搬到 RDS for PostgreSQL。
這次會驗證以下兩個功能:
本次使用 EC2 模擬地端自行管理的資料庫,所有資源放在同一個 VPC,聚焦在資料庫設定與同步,實際從機房搬遷時,還需要另外準備 VPN、Direct Connect 或其他適當的跨環境連線。
| 資源 | 名稱或設定 | 用途 |
|---|---|---|
| Region | ap-northeast-1 |
所有資源使用相同區域 |
| 來源 EC2 | day29-source-pg |
自行安裝 PostgreSQL,模擬地端資料庫 |
| EC2 作業系統 | Ubuntu Server 24.04 LTS | 執行來源 PostgreSQL 與 SQL 用戶端 |
| 目標 RDS | day29-target-pg |
接收搬移與同步的資料 |
| DMS Replication Instance | day29-dms |
執行完整載入與 CDC |
| PostgreSQL 主要版本 | 16 |
來源與目標使用相同主要版本 |
| 資料庫名稱 | 兩邊皆為 orders_lab |
存放測試資料 |
| 資料表 | public.orders |
測試訂單 |
來源 EC2 也兼作操作主機,透過 psql 連線到目標 RDS,因此不需要再建立另一台 EC2。
真正搬移資料的是 DMS,即使關閉操作視窗,只要來源資料庫、DMS 與目標 RDS 持續運作,同步仍會繼續。
本次使用 Replication Instance(複寫執行個體)與 Database Migration Task(資料庫遷移任務),請依照這組模式建立資源。
EC2、RDS 與 DMS 都會產生費用,請使用獨立測試環境,完成後依文末步驟清理。
使用同一個 VPC,並準備至少兩個不同 Availability Zone(AZ,可用區)的子網路,供 RDS 與 DMS 建立 Subnet Group(子網路群組)。
先建立以下三個 Security Group,再加入連入規則:
| Security Group | 使用資源 | Inbound 規則 |
|---|---|---|
day29-dms-sg |
DMS | 本次不需要額外開放連入規則 |
day29-source-sg |
來源 EC2 | TCP 5432,來源為 day29-dms-sg |
day29-target-sg |
目標 RDS | TCP 5432,來源為 day29-dms-sg |
day29-target-sg |
目標 RDS | TCP 5432,來源為 day29-source-sg |
5432 是 PostgreSQL 的連接埠,這些規則允許 DMS 讀取來源、寫入目標,也允許來源 EC2 連到 RDS 查詢結果。
進入 EC2 → Instances → Launch instances:
| 項目 | 設定 |
|---|---|
| Name | day29-source-pg |
| AMI | Ubuntu Server 24.04 LTS |
| Instance type | 例如 t3.small,供本次少量資料測試 |
| Storage | 20 GiB |
| VPC | 本次測試 VPC |
| Security Group | day29-source-sg |
| IAM instance profile | 已附加 AmazonSSMManagedInstanceCore 的 EC2 Role |
IAM instance profile 位於 Advanced details,如果尚未建立 Role,先到 IAM 建立信任 AWS service → EC2 的角色,附加 AmazonSSMManagedInstanceCore,再回來選取,本次透過 Session Manager 連線,不需要開放 SSH。
EC2 必須能連到 Systems Manager 與 Ubuntu 套件來源,測試時可使用具備公有 IP、且有 Internet Gateway 路由的公有子網路;若放在私有子網路,則需準備 NAT 或相應的服務連線與套件下載路徑。
AWS 提供的 Ubuntu AMI 通常已包含 SSM Agent,若使用自行製作的映像檔,須先確認 Agent 已安裝並啟動。
建立完成後,進入:
EC2 → 選取執行個體 → Connect → Session Manager → Connect ,後續所有終端機指令,都在這台 EC2 的 Session Manager 視窗執行。
在 EC2 執行:
sudo apt-get update
sudo apt-get install -y postgresql-16 postgresql-client-16 postgresql-contrib
sudo systemctl enable --now postgresql
確認版本與資料庫狀態:
psql --version
sudo pg_lsclusters
預期可以看到 PostgreSQL 16,以及 16 main 的狀態為 online。
這次使用 Ubuntu 套件建立的預設叢集,設定檔位於:
/etc/postgresql/16/main/
接下來要讓 PostgreSQL 支援兩件事:接受 DMS 的連線,以及提供訂單變更紀錄。
先允許 PostgreSQL 接受其他主機的連線:
sudo pg_conftool 16 main set listen_addresses '*'
PostgreSQL 預設主要接受同一台主機上的程式連線,但 DMS 大多執行在另一台主機,因此需要調整這項設定。
* 表示在所有網路介面接受連線,實際允許誰連入,仍由前面的 Security Group,以及稍後設定的 pg_hba.conf 控制。
接著設定資料變更的記錄方式:
sudo pg_conftool 16 main set wal_level logical
例如訂單 1001 從「待付款」改成「已付款」,DMS 需要讀取這項變更,才能更新目標 RDS,設為 logical,就是讓 PostgreSQL 記錄足夠的資訊,供 DMS 辨識這類新增、修改與刪除。
最後設定複寫所需的名額:
sudo pg_conftool 16 main set max_replication_slots 10
sudo pg_conftool 16 main set max_wal_senders 10
兩個設定分別控制:
本次兩者都設為 10,是容量上限,不會立即建立 10 個任務或連線。
這些設定需要重新啟動 PostgreSQL 才會完整生效,先繼續完成下方的帳號與連線規則設定,最後再一起重新啟動。
使用本機的 PostgreSQL 管理身分登入:
sudo -u postgres psql
執行:
CREATE DATABASE orders_lab;
CREATE ROLE dms_source WITH LOGIN SUPERUSER;
接著設定密碼:
\password dms_source
依提示輸入兩次密碼,再離開:
\q
\password會互動式要求輸入密碼,不必將密碼直接寫在 SQL 指令裡。
除了 Security Group,PostgreSQL 自己也有連線規則,需要設定 pg_hba.conf。
開啟:
sudo nano /etc/postgresql/16/main/pg_hba.conf
在檔案末尾加入以下規則,將 10.0.0.0/16 換成本次 VPC 的實際 IPv4 CIDR:
# 允許連線 orders_lab,包含指定此資料庫的邏輯複寫連線
hostssl orders_lab dms_source 10.0.0.0/16 scram-sha-256
# 依 AWS DMS 前置設定範例,另外允許實體複寫連線
hostssl replication dms_source 10.0.0.0/16 scram-sha-256
hostssl 要求使用加密連線,scram-sha-256 則指定密碼驗證方式。
本次使用 VPC CIDR 簡化設定,外層仍由 day29-source-sg 限制只有 DMS 可以連入 PostgreSQL,正式環境應依實際連線來源縮小網段。
設定完成後在 nano 中按 Ctrl+O → Enter 儲存,再按 Ctrl+X 離開。
重新啟動 PostgreSQL:
sudo systemctl restart postgresql
sudo pg_lsclusters
Ubuntu 套件預設會設定 PostgreSQL SSL,仍應確認實際狀態。登入:
sudo -u postgres psql -d orders_lab
執行:
SHOW listen_addresses;
SHOW wal_level;
SHOW max_replication_slots;
SHOW max_wal_senders;
SHOW ssl;
預期結果:
| 項目 | 預期結果 |
|---|---|
listen_addresses |
* |
wal_level |
logical |
max_replication_slots |
10 |
max_wal_senders |
10 |
ssl |
on |
完成後輸入 \q 離開。
這些檢查只確認來源設定,CDC 是否成功,仍要等 DMS Task 啟動後驗證。
進入 RDS → Subnet groups → Create DB subnet group:
day29-rds-subnets。Subnet group for Day 29 target RDS。DB Subnet Group 用來指定 RDS 可以使用哪些子網路,即使本次建立的是 Single-AZ 資料庫,仍需準備涵蓋至少兩個 AZ 的子網路群組。
進入 RDS → Databases → Create database,使用 Standard create:
| 項目 | 設定 |
|---|---|
| Engine | PostgreSQL |
| Deployment | Single DB instance |
| Engine version | PostgreSQL 16,選擇 DMS 支援的小版本 |
| DB instance identifier | day29-target-pg |
| Master username | labadmin |
| Credentials management | Self managed |
| Auto generate password | 不勾選,本次自行設定 |
| Master password | 自行設定符合畫面要求的密碼,並妥善保存 |
| Confirm master password | 再次輸入相同密碼 |
| Instance type | 選擇小型測試規格,例如區域與版本支援的 db.t3.small |
| Allocated storage | 依資料量做設定,本文以 50 GiB 作為示範 |
| VPC | 本次測試 VPC |
| DB subnet group | day29-rds-subnets |
| Public access | No |
| Security Group | day29-target-sg |
| Initial database name | orders_lab |
這組密碼是目標 RDS 帳號 labadmin 的密碼,與來源 PostgreSQL 的 dms_source 密碼分開管理,後續使用 psql 連線目標,或建立 DMS Target Endpoint 時,都要輸入這組 RDS 密碼。


以下截圖僅呈現建立 RDS 時的部分設定,完整設定請參照上方表格。
因此次僅為測試實作,所以在建立 RDS 時先將 Collect detailed database and per-query metrics、Enable Enhanced monitoring 以及 Enable automated backup 取消勾選,避免產生額外費用。
儲存空間使用所選規格允許的測試容量,密碼自行設定並妥善保存。
Initial database name 位於 Additional configuration,請填入 orders_lab,它是 PostgreSQL 裡的資料庫名稱,與 AWS 上的執行個體名稱 day29-target-pg 不同。
等待 RDS 狀態變成 Available,記下 Connectivity & security 裡的 Endpoint。

回到來源 EC2 的終端機,先分別登入兩個資料庫,確認連線成功後退出。
注意:psql 連線指令要在 EC2 終端機執行,看到 orders_lab=# 時,代表已經進入資料庫,必須先用 \q 離開,才能執行下一條連線指令。
在 EC2 終端機填入 RDS Endpoint:
export TARGET_DB_HOST='填入目標 RDS Endpoint'
這個變數只保留在目前的終端機工作階段,重新開啟 Session Manager 後需要再設定。
執行:
psql "host=127.0.0.1 port=5432 dbname=orders_lab user=dms_source sslmode=require" -W
輸入前面為 dms_source 設定的密碼,登入後確認連線資訊:
\conninfo
預期顯示:
orders_lab
dms_source
127.0.0.1
這裡使用 127.0.0.1,因為目前就在來源 EC2 上操作,稍後 DMS 連線來源時,則會使用 EC2 的私有 IP。
確認後,離開資料庫:
\q
回到 EC2 終端機後,再執行:
psql "host=$TARGET_DB_HOST port=5432 dbname=orders_lab user=labadmin sslmode=require" -W
這次輸入的是 RDS 的 master password。登入後確認:
\conninfo
預期顯示:
orders_lab
labadmin
確認後,再次離開:
\q
兩邊都能成功登入後,再繼續建立測試資料,後續每次切換來源與目標,都要先離開目前的資料庫,再從 EC2 終端機執行另一條連線指令。
連線來源資料庫後執行:
CREATE TABLE public.orders (
order_id integer PRIMARY KEY,
customer_name varchar(50) NOT NULL,
status varchar(20) NOT NULL,
amount numeric(10, 2) NOT NULL
);
INSERT INTO public.orders
(order_id, customer_name, status, amount)
VALUES
(1001, 'Alice', 'PENDING', 1200.00),
(1002, 'Bob', 'PAID', 800.00),
(1003, 'Carol', 'PENDING', 500.00);
SELECT * FROM public.orders ORDER BY order_id;
order_id 是 Primary Key(主鍵),用來識別每一筆訂單,讓後續修改與刪除能對應到正確資料。
離開來源,改用目標連線指令登入,再確認 \conninfo。
執行:
CREATE TABLE public.orders (
order_id integer PRIMARY KEY,
customer_name varchar(50) NOT NULL,
status varchar(20) NOT NULL,
amount numeric(10, 2) NOT NULL
);
SELECT COUNT(*) FROM public.orders;
預期筆數為 0,不要在目標新增訂單,接下來由 DMS 搬入資料。
DMS 這次會用到三個元件:
| 元件 | 用途 |
|---|---|
| Replication Instance(複寫執行個體) | 執行資料搬移與持續同步 |
| Endpoint(端點) | 保存資料庫連線設定 |
| Migration Task(遷移任務) | 指定搬哪些資料,以及使用哪種搬遷方式 |
進入 DMS → Subnet groups → Create subnet group:
day29-dms-subnets。這是 DMS 使用的子網路群組,與前面 RDS 的 DB Subnet Group 分開建立。
再進入 DMS → Migrate or replicate → Provisioned instances → Create replication instance:
| 項目 | 設定 |
|---|---|
| Name | day29-dms |
| Instance class | 例如區域支援的 dms.t3.small |
| Engine version | 支援本次 PostgreSQL 版本的 DMS 版本 |
| Allocated storage | 50 GiB |
| Availability | Single-AZ |
| VPC | 本次測試 VPC |
| Replication subnet group | day29-dms-subnets |
| Publicly accessible | 不勾選 |
| Security Group | day29-dms-sg |
等待狀態變成 Available。

初次使用 DMS,若服務所需的 IAM Role 不存在,AWS 會自動幫你建立所需的 IAM Role。
Endpoint 用來告訴 DMS:資料庫在哪裡,以及要使用哪組帳號密碼連線,這次需要分別建立來源與目標兩個 Endpoint。
進入 DMS → Endpoints → Create endpoint,先建立來源。
Endpoint type 選擇 Source endpoint,不要勾選 Select RDS DB instance,因為來源是 EC2 上自行安裝的 PostgreSQL。
在 Endpoint configuration 填入:
| 欄位 | 設定 |
|---|---|
| Endpoint identifier | day29-source |
| Descriptive Amazon Resource Name(ARN) | 選填,本次留空 |
| Source engine | PostgreSQL |
| Access to endpoint database | Provide access information manually |
一定要先選擇 Provide access information manually(手動提供存取資訊),才會出現資料庫位址、帳號與密碼欄位,若畫面顯示的是 Secret,代表目前選到 AWS Secrets Manager,請切換成手動輸入。
接著填入:
| 欄位 | 設定 |
|---|---|
| Server name | 來源 EC2 的私有 IPv4 位址 |
| Port | 5432 |
| User name | dms_source |
| Password | 前面使用 \password dms_source 設定的密碼 |
| Secure Socket Layer(SSL)mode | require |
| Database name | orders_lab |
| Endpoint settings | 維持預設,不額外加入設定 |
來源私有 IP 可以在 EC2 → 選取 day29-source-pg → Details → Private IPv4 addresses 找到。
如果 SSL mode 預設為 verify-full,本次請改成 require,require 會加密連線,但不驗證伺服器憑證身分;正式環境應另外準備合適的憑證與驗證設定。
再次進入 Create endpoint,這次 Endpoint type 選擇 Target endpoint。
為了與來源採用相同的填寫方式,本次也不要勾選 Select RDS DB instance,改成手動輸入 RDS 連線資訊:
| 欄位 | 設定 |
|---|---|
| Endpoint identifier | day29-target |
| Descriptive Amazon Resource Name(ARN) | 選填,本次留空 |
| Target engine | PostgreSQL |
| Access to endpoint database | Provide access information manually |
| Server name | 目標 RDS Endpoint 主機名稱 |
| Port | 5432 |
| User name | labadmin |
| Password | 建立 RDS 時設定的 master password |
| Secure Socket Layer(SSL)mode | require |
| Database name | orders_lab |
| Endpoint settings | 維持預設,不額外加入設定 |
RDS Endpoint 可在 RDS → Databases → day29-target-pg → Connectivity & security 找到。
在 Test endpoint connection 選擇:
day29-dms。執行 Run test,來源與目標都要確認結果為 Successful,若建立時沒有執行測試,也可以建立後選取 Endpoint,再從 Actions → Test connection 測試,Endpoint 測試成功,就代表基本連線與驗證通過。


進入 DMS → Migrate or replicate → Tasks → Create task:
在 Task configuration 中做以下設定:
| 畫面欄位 | 本次設定 |
|---|---|
| Task identifier | day29-orders-full-cdc |
| Descriptive Amazon Resource Name(ARN) | 選填,本次留空 |
| Source database endpoint | day29-source |
| Target database endpoint | day29-target |
| Why are you moving data between the same database engine today? | 選填,本次可不選 |
| Task mode | Provisioned |
| Provisioned instance | 選擇前面建立的 day29-dms |
| Task type | Migrate and replicate |
這個 Migration type 會先搬移既有資料,再持續同步後續變更。
其餘 Task configuration 維持預設值,本次沒有大型物件欄位或跨資料表的外鍵關聯,正式系統仍需依資料型別與關聯條件調整。
在 Settings → Data validation 選擇 Turn off,本次不啟用 DMS 自動資料驗證,後續會透過 SQL 查詢,手動核對來源與目標的測試訂單。
在 Table mappings 選擇 JSON editor,填入:
{
"rules": [
{
"rule-type": "selection",
"rule-id": "1",
"rule-name": "include-orders",
"object-locator": {
"schema-name": "public",
"table-name": "orders"
},
"rule-action": "include"
}
]
}
這份規則只包含 public.orders。

繼續往下設定:
| 項目 | 本次設定 |
|---|---|
| Premigration assessment(搬遷前評估) | 取消勾選 |
| Migration task startup configuration → Start migration task | Manually later(稍後手動啟動) |
搬遷前評估會檢查可能影響搬遷的問題,本次只有一張簡單的測試資料表故先不啟用,正式搬遷時建議執行。
確認後點選 Create task,因為選擇稍後手動啟動,此時任務不會立即開始搬移資料。
確認來源與目標 Endpoint 都已測試成功,再選取 day29-orders-full-cdc,手動啟動任務。
啟動後,進入任務的 Table statistics 查看:
Not enabled 是預期結果。Table completed 表示資料表已完成載入,資料內容是否正確,仍需透過後續 SQL 查詢核對。
完整載入結束後,任務會繼續執行 CDC,不需要另外建立任務。

先分清楚要接著同步,還是重新搬一次資料:
| 目的 | 操作 |
|---|---|
| 從停止的位置繼續同步 | Resume |
| 重新執行整個任務的完整載入 | Restart,選擇重新載入資料 |
| 任務執行期間,只重新載入指定資料表 | Reload tables |
Do nothing 不會自動清空目標資料 ,重新載入前如果目標還留有上次的訂單,可能因主鍵重複而失敗,需要依重新載入流程處理既有測試資料。
清除資料前務必確認連到目標,而且資料可以丟棄,單純使用 Resume 繼續 CDC 時,不要清空目標資料表。
在來源 EC2 上,使用目標連線指令登入 RDS,執行:
SELECT * FROM public.orders ORDER BY order_id;
完整載入完成後,預期看到:
| order_id | customer_name | status | amount |
|---|---|---|---|
| 1001 | Alice | PENDING | 1200.00 |
| 1002 | Bob | PAID | 800.00 |
| 1003 | Carol | PENDING | 500.00 |
這表示自建 PostgreSQL 原本的三筆訂單,已經搬到 RDS。

離開目標,使用來源連線指令登入,先以 \conninfo 確認,再執行:
BEGIN;
INSERT INTO public.orders
(order_id, customer_name, status, amount)
VALUES
(1004, 'David', 'PENDING', 1500.00);
UPDATE public.orders
SET status = 'PAID'
WHERE order_id = 1001;
DELETE FROM public.orders
WHERE order_id = 1003;
COMMIT;
SELECT * FROM public.orders ORDER BY order_id;
這次操作會新增 1004、修改 1001,並刪除 1003。

刪除操作只用來驗證 CDC,正式訂單是否允許刪除,仍需依業務規則決定。
重新連線目標,執行:
SELECT * FROM public.orders ORDER BY order_id;
等待同步後,預期結果為:
| order_id | customer_name | status | amount |
|---|---|---|---|
| 1001 | Alice | PAID | 1200.00 |
| 1002 | Bob | PAID | 800.00 |
| 1004 | David | PENDING | 1500.00 |
沒有重新執行完整載入,RDS 卻已反映這三項變更,就能確認 CDC 正在運作。

若資料持續未更新,再檢查 Task、Table statistics 與 CloudWatch Logs。
本次是單向同步:來源自建 PostgreSQL 負責寫入,目標 RDS 只用來查詢驗證 ,不要另外修改目標訂單,以免干擾同步。
這次完成的是從自建 PostgreSQL 到 RDS 的資料搬移與持續同步,正式搬遷還有三項工作:
例如正式訂單若使用 Sequence 自動產生編號,DMS 持續複寫不會自動同步序列狀態,完成最終同步並停止複寫後,要確認並校正目標的下一個編號,再開放新訂單寫入。
這些工作需要另外演練,才能判斷是否符合上一篇設定的停機時間。
保存需要的截圖與測試結果後,依序清理:
只停止 DMS Task,Replication Instance 仍會持續計費 ,不再使用時需要刪除執行個體。
這次透過 DMS 實作,驗證了自建 PostgreSQL 搬到 RDS 時,既有資料與後續變更如何同步,實作完成後,也別忘了處理持續計費的資源。
時間很快地來到最後一篇了,最後會簡短介紹成本最佳化,從找出主要花費、清理閒置資源,到評估穩定用量的折扣方案,並對這 30 天的內容做個總結!