顯示具有 devops 標籤的文章。 顯示所有文章
顯示具有 devops 標籤的文章。 顯示所有文章

2026年3月10日 星期二

[macOS] 使用 macstrap 批次快速設定 Mac Mini M4 與架設 OpenClaw 環境 @ macOS

最近拿到一台 Mac mini M4 32GB/1TB ,之前很習慣用 Notes 貼一下快速安裝的指令,想著想著自己十多年前都在用 ansible 管理百台 Ubuntu servers ,這個年代一直貼指令微遜 XD 就 vibe coding 一下,請 AI 小弟幫忙實現工具,於是乎就長出個 macstrap 工具,其特色就是讓不熟悉 ansible 的人也能快速上手,而安裝途徑是透過 ssh remote command ,這樣搞定一台後,未來有多台 mac mini 要初始化,也可以快速搞定。

首先,先把 Mac mini 開箱後,設定好登入的帳號密碼,接著在 System Settings -> General -> Sharing -> Remote login 打開,這樣就完成很基本的 ssh 可登入機制,然而 macstrap 是基於 python 的工具,所以 Mac mini 本地需要故意打一下 python 指令觸發系統去下載安裝 Python3

接下來就是 macstrap 領域了:

```
% cd /tmp
% python3 -m venv venv
% source /tmp/venv/bin/activate
% pip install macstrap
...
% macstrap --version 
macstrap, version 1.1.0
```

初始化一些範例來用:

```
% macstrap init --exmaples
% tree -L 1 examples
examples
├── ai-cli
├── openclaw
├── php8.3-dev
└── utilities-dev

5 directories, 0 files
```

設置連上 Mac mini 的帳密,假設 Mac mini IP 是 192.168.1.100 (或 mac-mini.local) 登入帳號是 macuser 且擁有 sudo 權限:

```
% macstrap ssh-auth mac-mini.local --user macuser
```

替 192.168.1.100 (或 mac-mini.local) 安裝 AI CLI:

```
% macstrap run --config examples/ai-cli mac-mini.local
...

TASK [nvm : Install global npm packages] *****
ok: [mac-mini.local] => (item=@github/copilot)
ok: [mac-mini.local] => (item=@anthropic-ai/claude-code)
ok: [mac-mini.local] => (item=@openai/codex)
ok: [mac-mini.local] => (item=@google/gemini-cli)

...

```

安裝 OpenClaw:

```
% macstrap run --config examples/openclaw mac-mini.local
...
TASK [nvm : Install global npm packages] *****
ok: [user-macmini.local] => (item=openclaw)
...
```

接著登入 mac-mini.local 後,就可以用 openclaw onboard 啟動他來做設定:

```
% ssh macuser@mac-mini.local
Now using node v22.22.1 (npm v10.9.4)
[env] node: v22.22.1 | npm: 10.9.4 | Python 3.9.6

% openclaw onboard

🦞 OpenClaw 2026.3.8 (3caab92)
   Runs on a Raspberry Pi. Dreams of a rack in Iceland.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██
██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██
██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
                  🦞 OPENCLAW 🦞                    
 
┌  OpenClaw onboarding
◇  Security ─────────────────────────────────────────────────────────────╮
│                                                                        │
│  Security warning — please read.                                       │
│                                                                        │
│  OpenClaw is a hobby project and still in beta. Expect sharp edges.    │
│  By default, OpenClaw is a personal agent: one trusted operator        │
│  boundary.                                                             │
│  This bot can read files and run actions if tools are enabled.         │
│  A bad prompt can trick it into doing unsafe things.                   │
│                                                                        │
│  OpenClaw is not a hostile multi-tenant boundary by default.           │
│  If multiple users can message one tool-enabled agent, they share      │
│  that delegated tool authority.                                        │
│                                                                        │
│  If you’re not comfortable with security hardening and access          │
│  control, don’t run OpenClaw.                                          │
│  Ask someone experienced to help before enabling tools or exposing it  │
│  to the internet.                                                      │
│                                                                        │
│  Recommended baseline:                                                 │
│  - Pairing/allowlists + mention gating.                                │
│  - Multi-user/shared inbox: split trust boundaries (separate           │
│    gateway/credentials, ideally separate OS users/hosts).              │
│  - Sandbox + least-privilege tools.                                    │
│  - Shared inboxes: isolate DM sessions (`session.dmScope:              │
│    per-channel-peer`) and keep tool access minimal.                    │
│  - Keep secrets out of the agent’s reachable filesystem.               │
│  - Use the strongest available model for any bot with tools or         │
│    untrusted inboxes.                                                  │
│                                                                        │
│  Run regularly:                                                        │
│  openclaw security audit --deep                                        │
│  openclaw security audit --fix                                         │
│                                                                        │
│  Must read: https://docs.openclaw.ai/gateway/security                  │
│                                                                        │
├────────────────────────────────────────────────────────────────────────╯
◆  I understand this is personal-by-default and shared/multi-user use requires
 lock-down. Continue?
│  ○ Yes / ● No

```

2015年11月11日 星期三

Ansible 筆記 - 透過 ansible 指令、ansible-playbook 執行批次腳本等用法快速上手 @ Ubuntu 14.04

剛接手一份用 Puppet 維護的服務,結果 Puppet 還沒玩熟,就先來改玩一下 Ansible,而 Ansible 也是一套機器部署的軟體,最大特色是不需要 master-slave(client) 架構,也就是被管理的機器不用安裝 ansible 啦!另外還有類似市集功能,例如你想要找 nginx 的設定方式,那只要上市集找一下,接著把它下載回來,套用一下,即可部署,十分便利。

