conohaのオブジェクトストレージをphpで利用する

conohaのオブジェクトストレージをphp経由で利用したのでその備忘録を残していきます。

基本の流れは
認証トークン取得 ⇒ 認証トークンを利用してREST APIをたたく ⇒ 必要に応じて戻り値を利用する

以下ソースコードの一部を抜粋します、実際のソースコードとは差異があります。(動作未確認)

ConohaAPI

   
class  ConohaAPI {
    //ユーザー名
    const userName = "gncu*****";
    //パスワード
    const pass = "*****";
    //テナント名
    const tenantName = "gnct*****";
    //テナントID
    const tenantId = "*****";
    //認証用URL
    const authUrl = "https://identity.tyo1.conoha.io/v2.0/";
    //オブジェクトストレージエンドポイントURL
    const endpointUrl = "https://object-storage.tyo1.conoha.io/v1/nc_".self::tenantId."/";

    //認証トークン
    private $token = "";

   //認証トークンの取得
    public function getToken(){
        if($this->token != ""){
            return $this->token;
        }
        $curl = curl_init();
        // HTTPヘッダー
        $headers = array(
         'Content-Type: application/json'
        );
        //認証用情報の設定
        $data = array(
            'auth' => array(
                'tenantName' => self::tenantName,
                'passwordCredentials' => array(
                'username' => self::userName,
                'password' => self::pass
                )
            )
        );
        $postdata = json_encode($data);

        // URL
       $url = self::authUrl . '/tokens';
        // cURLオプション
        $options = array(
            // HTTPヘッダーの設定
            CURLOPT_HTTPHEADER => $headers,
            // URL
            CURLOPT_URL => $url,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $postdata,
            CURLOPT_RETURNTRANSFER => true,
        );
        curl_setopt_array($curl, $options);
        // HTTPリクエストを実行
        $body = curl_exec($curl);
        
        if(curl_errno($curl)) {
            $msg = sprintf('cURL error: %s', curl_error($curl));
            throw new RuntimeException($msg);
        }
        // HTTPステータスコードを取得します。
        // 200:成功、401:失敗
        $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

        if(curl_errno($curl) OR $status_code != 200) {
            $msg = sprintf(
                'Authentication failed. The auth server returned status code(%d).',
                $status_code
            );
            throw new RuntimeException($msg);
        }
        // 戻り値のjson文字列をデコードする
        $authinfo = json_decode($body);
        // 認証トークン取得
        $this->token = $authinfo->access->token->id;
        return $this->token;
     }
    /**
     * 認証トークンを利用したリクエストを行います
     * @param type $url
     * @param type $type
     * @param type $optionAr
     * @param string $headers
     * @return type
     * @throws RuntimeException
     */
    private function request($url, $type = "GET",$optionAr = array(),$headerAr = array()){
        // cURLの初期化
        $curl = curl_init();
        // HTTPヘッダーが指定されていればそちらを優先する
        $headers = array(
            "X-Auth-Token"=>'X-Auth-Token: ' . $this->getToken(),
            "Accept"=>'Accept: application/json'
        );
        if($headerAr && is_array($headerAr)){
            //キーが重複する場合後のものが優先される
            $headers = $headers + $headerAr;
        }  
        
        // cURLのオプション
        $options = array(
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $headers,
        );
        if($type == "PUT"){
            $options[CURLOPT_PUT] = true;
//            $options[CURLOPT_CUSTOMREQUEST] = $type;
        }else{
            $options[CURLOPT_CUSTOMREQUEST] = $type;
        }

        
        //その他オプション指定があればマージする
        if($optionAr && is_array($optionAr)){
            //キーが重複する場合後のものが優先される
            $options = $options + $optionAr;
        }
        curl_setopt_array($curl, $options);
        // HTTPリクエストを実行
        // レスポンスには何も含まれていません。
        $body = curl_exec($curl);
        if(curl_errno($curl)) {
            $msg = sprintf('cURL error: %s', curl_error($curl));
            throw new RuntimeException($msg);
        }
        // HTTPステータスコードを取得します。
        $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        $array = array(
           "body"=> $body,
           "status_code"=> $status_code,
        );
        return $array;
    }

    /**
     * コンテナを作成する 作成済みの場合return true, 失敗した場合はExceptionを投げる
     * @param type $contenarName
     * @return boolean
     */
    function createContenar($contenarName){
        $url = self::endpointUrl.$contenarName;
        
        $result = $this->request($url,"PUT");
        if($result["status_code"] == 200 || $result["status_code"] == 201){
            return true;
        }
    }

