SE吾郎の備忘録

24歳SEの勉強用の備忘録です.皆様の役にも立てれば幸いです.

【備忘録】Jenkins新スレーブPC追加で発生したエラーと対処方法まとめ

こちらの記事を参考にJenkinsのスレーブPC(ノードPC)を追加したのですが,いくつか苦戦したポイントがあったので,エラーとその対処方法をまとめます.
【CI奮闘記】第6章:エージェントを作ってみよう!
エラー発生箇所としては「Lauch agent by connecting it to the master」を設定で選択し,新ノードPC側でJava Web Start (JNLP)を実行するタイミングでエラーが発生しました.

JNLP不正エラー

発生エラー


    INFO: Locating server among [http:://255.255.255.255:8080/jenkins/]
    ** **, 20** **:**:** ** org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver resolve
    INFO: Remoting server accepts the following protocols: [JNLP4-connect, Ping]
    ** **, 20** **:**:** ** org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver isPortVisible
    WARNING: connect timed out
    ** **, 20** **:**:** ** hudson.remoting.jnlp.Main$CuiListener error
    SEVERE: http:://255.255.255.255:8080/jenkins/ provided port:49600 is not reachable
    java.io.IOException: http:://255.255.255.255:8080/jenkins/provided port:49600 is not reachable
        at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:311)
        at hudson.remoting.Engine.innerRun(Engine.java:724)
        at hudson.remoting.Engine.run(Engine.java:540)

※(エラーログのurlと時間は変更しています)


まず,新ノードPCからアクセスするJenkinsのマスターのURLをIPアドレスからリネームしている方もIPアドレスからアクセスしてください.
次に,本エラーの対処方法ですが,私の場合は既に別のノードPCが接続された状態で,さらに新規のノードPCの追加中にこのエラーが発生しました.Jenkinsの設定,JenkinsのバージョンもJavaのバージョン,ファイルパスも合わせたのにエラーが発生してしまいました.


そこで,jenkins-agent.jnlpの中身を比較し,修正したところ接続することができました.
jenkins-agent.jnlpをエディタで開き,後ろの</argument>と</application-desc>の間に「<argument>-url</argument><argument>http://255.255.255.255:8080/jenkins/」を追加してください.(IPアドレス部分は自分のJenkinsマスターのIPアドレス)
この1文を追加するだけで,接続することができました!
なぜ,jenkins-agent.jnlpの中身が異なるものになっていたのか原因は分かりませんが,個人では中々気付くことが出来ないと思うので,同様のエラーが発生している方はやってみてください!

Java version違いエラー

発生エラー


    ** **, 20** **:**:** ** Main verifyJavaVersion
    SEVERE: Running with Java class version 63 which is not in the list of support versions: [52, 55]. Run with the --enable-future-java flag to enable such behavior, See https://jenkins.io/redirect/java-support/
    java.lang.UnsupportedClassVersionError: 63.0
        at Main.verifyJavaVersion(Main.java:130)
        at Main.main(Main.java:99)
    
    Jenkins requires Java versions [8, 11] but you are running with Java 19 from C:\**
        at Main.verifyJavaVersion(Main.Java:130)
        at Main.main(Main.java:99)

 

こちらのエラーはJenkinsでの対応可能なJavaのバージョンと使用環境のJavaバージョンの不整合により発生します.

JetnkinsでのJava対応バージョンは8か11なので,それ以外のバージョンを使用すると発生します.

私の場合はJavaのバージョンは19だったので,エラーが発生してしまいました.また,Jenkinのバージョン2.357以降はJava8の対応がなくなることがこちらの記事で発表されています.
Jenkins公式サイト(Java対応バージョン)
そのため,Javaをインストールし直す場合,バージョン11をおすすめします.

・agent.jar不足エラー

今後,追記予定

Javaパスエラー

今後,追記予定

【備忘録】instagram自動化プログラムが使えなくなった!?

こちらの記事で作成したインスタの自動実行プログラムなのですが,コードは変更していないのにエラーが発生し,実行出来なくなってしまいました.(リンク先記事のソースコードは修正済みです)

