在 RHEL 10.0 啟用 epel-release
sudo dnf update -y
sudo subscription-manager repos --enable codeready-builder-for-rhel-10-x86_64-rpms
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
sudo dnf update -y
sudo subscription-manager repos --enable codeready-builder-for-rhel-10-x86_64-rpms
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
PS C:\> aws ec2-instance-connect ssh --instance-id i-0aa38de21acf2aa1c --region ap-south-1
Bad permissions. Try removing permissions for user: \\OWNER RIGHTS (S-1-3-4) on file C:/Users/hikari/AppData/Local/Temp/tmpm9m1bf7j/private-key.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions for 'C:\\Users\\hikari\\AppData\\Local\\Temp\\tmpm9m1bf7j\\private-key' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "C:\\Users\\hikari\\AppData\\Local\\Temp\\tmpm9m1bf7j\\private-key": bad permissions
[email protected]: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
截至 2025/06/11 的驗證。
PS C:\> wsl -- aws ec2-instance-connect ssh --instance-id i-0aa38de21acf2aa1c --region ap-south-1
, #_
~\_ ####_ Amazon Linux 2023
~~ \_#####\
~~ \###|
~~ \#/ ___ https://aws.amazon.com/linux/amazon-linux-2023
~~ V~' '->
~~~ /
~~._. _/
_/ _/
_/m/'
Last login: Tue Jun 10 22:50:33 2025 from 192.168.0.183
[ec2-user@ip-192-168-0-4 ~]$
為什麼?
降級後就能連線。
希望能修正。
參考: https://github.com/aws/aws-cli/issues/9114
msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2-2.17.35.msi
EC2 Instance Connect 是用來簡化對 AWS EC2 執行個體的 SSH 連線的服務。
過去的 SSH 連線方式需要事先在執行個體放置公開金鑰,但使用 EC2 Instance Connect 可以將臨時的 SSH 公開金鑰傳送到執行個體以建立連線。(不過,除部分 AMI 外,需要安裝 Instance Connect 的套件)
連線到執行個體的方法有好幾種。

直接從網際網路連線的方式需要經由 Internet Gateway 或 NAT Gateway,且需要公有 IP 位址,因此若限定在私有網路則無法使用。
可以直接使用 ssh 指令,因此是最簡單的方式。
ssh <使用者名稱>@<公有 IP 位址>
使用 AWS CLI 並透過 EC2 Instance Connect 端點連線時,不需要公有 IP 位址。
另外,還可以省下那部分的費用(每月數百日圓)。
使用 AWS CLI 可用類似下面的指令連線,但需事先匯入金鑰對並為執行個體設定金鑰對。
例如可以使用以下指令:
aws ec2-instance-connect ssh --private-key-file .ssh/id_ed25519 --os-user <使用者名稱> --instance-id <執行個體 ID> --connection-type eice
※ 不過,要事先取得存取金鑰並用 aws configure 設定好。
如果不想讓執行個體連到網際網路,且想使用非官方 AMI,採用此連線方式最合適。
若是 Amazon Linux 或 Ubuntu,只要建立 Instance Connect 端點,就可以從管理主控台連到執行個體。
不過,除部分 AMI 外,仍需安裝 Instance Connect 的套件。
詳情請見:https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-connect-set-up.html
需為 Session Manager 建立兩個端點,並將允許透過 Session Manager 連線的 IAM 角色附加到執行個體。
此外,除部分 AMI 外,需安裝 Session Manager 的套件。
使用序列埠主控台可以直接連到執行個體。請注意,若未設定密碼就無法登入。
預設會允許所有流量,因此若使用預設設定通常不需要特別調整。
以下列出最基本需要的設定。
入站規則需允許 SSH(22)。
因為執行個體的 SSH 伺服器預設使用 22 埠,故需允許該埠。
出站規則需允許自訂 TCP(1024-65535)。
1024-65535 是 SSH 連線時客戶端會使用的埠範圍。
入站規則需允許 SSH(22)。
此設定為必要。
安全性群組是有狀態(stateful)的,所以通常不需要設定出站規則。
因為為有狀態,通常不需要設定入站規則。
需允許 SSH(22)。
因為要對執行個體的 22 埠進行通訊,所以必須允許此埠。
從官方頁面取得 AMI。
https://rockylinux.org/ja-JP/download
選擇要設定給實例的架構 (ARM (aarch64)),並選擇 Cloud Images 裡的 AWS AMI。

以版本號過濾,找到符合條件的映像。

AMI ID 無法直接複製,因此點擊 Deploy 按鈕,然後從 AWS 主控台複製。
用 AMI ID 搜尋會出現如下