    /**
     * オブジェクトのアップロードを行います。実行前にcreateContenarを実行してください
     * @param type $contenarName
     * @param type $fileName  //保存名
     * @param type $path    //データ
     * @return boolean
     */
    function uploadObject($contenarName = "", $fileName = "", $path = ""){

        if($contenarName == "" || $fileName == "" || $path == ""){
            return false;
        }
        
        //拡張子を決める
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $path);
        // Content-Typeヘッダーでアップロードするオブジェクトの形式を指定します。
        //目的に応じて随時修正してください。
        $contentType = "";
        if (preg_match('/jpeg$/', $mime)) {
                $contentType = "Content-Type: image/jpg;";
        } else if (preg_match('/gif$/', $mime)) {
                $contentType = "Content-Type: image/gif;";
        } else if (preg_match('/png$/', $mime)) {
                $contentType = "Content-Type: image/png";
        } 
        if ($contentType == "") {
                return false;
        }       
        
        $headerAr = array(
            "Content-Type"=>$contentType,

        );
        $opsionAr = array(
            CURLOPT_INFILE => fopen($path,"r"),
        );
        
        
        $url = self::endpointUrl.$contenarName."/".$fileName;
        $result = $this->request($url,"PUT", $opsionAr, $headerAr);
        // 201成功
        if($result["status_code"] === 201){
            return true;
        }
        return false; 
    }
    /**
     * コンテナ情報の取得、$pathを指定すると "$contenarName/$path/"配下を取得します
     * @param type $contenarName
     * @param type $path 取得するパス
     */
    function getContenarInfo($contenarName = "",$path = ""){
        $url = self::endpointUrl.$contenarName;
        if($path){
            $url = $url."?path={$path}";
        }
         
        $result = $this->request($url);
        print_r($result)
    }
    /**
     * オブジェクト情報の表示
     * @param type $contenarName
     * @param type $objName
     */
    function getObjectInfo($contenarName = "",$objName= "") {
        $url = self::endpointUrl.$contenarName."/".$objName;
        $result = $this->request($url);
        
        print_r($result);
    }

}

開発中のいろいろ

CURLOPT_PUTとCURLOPT_CUSTOMREQUESTの違い

アップロードの際はPUTを利用するのですがCURLOPT_CUSTOMREQUEST=PUTではファイルがうまくアップできずに悩みましたが、CURLOPT_PUTにすることで解決しました。
https://stackoverflow.com/questions/44918152/curlopt-put-vs-curlopt-postfields
CURLOPT_PUTとCURLOPT_CUSTOMREQUEST=PUTで動作が微妙に違うのですね。。

オブジェクトの名前と取得

ディレクトリを作って階層で管理したい場合はオブジェクト名に”dirName/filename”のようにすれば指定すればディレクトリが作成されるようです。

取得する場合はオブジェクト名に”dirName/filename”を指定する。階層ごとに取得したい場合はコンテナ取得のリクエストの際に”?path=dirName”のようにGETパラメータを追加すればできました。
上記コードを利用すると

//dirName/filename取得
$ConohaAPI->getObjectInfo("コンテナ名","dirName/filename")

//dirName配下のオブジェクト取得
$ConohaAPI->getContenarInfo("コンテナ名","dirName")

という形式になります。

正規表現は使えないのだろうか。。
リファレンスを見てもパラメータのみで説明がないため手探りで調べているんですがまだわかっていません。
それらしいパラメータもあるんですがうまくいかず用途が違うのか使い方が違うのか、そもそも使用できないのかはっきりしません。
ご存知の方教えてください。

とりあえず現状は正規表現なしでも大丈夫なので気が向いたら調べようと思います。

カテゴリー: php, server タグ: ,

vsftpdによるchrootの設定

ftpを設定していてchrootの設定をしたので備忘録として書きます。

chrootとは

簡単に言えばルートディレクトリを変更する機能です。
ルートディレクトリを変更することで別階層のディレクトリやファイルにアクセスできなくします。

vsftpdをインストール
$ yum -y install vsftpd
設定ファイルの編集
$ vi /etc/vsftpd/vsftpd.conf

