在 Openwrt 上使用迷雾通透明代理局域网流量

前言

(如果你不想听我 BB, 可以直接跳转到 最后一段: #结论.)

先解释一下为什么要这么做.

我原本用的是 clash + 澳洲 VPS (V2Ray), 奈何最近 GFW 相当疯狂, 从每个星期一封进步到现在的一天一封, 甚至上午换 IP 下午封, 强度这么大, 这谁吃得消? 我尝试使用 Oracle 提供的 API 检测封杀并自动切换 IP, 但是我的功力不到家, 还有就是 Oracle 的文档太过迷惑, 我愣是没写出来.

閱讀全文 在 Openwrt 上使用迷雾通透明代理局域网流量

WordPress Docker 優化: 添加 PhpRedis、Memcached 支持

環境:
Docker + WordPress, 用官方默認的 Apache 驅動, 不是 fpm.

因為 PhpRedis 不屬於 PHP 內置的模塊, 而 PHP 內置了一個 Predis 又太慢太蛋疼, 所以要重新構建 Docker Image 為 PHP 添加 Redis 模塊支持.

為了添加這個 Redis 模塊, 我特別做了一些搜索, 發現網上清一色的全部是 本教程只適合 fpm 版本云云. 我個人認為這是一種迷思啊, 因為不論是用 php-fpm 還是 apache + php-mod 應該都和 php 的插件沒有太多關係吧. 所以雖然上面環境說的是 wordpress-apache , 用 wordpress-fpm 的朋友照著這個教程做也沒差.

構建 Docker Image

mkdir /tmp/build
cd /tmp/build
# 添加 Memcached 支持
cat > /tmp/build/Dockerfile << EOF
FROM wordpress:latest
RUN apt-get update
RUN apt-get install -y libz-dev libmemcached-dev && \
    pecl install memcached && \ 
    docker-php-ext-enable memcached && \
RUN rm -rf /tmp/pear && \
    apt-get clean
EOF
# 添加 Redis 支持
cat > /tmp/build/Dockerfile << EOF
FROM wordpress:latest
RUN pecl install -o -f redis && \
    docker-php-ext-enable redis
RUN rm -rf /tmp/pear
EOF

關於 Memcached vs Redis… 這個網上都可以搜到吧. 我的意見是, 兩者都能給一個 WordPress 小博客提供相當不錯的 Object Cache. 效能的話, Redis 略勝一籌, 但是要求專門開一個 Redis 容器. 而 Memcached 安裝好之後直接用就可以了. 所以還是根據服務器的配置來選吧.

docker build -t wordpress_extended .
閱讀全文 WordPress Docker 優化: 添加 PhpRedis、Memcached 支持

WordPress + Cloudflared + V2ray 異地組網並隱藏流量

目標

  • 異地組網, 遠程訪問家中環境
  • 結合海外 VPS 翻牆 ( 不在此篇討論 )
  • 使用 V2Ray TLS 加密流量
  • 將 TLS 流量隱藏在正常的 HTTPS 流量中達到混淆目的
  • 使用 Cloudflare 的內網穿透服務, 達到防探測目的 ( 服務器直接和 CF 溝通, 沒有端口暴露 )
  • 使用 Cloudflare 的 CDN 服務, 達到防牆、加速作用

如果你並不是想要做異地組網, 而是想要在 VPS 上跑 (Nginx/Caddy/Apache) + WP + V2Ray, 那麼你其實用不著這麼複雜. 你只需要看這篇文章就可以了.

(這篇博文主要是討論一個沒多少人做過的方案的可行性, 所以你會看到後面還有一大通廢話; 作為教程本文是不合格的, 如果想要尋找教程請出門右轉谷歌搜索. )

網絡拓墣示意圖
閱讀全文 WordPress + Cloudflared + V2ray 異地組網並隱藏流量

Docker 無縫切換鏡像

警告: 我只是一個正在學習 Docker 的中學生,
一切在生產環境嘗試本文所述技巧所導致的損失都是自作自受.

有的時候, Docker 鏡像更新了, 但是容器並沒有更新, 你想要用更新過後的鏡像來部署容器; 奈何你並沒有像 Portainer 這樣的 GUI 介面, 你早就不記得當初部署容器時執行的指令了, 碰到這種情況應該怎麼辦?

下文便以更新 Portainer 為例來演示簡單的無縫切換鏡像.

Pull 来的镜像

第一步當然是要先拉取更新的鏡像.

docker pull portainer/portainer-ce:latest

接著, 使用 rekcod 這個工具來獲取部署容器時輸入的命令.

$ docker run --rm -i -v /var/run/docker.sock:/var/run/docker.sock nexdrew/rekcod <container>

# 樹莓派玩家
# docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nexdrew/rekcod:3.0.0arm

# 為該命令設別名
# alias rekcod="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nexdrew/rekcod:3.0.0arm"

短暫刪除舊的容器, 然後部署上新的.

$ docker stop <container>
$ docker rm <container>
$ <執行 rekcod 得到的結果>

至此, 新的容器就部署好了, 而當機時間不會超過幾秒鐘👌

最後別忘了刪除掉舊的鏡像喔.

自 Build 的鏡像

假設當初用來 Build 鏡像的 Dockerfile 已經遺失, 可以用 dfimage 這個神器來從鏡像中提取 Dockerfile 用於構建.

$ docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage  <image>

# 為該命令設別名
# alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage "

恢復出來後就可以愉快的繼續玩耍啦~ 但是要注意, 很多時候不是恢復出來就可以用的, 因為涉及到 Reverse Engineering, 恢復出來的文件基本是和原來的 Dockerfile 不一樣的. 因此還是建議好好保存 Dockerfile.

樹莓派 4B 安裝 Arm64 PiOS

安裝系統

  1. 在 Ubuntu 中安裝 Rpi-imager 來準備鏡像
sudo apt update -y && sudo apt upgrade -y
sudo snap install rpi-imager

2. 從 清華大學鏡像 下載 Arm64 的 PiOS_Lite.

3. 使用 Rpi-Imager 刷入鏡像. 使用 Ubuntu 自帶的磁碟工具擴展 rootfs 容量到最大. (也可以不要, 最新版本的系統第一次開機會自動 Resize Partitions 將 / 分區擴到最大)

4. 在 boot 目錄下新建空白的 ssh 文件

cd ~/media/boot/
touch ssh
  1. 彈出 SD 卡, 插入樹莓派並通電.

基礎配置

0. 連線到樹莓派.

ssh [email protected] #raspberry
# 補充: 以下 2 到 4 步可以用下面的命令替換 (快速執行)

pubkey="YOUR SSH PUBKEY"
sudo su
mkdir ~/.ssh
echo $pubkey >> ~/.ssh/authorized_keys 
chmod 700 ~/.ssh 
chmod 644 ~/.ssh/authorized_keys 
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak 
sed -i 's/^#PermitRootLogin/PermitRootLogin/' /etc/ssh/sshd_config 
sed -i 's/^#PubkeyAuthentication/PubkeyAuthentication/' /etc/ssh/sshd_config 
sed -i 's/^#AuthorizedKeysFile/AuthorizedKeysFile/' /etc/ssh/sshd_config 
sed -i 's/^PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config 
sed -i 's/^PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd.service
systemctl status sshd.service
exit
  1. 設置密碼
# 設置密碼
sudo passwd pi
sudo passwd root

2. 修改登錄方式

# 配置 SSH 登錄
su
cd ~/
mkdir .ssh
chmod 700 .ssh
vi /root/.ssh/authorized_keys # 複製你的公鑰進去
chmod 644 /root/.ssh/authorized_keys
vi /etc/ssh/sshd_config # 配置ssh登錄

2. 在 sshd_config 中修改以下幾句.

LoginGraceTime 10m                 # afk達10min自動登出
PermitRootLogin prohibit-password  # 只允許root用ssh登錄
MaxAuthTries 6                     # 最多允許三次錯誤登錄嘗試
MaxSessions 10                     # 最多允許三個client同時登錄
​
PubkeyAuthentication yes           # 開啓公鑰登錄
​
AuthorizedKeysFile      .ssh/authorized_keys #開啓公鑰
​
PasswordAuthentication no          # 不允許任何用戶使用密碼登錄
PermitEmptyPasswords no            # 不允許任何空密碼用戶登錄

3. 繼續執行一下語句, 確定 status 沒有報錯之後登出.

systemctl restart sshd.service
systemctl status sshd.service
exit

4. 重新以 root 身份登錄.

5. 更新/設置系統

apt update && apt upgrade -y
​
# 更新系統 (不推荐)
#rpi-update # 更新 Kernel
#reboot
#rpi-eeprom-update # 更新 Bootloader
#raspi-config # 6 -> A7 -> E1
​
# 設置系統
raspi-config
​
# 重新登錄
exit

更新: 不推荐执行 rpi-update 更新系统, 因为这行命令实际上是拉取了最新的不稳定的 Linux 内核. 如果已经执行了这个命令的同学, 可以考虑执行以下命令回滚内核:

sudo apt install --reinstall libraspberrypi0 libraspberrypi-{bin,dev,doc} raspberrypi-bootloader raspberrypi-kernel

具体的内容参考这篇官方文档: 点我跳转树莓派官网

設置系統的部分自己選擇, 畢竟選項和提示都寫的很清楚.

我是比較喜歡開啟 Overlay Filesystem + ReadOnly Boot Partition 的, 前者就是一個開機一鍵還原, 後者是防止突然斷電破壞 boot 分區. 我覺得這兩個都比較實用, 建議全部設置完之後開啟.

至於語言 (Locale) 設定, 我喜歡把所有 zh 開頭的都勾上, 然後選用 zh_CN.UTF8, 可以保證亂碼不會出現.

至於測試的話, 只要 mkdir 測試, 然後 ls 一看看一看中文是否正常顯示就可以了.

軟件安裝

  1. 基本設置於基礎軟件安裝
# 安裝基本軟件
apt install vim git make wget -y

# 設置 UFW
apt install ufw -y
ufw allow from 192.168.10.1/24
ufw default deny
ufw enable
ufw status

# 設置 NTP 服務
apt install ntpdate -y
echo '@reboot ntpdate 192.168.10.1' >> /etc/crontab

# 設置 開機掛載硬盤
mkdir /mnt/data
mount /dev/sda1 /mnt/data
blkid # 複製顯示的 uuid 和 type
echo 'UUID=2e2e58b1-8fe8-4f5c-9478-f5eef65c5aa5 /mnt/data ext4 defaults 1 2' >> /etc/fstab
mount -a # 確定沒有報錯!

# 開機掛載網絡硬盤
sudo apt install -y curlftpfs
echo '@reboot curlftpfs -o rw,allow_other ftp://username:[email protected]/ /media/ftpdrive' >> /etc/crontab

# 安裝 ZSH & OH MY ZSH
apt install zsh -y
sh -c "$(wget https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)"

# 重啟
reboot

簡單的解釋一下為什麼我上面是這樣配置:

  • 我因為樹莓派完全運行在家庭網絡裡面, 所有流量都要經過轉發, 所以防火牆只允許家裡設備訪問最穩妥.
  • 因為路由器的系統是 Openwrt, 開著 NTP 服務, 所以可以直接向它更新時間.
  • 插上硬盤之後, 最新的系統應該會自動創建一個掛載點 /media/pi/... , 我不想要它, 所以已經把它 unmount 掉了.

2. 安裝 Docker

# 安裝 Docker
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
docker -v
systemctl enable docker.service
systemctl enable containerd.service

# 將 Docker 數據卷掛到外接硬盤
systemctl stop docker
systemctl stop containerd

# 第一次裝系統使用這個方法
rsync -av /var/lib/docker /mnt/data/
rsync -av /var/lib/containerd /mnt/data/
rm -r /var/lib/docker /var/lib/containerd

# 如果重裝過系統想要恢復數據
# mv /var/lib/docker /var/lib/docker.bak
# mv /var/lib/containerd /var/lib/containerd.bak

ln -s /mnt/data/docker /var/lib/docker
ln -s /mnt/data/containerd /var/lib/containerd

systemctl start docker
systemctl start containerd

3. 安裝 Docker-compose ( 使用 pip3 因為 Github 上下載不到針對 Arm 的二進制包 )

apt install python3-venv python3-pip
apt install libffi-dev # 構建環境, 完成後可以卸載掉
pip3 install docker-compose
docker-compose -v

# 刪除構建緩存
rm -r ~/.cache/pip

4. 最後可以啟用 Overlay Filesystem + ReadOnly Boot Partition 了. 之後要是想裝東西可以直接使用 Docker, 改動會保存在外接硬碟中, 就算重裝系統也可以輕鬆恢復數據, 是目前我找到的最好的方案.

raspi-config

5月26日附: 在插拔 HDMI 线的时候不小心把电源也带出来了,赶紧插上,开机没发现任何问题,一分钟后博客恢复访问,lost+found下没添加任何文件。Overlay filesystem 确实是,高!

為官方 Nginx 鏡像安裝 Extras 插件

要用到 Nginx 的額外功能, 我需要安裝插件合集 nginx-extras . 但是官方 Nginx 鏡像並不提供插件功能, 而我也不想就為了這麽一個小功能就去用別人自己 Build 的鏡像, 鬼知道裡面有什麼東西.

我了解到 官方 Nginx, 如果沒有特殊指定的話, 是基於 Debian 的. 那麼在 Debian 裡面如果我想要安裝這個插件, 我只需要執行以下命令, 就可以用了

apt-get update && apt-get install -y nginx-extras

經過簡單的搜索, 我發現 Docker 提供 Dockerfile 的方法來構建自己的鏡像. 就是一個小腳本, 有點像 Git Actions 那樣的小腳本, 不過比那個還簡單.

Dockerfile Cheatsheet, 截取自 devhints.io

知道了 Dockerfile 怎麼使用之後, 就可以上手了

FROM nginx:latest
RUN apt-get update
RUN apt-get install -y nginx-extras
docker build -t nginx_extras .
docker image ls

因為我用了 Portainer, 我可以直接替換鏡像為我自己構建的 nginx_extras .

你也可以手動 rm 掉舊的容器然後用新的鏡像創建容器

到此為止, Nginx 已經可以用上這些插件了.

Docker 默認網段巨坑

簡單的紀錄一下我瞎搞Docker的時候碰到的一個坑:
我現在要配置一個Nginx, 跑在路由器上, 地址是 192.168.10.1 ;
我要用 Nginx 反向代理一個網站, 跑在這個局域網裡面, 地址是 192.168.10.10
這其實根本就超簡單, 正常情況下沒有 Docker, 我們就直接編輯 /etc/nginx/conf.d/default.conf, 然後在裡面扔以下配置, 就直接 Ok (緩存啥的都有照顧到, 我就拿這個作為模板)

server {
    listen 80;
    listen 443 ssl http2;
    server_name 127.0.0.1;

    ssl_certificate /etc/ssl/fullchain.pem;
    ssl_certificate_key /etc/ssl/private.key;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    add_header Strict-Transport-Security "max-age=31536000";
    error_page 497 https://$host$request_uri;


    location / {
        proxy_pass https://justin.education;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header REMOTE-HOST $remote_addr;

        add_header X-Cache $upstream_cache_status;

        #Set Nginx Cache

        proxy_set_header Accept-Encoding "";
        sub_filter "http://192.168.10.10" "https://192.168.10.1";
        sub_filter "https://192.168.10.10" "https://192.168.10.1";
        sub_filter_once off;


        proxy_ignore_headers Set-Cookie Cache-Control expires;
        proxy_cache_key $host$uri$is_args$args;
        proxy_cache_valid 200 304 301 302 10m;
    }
}

然後就一直提示我 502 Bad Gateway, 我就 …

光看截圖你就知道我該有多 Mad

愣是浪費我整整一個小時時間來檢查哪裡有問題, 所以說思維定勢真的不可取, 你說他每次用都好好的, 怎麼一到 Docker 上面就出問題?

不過萬幸的是我想起來起看了一眼日誌 (日誌一定得看啊) , 然後發現一直提示訪問 172.17.0.1 報錯…

於是我去谷歌上面搜了一下 鏈接 , 發現 172.17.0.1/16 是 Docker 自己的一個網段, 是通過 NAT 橋接到 Host 網絡的.

這就… 怪不得. 都不在一個網段裡面, 訪問得到才有鬼

知道就好辦, 直接在 Portainer 裡面從 Bridge 改為 Host 就解決了問題.

Docker 日誌塞滿服務器修復

早晨我例行的摸了一下鏈接到樹莓派的硬盤,結果發現它不轉了。Nani?
於是我立即嘗試登錄寶塔面板,結果發現卡在登錄頁面。
使用SSH重啓之後根本重連不上,網關提示根本沒給它分配IP,就是說它現在不能聯網。
我把樹莓派強制斷電後插電,可以連上了,但是提示的消息卻讓我比較震驚…

空間佔用 100% ?

於是我決心把他弄懂。

問題初步排查

首先肯定是要確定磁盤佔用是這樣…

$ df -h

Filesystem                                     Size  Used Avail Use% Mounted on
tmpfs                                          782M   60M  723M   8% /run
/dev/mmcblk0p2                                 117G  117G     0 100% /
tmpfs                                          3.9G     0  3.9G   0% /dev/shm
tmpfs                                          5.0M     0  5.0M   0% /run/lock
tmpfs                                          4.0M     0  4.0M   0% /sys/fs/cgroup
/dev/mmcblk0p1                                 253M  152M  101M  60% /boot/firmware
/dev/sda1                                      916G   95G  776G  11% /media/backup
tmpfs                                          782M  4.0K  782M   1% /run/user/0

是這樣沒錯,然後要定位到主目錄,看看到底那個文件夾出了問題。

$ cd /
$ du -h -x --max-depth=1 

74M    ./home
2.9G    ./usr
8.0K    ./.disk
24K    ./snap
4.0K    ./mnt
4.0K    ./srv
114M    ./root
72K    ./tmp
117M    ./boot
16K    ./opt
8.0K    ./patch
6.1M    ./etc
4.0K    ./media
4.1G    ./www
110G    ./var       <- 問題根源
16K    ./lost+found
117G    .

經過一番定位之後,現在可以發現問題的根源在這個文件夾裏:

/var/lib/docker/containers/6c8a****13f4/

進一步定位那個文件出了問題:

$ ll


total 112907424
drwx------ 4 root root         4096 Jan  7 04:41 ./
drwx------ 4 root root         4096 Feb  3 21:06 ../
-rw-r----- 1 root root 115617058816 Feb 18 13:05 6c8a****13f4-json.log
drwx------ 2 root root         4096 Feb  3 16:33 checkpoints/
-rw------- 1 root root         3958 Jan  7 04:41 config.v2.json
-rw-r--r-- 1 root root         1513 Jan  7 04:41 hostconfig.json
-rw-r--r-- 1 root root           13 Feb  5 13:29 hostname
-rw-r--r-- 1 root root          174 Feb  5 13:29 hosts
drwx------ 3 root root         4096 Feb  3 16:33 mounts/
-rw-r--r-- 1 root root          612 Feb  5 13:29 resolv.conf
-rw-r--r-- 1 root root           71 Feb  5 13:29 resolv.conf.hash

恐怖的發現…這個日誌文件竟然佔了這麼多的大小…

修復問題

看了一下日誌,整個日誌基本全是 P2P文件交換的記錄。
離譜的是這個日誌居然是以毫秒記錄的,有的地方甚至一毫秒幾十行…這個樹莓派是怎麼支撐過來的?
因爲沒有值得注意的東西,我就直接 rm 掉了。瞬間執行,滿足感還是很高的, WinServer不知道刪到猴年馬月去。
(Ext4文件系統刪除文件時並沒有對數據進行操作,我的理解中是將記錄文件信息的索引釋放掉了,在系統的想法裏這片空間又變回可寫的了)

$ df -h

Filesystem                                     Size  Used Avail Use% Mounted on
tmpfs                                          782M   79M  703M  11% /run
/dev/mmcblk0p2                                 117G  9.3G  103G   9% /
tmpfs                                          3.9G     0  3.9G   0% /dev/shm
tmpfs                                          5.0M     0  5.0M   0% /run/lock
tmpfs                                          4.0M     0  4.0M   0% /sys/fs/cgroup
/dev/mmcblk0p1                                 253M  152M  101M  60% /boot/firmware
/dev/sda1                                      916G   95G  776G  11% /media/backup
tmpfs                                          782M  4.0K  782M   1% /run/user/0

很好,空間佔用回到正常了。

防止問題重複

既然知道了是日誌文件的問題,現在就要搞清楚到底那個容器出了問題。

$ docker ps -a

CONTAINER ID        IMAGE
84e1a7934eb6        linuxserver/syncthing:latest
6c8a506e5aca        linuxserver/resilio-sync:latest

現在就知道哪裡出現問題了。可以看到,resilio-sync 的id對應了那個日誌文件的開頭。

我更改了resilio sync中的設置,分別更改爲

log_size = 0
log_ttl = 0
disk_low_priority = true

希望這樣可以有效果,我會在幾天後再來查看。

实测这样子并没有效果,看起來是Docker鏡像製作時留下來的問題, 直接在容器裡更改配置不起作用.

那這不簡單.

docker pause ResilioSync
rm 6c8a****13f4-json.log
ln -s /dev/null 6c8a****13f4-json.log
$ ll

總計 36K
drwx------ 2 root root 4.0K  1月  1  2038 checkpoints
-rw------- 1 root root 4.9K  5月  5 10:25 config.v2.json
lrwxrwxrwx 1 root root    9  5月  5 10:26 6c8a****13f4-json.log -> /dev/null
-rw-r--r-- 1 root root 2.2K  5月  5 10:25 hostconfig.json
-rw-r--r-- 1 root root   13  5月  4 22:14 hostname
-rw-r--r-- 1 root root  174  5月  4 22:14 hosts
drwx-----x 2 root root 4.0K  1月  1  2038 mounts
-rw-r--r-- 1 root root   61  5月  4 22:14 resolv.conf
-rw-r--r-- 1 root root   71  5月  4 22:14 resolv.conf.hash
docker unpause ResilioSync

问题到此解决

沒解決.

這樣做之後確實一時爽, 但是過了一天我回來看, 發現容器停止運行, 原因是

/mnt/data/docker/containers/6c8a****13f4-json.log/dev/null no file or directory

所以問題到底怎麼解決呢?

經過一番搜索(現在才想起來有谷歌這回事)之後, 我得到了結果, 問題完美解決.

原來是可以通過配置 docker-compose 來限制logsize的, 比如說我要限制 nginx 的 logsize 為5g, 我可以

nginx: 
  image: 
  restart: always 
  logging: 
    driver: “json-file” 
    options: 
      max-size: “5g” 

那麼我是用portainer管理docker的, 我可以

問題到此真正完美解決.

MacArm 用 Docker 安裝 WebAssembly 環境

需求

我正在配置 Qt wasm環境,
但是 emsdk 不支持 apple m1 issue 769 使用 Homebrew 安裝的 emsdk 一直報錯,
沒找到源碼編譯的 Guide 只好使用 Docker.

環境
MacOS Bigsur M1, nodejs

安裝 Docker Arm

官網指南: 點擊前往

根據官網的描述, Docker 是使用了 QEMU 來虛擬出 AMD/x86 的環境, 反正能正常工作.

首先需要安裝 Rosetta2, 應該都已經有了

 softwareupdate --install-rosetta

然後點擊下載 Docker Desktop RC3, 簡單的安裝後啟動, 會要求獲取 root 權限.

點擊 settings - Docker Engine, 在頁面中間的框框裡面加入這麼一行, 配置科大鏡像:

  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn/"
  ],
