Your wish is my command

It’s a long journey

OpenStack: Block Migration

잘 알고 있다시피.. live migration을 하려면 shared storage(NFS, DFS…)가 필요합니다. 근데 share storage는 필연적으로 local storage에 비해서 느릴 수 밖에 없습니다. 그렇지요.

그리고 live migration은 OpenStack 시스템 유지/ 보수를 위해서도 필요합니다. 호스트 머신의 업그레이드가 필요할 때, 호스트 머신에 뭔가 작업을 할 때, 해당 호스트에서 서비스되는 인스턴스를 다른 곳으로 옮기고 작업해야하니깐요…

위에 언급했다시피, live migration은 shared storage가 필요하고, 이를 위해서는 IO를 일정부분 포기해야하는 경우가 생겨서, 이를 어떻게 만회할까하고 여기저기 뒤져보다가… block migration 기능이 있다는 것을 알았습니다.

live migration은 다른 호스트로 메모리만 복사하는 반면에 block migration은 메모리와 블럭 디바이스(인스턴스 스토리지, 볼륨 스토리지)도 같이 대상 호스트로 복사합니다. 결국 live migration이 조금 느린 반면에 shared storage가 필요 없어서, 유지보수를 위한 migration에는 사용할 수 있을 것 같습니다. 물론 갑작스런 호스트 머신의 오류일 경우에는 해당 사항이 없겠습니다.

사용방법은 간단합니다. 기존의 live migration 명령에 -block_migration 옵션만 붙이면 됩니다. 즉, 아래와 같죠.

$ nova live-migration -block_migrate

이를 위한 OpenStack 설정은 live migration 문서처럼 설정하고, 추가로 /etc/nova/nova.conf에 아래처럼 block_migration_flag를 설정합니다.

block_migration_flag= \
    VIR_MIGRATE_UNDEFINE_SOURCE, \
    VIR_MIGRATE_PEER2PEER, \
    VIR_MIGRATE_NON_SHARED_INC, \
    VIR_MIGRATE_LIVE

별 문제 없으면 잘 될겁니다. live migration이 필요했지만, shared storage가 준비되지 않으신 분들.. 한번 써 보세요~

그리고 추가로 OpenStack에서 migration에 대해서 간단히 정리합니다.

(True) live migration

shared storage를 사용하는 경우, instance의 memory만 대상 호스트로 복사하여 live migration을 수행합니다. 당연히 shared storage(NFS, GlusterFS, CephFS…)가 필요합니다.

메모리만 복사하므로 수초내에 migration이 완료됩니다.

$ nova live-migration <instance> <dest host>

참고) shared storage를 사용할 경우, live migration을 사용하기 위해서 disk cache를 disable 해야합니다. file 또는 ceph를 사용할 경우는 이 설정이 자동으로 들어가지만,  GlusterFS를 instance directory에 mount해서 사용할 경우 disk cache를 disable하는 것이 동작하지 않습니다. 따라서 glusterfs를 사용할 경우는 live_migration_flag를 아래처럼 VIR_MIGRATE_UNSAFE를 추가합니다.

live_migration_flag = \
    VIR_MIGRATE_UNDEFINE_SOURCE, \
    VIR_MIGRATE_PEER2PEER, \
    VIR_MIGRATE_LIVE, \
    VIR_MIGRATE_UNSAFE

block migration

live migration에 추가로 스토리지도 복사합니다. 약간 느리지만 live migration의 장점과 local storage의 IO의 장점을 동시에 취할 수 있습니다. 그리고 qemu에서 제공되는 기능을 사용하기 때문에 별도의 설정은 필요 없습니다.

$ nova live-migration -block_migrate <instance> <dest host>

migration

이건 live migration이 아니고, instance shutdown -> disk image copy -> instance boot 과정을 거침니다. 당연히 해당 호스트는 리부팅 됩니다. 그리고 migration이 되는 호스트는 scheduler에 의해서 자동으로 선택됩니다

$ nova migrate <instance>

block migration은 다른 호스트로 복사하는 기능을 qemu의 기능을 사용하므로 별도의 설정이 필요없었지만, migrate는 qemu나 libvirt의 기능을 사용하지 않고 수동(^^)으로 rsync+ssh를 이용하여 복사하기 때문에, 이동하려는 nova compute 노드에 nova 유저로 ssh login이 아무런 장애없이 되야됩니다. 또한 ubuntu로 설치했다면 nova 계정의 login shell이 /bin/false로 되어있는데 /bin/bash로 수정합니다. 결과적으로 compute에서 다른 compute로 아래 명령을 수행했을 때 오류가 없어야 합니다(아직까지 문서에 없음^^).

root@compute01:~# sudo -u nova ssh compute02 hostname
compute02
  • ssh public key authentication
  • ssh host key 등록

ps. 예상대로라면 volume을 붙인 인스턴스에서는 볼륨은 굳이 복사할 필요가 없으므로 volume이 없는 인스턴스와 block migration 성능 차이가 없어야 하는데도, 이상하게 volume이 붙어있는 인스턴스는 느리군요.. 이거 왜 이럴까요?

ps. 약간 알콜이 들어간 상태에서 쓴 글이라… 오타 작렬.. 논지도 왔다갔다… ㅎㅎ

Chef: Definition과 Notifies

Chef Definition은 아주 간단하게 Resource를 만들 수 있습니다. 아래 코드를 보면 간단하게 ~/.ssh/authorized_keys를 설정하는 Resource를 만들 수 있지요

define :authorized_keys_for, :keys => nil, :group => nil, :home => nil do
  user = params[:name]
  group = params[:group] || user
  home = params[:home] || "/home/#{user}"
  keys = params[:keys].kind_of?(String) ? [params[:keys]] : params[:keys]

  if not keys.nil? and not keys.empty?
    directory "#{home}/.ssh" do
      owner user
      group group
      mode 0700
      action :create
      only_if "test -d #{home}"
    end

    file "#{home}/.ssh/authorized_keys" do
       owner user
       group group
       content keys.map{ |x| x.strip }.join("\n")
    end
  end
end