首先先摸一下 ansible 指令,熟了再把玩 ansible-playbook 批次指令,之後再從 ansible-galaxy 從市集找設定檔。

安裝 ansible ( http://docs.ansible.com/ansible/intro_installation.html#id17 ):

$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible
$ ansible --version
ansible 1.9.4


接著,來試試 ansible 指令吧!這個指令的用法就像執行 ssh remote commands 一樣:

$ ansible localhost -m raw -a "date"
localhost | success | rc=0 >>
Wed Nov 11 11:01:21 UTC 2015

$ ansible localhost -m setup
...用 JSON Format 列出系統資訊...

$ ansible localhost -m setup | grep -i ubuntu
        "ansible_distribution": "Ubuntu",
            "description": "Ubuntu 14.04.3 LTS",
            "id": "Ubuntu",


然而,localhost 是一個默認的字眼,若要對遠端機器操作時,必須先寫定 hosts 對應表,預設是使用 /etc/ansible/hosts 位置,但也可以透過 -i filename 來使用,這裡使用 -i 來試試,並用 127.0.0.1 為例。

$ cat hosts
[webserver]
127.0.0.1

$ ansible -i etc/ansible/hosts webservers --list-hosts
    127.0.0.1

$ ansible -i etc/ansible/hosts webservers -m raw -a "date"
127.0.0.1 | FAILED | rc=255 >>

$ ansible -i etc/ansible/hosts --private-key=~/.ssh/id_rsa webservers -m raw -a "date"
127.0.0.1 | success | rc=0 >>
Wed Nov 11 11:27:36 UTC 2015

$ ansible -i etc/ansible/hosts --private-key=~/.ssh/id_rsa webservers -m setup | grep -i cpu
            "Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz",
            "Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz"
        "ansible_processor_vcpus": 2,


其中 -m 代表要採用的模組,而 -a 是該模組的參數資訊。 raw 代表 -a 後頭的指令就給他執行下去就對了。所以一些常用的 ssh remote command 則是可以用 -m raw 來替代。其中 --private-key 是指定登入用的 key,而 -u 還可以指定登入的使用者。

接下來透過 yml 檔案描述來批次處理大量指令:

$ cat webserver.yml
---
- hosts: webserver
  tasks:
  - shell: date

$ ansible-playbook -i etc/ansible/hosts --private-key=~/.ssh/id_rsa webserver.yml
PLAY [webserver] **************************************************

GATHERING FACTS ***************************************************************
ok: [127.0.0.1]

TASK: [shell date] ************************************************************
changed: [127.0.0.1]

PLAY RECAP ********************************************************************
127.0.0.1            : ok=2    changed=1    unreachable=0    failed=0


以上是非常簡單的批次任務,在複雜下去之前,先介紹一招透過 yml 檔案來新增 ssh remote key 方式(等同於 ssh-copy-id 功能):

$ cat add_authorized_key.yml
- hosts: '{{host}}'
  remote_user: '{{user}}'
  vars:
      ssh_private_key_path: '{{ ssh_key_file }}'

  tasks:
  - name: Add RSA key to the remote host
    authorized_key: user='{{user}}' key="{{lookup('file', ssh_private_key_path )}}"

$ ansible-playbook add_authorized_key.yml -i hosts

PLAY [{{host}}] ***************************************************************
skipping: no hosts matched

PLAY RECAP ********************************************************************

$ ansible-playbook add_authorized_key.yml -i hosts --ask-pass --extra-vars "user=ubuntu host=localhost ssh_key_file=ansible-deploy.pub"                                                                                                            
SSH password:

PLAY [localhost] **************************************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [Add RSA key to the remote host] ****************************************
changed: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0


這個過程就是透過 add_authorized_key.yml 腳本,以及搭配的參數去執行,將 ansible-deploy.pub 檔案添加至 ubuntu@localhost 的 ~/.ssh/authorized_key 且確保此動作重複執行也不會添加重複筆數,在執行一次得到的結果:

$ ansible-playbook add_authorized_key.yml -i hosts --extra-vars "user=ubuntu host=localhost ssh_key_file=ansible-deploy.pub"                                                                                                            
SSH password:

PLAY [localhost] **************************************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [Add RSA key to the remote host] ****************************************
changed: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0


可以清楚看到最後一個回報的 changed=0 ,代表此執行執行成功但沒有改變東西。且由於 key 已經添加,這次可以去掉 --ask-pass 的用法。這件事代表可以每次都重跑一樣的工作,但已經生效的不會再重做。

接著來寫個範本是每次執行都會把 EC2 系統更新至最新版(-u 代表要用 ubuntu 帳號登入,-s 則是用 sudo 執行):

$ cat update-ec2-ubuntu.yml
---
- hosts: ec2-servers
  tasks:
  - apt: upgrade=dist

$ ansible-playbook update-ec2-ubuntu.yml -i hosts --private-key=ansible-deploy.pem -u ubuntu -s
PLAY [ec2-servers] **************************************************

GATHERING FACTS ***************************************************************
ok: [XX.XX.XX.XX]

TASK: [apt upgrade=dist] ******************************************************

changed: [XX.XX.XX.XX]

PLAY RECAP ********************************************************************
XX.XX.XX.XX             : ok=2    changed=1    unreachable=0    failed=0


同理再跑一次,可以看到 changed=0,代表第二次已沒有需要更新的。

未來則就可以依照需求撰寫各台機器的部署腳本,其中還有更豐富的 roles 跟 ansible-galaxy 還沒把玩,晚點再來試試吧。