segoro.hatenablog.com

ある日突然以下のエラーが出るようになってしまいました.
target = driver.find_elements_by_class_name('_9AhH0')[10] IndexError: list index out of range
エラーの内容としてはクラスの名前「_9AhH0」が見つかりません というエラーです.
しかし,今まではこのコードで動いていたはずですし,特に何も触っていないのに...
そこでひとつひとつ動作を確認してみましたが,やはり
target = driver.find_elements_by_class_name('_9AhH0')[10]
の部分でエラーが出てしまいます.読み込み速度による問題でもなさそうでした.(何箇所か以前より読み込み速度が遅くなりエラーになるところがありました.)
キー自体が存在しないのか!?と思いブラウザから確認してみるとビンゴでした.
instagram側のアップデートの際にキーが変わっていたみたいです.この他にも2~3箇所キーが変わっている場所がありました.
ひとつずつキーを探して修正しましたが,毎回アップデートの度に変わるのであれば面倒ですね...
何か良い方法はないでしょうか...
修正部分のコードを載せておきます.

before


    driver.find_element_by_class_name('HoLwm').click()
    target = driver.find_elements_by_class_name('_9AhH0')[10]
    driver.find_elements_by_class_name('_9AhH0')[9].click()
    driver.find_element_by_class_name('fr66n').click()
    driver.find_element_by_class_name('coreSpriteRightPaginationArrow').click()

after


    driver.find_element_by_class_name('_a9_1').click()
    target = driver.find_elements_by_class_name('_aa8k')[10]
    driver.find_elements_by_class_name('_aa8k')[9].click()
    driver.find_element_by_class_name('_aamw').click()
    driver.find_element_by_class_name('_aaqg').click()

【備忘録】seleniumは定期的なメンテナンスが必要!? Chrome Driverエラー対応方法

Chrome Driverバージョン不整合対応方法

seleniumで自動実行プログラムを作成してちょくちょく回していたのですが,突然以下のエラーが出てしまいました.
selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 94 Current browser version is 104.0.5112.101 with binary path /Applications/Google Chrome.app/Contents/MacOS/Google Chrome こちらのエラーはChromeのバージョンとChrome Driverのバージョンが一致していません というエラーです.
Chrome Driverは更新はしていないので,Chromeが自動更新されていたみたいです.
現在のChromeのバージョン(このエラーの場合,104.0.5112.101)を確認し,対応するChrome Driverに更新すれば解決します.
バージョンの1番大きい数字が合うものをダウンロードすればOKです.(今回はバージョン104)
また,Chromeの自動更新が走らないように設定の変更方法も記載します.

chrome_version

Chromeの自動アップデート停止方法

Mac

公式リンクMac版
どうやら「com.google.Keystone.plist」の値を変更すれば良いみたいですね.
これだけではよく分からないと思うので,やり方を解説します.自動アップデートを停止する場合,「chrome://settings/help」からの手動アップデートも出来なくなるので注意してください.
terminalからコマンドdefaults write com.google.Keystone.Agent checkInterval 0 で自動アップデートをオフにすることが出来ます.再び自動アップデートをオンにしたい場合はdefaults write com.google.Keystone.Agent checkInterval 1で戻すことが出きます.

Windows

公式リンクWindows版
1.「Windows」+「R」で「ファイルを指定して実行」を起動
2. コマンド「gpedit.msc」を入力
3. コンピュータの構成->管理用テンプレート を右クリックし,テンプレートの追加と削除をクリック
4. 追加をせクリックし,「コンピュータの構成\管理用テンプレート\クラシック管理用テンプレート(ADM)\ Google \ Google Update \ Applications \ Google Chrome」のパスを選択
5. 「Update Policy Override」をクリックし,「Updates disabled」を選択すればOKです.
6. PC再起動後にchromeの自動アップデートは無効になります

【備忘録】インスタのフォロワーが自動で増える!? pythonで自動いいねを押すプログラム