用擁有者過濾會比較好。
擁有者 = 792107900819

ssh-keygen -t ed25519 指令產生公鑰,將 .ssh/id_ed25519.pub 匯入成 Key pair比起 NAT Gateway,使用公開 IP 比較便宜,所以建立 Elastic IP。
架構圖大概長這樣。


建立 EC2 Instance Connect 端點後,可以從 AWS CLI 登入。
因此我用以下條件建立。
在 PC 上打開終端機,執行以下指令。
aws ec2-instance-connect ssh --private-key-file .ssh/id_ed25519 --os-user rocky --instance-id i-*****************
Rocky Linux 的 AMI 映像沒有包含 Instance Connect 套件,無法從管理控制台連線。因此需要安裝套件。
參考 https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-connect-set-up.html 下載套件。
curl https://amazon-ec2-instance-connect-us-west-2.s3.us-west-2.amazonaws.com/latest/linux_arm64/ec2-instance-connect.rhel8.rpm -o /tmp/ec2-instance-connect.rpm
curl https://amazon-ec2-instance-connect-us-west-2.s3.us-west-2.amazonaws.com/latest/linux_amd64/ec2-instance-connect-selinux.noarch.rpm -o /tmp/ec2-instance-connect-selinux.rpm
sudo dnf install -y /tmp/ec2-instance-connect.rpm /tmp/ec2-instance-connect-selinux.rpm
安裝完成後,就可以從 AWS 管理控制台存取。