뭐.. 간단한 일이면 이런 거 사용하면 됩니다. 저도 별 무리 없이 사용하고 있다가.. 여기서 notifies를 걸여줘야할 것이 생겼습니다. authorized_keys를 설정한 이후에 뭔가를 하려는 것이지요. 그래서 뭐 그냥 단순하게.. 아래처럼 했습니다…

authorized_keys_for charlie do
  keys ''
  notifies :create "ruby_bloc[foo]"
end

ruby_block "foo" do
  block do
    # do something with ruby
  end
  action :nothing
end

예.. 예상하시는데로.. 아무런 문제 없이 잘 될거라고 생각했지만, 역시나 안되었습니다(그러니까 이 글을 적고 있겠지요). 뭘까 뭘까? 하면서 다시 위 링크에 있는 문서를 다시 봤습니다.

A definition is not a resource or a lightweight resource

녜.. definition은 resource가 아닙니다. notifies나 subscribes는 resource에만 동작합니다. 그러니 아무리해도 안되지요… definition은 진짜로 C언어의 #define 처럼 동작하는 녀석인가 봅니다. ㅎㅎ

Keystone Token이 너무 많아요..

지난 일주일간 회사 워크삽으로 자리를 비웠다. 그 동안 단 한번도 노트북을 켜지 않았으니, 이거 참 대대한 일인 듯 하다. 작년 휴가에도 중간에 콜 받아서 노트북 켠 경험이 있는데 말이다…

각설하고.. 어쨌든 오늘 출근해서, 그래 내가 뭘 하고 있었던 거지? 하면서 설치된 오픈스택 구성을 둘러보려고, 딸랑 nova list 명령을 치는데, 이녀석이 먹통이다… 아무런 에러로 안뱉어내고 그냥 조용이 가만히 있는다.

이전에도 비슷하나 경험이 있었지만, 그때는 대충 넘어갔었지만, 들은 것도 있어서 뒤져봤는데 역시나… keystone의 token table에 데이터가 많이 쌓었나 보다.

mysql> select count(*) from token;
+----------+
| count(*) |
+----------+
|    20916 |
+----------+
1 row in set (25.17 sec)

뭐.. 개인 테스트 환경이고 해서, 무식하게 다 지우고 하니 nova list 명령이 잘 된다. 대략  들은 적이 있다. 각 호스트들의 agent들이 api를 수행할 때, 모두 keystone 인증 토큰을 발급 받는데, 이 토큰들이 계속 누적되어서 문제를 발생하는 것이다. 우선 간단하게 expire된 token을 날려버리는 것도 좋겠다.

mysql> delete from token where expires < current_timestamp [/code]

근데 저 정도의 숫자로 MySQL의 성능 이슈에 걸리는 것도 이상한 것이다. MySQL 튜닝 문제인 것 같다. token을 보면 primary key를 지외하고는 인덱스를 잡지 않았으며, 쿼리에 맞게 인덱스를 추가하면 되겠다.

# Query_time: 77.709733 Lock_time: 0.000087 Rows_sent: 0 Rows_examined: 20919 use keystone; SET timestamp=1371436405; SELECT token.id AS token_id, token.expires AS token_expires, token.extra AS token_extra, token.valid AS token_valid, token.user_id AS token_user_id, token.trust_id AS token_trust_id FROM token WHERE token.expires > '2013-06-17 02:32:08&' AND token.valid = 0;

그냥 무식하게 DELETE 하기 전의 token 테이블에 걸린 slow query를 보면 위와 같은데… expires, valid에 index를 걸어주면 좀 더 빠른 실행이 가능하지 않을까?

mysql> explain select id from token where expires > '2013-06-17 02:32:08' AND token.valid = 0;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | token | ALL  | NULL          | NULL | NULL    | NULL |  492 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

mysql> create index idx_token_expires_valid on token(expires, valid);
Query OK, 0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select id from token where expires > '2013-06-17 02:32:08' AND token.valid = 0;
+----+-------------+-------+-------+-------------------------+-------------------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys           | key                     | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+-------------------------+-------------------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | token | index | idx_token_expires_valid | idx_token_expires_valid | 10      | NULL |  492 | Using where; Using index |
+----+-------------+-------+-------+-------------------------+-------------------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

explain에 보듯이, 해당 slow query는 index를 타고 있다. 이제 일주일 후에도 같은 증상이 발생하는지 확인해보자..

추가) 그리고 어떤 녀석이 이런 token을 무지막지하게 만들어 내는지 확인해볼 필요가 있다.

mysql> select user_id, count(*) from token group by user_id;
+----------------------------------+----------+
| user_id                          | count(*) |
+----------------------------------+----------+
| 36068019a00e427c8c8e97e8a3cee8a2 |        1 |
| 5700bfc6b03b476f97021e5eede7989e |        4 |
| 8232fea985ff44c0be44b0473cac5fac |      557 |
+----------------------------------+----------+

위 처럼 특정 user_id에 의해서 집중적으로 토큰이 발생하는 것을 확인할 수 있으며

mysql> select id, name from user;
+----------------------------------+---------+
| id                               | name    |
+----------------------------------+---------+
| 5700bfc6b03b476f97021e5eede7989e | admin   |
| f09f583150f24c72a3a3ef1e1b12ae7d | cinder  |
| 8152fd3ea78a477eb096e829aa2e0bce | demo    |
| dcabff792209441794a4256f6611f43e | glance  |
| 36068019a00e427c8c8e97e8a3cee8a2 | nova    |
| 8232fea985ff44c0be44b0473cac5fac | quantum |
+----------------------------------+---------+
6 rows in set (0.00 sec)

처럼 quantum 서비스에 의해서 발생됩니다. quantum의 plugin 들이지요.

update 1) 저 무지막지한 토큰은 quantum에 의해 발생하는 것이 아니라, nova 서비스에서 quantum을 사용하면서 keystone token을 재사용하지 않아서 발생하는 문제입니다. 소스는 잊어버렸…

update 2) Launchpad에 버그가 등록되어 있습니다.

