티스토리 뷰
문제 현상
ONOS 컨테이너에서, 서로 다른 컨테이너가 관리하는 device 사이의 link가 없는 것으로 나타나고, 주기적으로 LLDP Packet failed to validate!
라는 warn 수준의 로그가 발생한다.
문제 현상 재현
Ubuntu에서 Docker로 ONOS 컨테이너를 2개 실행한다.
$ sudo docker run -it --rm --name onos1 onosproject/onos:2.2.0
$ sudo docker run -it --rm --name onos2 onosproject/onos:2.2.0
두 ONOS에서 org.onosproject.openflow
애플리케이션을 활성화한다.
$ onos-app 172.17.0.2 activate org.onosproject.openflow
$ onos-app 172.17.0.3 activate org.onosproject.openflow
Mininet으로 다음과 같은 구조의 네트워크를 구성한다.
그러면 ONOS 로그에는 LLDP Packet failed to validate!
라는 내용으로 warn 수준의 로그가 계속 발생하는 것을 볼 수 있다.
10:46:54.997 WARN [LinkDiscovery] LLDP Packet failed to validate!
ONOS CLI에서 devices
, links
명령어로 device와 link 목록을 확인해 보면, Device는 각각 3개씩 정상적으로 잡혀 있지만, link는 DIRECT
type 6개와 EDGE
type 1개가 아니라 DIRECT
type 6개만 잡혀 있다.
onos@root > devices
id=of:0000000000000001, available=true, local-status=connected 41s ago, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.5.5, serial=None, chassis=1, driver=ovs, channelId=172.17.0.1:54560, managementAddress=172.17.0.1, protocol=OF_13
id=of:0000000000000002, available=true, local-status=connected 41s ago, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.5.5, serial=None, chassis=2, driver=ovs, channelId=172.17.0.1:54554, managementAddress=172.17.0.1, protocol=OF_13
id=of:0000000000000003, available=true, local-status=connected 41s ago, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.5.5, serial=None, chassis=3, driver=ovs, channelId=172.17.0.1:54562, managementAddress=172.17.0.1, protocol=OF_13
onos@root > links
src=of:0000000000000001/1, dst=of:0000000000000003/1, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000001/2, dst=of:0000000000000002/2, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000002/1, dst=of:0000000000000003/2, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000002/2, dst=of:0000000000000001/2, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000003/1, dst=of:0000000000000001/1, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000003/2, dst=of:0000000000000002/1, type=DIRECT, state=ACTIVE, expected=false
해결 방법
일반적인 Ubuntu에서 ONOS를 구동할 때는 cluster 정보가 무작위로 생성되기 때문에 문제가 생기지 않는다. 그러나 Docker로 ONOS 컨테이너를 돌릴 때는 무작위의 cluster 정보를 자동으로 생성하지 않기 때문에 cluster 이름이 모두 default
로 중복되어서 위의 문제 현상이 발생한다. 따라서 cluster 정보만 넣어 주면 이 문제를 해결할 수 있다. 여기서는 별도의 cluster 정보 파일을 넣은 디렉터리를 컨테이너에 마운트하여 해결한다. (이 해결 방법 예제에서는 여러 ONOS를 cluster로 묶지는 않았다.)
Cluster 관련 설정 파일 구성
실행할 컨테이너 개수에 맞춰서 디렉터리를 생성한다. 이 예제에서는 ONOS 컨테이너 2개를 돌릴 것이므로, ~/onos-config
디렉터리 아래에 2개의 디렉터리 onos1
, onos2
를 만들었다. 각 디렉터리 안에 들어갈 cluster.json
까지 작성하면, ~/onos-config
디렉터리의 구조는 다음과 같다.
onos-config
├── onos1
│ └── cluster.json
└── onos2
└── cluster.json
각 디렉터리에서 cluster.json
파일을 다음과 같은 형태로 작성한다. 실행할 컨테이너에 맞게 각각 ip
와 id
를 지정한다. name
은 컨테이너의 이름과 달라도 상관없고, 서로 중복되지만 않으면 된다. port
는 cluster 내부에서 서로 통신하기 위한 포트 번호로 약속된 9876
으로 지정한다.
{
"name": "onos1",
"node": {
"ip": "172.17.0.2",
"id": "172.17.0.2",
"port": 9876
}
}
컨테이너 구동 및 확인
다음과 같이 -v <host-path>:<container-path>
옵션을 주어서, 방금 만든 cluster.json
을 포함하는 디렉터리를 컨테이너의 /root/onos/config
디렉터리에 마운트하여 컨테이너를 실행한다. 컨테이너의 이름은 앞서 만든 cluster.json
파일의 name
과 달라도 괜찮다.
$ sudo docker run -it -v ~/onos-config/onos1:/root/onos/config --name onos1 --rm onosproject/onos:2.2.0
$ sudo docker run -it -v ~/onos-config/onos2:/root/onos/config --name onos2 --rm onosproject/onos:2.2.0
ONOS가 OpenFlow device와 정보를 주고받아야 하므로 ONOS의 org.onosproject.openflow
애플리케이션을 활성화한다.
$ onos-app 172.17.0.2 activate org.onosproject.openflow
$ onos-app 172.17.0.3 activate org.onosproject.openflow
Mininet으로 다음과 같은 구조의 네트워크를 구성한다. “문제 상황 재현” 문단의 것과 동일하다.
이제 Mininet으로 위와 같이 네트워크를 구성해도 LLDP Packet failed to validate!
라는 로그가 발생하지 않는다. 더 확실히 확인하기 위해 ONOS CLI에서 link 목록을 확인한다. 이제 EDGE
type의 link 1개도 잘 잡힐 것이다.
onos@root > devices
id=of:0000000000000001, available=true, local-status=connected 11s ago, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.5.5, serial=None, chassis=1, driver=ovs, channelId=172.17.0.1:55214, managementAddress=172.17.0.1, protocol=OF_13
id=of:0000000000000002, available=true, local-status=connected 11s ago, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.5.5, serial=None, chassis=2, driver=ovs, channelId=172.17.0.1:55220, managementAddress=172.17.0.1, protocol=OF_13
id=of:0000000000000003, available=true, local-status=connected 11s ago, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.5.5, serial=None, chassis=3, driver=ovs, channelId=172.17.0.1:55216, managementAddress=172.17.0.1, protocol=OF_13
onos@root > links
src=of:0000000000000001/1, dst=of:0000000000000003/1, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000001/2, dst=of:0000000000000002/2, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000002/1, dst=of:0000000000000003/2, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000002/2, dst=of:0000000000000001/2, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000003/1, dst=of:0000000000000001/1, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000003/2, dst=of:0000000000000002/1, type=DIRECT, state=ACTIVE, expected=false
src=of:0000000000000005/3, dst=of:0000000000000002/3, type=EDGE, state=ACTIVE, expected=false
부록: 문제 원인 파악 및 해결 과정
LLDP Packet failed to validate!
라는 내용의 warn 수준의 로그는 link discovery 과정 중 destination device에서 받은 LLDP 메시지를 ONOS가 처리하는 부분에서 발생한다.
코드와 로그와 문제 상황을 보았을 때 이상한 점이 있다. 다른 cluster에서 발생한 LLDP 패킷을 받으면 link의 type이 EDGE
로 결정되고 LLDP 패킷을 검증하지 않아야 하는데, LLDP 패킷 검증을 진행해서 검증에 실패했다는 로그가 발생했다. 그 전에 다른 cluster에서 발생한 LLDP 패킷을 같은 cluster에서 온 것으로 잘못 판별했다고 볼 수 있다.
여기서 나는 출발지 cluster의 MAC과 자신(도착지)의 cluster의 MAC이 다른지 판별하는 notMy
메서드의 내용 중 LinkDiscoveryContext
type인 context
를 통해 호출되는 fingerprint
메서드를 따라가 보았다.
fingerprint
메서드는 LldpLinkProvider
class의 buildSrcMac
메서드를 호출하는 동작만 한다. buildSrcMac
메서드 이름의 Src
는 보내는 입장에서의 출발지이므로 자신의 cluster를 가리킨다. 이 메서드에서는 ClusterMetadataService
interface를 통해 ClusterMetadata
를 가져와서, 이것으로 자신이 속한 cluster의 MAC을 만든다.
ClusterMetadataService
interface의 getClusterMetadata
메서드는 ClusterMetadataManager
class에 구현되어 있다. getClusterMetadata
메서드는 설정에 따라서 적절한 ClusterMetadataProvider
를 찾아서 ClusterMetadata
값을 리턴한다. 이 provider를 찾는 과정에서 가장 먼저 primary provider를 가져오는데, 기본적으로는 ConfigFileBasedClusterMetadataProvider
를 가져온다. 곧바로 이 provider의 isAvailable
메서드를 호출하여 가용성을 검사한다.
ConfigFileBasedClusterMetadataProvider
의 가용성 검사는 설정 파일이 존재하는지 검사하는 방식으로 진행된다. 이 설정 파일의 경로는 ../config/cluster.json
이라고만 나와 있다. 그래서 아까 살펴본 isAvailable
메서드에서 설정 파일의 canonical path를 로그에 출력하도록 수정하여 확인해 보았다.
// core/net/src/main/java/org/onosproject/cluster/impl/ConfigFileBasedClusterMetadataProvider.java @ 2.2.0
@Override
public boolean isAvailable() {
try {
URL url = new URL(metadataUrl);
if ("file".equals(url.getProtocol())) {
File file = new File(metadataUrl.replaceFirst("file://", ""));
log.info("file.getCanonicalPath(): {}, file.exists(): {}", file.getCanonicalPath(), file.exists());
return file.exists();
} else {
// Return true for HTTP URLs since we allow blocking until HTTP servers come up
return "http".equals(url.getProtocol());
}
} catch (Exception e) {
log.warn("Exception accessing metadata file at {}:", metadataUrl, e);
return false;
}
}
11:14:21.924 INFO [ConfigFileBasedClusterMetadataProvider] file.getCanonicalPath(): /root/onos/config/cluster.json, file.exists(): false
컨테이너 안에서의 cluster 설정 파일 경로가 /root/onos/config/cluster.json
이라고 한다.
처음에는 Dockerfile
을 수정하여 빈 JSON object({}
)가 담긴 JSON 파일을 저 경로로 넣어서 이미지를 빌드했는데, 이 이미지에서는 ONOS 코어 자체가 제대로 작동하지 않았다. 그래서 tools/tutorials/vm/config/cluster-1.json
파일을 참고하여 JSON 파일을 다음과 같이 작성하여 다시 시도했다.
{
"node": {
"ip": "127.0.0.1",
"id": "127.0.0.1",
"port": 9876
},
"storage": [],
"name": "mycluster"
}
이때부터 서로 다른 cluster에 속한 device 사이의 link가 인식되기 시작했다. 그러나 이 link가 EDGE
type이 아니라 DIRECT
type으로 인식되었다. 그럼 MAC 검증이 실패해야 하지 않나 싶어서 또 코드를 읽어 보니까, config.json
에 clusterSecret
을 정의하지 않았기 때문에 무조건 검증에 성공한 것으로 나온 것이었다.
그래서 이때 사용한 이미지를 빌드했을 때와 동일한 소스 코드로 Ubuntu 가상 머신에서 ONOS를 빌드하여, 무엇이 다른지 확인해 보았다. /tmp/onos-<version>/config/cluster.json
파일의 name
이 onos-<random-number>
로 실행할 때마다 달랐다. 여기서 cluster의 이름이 고유해야 함을 유추할 수 있었다.
그런데 나는 cluster 설정 파일을 이미지 자체에 고정된 내용으로 넣었기 때문에, cluster 이름을 컨테이너마다 다르게 하려면 설정 파일 넣는 방법을 다시 생각해 봐야 했다. 이리저리 찾아보다가, docker run
명령어에서 -v <host-path>:<container-path>
옵션으로 <host-path>
디렉터리를 <container-path>
에 마운트할 수 있다는 것을 알았다. 그래서 다음과 같은 구조로 디렉터리를 만들고 그 안에 cluster.json
파일을 각각 만들어서, 컨테이너에 마운트하여 확인해 보았다.
onos-config
├── onos1
│ └── cluster.json
└── onos2
└── cluster.json
{
"node": {
"ip": "172.17.0.2",
"id": "172.17.0.2",
"port": 9876
},
"storage": [],
"name": "onos1"
}
{
"node": {
"ip": "172.17.0.3",
"id": "172.17.0.3",
"port": 9876
},
"storage": [],
"name": "onos2"
}
$ sudo docker run -it --rm -v "~/onos-config/onos1":"/root/onos/config" --name onos1 onosproject/onos:2.2.0
$ sudo docker run -it --rm -v "~/onos-config/onos2":"/root/onos/config" --name onos2 onosproject/onos:2.2.0
그 결과, link type까지 올바르게 인식되는 것을 확인할 수 있었다.
관련 자료
'2019 학부 졸업 프로젝트' 카테고리의 다른 글
ONOS에서 Provider 등록하여 ProviderService API 활용하기 (0) | 2020.01.23 |
---|---|
onos-create-app으로 새 ONOS 애플리케이션 만들기 (0) | 2020.01.22 |
501 HTTPS Required 에러 없이 ONOS 빌드하기 (1) | 2020.01.20 |
Ubuntu에서 ONOS 빌드하고 실행하기 (2) | 2020.01.14 |
2019년 졸업 프로젝트(ONOS REST Orchestration) 진행 과정 (1) | 2020.01.09 |