我做了 CDK 範例,放上來供參考。
請記得更改 keyName(Key pair)的名稱。
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
export interface RockyLinuxStackProps extends cdk.StackProps {
}
export class RockyLinuxStack extends cdk.Stack {
public constructor(scope: cdk.App, id: string, props: RockyLinuxStackProps = {}) {
super(scope, id, props);
// Resources
const ec2dhcpOptions = new ec2.CfnDHCPOptions(this, 'EC2DHCPOptions', {
domainName: 'ap-south-1.compute.internal',
domainNameServers: [
'AmazonProvidedDNS',
],
],
});
ec2dhcpOptions.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2InternetGateway = new ec2.CfnInternetGateway(this, 'EC2InternetGateway', {
{
value: 'igw',
key: 'Name',
},
],
});
ec2InternetGateway.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2vpc = new ec2.CfnVPC(this, 'EC2VPC', {
cidrBlock: '10.0.0.0/16',
enableDnsSupport: true,
instanceTenancy: 'default',
enableDnsHostnames: true,
{
value: 'vpc',
key: 'Name',
},
],
});
ec2vpc.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2VPCGatewayAttachment = new ec2.CfnVPCGatewayAttachment(this, 'EC2VPCGatewayAttachment', {
vpcId: ec2vpc.ref,
internetGatewayId: ec2InternetGateway.ref,
});
ec2VPCGatewayAttachment.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2NetworkAcl = new ec2.CfnNetworkAcl(this, 'EC2NetworkAcl', {
vpcId: ec2vpc.ref,
],
});
ec2NetworkAcl.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2RouteTable = new ec2.CfnRouteTable(this, 'EC2RouteTable', {
vpcId: ec2vpc.ref,
});
ec2RouteTable.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2SecurityGroup = new ec2.CfnSecurityGroup(this, 'EC2SecurityGroup', {
groupDescription: 'launch-wizard-1 created 2025-04-27T00:11:58.641Z',
groupName: 'launch-wizard-1',
vpcId: ec2vpc.ref,
securityGroupIngress: [
{
cidrIp: '0.0.0.0/0',
ipProtocol: 'tcp',
fromPort: 22,
toPort: 22,
},
{
cidrIp: '0.0.0.0/0',
ipProtocol: 'icmp',
fromPort: 8,
toPort: -1,
},
],
securityGroupEgress: [
{
cidrIp: '0.0.0.0/0',
ipProtocol: '-1',
fromPort: -1,
toPort: -1,
},
],
});
ec2SecurityGroup.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2Subnet = new ec2.CfnSubnet(this, 'EC2Subnet', {
vpcId: ec2vpc.ref,
mapPublicIpOnLaunch: false,
enableDns64: false,
availabilityZoneId: 'aps1-az1',
privateDnsNameOptionsOnLaunch: {
EnableResourceNameDnsARecord: false,
HostnameType: 'ip-name',
EnableResourceNameDnsAAAARecord: false,
},
cidrBlock: '10.0.0.0/20',
ipv6Native: false,
{
value: 'subnet-public1-ap-south-1a',
key: 'Name',
},
],
});
ec2Subnet.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2InstanceConnectEndpoint = new ec2.CfnInstanceConnectEndpoint(this, 'EC2InstanceConnectEndpoint', {
preserveClientIp: false,
securityGroupIds: [
ec2SecurityGroup.attrGroupId,
],
subnetId: ec2Subnet.attrSubnetId,
});
ec2InstanceConnectEndpoint.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2vpcdhcpOptionsAssociation = new ec2.CfnVPCDHCPOptionsAssociation(this, 'EC2VPCDHCPOptionsAssociation', {
vpcId: ec2vpc.ref,
dhcpOptionsId: ec2dhcpOptions.ref,
});
ec2vpcdhcpOptionsAssociation.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2RouteHg = new ec2.CfnRoute(this, 'EC2RouteHG', {
routeTableId: ec2RouteTable.ref,
destinationCidrBlock: '0.0.0.0/0',
gatewayId: ec2InternetGateway.ref,
});
ec2RouteHg.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2SubnetNetworkAclAssociation = new ec2.CfnSubnetNetworkAclAssociation(this, 'EC2SubnetNetworkAclAssociation', {
networkAclId: ec2NetworkAcl.ref,
subnetId: ec2Subnet.ref,
});
ec2SubnetNetworkAclAssociation.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2SubnetRouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation(this, 'EC2SubnetRouteTableAssociation', {
routeTableId: ec2RouteTable.ref,
subnetId: ec2Subnet.ref,
});
ec2SubnetRouteTableAssociation.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2Instance = new ec2.CfnInstance(this, 'EC2Instance', {
tenancy: 'default',
instanceInitiatedShutdownBehavior: 'stop',
cpuOptions: {
threadsPerCore: 1,
coreCount: 2,
},
blockDeviceMappings: [
{
ebs: {
volumeType: 'gp3',
iops: 3000,
volumeSize: 10,
encrypted: false,
deleteOnTermination: true,
},
deviceName: '/dev/sda1',
},
],
availabilityZone: 'ap-south-1a',
privateDnsNameOptions: {
enableResourceNameDnsARecord: false,
hostnameType: 'ip-name',
enableResourceNameDnsAaaaRecord: false,
},
ebsOptimized: true,
disableApiTermination: false,
keyName: 'hikari',
sourceDestCheck: true,
placementGroupName: '',
networkInterfaces: [
{
privateIpAddresses: [
{
privateIpAddress: '10.0.3.59',
primary: true,
},
],
secondaryPrivateIpAddressCount: 0,
deviceIndex: '0',
groupSet: [
ec2SecurityGroup.ref,
],
ipv6Addresses: [
],
subnetId: ec2Subnet.ref,
associatePublicIpAddress: true,
deleteOnTermination: true,
},
],
imageId: 'ami-0415efd8380284dc4',
instanceType: 't4g.medium',
monitoring: false,
],
creditSpecification: {
cpuCredits: 'unlimited',
},
});
ec2Instance.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2ElasticIp = new ec2.CfnEIP(this, 'EC2ElasticIp', {
domain: 'vpc',
{
key: 'Name',
value: 'elastic-ip',
},
],
});
ec2ElasticIp.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
const ec2EipAssociation = new ec2.CfnEIPAssociation(this, 'EC2EipAssociation', {
eip: ec2ElasticIp.ref,
instanceId: ec2Instance.ref,
});
ec2EipAssociation.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
}
}
※ 此處介紹的免費軟體一般可免費使用,與開源軟體有所區別。
選擇「具名管道」,並將管道名稱設定為「COM1」。

使用 sudo nano /etc/default/grub 開啟 GRUB 設定檔。
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash console=ttyS0,115200n8"
儲存後,執行以下指令套用 GRUB 設定:
sudo update-grub
設定服務以允許透過序列埠登入。
sudo systemctl enable [email protected]
sudo systemctl start [email protected]
以系統管理員身分啟動。

以系統管理員身分啟動。
| 序列線路 | 速率 | 連線類型: |
|---|---|---|
| \.\pipe\COM1 | 115200 | Serial |
設定以上項目。


以系統管理員身分啟動。