update 3) 이 문제를 해결하는 가장 간단한 방법은 token driver를 memcache로 변경하는 것이다.

[token]
driver = keystone.token.backends.memcache.Token

Ceph as OpenStack Instance Storage Backend

이 전 포스트에서 GlusterFS를 instance storage backend로 사용했었습니다. Ceph는 GlusterFS에 비해서 콤포넌트도 많고, 설치 과정도  복잡해서 “보여서” 우선 GlusterFS로 했었지요. 이번에는 Ceph로 live migration을 할 수 있게 instance storage backend로 설정해 봤습니다.

GlusterFS와 다르게 Ceph는 swift, {nova,cinder}-volume과 통합도 준비되어 있지만, 현재 목적에 맞지 않기에 여기서는 다루지 않습니다.

Overview

설치 전에 우선 Ceph의 구성을 알아보면, mon, mds, osd 세가지 프로세스로 구성되어 있으며, 각각의 기능은 대략 아래와 같습니다.

  • mon: cluster의 상태등의 데이터를 관리
  • mds: CephFS에 제공하는 메타정보 데이터 관리
  • osd: 데이터 저장, replication, recovery 등을 제공하고, mon 프로세스에 모니터링 정보를 제공

Ceph를 instance storage backend로 사용하기 위해서는 glusterfs와 마찬가지로 /var/lib/nova/instance 디렉토리를 cephfs로 마운트하여 사용합니다. qemu에서 librbd를 이용하여 직접 Ceph를 접근할 것으로 예상했지만 {nova,cinder}-volume은 문서상에 librbd를 이용하도록 나와있는 반면에, instance storage의 경우는 별도의 안내가 없습니다(제가 못찾은건지 모르겠지만….).

또한 ceph cluster를 별도로 구성하는 것을 권장하지만, 실험 환경이기 때문에 compute 노드에 osd를 glance 노드에 mon, msd를 구성하기로 합니다.

참고로 권장하는 최소 구성은 아래와 같습니다.

  • (mon + mds) x 3
  • osd x 3

권장은 저렇고, 제 테스트 환경에서는 충분한 노드가 없기 때문에 glusterfs 경우처럼 compute node에 osd를 glance 노드에 mon + mds, 그리고  control 노드에 ceph-deploy를 준비합니다.

설치 준비

Ceph의 설치는 ceph-deploy를 이용합니다. 이전 버전에서는 mkcephfs 등의 툴을 이용했었지만, 이제는 ceph-deploy를 이용해서 셋업합니다. ceph-deploy는 ssh로 설치하려는 노드에 접속하여 sudo를 통해 패키지 설치, 설정 등을 자동으로 진행합니다. chef, puppet, juju등 자동화 툴로 설치할 필요가 없다고 설명하고 있습니다.

ceph-deploy를 실행할 노드를 ceph admin 노드라고하고, 여기에서는 아래 준비가 필요합니다.

  • ceph 계정 만들기
  • ceph 계정의 private / public key 만들기
  • ceph 패키지 설치

ceph가 설치될 mon, mds, osd 가 실행될 노드에는 아래의 준비가 필요합니다.

  • ceph 계정 만들기
  • ceph 계정의 sudo 허용
  • admin 노드에서 생성된 public key로 접속 허용
  • ceph 패키지 설치: ceph-deploy가 패키지 설치를 하지만, 미러를 사용한다면 직접 설치를..

ceph 패키지 설치: 모든 노드

$ wget -q -O- 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' | sudo apt-key add - 
$ echo deb http://ceph.com/debian-cuttlefish/ $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/ceph.list
$ apt-get install ceph

ceph 유저 생성: 모든 노드

$ useradd -m ceph

sudoer에 등록: 모든 노드

ceph-deploy는 ssh로 ceph@node로 들어가서 sudo를 이용하여 설정을 하기 때문에 sudoer에 등록한다.

$echo "ceph ALL = (root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/ceph

ceph keypair 생성: admin 노드

$ su - ceph
$ ssh-keygen -t rsa -q -f /home/ceph/.ssh/id_rsa -P ""

ceph-deploy가 실행할 환경 설정

여기서 생성된 /home/ceph/.ssh/id_rsa.pub 파일을 모든 노드에 /home/ceph/.ssh/authorized_keys로 복사한다. 그리고 admin 노드의 ssh 설정을 아래처럼 하여 ssh hostname으로 ceph 유저로 로그인할 수 있도록 한다. 몰론 ceph-deploy가 접속한 모든 노드 (mon, mds, osd)를 모두 등록한다.

$ more ~/.ssh/config
Host compute01
Hostname compute01.stack
User ceph

그리고 처음으로 ssh로 연결하면 ssh key 확인 메시지가 뜨는데, 이를 없애기 위해서 미리 아래처럼 ssh server public key를 등록한다.

$ ssh-keyscan compute01.stack >> .ssh/known_hosts

모든게 정상적으로 설정 되었다면 아래 명령을 내렸을 때 아무런 입력 없이 바로 실행이 되어야 한다.

$ ssh compute01 sudo ls /

cluster 생성

클러스터 생성은 monitor 노드의 호스트를 지정하여 시작합니다. 여기서는 glance 노드를 monitor, mds로 사용하기로 했으므로 아래처럼 합니다.

$ ceph-deploy -v new glance
$ ls
ceph.conf ceph.log ceph.mon.keyring

실행하면 지정한 모니터로 ceph.conf 파일과 ceph.mon.keyring 파일을 생성합니다. 아직까지는 설정 파일만 만든 상태입니다.

만든 설정 파일을 monitor 호스트로 복사하고, 서비스를 시작하는 명령은 아래와 같습니다.

$ ceph-deploy -v mon create glance
Deploying mon, cluster ceph hosts glance
Deploying mon to glance
Distro Ubuntu codename precise, will use upstart

monitor 호스트인 glance에서 프로세스를 보면 ceph-mon 프로세스가 돌아가는 것을 확인할 수 있다. 그리고 glance 호스트에 보면 설정파일이 /etc/ceph/ceph.conf로 복사된 것을 확인할 수 있으며 /etc/ceph/ceph.client.admin.keyring 으로 어드민 키링이 생성된 것을 확인할 수 있다.