seleniumを使用したブラウザ自動操作によってフォロワーが自動で増えるプログラムの作成を目指します.
本記事はこんな方におすすめです.

  • seleniumを使用したプログラムの勉強をしたい人
  • インスタのフォロワーを何もせずに増やしたい人

実装するプログラムのアルゴリズムは以下のようになります.
seleniumを起動
・instagrmのページにアクセスし,ログインする.
・設定したタグを検索し,最新投稿から複数個の投稿にいいねをつける.
このような手順の方がフォロワーが増えやすいよというアドバイスがありましたら,コメントいただけると嬉しいです.
以下に実際のコードを載せます.実際に使用する場合は,コピペしてID****とpass****を自分のユーザIDとパスワードに変更するだけで実行可能です.
検索ワードも自分に合ったものに変えて見てください.


from selenium import webdriver
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
import time
import random

def login():
    # Instagramのログインページを指定
    driver.get('https://www.instagram.com/accounts/login/?source=auth_switcher')
    time.sleep(1)

    ### 追加箇所 Start ###
    # メアドとパスワードを入力
    driver.find_element_by_name('username').send_keys('ID*******')
    time.sleep(1)
    driver.find_element_by_name('password').send_keys('pass*****')
    time.sleep(1)

    # ログインボタンを押す
    driver.find_element_by_class_name('L3NKy').click()
    time.sleep(5)

    #後で
    driver.find_element_by_class_name('yWX7d').click()
    time.sleep(5)

    #後で
    driver.find_element_by_class_name('_a9_1').click()
    time.sleep(3)

def tagsearch(tag, driver):
    # tagで渡ってきたハッシュタグのページにアクセス
    tagurl = 'https://www.instagram.com/explore/tags/'
    driver.get(tagurl + tag)
    time.sleep(3)

def pushnice(nice_num, driver): # いいねの回数を引数で指定
    # 最新の投稿が見える位置まで移動(スクロール)
    time.sleep(3)
    target = driver.find_elements_by_class_name('_aa8k')[10]
    actions = ActionChains(driver)
    actions.move_to_element(target)
    actions.perform()
    time.sleep(3)

    # 最新の投稿の1枚目をクリックしていいねを実行
    driver.find_elements_by_class_name('_aa8k')[9].click()
    time.sleep(3)
    #次の投稿を表示(矢印を押す)
    driver.find_element_by_class_name('_aamw').click()
    time.sleep(5)

    # 指定回数繰り返し
    for nn in range(nice_num-1):
        # ループ中のエラー対策
        print(nn)
        MAX_RETRY = 10 # エラーリトライ上限
        for retry_n in range(MAX_RETRY): # エラー発生時MAX_RETRY回リトライする
            try:
                # 次の投稿へ移動
                driver.find_element_by_class_name('_aaqg').click()
                x = random.randint(0, 5)
                time.sleep(20 + x)

                # いいねを押す
                driver.find_element_by_class_name('_aamw').click()
                x = random.randint(0, 5)
                time.sleep(5)
            # エラー時にはリトライ
            except:
                time.sleep(5)
                print("except")
                x = random.randint(0, 5)
                # 次の投稿へ移動
                driver.find_element_by_class_name('_aaqg').click()
                x = random.randint(0, 5)
                time.sleep(20 + x)

            # エラーなし
            else:
                break


if __name__ == '__main__':
    # Macの方用のChrome Driverのパスの設定
    driver = webdriver.Chrome('./chromedriver')
    time.sleep(1)
    # ログイン
    login()

    #タグ検索
    # 検索するハッシュタグを指定する。
    taglist = ['followforfollowback', 'likeforfollow', 'l4f']
    countlist = [5, 5, 5] 
    
    for j in range (0, 10):
        for i in range(0, 3):
            print(taglist[i])
            tagsearch(taglist[i], driver)
            pushnice(countlist[i], driver) 

    

こちらから簡単にコードの動作と解説を行います.
chorme driverを起動後,login関数でinstagramのサイトにアクセスし,入力したIDとパスワードでログインを行います.
chrome driverの実行には少し時間がかかるので,sleep関数で時間を開けています.