[System.Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("utf-8")
[System.Console]::InputEncoding = [System.Text.Encoding]::GetEncoding("utf-8")
plink.exe -serial \\.\pipe\COM1 -sercfg 115200,8,n,1,N
按 Ctrl + C 結束。
VOICEVOX 是由編輯器、引擎與核心組成的。
參考: 整體構成
編輯器是應用程式、引擎是 HTTP 伺服器、核心則是執行語音合成處理的模組。
也就是說,編輯器對引擎呼叫 REST API(以下簡稱 API)。
因此這篇文章要觀察這個 API 的內容。
API 的擷取我使用 Wireshark。
我用 http and tcp.port == 50021 做了過濾,結果如下:

啟動時似乎會讀取以下資訊:
/version/engine_manifest/speakers(像是 ずんだもん 等角色名單)/singers(同上)取得 speakers 和 singers 之後,會更詳細地取得各角色的資訊(/speaker_info?speaker_uuid=xxx, /singer_info?speaker_uuid=xxx)。

接著我用 ずんだもん 發送語音合成請求,觀察 API。

語音的取得流程似乎如下:
/accent_phrases 取得重音/語調資訊/synthesis?speaker=3 合成 ずんだもん 的聲音在 (2.) 發送的請求主體看起來和 (1.) 的回應類似,像下面這樣。
因此流程是先在 (1.) 取得重音資訊,接著在 (2.) 依據這些資訊合成語音。
我使用 httpie 這個工具來呼叫 API。

可以看到 ずんだもん(Normal)的 id 是 3。
我用「ずんだもんなのだ」取得重音資訊。(與取得 speaker 不同,這是用 POST)

建立如下的請求主體:
{
"accent_phrases": <從 /accent_phrases 取得的資料>,
"speedScale": 1,
"pitchScale": 0,
"intonationScale": 1,
"volumeScale": 1,
"prePhonemeLength": 0.1,
"postPhonemeLength": 0.1,
"outputSamplingRate": 24000,
"outputStereo": false,
"kana": ""
}
因為 httpie 好像不能處理 wav,所以改用 PowerShell 送請求。
# 定義 URL 與 JSON 資料
$url = 'http://localhost:50021/synthesis?speaker=3'
$jsonBody = @"
{
"accent_phrases": [
{
"moras": [
{
"text": "ズ",
"consonant": "z",
"consonant_length": 0.12722788751125336,
"vowel": "u",
"vowel_length": 0.11318323761224747,
"pitch": 5.773037910461426
},
{
"text": "ン",
"consonant": null,
"consonant_length": null,
"vowel": "N",
"vowel_length": 0.09306197613477707,
"pitch": 6.108947277069092
},
{
"text": "ダ",
"consonant": "d",
"consonant_length": 0.04249810427427292,
"vowel": "a",
"vowel_length": 0.09372275322675705,
"pitch": 6.09743070602417
},
{
"text": "モ",
"consonant": "m",
"consonant_length": 0.07012023776769638,
"vowel": "o",
"vowel_length": 0.1172478124499321,
"pitch": 5.932623386383057
},
{
"text": "ン",
"consonant": null,
"consonant_length": null,
"vowel": "N",
"vowel_length": 0.06496299058198929,
"pitch": 5.745952129364014
},
{
"text": "ナ",
"consonant": "n",
"consonant_length": 0.038462959229946136,
"vowel": "a",
"vowel_length": 0.08576127141714096,
"pitch": 5.5794854164123535
}
],
"accent": 1,
"pause_mora": null,
"is_interrogative": false
},
{
"moras": [
{
"text": "ノ",
"consonant": "n",
"consonant_length": 0.05504273623228073,
"vowel": "o",
"vowel_length": 0.0903041884303093,
"pitch": 5.551316261291504
},
{
"text": "ダ",
"consonant": "d",
"consonant_length": 0.05024997144937515,
"vowel": "a",
"vowel_length": 0.20450790226459503,
"pitch": 5.633930206298828
}
],
"accent": 2,
"pause_mora": null,
"is_interrogative": false
}
],
"speedScale": 1,
"pitchScale": 0,
"intonationScale": 1,
"volumeScale": 1,
"prePhonemeLength": 0.1,
"postPhonemeLength": 0.1,
"outputSamplingRate": 24000,
"outputStereo": false,
"kana": ""
}
"@
# 建立 HTTP 標頭
$headers = @{
'Content-Type' = 'application/json'
}
# 發送 POST 請求並取得回應
$response = Invoke-WebRequest -Uri $url -Method Post -Headers $headers -Body $jsonBody -OutFile "output.wav"
# 開啟並播放
start output.wav
VOICEVOX:ずんだもん
以上!
「16 進位的小數 0.248 轉換成十進位的分數是什麼?」的問題,我將簡單介紹解法。
16 進位的問題使用 2 進位
將 16 進位轉換成 2 進位
0.248(16) = 0.0010 0100 1000(2)
去掉小數點
0.0010 0100 1000(2)
= 0.0010 0100 1000(2) × 0010 0000 0000(2) / 0010 0000 0000(2)
= 0100 1001(2) / 0010 0000 0000(2)
將 2 進位轉換成 10 進位
0100 1001(2) / 0010 0000 0000(2)
= (64 + 8 + 1) / 512
= 73 / 512