gatherkey 명령으로 monitor에 생성된 키를 가져온다.

$ ceph-deploy -v gatherkeys glance
Checking glance for /etc/ceph/ceph.client.admin.keyring
Got ceph.client.admin.keyring key from glance.
Have ceph.mon.keyring
Checking glance for /var/lib/ceph/bootstrap-osd/ceph.keyring
Got ceph.bootstrap-osd.keyring key from glance.
Checking glance for /var/lib/ceph/bootstrap-mds/ceph.keyring
Got ceph.bootstrap-mds.keyring key from glance.

ceph.client.admin.keyring, ceph.bootstrap-osd.keyring, ceph.bootstrap-mds.kerying 파일을 가져왔다. 이 키들은 osd, mds에서 사용된다. 여기서 가저온 ceph.client.admin.keyring은 아래처럼 사용할 수 있다.

$ ceph -c ceph.conf -k ceph.client.admin.keyring mon stat
e1: 1 mons at {glance=10.100.0.8:6789/0}, election epoch 2, quorum 0 glance

mds 설치: admin 노드

mds는 cephfs를 사용(cephfs로 마운트)한다면 필요한 것으로, rbd를 사용한다면 설치할 필요는 없다. 우리는 /var/lib/nova/instance를 cephfs로 마운트해서 사용할 것이므로 mds를 설치한다.

마찬가지로 glance host에 설치한다.

$ ceph-deploy -v mds create glance
Deploying mds, cluster ceph hosts glance:glance
Distro Ubuntu codename precise, will use upstart
Deploying mds bootstrap to glance
Host glance is now ready for MDS use.
Deploying mds.glance to glance

역시 glance 호스트에 보면 ceph-mds 프로세스가 떠있는 것을 확인할 수 있다.

osd 설치

osd는 ceph에 저장되는 데이터가 실제로 저장되는 곳입니다. disk와 journal로 구성이 됩니다.

Disk는 block device 또는 directory이고, 별도의 디스크를 권장하지만, 여기서는 /ceph/disk 디렉토리를 사용하겠습니다. 그리고 디렉토리를 사용할 경우 btrfs 또는 xfs를 권장하고 있지만, 여기서는 그냥 기존 파일 시스템에 쓰겠습니다.

Journal은 SSD를 권장하지만, 그냥 파일로 /ceph/journal 을 사용하겠습니다.

따라서 osd 노드에서는 아래처럼 미리 디렉토리를 생성합니다.

$ mkdir -p /ceph/disk

이제 osd를 설치합니다.

$ ceph-deploy -v osd create compute01:/ceph/disk:/ceph/journal
Preparing cluster ceph disks compute01:/ceph/disk:/ceph/journal
Deploying osd to compute01
Host compute01 is now ready for osd use.
Preparing host compute01 disk /ceph/disk journal /ceph/journal activate True

패키지가 설정되고, /etc/ceph/ceph.conf 파일이 복사됩니다. 하지만 아직은 osd가 활성화되지는 않았고, 아래처럼 활성화합니다.

$ ceph-deploy -v osd activate compute01:/ceph/disk:/ceph/journal
Activating cluster ceph disks compute01:/ceph/disk:/ceph/journal
Activating host compute01 disk /ceph/disk
Distro Ubuntu codename precise, will use upstart

이제 ceph-osd 프로세스가 시작되었으며, 아래처럼 osd가 하나 등록된 것을 확인할 수 있습니다.

$ ceph -c ceph.conf -k ceph.client.admin.keyring osd stat
e5: 1 osds: 1 up, 1 in

그리고 osd 노드에서도 ceph 관련 명령을 수행할 수 있게 admin 키를 보냅니다.

$ ceph-deploy -v admin compute01
Pushing admin keys and conf to compute01

이 키가 있어야 아래처럼 osd 노드에서도 ceph 명령을 수행할 수 있습니다.

ceph@compute01:~$ ceph osd stat e5: 1 osds: 1 up, 1 in

마찬가지로 compute02, compute03도 설치합니다.

instance 디렉토리 마운트

아래처럼 인스턴스 디렉토리를 마운트 합니다.

$ mount -t ceph 10.100.0.8:6789:/ /var/lib/nova/instances -o name=admin,secretkey=AQA+QrVRYNAnFhAAcN68f1a7xFqBzsSEqXQHmg==

10.100.0.8은 monitor 노드이고 secretkey는 /etc/ceph/ceph.admin.client.keyring에 있는 키를 사용합니다.

live migration

live migration 설정 및 방법은 glusterfs의 경우와 같습니다. 단 glustefs의 경우에는 VIR_MIGRATE_UNSAFE 옵션이 있어야 했지만, ceph의 경우는 해당 옵션이 없이 기본 값으로도 live migration이 됩니다.

$ nova show cirros1
+-------------------------------------+------------------------------------------------------------+
| Property                            | Value                                                      |
+-------------------------------------+------------------------------------------------------------+
| status                              | ACTIVE                                                     |
| updated                             | 2013-06-10T03:59:04Z                                       |
| OS-EXT-STS:task_state               | None                                                       |
| OS-EXT-SRV-ATTR:host                | compute02                                                  |
| key_name                            | admin                                                      |
| image                               | cirros-0.3.1-x86_64 (4f494d0b-ebdc-4ebd-8afc-8df804eaaf4e) |
| hostId                              | 4cf03b72c10726d78424eb6b1a62ddc4762cbbd1b148b8d684020c5b   |
| OS-EXT-STS:vm_state                 | active                                                     |
| OS-EXT-SRV-ATTR:instance_name       | instance-00000003                                          |
| OS-EXT-SRV-ATTR:hypervisor_hostname | compute01.stack                                            |
| flavor                              | m1.tiny (1)                                                |
| id                                  | a01a3bd7-6e49-485a-afc3-88c5b7c401d5                       |
| security_groups                     | [{u'name': u'default'}]                                    |
| user_id                             | 5700bfc6b03b476f97021e5eede7989e                           |
| name                                | cirros1                                                    |
| created                             | 2013-06-10T03:57:17Z                                       |
| tenant_id                           | 6f524cc90eb54f4b99ac70f3a3c070a7                           |
| OS-DCF:diskConfig                   | MANUAL                                                     |
| metadata                            | {}                                                         |
| admin network                       | 10.250.0.4                                                 |
| accessIPv4                          |                                                            |
| accessIPv6                          |                                                            |
| progress                            | 0                                                          |
| OS-EXT-STS:power_state              | 1                                                          |
| OS-EXT-AZ:availability_zone         | nova                                                       |
| config_drive                        |                                                            |
+-------------------------------------+------------------------------------------------------------+