Docker 鏡像源的配置

至此, Docker 配置完畢了.

安裝 emsdk

emscripten/emsdk : Docker Hub

以上是兩個所需鏡像, 第二個是可選項. 如果你不是配合 qt 使用的話, 只選第一個就可以了.

但是如果你要配合 qt 使用, 兩個都得裝, 原因會在下文提及.

首先先 拉取 emsdk 的鏡像, 根據 Qt官網 的描述, 我需要的版本是 1.39.8 , 所以我的命令是:

docker pull emscripten/emsdk:1.39.8

沒有特殊要求的話, 後面的 :1.39.8 可以改成 :latest, 或者根本不加, 直接拉取最新版本.

測試一下鏡像:

cd /tmp

# create helloworld.cpp
cat << EOF > helloworld.cpp
#include <iostream>
int main() {
  std::cout << "Hello World!" << std::endl;
  return 0;
}
EOF

# compile with docker image
docker run \
  --rm \
  -v $(pwd):$(pwd) \
  -u $(id -u):$(id -g) \
  emscripten/emsdk \
  emcc helloworld.cpp -o helloworld.js

# execute on host machine
node helloworld.js

如果看到 輸出 Hello World! 的話, 配置成功了.

設置快捷方式

每次使用打這麼一大串肯定不現實, 一般的思路是寫一個別名:

# vim ~/.zshrc
alias emcc='docker run \
  --rm \
  -v $(pwd):$(pwd) \
  -u $(id -u):$(id -g) \
  emscripten/emsdk \
  emcc'
alias em++='docker run \
  --rm \
  -v $(pwd):$(pwd) \
  -u $(id -u):$(id -g) \
  emscripten/emsdk \
  em++'

# 應用配置
source ~/.zshrc

這樣手敲就沒問題了, 但是用諸如 vscode 之類的工具配置環境會比較煩, 更加穩妥的方式是將其寫進 bash 腳本裡面:

echo '#!/bin/bash
/usr/local/bin/docker run \
  --rm \
  -v $(pwd):$(pwd) \
  -u $(id -u):$(id -g) \
  emscripten/emsdk \
  emcc $*' > /usr/local/bin/emcc

echo '#!/bin/bash
/usr/local/bin/docker run \
  --rm \
  -v $(pwd):$(pwd) \
  -u $(id -u):$(id -g) \
  emscripten/emsdk \
  em++ $*' > /usr/local/bin/em++

sudo chmod +x /usr/local/bin/emcc
sudo chmod +x /usr/local/bin/em++

至此, emsdk 的安裝已經基本完成.