以下を確認する
anonymous_enable=NO #匿名の許可
local_enable=YES #ローカルユーザの許可
write_enable=YES #書き込み許可
ascii_upload_enable=YES #アスキーモードの許可
ascii_download_enable=YES #アスキーモードの許可
chroot_local_user=YES #chrootの有効化
chroot_list_enable=YES #chroot_list_fileの有効化、yesの場合は最低でも空のファイルが必要になる
chroot_list_file=/etc/vsftpd.chroot_list #chroot_local_user=YESの場合はchrootを適応しないユーザーリスト、逆にNOの場合はchrootを適応するリスト。
ls_recurse_enable=YES ディレクトリごと一括での操作許可
allow_writeable_chroot=YES 書き込み権限があるとchrootができない使用なのでそれを無効化、デフォルトではなかったので追加

# 最終行へ追記:chrootのルートディレクトリ指定、指定しない場合はユーザーのホームディレクトリ直下になる
local_root=ルートパス

chroot_list_fileの設定(改行区切りで記述する)

$ vi /etc/vsftpd.chroot_list

終わったら再起動する
$ systemctl restart vsftpd

このままだとすべてのユーザーが同じルートディレクトリになってしまいます。
個別に分けるために以下の設定を行います。

設定ファイルに以下を追加
$ vi /etc/vsftpd/vsftpd.conf
user_config_dir=/etc/vsftpd/user_conf

/etc/vsftpd/user_confを作ってユーザー名のファイルを作成
$ vi /etc/vsftpd/user_conf/hoge
#local_rootを記述する
local_root=ルートパス

終わったら再起動する
$ systemctl restart vsftpd

カテゴリー: server, 未分類

centos上のdockerのcronが動作しない

タイトルの通りですがcentos上にdockerを構築、コンテナ内のcronが動作しないという現象が発生しました。

ネットで検索するとrsyslogというのがcronに必要なようなのでインストール、しかし動きません。。

設定ファイルをあれこれしてみるもののダメでした。

 

仕方がないので情報を集めているとどうもcentosの場合に起こるもののようなので対象のOSをubuntuに変更してdockerを再度構築、cronを見てみる。

動いた!

 

ubuntuは今まで利用していなかったのですがコンテナ内はcentosなのとdockerの利用方法はubuntuとcentosで差がなさそうなのでubuntuを利用することにします。

 

 

 

 

カテゴリー: 未分類 タグ:

webサーバーのdockerのコンテナを起動後、1~2分ほどでsystemctlによる操作ができなくなる

問題点

webサーバーのcentos7.4へdockerを構築しコンテナを起動、この時点では[$ systemctl status nginx]などが問題なく利用できるのですが少し待つと
$ systemctl status nginx
Failed to get properties: 接続がタイムアウトしました

となりその後systemctlを利用した操作が行えなくなりました。

なぜこの様なことが起こるのかは判明しませんでした(rsyslogが関係している?)が一時的な解決方法は見つけられました。

 

解決方法

私の場合はrsyslogが入っていなかったので、

「dockerを再起動しexecでコンテナに入りsystemctl が使用できなくなる前にrsyslogをインストール」、これで解決しました。

ただしコンテナを再起動するとまた同じ現象が発生することがあり、その場合はsystemctl が使用できなくなる前にsystemctl restart rsyslogでrsyslogを再起動すれば大丈夫そうでした。

 

あとがき

コンテナ再起動のたびにrsyslogを再起動するのは手間なので何とかしたいところです。。

この件はまた何かわかったら記事にします。

 

 

カテゴリー: 未分類 タグ:

Error response from daemon: cgroups: cannot found cgroup mount destination: unknown.

コンテナを再起動しようとして Error response from daemon: cgroups: cannot found cgroup mount destination: unknown. が発生。

エラー内容を調べてみるもリソース不足?でもcpuもメモリも余裕はある。。うーん、よくわからない。

VMを再起動すれば起動できるのと、環境が変わると出ていないようなので一旦置いておきます

 

カテゴリー: 未分類 タグ:

dockerのコンテナに同じネットワーク上にある別の端末でアクセスする

以下の手順でブリッジ接続の設定を有効にする。

  1. 利用するコンテナのあるVMを停止。
  2. 該当のVMの設定を開く。
  3. 設定のネットワークからアダプター3を選択
  4. 割り当てでブリッジアダプターを選ぶ。名前には現在利用しているネットワークを選択してokを押す。
  5. VM上で「# ip addr」などで有効になったIPアドレスを調べる。