$ nova live-migration cirros1 compute01

$ nova show cirros1
+-------------------------------------+------------------------------------------------------------+
| Property                            | Value                                                      |
+-------------------------------------+------------------------------------------------------------+
| status                              | ACTIVE                                                     |
| updated                             | 2013-06-10T04:03:03Z                                       |
| OS-EXT-STS:task_state               | None                                                       |
| OS-EXT-SRV-ATTR:host                | compute01                                                  |
| key_name                            | admin                                                      |
| image                               | cirros-0.3.1-x86_64 (4f494d0b-ebdc-4ebd-8afc-8df804eaaf4e) |
| hostId                              | beeddd176ef0ed6fdd0c7a04b376ee05dcda276e0596760ea021d649   |
| OS-EXT-STS:vm_state                 | active                                                     |
| OS-EXT-SRV-ATTR:instance_name       | instance-00000003                                          |
| OS-EXT-SRV-ATTR:hypervisor_hostname | compute01.stack                                            |
| flavor                              | m1.tiny (1)                                                |
| id                                  | a01a3bd7-6e49-485a-afc3-88c5b7c401d5                       |
| security_groups                     | [{u'name': u'default'}]                                    |
| user_id                             | 5700bfc6b03b476f97021e5eede7989e                           |
| name                                | cirros1                                                    |
| created                             | 2013-06-10T03:57:17Z                                       |
| tenant_id                           | 6f524cc90eb54f4b99ac70f3a3c070a7                           |
| OS-DCF:diskConfig                   | MANUAL                                                     |
| metadata                            | {}                                                         |
| admin network                       | 10.250.0.4                                                 |
| accessIPv4                          |                                                            |
| accessIPv6                          |                                                            |
| progress                            | 0                                                          |
| OS-EXT-STS:power_state              | 1                                                          |
| OS-EXT-AZ:availability_zone         | nova                                                       |
| config_drive                        |                                                            |
+-------------------------------------+------------------------------------------------------------+

아마도 이 영향인 것인지 live migration이 glusterfs에 비해서 약간 느리다는 인상을 받았습니다.

update) OpenStack 관련 문서에는 없지만, libvirtd 항목에 보면 rbd를 사용할 수 있습니다. 이건 나중에… ^^;

Glusterfs as OpenStack Instance Storage Backend

OpenStack에서 Live migration을 지원하려면 기본적으로 shared storage가 있어야 한다. 지금 요구사항 중의 하나가 장애에 대비한 live migration이라 shared storage를 설정해야했다.

요즘 대세는 Ceph이라고, 저번 오픈스택 모임에서 그랬는데, 잠깐 Ceph를 시도해본 결과, 설치과정이 그리 맘에 들지 않습니다. 그래요 완전히 개인적인 생각입니다.

그래서 상대적으로 쉬운 GlusterFS를 먼저 해보고려 합니다.

GlusterFS를 한다면 GlusterFS is Ready for OpenStack이라고 발표를 했는데, 문제는 아직 Beta 단계인 3.4 이야기이고, 자신들의 OpenStack Distribution인 RDO에 테스트 해 봤다는 이야기입니다.

내용을 보면 Instance Storage도 libgfapi를 이용해서 glusterfs에 직접 IO를 통해서 합니다. 그래서 중간의 파일 시스템에서 생기는 성능/ 캐쉬 등등의 문제를  없앴죠. Ceph도 그렇구요. 이거 제대로 된다면 바로 적용해 보려고 했지만… Ubuntu Package가 아직 없어요.. 그래서 정식 버전이 나오기 전에는 3.2에서 테스트 하기로 했습니다. 아고 서론이 길었어요.

Overview

테스트 환경에서는 별도의 glusterfs cluster를 구성하지 않고 glusterfs server는 compute node에 설치하기로 했습니다(아마도 별 문제 없으면 실제로도 그렇게 갈 생각이구요). 그리고 각 compute node는 /var/lib/nova/instance를 glusterfs localhost로 마운트 합니다.

Gluster FS 설치

간단히 각 compute node에서 설치합니다.

$ apt-get install glusterfs-server

Peer probe

$ gluster peer probe compute02.stack
$ gluster peer probe compute03.stack

Volume create

$ gluster volume create vm-instances replica 3 \
    compute01.stack:/gluster/instances \
    compute02.stack:/gluster/instances \
    compute03.stack:/gluster/instances

물론 각 compute node에는 만드려는 brick의 디렉토리인 /gluster/instance를 미리 만들어야합니다.

Volume start

$ gluster volume start vm-instances

마운트

마운트 하기 전에 아무런 인스턴스들이 생성되지 않은 상태로 합니다.

$ mount -t glusterfs localhost:vm-instances /var/lib/nova/instances

마운트하면 인스턴스 디렉토리의 소유권이 root.root가 되는데, nova.nova로 수정합니다. 신기하게도 이거 바꾸면 다른 노드도 같이 수정이 되는군요.

$ chown nova.nova /var/lib/nova/instances

한쪽 compute 노드에서 파일을 생성하면, 다른 컴퓨터 노드에도 같이 보이는 것을 확인할 수 있습니다.

nova.conf 설정

live migration을 하지 않는다면, 별도로 수정할 내용은 없습니다. 파일 권한이 제대로 되어있다면 바로 인스턴스를 생성 가능하고, 생셩된 인스턴스 파일은 다른 컴퓨트 노드에서 잘 보입니다.

live migration

