マルチホストで動作するDockerネットワーキングツールについてのまとめ第二段。
(第一弾:pipework+GRE)
(第三弾:weave)
今回はcoreos/flannelを取り上げる。
検証環境
SoftLayerに以下のVirtual Serverを立ち上げて検証した。
||DC|hostname|private IP(eth0)|public IP(eth1)|
|:--|:--|:--|:--|:--|:--|
|ホスト1|Dallas 9|flannel01|10.142.51.197|X.X.X.X|
|ホスト2|Dallas 9|flannel02|10.142.51.198|Y.Y.Y.Y|
|ホスト3|Dallas 6|flannel03|10.106.91.131|Z.Z.Z.Z|
ホスト1とホスト2は同一VLAN上にあり、ホスト3は別VLAN上でホスト1,2とL3で接続されている。
各種バージョンは以下の通り。
version | |
---|---|
distribution | Ubuntu 14.04.2 LTS |
kernel | 3.13.0-48-generic |
docker | 1.6.0 |
etcd | 2.0.4+git |
flannel | 0.3.1+git |
準備
すべてのホストで以下の作業を実施する。
root@flannel01:~# apt-get update
root@flannel01:~# apt-get upgrade -y
root@flannel01:~# apt-get install build-essential linux-libc-dev bridge-utils git curl -y
root@flannel01:~# wget -O - https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz | tar -C /usr/local -vxzf -
root@flannel01:~# echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile
root@flannel01:~# source /etc/profile
root@flannel01:~# wget -qO- https://get.docker.com/ | sh
root@flannel01:~# git clone https://github.com/coreos/etcd.git /opt/coreos/etcd
root@flannel01:~# cd /opt/coreos/etcd
root@flannel01:/opt/coreos/etcd# ./build
root@flannel01:/opt/coreos/etcd# ln -s /opt/coreos/etcd/bin/etcd /usr/local/bin/etcd
root@flannel01:/opt/coreos/etcd# ln -s /opt/coreos/etcd/bin/etcdctl /usr/local/bin/etcdctl
root@flannel01:~# git clone https://github.com/coreos/flannel.git /opt/coreos/flannel
root@flannel01:~# cd /opt/coreos/flannel
root@flannel01:/opt/coreos/flannel# ./build
root@flannel01:/opt/coreos/flannel# ln -s /opt/coreos/flannel/bin/flanneld /usr/local/bin/flanneld
etcdクラスタの構築
まずは以下の手順を参考に、全てのホストが参加するetcdクラスタを構築する。
https://github.com/coreos/etcd/blob/master/Documentation/clustering.md
etcdクラスタの起動はオプションが煩雑なので、bashスクリプトを作る。
#!/bin/bash
if [ $# -ne 1 ]; then
echo "usage: $0 nodename"
exit 1
fi
declare -A NODES
NODES["flannel1"]="http://10.142.51.197:2380"
NODES["flannel2"]="http://10.142.51.198:2380"
NODES["flannel3"]="http://10.106.91.131:2380"
TOKEN="flannel-cluster"
if [[ "${NODES[$1]+_}" != "_" ]]; then
echo "invalid nodename"
exit 1
else
NAME=$1
fi
CLUSTER=""
for i in ${!NODES[@]}; do
if [ -n "$CLUSTER" ]; then
CLUSTER=$CLUSTER","
fi
CLUSTER=$CLUSTER${i}=${NODES[$i]}
done
set -x
/usr/local/bin/etcd -name $NAME \
-initial-advertise-peer-urls ${NODES[$NAME]} \
-listen-peer-urls ${NODES[$NAME]} \
-initial-cluster-token $TOKEN \
-initial-cluster $CLUSTER \
-initial-cluster-state new \
> /var/log/etcd.log 2>&1 &
ホスト1
root@flannel01:~# ./start-etcd.sh flannel1
root@flannel01:~# ps aux | grep etcd
root 12199 0.8 1.9 32436 19968 pts/0 Sl 01:13 0:03 /usr/local/bin/etcd -name flannel1 -initial-advertise-peer-urls http://10.142.51.197:2380 -listen-peer-urls http://10.142.51.197:2380 -initial-cluster-token flannel-cluster -initial-cluster flannel1=http://10.142.51.197:2380,flannel2=http://10.142.51.198:2380,flannel3=http://10.106.91.131:2380 -initial-cluster-state new
ホスト2
root@flannel02:~# ./start-etcd.sh flannel2
root@flannel02:~# ps aux | grep etcd
root 10218 0.4 1.2 26988 12772 pts/0 Sl 01:17 0:00 /usr/local/bin/etcd -name flannel2 -initial-advertise-peer-urls http://10.142.51.198:2380 -listen-peer-urls http://10.142.51.198:2380 -initial-cluster-token flannel-cluster -initial-cluster flannel1=http://10.142.51.197:2380,flannel2=http://10.142.51.198:2380,flannel3=http://10.106.91.131:2380 -initial-cluster-state new
ホスト3
root@flannel03:~# ./start-etcd.sh flannel3
root@flannel03:~# ps aux | grep etcd
root 10304 0.4 1.1 25900 11620 pts/0 Sl 01:18 0:00 /usr/local/bin/etcd -name flannel3 -initial-advertise-peer-urls http://10.106.91.131:2380 -listen-peer-urls http://10.106.91.131:2380 -initial-cluster-token flannel-cluster -initial-cluster flannel1=http://10.142.51.197:2380,flannel2=http://10.142.51.198:2380,flannel3=http://10.106.91.131:2380 -initial-cluster-state new
flannel用の設定をetcdに投入
etcdにflannel用の設定を投入する。ホスト1で設定した情報はetcdクラスタで共有されるため、ホスト2やホスト3からも取得できる。
root@flannel01:~# etcdctl member list
12c356968a3f262f: name=flannel1 peerURLs=http://10.142.51.197:2380 clientURLs=http://localhost:2379,http://localhost:4001
7415c1f880800621: name=flannel3 peerURLs=http://10.106.91.131:2380 clientURLs=http://localhost:2379,http://localhost:4001
758e04f1ad0b9fef: name=flannel2 peerURLs=http://10.142.51.198:2380 clientURLs=http://localhost:2379,http://localhost:4001
root@flannel01:~# etcdctl cluster-health
cluster is healthy
member 12c356968a3f262f is healthy
member 7415c1f880800621 is healthy
member 758e04f1ad0b9fef is healthy
root@flannel01:~# etcdctl set /coreos.com/network/config '{"Network": "192.168.0.0/16", "SubnetLen": 24, "SubnetMin": "192.168.90.0", "SubnetMax": "192.168.99.0"}'
flanneld起動
各ホストでflanneldを起動すると、etcdに設定した仮想ネットワーク192.168.0.0/16から指定した**/24のサブネットが切りだされ、その設定が環境変数ファイル/run/flannel/subnet.env**に書きだされる。
※SSHセッションが切断された場合、以下の起動手順だとflanneldは停止する。SSHセッションが切断される可能性が有る場合、nohupしたほうが良い。
ホスト1
root@flannel01:~# flanneld -iface=eth0 > /var/log/flannel.log 2>&1 &
root@flannel01:~# ps aux | grep flanneld
root 13531 0.0 0.3 260804 3944 pts/0 Sl 01:36 0:00 flanneld -iface=eth0
root@flannel01:~# cat /run/flannel/subnet.env
FLANNEL_SUBNET=192.168.93.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
ホスト2
root@flannel02:~# flanneld -iface=eth0 > /var/log/flannel.log 2>&1 &
root@flannel02:~# ps aux | grep flanneld
root 11446 0.0 0.3 260804 3940 pts/0 Sl 01:38 0:00 flanneld -iface=eth0
root@flannel02:~# cat /run/flannel/subnet.env
FLANNEL_SUBNET=192.168.97.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
ホスト3
root@flannel03:~# flanneld -iface=eth0 > /var/log/flannel.log 2>&1 &
root@flannel03:~# ps aux | grep flanneld
root 11346 0.0 0.4 260804 4204 pts/0 Sl 01:38 0:00 flanneld -iface=eth0
root@flannel03:~#
root@flannel03:~# cat /run/flannel/subnet.env
FLANNEL_SUBNET=192.168.98.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
docker0の設定変更
flanneldが出力した環境変数ファイルを用いて起動スクリプトを修正してDockerデーモンを再起動し、仮想ブリッジdocker0の設定を適切に変更する。
全てのホストで下記の手順を実行する。
root@flannel01:~# service docker stop
root@flannel01:~# ip link set dev docker0 down
root@flannel01:~# brctl delbr docker0
root@flannel01:~# iptables -t nat -F POSTROUTING
root@flannel01:~# sed -i -e '/^FLANNEL.*/d' /etc/default/docker
root@flannel01:~# sed -i -e '/^DOCKER_OPTS="$DOCKER_OPTS --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}"$/d' /etc/default/docker
root@flannel01:~# cat /run/flannel/subnet.env >> /etc/default/docker
root@flannel01:~# echo 'DOCKER_OPTS="$DOCKER_OPTS --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}"' >> /etc/default/docker
root@flannel01:~# service docker start
これにより、flannelが割り当てたサブネットを利用するようにdocker0の設定が変更される。
ホスト1
root@flannel01:~# ip addr show docker0
9: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
inet 192.168.93.1/24 scope global docker0
valid_lft forever preferred_lft forever
root@flannel01:~# ip addr show flannel0
8: flannel0: <POINTOPOINT,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
link/none
inet 192.168.93.0/16 scope global flannel0
valid_lft forever preferred_lft forever
root@flannel01:~# ip route show
default via X.X.X.X dev eth1
10.0.0.0/8 via 10.142.51.193 dev eth0
10.142.51.192/26 dev eth0 proto kernel scope link src 10.142.51.197
X.X.X.x/28 dev eth1 proto kernel scope link src X.X.X.X
192.168.0.0/16 dev flannel0
192.168.93.0/24 dev docker0 proto kernel scope link src 192.168.93.1
ホスト2
root@flannel02:~# ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
inet 192.168.97.1/24 scope global docker0
valid_lft forever preferred_lft forever
root@flannel02:~# ip addr show flannel0
6: flannel0: <POINTOPOINT,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
link/none
inet 192.168.97.0/16 scope global flannel0
valid_lft forever preferred_lft forever
root@flannel02:~# ip route show
default via Y.Y.Y.Y dev eth1
10.0.0.0/8 via 10.142.51.193 dev eth0
10.142.51.192/26 dev eth0 proto kernel scope link src 10.142.51.198
Y.Y.Y.y/28 dev eth1 proto kernel scope link src Y.Y.Y.Y
192.168.0.0/16 dev flannel0 proto kernel scope link src 192.168.97.0
192.168.97.0/24 dev docker0 proto kernel scope link src 192.168.97.1
ホスト3
root@flannel03:~# ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
inet 192.168.98.1/24 scope global docker0
valid_lft forever preferred_lft forever
root@flannel03:~# ip addr show flannel0
6: flannel0: <POINTOPOINT,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
link/none
inet 192.168.98.0/16 scope global flannel0
valid_lft forever preferred_lft forever
root@flannel03:~# ip route show
default via Z.Z.Z.Z dev eth1
10.0.0.0/8 via 10.106.91.129 dev eth0
10.106.91.128/26 dev eth0 proto kernel scope link src 10.106.91.131
Z.Z.Z.z/28 dev eth1 proto kernel scope link src Z.Z.Z.Z
192.168.0.0/16 dev flannel0 proto kernel scope link src 192.168.98.0
192.168.98.0/24 dev docker0 proto kernel scope link src 192.168.98.1
ここまででdocker0のネットワークが適切に再設定され、192.168.0.0/16という仮想ネットワークが敷設される。
Dockerコンテナ起動
以下のようなコンテナを起動する。
falnnelはコンテナに割り振られるIPアドレスを指定することができないため、Dockerコンテナ起動後にdocker inspect
でIPアドレスを調べる。
ホスト | コンテナ名 | Dockerが割り当てたIPアドレス | |
---|---|---|---|
ホスト1 | httpd | 192.168.93.2/24 | apache2を起動 |
ホスト1 | client1 | 192.168.93.3/24 | |
ホスト2 | client2 | 192.168.97.2/24 | |
ホスト3 | client3 | 192.168.98.2/24 |
ホスト1
root@flannel01:~# HTTPD=$(docker run -d -i -t ubuntu:latest /bin/bash)
root@flannel01:~# docker inspect --format="{{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}" $HTTPD
192.168.93.2/24
root@flannel01:~# CLIENT1=$(docker run -d -i -t ubuntu:latest /bin/bash)
root@flannel01:~# docker inspect --format="{{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}" $CLIENT1
192.168.93.3/24
httpdコンテナ
root@flannel01:~# docker attach $HTTPD
root@7ba62b092066:/# ip addr show eth0
10: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:5d:02 brd ff:ff:ff:ff:ff:ff
inet 192.168.93.2/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:c0ff:fea8:5d02/64 scope link
valid_lft forever preferred_lft forever
root@7ba62b092066:/# ip route show
default via 192.168.93.1 dev eth0
192.168.93.0/24 dev eth0 proto kernel scope link src 192.168.93.2
root@7ba62b092066:/# apt-get install apache2 -y
root@7ba62b092066:/# apachectl start
client1コンテナ
root@flannel01:~# docker attach $CLIENT1
root@0914ea0ca6ab:/# ip addr show eth0
12: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:5d:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.93.3/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:c0ff:fea8:5d03/64 scope link
valid_lft forever preferred_lft forever
root@0914ea0ca6ab:/# ip route show
default via 192.168.93.1 dev eth0
192.168.93.0/24 dev eth0 proto kernel scope link src 192.168.93.3
root@0914ea0ca6ab:/# apt-get install curl -y
ホスト2
root@flannel02:~# CLIENT2=$(docker run -d -i -t ubuntu:latest /bin/bash)
root@flannel02:~# docker inspect --format="{{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}" $CLIENT2
192.168.97.2/24
client2コンテナ
root@flannel02:~# docker attach $CLIENT2
root@d875be3ae788:/# ip addr show eth0
8: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:61:02 brd ff:ff:ff:ff:ff:ff
inet 192.168.97.2/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:c0ff:fea8:6102/64 scope link
valid_lft forever preferred_lft forever
root@d875be3ae788:/# ip route show
default via 192.168.97.1 dev eth0
192.168.97.0/24 dev eth0 proto kernel scope link src 192.168.97.2
root@d875be3ae788:/# apt-get install curl -y
ホスト3
root@flannel03:~# CLIENT3=$(docker run -d -i -t ubuntu:latest /bin/bash)
root@flannel03:~# docker inspect --format="{{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}" $CLIENT3
192.168.98.2/24
client3コンテナ
root@flannel03:~# docker attach $CLIENT3
root@6e553d2a2ad1:/# ip addr show eth0
9: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:62:02 brd ff:ff:ff:ff:ff:ff
inet 192.168.98.2/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:c0ff:fea8:6202/64 scope link
valid_lft forever preferred_lft forever
root@6e553d2a2ad1:/# ip route show
default via 192.168.98.1 dev eth0
192.168.98.0/24 dev eth0 proto kernel scope link src 192.168.98.2
root@6e553d2a2ad1:/# apt-get install curl -y
疎通確認
ここまでで、複数ホストをまたがって敷設された192.168.0.0/16という仮想ネットワーク上に4つのコンテナが起動した。コンテナやホストから疎通確認を行う。
※パケットはflanneldがカプセル化しており、かつetcdがハートビートを頻繁に送り合っているため、tcpdumpを仕掛けてもどれがコンテナからのパケットなのか判別できない。。。
root@flannel01:~# tcpdump -v -i eth0 src host 10.142.51.198
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
04:44:10.100921 IP (tos 0x0, ttl 64, id 26599, offset 0, flags [DF], proto TCP (6), length 75)
10.142.51.198.2380 > 10.142.51.197.36000: Flags [P.], cksum 0xa8e2 (correct), seq 2675:2698, ack 1, win 235, options [nop,nop,TS val 6265207 ecr 6596379], length 23
04:44:10.103224 IP (tos 0x0, ttl 64, id 51049, offset 0, flags [DF], proto TCP (6), length 52)
10.142.51.198.46572 > 10.142.51.197.2380: Flags [.], cksum 0x5e92 (correct), ack 9388, win 257, options [nop,nop,TS val 6265207 ecr 6596504], length 0
04:44:10.103439 IP (tos 0x0, ttl 64, id 26600, offset 0, flags [DF], proto TCP (6), length 75)
10.142.51.198.2380 > 10.142.51.197.36000: Flags [P.], cksum 0xa74f (correct), seq 2698:2721, ack 1, win 235, options [nop,nop,TS val 6265207 ecr 6596503], length 23
04:44:10.105420 IP (tos 0x0, ttl 64, id 51050, offset 0, flags [DF], proto TCP (6), length 52)
10.142.51.198.46572 > 10.142.51.197.2380: Flags [.], cksum 0x5e54 (correct), ack 9449, win 257, options [nop,nop,TS val 6265208 ecr 6596504], length 0
...
ホスト1 → httpdコンテナ
httpdコンテナはホスト1上の仮想ブリッジdocker0につながっているため、そのまま通信できる。
root@flannel01:~# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=64 time=0.038 ms
...
root@flannel01:~# curl 192.168.93.2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
client1コンテナ → httpdコンテナ
client1コンテナとhttpdコンテナは同じ仮想ブリッジdocker0につながっているため、そのまま通信できる。
root@0914ea0ca6ab:/# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=64 time=0.059 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=64 time=0.050 ms
...
root@0914ea0ca6ab:/# curl 192.168.93.2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
ホスト2 → httpdコンテナ
flanneldによってパケットがラップされ、同一VLANのホスト1上のhttpdコンテナと通信できる。
root@flannel02:~# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=61 time=0.616 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=61 time=0.317 ms
...
root@flannel02:~# curl 192.168.93.2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
client2コンテナ → httpdコンテナ
flanneldによってパケットがラップされ、同一VLANのホスト1上のhttpdコンテナと通信できる。
root@d875be3ae788:/# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=60 time=0.728 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=60 time=0.314 ms
...
root@d875be3ae788:/# curl 192.168.93.2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
ホスト3 → httpdコンテナ
flanneldによってパケットがラップされ、異なるVLANのホスト1上のhttpdコンテナと通信できる。
root@flannel03:~# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=61 time=1.82 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=61 time=1.52 ms
...
root@flannel03:~# curl 192.168.93.2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
clien3コンテナ → httpdコンテナ
flanneldによってパケットがラップされ、異なるVLANのホスト1上のhttpdコンテナと通信できる。
root@6e553d2a2ad1:/# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=60 time=1.86 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=60 time=1.62 ms
...
root@6e553d2a2ad1:/# curl 192.168.93.2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
まとめ
etcdとflannelを用いることで、L2 & L3で接続された複数ホスト間に仮想ネットワークを敷設してDockerコンテナを所属させ、相互に通信させることができた。
ただしetcd+flannelはDockerコンテナのIPアドレスを明示的に指定する(あるいはDHCPを指定する)ことができないため、注意が必要である。