娘ちゃんの自由研究でつくったルールベースの対話ロボ、久しぶりに初代ラズパイを起動しようとしたら、Kernelパニックで起動しない状態となった。娘ちゃん😭
Rootパーティションが認識出来ない旨のメッセージが表示されていて、おそらくSDカードが壊れたようだが何とか復旧できないか試みたところ、意外とあっさり動作したのでメモ。
以下、手順(と言っても大したことないが)
SDをUSBカードリーダーにセット
生きの良いラズパイに接続
fdiks -l で/dev/sdb2として認識されることを確認(sdb1がBoot、sdb2がルートパーティション)
fsck -y /dev/sdb2 で復旧
mount -t ext4 /dev/sdb2 /mnt でマウント
重要なデータ(娘ちゃんの作ったプログラム)を救出
umount /mnt
初代ラズパイに戻して、起動 ⇒ 成功!!
Apacheの設定がOSインストール時の初期設定との差分を見たいとき、下記のようにRPMファイルをダウンロードして、解凍して、比較すると良い。
cd /tmp
yum reinstall --downloadonly --downloaddir=./ httpd
一瞬、yum のインストールが行われた様に見えるので、ドキッとするが、最後にこんな表示があった。
exiting because "Download Only" specified
※DeepLで翻訳すると・・・「ダウンロードのみ」が指定されているため、終了する。
で、ダウンロードされたRPMファイルを解凍する
mkdir httpd_rpm_work
cd httpd_rpm_work
rpm2cpio ../httpd-2.4.6-97.el7.centos.5.aarch64.rpm | cpio -idv
で、比較する
diff /etc/httpd/conf/httpd.conf ./etc/httpd/conf/httpd.conf
以上
ラズパイWebサーバー(CentOS7)が古くなってきたので、にRockyLinux8をインストールしてWebサーバー運用を検討した。
以前、CentOS7にて、mdadmのRAID1にて運用していたが不運が重なり2台とも故障した結果、データロストを経験した為、RockyLinuxへの移管とともに、RAIDも1→6にすることを思いついた。RAID6であれば2台同時故障にも耐え得るので安心感が大きいのだが、構築してみて大きなデメリットがあることに気がついた。 mdadmは週1回程度のRAIDのベリファイが必要なのだが、その負荷がRAID1と6では比べ物にならないくらい高く、また時間がかかることがわかった。ラズパイという非力なCPU上では、RAIDのベリファイが発生するとコンソールの受付もままならないほど、遅くなる。これではベリファイや障害時リカバリーの間は、Webサービスの提供が困難になってしまう。
それでは、ベリファイやリカバリーの負荷が少なく、RAID1よりも安全なものは何かというと、RAID10ということになる。24時間365日サービス提供の必要がある為、今回はRAID10を採用することとした。
RAIDについてはわかっているようでわかっていないことがまだまだあるのだと今回勉強になった。
参考ページ
PowerShellにて便利なWindows用のパッケージツール「Scoop」をインストールする。PS> Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
エラーが発生した場合は、下記にて実行ポリシーを許可する。PS> Set-ExecutionPolicy RemoteSigned -scope CurrentUser
再度、試みるPS> Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
Zipコマンドのインストール。PS> scoop install zip
GitBash(コマンドプロンプト)にてZipコマンドが使えるようになる!cmd> zip -r xxxxx.zip hogehoge -x '*.mp4'
ちなみに、Zipコマンド以外にも様々なパッケージ、例えばFireFoxなどもインストール出来るようです。これは便利!
scoop search firefox などでインストールしたいパッケージ検索可能とのこと。 パッケージが見つからない場合は、scoop bucket add extras にてextrasバケットを追加すると、インストールできるパッケージが一気に増えるとのこと
FreeNASで古いHDDを再利用する際、以前にRAID等で利用してたHDDを使用した場合、クイックWipeしてもReplace が成功しないことがある。
この場合、HDDを接続したら、最初に下記のddコマンドで、パーティション情報を保持しているセクタをサクッと初期化することで、WipeやReplaceに失敗せず利用可能となる。
sysctl kern.geom.debugflags=0x10
dd if=/dev/zero of=/dev/ada1 bs=512 count=1
ada1 の部分を、ターゲットのHDDに変更する。間違えると大変なのでタイプミスに要注意。
Cybozu Office からスケジュールデータをスクレイピングして、ical形式でレスポンスするスクリプトです。
元は名古屋の社長様が作られたもので、その後、望遠鏡ドットコム様が修正したものを勝手にメンテしたものとなります。
<?php
/*
機能説明: CybozuOffice10に対してスケジュールをスクレイピングし、ical形式に変換して返却するスクリプト
利用方法: https://hoge/cybouz2ical.php?user=username&pass=password
usernameはCybozuOfficeの下記ページの「ログイン名」欄にて確認できます。
https://hoge/fuga/ag.cgi?page=PersonalUser
改版履歴:
2009/05/13
名古屋ではたらく社長ブログだもんで
サイボウズofffice8と同期するツール暫定版
http://ameblo.jp/sitescope/entry-10260113074.html
2013/08/26
上記にて公開していただいているスクリプトをサイボウズ9用に勝手改変版
サイボウズ Office Version 9.3.1 20130703181739にて確認
望遠鏡ドットコム
サイボウズ9・サイボウズ10のスケジュールをiCal形式でGoogleカレンダーに同期(一方通行)
サイボウズ9・サイボウズ10のスケジュールをiCal形式でGoogleカレンダーに同期(一方通行)
2020/09/18
上記で公開しているソースについて以下を改修しました
・ icalのUIDの重複を避ける(Googleカレンダーで同日の複数予定が1日1件しか取り込めないのを修正)
・ 基本認証設置しているCybozuOfficeに対応
・ URLパラメタにUser/Passを含めるように修正(SSLを使用しないと平文で送られるので要注意)
・ その他微修正
*/
$username = $_GET['user'];
$password = $_GET['pass'];
if(empty($username) || empty($password)){
die("parameter error!");
}
//-------------------------------------------------------------------------------------------------------------------
// 設定
//-------------------------------------------------------------------------------------------------------------------
//define("CHAR_CODE", 'SJIS'); //サイボウズの文字コード サイボウズ9はSJIS
define("CHAR_CODE", 'UTF-8'); //サイボウズの文字コード サイボウズ10はUTF-8
define("SCHEDULE_TITLE", 'Cybozu'); //スケジュールタイトル
define("SCHEDULE_MEMO", TRUE); //スケジュールメモまで取得する場合は'TRUE'
define('PRODID', 'Cybozu2iCal'); //http://ameblo.jp/sitescope/entry-10260113074.html
define('CYBOZU_URL', 'https://hoge/fuga/ag.cgi'); //サイボウズのURL
define("VERSION", 10); //CybozuOfficeバージョン(9,10のみ対応)
define("SALU_ID", ''); //基本認証(ある場合だけセット)
define("SALU_PW", ''); //基本認証(ある場合だけセット)
//-------------------------------------------------------------------------------------------------------------------
//POSTデータ組み立て
$data = array(
"_System" => "login",
"_Login" => "1",
"LoginMethod" => "2",
"_Account" => $username,
"Password" => $password
);
$data = http_build_query($data, "", "&");
//headerにセット
$header = array(
"Content-Type: application/x-www-form-urlencoded",
"Content-Length: ".strlen($data)
);
//基本認証があるサイボウズの場合
if(!empty(SALU_ID)){
$header[] = "Authorization: Basic " . base64_encode(SALU_ID . ":" . SALU_PW);
}
$context = array(
"http" => array(
"method" => "POST",
"header" => implode("\r\n", $header),
"content" => $data
)
);
//個人月間スケジュール
$url = CYBOZU_URL . '?page=ScheduleUserMonth';
//サイボウズからHTMLデータ取得
$work = file_get_contents($url, false, stream_context_create($context));
//文字コードをUTF-8へ変換
$work = mb_convert_encoding($work, "UTF-8", CHAR_CODE);
//正規表現がめんどくさいのでそのまま文字列比較で不要なとこをまず削除
$work = substr(strchr($work,'<table class="schedule usermonth" id="schedulemonth" width="100%" >'), 0);
$work = substr($work, 0, strpos($work, '<table class="monthNavi" '));
$work = substr(strchr($work,'<tbody id="um__body">'), 0);
$work = substr($work, 0, strpos($work, '</table>'));
//文字列比較でとりあえず、スケジュールを配列に格納
$wdate1 = "";
$wdate2 = "";
$schedule_list = "";
$loop_flg = TRUE;
while ( $loop_flg == TRUE ) {
//日付の取得
$wdate = "";
$wdate = substr(strchr($work,'<span class="date">'), 19);
$wdate = substr($wdate, 0, strpos($wdate, '</span>'));
if ($wdate == "") {
break;
}
//日付のフォーマットを変更
$date_sp = "";
$date_sp = split('/', $wdate);
$schedule_date = Date('Y') . sprintf("%02d",$date_sp[0]) . sprintf("%02d",$date_sp[1]);
//次の日も取得しとく
$schedule_date_tommorow = "";
$schedule_date_tommorow = Date('Ymd', mktime(0, 0, 0, (int)$date_sp[0], (int)((int)$date_sp[1] + 1), (int)Date('Y') ) );
//日付部分をカット
$work = substr(strchr($work,'<span class="date">'), 30);
$loop_flg2 = TRUE;
while ( $loop_flg2 == TRUE ) {
if ( strpos($work, '<a class="event" href=') > strpos($work, '<span class="date">') ) {
break;
} else {
//スケジュールIDを取得
if( VERSION == 9 ){
// サイボウズ9
$schedule_id = substr(strchr($work,'<div class="eventLink scheduleMarkTitle0" name="'), 53);
$schedule_id = substr($schedule_id, 0, strpos($schedule_id, '">'));
}else{
// サイボウズ10
$schedule_id = substr(strchr($work,'<div class="eventLink scheduleMarkTitle0 highlight-event'), 56);
$schedule_id = substr($schedule_id, 0, strpos($schedule_id, '" '));
}
if ( $schedule_id == "" ) {
break;
}
//----------------------------------
if ( SCHEDULE_MEMO ) {
//スケジュール内容まで取得
$wlink = substr(strchr($work,'<a class="event" href="ag.cgi?'), 30);
$wlink = substr($wlink, 0, strpos($wlink, '" title="'));
$url = CYBOZU_URL . '?' . $wlink;
//サイボウズからHTMLデータ取得
$work2 = file_get_contents($url, false, stream_context_create($context));
//文字コードをUTF-8へ変換
$work2 = mb_convert_encoding($work2, "UTF-8", CHAR_CODE);
//正規表現がめんどくさいのでそのまま文字列比較で不要なとこをまず削除
$work2 = substr(strchr($work2,'<table class="dataView scheduleDataView" width="100%" >'), 0);
$work2 = substr($work2, 0, strpos($work2, '<span class="vr_scheduleUserCount">'));
//スケジュールタイトルを取得
$work2 = str_replace("\n\r","", $work2);
$work2 = str_replace("\n","", $work2);
$work2 = str_replace("\r","", $work2);
$schedule_description = substr(strchr($work2,'<div id="scheduleMemo"><tt>'), 27);
$schedule_description = substr($schedule_description, 0, strpos($schedule_description, '</tt>'));
$schedule_description = strip_tags($schedule_description);
}
//----------------------------------
//スケジュールタイトルを取得
$schedule_title = substr(strchr($work,'=sm" title="'), 12);
$schedule_title = substr($schedule_title, 0, strpos($schedule_title, '">'));
//時刻を取得
if (strpos($work, '<span class="eventDateTime">') < strpos($work, '=sm" title="')) {
$schedule_time = substr(strchr($work, '<span class="eventDateTime">'), 28);
$schedule_time = substr($schedule_time, 0, strpos($schedule_time, ' '));
} else {
$schedule_time = '';
}
//タイトル部分をカット
$work = substr(strchr($work,'=sm" title="'), 12);
//時間指定があるか判定
if ( !empty($schedule_time) ) {
$time_sp = "";
$time_sp = split('-', $schedule_time);
$from_sp = split(':', @$time_sp[0]);
//http://www.sunvisor.net/node/383
if ( count($time_sp) > 1 ) {
$to_sp = split(':', @$time_sp[1]);
} else {
$to_sp = $from_sp;
}
$from_sp[0] = sprintf('%02d', @$from_sp[0]);
$to_sp[0] = sprintf('%02d', @$to_sp[0]);
$from_hour = "";
$to_hour = "";
if ($from_sp[0]<10) {
$from_hour = '0' . @$from_sp[0];
} else {
$from_hour = $from_sp[0];
}
if ($to_sp[0]<10) {
$to_hour = '0' . $to_sp[0];
} else {
$to_hour = $to_sp[0];
}
if (count($to_sp)<2){
$to_hour = $from_hour + 1;
$to_sp[1] = $from_sp[1];
}
$schedule_summary = $schedule_title;
//http://www.sunvisor.net/node/383
$schedule_from = ':' . $schedule_date . 'T' . @$from_sp[0] . @$from_sp[1]. '00';
$schedule_to = ':' . $schedule_date . 'T' . @$to_sp[0] . @$to_sp[1]. '00';
$schedule_description = $schedule_title . " " . $schedule_description;
} else {
$schedule_summary = $schedule_title;
//http://www.sunvisor.net/node/383
$schedule_from = ';VALUE=DATE:' . $schedule_date;
$schedule_to = ';VALUE=DATE:' . $schedule_date_tommorow;
$schedule_description = $schedule_title . " " . $schedule_description;
}
//http://www.sunvisor.net/node/383
$schedule_list[] = Array('id' => $schedule_id.$schedule_date,
'description' => $schedule_description,
'dtstart' => $schedule_from,
'dtend' => $schedule_to,
'summary' => $schedule_summary);
}
}
}
//ここから出力
header('Content-Type: text/calendar; charset=utf-8');
echo 'BEGIN:VCALENDAR'. "\n";
echo 'PRODID:' . PRODID. "\n";
echo 'VERSION:2.0'. "\n";
echo 'METHOD:PUBLISH'. "\n";
echo 'CALSCALE:GREGORIAN'. "\n";
echo 'X-WR-CALNAME:' . SCHEDULE_TITLE . "\n";
echo 'X-WR-CALDESC:' . SCHEDULE_TITLE . "\n";
echo 'X-WR-TIMEZONE:Asia/Tokyo'. "\n";
if ( !empty($schedule_list) ) {
foreach ( $schedule_list as $key => $vale ) {
echo 'BEGIN:VEVENT'. "\n";
// UIDの重複を避ける(Googleカレンダーで同日の複数予定が1件しか取り込めないのを修正)
// echo 'UID:' . SCHEDULE_TITLE . '-' . $vale['id'] . "\n";
echo 'UID:' . SCHEDULE_TITLE . '-' . $vale['id'] . md5($vale['description'].$vale['dtstart'].$vale['dtend']) . "\n";
echo 'DESCRIPTION:' . $vale['description']. "\n";
//http://www.sunvisor.net/node/383
echo 'DTSTART;TZID=Asia/Tokyo' . $vale['dtstart'] . "\n";
echo 'DTEND;TZID=Asia/Tokyo' . $vale['dtend'] . "\n";
echo 'SUMMARY:'. $vale['summary']. "\n";
echo 'END:VEVENT'. "\n";
}
}
echo 'BEGIN:VTIMEZONE'. "\n";
echo 'TZID:Asia/Tokyo'. "\n";
echo 'BEGIN:STANDARD'. "\n";
echo 'DTSTART:19700101T000000'. "\n";
echo 'TZOFFSETFROM:+0900'. "\n";
echo 'TZOFFSETTO:+0900'. "\n";
echo 'END:STANDARD'. "\n";
echo 'END:VTIMEZONE'. "\n";
echo 'END:VCALENDAR'. "\n";
?>
CoreOSはもうサポート切れとなり、Fedora系に引き継がれているが我が家ではLAN内で使用している為、まだまだ現役である。
今回、サーバーをRebootしてもすぐに起動してくるよう、設定した際のメモを残す。
自動起動ファイル作成
$ sudo vi /etc/systemd/system/docker1.service
[Unit]
Description=My Service
After=docker.service
Requires=docker.service
[Service]
WorkingDirectory=/opt/app/laradock
ExecStart=/opt/bin/docker-compose up -d
[Install]
WantedBy=multi-user.target
$ sudo systemctl enable docker1
$ ln -s '/etc/systemd/system/docker1.service' '/etc/systemd/system/multi-user.target.wants/docker1.service'
一覧確認
$ ls -l /etc/systemd/system/multi-user.target.wants/docker*
最後に、Rebootして docker-compose ps で全てUPしていることを確認した。
WindowsのVisualStudioCode で PHP CS Fixer 拡張を使用する場合、 php.exeが必要みたいです。
このようなエラーに悩まされました。 PHP CS Fixer: executablePath not found, please check your settings. It will set to built-in php-cs-fixer.phar. Try again!
さて、下記が対処方法となります。 ・ PHP CS Fixer 拡張をVisualStudioCode 上でインストール ・Windows版のPHPを下記よりダウンロードし https://windows.php.net/download/ 適当なパス 、例えば C:\php\ にZipの中身を設置します。 ・さらに settings.json にパスを設定します。 C:\Users\[ユーザ名]\AppData\Roaming\Code\User\settings.json に “php.validate.executablePath”: “C:\\php\\php.exe”, を追加します。 これで動作するようになります。
コロナの影響でリモート接続が必要となり構築した際のメモ。 既存のセグメントに透過的にアクセス可能とするのではなく、新しい仮想のVPNセグメントを生成して、そのセグメントに必要なクライアントを参加させる形となる。
Pritunlサーバ構築
AWS(準備)
EC2のOS: Amazon Linux 2
EC2インスタンスタイプ: t3.micro
ストレージ: 8GB
ElasticIP付与: 必須(以降、111.111.111.111)
ドメイン設定: 任意
インバウンドポート設定
TCP SSH、HTTP、HTTPS 保守作業する場所(自宅/会社)のIPのみ許可
UDP 18055 VPNクライアント用(*後ほどPritunlのWeb設定画面で表示される番号)
AWS(EC2サーバ構築)
SSHにて、ec2-userでログイン
ここ を参考にPritunlをインストール(以下)
sudo tee /etc/yum.repos.d/mongodb-org-4.2.repo << EOF [mongodb-org-4.2] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/4.2/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-4.2.asc EOF
sudo tee /etc/yum.repos.d/pritunl.repo << EOF [pritunl] name=Pritunl Repository baseurl=https://repo.pritunl.com/stable/yum/amazonlinux/2/ gpgcheck=1 enabled=1 EOF
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm gpg –keyserver hkp://keyserver.ubuntu.com –recv-keys 7568D9BB55FF9E5287D586017AE645C0CF8E292A gpg –armor –export 7568D9BB55FF9E5287D586017AE645C0CF8E292A > key.tmp; sudo rpm –import key.tmp; rm -f key.tmp sudo yum -y install pritunl mongodb-org sudo systemctl start mongod pritunl sudo systemctl enable mongod pritunl
sudo pritunl setup-key 0602d77d26fe4b22a8330a8c13c00000 のようなキーが表示される
sudo pritunl default-password Administrator default password: username: “pritunl” password: “xxxxxxxxxxxxxx”
Pritunl コントロール画面(初期設定)
ブラウザで、Pritunlセットアップ画面を開き、セットアップキーを入力する。https://111.111.111.111/setup
先程生成した、ユーザ、初期パスワードを入力しログインする。
Settingsダイアログが表示されるのでNew Password、Lets Encrypt Domain(ドメイン取得時のみ)などを設定する。
オーガナイゼーション作成
上部メニューの Users をクリック
Add Organization ボタンをクリック ※最低この単位で、VPNのネットワークセグメントを分けることも可能
ユーザ作成
Add User ボタンをクリック
対象ユーザを全員分作成
Linuxのファイルサーバをクライアントにする場合、Pinやメールは設定しない
アカウント(半角)
メール
PIN(整数6文字以上)
user1
user1@hoge.jp
123456
user2
user1@hoge.jp
123456
user3
user3@hoge.jp
123456
linuxFileServer
なし
なし
Windowsクライアントアプリの導入
ここ より Pritunl.exe をダウンロードし、インストール
クライアント起動
設定ファイルをImport(予め、Pritunlのユーザ一覧画面の右側のダウンロードボタンで、各ユーザの.tarファイルをダンロードしておく)
右上のハンバーガーメニューより「Connect」
Pinをいれれば接続完了
リモートデスクトップを使用する場合は、接続元・先の2台にクライアントアプリをインストールします。 また、既存のファイルサーバにアクセスしたい場合は、ファイルサーバにクライアントをインストールします(次章を参照)
LinuxサーバにOpenVPNクライアントを導入
ファイルサーバ等、Linuxマシンにアクセスを行う必要がある場合などに必要となる。 ※ここ の記事のclient側についてのみ参考にさせていただきました。
yum install epel-release yum install –enablerepo=epel openvpn
cat /usr/share/doc/openvpn/sample/sample-config-files/client.conf > /etc/openvpn/client.conf vi /etc/openvpn/client.conf
下記項目を変更します。 remote 111.111.111.111 18055 ca /etc/openvpn/ca cert /etc/openvpn/cert key /etc/openvpn/key tls-auth /etc/openvpn/ta 1
上記の4つの証明書(ca,cert,key,ta)に対応したファイルを作成 Pritunlのユーザ一覧画面からダウンロードしたプロファイル(tarファイル)をLhaForge等のアーカイバで展開し、展開された .ovpnファイルを、サクラエディタ等のテキストエディタで開いて切り出し、ファイル化します。
<ca>~この部分をコピーして単独のファイル ca にする~</ca>
<tls-auth>~この部分をコピーして単独のファイル ta にする~</tls-auth>
<cert>~この部分をコピーして単独のファイル certに する~</cert>
<key>~この部分をコピーして単独のファイル key にする~</key>
※Linuxであれば tar -xf user1.tar
のようにアーカイブを展開し、展開後のovpnファイルを
vi user1.ovpn
等としてファイル内のタグで囲まれたテキスト部分を切り出してファイル化します。少々面倒なのですが、もっと良い方法を知っている方がいれば教えてほしいです・・・
openvpn /etc/openvpn/client.conf
接続ログが画面にずらずらと表示される。エラーなどがなければOK。 ※以下のようなPinエラーになる場合は、当該ユーザ作成時にPinを設定している為、Pinの設定を削除してください。
Mon Mar 30 02:03:30 2020 AUTH: Received control message: AUTH_FAILED,CRV1:R:f52cbb8978cc402da249d2e00000003:bmls:Enter Pin Mon Mar 30 02:03:30 2020 SIGTERM[soft,auth-failure] received, process exiting
vi /opt/openvpn-client.sh
#!/bin/bash
openvpn /etc/openvpn/client.conf
chmod 755 /opt/openvpn-client.sh vi /etc/systemd/system/openvpn-client.service
[Unit]
Description = OpenVPN-Client daemon
[Service]
ExecStart = /opt/openvpn-client.sh
Restart = always
Type = simple
[Install]
WantedBy=multi-user.target
systemctl start openvpn-client ps aux | grep openvpn
下記のようなプロセスがあれば問題ない root 862 0.0 0.1 23284 3420 ? Ss 3月31 0:00 /bin/bash /opt/openvpn-client.sh root 867 0.0 0.4 56504 7976 ? S 3月31 0:05 openvpn /etc/openvpn/client.conf
systemctl enable openvpn-client
サーバを再起動後しばらくして、Pritunl ユーザ一覧画面で、当該サーバが接続状態となっていればOK
「ヒアドキュメント」という言葉がいつも出てこなくなるので、メモ。
大体、いつもやりたくなるのは、下記のようにシェルで変数にぶち込みたくなるパタンが多い。
HOGE=$(cat << EOS
hoge
fuga
EOS
)
もしくは
HOGE=`cat << EOS
hoge
fuga
EOS
`
最近のコメント