インスタ自動ログイン画面

次に,「ログイン情報の保存」と「お知らせをオンにする」のポップアップの後でボタンを押してログイン関数は終了になります

ポップアップ画面スキップ

pushnice関数では,


       target = driver.find_elements_by_class_name('_aa8k')[10] 

検索した投稿の最新投稿の画面に移動し,


       driver.find_elements_by_class_name('_aa8k')[9].click()

いいねを押し,


       driver.find_element_by_class_name('_aamw').click()

次の投稿に移動の矢印を押す というプロセスを繰り返します.
ループ内ではsleepあ関数の待ち時間をランダムにすることで,インスタ側からの機能制限を回避する狙いがあります.
また,何らかの失敗が発生しても自動プログラムが終了しないように例外処理で次の投稿に進むように設定しています.

みなさんも自動プログラムで,寝ている間にフォロワー獲得を目指してみませんか?

【備忘録】pythonからはてなブログを自動投稿(更新)する方法<実装編>

今回の記事は前回の続きで実装編です.
pythonコードを丸ごと載せているので,前回記事で取得した情報を使って一部書き換えることで動作することができます

segoro.hatenablog.com

変更するのはsetting部分のみで,usernameにの''の間に自分のユーザーネームを入力してください.
同様に,blognameにブログの名前,blogtitleにブログのタイトル,api_keyにブログのキーを入力してください.
こちらが実装のコードとなります.


from datetime import datetime
import requests as req
import hashlib
import random
import base64
import requests

# setting -----------------------------------------------------------                                
username = 'username'
blogname  = 'segoro.hatenablog.com'
blogtitle = '12345678901234'
api_key = '*******'
draft = 'no' # yes or no    

# time check --------------------------------------------------------
import datetime
now = datetime.datetime.now()
dtime = str(now.year)+"""-"""+str(now.month)+"""-"""+str(now.day)+"""T"""+str(now.hour)+""":"""+str(\
now.minute)+""":"""+str(now.second)

# function  ---------------------------------------------------------

def create_data(title,body):
    template = """<?xml version="1.0" encoding="utf-8"?>                                             
    <entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app">               
    <title>{0}</title>                                                                               
    <author><name>{1}</name></author>                                                                
    <content type="text/html">                                                                 
    {2}                                                                             
    </content>                                                                                       
    <updated>{3}</updated>                                                                           
    <category term="" />                                                                             
    <app:control>                                                                                    
    <app:draft>{4}</app:draft>                                                                       
    </app:control>                                                                                   
    </entry>                                                                                         
    """
     
    data = template.format(title, username, body, dtime, draft).encode()
    return data
    
    
def post_hatena(data):
    headers = {'X-WSSE': wsse(username, api_key)}
    url = 'http://blog.hatena.ne.jp/{0}/{1}/atom/entry'.format(username, blogname)
    r = requests.post(url, data=data, headers=headers)
    
    
def get_hatena(data):
    uri = 'https://ipocalender.hatenablog.com/entry/IPOsummary/2021/Nov'
    url = 'https://blog.hatena.ne.jp/{0}/{1}/atom/entry/{2}'.format(username, blogname, blogtitle)
    r = requests.get(uri, auth=(username, api_key))


def wsse(username, api_key):
    created = datetime.datetime.now().isoformat() + "Z"
    b_nonce = hashlib.sha1(str(random.random()).encode()).digest()
    b_digest = hashlib.sha1(b_nonce + created.encode() + api_key.encode()).digest()
    c = 'UsernameToken Username="{0}", PasswordDigest="{1}", Nonce="{2}", Created="{3}"'
    return c.format(username, base64.b64encode(b_digest).decode(), base64.b64encode(b_nonce).decode(\
    ), created)


if __name__ == '__main__':
    title = 'test'
    print(dtime)
    data = create_data(title, dtime)
    put_hatena(data)    

    

【備忘録】pythonからはてなブログを自動投稿(更新)する方法<準備編>

