인터넷은 되는데 시간이 안 맞는다 — networkd 가 ‘오프라인’이라고 믿고 있었다
M8-4에서 OCPP 메시지를 보다가 이상한 점을 하나 적어 두었다. 보드가 보낸 시각이 서버가 받은 시각보다 5초 늦다. 보낸 것이 받은 것보다 늦을 수는 없으니, 보드 시계가 5초 빠르다는 뜻이다.
서버 수신 시각 방향 메시지 보드가 적은 timestamp
10:57:18.590 (KST) 보드→서버 StatusNotification 2026-11-06T01:57:23.645Z
= 10:57:23.645 KST ← 5초 앞섬
OCPP에서 이 시각은 그대로 거래 시작·종료 시각이 된다. 과금에 들어가는 값이다. M8-5 동안은 미뤄 두었다가 오늘 오후에 원인을 찾았다.
| 단계 | 결과 |
|---|---|
| 네트워크·DNS | ✅ 정상 |
| timesyncd | ❌ 요청 0회 (시도조차 안 함) |
| 원인 | networkd 전체 상태가 offline |
| 해결 | 60-wlan.network에 RequiredForOnline=routable 한 줄 |
| 결과 | ✅ 동기화, 재부팅 후에도 자동 |
1. 네트워크는 멀쩡하다
보드 시계는 systemd-timesyncd가 맞춘다. timedatectl은 전에 이렇게 나왔다.
NTP service: active
System clock synchronized: no
서비스는 떠 있는데 맞추지 못했다. 이럴 때 흔한 원인은 세 가지다. 서버가 설정되지 않았거나, DNS가 안 되거나, UDP 123 포트가 막혔거나. 하나씩 봤다.
$ timedatectl show-timesync -p SystemNTPServers -p FallbackNTPServers -p LinkNTPServers
LinkNTPServers=
SystemNTPServers=
FallbackNTPServers=time1.google.com time2.google.com time3.google.com time4.google.com
$ cat /etc/resolv.conf
nameserver 210.220.163.82
nameserver 219.250.36.130
$ ping -c 2 time.google.com
PING time.google.com (216.239.35.0): 56 data bytes
64 bytes from 216.239.35.0: seq=0 ttl=116 time=34.237 ms
- 공유기가 DHCP로 NTP 서버를 알려 주지 않아
LinkNTPServers는 비어 있다. 대신 빌드에 들어간 기본 서버(google)가 있다. - DNS는 이름을 풀고, 인터넷도 된다.
세 가지 다 아니었다. 그런데 timesyncd 상태가 이상했다.
$ timedatectl timesync-status
Server: n/a (n/a)
Poll interval: 0 (min: 32s; max 34min 8s)
Packet count: 0
$ journalctl -u systemd-timesyncd -b
Nov 06 09:43:31 sc6 systemd[1]: Starting Network Time Synchronization...
Nov 06 09:43:33 sc6 systemd[1]: Started Network Time Synchronization.
부팅하고 네 시간이 지났는데 서버를 고르지도 않았고, 패킷을 하나도 보내지 않았다. 로그도 시작했다는 두 줄뿐이다. 실패한 것이 아니라 시도를 안 한 것이다.
2. networkd 는 ‘오프라인’이라고 생각하고 있었다
timesyncd는 networkd가 “네트워크가 온라인이다”라고 할 때만 시간 서버에 접속한다. 오프라인일 때 요청을 보내 봐야 실패하니 기다리는 것이다. 그래서 networkd의 판단을 봤다.
$ networkctl
IDX LINK TYPE OPERATIONAL SETUP
1 lo loopback carrier unmanaged
2 end0 ether no-carrier configuring ← 유선, 케이블 없음
3 eth0 ether degraded configured ← QCA7005 PLC
4 wlan0 wlan routable configured ← WiFi, 인터넷 됨
5 usb0 gadget no-carrier configuring ← USB 가젯
$ grep -E 'OPER|ONLINE' /run/systemd/netif/state
OPER_STATE=routable
ONLINE_STATE=offline
두 줄이 서로 다른 말을 한다.
OPER_STATE=routable: 인터페이스 중 하나(wlan0)는 바깥으로 나갈 수 있다.ONLINE_STATE=offline: 그런데 시스템 전체는 오프라인이다.
ONLINE_STATE는 모든 인터페이스를 보지 않는다. .network 파일에서 RequiredForOnline(온라인 판단에 넣을지) 이 켜진 인터페이스만 본다. 우리 보드는 이랬다.
| 인터페이스 | 설정 파일 | RequiredForOnline | 언제 정했나 |
|---|---|---|---|
| eth0 (PLC) | 30-plc-qca7000.network |
no | M6 |
| wlan0 (WiFi) | 60-wlan.network |
no | M6 |
| usb0 (USB 가젯) | ST 기본 + 수정 | no | M6 |
| end0 (유선) | systemd 기본 80-wired.network |
yes (기본값) | — |
판단 대상은 end0 하나뿐이고, 거기에는 케이블이 없다. 그래서 WiFi로 인터넷이 잘 되는데도 networkd는 계속 “오프라인”이라고 보고 있었다.
왜 wlan0 을 뺐었나
M6에서 일부러 뺐다
나중에 정리 URL 링크 정리 필요 ({ post_url 2026-11-04-sc6-m6-system-settings }, { post_url 2026-11-04-sc6-m6-image-regression }).
당시 문제는 systemd-networkd-wait-online이었다. 이 서비스는 판단 대상 인터페이스가 모두 연결될 때까지 기다린다. WiFi 접속 정보가 없는 새 카드로 부팅하면 2분을 기다리다 실패했고, 그 뒤에 뜨는 서비스들도 2분 늦어졌다. 그래서 PLC, WiFi, USB 가젯을 판단 대상에서 뺐다.
그런데 M7에서 상황이 바뀌었다({ post_url 2026-11-04-sc6-m7-everest-cp-bsp }). network-online.target을 기다리던 유일한 서비스인 mosquitto를 고쳤더니, wait-online이 아예 실행되지 않게 됐다. 원래 이유가 사라졌는데 설정만 남아, 이번에는 timesyncd를 막고 있었다.
3. 고치기: wlan0 을 다시 판단에 넣는다
networkd의 온라인 상태는 세 가지다.
| ONLINE_STATE | 뜻 |
|---|---|
offline |
판단 대상이 하나도 연결 안 됨 |
partial |
판단 대상 중 일부가 연결됨 |
online |
판단 대상이 모두 연결됨 |
timesyncd는 partial도 온라인으로 친다. 그래서 wlan0을 판단 대상에 넣으면 이렇게 된다.
- WiFi만 연결: end0 끊김 + wlan0 연결 →
partial→ 동기화 됨 - 유선만 연결: end0 연결 + wlan0 끊김 →
partial→ 동기화 됨 - 둘 다 없음:
offline→ 기다림 (어차피 할 수 없음)
어느 쪽이든 하나만 붙어 있으면 된다. 충전기에 맞는 동작이다.
보드에서 먼저 확인
이미지를 다시 굽기 전에, 보드의 /etc에 덮어쓰기(drop-in) 파일을 두고 시험했다. 지우면 원래대로 돌아간다.
mkdir -p /etc/systemd/network/60-wlan.network.d
printf '[Link]\nRequiredForOnline=routable\n' > /etc/systemd/network/60-wlan.network.d/10-online.conf
networkctl reload
routable은 “바깥으로 나갈 수 있어야 온라인”이라는 뜻이다. 기본값(yes)은 링크 로컬 주소만 있어도(degraded) 온라인으로 치는데, 시간 서버에 접속하려면 그것으로는 부족하다.
Fri Nov 6 13:22:24 KST 2026
[13156.047197] systemd-journald[276]: Time jumped backwards, rotating.
ONLINE_STATE=partial
$ timedatectl timesync-status
Server: 216.239.35.0 (time1.google.com)
Stratum: 1
Offset: -5.557351s
Delay: 48.191ms
Packet count: 1
System clock synchronized: yes
reload 하자마자 상태가 partial이 됐고, timesyncd가 바로 접속해 5.557초를 뒤로 맞췄다. journald의 “Time jumped backwards”는 이 점프 때 나오는 정상 메시지다.
OCPP 는 괜찮았나
networkctl reload는 wlan0을 다시 설정하므로 연결이 잠깐 끊길 수 있다. 원격 OCPP 감시 로그를 봤다.
13:22:33.097 [SC6-0001] Connection is closed, status: CloseStatus[code=1006]
13:22:36.635 [SC6-0001] Connection is established
13:22:36.636 서버→보드 GetConfiguration
13:22:36.665 보드→서버 └ GetConfiguration 응답
13:22:36.666 보드→서버 StatusNotification "timestamp":"2026-11-06T04:22:36.645Z"
- 3.5초 만에 다시 붙었다.
- 접속 1 ms 만에 서버의 GetConfiguration이 또 먼저 왔다. 오전에 패치한 그 상황이다({ post_url 2026-11-06-sc6-m8-5-network-loss-libocpp-patch }). 이번에는 문제없이 지나갔다.
- 보드 timestamp
04:22:36.645Z(= 13:22:36.645 KST)가 서버 수신 시각13:22:36.666보다 21 ms 앞선다. 이제야 앞뒤가 맞는다.
재부팅
설정 파일이 부팅 때도 제대로 적용되는지 봤다.
ONLINE_STATE=partial
System clock synchronized: yes
13:38:07 systemd[1]: Started Network Time Synchronization.
13:38:15 systemd-timesyncd: Network configuration changed, trying to establish connection.
13:38:15 systemd-timesyncd: Contacted time server 216.239.35.0:123 (time1.google.com).
13:38:15 systemd-timesyncd: Initial clock synchronization to Fri 2026-11-06 13:38:15.383627 KST.
“Network configuration changed, trying to establish connection” 줄이 핵심이다. WiFi가 붙자 networkd가 상태를 바꿨고, timesyncd가 그것을 보고 바로 접속했다. 수정 전 로그에는 이 줄이 없었다.
4. 레이어 반영
보드에서 확인한 내용을 meta-sc6의 원본 파일에 넣었다. 이유를 주석으로 남겼다. 나중에 누가 “wait-online 때문에 no로 해야 하는 것 아닌가” 하고 되돌리지 않게.
# recipes-connectivity/sc6-network-config/files/60-wlan.network
# RequiredForOnline: 온라인 판단에 넣는다. 빼 두면 판단 대상이 케이블 없는 end0 뿐이라
# 전체 상태가 늘 offline 이 되고, timesyncd 가 시간 동기화를 아예 시도하지 않는다.
# wlan0/end0 중 하나만 붙어도 partial(온라인 취급)이 된다.
# wait-online 은 network-online.target 을 원하는 서비스가 없어 실행되지 않는다(M7 mosquitto 수정).
# 충전기 서비스에 network-online.target 의존을 넣으면 WiFi 없을 때 부팅이 2분 늦어지니 넣지 말 것.
[Link]
RequiredForOnline=routable
마지막 줄이 M7-6에 주는 숙제다. EVerest 자동 시작 서비스를 만들 때 After=network-online.target을 습관처럼 넣으면, wait-online이 다시 돌기 시작한다. 그러면 wlan0과 end0이 모두 붙을 때까지 기다리게 되고, 현장에서 매번 2분씩 늦게 충전이 시작된다. 충전기는 네트워크 없이도 충전돼야 하므로 이 의존은 넣지 않는다.
보드의 drop-in 파일(/etc/systemd/network/60-wlan.network.d/10-online.conf)은 새 이미지를 올린 뒤 지운다.
5. 정리
커밋
meta-sc6 1297bdb network: wlan0 을 온라인 판단에 넣어 시간 동기화가 되게 함
sc6-work 8a64055 scripts: 원격 OCPP 감시 로그 끝부분 보기, NTP 수정 커밋 스크립트
오늘 배운 것
- “실패”와 “시도 안 함”은 다르다.
Packet count: 0과 로그 두 줄이 그것을 말해 줬다. 네트워크를 의심하기 전에 요청이 나가기는 했는지부터 보자. OPER_STATE와ONLINE_STATE는 다르다. 앞의 것은 실제 연결 상태, 뒤의 것은 “판단 대상”만 본 결과다. 그리고 다른 서비스들은 뒤의 것을 믿는다.- 설정을 바꾼 이유가 사라지면 설정도 다시 보자. wlan0을 뺀 이유(wait-online)는 M7에서 없어졌다. 설정만 남아 다른 곳을 막았다. 그래서 이번에는 이유를 파일에 적어 두었다.
- 시계는 과금 데이터다. 5초는 작아 보이지만, OCPP 거래 시각에 그대로 들어간다. M8-4에서 시각을 ms 단위로 나란히 놓고 본 덕분에 찾았다.
남은 것
| 항목 | 내용 |
|---|---|
| 시계가 빨리 가는가 | 3.6시간에 5.5초. 부팅 때부터 틀렸던 건지, 실제로 빨리 가는 건지는 timesyncd의 Frequency 값을 시간을 두고 보면 안다. 동기화가 되면 timesyncd가 계속 보정하므로 당장 문제는 없다 |
| 네트워크 없이 부팅 | 시간 서버에 닿기 전까지는 RTC 시각을 쓴다. RTC 백업 전원(배터리) 여부는 설계자 확인 |
| 이미지 반영 | 다음 이미지 빌드 때 함께. 보드의 drop-in은 그 뒤 삭제 |
| M7-6 | 자동 시작 서비스에 network-online.target 의존을 넣지 않기 |
댓글남기기