Live Migration을 하려면 우선 OpenStack에 있는데로 설정합니다. 그런데 이 상태로 live migration을 하면 바로 아래처럼 오류가 발생합니다.

2013-06-03 11:52:20.743 11492 ERROR nova.virt.libvirt.driver \[-\] \[instance: 4717b4d4-61a7-4f4d-b5fc-5ff526822e89\] Live Migration failure: Unsafe migration: Migration may lead to data corruption if disks use cache != none

이것은 libvirt가 live migration을 할 때 안정성을 이유로 디스크 캐쉬가 된 상태에서는 live migration을 수행하지 않습니다. nova.conf에서 disk_modes=file=none,block=none 형태로 캐쉬를 안해주면 되겠지만, 이렇게 하고 하면 인스턴스를 생성할 때 부터 문제가 생깁니다. ㅎㅎ

그래서 불안정하게라도 live migration 하겠다고 하면 디스크가 캐쉬가 된 상태도 라이브 마이그레이션을 지원합니다. 이를 설정하면 nova.conf에 아래처럼 설정합니다.

live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE, \
                    VIR_MIGRATE_PEER2PEER, \
                    VIR_MIGRATE_LIVE, \
                    VIR_MIGRATE_UNSAFE

마지막의 VIR_MIGRATE_UNSAFE가 그것이죠.

어렇게 하고 live migration을 하면 잘 되는 것이죠.

root@control:~# nova show cirros1
+-------------------------------------+------------------------------------------------------------+
| Property                            | Value                                                      |
+-------------------------------------+------------------------------------------------------------+
| status                              | ACTIVE                                                     |
| updated                             | 2013-06-03T07:17:53Z                                       |
| OS-EXT-STS:task_state               | None                                                       |
| OS-EXT-SRV-ATTR:host                | compute01                                                  |
| key_name                            | admin_key                                                  |
| image                               | cirros-0.3.1-x86_64 (f81518a9-9dc2-4a2a-aeaf-331cc074d063) |
| hostId                              | a2b92d1a2237ab4772f395b2fde3e462bfad9420e0ffc33ad3d1a19d   |
| OS-EXT-STS:vm_state                 | active                                                     |
| OS-EXT-SRV-ATTR:instance_name       | instance-00000011                                          |
| OS-EXT-SRV-ATTR:hypervisor_hostname | compute01.stack                                            |
| flavor                              | m1.tiny (1)                                                |
| id                                  | 2991ad7d-b33e-4d3c-828b-da07338119f1                       |
| security_groups                     | [{u'name': u'default'}]                                    |
| user_id                             | 8f5b56a6a51c45338b0c1ba7cd7d9ebb                           |
| name                                | cirros1                                                    |
| created                             | 2013-06-03T06:21:27Z                                       |
| tenant_id                           | b5db61c3fa3845848b0f15c010dd500c                           |
| OS-DCF:diskConfig                   | MANUAL                                                     |
| metadata                            | {}                                                         |
| admin network                       | 10.250.0.2                                                 |
| accessIPv4                          |                                                            |
| accessIPv6                          |                                                            |
| progress                            | 0                                                          |
| OS-EXT-STS:power_state              | 1                                                          |
| OS-EXT-AZ:availability_zone         | nova                                                       |
| config_drive                        |                                                            |
+-------------------------------------+------------------------------------------------------------+

cirros1은 compute01에 있습니다. 이를 compute02로 옮긴다면..

root@control:~# nova live-migration cirros1 compute02

이제 compute02로 이사갔어요…

root@control:~# nova show cirros1
+-------------------------------------+------------------------------------------------------------+
| Property                            | Value                                                      |
+-------------------------------------+------------------------------------------------------------+
| status                              | MIGRATING                                                  |
| updated                             | 2013-06-03T07:18:59Z                                       |
| OS-EXT-STS:task_state               | migrating                                                  |
| OS-EXT-SRV-ATTR:host                | compute01                                                  |
| key_name                            | admin_key                                                  |
| image                               | cirros-0.3.1-x86_64 (f81518a9-9dc2-4a2a-aeaf-331cc074d063) |
| hostId                              | a2b92d1a2237ab4772f395b2fde3e462bfad9420e0ffc33ad3d1a19d   |
| OS-EXT-STS:vm_state                 | active                                                     |
| OS-EXT-SRV-ATTR:instance_name       | instance-00000011                                          |
| OS-EXT-SRV-ATTR:hypervisor_hostname | compute01.stack                                            |
| flavor                              | m1.tiny (1)                                                |
| id                                  | 2991ad7d-b33e-4d3c-828b-da07338119f1                       |
| security_groups                     | [{u'name': u'default'}]                                    |
| user_id                             | 8f5b56a6a51c45338b0c1ba7cd7d9ebb                           |
| name                                | cirros1                                                    |
| created                             | 2013-06-03T06:21:27Z                                       |
| tenant_id                           | b5db61c3fa3845848b0f15c010dd500c                           |
| OS-DCF:diskConfig                   | MANUAL                                                     |
| metadata                            | {}                                                         |
| accessIPv4                          |                                                            |
| accessIPv6                          |                                                            |
| admin network                       | 10.250.0.2                                                 |
| OS-EXT-STS:power_state              | 1                                                          |
| OS-EXT-AZ:availability_zone         | nova                                                       |
| config_drive                        |                                                            |
+-------------------------------------+------------------------------------------------------------+

Update: Andrew님의 제보에 의하면 3.2 버전에서는 파일 싱크할 때 파일이 read only로 걸려서 instance도 read lock이 걸리는 현상이 있다는군요. 3.3 부터는 OK

OpenStack Havana에 기대하는 몇가지…

요즘 grizzly 버전가지고 열심히 놀구 있습니다. 써보니깐.. private cloud까지는 훌륭히 만들 수 있을 것 같아요..

그러다 havana 릴리스에 어떤 blue print가 있는가 대충 훑어봤는데, 그중에 몇 개 이야기 해봅니다.

Support for Quantum L3 plugin in Devstack