今回,解説する内容はpythonによるはてなブログ投稿です.
作成したコードを自動化する方法は,JenkinsとGoogleCloudPlatformの設定に関する過去記事を参考にしてください.

segoro.hatenablog.com

segoro.hatenablog.com

投稿用ブログページを作成する

仮のページで良いので,一度投稿用ページを作成してください.
今回の手法では同じページを自動更新する方法おを紹介します.
ブログの中からこれらの情報が必要になります.

    ・ブログのユーザネーム
    ・ブログの名前
    ・ブログのタイトル
    ・ブログのキー

これら4つの情報が自動取得する際に必要な情報です.
確認の仕方を含めて解説していきたいと思います.

・ユーザーネームの取得

設定->アカウント設定 を開いてください.
プロフィールのはてなIDがブログのユーザーネームに該当します.

ユーザーネームの取得
・ブログのキー

設定->アカウント設定 でキーの取得も一緒にしていまいましょう.
1番下までスクロールすると,APIキーという項目があるので全てコピーしてください.

・ブログ名の取得

設定->基本設定 のブログ全体の設定の先頭部分にブログURLが存在するのでこちらを確認してください.
必要な部分としてはhttps://から/までの間の文字列です.私のブログの場合,URLが https://segoro.hatenablog.com/ なので必要な情報は segoro.hatenablog.com になります.

ブログ名の取得
・ブログのタイトル

記事の管理 から仮に作成したページの作成画面に進みます.
この時のURLを取得してください. このようなURLになっていると思います. https://blog.hatena.ne.jp/ユーザーネーム/ブログ名/edit?entry=123456789012345123
最後のentry=以下の数値の部分がブログのタイトルになります.
ついでに先ほど調べたうユーザーネームとブログ名が正しいか確認してください

ここまでで,ユーザーネーム,ブログ名,ブログのタイトル,ブログのキーを取得することができました.
これで準備は完了になります.次の記事で実際に実行するpythonコードを取り扱います.

【備忘録】Google Cloud Platformを使用してpython定期実行する設定

前回,Jenkinsでpythonの自動実行を出来るように設定しました.

segoro.hatenablog.com

しかし,私の個人環境ではサーバPCなどないため,ノートPCを常に接続していないと定期実行出来ないためあまり恩恵を受けられませんでした.

(Jenkinsの勉強にはなったので良かったですが)

そのため,クラウドサーバ上の定期実行が無料で行うことが出来るGoogle Cloud Platform(GCP)を使用することにしました.

GCPで定期実行出来るようになるまでの設定の仕方と苦戦したポイントを備忘録として残しておきたいと思います.

  • GCPの設定で苦戦している人
  • プログラムの定期実行をしたい人
  • どのクラウドサーバプラットフォームを使えば良いか分からない人

プラットフォームの選択

まず,GCPを使用するに至った経緯について説明します.世の中にはクラウドサーバプラットフォームも複数種類あり,どれを使用すれば良いのか迷ってしまいます.

良く使用されているクラウドプラットフォームのGoogle Cloud Platform,Amazon Web Service, Herokuの3種類ピックアップして比較します.

Platform GCP AWS Heroku
無料枠 200万回/月の呼び出し
40万GB秒のコンピューティング時間
100万回/月の呼び出し
40万GB秒のコンピューティング時間
1000時間/月のコンピューティング時間
対応言語 Node.js、PythonRuby
Go、PHPJava
Java、Go、PowerShell
Node.js、C#PythonRuby
RubyJavaPHPPython
Node、Go、ScalaClojure

対応言語はHerokuが一番多いですね.個人的にはpythonが使えれば問題ないですが,対応言語が少ない場合は自然に使用するPFが決まってしまう可能性もありますね.

この中ではHeroku先駆者で,AWSGCPの方が後発なのですが,AmazonGoogleの資本に参入されてしまうと資本の力でシェアを取られてしまうことも多いので,AWSGCPから選ぶことにしました.

AWSGCPではGCPの方が無料枠が広く,AWSは少し複雑という記事を見たのでGCPを選択しました.

実際,アカウント開設から定期実行の設定完了まですぐ終える事ができました.

 

