<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>SW</title>
    <link>https://limsw.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 15 Jul 2026 01:51:17 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>limsw</managingEditor>
    <item>
      <title>[Git] insufficient permission for adding an object to repository database .git/objects - 에러 해결 방법</title>
      <link>https://limsw.tistory.com/149</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 Git 커밋 시 발생하는 아래 에러에 대한 해결 방법을 알아본다.&lt;/p&gt;
&lt;pre id=&quot;code_1742365142396&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;error: insufficient permission for adding an object to repository database .git/objects&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;본론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 에러는 Git 저장소의 파일 시스템 권한 부족 문제로 발생한 에러이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 리눅스 환경에서 사용자 계정의 권한이 부족하거나, .git/objects 디렉토리에 대한 접근 권한이 제한되어 있을 때 발생한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결 방안&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 문제&amp;nbsp;확인:&amp;nbsp;.git/objects&amp;nbsp;디렉토리&amp;nbsp;권한&amp;nbsp;확인&lt;/b&gt; &lt;/p&gt;
&lt;pre id=&quot;code_1742365312678&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ cd /path/to/your/git/repository/.git/objects
$ ls -al&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 출력 결과 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소유자 및 그룹이 root로 설정되어 있거나, 일반 사용자의 쓰기 권한이 없는 디렉토리가 있는지 확인&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시: &lt;span style=&quot;color: #0593d3;&quot;&gt;drwxr-xr-x 2 root root 4096 Mar 13 13:48 da/&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 권한 변경 : 사용자에게&amp;nbsp;소유권&amp;nbsp;및&amp;nbsp;쓰기&amp;nbsp;권한&amp;nbsp;부여&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1742365491848&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo chown -R 사용자명:사용자명 .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자명은 현재 사용자의 계정 이름으로 변경한다. (예 chown -R gildong:gildong .)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령은 .git/objects 하위 모든 디렉토리에 대해서 소유자 및 그룹 권한을 변경하는 것이며, 특정 디렉토리의 권한을 변경하고자 하는 경우 아래와 같이 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1742365720486&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo chmod -R gildong:gildong da # da는 특정 디렉토리 이름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Util</category>
      <category>GIT</category>
      <category>github</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/149</guid>
      <comments>https://limsw.tistory.com/149#entry149comment</comments>
      <pubDate>Wed, 19 Mar 2025 15:31:22 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] 리눅스(CentOS/RHEL) 안전 모드 실행 방법 - Rescue Mode</title>
      <link>https://limsw.tistory.com/148</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 레드햇 부팅 시 안전모드(Rescue Mode)로 부팅하는 방법에 대해서 설명한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CentOS 환경에서도 동일하게 사용이 가능하나, 이번 포스팅은 레드햇 운영체제를 기준으로 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Rescue Mode&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Rescue Mode는 리눅스 시스템이 제대로 부팅되지 않거나, 주요 서비스가 실패했을 때 문제를 진단하고 해결하기 위한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;b&gt;제한된 환경&lt;/b&gt;&lt;/u&gt;을 제공한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;시스템이 최소한의 리소스와 서비스를 사용하는 상태로 부팅되며, 일반적으로 루트 파일 시스템만 마운트 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Rescue Mode 사용 필요성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때때로 우리는 리눅스 시스템을 사용하다가 디스크 용량 부족을 경험하기도 하고, 파일 시스템을 잘못 건드려서 파일 시스템 자체가 손상되는 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 현상들은 시스템 부팅의 실패를 초래할 수 있으며, 이는 리눅스 시스템을 정상적으로 사용하기 어렵게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령, 가상 환경에서 CentOS, RHEL 운영체제를 운영하는 경우에는 동적으로 디스크 용량을 늘릴 수 있지만, 특정 하드웨어 장비에 서버가 탑재되어 운영되는 경우에는 CD, USB 등이 있지 않으면 동적으로 디스크 용량을 늘리거나 확보하기가 어려우며, 특히&amp;nbsp;폐쇄망 환경에서는 더욱이 디스크 용량을 늘리기가 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이러한 환경에서 흔히 안전 모드(?) 라고 불리는 Rescue Mode를 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rescue Mode 사용이 필요한 시점을 추가적으로 정리해보면 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt; 손상된 파일 시스템 복구&lt;/b&gt;:&amp;nbsp; 루트 파일 시스템이나 기타 마운트된 파일 시스템이 손상&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 부트 로더 문제&lt;/b&gt;: 부트 로더가 손상되어 시스템 부팅이 실패할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 루트 암호 복구&lt;/b&gt;: 루트 비밀번호를 분실했을 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 커널 문제&lt;/b&gt;: 커널 모듈 충돌이나 업그레이드 후 시스템이 부팅되지 않을 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 중요 시스템 파일 수정&lt;/b&gt;: 중요한 설정 파일이 잘못되어 정상적인 부팅이 불가능할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 하드웨어 문제&lt;/b&gt;: 디스크 드라이브, RAID, 또는 기타 하드웨어가 실패했을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Rescue Mode 활성화 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 최초 시스템을 부팅하면 아래와 같이 이미지를 선택하는 화면이 나오는데 해당 화면에서 알파벳 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;e&lt;/b&gt;&lt;/span&gt; 버튼을 누른다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일정 시간이 지나면 자동적으로 이미지 선택 후 부팅 화면으로 넘어가기 때문에, 타이밍을 잘 맞추어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1owVy/btsLoa91K2E/aaMgosQHIKkRN62yl5pnH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1owVy/btsLoa91K2E/aaMgosQHIKkRN62yl5pnH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1owVy/btsLoa91K2E/aaMgosQHIKkRN62yl5pnH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1owVy%2FbtsLoa91K2E%2FaaMgosQHIKkRN62yl5pnH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;383&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;알파벳&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;e&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 버튼을 누르면 아래와 같은 화면이 나온다. 우리는 여기서 linux ($root) ..{이하생략}.. 부분의 맨 끝에 명령어를 추가할 예정이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfw8RN/btsLmTaoZRU/ZK95bp9vRv8xdARS6ehCdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfw8RN/btsLmTaoZRU/ZK95bp9vRv8xdARS6ehCdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfw8RN/btsLmTaoZRU/ZK95bp9vRv8xdARS6ehCdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdfw8RN%2FbtsLmTaoZRU%2FZK95bp9vRv8xdARS6ehCdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;394&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;systemd.unit=rescue.target&lt;/b&gt; 명령어를 추가한다. 그 다음으로는, &lt;b&gt;Ctrl + X&lt;/b&gt;를 함께 누른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;33.png&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfJylS/btsLn8RUytI/5yC8QkhKDcHdyreVU3pQhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfJylS/btsLn8RUytI/5yC8QkhKDcHdyreVU3pQhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfJylS/btsLn8RUytI/5yC8QkhKDcHdyreVU3pQhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfJylS%2FbtsLn8RUytI%2F5yC8QkhKDcHdyreVU3pQhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;394&quot; data-filename=&quot;33.png&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ctrl + X&lt;/b&gt;를 누르면 아래 사진과 같이 루트 패스워드를 입력하는 화면이 나온다. 루트 패스워드를 입력해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;44.png&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k17Vo/btsLnY9QsdD/z7mKE3Pn7QZ986Fk6r1Ask/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k17Vo/btsLnY9QsdD/z7mKE3Pn7QZ986Fk6r1Ask/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k17Vo/btsLnY9QsdD/z7mKE3Pn7QZ986Fk6r1Ask/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk17Vo%2FbtsLnY9QsdD%2Fz7mKE3Pn7QZ986Fk6r1Ask%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;399&quot; data-filename=&quot;44.png&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 패스워드를 입력하고 나면 아래와 같이 CLI 환경이 활성화된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림은 systemctl list-units --type=target 명령어를 입력하여 rescue.target이 활성화 되어 있는지 확인하는 그림이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태가 active로 출력되면 정상적으로 Rescue Mode 접근에 성공한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;55.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHOF72/btsLmnC1tHt/aP6QKcQdZBFcSgqTzxfm5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHOF72/btsLmnC1tHt/aP6QKcQdZBFcSgqTzxfm5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHOF72/btsLmnC1tHt/aP6QKcQdZBFcSgqTzxfm5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHOF72%2FbtsLmnC1tHt%2FaP6QKcQdZBFcSgqTzxfm5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;397&quot; data-filename=&quot;55.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Linux</category>
      <category>CentOS</category>
      <category>linux</category>
      <category>redhat</category>
      <category>Rescue</category>
      <category>RHEL</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/148</guid>
      <comments>https://limsw.tistory.com/148#entry148comment</comments>
      <pubDate>Wed, 18 Dec 2024 16:18:41 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] 레드햇(RedHat) Local Repository 설정하기</title>
      <link>https://limsw.tistory.com/147</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 레드햇 운영체제에서 Local Repository를 설정하는 방법을 알아본다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테스트 환경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 환경은 가상머신(Virtual Box)에서 진행했으며, 레드햇 버전은 8.4 버전을 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;rhel-8.4-x86_64-dvd.iso&amp;nbsp;&lt;/b&gt;파일을 레드햇 공식 홈페이지에서 다운로드 받아 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 다운로드 받은 rhel-8.4-x86_64-dvd.iso 파일을 서버에 업로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일은 /root 경로에 업로드&lt;/li&gt;
