背景
为 OP Stack L2 Rollup 部署区块链浏览器,提供交易查询、区块浏览、合约验证等功能。
服务器配置
| 主机 | 配置 | 角色 |
|---|---|---|
| prod-explorer-10-169-20-30 | 8c/16g/200g | Blockscout 浏览器 |
数据库准备
安装 psql 客户端
apt install postgresql-client -y
数据库操作
# 连接数据库
psql -h pgm-3ns8716n6k923d80.pg.rds.aliyuncs.com -p 5432 -U blockscout -d blockscout
# 密码: V3K76N4gS18K
常用操作:
-- 查询当前用户
SELECT current_user;
-- 查询所有数据库
SELECT datname FROM pg_database;
-- 创建数据库
CREATE DATABASE blockscout;
-- 授权
GRANT ALL PRIVILEGES ON DATABASE blockscout TO blockscout;
-- 查询数据库大小
SELECT pg_size_pretty(pg_database_size('blockscout')) AS size;
-- 查询所有表大小
SELECT
relname AS "Table",
pg_size_pretty(pg_total_relation_size(relid)) AS "Size"
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
-- 终止会话(重建前)
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'blockscout';
-- 重命名数据库
ALTER DATABASE blockscout RENAME TO old_blockscout;
源码部署
下载源码
mkdir -pv /data/op-explorer/
cd /data/op-explorer/
# 克隆指定分支
git clone https://github.com/blockscout/blockscout.git -b production-optimism
cd blockscout/docker-compose
清理重建
cd /data/op-explorer/blockscout/docker-compose/services
# 删除旧数据
rm -rf redis-data logs stats-db-data
Nginx 配置
cd /data/op-explorer/blockscout/docker-compose/proxy
# 备份
cp default.conf.template default.conf.template_backup
# 写入配置
cat > default.conf.template <<'EOF'
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream back-proxy-pass {
server ${BACK_PROXY_PASS};
}
upstream front-proxy-pass {
server ${FRONT_PROXY_PASS};
}
upstream stats-proxy-pass {
server stats:8050;
}
server {
listen 80;
server_name scan.nal.network;
proxy_http_version 1.1;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' '*';
location ~ ^/(api|socket|sitemap.xml|auth/auth0|auth/auth0/callback|auth/logout) {
add_header Content-Security-Policy "upgrade-insecure-requests;connect-src *";
proxy_pass http://back-proxy-pass;
proxy_http_version 1.1;
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 X-Forwarded-Proto "$scheme";
proxy_set_header Upgrade "$http_upgrade";
proxy_set_header Connection $connection_upgrade;
proxy_cache_bypass $http_upgrade;
}
location / {
add_header Content-Security-Policy "upgrade-insecure-requests;connect-src *";
proxy_pass http://front-proxy-pass;
proxy_http_version 1.1;
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 X-Forwarded-Proto "$scheme";
proxy_set_header Upgrade "$http_upgrade";
proxy_set_header Connection $connection_upgrade;
proxy_cache_bypass $http_upgrade;
}
}
server {
listen 80;
server_name stats.nal.network;
proxy_http_version 1.1;
location / {
add_header 'Access-Control-Allow-Origin' *;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
add_header Content-Security-Policy "upgrade-insecure-requests;connect-src *";
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://stats-proxy-pass;
proxy_http_version 1.1;
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 X-Forwarded-Proto "$scheme";
proxy_set_header Upgrade "$http_upgrade";
proxy_set_header Connection $connection_upgrade;
proxy_cache_bypass $http_upgrade;
}
}
server {
listen 8081;
server_name scan.nal.network;
proxy_http_version 1.1;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' '*';
location / {
proxy_pass http://visualizer:8050/;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host "$host";
proxy_set_header X-Real-IP "$remote_addr";
proxy_connect_timeout 30m;
proxy_read_timeout 30m;
proxy_send_timeout 30m;
proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for";
proxy_set_header X-Forwarded-Proto "$scheme";
proxy_set_header Upgrade "$http_upgrade";
proxy_set_header Connection $connection_upgrade;
proxy_cache_bypass $http_upgrade;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always;
add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,x-csrf-token' always;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}
}
EOF
Docker 配置
修改数据目录
# AWS 使用 snapd 管理 docker,需迁移到独立目录
sudo snap stop docker
sudo systemctl stop snapd.socket
sudo systemctl stop snapd.service
# 创建新目录
mkdir -vp /data/docker/workspace
mkdir -vp /etc/docker/
# 修改配置
cat > /etc/docker/daemon.json <<'EOF'
{
"data-root": "/data/docker/workspace"
}
EOF
# 迁移数据
rsync -aP /var/lib/docker/ /data/docker/workspace
# 重启
sudo systemctl daemon-reload
sudo systemctl restart docker.service
# 验证
docker info | grep "Docker Root Dir"
Nginx 服务配置
cat > /data/op-explorer/blockscout/docker-compose/services/nginx.yml <<'EOF'
version: '3.9'
services:
proxy:
image: nginx
container_name: proxy
extra_hosts:
- 'host.docker.internal:host-gateway'
volumes:
- "../proxy:/etc/nginx/templates"
environment:
BACK_PROXY_PASS: ${BACK_PROXY_PASS:-backend:4000}
FRONT_PROXY_PASS: ${FRONT_PROXY_PASS:-frontend:3000}
ports:
- target: 80
published: 80
- target: 8080
published: 8080
- target: 8081
published: 8081
EOF
环境变量配置
common-blockscout.env
cd /data/op-explorer/blockscout/docker-compose/envs
# 备份
cp common-blockscout.env common-blockscout.env_back
cat > common-blockscout.env <<'EOF'
# RPC 配置
ETHEREUM_JSONRPC_HTTP_URL=http://10.169.20.40:8545/
ETHEREUM_JSONRPC_FALLBACK_HTTP_URL=http://10.169.20.40:8545/
ETHEREUM_JSONRPC_TRACE_URL=http://10.169.20.40:8545/
ETHEREUM_JSONRPC_FALLBACK_TRACE_URL=http://10.169.20.40:8545/
ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL=http://10.169.20.40:8545/
ETHEREUM_JSONRPC_ETH_CALL_URL=http://10.169.20.40:8545/
# 数据库
DATABASE_URL=postgresql://blockscout:V3K76N4gS18K@pgm-3ns8716n6k923d80.pg.rds.aliyuncs.com:5432/blockscout
# 链配置
DOCKER_REPO=blockscout-optimism
DOCKER_TAG=6.6.0-postrelease-c886b77c
CHAIN_TYPE=optimism
NETWORK=NAL
SUBNETWORK=NAL
COIN=ETH
CHAIN_ID=328527
# L1 配置
INDEXER_BEACON_RPC_URL=http://10.169.21.21:3500
INDEXER_OPTIMISM_L1_RPC=http://10.169.21.21:8545
INDEXER_OPTIMISM_L1_BATCH_START_BLOCK=20180082
INDEXER_OPTIMISM_L1_BATCH_INBOX=0xff00000000000000000000000000000000328527
INDEXER_OPTIMISM_L1_BATCH_SUBMITTER=0xB1b676357de100c5bd846299CF6C85436803e839
INDEXER_OPTIMISM_L1_PORTAL_CONTRACT=0x872902b91fB2aa95147fCDc346a567B7970DBe47
INDEXER_OPTIMISM_L1_OUTPUT_ROOTS_START_BLOCK=20180082
INDEXER_OPTIMISM_L1_OUTPUT_ORACLE_CONTRACT=0xaE25ea4Cc185585Fa6abf344F3354bf8207Cd7D1
INDEXER_OPTIMISM_L1_WITHDRAWALS_START_BLOCK=20180082
INDEXER_OPTIMISM_L2_WITHDRAWALS_START_BLOCK=1
INDEXER_OPTIMISM_L2_MESSAGE_PASSER_CONTRACT=0x4200000000000000000000000000000000000016
INDEXER_OPTIMISM_L1_DEPOSITS_START_BLOCK=20180082
INDEXER_OPTIMISM_L1_BATCH_BLOCKSCOUT_BLOBS_API_URL=https://eth.blockscout.com/api/v2/blobs
INDEXER_OPTIMISM_L2_BATCH_GENESIS_BLOCK_NUMBER=0
# 性能优化
ETHEREUM_JSONRPC_GETH_TRACE_BY_BLOCK=true
EOF
common-frontend.env
cat > common-frontend.env <<'EOF'
NEXT_PUBLIC_API_HOST=scan.nal.network
NEXT_PUBLIC_API_PROTOCOL=https
NEXT_PUBLIC_STATS_API_HOST=https://stats.nal.network
NEXT_PUBLIC_NETWORK_NAME=NAL
NEXT_PUBLIC_NETWORK_SHORT_NAME=Nal chain
NEXT_PUBLIC_NETWORK_ID=328527
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_APP_HOST=scan.nal.network
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_VISUALIZE_API_HOST=https://scan.nal.network:8081
NEXT_PUBLIC_IS_TESTNET=false
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com/
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://bridge.nal.network/withdrawal
NEXT_PUBLIC_AD_TEXT_PROVIDER=none
NEXT_PUBLIC_AD_BANNER_PROVIDER=none
NEXT_PUBLIC_GAS_TRACKER_ENABLED=true
NEXT_PUBLIC_GAS_TRACKER_UNITS=['gwei']
NEXT_PUBLIC_WEB3_WALLETS=['metamask']
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=true
NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=true
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
NEXT_PUBLIC_NETWORK_ICON=https://qa-e-chain.obs.cn-north-219.jrzq.huaweicloud.com/nal-icon/nal.svg
NEXT_PUBLIC_NETWORK_LOGO=https://qa-e-chain.obs.cn-north-219.jrzq.huaweicloud.com/nal-icon/nal.svg
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=radial-gradient(103.03% 103.03% at 0% 0%, rgba(28,100,175,0.8) 0%, rgba(46,35,136,0.8) 100%), var(--chakra-colors-blue-300)
FAVICON_GENERATOR_API_KEY=c91723cf3aa4406388ba51b4552fd004d95c114d
FAVICON_MASTER_URL=https://i.postimg.cc/7ZjL5Jsd/nal.png
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=b6a7972b75af75d28a8aeb35a6af7871
NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.nal.network
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=false
EOF
common-stats.env
cat > common-stats.env <<'EOF'
STATS__CREATE_DATABASE=true
STATS__RUN_MIGRATIONS=true
EOF
stats.yml
cat > /data/op-explorer/blockscout/docker-compose/services/stats.yml <<'EOF'
STATS__BLOCKSCOUT_DB_URL=${STATS__BLOCKSCOUT_DB_URL:-postgresql://blockscout:V3K76N4gS18K@pgm-3ns8716n6k923d80.pg.rds.aliyuncs.com:5432/blockscout}
EOF
backend.yml
cat > /data/op-explorer/blockscout/docker-compose/services/backend.yml <<'EOF'
# 修改镜像
image: blockscout/blockscout-optimism:6.7.2
EOF
frontend.yml
cat > /data/op-explorer/blockscout/docker-compose/services/frontend.yml <<'EOF'
version: '3.9'
services:
frontend:
image: ghcr.io/blockscout/frontend:v1.32.0
pull_policy: always
platform: linux/amd64
restart: always
container_name: 'frontend'
env_file:
- ../envs/common-frontend.env
EOF
启动服务
cd /data/op-explorer/blockscout/docker-compose
# 启动
docker compose -f external-db.yml up -d
# 停止
docker compose -f external-db.yml down
镜像更新
# 修改镜像标签
docker tag 5fa06fa31aed blockscout/blockscout-optimism:newtag
# 验证
docker images
# 修改 backend.yml 使用新镜像
image: blockscout/blockscout-optimism:newtag
复盘
问题根因:Docker 默认目录在系统盘,日志增长导致磁盘占满
解决:迁移 Docker 数据目录到 /data 独立磁盘
改进措施:
- 配置日志轮转,限制容器日志大小
- 监控磁盘使用率,80% 触发告警
- 定期清理旧镜像和未使用卷
本文首发于 wr.mrchi.cn,转载请注明出处。