GCPの無料サーバー設定

ここから,GCPの設定について説明していきます.下記サイトを参考に設定を進めました.

GCP初期設定参考リンク

まず,任意の名前で新しいプロジェクトを作成します.

そして,左上のナビゲーションメニューからCompute Engine > VMインスタンスを選択します.

VMインスタンスの生成を押すと使用するサーバーの設定画面に移ります.正しく設定できていない場合,使用料を払うことになるので注意してください.

f:id:IPOgoro:20211204203032p:plain

f:id:IPOgoro:20211204203038p:plain

名前 : free

    (任意なので分かりやすい名前を設定してください)

リージョン : us-wesr1(オレゴン)

    (リージョンは低CO2のエコマークが付いている地域ならどこでもOKです)

ゾーン : us-west1-b

     (これもどこでも構いません)

シリーズ : E2

マシンタイプ : e2-micro

    (e2-microに変更必須です)

ブートディスク : 新しい標準永続ディスク30GB

    (デフォルトは10GBですが,30GBまで料金が変わらないので30GBに変更しましょう)

 

これらの設定をしても,月間予測の料金がいくらか発生することがありますが,マシンを回し続けた場合の料金なので安心してください.

こちら一日一回実行した時の実際の請求書なのですが,きちんと0円になっています.

f:id:IPOgoro:20211204203352p:plain

リンクの設定ではsshの設定をしていますが,今回は使用しないので設定しませんでした.ssh接続を使用する方は設定リンクを参考に設定してください.

 

GCP python定期実行プログラム

次に実際に定期実行を行うプログラム部分について説明します.

まず,定期実行のスケジューリング設定を行います.

ナビゲーションメニューからPub/Subを選択し,任意の名前を設定してください.

次に,ナビゲーションメニューからCloud Schedulerを選択します.

f:id:IPOgoro:20211204211309p:plain

タイムゾーンを日本に設定し,頻度をcron形式で入力します.

0 9 * * *では毎日9:00に実行される,

0 9 1 1 *では毎年1/1 9:00に実行される,

0 9 * * 1-51では月曜から金曜の9:00に実行されるといった形式になっています.

 

スケジュールジョブの定義が完了したら,実行内容を構成するを選択します.

f:id:IPOgoro:20211204213650p:plain

ターゲットタイプでPub/Subを選択し,トピック選択で先ほど作成したトピックを選んでください.

ここまでで定期実行の設定完了になり,実行するプログラムの中身を作成していきます.

 

スケジュール設定が完了したら,ナビゲーションメニューからCloud Functionを選択します.

関数の作成を選択し,トリガーでCloud Pub/Subを選択し,トピックで先ほど生成した名前を選択します.

次に関数の編集画面に移ります.

pythonの定期実行を例に説明しますが,使用する言語が違う場合は各自対応してください.

f:id:IPOgoro:20211204215857p:plain

デフォルトの関数を消して,main関数を作成しました.エントリポイントに設定している関数がmain関数になります.

また,注意点としてCloud Function上でエラーを発生させてしまった場合エラーコードは全て同じ以下のコードが出てしまいます.

$ Error: function terminated. Recommended action: inspect logs for termination reason. Additional troubleshooting documentation can be found at https://cloud.google.com/functions/docs/troubleshooting#logging Details: 500 Internal Server Error: The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

f:id:IPOgoro:20211204220449p:plain

そのため,一度自分のローカルで実行に成功したコードをコピーすることをお勧めします.

また,全コピーしてmainの引数なしにしてしまうとエラーになってしまうので気をつけてください.mainの引数は最初に作成されるデフォルトの関数を参考にしてください.

 

コードを書き終えたら,デプロイし,編集した関数を選択します.

テスト中のタブを選択し,[...]関数をテストするを選択してください.

f:id:IPOgoro:20211204221513p:plain

出力部分でOKと出れば,エラーなしで関数が実行されたことになります.

また,printなどはログ部分に出力されます.

これで,毎日9時にHello, worldと出力されるpython定期実行プログラムの完成です!