運用保守 〜作業自動化と効率化の実践〜

## この記事は？
運用保守は、作業の自動化と効率化が特に大事になってくる分野で、それによって日々の生産性が上下されると考えます。そこで、今回は著者が[@cosme](https://www.cosme.net/)の開発において実践してきた運用保守の内容をまとめてみました。

## 運用保守の基礎
日々運用保守ではどういった仕事を行なっているのでしょうか？

運用保守の作業ではルーティンワークが多くなりがちですが、障害が発生した時にはすぐに対応できるようにSlackにも通知を送るようにしています。

また、**品質を管理するために、自動化テストを推し進める**ようにします。そうでないと、毎回機能が入るたびに手動でのテストを行わなければならず効率が悪いからです。手動テストだけでテストを行うと、目視で確認できなかったバグをそのままリリースしてしまうことにもなりかねず、技術そのものをアップデートする際にも支障が出るため、テストを書く必要があります。

最後に、技術は使っていくごとに古くなり、EOLが切れた技術には脆弱性が含まれるリスクが高まるため、結果として、セキュリティリスクが増えます。**サービスを提供する事業者の責任として、古い技術への脆弱性対策を行うことが必要**です。
## ログとアラート
日々の開発において著者の環境では、SlackおよびインフラにはAWSとNewRelicを使っています。アプリケーションが出す標準出力のうちクリティカルなエラーはCloudWatchに溜まったログがSNSによってSlackに出力され、メンバーに通知が飛ぶような設定になっています。また、NewRelicによってCPUやメモリーなどを監視させておき、急激なパフォーマンス低下があった時にSlackにも通知を飛ばします。

(**例: エラーはチャンネルに通知されてメンバーが対応する**
![スクリーンショット 2024-10-04 14.47.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470696/04a7a9b7-651b-4827-0074-72f4b9e3a6a9.png)

## 日常作業の自動化
日々日常的に行なっている作業は徹底的に自動化していきます。例えば、毎週の定常リリースごとにバックログのチケットを発行するような作業は、以下のようにAPIを作っておき、定常実行させることができます。

**・backlog Create APIを呼び出すコードサンプル**
```py
import requests
from datetime import datetime, timedelta

today = datetime.today()
future_date = today + timedelta(days=7)
future_date_str = future_date.strftime('%Y/%m/%d')
title = f'{future_date_str} リリース依頼'

url = 'https://xyz-inc.backlog.com/api/v2/issues?apiKey=xxxx'
data = {
        'projectId': 'xxxx',
        'summary': title,
	'description': '以下の資源についてリリースを行う。',
        'issueTypeId': 'xxxx',
        'priorityId': '3'
 }   
headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post(url, data = data, headers = headers)
print(r.status_code)
print(r.json())
```

作成が終わったら、チャンネルにその旨が通知される。私のチームでは、毎週火曜15時にある定例に合わせて自動でチケット作成を行っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470696/8504194b-2223-87e7-b630-52ada91df7b8.png)


## テストの自動化
コードの品質を担保するために、プロダクト開発においてはユニットテストを書くことを徹底します。マージ条件として、テストが通ったPRのみをマージできるようにして、危険な変更を含む変更をブロックすることができます。

![スクリーンショット 2024-10-04 15.11.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470696/382db610-e8b9-59ec-80e9-d0fd6ff14cb0.png)

なお、テストに関しては、著者の前記事もご覧ください。

https://qiita.com/muratak-dev/items/a89968fdab43f7d2589a

## 脆弱性への対応
日々技術を使っていると古くなり、特にEOLを迎えた技術には脆弱性が含まれるリスクが可能性があるため、定期的にアップデートを行う必要があります。特に脆弱性のあるライブラリーのアップデートには、dependabotを使って依存関係を洗い出し、PRとして上がってきたものを日々潰して対応を行っています。

・**dependabotが出したPRは自動アサインされ各自が対応する** 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470696/b0d78334-31d2-13b2-c07b-d01dd24ff054.png)

・以下、著者のプロジェクトでの自動アサイン実装例。本筋とは逸れるので解説は省略するが、SlackBotに通知まで行っている。

```yml
name: Dependabot auto-assign and notify

on:
  pull_request_target:
    types: [opened]

permissions:
  pull-requests: write
  contents: write
  
jobs:
  dependabot:
    runs-on: ubuntu-latest
    if: ${{ github.actor == 'dependabot[bot]' }}
    timeout-minutes: 5
    steps:
      - name: Assign one member from List
        id: set_assign
        uses: actions/github-script@v6
        with:
          script: |
            const names = [
              // MEMO: change members accordingly
              'muratak-dev',
              ...otherMenbers
            ];
            const index = Math.floor(Math.random() * names.length);
            const assignee = names[index];
            github.rest.issues.addAssignees({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              assignees: [assignee]
            });
            core.setOutput('assignee_member', assignee);
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        if: ${{ toJSON(github.event.pull_request.assignees) == '[]' }}

      - name: Send custom JSON data to Slack
        if: ${{ success() && github.actor == 'dependabot[bot]' }}
        uses: slackapi/slack-github-action@v1.25.0
        with:
          payload: |
            {
              "text": "${{ github.event.pull_request.title }}\n${{ github.event.pull_request.html_url }}\n@${{ steps.set_assign.outputs.assignee_member }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFICATION_WEBHOOK_URL }}

```


なお、著者が担当しているバックエンドのプロジェクトではNode.jsを使っていますが、現在最新の22系で動くようになっています。**1年に1,2回の頻度でメジャーバージョンのアップデートを行う**ことで、日々技術を最新に保っています。

## 終わりに
運用保守は、単に運用するだけでなく作業の自動化と効率化、テスト、技術を日々刷新するところまで含まれています。これらのプラクティスを実践し、より良い運用保守になることを願っています。