以上です
ポートフォワードという手段もあるみたいですが今回はこちらを採用しました。

カテゴリー: 未分類 タグ:

Kitematicを触ってみる、Volumesについて

Kitematicを使ってみます

とりあえず適当なimageを入れてみます。今回はぱっと見で一番上にあった「hello-world-nginx」を入れます。

「hello-world-nginx」の「CREATE」を押してインストール。

インストールすると左下にVolumesがあるのでクリックすると選択肢が出てきたので「Enable Volumes」を選択。

するとローカルにディレクトリとindex.htmlが生成されました。

index.htmlを適当に変更して保存、ブラウザで見てみると変更が反映されました。
開発に利用する場合はここがメインになりそうです。

Volumesについて

このVolumesですが指定されたディレクトリを下記のような感じで書くとコンテナの内容をマウントしてくれるらしい。

$ docker run -v host_path:container_path image

いろいろできそう。

参考
https://qiita.com/lciel/items/e21a4ede3bac7fb3ec5a


ここで自分で作ったimageを見てみます。

検索して該当のimageの「CREATE」を押す。

(HTTP code 404) unexpected - {"message":"manifest for kishida/test:latest not found"

というエラーが表示されました。どうもリポジトリにlatestが無いとのことです。

リポジトリにlatestのタグを用意するか、Kitematicで「CREATE」の隣の「○○○」から「SELECTED TAG」で取得するタグを変更すれば無事にimageを入手できました。

 

無事入手できたのでimageを見ます。すると肝心のVolumesがありませんでした…

多分[run -v]で起動してコミットするんだな。ということで試してみます。

$ docker run -it -v /volume kishida/test:1.0
$ docker commit 5262ccd30a1a kishida/test:latest
$ docker push kishida/test:latest

Kitematicで再度取得するとVolumesが表示されました!

あとはマウントしたいパスをhost_pathに指定してやれば良さそうです。

カテゴリー: 未分類, 未分類 タグ: , ,

Dockerを導入してみる

お久しぶりです。2年ぶりぐらいの記事です。
現在Dockerを導入している最中なのでそのメモ書きのようなものを載せていきます。
私の環境はwindowsです。

dockerの導入手順

https://www.docker.com/products/docker-toolbox
にてDocker Toolbox入手
入手したDocker Toolboxを起動してセットアップウィザードを進めます。
今回は1から構築するのでデフォルト選択のままで。

インストール後、デスクトップにDocker Quickstart TerminalKitematic (Alpha)Oracle VM VirtualBoxのアイコンが存在すると思います。

それぞれ以下のような代物です。

  • Docker Quickstart Terminal

    Dockerを利用できる環境をパパっと用意してくれるいいやつ。

  • Kitematic (Alpha)

    DockerをGUI経由で使用できるツール。どこまでできるかは調査中です。

  • Oracle VM VirtualBox

    Dockerを利用する仮想環境。

 

次は各ツールを簡単に動かしてみます。
まずDocker Quickstart Terminalを起動します。仮想環境が構築されるので処理が終わるまで待ちましょう。
処理が終わったらOracle VM VirtualBoxを起動すると特別な作業をしていなければdefaultという名前の仮想環境が実行中になっていると思います。
ちなみにVirtualBoxから抜ける場合は「右側のctl」で抜けられます。
 

次は稼働中のdefaultにアクセスしてみます。今回はTeratermを利用します。

defaulの場合は以下の設定で以下の情報でログインできると思います
ホスト :192.168.99.100
ユーザ名:docker
パス :tcuser

ログインできたら何か入力してみます

$ docker run ubuntu:latest /bin/echo 'Hello world'

結果

docker@default:~$ docker run ubuntu:latest /bin/echo 'Hello world'
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
ae79f2514705: Pull complete
c59d01a7e4ca: Pull complete
41ba73a9054d: Pull complete
f1bbfd495cc1: Pull complete
0c346f7223e2: Pull complete
Digest: sha256:6eb24585b1b2e7402600450d289ea0fd195cfb76893032bbbb3943e041ec8a65
Status: Downloaded newer image for ubuntu:latest
Hello world
docker@default:~$ docker run ubuntu:latest /bin/echo 'Hello world'
Hello world
docker@default:~$

環境がダウンロードされて’Hello world’が表示されました。もう一度実行すると’Hello world’のみ表示されました。

次はKitematic (Alpha)起動してみます。

Kitematic (Alpha)を利用するのにアカウントが必要なのでhttps://hub.docker.com/で取得します。
※まだ調査中なのでとりあえず起動まで。


dockerの運用

ここまでで入手したツールを簡単に動かしました。次はDockerを利用する流れを記載していきます。

開発に利用するosを入手、起動
([-it]を付けることでコンテナ内で操作を行えます。)
$ docker run -it ubuntu:latest

必要なミドルウェアを導入する

$ apt-get install -y curl

環境の構築ができたら[$ exit]で終了いったん終了します。
[$ docker ps -a]で先ほど作ったコンテナの情報を表示、ここで表示されたコンテナIDを利用してコミットを行います。

$ docker commit 09d4d84350d2 user01/test:1.0

コミットすると[docker images]で追加されたimage(この場合はuser01/test:1.0)が表示されます。
追加されているのを確認したらDockerのリポジトリに変更をプッシュします。

$ docker login
#ログイン情報を入力

$ docker push user01/test:1.0

後はプッシュしたimageをpullすることで自分が作った環境を別の環境で再現できるようになると思います。

プルする場合は以下のようになります。
$ docker pull user01/test:1.0

はじめはタグ名を無しで
$ docker pull user01/test
のように実行したんですが「Error response from daemon: manifest for user01/test:latest not found」というエラーが出てしまいました。


総評

1度作った環境がサクッと別の環境で動かせるのはなかなか便利だと思いました。
コンソールから操作するのとKitematic (Alpha)を利用するのはどちらが覚えやすいんだろうか。。

カテゴリー: git, 未分類 タグ:

androidでビルドに失敗した時

kishidaです。また久しぶりの投稿になります。

androidプロジェクトをビルドをした時に下記のようなエラーが出ました。

android.content.res.Resources$NotFoundException: Unable to find resource ID

リソースに登録されていませんよ、的な内容なのですがイマイチ原因がわからなくて悩みました。
原因はandroid SDKバージョンが古いものがあることでした。少し前に一部アップデートしていたので大丈夫だと思っていたんですが漏れていたようで・・・

これからは多少時間がかかっても全て新しくするようにします。

そんな反省の記事でした。

カテゴリー: 未分類

socket.ioについて

kishidaです。今年初投稿ということであけましておめでとうございます。といっても既に2月なので遅すぎる挨拶ですが。

今回はsocket.ioを使用してチャットの機能を作ったので使用感とメモ書きを書いていこうと思います。

server.js

var server = require(“http”).createServer(function(req, res) {
res.writeHead(200, {“Content-Type”: “text/html”});
var output = fs.readFileSync(“./index.html”, “utf-8”);
res.end(output);
}).listen(3000);
var io = require(“socket.io”).listen(server);
io.sockets.on(“connection”, function(socket) {
//index.htmlからhogeというリクエストを受け取る
socket.on(“hoge”, function() {
/*****処理*****/
//index.htmlにhogeというリクエストを投げる
socket.emit(‘hoge’, socket.id);
});
//終了時のイベント
socket.on(“disconnect”, function() {
/*****処理*****/
});
});

index.html

var socketio = io.connect(‘http://localhost:3000’);
//server.jsへhogeというリクエストを投げる
socketio.emit(“hoge”);
//server.jsからhogeというリクエストを受け取る
socketio.on(“hoge”, function(id) {
/*****処理*****/
});
//socketio.disconnect(); //切断

基本はemit()でリクエストを投げてon()でリクエストを処理する認識でいいと思います。
接続終了時にはdisconnectのリクエストが発生し、index.htmlで切断する場合は.disconnect()を使用します。

処理の流れがシンプルで作るだけなら非常に便利だと感じました。ただ要件によってはやり取りをするメソッドが増えてトレースが大変になりそうなのでなるべく冗長を意識する必要がありそうです。

memo

.disconnect()の後自由なタイミングで再接続したい場合

socketio.connect();

で再接続できる。

 

リクエスト名に「connect」を使用できない
index.html

socketio.emit(“connect”);

server.js

socket.on(“connect”, function(data) {
console.log(‘connect’);
});

予約語の類だと思うのですが今のところリクエストも発生していないためわかったらまた報告します。

 

参考
http://www18407ue.sakura.ne.jp/wordpress/?p=200

カテゴリー: javascript, mysql, server