&lt;li&gt;이후 아래 명령어를 수행할 때에도 root 권한을 얻은 뒤 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는, 아래 명령어를 차례대로 입력해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1721724063162&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ mount -t iso9660 -o loop /root/rhel-8.5-x86_64-dvd.iso /media
$ mkdir /repository
$ cp -a /media/* /repository/
$ vi /etc/yum.repos.d/RHEL8.repo&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RHEL8.repo 파일 안에는 아래 내용으로 채워준다.&lt;/p&gt;
&lt;pre id=&quot;code_1721724112554&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[InstallMedia-BaseOS]
name=RHEL 8 - BaseOS
metadata_expire=-1
gpgcheck=1
enabled=1
baseurl=file:///repository/BaseOS
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

[InstallMedia-AppStream]
name=RHEL 8 - AppStream
metadata_expire=-1
gpgcheck=1
enabled=1
baseurl=file:///repository/AppStream
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wq! 입력으로 저장한 뒤 파일을 빠져나오고, 차례대로 아래 명령어를 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1721724159440&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ dnf clean all
$ subscription-manager clean
$ yum grouplist&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝.&lt;/p&gt;</description>
      <category>Linux</category>
      <category>linux</category>
      <category>local Repository</category>
      <category>redhat</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/147</guid>
      <comments>https://limsw.tistory.com/147#entry147comment</comments>
      <pubDate>Tue, 23 Jul 2024 17:44:23 +0900</pubDate>
    </item>
    <item>
      <title>크롬 에러 해결 - ERR_SSL_KEY_USAGE_INCOMPATIBLE</title>
      <link>https://limsw.tistory.com/146</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;Trouble Shooting : ERR_SSL_KEY_USAGE_INCOMPATIBLE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 OpenSSL을 통해 HTTPS 리액트 환경을 구축하는 방법을 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://limsw.tistory.com/145&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[React] OpenSSL을 이용한 HTTPS 환경 구축하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1712882937602&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[React] OpenSSL을 이용한 HTTPS 환경 구축하기&quot; data-og-description=&quot;서론 이번 글에서는 오픈소스 OpenSSL 패키지를 이용하여 자체 인증서를 생성하고 해당 인증서를 리액트에 적용하여 HTTPS 환경으로 클라이언트를 실행하는 방법을 알아보고자 한다. OpenSSL 이란? Op&quot; data-og-host=&quot;limsw.tistory.com&quot; data-og-source-url=&quot;https://limsw.tistory.com/145&quot; data-og-url=&quot;https://limsw.tistory.com/145&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1lxMt/hyVPOaoT4z/cobtL5fRxuYm6kNYM1SJpK/img.jpg?width=150&amp;amp;height=150&amp;amp;face=0_0_150_150,https://scrap.kakaocdn.net/dn/dg0IVN/hyVPNJkJ7x/HKKN5nS3OUAXyCl3OzoeCk/img.jpg?width=150&amp;amp;height=150&amp;amp;face=0_0_150_150,https://scrap.kakaocdn.net/dn/byNoBp/hyVPJNHNew/JcYVpNKfuTfvysdIaMythK/img.png?width=543&amp;amp;height=830&amp;amp;face=0_0_543_830&quot;&gt;&lt;a href=&quot;https://limsw.tistory.com/145&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://limsw.tistory.com/145&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1lxMt/hyVPOaoT4z/cobtL5fRxuYm6kNYM1SJpK/img.jpg?width=150&amp;amp;height=150&amp;amp;face=0_0_150_150,https://scrap.kakaocdn.net/dn/dg0IVN/hyVPNJkJ7x/HKKN5nS3OUAXyCl3OzoeCk/img.jpg?width=150&amp;amp;height=150&amp;amp;face=0_0_150_150,https://scrap.kakaocdn.net/dn/byNoBp/hyVPJNHNew/JcYVpNKfuTfvysdIaMythK/img.png?width=543&amp;amp;height=830&amp;amp;face=0_0_543_830');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[React] OpenSSL을 이용한 HTTPS 환경 구축하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;서론 이번 글에서는 오픈소스 OpenSSL 패키지를 이용하여 자체 인증서를 생성하고 해당 인증서를 리액트에 적용하여 HTTPS 환경으로 클라이언트를 실행하는 방법을 알아보고자 한다. OpenSSL 이란? Op&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;limsw.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위 절차대로 하면, 로컬호스트 서버에서는 HTTPS 경로로 잘 접속이 되지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트북, 데스크탑PC 등 원격에서 브라우저로 접속을 시도하면 다음과 같은 에러가 발생했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FCPu4/btsGzkJVUTM/OK2Bmlk94TAlfXqaYnmno0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FCPu4/btsGzkJVUTM/OK2Bmlk94TAlfXqaYnmno0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FCPu4/btsGzkJVUTM/OK2Bmlk94TAlfXqaYnmno0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFCPu4%2FbtsGzkJVUTM%2FOK2Bmlk94TAlfXqaYnmno0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;326&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenSSL 인증서를 생성할 때 keyUsage 부분을 다음과 같이 수정하여 에러를 해결했다.&lt;/p&gt;
&lt;pre id=&quot;code_1712883136382&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# build.sh

# keyUsage = keyCertSign, cRLSign
keyUsage = nonRepudiation, digitalSignature, keyEncipherment&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Network</category>
      <category>Chrome</category>
      <category>OpenSSL</category>
      <category>React</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/146</guid>
      <comments>https://limsw.tistory.com/146#entry146comment</comments>
      <pubDate>Fri, 12 Apr 2024 09:54:58 +0900</pubDate>
    </item>
    <item>
      <title>[React] OpenSSL을 이용한 HTTPS 환경 구축하기</title>
      <link>https://limsw.tistory.com/145</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 오픈소스 OpenSSL 패키지를 이용하여 자체 인증서를 생성하고 해당 인증서를 리액트에 적용하여 HTTPS 환경으로 클라이언트를 실행하는 방법을 알아보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;OpenSSL 이란?&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OpenSSL은 오픈 소스로 개발된 암호 및 보안 라이브러리이다. OpenSSL은 다양한 암호화 알고리즘과 프로토콜을 구현하여 네트워크 통신 간 데이터를 보호하는데 사용된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 내용은 OpenSSL 에서 제공하는 기능 목록이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;암호화 및 복호화&lt;/b&gt;&lt;/span&gt;: OpenSSL은 대표적인 대칭 및 비대칭 암호화 알고리즘을 제공하여, 데이터를 암(복)호화할수 있다. 대표적으로는 &lt;b&gt;AES, RSA, DES, Triple DES&lt;/b&gt; 등의 알고리즘을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;디지털 서명&lt;/b&gt;&lt;/span&gt;: OpenSSL은 디지털 서명을 생성하고 검증하는 기능을 제공하여, 메시지의 무결성을 보호한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;SSL/TLS 프로토콜 구현&lt;/b&gt;&lt;/span&gt;: OpenSSL은 &lt;b&gt;SSL&lt;/b&gt; (Secure Sockets Layer) 및 &lt;b&gt;TLS&lt;/b&gt; (Transport Layer Security) 프로토콜을 제공하여 네트워크 통신 보안성을 향상시킨다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;서버 및 클라이언트 인증&lt;/b&gt;&lt;/span&gt;: OpenSSL은 &lt;b&gt;X509 인증서&lt;/b&gt;를 사용하여 서버 및 클라이언트 간 상호 인증을 지원한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;암호 해싱&lt;/b&gt;&lt;/span&gt;: OpenSSL은 다양한 해싱 알고리즘을 지원하여 데이터의 무결성을 보장한다. &lt;br /&gt;대표적으로 &lt;b&gt;SHA-1, SHA-256, SHA-512&lt;/b&gt; 등의 알고리즘이 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;본론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 자체 인증서를 생성하기 위한 스크립트를 만들어보자.&lt;/p&gt;
&lt;pre id=&quot;code_1712881362451&quot; class=&quot;shell&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;#!/bin/bash

# Create RSA Key
openssl genrsa -aes256 -out rootca.key 2048

chmod 600 rootca.key

# Creating a Configuration File to Use for CSR Files
cat &amp;gt; rootca.conf &amp;lt;&amp;lt;EOL
[ req ]
default_bits            = 2048
default_md              = sha1
default_keyfile         = rootca.key
distinguished_name      = req_distinguished_name
extensions              = v3_ca
req_extensions = v3_ca

[ v3_ca ]
basicConstraints       = critical, CA:TRUE, pathlen:0
subjectKeyIdentifier   = hash
keyUsage               = keyCertSign, cRLSign
nsCertType             = sslCA, emailCA, objCA

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = KR
countryName_min                 = 2
countryName_max                 = 2

organizationName                = Organization Name (eg, company)
organizationName_default        = TEST

organizationalUnitName          = Organization Name (eg, company)
organizationalUnitName_default  = TEST

# SSL domain
commonName                     = Common Name (eg, your name or your server's hostname)
commonName_default             = Self Signed CA
commonName_max                 = 64
EOL

# Create CSR file
openssl req -new -key rootca.key -out rootca.csr -config rootca.conf

# Craete self signed certification
openssl x509 -req -days 365 -extensions v3_ca -set_serial 1 -in rootca.csr -signkey rootca.key -out rootca.crt -extfile rootca.conf

# Create Root CA certification
openssl x509 -text -in rootca.crt

# Delete Configure files
rm -rf rootca.conf

# replace key, crt to pem files
openssl rsa -in rootca.key -text &amp;gt; rootca-key.pem
openssl x509 -inform PEM -in rootca.crt &amp;gt; rootca-crt.pem&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 내용을 담고있는 &lt;u&gt;&lt;b&gt;build.sh&lt;/b&gt;&lt;/u&gt; 파일을 생성해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 &lt;u&gt;&lt;b&gt;chmod 777 build.sh&lt;/b&gt;&lt;/u&gt; 명령어를 통해 쉘스크립트 파일을 실행 가능하도록 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘스크립트 파일을 실행하여 rootCA 인증서를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1712881740004&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./build.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMln6g/btsGw2wX4ne/a8oA9pNtSPWZG7YMWpFAu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMln6g/btsGw2wX4ne/a8oA9pNtSPWZG7YMWpFAu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMln6g/btsGw2wX4ne/a8oA9pNtSPWZG7YMWpFAu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMln6g%2FbtsGw2wX4ne%2Fa8oA9pNtSPWZG7YMWpFAu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;818&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PBum4/btsGw06Xr84/o0QVXPJCINeoyMMIKjRyO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PBum4/btsGw06Xr84/o0QVXPJCINeoyMMIKjRyO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PBum4/btsGw06Xr84/o0QVXPJCINeoyMMIKjRyO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPBum4%2FbtsGw06Xr84%2Fo0QVXPJCINeoyMMIKjRyO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;570&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;pass phrase&lt;/b&gt;&lt;/span&gt; 문구가 나오는 부분은 인증서를 생성하기 위해 사용자가 사용할 비밀번호를 입력한다.&lt;/li&gt;
&lt;li&gt;Country Name, Organization Name, Common Name은 사용자 소요에 맞게 작성한다. (스크립트 내 Default 설정 되어있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 생성이 완료되면 총 5개의 파일이 만들어질 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rootca.crt / rootca-crt.pem / rootca.csr / rootca.key / rootca-key.pem&lt;/li&gt;
&lt;li&gt;rootca-crt.pem 및 rootca-key.pem 파일은 rootca.crt 및 rootca.key 파일을 기반으로 생성된 파일이므로 인증서 내 내용은 동일하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;리액트에 인증서 적용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json 파일을 열어 아래 내용을 scripts 부분에 추가한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고로 2개의 pem 파일은 package.json 파일이 존재하는 경로에서 cert 라는 디렉토리를 만들고, 해당 디렉토리 안에 위치시켰다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1712882283445&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
    &quot;start:windows&quot;: &quot;HTTPS=true SSL_CRT_FILE=./cert/rootca-crt.pem SSL_KEY_FILE=./cert/rootca-key.pem react-scripts start&quot;,
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어를 통해 리액트 클라이언트를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1712882392908&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$  npm run start:windwos&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실행 결과&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFo6j5/btsGz1C7EPP/OPSTVgojuj8dnfi9seiKt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFo6j5/btsGz1C7EPP/OPSTVgojuj8dnfi9seiKt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFo6j5/btsGz1C7EPP/OPSTVgojuj8dnfi9seiKt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFo6j5%2FbtsGz1C7EPP%2FOPSTVgojuj8dnfi9seiKt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;171&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 클라이언트가 정상적으로 실행되면, HTTPS 경로를 통해 접속이 가능하다는 메세지를 확인할 수있다.&lt;/p&gt;</description>
      <category>React</category>
      <category>https</category>
      <category>Network</category>
      <category>OpenSSL</category>
      <category>React</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/145</guid>
      <comments>https://limsw.tistory.com/145#entry145comment</comments>
      <pubDate>Fri, 12 Apr 2024 09:44:20 +0900</pubDate>
    </item>
    <item>
      <title>[React] vis-network를 이용한 네트워크 토폴로지 그리기</title>
      <link>https://limsw.tistory.com/144</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 오픈소스 라이브러리인 vis-network 라이브러리를 이용하여 네트워크 토폴로지는 그리는 방법에 대해서 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vis-network 라이브러리를 이용하면 각 데이터를 표현하는 &lt;u&gt;&lt;b&gt;노드(Node)&lt;/b&gt;&lt;/u&gt;와 노드 간 연결정보를 표현하는 &lt;u&gt;&lt;b&gt;엣지(Edge)&lt;/b&gt;&lt;/u&gt;를 화면상에 시각화 할 수 있으며, &lt;b&gt;모양/스타일/색상/크기 등&lt;/b&gt;을 자유롭게 커스터마이징 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 라이브러리에 대한 자세한 내용은 아래 공식 문서를 참고하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://visjs.github.io/vis-network/docs/network/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://visjs.github.io/vis-network/docs/network/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1712277312302&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;vis.js - Network documentation.&quot; data-og-description=&quot;Returns: Array Nodes can be in clusters. Clusters can also be in clusters. This function returns an array of nodeIds showing where the node is. If any nodeId in the chain, especially the first passed in as a parameter, is not present in the current nodes l&quot; data-og-host=&quot;visjs.github.io&quot; data-og-source-url=&quot;https://visjs.github.io/vis-network/docs/network/&quot; data-og-url=&quot;https://visjs.github.io/vis-network/docs/network/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://visjs.github.io/vis-network/docs/network/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://visjs.github.io/vis-network/docs/network/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;vis.js - Network documentation.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Returns: Array Nodes can be in clusters. Clusters can also be in clusters. This function returns an array of nodeIds showing where the node is. If any nodeId in the chain, especially the first passed in as a parameter, is not present in the current nodes l&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;visjs.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;본론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 라이브러리를 사용하기 위해 패키지를 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1712277356138&quot; class=&quot;sql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;npm install vis-network --save&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 설치가 완료되면 다음과 같이 예제 코드를 작성해보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;App.js&lt;/h4&gt;
&lt;pre id=&quot;code_1712277470380&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, {useEffect, useRef} from 'react';
import { Network } from &quot;vis-network&quot;;

function App() {
  
  const container = useRef(null);
  
  const nodes = [
    {id: 1, label: 'South Korea', size: 20, shape: 'square'},
    {id: 2, label: 'Seoul'},
    {id: 3, label: 'Jeju'},
    {id: 4, label: 'Busan'},
    {id: 5, label: 'Incheon'},
  ]
  const edges = [
    { from: 2, to: 1, label: 'label1'},
    { from: 3, to: 1, label: 'label2'},
    { from: 4, to: 1, label: 'label3'},
    { from: 5, to: 1, label: 'label4'},
  ];
  
  // network topology options.
  const options = {
    nodes: {
      shape: &quot;dot&quot;,
      size: 12,
      shadow: true,
      color: {
        border: 'white',
        background: 'skyblue'
      },
      font: {
        color: '#000'
      }
    },
    edges: {
      color: 'gray'
    },
    interaction: {
      hover: true
    }
  };

  // create topology using edges, nodes, options
  useEffect(() =&amp;gt; {
    const network = container.current &amp;amp;&amp;amp; new Network(container.current, { nodes, edges }, options);

    // Use `network` here to configure events, etc
    network.on(&quot;doubleClick&quot;, (event) =&amp;gt; {
      const { nodes: clickedNodes } = event;
      alert(`id ${clickedNodes} node is clicked.`);
      
    });
  }, [container, nodes, edges]);
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div ref={container} style={{ height: &quot;500px&quot;, width: &quot;100%&quot; }} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드의 값으로는 &lt;u&gt;&lt;b&gt;노드ID(id), 노드명(label)&lt;/b&gt;&lt;/u&gt; 등을 포함한 다양한 옵션이 들어갈 수 있다.&lt;/li&gt;
&lt;li&gt;엣지의 값으로는 &lt;u&gt;&lt;b&gt;출발노드(from), 도착노드(to)&lt;/b&gt;&lt;/u&gt;를 명시하여 연결 관계를 정의할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실행한 결과는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/07CtM/btsGo6YJkFE/1ZKv4UP6KtdOrWlNL2Bk3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/07CtM/btsGo6YJkFE/1ZKv4UP6KtdOrWlNL2Bk3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/07CtM/btsGo6YJkFE/1ZKv4UP6KtdOrWlNL2Bk3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F07CtM%2FbtsGo6YJkFE%2F1ZKv4UP6KtdOrWlNL2Bk3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;376&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 options 변수에서 노드의 스타일을 점(dot)으로 정의했지만, nodes 변수에 정의된 스타일이 우선순위가 높아 South Korea 노드의 경우에는 사각형(square)으로 나타나는 것을 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션에 &lt;b&gt;interaction: { hover: true }&lt;/b&gt; 를 넣어 노드에 hovering 했을 때 해당 노드 및 연결된 엣지가 굵어지는 것을 확인할 수 있다. 이외에도 토폴로지에 사용할 수 있는 옵션은 매우 다양하여 공식 문서를 참고하길 바란다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhiymM/btsGnDb7YuB/jZ2iUcxq0flb4zBmsXYBy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhiymM/btsGnDb7YuB/jZ2iUcxq0flb4zBmsXYBy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhiymM/btsGnDb7YuB/jZ2iUcxq0flb4zBmsXYBy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhiymM%2FbtsGnDb7YuB%2FjZ2iUcxq0flb4zBmsXYBy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;416&quot; height=&quot;359&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, network 객체에 &lt;b&gt;doubleClick 이벤트&lt;/b&gt;를 추가했으며 노드를 더블클릭 했을 때 나오는 결과는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2be09/btsGobe8d32/Yd83X0qPkknxmGWICSlskK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2be09/btsGobe8d32/Yd83X0qPkknxmGWICSlskK/img.png&quot; data-alt=&quot;해당 노드의 ID 값을 Alert 메세지로 출력한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2be09/btsGobe8d32/Yd83X0qPkknxmGWICSlskK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2be09%2FbtsGobe8d32%2FYd83X0qPkknxmGWICSlskK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;151&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;151&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해당 노드의 ID 값을 Alert 메세지로 출력한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>React</category>
      <category>Network</category>
      <category>React</category>
      <category>topology</category>
      <category>vis-network</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/144</guid>
      <comments>https://limsw.tistory.com/144#entry144comment</comments>
      <pubDate>Fri, 5 Apr 2024 09:52:20 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 도커를 이용하여 NTP(Chrony) 서버 구축하기 - Linux</title>
      <link>https://limsw.tistory.com/143</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 서버에서 직접적인 NTP(Chrony) 서비스의 설치 없이 도커를 이용하여 NTP 서버를 구축하는 방법을 알아본다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;NTP(Network Time Protocol)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NTP는 시간 동기화 프로토콜로 서버 시간을 특정 표준 시간대로 동기화 하는 기능을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 서버에서는 NTP 또는 Chrony를 주로 사용하고 있으며, 둘 중 아무거나 사용해도 되지만 Chrony가 NTP의 단점을 보완하여 만들어진 서비스이므로 &lt;b&gt;Chrony를 많이 사용하는 추세&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 글에서는 NTP 개념을 알고 있다는 가정하에 작성하는 글이므로, 디테일한 개념 설명은 넘어간다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;본론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지는 도커 허브(Docker hub)에 있는 &lt;b&gt;cturra/ntp&lt;/b&gt; 이미지를 이용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/cturra/ntp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hub.docker.com/r/cturra/ntp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1709016991520&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Docker&quot; data-og-description=&quot;&quot; data-og-host=&quot;hub.docker.com&quot; data-og-source-url=&quot;https://hub.docker.com/r/cturra/ntp&quot; data-og-url=&quot;https://hub.docker.com/r/cturra/ntp&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://hub.docker.com/r/cturra/ntp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hub.docker.com/r/cturra/ntp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hub.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 해당 이미지를 가져오고 도커 컨테이너를 실행하는 &lt;b&gt;docker-compose.yml&lt;/b&gt; 파일을 작성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1709017083465&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3'

services:
  ntp:
    image: cturra/ntp:latest
    container_name: ntp
    restart: always
    ports:
      - 123:123/udp
    environment:
      - NTP_SERVERS=&quot;time.cloudflare.com&quot;
      - LOG_LEVEL=0
      - TZ=Asia/Seoul&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NTP_SERVERS를 &lt;b&gt;특정 표준시&lt;/b&gt;로 설정하면 해당 서비스는 &lt;b&gt;NTP Client의 역할&lt;/b&gt;을 한다.&lt;br /&gt;위처럼 구성하면 도커 서비스는 time.cloudfare.com 도메인의 시간대로 동기화를 하는 것이다.&lt;/li&gt;
&lt;li&gt;NTP_SERVERS를 &lt;b&gt;127.127.1.0&lt;/b&gt;으로 설정하면 해당 서비스는 &lt;b&gt;NTP Server의 역할&lt;/b&gt;을 한다.&lt;br /&gt;다시 말하면, 해당 서버는 로컬 시간을 유지하며 타 NTP 클라이언트에게 시간을 공유할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성한 도커 컴포즈 파일을 실행하기 위해 다음 명령어를 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1709017403602&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// -d 옵션은 백그라운드 실행 옵션이다.
docker-compose up -d ntp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 출력이 나오면 성공적으로 도커 컨테이너를 이용한 NTP 서비스가 활성화 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k9nVu/btsFkJEz4RA/qbozbTBd2p8WbOv1GDa8nK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k9nVu/btsFkJEz4RA/qbozbTBd2p8WbOv1GDa8nK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k9nVu/btsFkJEz4RA/qbozbTBd2p8WbOv1GDa8nK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk9nVu%2FbtsFkJEz4RA%2FqbozbTBd2p8WbOv1GDa8nK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;978&quot; height=&quot;226&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정상 동작 확인하기&lt;/h4&gt;
&lt;pre id=&quot;code_1709017717826&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker exec ntp chronyc sources&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mMz4u/btsFnXojz8Q/OKFNw7PWY2w58Z7ZlG2tCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mMz4u/btsFnXojz8Q/OKFNw7PWY2w58Z7ZlG2tCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mMz4u/btsFnXojz8Q/OKFNw7PWY2w58Z7ZlG2tCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmMz4u%2FbtsFnXojz8Q%2FOKFNw7PWY2w58Z7ZlG2tCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;966&quot; height=&quot;81&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1709017721997&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;$ docker exec ntp chronyc tracking&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/88Ont/btsFlU6WZ7B/qJjj3IUm0Muj89KWbWISGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/88Ont/btsFlU6WZ7B/qJjj3IUm0Muj89KWbWISGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/88Ont/btsFlU6WZ7B/qJjj3IUm0Muj89KWbWISGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F88Ont%2FbtsFlU6WZ7B%2FqJjj3IUm0Muj89KWbWISGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;320&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1709017765731&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ netstat -lnup&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;123/udp 포트가 리스닝 상태인지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAkke3/btsFm7SeLBZ/jmqdod1ddg2DeoXX5kFpm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAkke3/btsFm7SeLBZ/jmqdod1ddg2DeoXX5kFpm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAkke3/btsFm7SeLBZ/jmqdod1ddg2DeoXX5kFpm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAkke3%2FbtsFm7SeLBZ%2Fjmqdod1ddg2DeoXX5kFpm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1166&quot; height=&quot;102&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1709017825807&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;$ docker exec ntp cat /etc/chrony/chrony.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.yml 파일에 설정한 NTP_SERVERS의 값이 제대로 설정되어 있는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;749&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CX32y/btsFjKDGZ1H/YfkS0izTW1UtxkQF5UM8D0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CX32y/btsFjKDGZ1H/YfkS0izTW1UtxkQF5UM8D0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CX32y/btsFjKDGZ1H/YfkS0izTW1UtxkQF5UM8D0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCX32y%2FbtsFjKDGZ1H%2FYfkS0izTW1UtxkQF5UM8D0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;749&quot; height=&quot;296&quot; data-origin-width=&quot;749&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1709017900477&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker exec ntp date&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 입력했을 때 설정한 Timezone에 따라 시간이 현재 시간대로 동기화되어 출력되는지 확인한다.&lt;/p&gt;</description>
      <category>Docker</category>
      <category>chrony</category>
      <category>docker</category>
      <category>linux</category>
      <category>NTP</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/143</guid>
      <comments>https://limsw.tistory.com/143#entry143comment</comments>
      <pubDate>Tue, 27 Feb 2024 16:13:09 +0900</pubDate>
    </item>
    <item>
      <title>[FastAPI] Lifespan을 이용한 생명주기 관리 (Event Handler)</title>
      <link>https://limsw.tistory.com/142</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 FastAPI의 Lifespan을 이용하여 이벤트를 관리하는 방법을 알아보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서 말하는 이벤트 핸들링은 &lt;b&gt;FastAPI 서비스가 시작 또는 종료될 때 특정 함수를 호출하는 등의 행위&lt;/b&gt;를 말한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;본론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Lifespan이 생기기 이전에 FastAPI는 &lt;b&gt;on_event&lt;/b&gt;를 이용하여 이벤트를 관리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, FastAPI가 시작(종료)될 때 특정한 함수를 실행시키기 위해서는 다음과 같이 코드를 작성할 수 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1708953932289&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()

@app.on_event(&quot;startup&quot;)
async def startup_event():
    print(&quot;service is started.&quot;)

@app.on_event(&quot;shutdown&quot;)
async def shutdown_event():
    print(&quot;service is stopped.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;startup : FastAPI가 시작될 때 호출되는 함수&lt;/li&gt;
&lt;li&gt;shutdown : FastAPI가 종료될 때 호출되는 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실행/종료 했을 때의 결과는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq3hsG/btsFkXCpSYE/51Tts8nZ2hfomc8gmAXJI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq3hsG/btsFkXCpSYE/51Tts8nZ2hfomc8gmAXJI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq3hsG/btsFkXCpSYE/51Tts8nZ2hfomc8gmAXJI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq3hsG%2FbtsFkXCpSYE%2F51Tts8nZ2hfomc8gmAXJI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;279&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;279&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Lifespan을 사용하여 생명주기 관리하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 최근에는 on_event 사용을 지양하고 lifespan을 이용하여 이벤트를 관리하는 방법을 권장하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 작성한 코드를 Lifespan을 사용하여 구현하면 다음과 같이 코드를 작성할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708954861519&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from contextlib import asynccontextmanager
from fastapi import FastAPI

def start():
    print(&quot;service is started.&quot;)
    
def shutdown():
    print(&quot;service is stopped.&quot;)    

@asynccontextmanager
async def lifespan(app: FastAPI):
    # When service starts.
    start()
    
    yield
    
    # When service is stopped.
    shutdown()

app = FastAPI(lifespan=lifespan)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;app 객체를 선언할 때 인자로 lifespan을 정의한다.&lt;/li&gt;
&lt;li&gt;yield를 기준으로 &lt;b&gt;앞&lt;/b&gt;에 오는 코드는 &lt;b&gt;서비스가 시작될 때 호출되는 함수(코드)&lt;/b&gt; 이다.&lt;/li&gt;
&lt;li&gt;yield를 기준으로 &lt;b&gt;뒤&lt;/b&gt;에 오는 코드는 &lt;b&gt;서비스가 종료될 때 호출되는 함수(코드)&lt;/b&gt; 이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 실행 시 결과는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hlw5x/btsFh43jfK5/AKS74sV1DuoOUgDynY29tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hlw5x/btsFh43jfK5/AKS74sV1DuoOUgDynY29tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hlw5x/btsFh43jfK5/AKS74sV1DuoOUgDynY29tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHlw5x%2FbtsFh43jfK5%2FAKS74sV1DuoOUgDynY29tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;709&quot; height=&quot;258&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 동일하지만 on_event함수는 공식적으로 지원하지 않는다고 하니, Lifespan을 사용하는 것을 지향한다고 한다.&lt;/p&gt;</description>
      <category>Python</category>
      <category>fastapi</category>
      <category>lifespan</category>
      <category>on_event</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/142</guid>
      <comments>https://limsw.tistory.com/142#entry142comment</comments>
      <pubDate>Mon, 26 Feb 2024 22:52:55 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] Docker in Docker(DinD) - 도커 컨테이너 안에 도커 사용</title>
      <link>https://limsw.tistory.com/141</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker In Docker(DinD)는 실행중인 특정 도커 컨테이너에서 도커를 사용하는 기술이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 DinD를 사용하기 위해 필수적으로 해야하는 설정을 알아보고자 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;본론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 도커 컨테이너 안에서 로컬 서버의 도커 컨테이너 목록을 조회하는 기능을 구현한다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 도커 컨테이너 목록을 조회하기 위해 &lt;b&gt;docker ps -a&lt;/b&gt; 명령어를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 명령어를 기반으로 docker 이미지를 가진 컨테이너에서 docker ps -a 명령어를 수행하려면 다음과 같이 도커 명령어를 작성할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708566428938&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// --rm 명령어는 프로세스 종료 시 컨테이너 자동 제거 옵션
$ docker run --rm docker docker ps -a&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 받고난 후 명령어가 수행되면 다음과 같은 에러가 발생할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1708566644673&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;error during connect: Get &quot;http://docker:2375/v1.24/containers/json?all=1&quot;: dial tcp: lookup docker on 8.8.8.8:53: no such host&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 에러가 발생하는 이유는 로컬의 Docker Daemon에 명령어를 전달하기 위한 소켓(docker.sock)이 존재하지 않아서 발생하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서는 도커 컨테이너 시작 시 &lt;b&gt;볼륨 마운트&lt;/b&gt;를 통해 문제를 해결할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708567025034&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --rm -v /var/run/docker.sock:/var/run/docker.sock docker docker ps -a&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2023&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvrrp0/btsE91Z1Os9/UcN6PJtTfhWmhKi2DnywX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvrrp0/btsE91Z1Os9/UcN6PJtTfhWmhKi2DnywX1/img.png&quot; data-alt=&quot;볼륨 마운트 설정을 통한 DinD 기능 구현&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvrrp0/btsE91Z1Os9/UcN6PJtTfhWmhKi2DnywX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvrrp0%2FbtsE91Z1Os9%2FUcN6PJtTfhWmhKi2DnywX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2023&quot; height=&quot;155&quot; data-origin-width=&quot;2023&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;볼륨 마운트 설정을 통한 DinD 기능 구현&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림과 같이 도커 컨테이너 안에서 로컬 서버의 도커 컨테이너 목록을 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.yml 파일에 볼륨 마운트를 하려면 다음과 같이 작성하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1708567099137&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  myservice:
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Docker</category>
      <category>docker</category>
      <category>Docker in Docker</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/141</guid>
      <comments>https://limsw.tistory.com/141#entry141comment</comments>
      <pubDate>Thu, 22 Feb 2024 11:00:41 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 파이썬으로 도커 컨테이너(Docker Container) 관리하기</title>
      <link>https://limsw.tistory.com/140</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;서론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 파이썬에서 도커 컨테이너를 관리하는 방법에 대해서 알아본다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라이브러리 설치&lt;/h4&gt;
&lt;pre id=&quot;code_1708413791858&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;pip3 install docker # pip / pip3 적절히 설치&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드 예제1 - 실행중인 도커 컨테이너 목록 및 상태 출력&lt;/h4&gt;
&lt;pre id=&quot;code_1708413920212&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import docker

def get_docker_container_status():
    try:
        # Create Docker clients
        client = docker.from_env()

        # Import list of running containers
        containers = client.containers.list(all=True)

        # Return Container List and Status
        container_info = {}
        for container in containers:

            container_info[container.name] = {
                'container_id': container.id,
                'status': container.status,
            }

        return container_info

    except docker.errors.APIError as e:
        return f&quot;Error: {e}&quot;

if __name__ == '__main__':
    result = get_docker_container_status()
    print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1708414640116&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  'sniffer': {
    'container_id': '7c150654117f1624172f010a9cd493beb61f565cff20f0b10e7f792e0afe80fc', 
    'status': 'exited'
    },
  'ntp': {
    'container_id': 'f44ffc41cde7b0c78eedc309a483cc47e79e5386fa85fd7393ee51611e202481', 
    'status': 'running'
    }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드 예제2 - 컨테이너 이름을 통해 컨테이너 상태 출력&lt;/h4&gt;
&lt;pre id=&quot;code_1708414489019&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import docker

def get_docker_container_status_using_conName():
    try:
        # Create Docker clients
        client = docker.from_env()

        # 컨테이너 이름을 통해 해당 컨테이너의 객체를 리턴으로 받음
        container = client.containers.get('ntp')

        return {
            'container_id': container.id,
            'status': container.status,
            'container_name': container.name,
            'container_ip': container.attrs['NetworkSettings']['Networks']['docker-ntp_default']['IPAddress']
        }

    except docker.errors.APIError as e:
        return f&quot;Error: {e}&quot;

if __name__ == '__main__':
    result = get_docker_container_status_using_conName()
    print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로, 컨테이너 IP를 추출할 때 &lt;b&gt;docker-ntp_default&lt;/b&gt;가 명시되어 있는 이유는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker network ls&lt;/b&gt; 명령어를 입력했을 때 나오는 네트워크 이름이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LKebo/btsE9HlTggS/xc0a7pAvdVUrxX15AKi1I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LKebo/btsE9HlTggS/xc0a7pAvdVUrxX15AKi1I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LKebo/btsE9HlTggS/xc0a7pAvdVUrxX15AKi1I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLKebo%2FbtsE9HlTggS%2Fxc0a7pAvdVUrxX15AKi1I0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;94&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;94&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1708414514436&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
'container_id': 'f44ffc41cde7b0c78eedc309a483cc47e79e5386fa85fd7393ee51611e202481', 
'status': 'running', 
'container_name': 'ntp', 
'container_ip': '172.21.0.2'
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드 예제3 - Exited 상태의 컨테이너 재시작하기&lt;/h4&gt;
&lt;pre id=&quot;code_1708414782451&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import docker

def restart_stopped_containers(container_name: str):

    try:
         # Create Docker clients
        client = docker.from_env()

        # 컨테이너 이름을 통해 해당 컨테이너의 객체를 리턴으로 받음
        container = client.containers.get(container_name)

        # Restarting a Exited Container
        if container.status == 'exited':
            container.restart()
            return {&quot;status&quot;: &quot;Container restarted successfully&quot;}
        else:
            return {&quot;status&quot;: &quot;Container is not in 'exited' state, no restart needed&quot;}
        
    except docker.errors.APIError as e:
        return f&quot;Error: {e}&quot;
    
if __name__ == '__main__':
    result = restart_stopped_containers('ntp')
    print(result)

    result = restart_stopped_containers('sniffer')
    print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1708414820412&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# container_name : ntp 결과
{'status': &quot;Container is not in 'exited' state, no restart needed&quot;}

# container_name : sniffer 결과
{'status': 'Container restarted successfully'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재시작 된 컨테이너에 대해서는 &lt;b&gt;docker ps -a&lt;/b&gt; 명령어로 확인하면 Up 타임이 최신화 되어 있는 것을 확인할 수 있다.&lt;/p&gt;</description>
      <category>Python</category>
      <category>docker</category>
      <category>python</category>
      <author>limsw</author>
      <guid isPermaLink="true">https://limsw.tistory.com/140</guid>
      <comments>https://limsw.tistory.com/140#entry140comment</comments>
      <pubDate>Tue, 20 Feb 2024 16:45:12 +0900</pubDate>
    </item>
  </channel>
</rss>