l3 agent는 linux box에서 돌아가도록 되어있습니다. plugin 형태로 구축하면 hardware 장비를 조절하여 l3의 기능을 처리할 수 있겠죠. 이렇게 하면 l3 agent host에 부하가 줄어들어 보다 안정적인 서비스를 할 수 있습니다.

게다가 인프라 쪽은 s/w 보다는 h/w를 믿는 경향(?)이 있어서, 이게 더 심리적인 안정감을 줄 것 같습니다.

Enable loadbalancing vendors to implement their drivers - step0

이것도 위와 마찬가지로, haproxy로 되어있는 구성을 l4 장비를 이용해서 처리하는 기능이죠.

Dynamic DNS support for instances

aws ec2처럼 dns로 연결하고 싶습니다.

IPSec VPNaaS Python APIs / CRUD Operations

VPN은 public cloud를 한다면 필요한 기능입니다. havana까지는 api가 정리가 될 것 같고, 다음 릴리스에서는 뭔가 사용할 만한 결과물이 나오겠지요. OpenVPN을 이용한 client vpn, site-to-site vpn이면 훌륭합니다.

nova-network에서는 CloudPipe에서 지원했었지만, 아직 Quantum에는 없네요.

QoS API implementation: OpenVSwitch w/ DSCP

네트워크 서비스에 QoS는 기본이겠죠? 옆에 누군가가 엄청나게 네트워크를 써 버린다면.. ㅎㅎ

Ceilometer

metering입니다. 즉 어느 사용자가 얼마나 사용했는지를 나타냅니다. 효율을 측정하기 위해서는 반드시 필요하겠죠?

grizzly에 들어가긴 했지만, havana에 제대로 들어갈 예정입니다.

Heat

Heat는 CloudFormation 처럼 web site stack을 정의하고, 그에 따라서 API를 이용하여 OpenStack을 설정하는 콤포넌트 입니다. 이 기능보다 제가 주목하고 있는 것은 여기에 AutoScaling 기능이 들어간다는 점이죠.

역시 주 관심사가 Quantum이라 네트워크가 대부분이군요. ^^

Quantum: Multiple L3-agent

grizzly에는 multiple quantum agent가 된다는 정보가 있습니다.

l3-agent가 돌아가는 곳은 모든 네트워크 트래픽이 통과하기에 SPoF의 문제를 해결해야하는데, multiple quantum agent가 된다면 하나의 agent가 문제가 생겼을 경우 다른 agent로 traffic을 전달하여 failover를 수행할 수 있기에 테스트 해 봤습니다.

단순하게 기존의 network 노드와 똑같이 하나 더 network 노드를 추가한 후에 상황은 다음과 같습니다.

root@control:~# quantum agent-list
+--------------------------------------+--------------------+-----------------+-------+----------------+
| id                                   | agent_type         | host            | alive | admin_state_up |
+--------------------------------------+--------------------+-----------------+-------+----------------+
| 0665ee95-55b4-4f7a-a647-e1cf75c019b3 | Open vSwitch agent | network02.stack | :-)   | True           |
| 3d324ac7-a900-4ee9-b97a-df43a30a14e2 | Open vSwitch agent | network.stack   | :-)   | True           |
| 405275c2-48e2-43e4-9853-ce314976dc61 | DHCP agent         | network02.stack | :-)   | True           |
| 6d47b5ce-9d07-4fdb-ad2d-239123ad4087 | L3 agent           | network02.stack | :-)   | True           |
| 91b431b0-ece9-4fbb-a0b9-757c22034ffc | Open vSwitch agent | compute01.stack | :-)   | True           |
| a4887a55-6baf-43ad-a0fa-fd67a07181f2 | DHCP agent         | network.stack   | :-)   | True           |
| da17342b-d2cb-494a-9cf9-5a264b814dde | L3 agent           | network.stack   | :-)   | True           |
| f6d3146a-7a9e-4483-8cbd-c76ef1c76081 | Open vSwitch agent | compute02.stack | :-)   | True           |
+--------------------------------------+--------------------+-----------------+-------+----------------+

여기서 보면 network.stack과 network02.stack에 같은 쌍의 quantum agent가 동작하고 있습니다.

root@control:~# quantum l3-agent-list-hosting-router router_admin_ext
+--------------------------------------+---------------+----------------+-------+
| id                                   | host          | admin_state_up | alive |
+--------------------------------------+---------------+----------------+-------+
| da17342b-d2cb-494a-9cf9-5a264b814dde | network.stack | True           | :-)   |
+--------------------------------------+---------------+----------------+-------+

다시 확인하면 admin tenant의 router인 router_admin_ext 라우터는 network.stack에서 동작하는 것을 확인할 수 있습니다. 물론 network.stack 노드에서도 아래처럼 l3 agent가 정상적으로 설정되어 있는 것을 확인할 수 있습니다.

root@network:~# ip netns
qrouter-f3fe0c48-3248-4b2d-9f5b-c4a9c5fc01ed
qdhcp-85e58593-f869-4233-9e5f-26e7b63df016
root@network:~# ip netns exec qrouter-f3fe0c48-3248-4b2d-9f5b-c4a9c5fc01ed ip addr
15: qr-8acb1ecd-d0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether fa:16:3e:74:31:5d brd ff:ff:ff:ff:ff:ff
    inet 10.250.0.1/24 brd 10.250.0.255 scope global qr-8acb1ecd-d0
    inet6 fe80::f816:3eff:fe74:315d/64 scope link
       valid_lft forever preferred_lft forever
16: qg-a364f00b-f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether fa:16:3e:5e:80:f9 brd ff:ff:ff:ff:ff:ff
    inet 10.200.0.2/16 brd 10.200.255.255 scope global qg-a364f00b-f1
    inet6 fe80::f816:3eff:fe5e:80f9/64 scope link
       valid_lft forever preferred_lft forever
17: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

이제 이 router를 network02.stack으로 옮겨보죠. 옮기는 것도 자동으로 되면 좋겠는데, 아직 그러한 기능은 없는 것으로 보이며 l3-agent-router-remove, l3-agent-router-add 명령을 이용해서 옮기면 됩니다.

root@control:~# quantum l3-agent-router-remove da17342b-d2cb-494a-9cf9-5a264b814dde router_admin_ext
Removed Router router_admin_ext to L3 agent

이렇게 하면 network.stack에서 동작하는 l3-agent에서 router_admin_ext를 제외합니다. 이 명령을 내린 후에 network.stack 노드에서 해당 namespace를 확인하면 ip address 설정들이 모두 삭제되어 있습니다.

이제 network02.stack l3-agent에 router_admin_ext를 할당하면 됩니다.

root@control:~# quantum l3-agent-router-add 6d47b5ce-9d07-4fdb-ad2d-239123ad4087 router_admin_ext
Added router router_admin_ext to L3 agent
root@control:~# quantum l3-agent-list-hosting-router router_admin_ext
+--------------------------------------+-----------------+----------------+-------+
| id                                   | host            | admin_state_up | alive |
+--------------------------------------+-----------------+----------------+-------+
| 6d47b5ce-9d07-4fdb-ad2d-239123ad4087 | network02.stack | True           | :-)   |
+--------------------------------------+-----------------+----------------+-------+

이렇게 하면 옮겨지는 네트워크 노드인 network02.stack에 router에 해당하는 network namespace가 생기고, 그리고 여기에 l3-agent가 동작할 수 있는 설정이 생깁니다.

root@network02:~# ip netns
qrouter-f3fe0c48-3248-4b2d-9f5b-c4a9c5fc01ed
root@network02:~# ip netns exec qrouter-f3fe0c48-3248-4b2d-9f5b-c4a9c5fc01ed ip addr
12: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
13: qr-8acb1ecd-d0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether fa:16:3e:74:31:5d brd ff:ff:ff:ff:ff:ff
    inet 10.250.0.1/24 brd 10.250.0.255 scope global qr-8acb1ecd-d0
    inet6 fe80::f816:3eff:fe74:315d/64 scope link
       valid_lft forever preferred_lft forever
14: qg-a364f00b-f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether fa:16:3e:5e:80:f9 brd ff:ff:ff:ff:ff:ff
    inet 10.200.0.2/16 brd 10.200.255.255 scope global qg-a364f00b-f1
    inet6 fe80::f816:3eff:fe5e:80f9/64 scope link
       valid_lft forever preferred_lft forever

이상으로 multiple quantum agent의 간단한 기능을 확인해 봤습니다. 이 기능을 보면서 자동 failover/ load balancing 등의 기능이 있으면 좋겠구나 했지만 그런 기능은 아쉽지만 없네요. 하지만 간단한 작업으로 손쉽게 추가할 수 있을 것으로 보입니다.

update) 이를 자동화한 스크립트

Moinmoin에 지속적으로 계정 생성을 시도하는 Ip 차단: 가난한 자의 Ddos 방어..

본 서버에 같이 돌아가는 서비스 중에 하나가 moinmoin 입니다. 그냥 혼자 이것저것 정리한 내용들을 두서없이 적는데…

어떤 녀석들이 여기에다가 지속적으로 새로운 계정 생성을 시도하면서 스팸 짓을 합니다. 녜.. 여기 서버는 가상서버에다가 메모리도 1기가로 아주 작아서 이런 요청이 계속 들어오면 apache 프로세스그 많아서서 서버가 엄청 느려집니다.

그래서 해당 ip를 그냥 .htaccess에서 접근차단 하기로 했습니다. 아주 간단하게.~~

이 스크립트를 그냥 crontab에서 돌려 놓으세요~

물론 다른 나이스한 방법이 있겠지만.. 우선 귀찮아서… ^^;

Quantum: Ping은 되는데 인터넷이 안된다.

OpenStack 네트워크를 OpenVSwitch, GRE tunneling으로 구성하였을 경우에, 인스턴스에서 외부로 ping은 나가나 인터넷이 원할하지 않는 현상을 보일 때가 있습니다. 아주 일반적이지 않는 경우죠.

ping이 아주 잘 되고, dns lookup까지 아주 잘되면 일반적으로 인터넷이 잘된다고 할 수 있으나,

$ curl google.com # ---- [1]
$ curl www.google.com # ---- [2]

[1] 번의 경우에는 잘 되고, [2]번은 안되는 경우가 발생합니다.

이 것은 gre tunneling의 특성때문에 발생하는 것으로 CISCO의  Why Can’t I Browse the Internet when Using a GRE Tunnel?에 아주 자세히 설명이 나와있습니다.

간단히 요약하면 gre tunneling을 하려면 packet을 gre header를 포함하여 encapsuling하게 되는데, 이 때문에 tcp/ip 패킷에 한번에 담을 수 있는 사이즈는 1500(기본 MTU) - 24(GRE Header) = 1476이 됩니다. 그런데 gre tunneling 외부(L3 agent)에서 부터는 mtu가 1500이므로 상대방 웹 서버에서는 mtu인 1500으로 보내주는데, gre tunneling 안에서는 이를 분리하여 보낼 수 없으므로 최대 패킷 크기 mtu(SMSS)를 조정하는 ICMP를 호출합니다. 여기서 ICMP로 mtu 조절하는 명령이 막히면 서로 mtu 조절에 실패하여 통신이 막히는 것으로 보이는 것이죠..

위의 경우를 보면 [1]은 전송되는 데이터가 작아서 packet 한번에 전송됩니다. 그래서 SMSS를 조절할 필요가  없는데, 2의 경우는 패킷이 커서 SMSS를 조절할 필요가 있는데, 여기서 막히는 것입니다.

이를 해결하는 간단한 방법은 인스턴스 인터페이스의 mtu를 적당히 조절하면 됩니다. 저의 경우는 1454로 조절하면 되더군요. 그리고 이를 모든 인스턴스에 적용하게 하려면 quantum-dhcp-agent의 옵션으로 두면 되겠습니다.

그리고 production에 간다면, 당연히 인스턴스의 mtu를 조절하는 방법은 이상하고, gre tunneling이 사용하는 switch, interface의 mtu를 1524 이상으로 조절하는 것이 좋겠습니다.